Skip to content

Commit d2a633d

Browse files
authored
feat(cli): Bun-first CLI migration with 7 commands and comprehensive test suite (#259)
* feat(cli): migrate packages/cli to Bun-first with full test suite - Replace fs-extra with Bun.file/Bun.write/node:fs/promises across all 9 source files - Fix --help fast-path so all 8 subcommands are visible - Replace tsup/tsx/vitest with bun build/bun run/bun:test - Replace __dirname with import.meta.dir; use JSON import assertion for version - Fix hardcoded OAC_VERSION in add.ts; add hashesMatch import to status.ts - Replace computeFileHash with Bun.file().bytes() in sha256.ts - Rename checkNodeVersion → checkBunVersion; parallelise doctor checks - Fix TypeScript void-inference errors in update.ts, list.ts, status.ts by replacing .catch() with let/try-catch where result is used downstream - Add ManifestError named error class; remove unsafe type casts - Add 43 bun:test unit tests (sha256, manifest, installer, version) — 0 failures * fix(cli): resolve critical bugs and standards violations found in review Critical bug fixes: - ide-detect.ts: replace Bun.file(dir).exists() with stat().isDirectory() for all directory checks — Bun.file().exists() always returns false for directories, breaking Cursor/Windsurf/OpenCode/Claude detection entirely - bundled.ts: add registry.json exclusion anchor to findPackageRoot() to prevent monorepo root from matching before the CLI package root Standards cleanup (§4.1, §5.1, §6.1, §15.3, §21.1): - status.ts: parallelise findModifiedFiles + detectIdes with Promise.all - apply.ts: fix duplicate warn/limit messages; remove else after return in reportWarnings; remove redundant mkdir before Bun.write - version.ts: remove unnecessary (pkgJson as {version?:string}) cast - manifest.ts: remove redundant mkdir before Bun.write; remove as unknown cast - installer.ts: remove redundant mkdir calls before Bun.write throughout - add.ts: add comment explaining why node:fs/promises rm is used Tests (43 → 142, +99 new tests across 4 new files): - ide-detect.test.ts: 26 tests covering all 4 IDEs, both claude indicators, detectIdes parallel, isIdePresent — directly validates the directory fix - bundled.test.ts: 27 tests for classifyBundledFile, findPackageRoot, listBundledFiles, getBundledFilePath, bundledFileExists - config.test.ts: 25 tests for readConfig/writeConfig round-trips, createDefaultConfig, mergeConfig, isYoloMode, isAutoBackup - installer-update.test.ts: 14 tests covering all 5 updateFiles decision branches (install/update/skip/yolo/dry-run) plus isProjectRoot - sha256.test.ts: +4 tests for empty file, large file, binary content * fix(cli): fix P0 bugs — global flags, .git detection, package root resolution - Remove duplicate --dry-run/--yolo/--verbose from parent program; Commander.js global option stealing caused all safety flags to be silently dropped in every subcommand action callback - Fix isProjectRoot() to use stat() for .git detection; Bun.file().exists() returns false for directories, breaking oac init in standard git repos - Add OAC_PACKAGE_ROOT env var override to getPackageRoot() for dev/monorepo mode; registry.json heuristic excluded the repo root causing oac init/add/update to throw when run from source - Fix build script: remove --banner flag that caused double shebang in dist/index.js - Rewrite bin/oac.js to invoke bun instead of node (dist is bun-only target) - Refactor installer.ts let accumulators to const using Promise.all + reduce - Add MVP planning docs (00-MVP-PLAN.md, master synthesis, project breakdown) * fix(install.sh): handle both singular and plural component type formats The get_registry_key() function was always adding 's' to the type, causing 'contexts' to become 'contextss' which doesn't exist in registry.json. Now handles: - Singular forms: context → contexts, agent → agents, skill → skills - Plural forms: contexts → contexts (unchanged), agents → agents - Config stays singular - Fallback for any type ending in 's' Fixes #257
1 parent 40dd267 commit d2a633d

38 files changed

+14966
-2018
lines changed

bin/oac.js

Lines changed: 13 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,23 @@
11
#!/usr/bin/env node
2+
'use strict';
23

3-
/**
4-
* OpenAgents Control (OAC) CLI
5-
*
6-
* This is the main entry point for the @openagents/control package.
7-
* It runs the install.sh script to set up the OpenAgents Control system.
8-
*/
9-
10-
const { spawn } = require('child_process');
4+
const { execFileSync } = require('child_process');
115
const path = require('path');
126
const fs = require('fs');
137

14-
// Get the package root directory
15-
const packageRoot = path.join(__dirname, '..');
16-
17-
// Path to install.sh
18-
const installScript = path.join(packageRoot, 'install.sh');
8+
const cliDist = path.join(__dirname, '..', 'packages', 'cli', 'dist', 'index.js');
199

20-
// Check if install.sh exists
21-
if (!fs.existsSync(installScript)) {
22-
console.error('Error: install.sh not found at', installScript);
10+
if (!fs.existsSync(cliDist)) {
11+
console.error('Error: OAC CLI not built yet. Run: npm run build -w packages/cli');
2312
process.exit(1);
2413
}
2514

26-
// Get command line arguments (skip node and script path)
27-
const args = process.argv.slice(2);
28-
29-
// If no arguments provided, show help
30-
if (args.length === 0) {
31-
console.log(`
32-
╔═══════════════════════════════════════════════════════════════╗
33-
║ OpenAgents Control (OAC) ║
34-
║ AI agent framework for plan-first development workflows ║
35-
╚═══════════════════════════════════════════════════════════════╝
36-
37-
Usage:
38-
oac [profile] Install with a specific profile
39-
oac --help Show this help message
40-
oac --version Show version information
41-
42-
Available Profiles:
43-
essential Minimal setup (OpenAgent only)
44-
developer Full development setup (recommended)
45-
business Business-focused agents
46-
advanced Advanced features and specialists
47-
full Everything included
48-
49-
Examples:
50-
oac Interactive installation
51-
oac developer Install with developer profile
52-
oac --help Show detailed help
53-
54-
For more information, visit:
55-
https://github.com/darrenhinde/OpenAgentsControl
56-
`);
57-
process.exit(0);
58-
}
59-
60-
// Handle --version flag
61-
if (args.includes('--version') || args.includes('-v')) {
62-
const packageJson = require(path.join(packageRoot, 'package.json'));
63-
console.log(`@openagents/control v${packageJson.version}`);
64-
process.exit(0);
65-
}
66-
67-
// Handle --help flag
68-
if (args.includes('--help') || args.includes('-h')) {
69-
// Run install.sh with --help
70-
args.push('--help');
71-
}
72-
73-
// Run the install script with bash
74-
const child = spawn('bash', [installScript, ...args], {
75-
cwd: packageRoot,
76-
stdio: 'inherit',
77-
env: {
78-
...process.env,
79-
OAC_PACKAGE_ROOT: packageRoot
15+
try {
16+
execFileSync('bun', [cliDist, ...process.argv.slice(2)], { stdio: 'inherit' });
17+
} catch (err) {
18+
if (err.code === 'ENOENT') {
19+
console.error('Error: Bun is required to run OAC CLI. Install from https://bun.sh');
20+
process.exit(1);
8021
}
81-
});
82-
83-
child.on('error', (error) => {
84-
console.error('Error running install script:', error.message);
85-
process.exit(1);
86-
});
87-
88-
child.on('exit', (code) => {
89-
process.exit(code || 0);
90-
});
22+
process.exitCode = err.status ?? 1;
23+
}

bun.lock

Lines changed: 208 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/planning/00-INDEX.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@
77

88
---
99

10-
## 📁 Planning Documents
10+
## 🎯 MVP Plan (START HERE)
11+
12+
**`mvp/00-MVP-PLAN.md`** — The 20% that delivers 80% of the value.
13+
5 commands, 6 weeks, focused on what users actually care about.
14+
This is what we build first. Everything else is v1.1+.
15+
16+
---
17+
18+
## 📁 Full Planning Documents (Reference)
1119

1220
### Core Planning
1321

0 commit comments

Comments
 (0)