Skip to content

feat(cli): add managed daemon commands - #55

Draft
ahrav wants to merge 1 commit into
stack/mc-host-07-plugin-demandfrom
stack/mc-host-08-cli
Draft

feat(cli): add managed daemon commands#55
ahrav wants to merge 1 commit into
stack/mc-host-07-plugin-demandfrom
stack/mc-host-08-cli

Conversation

@ahrav

@ahrav ahrav commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • add daemon start, stop, restart, status, and doctor commands
  • expose stable magic-context.daemon/v1 JSON and human rendering
  • bypass unrelated SQLite preflight for daemon operations

Stack

PR 8 of 10. Base: stack/mc-host-07-plugin-demand.

Validation

  • CLI daemon command and dispatch tests
  • CLI typecheck and build

Post-Deploy Monitoring & Validation

Watch command exit codes and reason/effect distributions for one release cycle. Roll back if status/doctor mutate state, JSON shape drifts, or restart effects disagree with observed lifecycle state. Owner: CLI maintainers.

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

Comment thread packages/cli/src/index.ts
throw error;
return realpathSync.native(entry) === realpathSync.native(fileURLToPath(import.meta.url));
} catch {
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExecutableEntry() silently disables the whole CLI if realpathSync.native throws. The old index.ts always invoked main() unconditionally; now, on an install layout where process.argv[1] can't be realpath-resolved (ENOENT/EACCES, an unusual global-install symlink chain, or the bin file briefly missing during an atomic npm install), the catch swallows the error and this returns false. dispatchCli() is then never called, process.exitCode stays at its default 0, and the CLI silently does nothing instead of running the command or reporting an error.

No test exercises this exception path — index.test.ts only covers the "import as module" and "symlink success" cases. Consider at least logging/exiting non-zero when the realpath comparison throws, rather than treating it the same as "this is not the entry module."

Comment on lines +45 to +91
export function usageText(): string {
return [
"",
" Magic Context CLI",
" -----------------",
"",
" Commands:",
" setup Interactive setup wizard",
" doctor Check and fix configuration issues",
" daemon start Start the managed mc-host",
" daemon stop Stop the managed mc-host",
" daemon restart Restart the managed mc-host as one transaction",
" daemon status Show lifecycle and readiness state without mutation",
" daemon doctor Run read-only lifecycle diagnostics",
"",
" Daemon output:",
" --json Emit one magic-context.daemon/v1 JSON object",
"",
" Doctor options:",
" doctor --force Force-clear plugin cache",
" doctor --issue Collect diagnostics and open a GitHub issue",
" doctor --clear Interactive cache cleanup picker",
" doctor --check-v22-backfill Show v22 memory backfill status",
" doctor --retry-v22-backfill Retry failed v22 memory backfill rows",
" doctor --rekey-v22-dir-identity <path> Re-key legacy dir identity rows",
" doctor --check-claims-backfill Show v84 claims backfill status",
" doctor --retry-claims-backfill Repair and resume the v84 claims backfill",
' doctor --waive-claims-backfill-failure <id> --rationale "<why>"',
" doctor drain-authority <project> Drain module memory/note authority to TypeScript",
" doctor migrate Migrate OpenCode session to Pi or OMP JSONL",
" doctor migrate-session Re-home an OpenCode session to another directory",
" doctor merge-identity Merge project rows (--from ID --to ID [--dry-run] [--yes])",
" doctor repair-db Back up and salvage a corrupted shared database",
"",
" Harness selection:",
" --harness opencode Target OpenCode only",
" --harness pi Target Pi only",
" --harness omp Target Oh My Pi (OMP) only",
" (default: auto-detect, prompt if multiple installed)",
"",
" Usage:",
" npx @cortexkit/magic-context@latest setup",
" npx @cortexkit/magic-context@latest doctor",
" npx @cortexkit/magic-context@latest daemon status --json",
"",
].join("\n");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usageText() regresses --help output vs. the previous printUsage() in index.ts. Several lines present in the old implementation are missing here:

  • the --dry-run tip under setup ("add --dry-run to preview the wizard without writing any files")
  • the doctor --issue usage example
  • the two-line doctor migrate --from opencode --to <pi|omp> --session ses_xxx --dry-run example
  • the trailing "Waive a reviewed lineage failure" description after the --waive-claims-backfill-failure <id> --rationale "<why>" line (now cut off mid-sentence at line 72)

index.test.ts only asserts daemon actions and --json appear in the help text, so this regression isn't caught by tests. Worth restoring for parity, or confirming the drop was intentional.

Comment on lines +146 to +147
const redacted = redactResult(result, dependencies.env);
dependencies.stdout(parsed.json ? JSON.stringify(redacted) : renderDaemonHuman(redacted));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redactResult() and the stdout/renderDaemonHuman() call run outside the try block above (lines 136-144), which is the block that's supposed to guarantee "bounded stderr, no partial output" on failure (see the daemon.test.ts case "policy exceptions produce bounded stderr without a partial v1 object"). That guarantee is only tested for createPolicy/invoke throwing — if redactResultsanitizeDiagnosticText ever throws (e.g. a future contract change or alternate DaemonPolicy implementation returns a versions field that isn't strictly string | null), the error propagates uncaught out of runDaemonCommand instead of hitting the bounded-stderr path.

Might be worth extending the try to cover redaction + rendering as well, so the "no partial/unredacted output" contract holds for the whole function, not just the policy call.

@claude

claude Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review summary

Reviewed feat(cli): add managed daemon commands (PR #55) for correctness, security, and code quality. Overall the refactor into dispatch.ts + daemon.ts is clean, well-tested (subprocess-level tests for the real entrypoint, redaction tests, bounded-stderr-on-failure tests), and the daemon result redaction/JSON contract is a good pattern. Three issues found, left as inline comments:

  1. packages/cli/src/index.ts — the new isExecutableEntry() guard (added for import-safety) fails closed: if realpathSync.native throws for any reason, the CLI silently does nothing (exit code 0, no output) instead of running or erroring. The old code invoked main() unconditionally. No test covers this exception path.
  2. packages/cli/src/dispatch.tsusageText() dropped several --help lines that existed in the old printUsage(): the setup --dry-run tip, the doctor --issue example, the doctor migrate ... --dry-run example, and the trailing description on the --waive-claims-backfill-failure line (now truncated mid-sentence). Likely just needs porting over.
  3. packages/cli/src/commands/daemon.tsredactResult()/renderDaemonHuman()/stdout() run after the try/catch that's meant to guarantee bounded, redacted stderr on failure. If redaction itself ever throws, it escapes uncaught rather than hitting the bounded-stderr path the tests otherwise verify.

No security concerns beyond #3 (which is about failure-mode robustness, not an exploitable issue) — the redaction/sanitization approach for daemon output looks sound, and the SQLite-preflight bypass for daemon commands is scoped correctly (daemon short-circuits before the doctor preflight check, per dispatch.ts).

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