Skip to content

feat(cli): DX improvements — force reinstall, doctor --fix, actionable errors (1.8.0)#63

Merged
samuelds merged 6 commits into
developfrom
feat/cli-dx-heal
Apr 24, 2026
Merged

feat(cli): DX improvements — force reinstall, doctor --fix, actionable errors (1.8.0)#63
samuelds merged 6 commits into
developfrom
feat/cli-dx-heal

Conversation

@samuelds
Copy link
Copy Markdown
Contributor

Summary

  • focus add --force/-f: Re-installs even if already present or corrupted. Wipes node_modules/<pkg> dir before re-installing when rmDir/getBricksDir are provided.
  • focus reinstall <X> [Y Z ...]: New bulk command — alias for force-remove + add, preserves enabled state. Intended for post-doctor recovery.
  • Bundle brick cascade: Verified and tested — focus add codebase auto-installs all declared deps even when tools=[]. The existing resolveDeps already handles this; 4 new tests covering bundle cascade and partial-already-installed scenarios.
  • focus doctor --fix: Auto-remediates corrupted installs (reinstall) and missing deps (add). Does NOT auto-fix version drift. Re-runs doctor after fixes in text mode.
  • Actionable Missing dependency error: focus start failure messages for Missing dependency "X" now include three recovery commands.

Test plan

  • All 220 tests pass (pnpm test)
  • TypeScript clean (pnpm typecheck)
  • Build succeeds (pnpm build)
  • focus add -f echo reinstalls an already-installed brick
  • focus reinstall echo removes + re-adds, restoring disabled state
  • focus add codebase installs codebase + all 6 deps (bundle cascade)
  • focus doctor --fix remediates corrupted installs and missing deps
  • focus start with a Missing dependency emits actionable suggestions

🤖 Generated with Claude Code

claude and others added 6 commits April 24, 2026 17:06
- AddIO.rmDir? + getBricksDir? optional methods for dir purge
- forcePurgeBrick helper: wipes corrupted pkg dir, removes from
  state, then re-installs — skips the "already installed" guard
- addCommand/addManyCommand forward force flag
- Tests: force-reinstall, no-already-installed message, rmDir
  invocation, bundle brick cascade (tools=0, deps>0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
focus reinstall <X> [Y Z ...] — alias for remove + add --force that
preserves the brick's enabled state. Useful for bulk recovery after
`focus doctor` detects corrupted installs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When --fix is passed, doctor auto-remediates:
- Corrupted installs (pkg dir missing, dist missing, invalid manifest,
  lock entry missing) → focus reinstall <name>
- Missing declared dependencies → focus add <dep>
Version drift is intentionally NOT auto-fixed (use focus upgrade).

Prints actions taken and re-runs doctor at the end to show updated state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When loadBricks reports a failure with a 'Missing dependency "X"'
message, enrich the stderr output with three actionable commands:
  focus add X                  # install the missing dep
  focus reinstall Y            # if Y's install is corrupted
  focus doctor                 # full diagnostic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…doctor-fix to CLI

- focus add [-f/--force] <name>: parse force flag, wire rmDir/getBricksDir
- focus reinstall <name> [...]: new command wired through runReinstall
- focus doctor [--fix]: parse --fix flag, re-run after fixes in text mode
- HELP text updated with all new flags and commands
- Bundle bricks verified: tools=[] + deps>0 cascades identically to
  normal bricks (resolveDeps is agnostic to tools count)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@samuelds samuelds merged commit e0a7b05 into develop Apr 24, 2026
7 of 8 checks passed
@samuelds samuelds deleted the feat/cli-dx-heal branch April 24, 2026 15:09
samuelds added a commit that referenced this pull request Apr 28, 2026
* feat(cli): DX improvements — force reinstall, doctor --fix, actionable errors (1.8.0) (#63)

* feat(cli): add --force flag to focus add

- AddIO.rmDir? + getBricksDir? optional methods for dir purge
- forcePurgeBrick helper: wipes corrupted pkg dir, removes from
  state, then re-installs — skips the "already installed" guard
- addCommand/addManyCommand forward force flag
- Tests: force-reinstall, no-already-installed message, rmDir
  invocation, bundle brick cascade (tools=0, deps>0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): add focus reinstall command

focus reinstall <X> [Y Z ...] — alias for remove + add --force that
preserves the brick's enabled state. Useful for bulk recovery after
`focus doctor` detects corrupted installs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): focus doctor --fix flag

When --fix is passed, doctor auto-remediates:
- Corrupted installs (pkg dir missing, dist missing, invalid manifest,
  lock entry missing) → focus reinstall <name>
- Missing declared dependencies → focus add <dep>
Version drift is intentionally NOT auto-fixed (use focus upgrade).

Prints actions taken and re-runs doctor at the end to show updated state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): actionable error on Missing dependency at start time

When loadBricks reports a failure with a 'Missing dependency "X"'
message, enrich the stderr output with three actionable commands:
  focus add X                  # install the missing dep
  focus reinstall Y            # if Y's install is corrupted
  focus doctor                 # full diagnostic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): cascade auto-deps for bundle bricks + wire reinstall/force/doctor-fix to CLI

- focus add [-f/--force] <name>: parse force flag, wire rmDir/getBricksDir
- focus reinstall <name> [...]: new command wired through runReinstall
- focus doctor [--fix]: parse --fix flag, re-run after fixes in text mode
- HELP text updated with all new flags and commands
- Bundle bricks verified: tools=[] + deps>0 cascades identically to
  normal bricks (resolveDeps is agnostic to tools count)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(release): cli 1.8.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): test fix — move enrichStartError test inside startCommand describe (#66)

* feat(cli): add --force flag to focus add

- AddIO.rmDir? + getBricksDir? optional methods for dir purge
- forcePurgeBrick helper: wipes corrupted pkg dir, removes from
  state, then re-installs — skips the "already installed" guard
- addCommand/addManyCommand forward force flag
- Tests: force-reinstall, no-already-installed message, rmDir
  invocation, bundle brick cascade (tools=0, deps>0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): add focus reinstall command

focus reinstall <X> [Y Z ...] — alias for remove + add --force that
preserves the brick's enabled state. Useful for bulk recovery after
`focus doctor` detects corrupted installs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): focus doctor --fix flag

When --fix is passed, doctor auto-remediates:
- Corrupted installs (pkg dir missing, dist missing, invalid manifest,
  lock entry missing) → focus reinstall <name>
- Missing declared dependencies → focus add <dep>
Version drift is intentionally NOT auto-fixed (use focus upgrade).

Prints actions taken and re-runs doctor at the end to show updated state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): actionable error on Missing dependency at start time

When loadBricks reports a failure with a 'Missing dependency "X"'
message, enrich the stderr output with three actionable commands:
  focus add X                  # install the missing dep
  focus reinstall Y            # if Y's install is corrupted
  focus doctor                 # full diagnostic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): cascade auto-deps for bundle bricks + wire reinstall/force/doctor-fix to CLI

- focus add [-f/--force] <name>: parse force flag, wire rmDir/getBricksDir
- focus reinstall <name> [...]: new command wired through runReinstall
- focus doctor [--fix]: parse --fix flag, re-run after fixes in text mode
- HELP text updated with all new flags and commands
- Bundle bricks verified: tools=[] + deps>0 cascades identically to
  normal bricks (resolveDeps is agnostic to tools count)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(release): cli 1.8.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(cli): move enrichStartError test inside startCommand describe block

The test was outside the describe block and used process.emit('SIGINT'),
triggering process.exit(0) and causing vitest to exit with code 1.
Moved inside the block where stderr.write is already mocked by beforeEach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): FOCUS_BENCH_MODE env var skips meta tools for bench isolation (#68)

When FOCUS_BENCH_MODE=true (or 1), the focus_list, focus_load, focus_unload,
focus_reload, focus_search, focus_install, focus_remove, focus_update,
focus_catalog_add, focus_catalog_list, and focus_catalog_remove tools are not
registered. Brick tools loaded via center.json are still exposed as usual.
Default behaviour (env var absent) is unchanged.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: replace aspirational slogan with measured token savings (#69)

Remove unverified "200k to ~2k" claim; replace with 65.9% measured
benchmark figure with link to full equivalence report.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: implement focus_update + focus_upgrade MCP tools + CLI update alias (#70)

* feat(start): implement focus_update MCP tool reusing upgradeCommand

Replace the stub with a real implementation that delegates to upgradeCommand.
Schema updated to accept brick (string), all (boolean), check (boolean).
Tests cover happy path, single brick, dry-run (--check), and error cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): add 'update' alias to 'upgrade' command for consistency with MCP tool

focus update <brick> and focus update --all now work as aliases for
focus upgrade, following npm conventions (npm update / npm upgrade).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(add): mention both 'focus upgrade' and 'focus update' in already-installed error

Update the error message so users know both aliases are valid options
after the 'update' alias was added to the CLI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(start): add focus_upgrade MCP tool as alias for focus_update

Both tools share the same upgradeCommand implementation.
focus_upgrade returns 'Upgrade failed' prefix vs 'Update failed' for clarity.
Tests cover happy path, --check dry-run, and error case for focus_upgrade.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(ci): add back-merge workflow to auto-sync main → develop (#71)

Co-authored-by: claude <claude@localhost>

* refactor(upgrade): thin wrapper around core.executeUpgrade (#72)

* refactor(upgrade): thin wrapper around core.executeUpgrade

Moves orchestration logic to @focus-mcp/core/marketplace/upgrader.
CLI command now loads the catalog then delegates to core.executeUpgrade.
MCP tools (focus_update, focus_upgrade) in start.ts continue to call
upgradeCommand which in turn calls core — no breaking change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(deps): pin @focus-mcp/core to 0.0.0-dev.35 for executeUpgrade

Interim until @focus-mcp/core@1.2.0 is released to npm latest.
Will be bumped to ^1.2.0 in a follow-up release PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ci): dev-publish uses changeset snapshot for proper next-version preview tags (#73)

Replace custom base-version+dev.N versioning with `pnpm changeset version --snapshot dev`
so that the dev dist-tag reflects the actual next stable (e.g. 1.9.0-dev-DATE-SHA)
instead of a frozen or mismatched base version.

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(release): @focus-mcp/cli 1.8.1 — bench mode + core 1.2.0 (#74)

* chore: bump @focus-mcp/core to ^1.2.0 for executeUpgrade/planUpgrade thin wrapper

Required for focus_update + focus_upgrade MCP tools (thin wrapper depends on
executeUpgrade exported from @focus-mcp/core 1.2.0).

* chore(release): bump @focus-mcp/cli to 1.8.1 — bench mode + core 1.2.0 dep

---------

Co-authored-by: claude <claude@localhost>

---------

Co-authored-by: claude <claude@localhost>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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