brainclaw 0.29.2 → 1.5.4

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 (197) hide show
  1. package/LICENSE +21 -74
  2. package/README.md +199 -176
  3. package/dist/brainclaw-vscode.vsix +0 -0
  4. package/dist/cli.js +710 -25
  5. package/dist/commands/accept.js +3 -0
  6. package/dist/commands/add-step.js +11 -26
  7. package/dist/commands/agent-board.js +70 -3
  8. package/dist/commands/audit.js +19 -0
  9. package/dist/commands/check-policy.js +54 -0
  10. package/dist/commands/check-security-mcp.js +145 -0
  11. package/dist/commands/check-security.js +106 -0
  12. package/dist/commands/claim-resource.js +1 -0
  13. package/dist/commands/codev.js +672 -0
  14. package/dist/commands/compact.js +74 -0
  15. package/dist/commands/complete-step.js +16 -26
  16. package/dist/commands/constraint.js +8 -20
  17. package/dist/commands/decision.js +9 -20
  18. package/dist/commands/delete-plan.js +10 -12
  19. package/dist/commands/delete-step.js +16 -0
  20. package/dist/commands/dispatch.js +163 -0
  21. package/dist/commands/doctor.js +1122 -49
  22. package/dist/commands/enable-agent.js +1 -0
  23. package/dist/commands/export.js +280 -22
  24. package/dist/commands/handoff.js +33 -0
  25. package/dist/commands/harvest.js +189 -0
  26. package/dist/commands/hooks.js +82 -25
  27. package/dist/commands/inbox.js +169 -0
  28. package/dist/commands/init.js +38 -31
  29. package/dist/commands/install-hooks.js +71 -44
  30. package/dist/commands/link.js +89 -0
  31. package/dist/commands/list-claims.js +48 -3
  32. package/dist/commands/list-plans.js +129 -25
  33. package/dist/commands/loops-handlers.js +409 -0
  34. package/dist/commands/mcp-read-handlers.js +1628 -0
  35. package/dist/commands/mcp-schemas.generated.js +269 -0
  36. package/dist/commands/mcp.js +4224 -1501
  37. package/dist/commands/plan-resource.js +64 -0
  38. package/dist/commands/plan.js +12 -26
  39. package/dist/commands/prune.js +37 -2
  40. package/dist/commands/reflect.js +20 -7
  41. package/dist/commands/release-claim.js +11 -6
  42. package/dist/commands/release-notes.js +170 -0
  43. package/dist/commands/repair.js +210 -0
  44. package/dist/commands/run-profile.js +57 -0
  45. package/dist/commands/sequence.js +113 -0
  46. package/dist/commands/session-end.js +423 -14
  47. package/dist/commands/session-start.js +214 -41
  48. package/dist/commands/setup-security.js +103 -0
  49. package/dist/commands/setup.js +42 -4
  50. package/dist/commands/stale.js +109 -0
  51. package/dist/commands/switch.js +100 -2
  52. package/dist/commands/trap.js +14 -31
  53. package/dist/commands/update-handoff.js +63 -4
  54. package/dist/commands/update-plan.js +21 -28
  55. package/dist/commands/update-step.js +37 -0
  56. package/dist/commands/upgrade.js +313 -6
  57. package/dist/commands/usage.js +102 -0
  58. package/dist/commands/version.js +20 -0
  59. package/dist/commands/who.js +33 -5
  60. package/dist/commands/worktree.js +105 -0
  61. package/dist/core/actions.js +315 -0
  62. package/dist/core/agent-capability.js +610 -17
  63. package/dist/core/agent-context.js +7 -1
  64. package/dist/core/agent-files.js +1169 -85
  65. package/dist/core/agent-integrations.js +160 -5
  66. package/dist/core/agent-inventory.js +2 -0
  67. package/dist/core/agent-profiles.js +93 -0
  68. package/dist/core/agent-registry.js +162 -30
  69. package/dist/core/agentrun-reconciler.js +345 -0
  70. package/dist/core/agentruns.js +424 -0
  71. package/dist/core/ai-agent-detection.js +31 -10
  72. package/dist/core/archival.js +77 -0
  73. package/dist/core/assignment-sweeper.js +82 -0
  74. package/dist/core/assignments.js +367 -0
  75. package/dist/core/audit.js +30 -0
  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 +381 -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/events.js +106 -2
  98. package/dist/core/execution-adapters.js +154 -0
  99. package/dist/core/execution-context.js +63 -0
  100. package/dist/core/execution-profile.js +270 -0
  101. package/dist/core/execution.js +255 -0
  102. package/dist/core/facade-schema.js +81 -0
  103. package/dist/core/federation-cloud.js +99 -0
  104. package/dist/core/federation-message.js +52 -0
  105. package/dist/core/federation-transport.js +65 -0
  106. package/dist/core/gc-semantic.js +482 -0
  107. package/dist/core/governance.js +247 -0
  108. package/dist/core/guards.js +19 -0
  109. package/dist/core/ideation.js +72 -0
  110. package/dist/core/identity.js +110 -25
  111. package/dist/core/ids.js +6 -0
  112. package/dist/core/input-validation.js +2 -2
  113. package/dist/core/instruction-templates.js +344 -136
  114. package/dist/core/io.js +90 -11
  115. package/dist/core/lock.js +6 -2
  116. package/dist/core/loops/brief-assembly.js +213 -0
  117. package/dist/core/loops/facade-schema.js +148 -0
  118. package/dist/core/loops/index.js +7 -0
  119. package/dist/core/loops/iteration-engine.js +139 -0
  120. package/dist/core/loops/lock.js +385 -0
  121. package/dist/core/loops/store.js +201 -0
  122. package/dist/core/loops/types.js +403 -0
  123. package/dist/core/loops/verbs.js +534 -0
  124. package/dist/core/markdown.js +15 -3
  125. package/dist/core/memory-compactor.js +432 -0
  126. package/dist/core/memory-git.js +152 -8
  127. package/dist/core/messaging.js +278 -0
  128. package/dist/core/migration.js +32 -1
  129. package/dist/core/mutation-pipeline.js +4 -2
  130. package/dist/core/operations/memory-mutation.js +129 -0
  131. package/dist/core/operations/memory-write.js +78 -0
  132. package/dist/core/operations/plan.js +190 -0
  133. package/dist/core/policy.js +169 -0
  134. package/dist/core/reputation.js +9 -3
  135. package/dist/core/schema.js +491 -6
  136. package/dist/core/search.js +21 -2
  137. package/dist/core/security-cache.js +71 -0
  138. package/dist/core/security-guard.js +152 -0
  139. package/dist/core/security-scoring.js +86 -0
  140. package/dist/core/sequence.js +130 -0
  141. package/dist/core/socket-client.js +113 -0
  142. package/dist/core/staleness.js +246 -0
  143. package/dist/core/state.js +98 -22
  144. package/dist/core/store-resolution.js +43 -11
  145. package/dist/core/toml-writer.js +76 -0
  146. package/dist/core/upgrades/backup.js +232 -0
  147. package/dist/core/upgrades/health-check.js +169 -0
  148. package/dist/core/upgrades/patches/candidate-archive.js +145 -0
  149. package/dist/core/upgrades/patches/handoff-review-strip.js +128 -0
  150. package/dist/core/upgrades/patches/provenance-rollout.js +136 -0
  151. package/dist/core/upgrades/schema-version.js +97 -0
  152. package/dist/core/worktree.js +606 -0
  153. package/dist/facts.js +114 -0
  154. package/dist/facts.json +111 -0
  155. package/docs/architecture/project-refs.md +5 -1
  156. package/docs/cli.md +690 -43
  157. package/docs/concepts/ideation-loop.md +317 -0
  158. package/docs/concepts/loop-engine.md +456 -0
  159. package/docs/concepts/mcp-governance.md +268 -0
  160. package/docs/concepts/memory-staleness.md +122 -0
  161. package/docs/concepts/multi-agent-workflows.md +166 -0
  162. package/docs/concepts/plans-and-claims.md +31 -6
  163. package/docs/concepts/project-md-convention.md +35 -0
  164. package/docs/concepts/troubleshooting.md +220 -0
  165. package/docs/concepts/upgrade-cli.md +202 -0
  166. package/docs/concepts/upgrade-dogfood-procedure.md +114 -0
  167. package/docs/context-format-changelog.md +2 -2
  168. package/docs/context-format.md +2 -2
  169. package/docs/index.md +68 -0
  170. package/docs/integrations/agents.md +15 -16
  171. package/docs/integrations/cline.md +88 -0
  172. package/docs/integrations/codex.md +75 -23
  173. package/docs/integrations/continue.md +60 -0
  174. package/docs/integrations/copilot.md +67 -9
  175. package/docs/integrations/kilocode.md +72 -0
  176. package/docs/integrations/mcp.md +304 -21
  177. package/docs/integrations/mistral-vibe.md +122 -0
  178. package/docs/integrations/opencode.md +84 -0
  179. package/docs/integrations/overview.md +23 -8
  180. package/docs/integrations/roo.md +74 -0
  181. package/docs/integrations/windsurf.md +83 -0
  182. package/docs/mcp-schema-changelog.md +191 -1
  183. package/docs/playbooks/integration/index.md +121 -0
  184. package/docs/playbooks/productivity/index.md +102 -0
  185. package/docs/playbooks/team/index.md +122 -0
  186. package/docs/product/agent-first-model.md +184 -0
  187. package/docs/product/entity-model-audit.md +462 -0
  188. package/docs/product/positioning.md +10 -10
  189. package/docs/quickstart-existing-project.md +135 -0
  190. package/docs/quickstart.md +124 -37
  191. package/docs/release-maintenance.md +79 -0
  192. package/docs/review.md +2 -0
  193. package/docs/server-operations.md +118 -0
  194. package/package.json +21 -13
  195. package/dist/commands/claude-desktop-extension.js +0 -18
  196. package/dist/commands/diff.js +0 -99
  197. 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,66 @@ 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 parseLeadingGlobalOptions(argv) {
113
+ const result = {};
114
+ for (let i = 0; i < argv.length; i++) {
115
+ const token = argv[i];
116
+ if (!token.startsWith('-')) {
117
+ break;
118
+ }
119
+ if (token === '--verbose') {
120
+ result.verbose = true;
121
+ continue;
122
+ }
123
+ if (token === '--debug') {
124
+ result.debug = true;
125
+ continue;
126
+ }
127
+ if (token === '--cwd') {
128
+ result.cwd = argv[i + 1];
129
+ i++;
130
+ continue;
131
+ }
132
+ if (token.startsWith('--cwd=')) {
133
+ result.cwd = token.slice('--cwd='.length);
134
+ continue;
135
+ }
136
+ if (token === '--project') {
137
+ result.project = argv[i + 1];
138
+ i++;
139
+ continue;
140
+ }
141
+ if (token.startsWith('--project=')) {
142
+ result.project = token.slice('--project='.length);
143
+ continue;
144
+ }
145
+ }
146
+ return result;
147
+ }
148
+ function isCodevEnabled() {
149
+ return process.env.BRAINCLAW_ENABLE_CODEV === '1';
150
+ }
91
151
  program
