Skip to content

feat(cli): Bun-first CLI migration with 7 commands and comprehensive test suite#259

Open
darrenhinde wants to merge 5 commits intomainfrom
feature/oac-package-refactor-v2
Open

feat(cli): Bun-first CLI migration with 7 commands and comprehensive test suite#259
darrenhinde wants to merge 5 commits intomainfrom
feature/oac-package-refactor-v2

Conversation

@darrenhinde
Copy link
Owner

Summary

This PR introduces a complete Bun-first CLI implementation for the OAC (OpenAgents Control) package, migrating from Node.js/tsup/vitest to Bun-native tooling.

Changes Included

🚀 Major Features

  • Bun-first migration: Replaced tsup/tsx/vitest with bun build, bun run, and bun:test
  • 7 CLI commands implemented:
    • oac init - Initialize OAC in a project with bundled files
    • oac update - Update with SHA256-based change detection
    • oac add/remove - Add/remove specific components from registry
    • oac apply - Generate IDE-specific files (Cursor, Claude, Windsurf)
    • oac doctor - Health checks and diagnostics
    • oac list - Show installed components
    • oac status - One-screen summary

🔧 Technical Improvements

  • Eliminated fs-extra dependency - using Bun APIs + node:fs/promises
  • Replaced __dirname with import.meta.dir (Bun-native)
  • Migrated to bun:test from vitest
  • Full TypeScript with strict mode
  • Pure functional patterns following CODEBASE_STANDARDS.md
  • Lazy imports for <100ms startup time

📦 Package Structure

packages/cli/
├── src/
│   ├── commands/     # 7 command implementations
│   ├── lib/         # Core libraries (manifest, config, installer, etc.)
│   ├── ui/          # Logger and spinner components
│   └── index.ts     # Commander.js entry point
├── package.json     # Bun-first configuration
└── tsconfig.json    # TypeScript strict mode

✅ Exit Criteria Met

  • bun run src/index.ts --version works
  • bun run src/index.ts --help shows all 7 subcommands
  • bun run src/index.ts doctor runs without crashing
  • bun build succeeds
  • tsc --noEmit passes
  • No fs-extra in source
  • No __dirname in source
  • No vitest or tsup in dependencies

📚 Documentation

  • Added comprehensive MVP planning docs
  • Updated planning index
  • External context research on provider patterns

Testing

  • Full test suite with bun:test
  • All 22 MVP subtasks completed
  • Bun migration phases 1-4 in progress

Related

  • Brings in latest main fixes (plugin hooks, context management, AOC→OAC rename)
  • Ready for integration with compatibility-layer for IDE adapters

Note: The .cursorrules.bak file in the working directory is a temporary backup and not included in this PR.

- 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
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
…solution

- 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)
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant