brainclaw 0.28.0 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +193 -170
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +683 -23
  4. package/dist/commands/accept.js +3 -0
  5. package/dist/commands/add-step.js +11 -26
  6. package/dist/commands/agent-board.js +70 -3
  7. package/dist/commands/audit.js +19 -0
  8. package/dist/commands/check-policy.js +54 -0
  9. package/dist/commands/check-security-mcp.js +145 -0
  10. package/dist/commands/check-security.js +106 -0
  11. package/dist/commands/claim-resource.js +1 -0
  12. package/dist/commands/codev.js +672 -0
  13. package/dist/commands/compact.js +74 -0
  14. package/dist/commands/complete-step.js +16 -26
  15. package/dist/commands/constraint.js +8 -20
  16. package/dist/commands/decision.js +9 -20
  17. package/dist/commands/delete-plan.js +10 -12
  18. package/dist/commands/delete-step.js +16 -0
  19. package/dist/commands/dispatch.js +163 -0
  20. package/dist/commands/doctor.js +1122 -49
  21. package/dist/commands/enable-agent.js +1 -0
  22. package/dist/commands/export.js +280 -22
  23. package/dist/commands/handoff.js +33 -0
  24. package/dist/commands/harvest.js +189 -0
  25. package/dist/commands/hooks.js +82 -25
  26. package/dist/commands/inbox.js +169 -0
  27. package/dist/commands/init.js +38 -31
  28. package/dist/commands/install-hooks.js +71 -44
  29. package/dist/commands/link.js +89 -0
  30. package/dist/commands/list-claims.js +48 -3
  31. package/dist/commands/list-plans.js +129 -25
  32. package/dist/commands/loops-handlers.js +409 -0
  33. package/dist/commands/mcp-read-handlers.js +1628 -0
  34. package/dist/commands/mcp-schemas.generated.js +74 -0
  35. package/dist/commands/mcp.js +4244 -1475
  36. package/dist/commands/plan-resource.js +64 -0
  37. package/dist/commands/plan.js +12 -26
  38. package/dist/commands/prune.js +37 -2
  39. package/dist/commands/reflect.js +20 -7
  40. package/dist/commands/release-claim.js +11 -6
  41. package/dist/commands/release-notes.js +170 -0
  42. package/dist/commands/repair.js +210 -0
  43. package/dist/commands/run-profile.js +57 -0
  44. package/dist/commands/sequence.js +113 -0
  45. package/dist/commands/session-end.js +423 -14
  46. package/dist/commands/session-start.js +214 -41
  47. package/dist/commands/setup-security.js +103 -0
  48. package/dist/commands/setup.js +42 -4
  49. package/dist/commands/stale.js +109 -0
  50. package/dist/commands/switch.js +131 -10
  51. package/dist/commands/trap.js +14 -31
  52. package/dist/commands/update-handoff.js +63 -4
  53. package/dist/commands/update-plan.js +21 -28
  54. package/dist/commands/update-step.js +37 -0
  55. package/dist/commands/upgrade.js +313 -6
  56. package/dist/commands/usage.js +102 -0
  57. package/dist/commands/version.js +20 -0
  58. package/dist/commands/who.js +124 -0
  59. package/dist/commands/worktree.js +105 -0
  60. package/dist/core/actions.js +315 -0
  61. package/dist/core/agent-capability.js +610 -17
  62. package/dist/core/agent-context.js +7 -1
  63. package/dist/core/agent-files.js +1169 -85
  64. package/dist/core/agent-integrations.js +160 -5
  65. package/dist/core/agent-inventory.js +2 -0
  66. package/dist/core/agent-profiles.js +93 -0
  67. package/dist/core/agent-registry.js +162 -30
  68. package/dist/core/agentrun-reconciler.js +345 -0
  69. package/dist/core/agentruns.js +424 -0
  70. package/dist/core/ai-agent-detection.js +31 -10
  71. package/dist/core/archival.js +77 -0
  72. package/dist/core/assignment-sweeper.js +82 -0
  73. package/dist/core/assignments.js +367 -0
  74. package/dist/core/audit.js +30 -0
  75. package/dist/core/bootstrap.js +61 -10
  76. package/dist/core/brainclaw-version.js +94 -2
  77. package/dist/core/candidates.js +93 -2
  78. package/dist/core/claims.js +419 -0
  79. package/dist/core/codev-metrics.js +77 -0
  80. package/dist/core/codev-personas.js +31 -0
  81. package/dist/core/codev-plan-gen.js +35 -0
  82. package/dist/core/codev-prompts.js +74 -0
  83. package/dist/core/codev-responses.js +62 -0
  84. package/dist/core/codev-rounds.js +218 -0
  85. package/dist/core/config.js +4 -0
  86. package/dist/core/context.js +454 -34
  87. package/dist/core/coordination.js +201 -6
  88. package/dist/core/cross-project.js +230 -16
  89. package/dist/core/default-profiles/doctor.yaml +11 -0
  90. package/dist/core/default-profiles/janitor.yaml +11 -0
  91. package/dist/core/default-profiles/onboarder.yaml +11 -0
  92. package/dist/core/default-profiles/reviewer.yaml +13 -0
  93. package/dist/core/dispatcher.js +1189 -0
  94. package/dist/core/duplicates.js +2 -2
  95. package/dist/core/entity-operations.js +450 -0
  96. package/dist/core/entity-registry.js +344 -0
  97. package/dist/core/event-log.js +1 -0
  98. package/dist/core/events.js +106 -2
  99. package/dist/core/execution-adapters.js +154 -0
  100. package/dist/core/execution-context.js +63 -0
  101. package/dist/core/execution-profile.js +270 -0
  102. package/dist/core/execution.js +255 -0
  103. package/dist/core/facade-schema.js +81 -0
  104. package/dist/core/federation-cloud.js +99 -0
  105. package/dist/core/federation-message.js +52 -0
  106. package/dist/core/federation-transport.js +65 -0
  107. package/dist/core/gc-semantic.js +482 -0
  108. package/dist/core/governance.js +247 -0
  109. package/dist/core/guards.js +19 -0
  110. package/dist/core/ideation.js +72 -0
  111. package/dist/core/identity.js +252 -28
  112. package/dist/core/ids.js +6 -0
  113. package/dist/core/input-validation.js +2 -2
  114. package/dist/core/instruction-templates.js +344 -136
  115. package/dist/core/io.js +90 -11
  116. package/dist/core/lock.js +6 -2
  117. package/dist/core/loops/brief-assembly.js +213 -0
  118. package/dist/core/loops/facade-schema.js +148 -0
  119. package/dist/core/loops/index.js +7 -0
  120. package/dist/core/loops/iteration-engine.js +139 -0
  121. package/dist/core/loops/lock.js +385 -0
  122. package/dist/core/loops/store.js +201 -0
  123. package/dist/core/loops/types.js +403 -0
  124. package/dist/core/loops/verbs.js +534 -0
  125. package/dist/core/markdown.js +15 -3
  126. package/dist/core/memory-compactor.js +432 -0
  127. package/dist/core/memory-git.js +152 -8
  128. package/dist/core/messaging.js +278 -0
  129. package/dist/core/migration.js +32 -1
  130. package/dist/core/mutation-pipeline.js +4 -2
  131. package/dist/core/operations/memory-mutation.js +129 -0
  132. package/dist/core/operations/memory-write.js +78 -0
  133. package/dist/core/operations/plan.js +190 -0
  134. package/dist/core/policy.js +169 -0
  135. package/dist/core/repo-analysis.js +67 -0
  136. package/dist/core/reputation.js +9 -3
  137. package/dist/core/schema.js +546 -21
  138. package/dist/core/search.js +21 -2
  139. package/dist/core/security-cache.js +71 -0
  140. package/dist/core/security-guard.js +152 -0
  141. package/dist/core/security-scoring.js +86 -0
  142. package/dist/core/sequence.js +130 -0
  143. package/dist/core/socket-client.js +113 -0
  144. package/dist/core/staleness.js +246 -0
  145. package/dist/core/state.js +98 -22
  146. package/dist/core/store-resolution.js +54 -12
  147. package/dist/core/toml-writer.js +76 -0
  148. package/dist/core/upgrades/backup.js +232 -0
  149. package/dist/core/upgrades/health-check.js +169 -0
  150. package/dist/core/upgrades/patches/candidate-archive.js +145 -0
  151. package/dist/core/upgrades/patches/handoff-review-strip.js +128 -0
  152. package/dist/core/upgrades/patches/provenance-rollout.js +136 -0
  153. package/dist/core/upgrades/schema-version.js +97 -0
  154. package/dist/core/worktree.js +606 -0
  155. package/dist/facts.js +114 -0
  156. package/dist/facts.json +111 -0
  157. package/docs/architecture/project-refs.md +5 -1
  158. package/docs/cli.md +690 -43
  159. package/docs/concepts/ideation-loop.md +317 -0
  160. package/docs/concepts/loop-engine.md +456 -0
  161. package/docs/concepts/mcp-governance.md +268 -0
  162. package/docs/concepts/memory-staleness.md +122 -0
  163. package/docs/concepts/multi-agent-workflows.md +166 -0
  164. package/docs/concepts/plans-and-claims.md +31 -6
  165. package/docs/concepts/project-md-convention.md +35 -0
  166. package/docs/concepts/troubleshooting.md +220 -0
  167. package/docs/concepts/upgrade-cli.md +202 -0
  168. package/docs/concepts/upgrade-dogfood-procedure.md +114 -0
  169. package/docs/context-format-changelog.md +2 -2
  170. package/docs/context-format.md +2 -2
  171. package/docs/index.md +68 -0
  172. package/docs/integrations/agents.md +15 -16
  173. package/docs/integrations/cline.md +88 -0
  174. package/docs/integrations/codex.md +75 -23
  175. package/docs/integrations/continue.md +60 -0
  176. package/docs/integrations/copilot.md +67 -9
  177. package/docs/integrations/kilocode.md +72 -0
  178. package/docs/integrations/mcp.md +304 -21
  179. package/docs/integrations/mistral-vibe.md +122 -0
  180. package/docs/integrations/opencode.md +84 -0
  181. package/docs/integrations/overview.md +23 -8
  182. package/docs/integrations/roo.md +74 -0
  183. package/docs/integrations/windsurf.md +83 -0
  184. package/docs/mcp-schema-changelog.md +191 -1
  185. package/docs/playbooks/integration/index.md +121 -0
  186. package/docs/playbooks/productivity/index.md +102 -0
  187. package/docs/playbooks/team/index.md +122 -0
  188. package/docs/product/agent-first-model.md +184 -0
  189. package/docs/product/entity-model-audit.md +462 -0
  190. package/docs/quickstart-existing-project.md +135 -0
  191. package/docs/quickstart.md +124 -37
  192. package/docs/release-maintenance.md +79 -0
  193. package/docs/review.md +2 -0
  194. package/docs/server-operations.md +118 -0
  195. package/package.json +20 -12
  196. package/dist/commands/claude-desktop-extension.js +0 -18
  197. package/dist/commands/diff.js +0 -99
  198. package/dist/core/claude-desktop-extension.js +0 -224
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { Command } from 'commander';
4
4
  import { runInit } from './commands/init.js';