92
152
  .name('brainclaw')
93
153
  .description('Shared project memory for humans and coding agents.')
@@ -95,15 +155,33 @@ program
95
155
  .option('--verbose', 'Show info-level log messages on stderr')
96
156
  .option('--debug', 'Show debug-level log messages on stderr')
97
157
  .option('--cwd <path>', 'Override working directory for this invocation')
158
+ .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
159
  .hook('preAction', (_thisCommand, actionCommand) => {
99
- const root = actionCommand.optsWithGlobals();
160
+ const root = parseLeadingGlobalOptions(process.argv.slice(2));
100
161
  initLogLevel({ verbose: root.verbose, debug: root.debug });
101
162
  // Skip effective cwd resolution for commands that create the store
102
163
  const cmdName = actionCommand.name();
103
164
  const skipResolution = cmdName === 'init' || cmdName === 'setup';
165
+ // pln#359 phase 1c — `--project <name>` resolves a linked project to an
166
+ // absolute path via resolveProjectCwd, then feeds the same chdir flow
167
+ // as --cwd. Mutually exclusive with --cwd to avoid ambiguity.
168
+ let explicitCwd = root.cwd;
169
+ if (root.project) {
170
+ if (root.cwd) {
171
+ console.error('Error: --project and --cwd are mutually exclusive. Use one.');
172
+ process.exit(1);
173
+ }
174
+ try {
175
+ explicitCwd = resolveProjectCwd(root.project, process.cwd());
176
+ }
177
+ catch (err) {
178
+ console.error(`Error: ${err.message}`);
179
+ process.exit(1);
180
+ }
181
+ }
104
182
  if (!skipResolution) {
105
- // Resolve effective cwd (--cwd > BRAINCLAW_PROJECT > active-project > process.cwd)
106
- const effectiveCwd = resolveEffectiveCwd({ explicitCwd: root.cwd });
183
+ // Resolve effective cwd (explicit > BRAINCLAW_PROJECT > active-project > process.cwd)
184
+ const effectiveCwd = resolveEffectiveCwd({ explicitCwd });
107
185
  if (effectiveCwd !== process.cwd()) {
108
186
  // Change process.cwd() so all commands resolve the correct store
109
187
  // without needing individual --cwd plumbing
@@ -115,9 +193,9 @@ program
115
193
  logger.info(`Cleaned ${removed} orphan lock/tmp file(s) in ${memoryDir()}`);
116
194
  }
117
195
  }
118
- else if (root.cwd) {
119
- // For init/setup, still respect explicit --cwd but nothing else
120
- process.chdir(path.resolve(root.cwd));
196
+ else if (explicitCwd) {
197
+ // For init/setup, still respect explicit --cwd / --project but nothing else
198
+ process.chdir(path.resolve(explicitCwd));
121
199
  }
122
200
  });
123
201
  // --- init ---
@@ -131,6 +209,7 @@ program
131
209
  .option('--project-mode <mode>', 'Project mode: single-project, multi-project, auto')
132
210
  .option('--project-strategy <strategy>', 'Project strategy for multi-project mode: manual, folder')
133
211
  .option('--no-analyze-repo', 'Skip repository analysis when suggesting a project mode')
212
+ .option('--no-ai-scan', 'Skip AI surface scan during init')
134
213
  .option('--scan', 'Scan subdirectories for service boundaries and suggest init targets')
135
214
  .action(async (options) => {
136
215
  await runInit(options);
@@ -165,15 +244,34 @@ program
165
244
  // --- memory-rollback ---
166
245
  program
167
246
  .command('memory-rollback <ref>')
168
- .description('Rollback entire memory to a previous git snapshot (use memory-log to find refs)')
169
- .action((ref) => {
247
+ .description('Restore live project memory from a previous git snapshot without deleting audit or archive artifacts')
248
+ .option('--actor <name>', 'Registered human identity required to authorize the rollback')
249
+ .action((ref, options) => {
250
+ const cwd = process.cwd();
170
251
  if (!hasMemoryRepo()) {
171
252
  console.error('Error: no memory git repo. Run `brainclaw init --force` to enable.');
172
253
  process.exit(1);
173
254
  }
174
- const success = rollbackMemory(ref);
255
+ let actor;
256
+ try {
257
+ actor = requireRegisteredAgentIdentity({
258
+ agentName: options.actor,
259
+ allowCurrent: true,
260
+ allowEnv: true,
261
+ cwd,
262
+ });
263
+ }
264
+ catch (err) {
265
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
266
+ process.exit(1);
267
+ }
268
+ if (actor.kind !== 'human') {
269
+ console.error(`Error: memory-rollback is reserved to registered human identities. Resolved actor '${actor.agent_name}' is kind='${actor.kind}'.`);
270
+ process.exit(1);
271
+ }
272
+ const success = rollbackMemory(ref, cwd);
175
273
  if (success) {
176
- console.log(`✔ Memory rolled back to ${ref}`);
274
+ console.log(`✔ Live project memory restored to ${ref} (audit, archives, backups preserved)`);
177
275
  }
178
276
  else {
179
277
  console.error(`Error: failed to rollback to '${ref}'. Check memory-log for valid refs.`);
@@ -186,12 +284,45 @@ program
186
284
  .description('Upgrade project memory structure and refresh managed workspace agent files without losing data')
187
285
  .option('--json', 'Output as JSON')
188
286
  .option('--dry-run', 'Show what would be done without making changes')
287
+ .option('--self-update', 'Check for a newer brainclaw package version and install it before upgrading memory')
288
+ .option('--to <version>', 'One-shot target schema version (e.g. --to=1.0). Real runs require a backup.')
289
+ .option('--backup', 'Create a timestamped backup of .brainclaw/ before any write (always on for --to runs)')
290
+ .option('--no-backup', 'Disable the automatic backup for housekeeping-only upgrade runs')
291
+ .option('--rollback', 'Restore the most recent backup, park the current live store, exit')
292
+ .option('--yes', 'Skip interactive confirmations (reserved for later prompt additions)')
189
293
  .action((options) => {
190
294
  runUpgrade({
191
295
  json: options.json,
192
296
  dryRun: options.dryRun,
297
+ selfUpdate: options.selfUpdate,
298
+ to: options.to,
299
+ backup: options.backup,
300
+ rollback: options.rollback,
301
+ yes: options.yes,
193
302
  });
194
303
  });
304
+ // --- patch-configs ---
305
+ program
306
+ .command('patch-configs')
307
+ .description('Patch all MCP config files to use the current brainclaw binary path')
308
+ .option('--json', 'Output as JSON')
309
+ .action((options) => {
310
+ const cwd = process.env.BRAINCLAW_CWD ?? process.cwd();
311
+ const results = patchAllMcpConfigs(cwd);
312
+ if (options.json) {
313
+ console.log(JSON.stringify(results, null, 2));
314
+ }
315
+ else if (results.length === 0) {
316
+ console.log('✔ All MCP configs are already up to date.');
317
+ }
318
+ else {
319
+ for (const r of results) {
320
+ const tag = r.created ? 'created' : 'updated';
321
+ console.log(`✔ ${r.filePath} (${tag}) — ${r.label}`);
322
+ }
323
+ console.log(`\n${results.length} MCP config(s) patched.`);
324
+ }
325
+ });
195
326
  // --- machine-profile ---
196
327
  program
197
328
  .command('machine-profile')
@@ -346,6 +477,11 @@ program
346
477
  .option('--plan <id>', 'Optional linked plan item ID')
347
478
  .option('--author <author>', 'Author name')
348
479
  .option('--capture-diff', 'Capture current git diff into the handoff snapshot')
480
+ .option('--files <files...>', 'Files touched in this handoff')
481
+ .option('--pre-condition <conditions...>', 'Pre-conditions for the receiving agent')
482
+ .option('--post-condition <conditions...>', 'Post-conditions the receiving agent must satisfy')
483
+ .option('--test <tests...>', 'Tests the receiving agent should verify')
484
+ .option('--linked-plan <plans...>', 'Linked plan IDs')
349
485
  .action((text, options) => {
350
486
  runHandoff(text, options);
351
487
  });
@@ -376,8 +512,10 @@ program
376
512
  .option('--estimate <minutes>', 'Estimated effort in minutes (positive integer, e.g. --estimate 30)')
377
513
  .option('--actual-effort <effort>', 'Actual effort spent (e.g. "20min", "1h30m")')
378
514
  .option('--store <target>', 'Target store level: local (default), repo, workspace, user')
515
+ .option('--recursive', 'Include plans from descendant brainclaw projects (for list)')
516
+ .option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
379
517
  .action((subcommand, args, options) => {
380
- runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort });
518
+ runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort, localOnly: options.localOnly });
381
519
  });
382
520
  // --- list-plans ---
383
521
  program
@@ -389,8 +527,24 @@ program
389
527
  .option('--assignee <assignee>', 'Filter by assignee')
390
528
  .option('--project <project>', 'Filter by project namespace')
391
529
  .option('--all', 'Include done and dropped plan items')
530
+ .option('--recursive', 'Include plans from descendant brainclaw projects')
531
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
392
532
  .action((options) => {
393
- runListPlans(options);
533
+ runListPlans({ ...options, localOnly: options.localOnly });
534
+ });
535
+ program
536
+ .command('sequence <subcommand> [args...]')
537
+ .description('Manage coordination sequences (create, list, show, update)')
538
+ .option('--json', 'Output as JSON')
539
+ .option('--description <text>', 'Optional sequence description')
540
+ .option('--status <status>', 'Sequence status: draft, active, archived')
541
+ .option('--owner <owner>', 'Optional sequence owner')
542
+ .option('--items <json>', 'Sequence items JSON array')
543
+ .option('--name <name>', 'Optional sequence name for update')
544
+ .option('--tag <tags...>', 'Tags for this sequence')
545
+ .option('--author <author>', 'Author name')
546
+ .action((subcommand, args, options) => {
547
+ runSequenceResource(subcommand, args, options);
394
548
  });
395
549
  // --- add-step ---
396
550
  program
@@ -407,6 +561,23 @@ program
407
561
  .action((planId, stepId) => {
408
562
  runCompleteStep(planId, stepId);
409
563
  });
564
+ // --- update-step ---
565
+ program
566
+ .command('update-step <planId> <stepId>')
567
+ .description('Update a plan step (status, text, assignee)')
568
+ .option('--status <status>', 'New status: todo, in_progress, testing, done, blocked')
569
+ .option('--text <text>', 'Replace step description')
570
+ .option('--assign <assignee>', 'Assign the step (empty string to unassign)')
571
+ .action((planId, stepId, options) => {
572
+ runUpdateStep(planId, stepId, options);
573
+ });
574
+ // --- delete-step ---
575
+ program
576
+ .command('delete-step <planId> <stepId>')
577
+ .description('Remove a step from a plan')
578
+ .action((planId, stepId) => {
579
+ runDeleteStep(planId, stepId);
580
+ });
410
581
  // --- estimation-report ---
411
582
  program
412
583
  .command('estimation-report')
@@ -460,11 +631,25 @@ program
460
631
  // --- update-handoff ---
461
632
  program
462
633
  .command('update-handoff <id>')
463
- .description('Update the status of a handoff')
634
+ .description('Update the status, recipient, or review state of a handoff')
464
635
  .option('--status <status>', 'Status: open, accepted, closed')
465
636
  .option('--to <agent>', 'Change the receiving agent')
637
+ .option('--narrative <text>', 'Update the narrative attached to the handoff')
638
+ .option('--reviewer <agent>', 'Set or override the assigned reviewer')
639
+ .option('--review-verdict <verdict>', 'Set review verdict: approve or request_changes')
640
+ .option('--reviewed-by <agent>', 'Set the reviewer identity that produced the verdict')
641
+ .option('--review-summary <text>', 'Attach a short review summary')
642
+ .option('--blocking-issue <text>', 'Add a blocking review issue (repeatable)', collect, [])
643
+ .option('--suggestion <text>', 'Add a non-blocking review suggestion (repeatable)', collect, [])
466
644
  .action((id, options) => {
467
- runUpdateHandoff(id, options);
645
+ runUpdateHandoff(id, {
646
+ ...options,
647
+ review_verdict: options.reviewVerdict,
648
+ reviewed_by: options.reviewedBy,
649
+ review_summary: options.reviewSummary,
650
+ blocking_issues: options.blockingIssue,
651
+ suggestions: options.suggestion,
652
+ });
468
653
  });
469
654
  // --- doctor ---
470
655
  program
@@ -473,8 +658,34 @@ program
473
658
  .option('--json', 'Output as JSON dashboard')
474
659
  .option('--migration-check', 'Report versioned documents that need schema migration')
475
660
  .option('--fix-agent-ignore', 'Add missing .gitignore entries for generated local Brainclaw agent files')
661
+ .option('--fix', 'Fix auto-resolvable issues (e.g. drifting MCP configs)')
662
+ .option('--repair', 'Rebuild dist/ when the MCP runtime is missing or stale')
663
+ .option('--after-migration', 'Run the v1.0 post-migration health check only (exits non-zero on any failure)')
664
+ .option('--dispatch', 'Run dispatch-health diagnostic only: reconcile open agent_runs and report stuck/unverified/silent failures (pln#496 step stp_8c072d75)')
476
665
  .action((options) => {
477
- runDoctor(options);
666
+ runDoctor({ ...options, afterMigration: options.afterMigration, dispatch: options.dispatch });
667
+ });
668
+ // --- repair (Phase 4 Sprint 2 Lane C / pln#397) ---
669
+ program
670
+ .command('repair')
671
+ .description('Apply safe, non-destructive fixes for the repair candidates surfaced by doctor')
672
+ .option('--dry-run', 'Print the plan without executing anything')
673
+ .option('--include-unsafe', 'Also apply candidates flagged unsafe (preserves data but requires confirmation)')
674
+ .option('--json', 'Output as JSON')
675
+ .action((options) => {
676
+ runRepair({
677
+ dryRun: options.dryRun,
678
+ includeUnsafe: options.includeUnsafe,
679
+ json: options.json,
680
+ });
681
+ });
682
+ // --- stale (Phase 4 Sprint 1 Lane A / pln#390) ---
683
+ program
684
+ .command('stale [subcommand] [id]')
685
+ .description('List or resolve stale memory items (plans, traps, handoffs, candidates, runtime notes). Subcommands: list (default), resolve <id>.')
686
+ .option('--json', 'Output as JSON')
687
+ .action((subcommand, id, options) => {
688
+ runStale(subcommand, id, { json: options.json });
478
689
  });
479
690
  // --- version ---
480
691
  program
@@ -482,11 +693,23 @@ program
482
693
  .description('Show the installed brainclaw version and the project version policy')
483
694
  .option('--check', 'Check the configured installable update source')
484
695
  .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')
696
+ .option('--release-notes <text>', 'Attach plain-text release notes to the generated local-pack manifest')
697
+ .option('--agent-release-notes <json>', 'Attach structured agent-first release notes (JSON) to the generated local-pack manifest')
698
+ .option('--auto-release-notes', 'Auto-generate agent-first release notes from git log (use with --publish-local)')
486
699
  .option('--json', 'Output as JSON')
487
700
  .action((options) => {
488
701
  runVersion(options);
489
702
  });
703
+ // --- release-notes ---
704
+ program
705
+ .command('release-notes')
706
+ .description('Show or generate agent-first release notes')
707
+ .option('--generate', 'Generate release notes from git log instead of showing configured ones')
708
+ .option('--since <ref>', 'Git ref to generate from (default: last version tag)')
709
+ .option('--json', 'Output as JSON')
710
+ .action((options) => {
711
+ runReleaseNotes(options);
712
+ });
490
713
  // --- uninstall ---
491
714
  import { runUninstall } from './commands/uninstall.js';
492
715
  program
@@ -662,6 +885,7 @@ program
662
885
  .command('enable-agent <name>')
663
886
  .description('Activate a supported coding agent on an already initialized project')
664
887
  .option('--kind <kind>', 'Identity kind: agent, human, unknown', 'agent')
888
+ .option('--context-profile <profile>', 'Default context profile: dev, dense, compact, copilot, quick, openclaw, ops, research')
665
889
  .option('--capability <value>', 'Declare a capability on the agent profile (repeatable)', collect, [])
666
890
  .option('--replace-capabilities', 'Replace existing capabilities instead of merging')
667
891
  .option('--generate-fingerprint', 'Generate or rotate a local public identity fingerprint for this agent')
@@ -749,6 +973,17 @@ program
749
973
  .action((id, options) => {
750
974
  runReject(id, options.reason, options.by);
751
975
  });
976
+ // --- harvest-candidates ---
977
+ program
978
+ .command('harvest-candidates')
979
+ .description('Harvest candidates from worktree inboxes into the main project store (codex sandbox bridge)')
980
+ .option('--dry-run', 'Preview what would be imported without writing anything')
981
+ .option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
982
+ .option('--json', 'Output as JSON')
983
+ .action((options) => {
984
+ const globalOpts = program.opts();
985
+ runHarvestCandidates({ ...options, cwd: globalOpts.cwd });
986
+ });
752
987
  // --- prune-candidates ---
753
988
  program
754
989
  .command('prune-candidates')
@@ -758,6 +993,35 @@ program
758
993
  .action((options) => {
759
994
  runPruneCandidates(options);
760
995
  });
996
+ // --- cleanup-candidates ---
997
+ program
998
+ .command('cleanup-candidates')
999
+ .description('Remove stale auto-generated pending candidates')
1000
+ .option('--max-age <days>', 'Max age in days before cleanup', parseInt)
1001
+ .option('--dry-run', 'Preview without deleting')
1002
+ .action((options) => {
1003
+ if (!memoryExists()) {
1004
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
1005
+ process.exit(1);
1006
+ }
1007
+ const maxAgeDays = options.maxAge ?? 30;
1008
+ const result = cleanupStaleCandidates({
1009
+ maxAgeDays,
1010
+ dryRun: options.dryRun,
1011
+ });
1012
+ if (result.matched === 0) {
1013
+ console.log(`No stale auto-generated candidates older than ${maxAgeDays} days found.`);
1014
+ return;
1015
+ }
1016
+ if (options.dryRun) {
1017
+ console.log(`Would remove ${result.matched} stale auto-generated candidate(s):`);
1018
+ for (const candidate of result.candidates) {
1019
+ console.log(` [${candidate.id}] ${candidate.text}`);
1020
+ }
1021
+ return;
1022
+ }
1023
+ console.log(`✔ Removed ${result.deleted} stale auto-generated candidate(s) older than ${maxAgeDays} days.`);
1024
+ });
761
1025
  // --- claim ---
762
1026
  program
763
1027
  .command('claim <subcommand> [args...]')
@@ -771,8 +1035,9 @@ program
771
1035
  .option('--json', 'Output as JSON for list')
772
1036
  .option('--plan-status <status>', 'Optional linked plan status when releasing: todo, in_progress, blocked, done, dropped')
773
1037
  .option('--store <target>', 'Target store level: local (default), repo, workspace')
1038
+ .option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
774
1039
  .action((subcommand, args, options) => {
775
- runClaimResource(subcommand, args, { ...options, planStatus: options.planStatus });
1040
+ runClaimResource(subcommand, args, { ...options, planStatus: options.planStatus, localOnly: options.localOnly });
776
1041
  });
777
1042
  // --- list-claims ---
778
1043
  program
@@ -783,8 +1048,9 @@ program
783
1048
  .option('--project <project>', 'Filter by project namespace')
784
1049
  .option('--plan <id>', 'Filter by linked plan item')
785
1050
  .option('--agent <agent>', 'Filter by agent name')
1051
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
786
1052
  .action((options) => {
787
- runListClaims(options);
1053
+ runListClaims({ ...options, localOnly: options.localOnly });
788
1054
  });
789
1055
  // --- release-claim ---
790
1056
  program
@@ -818,6 +1084,7 @@ program
818
1084
  .option('--capabilities', 'List all registered agents with their declared capabilities')
819
1085
  .option('--suggest <query>', 'Suggest agents whose capabilities match a query string')
820
1086
  .option('--include-session-meta', 'Include session_start/session_end runtime notes (hidden by default)')
1087
+ .option('--all-agents', 'Show unfiltered board (supervisor mode — all claims, all agents)')
821
1088
  .action((options) => {
822
1089
  runAgentBoard(options);
823
1090
  });
@@ -836,6 +1103,24 @@ program
836
1103
  .action((text, options) => {
837
1104
  runRuntimeNote(text, { ...options, autoReflect: options.autoReflect });
838
1105
  });
1106
+ // --- note create ---
1107
+ const noteCommand = program
1108
+ .command('note')
1109
+ .description('Manage runtime notes');
1110
+ noteCommand
1111
+ .command('create <text>')
1112
+ .description('Alias for runtime-note')
1113
+ .option('--agent <agent>', 'Agent name; defaults to the configured current agent')
1114
+ .option('--project <project>', 'Optional project namespace')
1115
+ .option('--plan <id>', 'Optional linked plan item ID')
1116
+ .option('--visibility <visibility>', 'Visibility: shared, machine, private', 'shared')
1117
+ .option('--host <host>', 'Optional host identifier override for machine/private runtime notes')
1118
+ .option('--tag <tags...>', 'Tags')
1119
+ .option('--ttl <duration>', 'Time-to-live: 30m, 2h, 7d (note auto-expires after this duration)')
1120
+ .option('--auto-reflect', 'Attempt to turn this runtime note into durable memory immediately')
1121
+ .action((text, options) => {
1122
+ runRuntimeNote(text, { ...options, autoReflect: options.autoReflect });
1123
+ });
839
1124
  // --- runtime-status ---
840
1125
  program
841
1126
  .command('runtime-status')
@@ -872,6 +1157,46 @@ program
872
1157
  .action((options) => {
873
1158
  runCheckConstraints({ staged: options.staged, files: options.files, json: options.json });
874
1159
  });
1160
+ // --- check-policy ---
1161
+ program
1162
+ .command('check-policy')
1163
+ .description('Pre-execution policy check: verify claims, constraints, traps and instructions for a scope')
1164
+ .requiredOption('--scope <path>', 'File or directory scope to check')
1165
+ .option('--agent <name>', 'Agent name to check claims for')
1166
+ .option('--agent-id <id>', 'Agent id to check claims for')
1167
+ .option('--action <action>', 'Intended action: edit, create, delete')
1168
+ .option('--json', 'Output as JSON')
1169
+ .action((options) => {
1170
+ runCheckPolicy({
1171
+ scope: options.scope,
1172
+ agent: options.agent,
1173
+ agentId: options.agentId,
1174
+ action: options.action,
1175
+ json: options.json,
1176
+ });
1177
+ });
1178
+ // --- check-security ---
1179
+ program
1180
+ .command('check-security')
1181
+ .description('Check supply chain security scores for packages via Socket.dev')
1182
+ .requiredOption('--packages <names>', 'Comma-separated package names (e.g. "axios,express" or "axios@1.14.1")')
1183
+ .option('--ecosystem <type>', 'Package ecosystem: npm or pypi', 'npm')
1184
+ .option('--json', 'Output as JSON')
1185
+ .action(async (options) => {
1186
+ await runCheckSecurity({
1187
+ packages: options.packages,
1188
+ ecosystem: options.ecosystem,
1189
+ json: options.json,
1190
+ });
1191
+ });
1192
+ // --- setup-security ---
1193
+ program
1194
+ .command('setup-security')
1195
+ .description('Enable supply chain security gate: generate wrapper scripts and configure preinstall checks')
1196
+ .option('--mode <mode>', 'Security mode: advisory (default) or enforced', 'advisory')
1197
+ .action((options) => {
1198
+ runSetupSecurity({ mode: options.mode });
1199
+ });
875
1200
  // --- install-hooks ---
876
1201
  program
877
1202
  .command('install-hooks')
@@ -894,9 +1219,26 @@ program
894
1219
  .command('prune')
895
1220
  .description('Prune expired constraints and stale memory')
896
1221
  .option('--expired', 'Also prune expired runtime notes and traps')
1222
+ .option('--archive', 'Archive done plans and closed handoffs (>30 days) to cold storage JSONL')
1223
+ .option('--semantic', 'Detect near-duplicate clusters and stale items via semantic analysis')
1224
+ .option('--dry-run', 'Preview compaction without applying (use with --semantic)')
897
1225
  .action((options) => {
898
1226
  runPrune(options);
899
1227
  });
1228
+ // --- compact ---
1229
+ program
1230
+ .command('compact')
1231
+ .description('LLM-driven semantic memory compaction — archive old items and get a summary template')
1232
+ .option('--assess', 'Show pressure assessment and compaction template without archiving')
1233
+ .option('--dry-run', 'Preview eligible items without archiving')
1234
+ .option('--max-items <n>', 'Maximum items to compact (default: 20)', parseInt)
1235
+ .option('--min-age <days>', 'Minimum age in days for eligibility (default: 7)', parseInt)
1236
+ .option('--no-dedup-handoffs', 'Skip deduplication of auto-generated session-end handoffs')
1237
+ .option('--no-purge-claims', 'Skip archival of released claims')
1238
+ .option('--no-purge-session-notes', 'Skip archival of session-lifecycle runtime_notes')
1239
+ .action((options) => {
1240
+ runCompact(options);
1241
+ });
900
1242
  // --- mcp ---
901
1243
  program
902
1244
  .command('mcp')
@@ -921,6 +1263,8 @@ program
921
1263
  .option('--agent <agent>', 'Agent name (defaults to current configured agent)')
922
1264
  .option('--context <path>', 'Context target path for initial hash capture')
923
1265
  .option('--model <id>', 'Model identifier (e.g. claude-sonnet-4-6)')
1266
+ .option('--maintenance-mode <mode>', 'Maintenance mode: full (default) or fast')
1267
+ .option('--include-context', 'Output full project context after starting session (replaces separate context call)')
924
1268
  .option('--json', 'Output as JSON')
925
1269
  .action((options) => {
926
1270
  runSessionStart(options);
@@ -934,10 +1278,21 @@ program
934
1278
  .option('--summary <text>', 'Session summary text')
935
1279
  .option('--auto-reflect', 'Auto-reflect session notes as pending candidates')
936
1280
  .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')
1281
+ .option('--reflect-handoff', 'Materialize an open handoff from git commits since session start')
1282
+ .option('--dispatch-review', 'When used with --reflect-handoff, auto-dispatch a code review if the handoff is reviewable')
1283
+ .option('--reviewer <name>', 'Explicit reviewer to route the reflected handoff review to')
1284
+ .option('--reflect', 'Include structured reflection questions for the agent to answer')
938
1285
  .option('--json', 'Output as JSON')
939
1286
  .action((options) => {
940
- runSessionEnd({ ...options, autoReflect: options.autoReflect, autoRelease: options.autoRelease, reflectHandoff: options.reflectHandoff });
1287
+ runSessionEnd({
1288
+ ...options,
1289
+ autoReflect: options.autoReflect,
1290
+ autoRelease: options.autoRelease,
1291
+ reflectHandoff: options.reflectHandoff,
1292
+ dispatchReview: options.dispatchReview,
1293
+ reviewer: options.reviewer,
1294
+ reflect: options.reflect,
1295
+ });
941
1296
  });
942
1297
  // --- whoami ---
943
1298
  program
@@ -947,6 +1302,17 @@ program
947
1302
  .action((options) => {
948
1303
  runWhoami(options);
949
1304
  });
1305
+ // --- usage ---
1306
+ program
1307
+ .command('usage')
1308
+ .description('Show brainclaw context volume stats (tokens injected per agent/tool)')
1309
+ .option('--agent <name>', 'Filter by agent name')
1310
+ .option('--tool <name>', 'Filter by tool name')
1311
+ .option('--days <n>', 'Limit to last N days', parseInt)
1312
+ .option('--json', 'Output as JSON')
1313
+ .action((options) => {
1314
+ runUsage(options);
1315
+ });
950
1316
  // --- search ---
951
1317
  program
952
1318
  .command('search <query>')
@@ -968,6 +1334,7 @@ program
968
1334
  .option('--detect', 'Auto-detect agent environment and write to its native file')
969
1335
  .option('--all', 'Write all known agent instruction files at once (claude-md, agents-md, copilot-instructions, cursor-rules, etc.)')
970
1336
  .option('--write', 'Write to canonical file path instead of stdout (when --format is given); local files are gitignored by default')
1337
+ .option('--include-live', 'Also write the native live companion file when the target agent supports one')
971
1338
  .option('--shared', 'Keep the main exported instruction file versionable instead of auto-ignoring it (companions remain local)')
972
1339
  .option('--output <file>', 'Write to a specific file path instead of stdout')
973
1340
  .option('--project <project>', 'Project namespace filter')
@@ -975,6 +1342,12 @@ program
975
1342
  .action((options) => {
976
1343
  runExport(options);
977
1344
  });
1345
+ program
1346
+ .command('refresh')
1347
+ .description('Refresh live companion files with current state (plans, claims, traps, sequences). Gitignored, safe to run frequently.')
1348
+ .action(() => {
1349
+ runRefresh();
1350
+ });
978
1351
  program
979
1352
  .command('reconcile')
980
1353
  .description('Refresh machine and workspace bootstrap state after updates or onboarding on complex installs')
@@ -1012,6 +1385,110 @@ program
1012
1385
  .action((options) => {
1013
1386
  runWatch({ ...options, autoClaim: options.autoClaim });
1014
1387
  });
1388
+ // --- dispatch ---
1389
+ const dispatchCmd = program
1390
+ .command('dispatch')
1391
+ .description('Local agent dispatcher — analyze lanes and assign work');
1392
+ dispatchCmd
1393
+ .command('analysis')
1394
+ .description('Analyze the active sequence: show ready, active, blocked, and done lanes')
1395
+ .option('--json', 'Output as JSON')
1396
+ .action((options) => {
1397
+ runDispatchAnalysis({ json: options.json });
1398
+ });
1399
+ dispatchCmd
1400
+ .command('run')
1401
+ .description('Run a dispatch cycle: assign ready lanes to available agents')
1402
+ .option('--agents <names>', 'Comma-separated list of agents to dispatch to')
1403
+ .option('--lanes <names>', 'Comma-separated list of lanes to dispatch')
1404
+ .option('--max <n>', 'Maximum assignments', parseInt)
1405
+ .option('--dry', 'Preview assignments without sending messages')
1406
+ .option('--spawn', 'Autonomously launch CLI agents with invoke templates')
1407
+ .option('--agent <name>', 'Dispatcher agent name')
1408
+ .option('--json', 'Output as JSON')
1409
+ .action((options) => {
1410
+ runDispatch({
1411
+ agents: options.agents,
1412
+ lanes: options.lanes,
1413
+ max: options.max,
1414
+ dry: options.dry,
1415
+ spawn: options.spawn,
1416
+ agent: options.agent,
1417
+ json: options.json,
1418
+ });
1419
+ });
1420
+ dispatchCmd
1421
+ .command('review')
1422
+ .description('Dispatch code reviews for completed handoffs')
1423
+ .option('--handoff <id>', 'Specific handoff ID to review')
1424
+ .option('--reviewer <name>', 'Specific reviewer agent')
1425
+ .option('--spawn', 'Launch the reviewer CLI agent')
1426
+ .option('--dry', 'Preview without sending')
1427
+ .option('--agent <name>', 'Dispatcher agent name')
1428
+ .option('--json', 'Output as JSON')
1429
+ .action((options) => {
1430
+ runDispatchReview({
1431
+ handoff: options.handoff,
1432
+ reviewer: options.reviewer,
1433
+ spawn: options.spawn,
1434
+ dry: options.dry,
1435
+ agent: options.agent,
1436
+ json: options.json,
1437
+ });
1438
+ });
1439
+ // --- inbox ---
1440
+ const inboxCmd = program
1441
+ .command('inbox')
1442
+ .description('Inter-agent messaging inbox');
1443
+ inboxCmd
1444
+ .command('list')
1445
+ .description('List inbox messages (default: pending only)')
1446
+ .option('--agent <name>', 'Agent name')
1447
+ .option('--status <status>', 'Filter by status: pending, read, acknowledged, archived')
1448
+ .option('--type <type>', 'Filter by type: assign, review, rfc, info, reply')
1449
+ .option('--thread <id>', 'Filter by thread ID')
1450
+ .option('--all', 'Show all messages, not just pending')
1451
+ .option('--json', 'Output as JSON')
1452
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
1453
+ .action((options) => {
1454
+ runInboxList({ ...options, localOnly: options.localOnly });
1455
+ });
1456
+ inboxCmd
1457
+ .command('ack <id>')
1458
+ .description('Acknowledge a message')
1459
+ .option('--agent <name>', 'Agent name')
1460
+ .option('--json', 'Output as JSON')
1461
+ .action((id, options) => {
1462
+ runInboxAck(id, options);
1463
+ });
1464
+ inboxCmd
1465
+ .command('archive <id>')
1466
+ .description('Archive a message')
1467
+ .option('--agent <name>', 'Agent name')
1468
+ .option('--json', 'Output as JSON')
1469
+ .action((id, options) => {
1470
+ runInboxArchive(id, options);
1471
+ });
1472
+ inboxCmd
1473
+ .command('send <to> <text>')
1474
+ .description('Send a message to another agent')
1475
+ .option('--type <type>', 'Message type: assign, review, rfc, info, reply (default: info)')
1476
+ .option('--ref <id>', 'Reference to a plan, sequence, or other entity')
1477
+ .option('--scope <path>', 'File scope')
1478
+ .option('--thread <id>', 'Thread ID for conversations')
1479
+ .option('--ack', 'Require acknowledgment')
1480
+ .option('--agent <name>', 'Sender agent name')
1481
+ .option('--json', 'Output as JSON')
1482
+ .action((to, text, options) => {
1483
+ runInboxSend(to, text, options);
1484
+ });
1485
+ inboxCmd
1486
+ .command('thread <id>')
1487
+ .description('Show all messages in a thread')
1488
+ .option('--json', 'Output as JSON')
1489
+ .action((id, options) => {
1490
+ runInboxThread(id, options);
1491
+ });
1015
1492
  // --- check-events ---
1016
1493
  program
1017
1494
  .command('check-events')
@@ -1063,14 +1540,16 @@ program
1063
1540
  // --- audit ---
1064
1541
  program
1065
1542
  .command('audit')
1066
- .description('View the append-only audit log of all memory mutations')
1543
+ .description('View the append-only audit log, or generate a governance posture report with --governance')
1067
1544
  .option('--since <date>', 'Show entries since this ISO date')
1068
1545
  .option('--actor <agent>', 'Filter by actor name or agent ID')
1069
1546
  .option('--action <action>', 'Filter by action type (create, accept, reject, etc.)')
1070
1547
  .option('--limit <n>', 'Show last N entries', parseInt)
1071
1548
  .option('--json', 'Output as JSON')
1549
+ .option('--governance', 'Generate a governance posture report (aggregated view of claims, constraints, traps, instructions)')
1550
+ .option('--scope <path>', 'Filter governance report by scope (used with --governance)')
1072
1551
  .action((options) => {
1073
- runAuditCommand({ since: options.since, actor: options.actor, action: options.action, limit: options.limit, json: options.json });
1552
+ runAuditCommand({ since: options.since, actor: options.actor, action: options.action, limit: options.limit, json: options.json, governance: options.governance, scope: options.scope });
1074
1553
  });
1075
1554
  // --- history ---
1076
1555
  program
@@ -1102,6 +1581,25 @@ program
1102
1581
  store: options.store,
1103
1582
  });
1104
1583
  });
1584
+ program
1585
+ .command('link <subcommand> [args...]')
1586
+ .description('Manage cross-project federation links (add, list, remove)')
1587
+ .option('--name <slug>', 'Override the auto-derived link name')
1588
+ .option('--role <role>', 'Link role: publisher (push signals out) or subscriber (default)')
1589
+ .option('--channels <list>', 'Comma-separated allow-list of channels: candidate,handoff,runtime_note', (val) => val.split(',').map((s) => s.trim()).filter(Boolean))
1590
+ .option('--force', 'Replace an existing link of the same name/path')
1591
+ .option('--json', 'Output as JSON')
1592
+ .option('--store <target>', 'Store level: local (default), repo, workspace, user')
1593
+ .action((subcommand, args, options) => {
1594
+ runLink(subcommand, args, {
1595
+ name: options.name,
1596
+ role: options.role,
1597
+ channels: options.channels,
1598
+ force: options.force,
1599
+ json: options.json,
1600
+ store: options.store,
1601
+ });
1602
+ });
1105
1603
  program
1106
1604
  .command('tool <subcommand> [args...]')
1107
1605
  .description('Manage project tools (list, add, describe, search)')
@@ -1163,10 +1661,197 @@ program
1163
1661
  .option('--json', 'Output as JSON')
1164
1662
  .option('--all', 'Include stale sessions')
1165
1663
  .option('--gc', 'Remove stale sessions')
1664
+ .option('--local-only', 'Read claims from local store only (skip parent stores in chain)')
1166
1665
  .action(async (options) => {
1167
1666
  const globalOpts = program.opts();
1168
1667
  const { runWho } = await import('./commands/who.js');
1169
- runWho({ json: options.json, all: options.all, gc: options.gc, cwd: globalOpts.cwd });
1668
+ runWho({ json: options.json, all: options.all, gc: options.gc, cwd: globalOpts.cwd, localOnly: options.localOnly });
1669
+ });
1670
+ const worktreeCmd = program
1671
+ .command('worktree')
1672
+ .description('Manage git worktrees for parallel agent isolation');
1673
+ worktreeCmd
1674
+ .command('create <branch>')
1675
+ .description('Create a linked git worktree for a given branch')
1676
+ .option('--session-id <id>', 'Associate this worktree with a brainclaw session')
1677
+ .option('--agent <name>', 'Associate this worktree with an agent name')
1678
+ .action((branch, options) => {
1679
+ const globalOpts = program.opts();
1680
+ runWorktreeCreate({ branch, sessionId: options.sessionId, agent: options.agent, cwd: globalOpts.cwd });
1681
+ });
1682
+ worktreeCmd
1683
+ .command('list')
1684
+ .description('List all git worktrees for this project')
1685
+ .action(() => {
1686
+ const globalOpts = program.opts();
1687
+ runWorktreeList({ cwd: globalOpts.cwd });
1688
+ });
1689
+ worktreeCmd
1690
+ .command('remove <path>')
1691
+ .description('Remove a linked git worktree')
1692
+ .option('--force', 'Force removal even with uncommitted changes')
1693
+ .action((worktreePath, options) => {
1694
+ const globalOpts = program.opts();
1695
+ runWorktreeRemove({ path: worktreePath, force: options.force, cwd: globalOpts.cwd });
1696
+ });
1697
+ worktreeCmd
1698
+ .command('prune')
1699
+ .description('Prune stale worktree administrative files')
1700
+ .action(() => {
1701
+ const globalOpts = program.opts();
1702
+ runWorktreePrune({ cwd: globalOpts.cwd });
1703
+ });
1704
+ worktreeCmd
1705
+ .command('clean')
1706
+ .description('Remove worktrees whose branch is fully merged and orphan worktree directories')
1707
+ .option('--force', 'Force removal even with uncommitted changes')
1708
+ .option('--dry-run', 'Show what would be removed without actually removing')
1709
+ .action((options) => {
1710
+ const globalOpts = program.opts();
1711
+ runWorktreeClean({ force: options.force, dryRun: options.dryRun, cwd: globalOpts.cwd });
1712
+ });
1713
+ worktreeCmd
1714
+ .command('merge <branch>')
1715
+ .description('Merge a worktree branch with auto-restoration of parasitic deletions')
1716
+ .option('-m, --message <message>', 'Merge commit message')
1717
+ .option('--dry-run', 'Show what would be merged without committing')
1718
+ .action((branch, options) => {
1719
+ const globalOpts = program.opts();
1720
+ runWorktreeMerge({ branch, message: options.message, dryRun: options.dryRun, cwd: globalOpts.cwd });
1721
+ });
1722
+ // --- federation cloud ---
1723
+ const federationCmd = program
1724
+ .command('federation')
1725
+ .description('Cloud federation — sync signals with app.brainclaw.dev');
1726
+ federationCmd
1727
+ .command('push <message>')
1728
+ .description('Push a test signal to the cloud')
1729
+ .option('--type <type>', 'Signal type', 'runtime_note')
1730
+ .option('--to-project <project>', 'Target project name')
1731
+ .option('--to-agent <agent>', 'Target agent name')
1732
+ .action(async (message, options) => {
1733
+ const { pushSignalToCloud, isCloudConfigured } = await import('./core/federation-cloud.js');
1734
+ const { createFederationMessage } = await import('./core/federation-message.js');
1735
+ const { loadConfig } = await import('./core/config.js');
1736
+ const { resolveCurrentAgentName } = await import('./core/agent-registry.js');
1737
+ if (!isCloudConfigured()) {
1738
+ console.error('Error: cloud not configured. Set BRAINCLAW_CLOUD_API_KEY env var.');
1739
+ process.exit(1);
1740
+ }
1741
+ const config = loadConfig();
1742
+ const agent = resolveCurrentAgentName() ?? 'unknown';
1743
+ const msg = createFederationMessage({
1744
+ version: 1,
1745
+ from: { project_name: config.project_name, project_path: process.cwd(), agent_name: agent },
1746
+ to: { project_name: options.toProject ?? 'broadcast', project_path: '' },
1747
+ type: options.type,
1748
+ payload: { text: message },
1749
+ });
1750
+ const ok = await pushSignalToCloud(msg);
1751
+ if (ok) {
1752
+ console.log(`✔ Signal pushed to cloud: [${msg.id}] ${message}`);
1753
+ }
1754
+ else {
1755
+ console.error('Error: failed to push signal to cloud.');
1756
+ process.exit(1);
1757
+ }
1758
+ });
1759
+ federationCmd
1760
+ .command('pull')
1761
+ .description('Pull signals from the cloud inbox')
1762
+ .option('--agent <name>', 'Agent name to pull for')
1763
+ .option('--since <date>', 'Only pull signals after this ISO date')
1764
+ .option('--limit <n>', 'Max signals to pull', '20')
1765
+ .action(async (options) => {
1766
+ const { pullSignalsFromCloud, isCloudConfigured } = await import('./core/federation-cloud.js');
1767
+ const { resolveCurrentAgentName } = await import('./core/agent-registry.js');
1768
+ if (!isCloudConfigured()) {
1769
+ console.error('Error: cloud not configured. Set BRAINCLAW_CLOUD_API_KEY env var.');
1770
+ process.exit(1);
1771
+ }
1772
+ const agent = options.agent ?? resolveCurrentAgentName() ?? 'unknown';
1773
+ const signals = await pullSignalsFromCloud(agent, {
1774
+ since: options.since,
1775
+ limit: parseInt(options.limit, 10),
1776
+ });
1777
+ if (signals.length === 0) {
1778
+ console.log('No signals in cloud inbox.');
1779
+ return;
1780
+ }
1781
+ console.log(`${signals.length} signal(s) from cloud:\n`);
1782
+ for (const s of signals) {
1783
+ const payload = typeof s.payload === 'object' && s.payload !== null ? s.payload.text ?? JSON.stringify(s.payload) : String(s.payload);
1784
+ console.log(` [${s.id}] ${s.type} from ${s.from.project_name}/${s.from.agent_name}`);
1785
+ console.log(` ${String(payload).slice(0, 120)}`);
1786
+ console.log(` ${s.created_at}\n`);
1787
+ }
1788
+ });
1789
+ federationCmd
1790
+ .command('status')
1791
+ .description('Check cloud federation configuration')
1792
+ .action(async () => {
1793
+ const { isCloudConfigured } = await import('./core/federation-cloud.js');
1794
+ const url = process.env.BRAINCLAW_CLOUD_URL ?? 'https://app.brainclaw.dev';
1795
+ console.log(`Cloud URL: ${url}`);
1796
+ console.log(`API Key: ${process.env.BRAINCLAW_CLOUD_API_KEY ? '***configured***' : 'NOT SET'}`);
1797
+ console.log(`Configured: ${isCloudConfigured() ? 'yes' : 'no'}`);
1798
+ if (isCloudConfigured()) {
1799
+ try {
1800
+ const res = await fetch(`${url}/api/v1/health`);
1801
+ const data = await res.json();
1802
+ console.log(`Cloud status: ${data.status} (v${data.version})`);
1803
+ }
1804
+ catch (e) {
1805
+ console.error(`Cloud unreachable: ${e.message}`);
1806
+ }
1807
+ }
1808
+ });
1809
+ // --- codev (legacy experimental) ---
1810
+ if (isCodevEnabled()) {
1811
+ program
1812
+ .command('codev [topic]')
1813
+ .description('Experimental legacy ideation session using persona-based consultation')
1814
+ .option('--personas <tier>', 'Persona tier: tier1 (default), tier2, or list', 'tier1')
1815
+ .option('--checkpoint', 'Pause after clarification for human input')
1816
+ .option('--spawn', 'Spawn each consultant as an agent CLI instance')
1817
+ .option('--fresh', 'Clear cached responses before starting a new run')
1818
+ .option('--agents <list>', 'Comma-separated agent names for spawn (e.g. claude-code,codex,antigravity). Default: auto-detect')
1819
+ .option('--rounds <N>', 'Number of discussion rounds in spawn mode (default 3, min 2)', '3')
1820
+ .option('--target-duration <seconds>', 'Target duration per round indicated to agents (default 120)', '120')
1821
+ .option('--quorum <N>', 'Advance to next round after N agent responses (default: all)')
1822
+ .option('--model-map <map>', 'Per-persona model overrides, e.g. simplificateur:sonnet,stratege:opus')
1823
+ .option('--metrics', 'Display response timing metrics at end of session')
1824
+ .option('--json', 'Output as JSON')
1825
+ .action(async (topic, options) => {
1826
+ const globalOpts = program.opts();
1827
+ const { runCodev } = await import('./commands/codev.js');
1828
+ runCodev(topic, {
1829
+ ...options,
1830
+ rounds: parseInt(options.rounds, 10),
1831
+ targetDuration: parseInt(options.targetDuration, 10),
1832
+ quorum: options.quorum != null ? parseInt(options.quorum, 10) : undefined,
1833
+ cwd: globalOpts.cwd,
1834
+ });
1835
+ });
1836
+ program
1837
+ .command('codev-metrics <thread>')
1838
+ .description('Show per-agent avg/p95 response metrics for an experimental CoDev thread')
1839
+ .option('--json', 'Output as JSON')
1840
+ .action(async (thread, options) => {
1841
+ const globalOpts = program.opts();
1842
+ const { runCodevMetrics } = await import('./commands/codev.js');
1843
+ runCodevMetrics(thread, { ...options, cwd: globalOpts.cwd });
1844
+ });
1845
+ }
1846
+ // --- run (agent profiles) ---
1847
+ program
1848
+ .command('run [profile-name]')
1849
+ .description('Run an agent profile (list profiles if no name given)')
1850
+ .option('--dry', 'Print the resolved command without executing')
1851
+ .option('--agent <agent>', 'Override the invoke template with a known agent')
1852
+ .action((profileName, options) => {
1853
+ const globalOpts = program.opts();
1854
+ runRunProfile(profileName, { ...options, cwd: globalOpts.cwd });
1170
1855
  });
1171
1856
  program.parseAsync(process.argv).catch((err) => {
1172
1857
  console.error(err);