5
5
  import { runSetup } from './commands/setup.js';
6
6
  import { runUpgrade } from './commands/upgrade.js';
7
+ import { patchAllMcpConfigs } from './core/agent-files.js';
7
8
  import { runReconcile } from './commands/reconcile.js';
8
9
  import { getMemoryLog, rollbackMemory, hasMemoryRepo } from './core/memory-git.js';
9
10
  import { buildMachineProfile, saveMachineProfile, loadMachineProfile, renderMachineProfileSummary } from './core/machine-profile.js';
@@ -18,7 +19,10 @@ import { runListPlans } from './commands/list-plans.js';
18
19
  import { runUpdatePlan } from './commands/update-plan.js';
19
20
  import { runDeletePlan } from './commands/delete-plan.js';
20
21
  import { runPlanResource } from './commands/plan-resource.js';
22
+ import { runSequenceResource } from './commands/sequence.js';
21
23
  import { runAddStep } from './commands/add-step.js';
24
+ import { runDeleteStep } from './commands/delete-step.js';
25
+ import { runUpdateStep } from './commands/update-step.js';
22
26
  import { runEstimationReport } from './commands/estimation-report.js';
23
27
  import { runCompleteStep } from './commands/complete-step.js';
24
28
  import { runUpdateHandoff } from './commands/update-handoff.js';
@@ -27,6 +31,8 @@ import { runListAgents } from './commands/list-agents.js';
27
31
  import { runSurfaceTaskResource } from './commands/surface-task-resource.js';
28
32
  import { runListInstructions } from './commands/list-instructions.js';
29
33
  import { runDoctor } from './commands/doctor.js';
34
+ import { runRepair } from './commands/repair.js';
35
+ import { runStale } from './commands/stale.js';
30
36
  import { runRebuild } from './commands/rebuild.js';
31
37
  import { runReflect } from './commands/reflect.js';
32
38
  import { runReflectRuntimeNote } from './commands/reflect-runtime-note.js';
@@ -37,6 +43,7 @@ import { runUseCandidate } from './commands/use-candidate.js';
37
43
  import { runAccept } from './commands/accept.js';
38
44
  import { runReject } from './commands/reject.js';
39
45
  import { runPruneCandidates } from './commands/prune-candidates.js';
46
+ import { cleanupStaleCandidates } from './core/candidates.js';
40
47
  import { runListClaims } from './commands/list-claims.js';
41
48
  import { runReleaseClaim } from './commands/release-claim.js';
42
49
  import { runClaimResource } from './commands/claim-resource.js';
@@ -52,9 +59,13 @@ import { runEnv } from './commands/env.js';
52
59
  import { runAdapterOpenclawImport } from './commands/adapter-openclaw-import.js';
53
60
  import { runInstallHooks } from './commands/install-hooks.js';
54
61
  import { runCheckConstraints } from './commands/check-constraints.js';
62
+ import { runCheckPolicy } from './commands/check-policy.js';
63
+ import { runCheckSecurity } from './commands/check-security.js';
64
+ import { runSetupSecurity } from './commands/setup-security.js';
55
65
  import { runRegisterAgent } from './commands/register-agent.js';
56
66
  import { runEnableAgent } from './commands/enable-agent.js';
57
67
  import { runVersion } from './commands/version.js';
68
+ import { runReleaseNotes } from './commands/release-notes.js';
58
69
  import { runDiff } from './commands/changes.js';
59
70
  import { runPrune } from './commands/prune.js';
60
71
  import { runMcp } from './commands/mcp.js';
@@ -62,10 +73,13 @@ import { runSetTrust } from './commands/set-trust.js';
62
73
  import { runSessionStart } from './commands/session-start.js';
63
74
  import { runSessionEnd } from './commands/session-end.js';
64
75
  import { runWhoami } from './commands/whoami.js';
76
+ import { runUsage } from './commands/usage.js';
65
77
  import { runSearch } from './commands/search.js';
66
- import { runExport } from './commands/export.js';
78
+ import { runExport, runRefresh } from './commands/export.js';
67
79
  import { runHooks } from './commands/hooks.js';
68
80
  import { runWatch } from './commands/watch.js';
81
+ import { runDispatchAnalysis, runDispatch, runDispatchReview } from './commands/dispatch.js';
82
+ import { runInboxList, runInboxAck, runInboxArchive, runInboxSend, runInboxThread } from './commands/inbox.js';
69
83
  import { runMetrics } from './commands/metrics.js';
70
84
  import { runRollback } from './commands/rollback.js';
71
85
  import { runPull } from './commands/pull.js';
@@ -74,20 +88,30 @@ import { runAuditCommand } from './commands/audit.js';
74
88
  import { runHistory } from './commands/history.js';
75
89
  import { runContextDiff } from './commands/context-diff.js';
76
90
  import { runCapability } from './commands/capability.js';
91
+ import { runLink } from './commands/link.js';
77
92
  import { runTool } from './commands/tool.js';
78
93
  import { runExplore } from './commands/explore.js';
79
94
  import { getInstalledBrainclawVersion } from './core/brainclaw-version.js';
80
- import { cleanOrphanFiles, memoryDir } from './core/io.js';
95
+ import { cleanOrphanFiles, memoryDir, memoryExists } from './core/io.js';
81
96
  import { initLogLevel, logger } from './core/logger.js';
82
97
  import { resolveEffectiveCwd } from './core/store-resolution.js';
98
+ import { resolveProjectCwd } from './core/cross-project.js';
83
99
  import { runSwitch } from './commands/switch.js';
100
+ import { runWorktreeCreate, runWorktreeList, runWorktreeRemove, runWorktreePrune, runWorktreeClean, runWorktreeMerge } from './commands/worktree.js';
84
101
  import { runCheckEvents } from './commands/check-events.js';
85
102
  import { runDiscover } from './commands/discover.js';
86
103
  import { runMigrate } from './commands/migrate.js';
104
+ import { runRunProfile } from './commands/run-profile.js';
105
+ import { runCompact } from './commands/compact.js';
106
+ import { runHarvestCandidates } from './commands/harvest.js';
107
+ import { requireRegisteredAgentIdentity } from './core/agent-registry.js';
87
108
  const program = new Command();
88
109
  function collect(value, previous) {
89
110
  return [...previous, value];
90
111
  }
112
+ function isCodevEnabled() {
113
+ return process.env.BRAINCLAW_ENABLE_CODEV === '1';
114
+ }
91
115
  program
92
116
  .name('brainclaw')
93
117
  .description('Shared project memory for humans and coding agents.')
@@ -95,15 +119,33 @@ program
95
119
  .option('--verbose', 'Show info-level log messages on stderr')
96
120
  .option('--debug', 'Show debug-level log messages on stderr')
97
121
  .option('--cwd <path>', 'Override working directory for this invocation')
122
+ .option('--project <name>', 'Run the command against a linked project (cross_project_links or workspace store-chain child). Resolves via resolveProjectCwd; mutually exclusive with --cwd.')
98
123
  .hook('preAction', (_thisCommand, actionCommand) => {
99
124
  const root = actionCommand.optsWithGlobals();
100
125
  initLogLevel({ verbose: root.verbose, debug: root.debug });
101
126
  // Skip effective cwd resolution for commands that create the store
102
127
  const cmdName = actionCommand.name();
103
128
  const skipResolution = cmdName === 'init' || cmdName === 'setup';
129
+ // pln#359 phase 1c — `--project <name>` resolves a linked project to an
130
+ // absolute path via resolveProjectCwd, then feeds the same chdir flow
131
+ // as --cwd. Mutually exclusive with --cwd to avoid ambiguity.
132
+ let explicitCwd = root.cwd;
133
+ if (root.project) {
134
+ if (root.cwd) {
135
+ console.error('Error: --project and --cwd are mutually exclusive. Use one.');
136
+ process.exit(1);
137
+ }
138
+ try {
139
+ explicitCwd = resolveProjectCwd(root.project, process.cwd());
140
+ }
141
+ catch (err) {
142
+ console.error(`Error: ${err.message}`);
143
+ process.exit(1);
144
+ }
145
+ }
104
146
  if (!skipResolution) {
105
- // Resolve effective cwd (--cwd > BRAINCLAW_PROJECT > active-project > process.cwd)
106
- const effectiveCwd = resolveEffectiveCwd({ explicitCwd: root.cwd });
147
+ // Resolve effective cwd (explicit > BRAINCLAW_PROJECT > active-project > process.cwd)
148
+ const effectiveCwd = resolveEffectiveCwd({ explicitCwd });
107
149
  if (effectiveCwd !== process.cwd()) {
108
150
  // Change process.cwd() so all commands resolve the correct store
109
151
  // without needing individual --cwd plumbing
@@ -115,9 +157,9 @@ program
115
157
  logger.info(`Cleaned ${removed} orphan lock/tmp file(s) in ${memoryDir()}`);
116
158
  }
117
159
  }
118
- else if (root.cwd) {
119
- // For init/setup, still respect explicit --cwd but nothing else
120
- process.chdir(path.resolve(root.cwd));
160
+ else if (explicitCwd) {
161
+ // For init/setup, still respect explicit --cwd / --project but nothing else
162
+ process.chdir(path.resolve(explicitCwd));
121
163
  }
122
164
  });
123
165
  // --- init ---
@@ -131,6 +173,7 @@ program
131
173
  .option('--project-mode <mode>', 'Project mode: single-project, multi-project, auto')
132
174
  .option('--project-strategy <strategy>', 'Project strategy for multi-project mode: manual, folder')
133
175
  .option('--no-analyze-repo', 'Skip repository analysis when suggesting a project mode')
176
+ .option('--no-ai-scan', 'Skip AI surface scan during init')
134
177
  .option('--scan', 'Scan subdirectories for service boundaries and suggest init targets')
135
178
  .action(async (options) => {
136
179
  await runInit(options);
@@ -165,15 +208,34 @@ program
165
208
  // --- memory-rollback ---
166
209
  program
167
210
  .command('memory-rollback <ref>')
168
- .description('Rollback entire memory to a previous git snapshot (use memory-log to find refs)')
169
- .action((ref) => {
211
+ .description('Restore live project memory from a previous git snapshot without deleting audit or archive artifacts')
212
+ .option('--actor <name>', 'Registered human identity required to authorize the rollback')
213
+ .action((ref, options) => {
214
+ const cwd = process.cwd();
170
215
  if (!hasMemoryRepo()) {
171
216
  console.error('Error: no memory git repo. Run `brainclaw init --force` to enable.');
172
217
  process.exit(1);
173
218
  }
174
- const success = rollbackMemory(ref);
219
+ let actor;
220
+ try {
221
+ actor = requireRegisteredAgentIdentity({
222
+ agentName: options.actor,
223
+ allowCurrent: true,
224
+ allowEnv: true,
225
+ cwd,
226
+ });
227
+ }
228
+ catch (err) {
229
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
230
+ process.exit(1);
231
+ }
232
+ if (actor.kind !== 'human') {
233
+ console.error(`Error: memory-rollback is reserved to registered human identities. Resolved actor '${actor.agent_name}' is kind='${actor.kind}'.`);
234
+ process.exit(1);
235
+ }
236
+ const success = rollbackMemory(ref, cwd);
175
237
  if (success) {
176
- console.log(`✔ Memory rolled back to ${ref}`);
238
+ console.log(`✔ Live project memory restored to ${ref} (audit, archives, backups preserved)`);
177
239
  }
178
240
  else {
179
241
  console.error(`Error: failed to rollback to '${ref}'. Check memory-log for valid refs.`);
@@ -186,12 +248,45 @@ program
186
248
  .description('Upgrade project memory structure and refresh managed workspace agent files without losing data')
187
249
  .option('--json', 'Output as JSON')
188
250
  .option('--dry-run', 'Show what would be done without making changes')
251
+ .option('--self-update', 'Check for a newer brainclaw package version and install it before upgrading memory')
252
+ .option('--to <version>', 'One-shot target schema version (e.g. --to=1.0). Real runs require a backup.')
253
+ .option('--backup', 'Create a timestamped backup of .brainclaw/ before any write (always on for --to runs)')
254
+ .option('--no-backup', 'Disable the automatic backup for housekeeping-only upgrade runs')
255
+ .option('--rollback', 'Restore the most recent backup, park the current live store, exit')
256
+ .option('--yes', 'Skip interactive confirmations (reserved for later prompt additions)')
189
257
  .action((options) => {
190
258
  runUpgrade({
191
259
  json: options.json,
192
260
  dryRun: options.dryRun,
261
+ selfUpdate: options.selfUpdate,
262
+ to: options.to,
263
+ backup: options.backup,
264
+ rollback: options.rollback,
265
+ yes: options.yes,
193
266
  });
194
267
  });
268
+ // --- patch-configs ---
269
+ program
270
+ .command('patch-configs')
271
+ .description('Patch all MCP config files to use the current brainclaw binary path')
272
+ .option('--json', 'Output as JSON')
273
+ .action((options) => {
274
+ const cwd = process.env.BRAINCLAW_CWD ?? process.cwd();
275
+ const results = patchAllMcpConfigs(cwd);
276
+ if (options.json) {
277
+ console.log(JSON.stringify(results, null, 2));
278
+ }
279
+ else if (results.length === 0) {
280
+ console.log('✔ All MCP configs are already up to date.');
281
+ }
282
+ else {
283
+ for (const r of results) {
284
+ const tag = r.created ? 'created' : 'updated';
285
+ console.log(`✔ ${r.filePath} (${tag}) — ${r.label}`);
286
+ }
287
+ console.log(`\n${results.length} MCP config(s) patched.`);
288
+ }
289
+ });
195
290
  // --- machine-profile ---
196
291
  program
197
292
  .command('machine-profile')
@@ -346,6 +441,11 @@ program
346
441
  .option('--plan <id>', 'Optional linked plan item ID')
347
442
  .option('--author <author>', 'Author name')
348
443
  .option('--capture-diff', 'Capture current git diff into the handoff snapshot')
444
+ .option('--files <files...>', 'Files touched in this handoff')
445
+ .option('--pre-condition <conditions...>', 'Pre-conditions for the receiving agent')
446
+ .option('--post-condition <conditions...>', 'Post-conditions the receiving agent must satisfy')
447
+ .option('--test <tests...>', 'Tests the receiving agent should verify')
448
+ .option('--linked-plan <plans...>', 'Linked plan IDs')
349
449
  .action((text, options) => {
350
450
  runHandoff(text, options);
351
451
  });
@@ -376,8 +476,10 @@ program
376
476
  .option('--estimate <minutes>', 'Estimated effort in minutes (positive integer, e.g. --estimate 30)')
377
477
  .option('--actual-effort <effort>', 'Actual effort spent (e.g. "20min", "1h30m")')
378
478
  .option('--store <target>', 'Target store level: local (default), repo, workspace, user')
479
+ .option('--recursive', 'Include plans from descendant brainclaw projects (for list)')
480
+ .option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
379
481
  .action((subcommand, args, options) => {
380
- runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort });
482
+ runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort, localOnly: options.localOnly });
381
483
  });
382
484
  // --- list-plans ---
383
485
  program
@@ -389,8 +491,24 @@ program
389
491
  .option('--assignee <assignee>', 'Filter by assignee')
390
492
  .option('--project <project>', 'Filter by project namespace')
391
493
  .option('--all', 'Include done and dropped plan items')
494
+ .option('--recursive', 'Include plans from descendant brainclaw projects')
495
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
392
496
  .action((options) => {
393
- runListPlans(options);
497
+ runListPlans({ ...options, localOnly: options.localOnly });
498
+ });
499
+ program
500
+ .command('sequence <subcommand> [args...]')
501
+ .description('Manage coordination sequences (create, list, show, update)')
502
+ .option('--json', 'Output as JSON')
503
+ .option('--description <text>', 'Optional sequence description')
504
+ .option('--status <status>', 'Sequence status: draft, active, archived')
505
+ .option('--owner <owner>', 'Optional sequence owner')
506
+ .option('--items <json>', 'Sequence items JSON array')
507
+ .option('--name <name>', 'Optional sequence name for update')
508
+ .option('--tag <tags...>', 'Tags for this sequence')
509
+ .option('--author <author>', 'Author name')
510
+ .action((subcommand, args, options) => {
511
+ runSequenceResource(subcommand, args, options);
394
512
  });
395
513
  // --- add-step ---
396
514
  program
@@ -407,6 +525,23 @@ program
407
525
  .action((planId, stepId) => {
408
526
  runCompleteStep(planId, stepId);
409
527
  });
528
+ // --- update-step ---
529
+ program
530
+ .command('update-step <planId> <stepId>')
531
+ .description('Update a plan step (status, text, assignee)')
532
+ .option('--status <status>', 'New status: todo, in_progress, testing, done, blocked')
533
+ .option('--text <text>', 'Replace step description')
534
+ .option('--assign <assignee>', 'Assign the step (empty string to unassign)')
535
+ .action((planId, stepId, options) => {
536
+ runUpdateStep(planId, stepId, options);
537
+ });
538
+ // --- delete-step ---
539
+ program
540
+ .command('delete-step <planId> <stepId>')
541
+ .description('Remove a step from a plan')
542
+ .action((planId, stepId) => {
543
+ runDeleteStep(planId, stepId);
544
+ });
410
545
  // --- estimation-report ---
411
546
  program
412
547
  .command('estimation-report')
@@ -460,11 +595,25 @@ program
460
595
  // --- update-handoff ---
461
596
  program
462
597
  .command('update-handoff <id>')
463
- .description('Update the status of a handoff')
598
+ .description('Update the status, recipient, or review state of a handoff')
464
599
  .option('--status <status>', 'Status: open, accepted, closed')
465
600
  .option('--to <agent>', 'Change the receiving agent')
601
+ .option('--narrative <text>', 'Update the narrative attached to the handoff')
602
+ .option('--reviewer <agent>', 'Set or override the assigned reviewer')
603
+ .option('--review-verdict <verdict>', 'Set review verdict: approve or request_changes')
604
+ .option('--reviewed-by <agent>', 'Set the reviewer identity that produced the verdict')
605
+ .option('--review-summary <text>', 'Attach a short review summary')
606
+ .option('--blocking-issue <text>', 'Add a blocking review issue (repeatable)', collect, [])
607
+ .option('--suggestion <text>', 'Add a non-blocking review suggestion (repeatable)', collect, [])
466
608
  .action((id, options) => {
467
- runUpdateHandoff(id, options);
609
+ runUpdateHandoff(id, {
610
+ ...options,
611
+ review_verdict: options.reviewVerdict,
612
+ reviewed_by: options.reviewedBy,
613
+ review_summary: options.reviewSummary,
614
+ blocking_issues: options.blockingIssue,
615
+ suggestions: options.suggestion,
616
+ });
468
617
  });
469
618
  // --- doctor ---
470
619
  program
@@ -473,8 +622,34 @@ program
473
622
  .option('--json', 'Output as JSON dashboard')
474
623
  .option('--migration-check', 'Report versioned documents that need schema migration')
475
624
  .option('--fix-agent-ignore', 'Add missing .gitignore entries for generated local Brainclaw agent files')
625
+ .option('--fix', 'Fix auto-resolvable issues (e.g. drifting MCP configs)')
626
+ .option('--repair', 'Rebuild dist/ when the MCP runtime is missing or stale')
627
+ .option('--after-migration', 'Run the v1.0 post-migration health check only (exits non-zero on any failure)')
628
+ .option('--dispatch', 'Run dispatch-health diagnostic only: reconcile open agent_runs and report stuck/unverified/silent failures (pln#496 step stp_8c072d75)')
629
+ .action((options) => {
630
+ runDoctor({ ...options, afterMigration: options.afterMigration, dispatch: options.dispatch });
631
+ });
632
+ // --- repair (Phase 4 Sprint 2 Lane C / pln#397) ---
633
+ program
634
+ .command('repair')
635
+ .description('Apply safe, non-destructive fixes for the repair candidates surfaced by doctor')
636
+ .option('--dry-run', 'Print the plan without executing anything')
637
+ .option('--include-unsafe', 'Also apply candidates flagged unsafe (preserves data but requires confirmation)')
638
+ .option('--json', 'Output as JSON')
476
639
  .action((options) => {
477
- runDoctor(options);
640
+ runRepair({
641
+ dryRun: options.dryRun,
642
+ includeUnsafe: options.includeUnsafe,
643
+ json: options.json,
644
+ });
645
+ });
646
+ // --- stale (Phase 4 Sprint 1 Lane A / pln#390) ---
647
+ program
648
+ .command('stale [subcommand] [id]')
649
+ .description('List or resolve stale memory items (plans, traps, handoffs, candidates, runtime notes). Subcommands: list (default), resolve <id>.')
650
+ .option('--json', 'Output as JSON')
651
+ .action((subcommand, id, options) => {
652
+ runStale(subcommand, id, { json: options.json });
478
653
  });
479
654
  // --- version ---
480
655
  program
@@ -482,11 +657,23 @@ program
482
657
  .description('Show the installed brainclaw version and the project version policy')
483
658
  .option('--check', 'Check the configured installable update source')
484
659
  .option('--publish-local', 'Create/update the local installable .releases channel via npm pack')
485
- .option('--release-notes <text>', 'Attach release notes to the generated local-pack manifest')
660
+ .option('--release-notes <text>', 'Attach plain-text release notes to the generated local-pack manifest')
661
+ .option('--agent-release-notes <json>', 'Attach structured agent-first release notes (JSON) to the generated local-pack manifest')
662
+ .option('--auto-release-notes', 'Auto-generate agent-first release notes from git log (use with --publish-local)')
486
663
  .option('--json', 'Output as JSON')
487
664
  .action((options) => {
488
665
  runVersion(options);
489
666
  });
667
+ // --- release-notes ---
668
+ program
669
+ .command('release-notes')
670
+ .description('Show or generate agent-first release notes')
671
+ .option('--generate', 'Generate release notes from git log instead of showing configured ones')
672
+ .option('--since <ref>', 'Git ref to generate from (default: last version tag)')
673
+ .option('--json', 'Output as JSON')
674
+ .action((options) => {
675
+ runReleaseNotes(options);
676
+ });
490
677
  // --- uninstall ---
491
678
  import { runUninstall } from './commands/uninstall.js';
492
679
  program
@@ -662,6 +849,7 @@ program
662
849
  .command('enable-agent <name>')
663
850
  .description('Activate a supported coding agent on an already initialized project')
664
851
  .option('--kind <kind>', 'Identity kind: agent, human, unknown', 'agent')
852
+ .option('--context-profile <profile>', 'Default context profile: dev, dense, compact, copilot, quick, openclaw, ops, research')
665
853
  .option('--capability <value>', 'Declare a capability on the agent profile (repeatable)', collect, [])
666
854
  .option('--replace-capabilities', 'Replace existing capabilities instead of merging')
667
855
  .option('--generate-fingerprint', 'Generate or rotate a local public identity fingerprint for this agent')
@@ -749,6 +937,17 @@ program
749
937
  .action((id, options) => {
750
938
  runReject(id, options.reason, options.by);
751
939
  });
940
+ // --- harvest-candidates ---
941
+ program
942
+ .command('harvest-candidates')
943
+ .description('Harvest candidates from worktree inboxes into the main project store (codex sandbox bridge)')
944
+ .option('--dry-run', 'Preview what would be imported without writing anything')
945
+ .option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
946
+ .option('--json', 'Output as JSON')
947
+ .action((options) => {
948
+ const globalOpts = program.opts();
949
+ runHarvestCandidates({ ...options, cwd: globalOpts.cwd });
950
+ });
752
951
  // --- prune-candidates ---
753
952
  program
754
953
  .command('prune-candidates')
@@ -758,6 +957,35 @@ program
758
957
  .action((options) => {
759
958
  runPruneCandidates(options);
760
959
  });
960
+ // --- cleanup-candidates ---
961
+ program
962
+ .command('cleanup-candidates')
963
+ .description('Remove stale auto-generated pending candidates')
964
+ .option('--max-age <days>', 'Max age in days before cleanup', parseInt)
965
+ .option('--dry-run', 'Preview without deleting')
966
+ .action((options) => {
967
+ if (!memoryExists()) {
968
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
969
+ process.exit(1);
970
+ }
971
+ const maxAgeDays = options.maxAge ?? 30;
972
+ const result = cleanupStaleCandidates({
973
+ maxAgeDays,
974
+ dryRun: options.dryRun,
975
+ });
976
+ if (result.matched === 0) {
977
+ console.log(`No stale auto-generated candidates older than ${maxAgeDays} days found.`);
978
+ return;
979
+ }
980
+ if (options.dryRun) {
981
+ console.log(`Would remove ${result.matched} stale auto-generated candidate(s):`);
982
+ for (const candidate of result.candidates) {
983
+ console.log(` [${candidate.id}] ${candidate.text}`);
984
+ }
985
+ return;
986
+ }
987
+ console.log(`✔ Removed ${result.deleted} stale auto-generated candidate(s) older than ${maxAgeDays} days.`);
988
+ });
761
989
  // --- claim ---
762
990
  program
763
991
  .command('claim <subcommand> [args...]')
@@ -771,8 +999,9 @@ program
771
999
  .option('--json', 'Output as JSON for list')
772
1000
  .option('--plan-status <status>', 'Optional linked plan status when releasing: todo, in_progress, blocked, done, dropped')
773
1001
  .option('--store <target>', 'Target store level: local (default), repo, workspace')
1002
+ .option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
774
1003
  .action((subcommand, args, options) => {
775
- runClaimResource(subcommand, args, { ...options, planStatus: options.planStatus });
1004
+ runClaimResource(subcommand, args, { ...options, planStatus: options.planStatus, localOnly: options.localOnly });
776
1005
  });
777
1006
  // --- list-claims ---
778
1007
  program
@@ -783,8 +1012,9 @@ program
783
1012
  .option('--project <project>', 'Filter by project namespace')
784
1013
  .option('--plan <id>', 'Filter by linked plan item')
785
1014
  .option('--agent <agent>', 'Filter by agent name')
1015
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
786
1016
  .action((options) => {
787
- runListClaims(options);
1017
+ runListClaims({ ...options, localOnly: options.localOnly });
788
1018
  });
789
1019
  // --- release-claim ---
790
1020
  program
@@ -818,6 +1048,7 @@ program
818
1048
  .option('--capabilities', 'List all registered agents with their declared capabilities')
819
1049
  .option('--suggest <query>', 'Suggest agents whose capabilities match a query string')
820
1050
  .option('--include-session-meta', 'Include session_start/session_end runtime notes (hidden by default)')
1051
+ .option('--all-agents', 'Show unfiltered board (supervisor mode — all claims, all agents)')
821
1052
  .action((options) => {
822
1053
  runAgentBoard(options);
823
1054
  });
@@ -836,6 +1067,24 @@ program
836
1067
  .action((text, options) => {
837
1068
  runRuntimeNote(text, { ...options, autoReflect: options.autoReflect });
838
1069
  });
1070
+ // --- note create ---
1071
+ const noteCommand = program
1072
+ .command('note')
1073
+ .description('Manage runtime notes');
1074
+ noteCommand
1075
+ .command('create <text>')
1076
+ .description('Alias for runtime-note')
1077
+ .option('--agent <agent>', 'Agent name; defaults to the configured current agent')
1078
+ .option('--project <project>', 'Optional project namespace')
1079
+ .option('--plan <id>', 'Optional linked plan item ID')
1080
+ .option('--visibility <visibility>', 'Visibility: shared, machine, private', 'shared')
1081
+ .option('--host <host>', 'Optional host identifier override for machine/private runtime notes')
1082
+ .option('--tag <tags...>', 'Tags')
1083
+ .option('--ttl <duration>', 'Time-to-live: 30m, 2h, 7d (note auto-expires after this duration)')
1084
+ .option('--auto-reflect', 'Attempt to turn this runtime note into durable memory immediately')
1085
+ .action((text, options) => {
1086
+ runRuntimeNote(text, { ...options, autoReflect: options.autoReflect });
1087
+ });
839
1088
  // --- runtime-status ---
840
1089
  program
841
1090
  .command('runtime-status')
@@ -872,6 +1121,46 @@ program
872
1121
  .action((options) => {
873
1122
  runCheckConstraints({ staged: options.staged, files: options.files, json: options.json });
874
1123
  });
1124
+ // --- check-policy ---
1125
+ program
1126
+ .command('check-policy')
1127
+ .description('Pre-execution policy check: verify claims, constraints, traps and instructions for a scope')
1128
+ .requiredOption('--scope <path>', 'File or directory scope to check')
1129
+ .option('--agent <name>', 'Agent name to check claims for')
1130
+ .option('--agent-id <id>', 'Agent id to check claims for')
1131
+ .option('--action <action>', 'Intended action: edit, create, delete')
1132
+ .option('--json', 'Output as JSON')
1133
+ .action((options) => {
1134
+ runCheckPolicy({
1135
+ scope: options.scope,
1136
+ agent: options.agent,
1137
+ agentId: options.agentId,
1138
+ action: options.action,
1139
+ json: options.json,
1140
+ });
1141
+ });
1142
+ // --- check-security ---
1143
+ program
1144
+ .command('check-security')
1145
+ .description('Check supply chain security scores for packages via Socket.dev')
1146
+ .requiredOption('--packages <names>', 'Comma-separated package names (e.g. "axios,express" or "axios@1.14.1")')
1147
+ .option('--ecosystem <type>', 'Package ecosystem: npm or pypi', 'npm')
1148
+ .option('--json', 'Output as JSON')
1149
+ .action(async (options) => {
1150
+ await runCheckSecurity({
1151
+ packages: options.packages,
1152
+ ecosystem: options.ecosystem,
1153
+ json: options.json,
1154
+ });
1155
+ });
1156
+ // --- setup-security ---
1157
+ program
1158
+ .command('setup-security')
1159
+ .description('Enable supply chain security gate: generate wrapper scripts and configure preinstall checks')
1160
+ .option('--mode <mode>', 'Security mode: advisory (default) or enforced', 'advisory')
1161
+ .action((options) => {
1162
+ runSetupSecurity({ mode: options.mode });
1163
+ });
875
1164
  // --- install-hooks ---
876
1165
  program
877
1166
  .command('install-hooks')
@@ -894,9 +1183,26 @@ program
894
1183
  .command('prune')
895
1184
  .description('Prune expired constraints and stale memory')
896
1185
  .option('--expired', 'Also prune expired runtime notes and traps')
1186
+ .option('--archive', 'Archive done plans and closed handoffs (>30 days) to cold storage JSONL')
1187
+ .option('--semantic', 'Detect near-duplicate clusters and stale items via semantic analysis')
1188
+ .option('--dry-run', 'Preview compaction without applying (use with --semantic)')
897
1189
  .action((options) => {
898
1190
  runPrune(options);
899
1191
  });
1192
+ // --- compact ---
1193
+ program
1194
+ .command('compact')
1195
+ .description('LLM-driven semantic memory compaction — archive old items and get a summary template')
1196
+ .option('--assess', 'Show pressure assessment and compaction template without archiving')
1197
+ .option('--dry-run', 'Preview eligible items without archiving')
1198
+ .option('--max-items <n>', 'Maximum items to compact (default: 20)', parseInt)
1199
+ .option('--min-age <days>', 'Minimum age in days for eligibility (default: 7)', parseInt)
1200
+ .option('--no-dedup-handoffs', 'Skip deduplication of auto-generated session-end handoffs')
1201
+ .option('--no-purge-claims', 'Skip archival of released claims')
1202
+ .option('--no-purge-session-notes', 'Skip archival of session-lifecycle runtime_notes')
1203
+ .action((options) => {
1204
+ runCompact(options);
1205
+ });
900
1206
  // --- mcp ---
901
1207
  program
902
1208
  .command('mcp')
@@ -921,6 +1227,8 @@ program
921
1227
  .option('--agent <agent>', 'Agent name (defaults to current configured agent)')
922
1228
  .option('--context <path>', 'Context target path for initial hash capture')
923
1229
  .option('--model <id>', 'Model identifier (e.g. claude-sonnet-4-6)')
1230
+ .option('--maintenance-mode <mode>', 'Maintenance mode: full (default) or fast')
1231
+ .option('--include-context', 'Output full project context after starting session (replaces separate context call)')
924
1232
  .option('--json', 'Output as JSON')
925
1233
  .action((options) => {
926
1234
  runSessionStart(options);
@@ -934,10 +1242,21 @@ program
934
1242
  .option('--summary <text>', 'Session summary text')
935
1243
  .option('--auto-reflect', 'Auto-reflect session notes as pending candidates')
936
1244
  .option('--auto-release', 'Auto-release any active claims at session end')
937
- .option('--reflect-handoff', 'Generate a handoff candidate from git commits since session start')
1245
+ .option('--reflect-handoff', 'Materialize an open handoff from git commits since session start')
1246
+ .option('--dispatch-review', 'When used with --reflect-handoff, auto-dispatch a code review if the handoff is reviewable')
1247
+ .option('--reviewer <name>', 'Explicit reviewer to route the reflected handoff review to')
1248
+ .option('--reflect', 'Include structured reflection questions for the agent to answer')
938
1249
  .option('--json', 'Output as JSON')
939
1250
  .action((options) => {
940
- runSessionEnd({ ...options, autoReflect: options.autoReflect, autoRelease: options.autoRelease, reflectHandoff: options.reflectHandoff });
1251
+ runSessionEnd({
1252
+ ...options,
1253
+ autoReflect: options.autoReflect,
1254
+ autoRelease: options.autoRelease,
1255
+ reflectHandoff: options.reflectHandoff,
1256
+ dispatchReview: options.dispatchReview,
1257
+ reviewer: options.reviewer,
1258
+ reflect: options.reflect,
1259
+ });
941
1260
  });
942
1261
  // --- whoami ---
943
1262
  program
@@ -947,6 +1266,17 @@ program
947
1266
  .action((options) => {
948
1267
  runWhoami(options);
949
1268
  });
1269
+ // --- usage ---
1270
+ program
1271
+ .command('usage')
1272
+ .description('Show brainclaw context volume stats (tokens injected per agent/tool)')
1273
+ .option('--agent <name>', 'Filter by agent name')
1274
+ .option('--tool <name>', 'Filter by tool name')
1275
+ .option('--days <n>', 'Limit to last N days', parseInt)
1276
+ .option('--json', 'Output as JSON')
1277
+ .action((options) => {
1278
+ runUsage(options);
1279
+ });
950
1280
  // --- search ---
951
1281
  program
952
1282
  .command('search <query>')
@@ -968,6 +1298,7 @@ program
968
1298
  .option('--detect', 'Auto-detect agent environment and write to its native file')
969
1299
  .option('--all', 'Write all known agent instruction files at once (claude-md, agents-md, copilot-instructions, cursor-rules, etc.)')
970
1300
  .option('--write', 'Write to canonical file path instead of stdout (when --format is given); local files are gitignored by default')
1301
+ .option('--include-live', 'Also write the native live companion file when the target agent supports one')
971
1302
  .option('--shared', 'Keep the main exported instruction file versionable instead of auto-ignoring it (companions remain local)')
972
1303
  .option('--output <file>', 'Write to a specific file path instead of stdout')
973
1304
  .option('--project <project>', 'Project namespace filter')
@@ -975,6 +1306,12 @@ program
975
1306
  .action((options) => {
976
1307
  runExport(options);
977
1308
  });
1309
+ program
1310
+ .command('refresh')
1311
+ .description('Refresh live companion files with current state (plans, claims, traps, sequences). Gitignored, safe to run frequently.')
1312
+ .action(() => {
1313
+ runRefresh();
1314
+ });
978
1315
  program
979
1316
  .command('reconcile')
980
1317
  .description('Refresh machine and workspace bootstrap state after updates or onboarding on complex installs')
@@ -1012,6 +1349,110 @@ program
1012
1349
  .action((options) => {
1013
1350
  runWatch({ ...options, autoClaim: options.autoClaim });
1014
1351
  });
1352
+ // --- dispatch ---
1353
+ const dispatchCmd = program
1354
+ .command('dispatch')
1355
+ .description('Local agent dispatcher — analyze lanes and assign work');
1356
+ dispatchCmd
1357
+ .command('analysis')
1358
+ .description('Analyze the active sequence: show ready, active, blocked, and done lanes')
1359
+ .option('--json', 'Output as JSON')
1360
+ .action((options) => {
1361
+ runDispatchAnalysis({ json: options.json });
1362
+ });
1363
+ dispatchCmd
1364
+ .command('run')
1365
+ .description('Run a dispatch cycle: assign ready lanes to available agents')
1366
+ .option('--agents <names>', 'Comma-separated list of agents to dispatch to')
1367
+ .option('--lanes <names>', 'Comma-separated list of lanes to dispatch')
1368
+ .option('--max <n>', 'Maximum assignments', parseInt)
1369
+ .option('--dry', 'Preview assignments without sending messages')
1370
+ .option('--spawn', 'Autonomously launch CLI agents with invoke templates')
1371
+ .option('--agent <name>', 'Dispatcher agent name')
1372
+ .option('--json', 'Output as JSON')
1373
+ .action((options) => {
1374
+ runDispatch({
1375
+ agents: options.agents,
1376
+ lanes: options.lanes,
1377
+ max: options.max,
1378
+ dry: options.dry,
1379
+ spawn: options.spawn,
1380
+ agent: options.agent,
1381
+ json: options.json,
1382
+ });
1383
+ });
1384
+ dispatchCmd
1385
+ .command('review')
1386
+ .description('Dispatch code reviews for completed handoffs')
1387
+ .option('--handoff <id>', 'Specific handoff ID to review')
1388
+ .option('--reviewer <name>', 'Specific reviewer agent')
1389
+ .option('--spawn', 'Launch the reviewer CLI agent')
1390
+ .option('--dry', 'Preview without sending')
1391
+ .option('--agent <name>', 'Dispatcher agent name')
1392
+ .option('--json', 'Output as JSON')
1393
+ .action((options) => {
1394
+ runDispatchReview({
1395
+ handoff: options.handoff,
1396
+ reviewer: options.reviewer,
1397
+ spawn: options.spawn,
1398
+ dry: options.dry,
1399
+ agent: options.agent,
1400
+ json: options.json,
1401
+ });
1402
+ });
1403
+ // --- inbox ---
1404
+ const inboxCmd = program
1405
+ .command('inbox')
1406
+ .description('Inter-agent messaging inbox');
1407
+ inboxCmd
1408
+ .command('list')
1409
+ .description('List inbox messages (default: pending only)')
1410
+ .option('--agent <name>', 'Agent name')
1411
+ .option('--status <status>', 'Filter by status: pending, read, acknowledged, archived')
1412
+ .option('--type <type>', 'Filter by type: assign, review, rfc, info, reply')
1413
+ .option('--thread <id>', 'Filter by thread ID')
1414
+ .option('--all', 'Show all messages, not just pending')
1415
+ .option('--json', 'Output as JSON')
1416
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
1417
+ .action((options) => {
1418
+ runInboxList({ ...options, localOnly: options.localOnly });
1419
+ });
1420
+ inboxCmd
1421
+ .command('ack <id>')
1422
+ .description('Acknowledge a message')
1423
+ .option('--agent <name>', 'Agent name')
1424
+ .option('--json', 'Output as JSON')
1425
+ .action((id, options) => {
1426
+ runInboxAck(id, options);
1427
+ });
1428
+ inboxCmd
1429
+ .command('archive <id>')
1430
+ .description('Archive a message')
1431
+ .option('--agent <name>', 'Agent name')
1432
+ .option('--json', 'Output as JSON')
1433
+ .action((id, options) => {
1434
+ runInboxArchive(id, options);
1435
+ });
1436
+ inboxCmd
1437
+ .command('send <to> <text>')
1438
+ .description('Send a message to another agent')
1439
+ .option('--type <type>', 'Message type: assign, review, rfc, info, reply (default: info)')
1440
+ .option('--ref <id>', 'Reference to a plan, sequence, or other entity')
1441
+ .option('--scope <path>', 'File scope')
1442
+ .option('--thread <id>', 'Thread ID for conversations')
1443
+ .option('--ack', 'Require acknowledgment')
1444
+ .option('--agent <name>', 'Sender agent name')
1445
+ .option('--json', 'Output as JSON')
1446
+ .action((to, text, options) => {
1447
+ runInboxSend(to, text, options);
1448
+ });
1449
+ inboxCmd
1450
+ .command('thread <id>')
1451
+ .description('Show all messages in a thread')
1452
+ .option('--json', 'Output as JSON')
1453
+ .action((id, options) => {
1454
+ runInboxThread(id, options);
1455
+ });
1015
1456
  // --- check-events ---
1016
1457
  program
1017
1458
  .command('check-events')
@@ -1063,14 +1504,16 @@ program
1063
1504
  // --- audit ---
1064
1505
  program
1065
1506
  .command('audit')
1066
- .description('View the append-only audit log of all memory mutations')
1507
+ .description('View the append-only audit log, or generate a governance posture report with --governance')
1067
1508
  .option('--since <date>', 'Show entries since this ISO date')
1068
1509
  .option('--actor <agent>', 'Filter by actor name or agent ID')
1069
1510
  .option('--action <action>', 'Filter by action type (create, accept, reject, etc.)')
1070
1511
  .option('--limit <n>', 'Show last N entries', parseInt)
1071
1512
  .option('--json', 'Output as JSON')
1513
+ .option('--governance', 'Generate a governance posture report (aggregated view of claims, constraints, traps, instructions)')
1514
+ .option('--scope <path>', 'Filter governance report by scope (used with --governance)')
1072
1515
  .action((options) => {
1073
- runAuditCommand({ since: options.since, actor: options.actor, action: options.action, limit: options.limit, json: options.json });
1516
+ runAuditCommand({ since: options.since, actor: options.actor, action: options.action, limit: options.limit, json: options.json, governance: options.governance, scope: options.scope });
1074
1517
  });
1075
1518
  // --- history ---
1076
1519
  program
@@ -1102,6 +1545,25 @@ program
1102
1545
  store: options.store,
1103
1546
  });
1104
1547
  });
1548
+ program
1549
+ .command('link <subcommand> [args...]')
1550
+ .description('Manage cross-project federation links (add, list, remove)')
1551
+ .option('--name <slug>', 'Override the auto-derived link name')
1552
+ .option('--role <role>', 'Link role: publisher (push signals out) or subscriber (default)')
1553
+ .option('--channels <list>', 'Comma-separated allow-list of channels: candidate,handoff,runtime_note', (val) => val.split(',').map((s) => s.trim()).filter(Boolean))
1554
+ .option('--force', 'Replace an existing link of the same name/path')
1555
+ .option('--json', 'Output as JSON')
1556
+ .option('--store <target>', 'Store level: local (default), repo, workspace, user')
1557
+ .action((subcommand, args, options) => {
1558
+ runLink(subcommand, args, {
1559
+ name: options.name,
1560
+ role: options.role,
1561
+ channels: options.channels,
1562
+ force: options.force,
1563
+ json: options.json,
1564
+ store: options.store,
1565
+ });
1566
+ });
1105
1567
  program
1106
1568
  .command('tool <subcommand> [args...]')
1107
1569
  .description('Manage project tools (list, add, describe, search)')
@@ -1157,6 +1619,204 @@ program
1157
1619
  cwd: globalOpts.cwd,
1158
1620
  });
1159
1621
  });
1622
+ program
1623
+ .command('who')
1624
+ .description('Show active agent sessions on this workspace')
1625
+ .option('--json', 'Output as JSON')
1626
+ .option('--all', 'Include stale sessions')
1627
+ .option('--gc', 'Remove stale sessions')
1628
+ .option('--local-only', 'Read claims from local store only (skip parent stores in chain)')
1629
+ .action(async (options) => {
1630
+ const globalOpts = program.opts();
1631
+ const { runWho } = await import('./commands/who.js');
1632
+ runWho({ json: options.json, all: options.all, gc: options.gc, cwd: globalOpts.cwd, localOnly: options.localOnly });
1633
+ });
1634
+ const worktreeCmd = program
1635
+ .command('worktree')
1636
+ .description('Manage git worktrees for parallel agent isolation');
1637
+ worktreeCmd
1638
+ .command('create <branch>')
1639
+ .description('Create a linked git worktree for a given branch')
1640
+ .option('--session-id <id>', 'Associate this worktree with a brainclaw session')
1641
+ .option('--agent <name>', 'Associate this worktree with an agent name')
1642
+ .action((branch, options) => {
1643
+ const globalOpts = program.opts();
1644
+ runWorktreeCreate({ branch, sessionId: options.sessionId, agent: options.agent, cwd: globalOpts.cwd });
1645
+ });
1646
+ worktreeCmd
1647
+ .command('list')
1648
+ .description('List all git worktrees for this project')
1649
+ .action(() => {
1650
+ const globalOpts = program.opts();
1651
+ runWorktreeList({ cwd: globalOpts.cwd });
1652
+ });
1653
+ worktreeCmd
1654
+ .command('remove <path>')
1655
+ .description('Remove a linked git worktree')
1656
+ .option('--force', 'Force removal even with uncommitted changes')
1657
+ .action((worktreePath, options) => {
1658
+ const globalOpts = program.opts();
1659
+ runWorktreeRemove({ path: worktreePath, force: options.force, cwd: globalOpts.cwd });
1660
+ });
1661
+ worktreeCmd
1662
+ .command('prune')
1663
+ .description('Prune stale worktree administrative files')
1664
+ .action(() => {
1665
+ const globalOpts = program.opts();
1666
+ runWorktreePrune({ cwd: globalOpts.cwd });
1667
+ });
1668
+ worktreeCmd
1669
+ .command('clean')
1670
+ .description('Remove worktrees whose branch is fully merged and orphan worktree directories')
1671
+ .option('--force', 'Force removal even with uncommitted changes')
1672
+ .option('--dry-run', 'Show what would be removed without actually removing')
1673
+ .action((options) => {
1674
+ const globalOpts = program.opts();
1675
+ runWorktreeClean({ force: options.force, dryRun: options.dryRun, cwd: globalOpts.cwd });
1676
+ });
1677
+ worktreeCmd
1678
+ .command('merge <branch>')
1679
+ .description('Merge a worktree branch with auto-restoration of parasitic deletions')
1680
+ .option('-m, --message <message>', 'Merge commit message')
1681
+ .option('--dry-run', 'Show what would be merged without committing')
1682
+ .action((branch, options) => {
1683
+ const globalOpts = program.opts();
1684
+ runWorktreeMerge({ branch, message: options.message, dryRun: options.dryRun, cwd: globalOpts.cwd });
1685
+ });
1686
+ // --- federation cloud ---
1687
+ const federationCmd = program
1688
+ .command('federation')
1689
+ .description('Cloud federation — sync signals with app.brainclaw.dev');
1690
+ federationCmd
1691
+ .command('push <message>')
1692
+ .description('Push a test signal to the cloud')
1693
+ .option('--type <type>', 'Signal type', 'runtime_note')
1694
+ .option('--to-project <project>', 'Target project name')
1695
+ .option('--to-agent <agent>', 'Target agent name')
1696
+ .action(async (message, options) => {
1697
+ const { pushSignalToCloud, isCloudConfigured } = await import('./core/federation-cloud.js');
1698
+ const { createFederationMessage } = await import('./core/federation-message.js');
1699
+ const { loadConfig } = await import('./core/config.js');
1700
+ const { resolveCurrentAgentName } = await import('./core/agent-registry.js');
1701
+ if (!isCloudConfigured()) {
1702
+ console.error('Error: cloud not configured. Set BRAINCLAW_CLOUD_API_KEY env var.');
1703
+ process.exit(1);
1704
+ }
1705
+ const config = loadConfig();
1706
+ const agent = resolveCurrentAgentName() ?? 'unknown';
1707
+ const msg = createFederationMessage({
1708
+ version: 1,
1709
+ from: { project_name: config.project_name, project_path: process.cwd(), agent_name: agent },
1710
+ to: { project_name: options.toProject ?? 'broadcast', project_path: '' },
1711
+ type: options.type,
1712
+ payload: { text: message },
1713
+ });
1714
+ const ok = await pushSignalToCloud(msg);
1715
+ if (ok) {
1716
+ console.log(`✔ Signal pushed to cloud: [${msg.id}] ${message}`);
1717
+ }
1718
+ else {
1719
+ console.error('Error: failed to push signal to cloud.');
1720
+ process.exit(1);
1721
+ }
1722
+ });
1723
+ federationCmd
1724
+ .command('pull')
1725
+ .description('Pull signals from the cloud inbox')
1726
+ .option('--agent <name>', 'Agent name to pull for')
1727
+ .option('--since <date>', 'Only pull signals after this ISO date')
1728
+ .option('--limit <n>', 'Max signals to pull', '20')
1729
+ .action(async (options) => {
1730
+ const { pullSignalsFromCloud, isCloudConfigured } = await import('./core/federation-cloud.js');
1731
+ const { resolveCurrentAgentName } = await import('./core/agent-registry.js');
1732
+ if (!isCloudConfigured()) {
1733
+ console.error('Error: cloud not configured. Set BRAINCLAW_CLOUD_API_KEY env var.');
1734
+ process.exit(1);
1735
+ }
1736
+ const agent = options.agent ?? resolveCurrentAgentName() ?? 'unknown';
1737
+ const signals = await pullSignalsFromCloud(agent, {
1738
+ since: options.since,
1739
+ limit: parseInt(options.limit, 10),
1740
+ });
1741
+ if (signals.length === 0) {
1742
+ console.log('No signals in cloud inbox.');
1743
+ return;
1744
+ }
1745
+ console.log(`${signals.length} signal(s) from cloud:\n`);
1746
+ for (const s of signals) {
1747
+ const payload = typeof s.payload === 'object' && s.payload !== null ? s.payload.text ?? JSON.stringify(s.payload) : String(s.payload);
1748
+ console.log(` [${s.id}] ${s.type} from ${s.from.project_name}/${s.from.agent_name}`);
1749
+ console.log(` ${String(payload).slice(0, 120)}`);
1750
+ console.log(` ${s.created_at}\n`);
1751
+ }
1752
+ });
1753
+ federationCmd
1754
+ .command('status')
1755
+ .description('Check cloud federation configuration')
1756
+ .action(async () => {
1757
+ const { isCloudConfigured } = await import('./core/federation-cloud.js');
1758
+ const url = process.env.BRAINCLAW_CLOUD_URL ?? 'https://app.brainclaw.dev';
1759
+ console.log(`Cloud URL: ${url}`);
1760
+ console.log(`API Key: ${process.env.BRAINCLAW_CLOUD_API_KEY ? '***configured***' : 'NOT SET'}`);
1761
+ console.log(`Configured: ${isCloudConfigured() ? 'yes' : 'no'}`);
1762
+ if (isCloudConfigured()) {
1763
+ try {
1764
+ const res = await fetch(`${url}/api/v1/health`);
1765
+ const data = await res.json();
1766
+ console.log(`Cloud status: ${data.status} (v${data.version})`);
1767
+ }
1768
+ catch (e) {
1769
+ console.error(`Cloud unreachable: ${e.message}`);
1770
+ }
1771
+ }
1772
+ });
1773
+ // --- codev (legacy experimental) ---
1774
+ if (isCodevEnabled()) {
1775
+ program
1776
+ .command('codev [topic]')
1777
+ .description('Experimental legacy ideation session using persona-based consultation')
1778
+ .option('--personas <tier>', 'Persona tier: tier1 (default), tier2, or list', 'tier1')
1779
+ .option('--checkpoint', 'Pause after clarification for human input')
1780
+ .option('--spawn', 'Spawn each consultant as an agent CLI instance')
1781
+ .option('--fresh', 'Clear cached responses before starting a new run')
1782
+ .option('--agents <list>', 'Comma-separated agent names for spawn (e.g. claude-code,codex,antigravity). Default: auto-detect')
1783
+ .option('--rounds <N>', 'Number of discussion rounds in spawn mode (default 3, min 2)', '3')
1784
+ .option('--target-duration <seconds>', 'Target duration per round indicated to agents (default 120)', '120')
1785
+ .option('--quorum <N>', 'Advance to next round after N agent responses (default: all)')
1786
+ .option('--model-map <map>', 'Per-persona model overrides, e.g. simplificateur:sonnet,stratege:opus')
1787
+ .option('--metrics', 'Display response timing metrics at end of session')
1788
+ .option('--json', 'Output as JSON')
1789
+ .action(async (topic, options) => {
1790
+ const globalOpts = program.opts();
1791
+ const { runCodev } = await import('./commands/codev.js');
1792
+ runCodev(topic, {
1793
+ ...options,
1794
+ rounds: parseInt(options.rounds, 10),
1795
+ targetDuration: parseInt(options.targetDuration, 10),
1796
+ quorum: options.quorum != null ? parseInt(options.quorum, 10) : undefined,
1797
+ cwd: globalOpts.cwd,
1798
+ });
1799
+ });
1800
+ program
1801
+ .command('codev-metrics <thread>')
1802
+ .description('Show per-agent avg/p95 response metrics for an experimental CoDev thread')
1803
+ .option('--json', 'Output as JSON')
1804
+ .action(async (thread, options) => {
1805
+ const globalOpts = program.opts();
1806
+ const { runCodevMetrics } = await import('./commands/codev.js');
1807
+ runCodevMetrics(thread, { ...options, cwd: globalOpts.cwd });
1808
+ });
1809
+ }
1810
+ // --- run (agent profiles) ---
1811
+ program
1812
+ .command('run [profile-name]')
1813
+ .description('Run an agent profile (list profiles if no name given)')
1814
+ .option('--dry', 'Print the resolved command without executing')
1815
+ .option('--agent <agent>', 'Override the invoke template with a known agent')
1816
+ .action((profileName, options) => {
1817
+ const globalOpts = program.opts();
1818
+ runRunProfile(profileName, { ...options, cwd: globalOpts.cwd });
1819
+ });
1160
1820
  program.parseAsync(process.argv).catch((err) => {
1161
1821
  console.error(err);
1162
1822
  process.exit(1);