brainclaw 1.8.0 → 1.9.0

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 (140) hide show
  1. package/README.md +12 -11
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +138 -13
  4. package/dist/commands/add-step.js +1 -1
  5. package/dist/commands/bootstrap.js +2 -26
  6. package/dist/commands/check-security-mcp.js +50 -33
  7. package/dist/commands/check-security.js +86 -43
  8. package/dist/commands/claim.js +22 -21
  9. package/dist/commands/confirm.js +26 -0
  10. package/dist/commands/context-diff.js +1 -1
  11. package/dist/commands/dispatch-watch.js +142 -0
  12. package/dist/commands/doctor.js +113 -2
  13. package/dist/commands/estimation-report.js +115 -16
  14. package/dist/commands/harvest.js +285 -22
  15. package/dist/commands/init.js +123 -21
  16. package/dist/commands/loops-handlers.js +4 -0
  17. package/dist/commands/mcp-read-handlers.js +198 -29
  18. package/dist/commands/mcp.js +588 -92
  19. package/dist/commands/memory.js +21 -17
  20. package/dist/commands/migrate.js +81 -17
  21. package/dist/commands/prune.js +78 -4
  22. package/dist/commands/reflect.js +26 -20
  23. package/dist/commands/register-agent.js +57 -1
  24. package/dist/commands/repair.js +20 -0
  25. package/dist/commands/session-end.js +15 -6
  26. package/dist/commands/session-start.js +18 -1
  27. package/dist/commands/setup-security.js +39 -18
  28. package/dist/commands/setup.js +26 -27
  29. package/dist/commands/stale.js +16 -2
  30. package/dist/commands/uninstall.js +126 -34
  31. package/dist/commands/update-step.js +6 -0
  32. package/dist/commands/worktree.js +60 -0
  33. package/dist/core/actions.js +12 -3
  34. package/dist/core/agent-capability.js +11 -13
  35. package/dist/core/agent-files.js +844 -547
  36. package/dist/core/agent-integrations.js +0 -3
  37. package/dist/core/agent-inventory.js +67 -0
  38. package/dist/core/agent-registry.js +163 -29
  39. package/dist/core/agentrun-reconciler.js +33 -2
  40. package/dist/core/agentruns.js +7 -1
  41. package/dist/core/ai-agent-detection.js +31 -44
  42. package/dist/core/archival.js +15 -9
  43. package/dist/core/assignment-reconciler.js +56 -0
  44. package/dist/core/assignment-sweeper.js +127 -4
  45. package/dist/core/assignments.js +69 -11
  46. package/dist/core/bootstrap.js +233 -67
  47. package/dist/core/brainclaw-version.js +22 -0
  48. package/dist/core/candidates.js +21 -1
  49. package/dist/core/claims.js +313 -150
  50. package/dist/core/config.js +6 -1
  51. package/dist/core/context-diff.js +148 -20
  52. package/dist/core/context.js +129 -8
  53. package/dist/core/coordination.js +22 -3
  54. package/dist/core/dispatch-status.js +79 -5
  55. package/dist/core/dispatcher.js +64 -11
  56. package/dist/core/entity-operations.js +45 -24
  57. package/dist/core/entity-registry.js +31 -5
  58. package/dist/core/event-log.js +138 -21
  59. package/dist/core/events/checkpoint.js +258 -0
  60. package/dist/core/events/genesis.js +220 -0
  61. package/dist/core/events/journal.js +507 -0
  62. package/dist/core/events/materialize.js +126 -0
  63. package/dist/core/events/registry-post-image.js +110 -0
  64. package/dist/core/events/verify.js +109 -0
  65. package/dist/core/execution-adapters.js +23 -0
  66. package/dist/core/facade-schema.js +38 -0
  67. package/dist/core/gc-semantic.js +130 -5
  68. package/dist/core/handoff-snapshot.js +68 -0
  69. package/dist/core/ids.js +19 -8
  70. package/dist/core/instruction-templates.js +34 -115
  71. package/dist/core/io.js +39 -3
  72. package/dist/core/json-store.js +10 -1
  73. package/dist/core/lock.js +153 -28
  74. package/dist/core/loops/bootstrap-acquire.js +25 -1
  75. package/dist/core/loops/facade-schema.js +2 -0
  76. package/dist/core/loops/hooks/survey-signals-baseline.js +36 -0
  77. package/dist/core/loops/index.js +1 -0
  78. package/dist/core/loops/presets/bootstrap.js +7 -0
  79. package/dist/core/loops/store.js +17 -0
  80. package/dist/core/loops/verbs.js +24 -1
  81. package/dist/core/markdown.js +8 -76
  82. package/dist/core/mcp-command-resolution.js +245 -0
  83. package/dist/core/memory-compactor.js +5 -3
  84. package/dist/core/memory-lifecycle.js +282 -0
  85. package/dist/core/merge-risk.js +150 -0
  86. package/dist/core/messaging.js +8 -1
  87. package/dist/core/migration.js +11 -1
  88. package/dist/core/observer-mode.js +26 -0
  89. package/dist/core/operations/memory-mutation.js +90 -65
  90. package/dist/core/operations/plan.js +27 -1
  91. package/dist/core/protocol-skills.js +210 -0
  92. package/dist/core/reflection-safety.js +6 -7
  93. package/dist/core/reputation.js +84 -2
  94. package/dist/core/runtime-signals.js +71 -9
  95. package/dist/core/runtime.js +84 -1
  96. package/dist/core/schema.js +114 -0
  97. package/dist/core/security-detectors.js +125 -0
  98. package/dist/core/security-extract.js +189 -0
  99. package/dist/core/security-guard.js +107 -29
  100. package/dist/core/security-packages.js +121 -0
  101. package/dist/core/security-scoring.js +76 -9
  102. package/dist/core/security.js +34 -2
  103. package/dist/core/sequence.js +11 -2
  104. package/dist/core/setup-flow.js +141 -13
  105. package/dist/core/staleness.js +72 -1
  106. package/dist/core/state.js +250 -54
  107. package/dist/core/store-resolution.js +19 -5
  108. package/dist/core/worktree.js +72 -8
  109. package/dist/facts.js +8 -8
  110. package/dist/facts.json +7 -7
  111. package/docs/PROTOCOL.md +223 -0
  112. package/docs/cli.md +11 -10
  113. package/docs/concepts/coordinator-runbook.md +129 -0
  114. package/docs/concepts/event-log-store-critique-A.md +333 -0
  115. package/docs/concepts/event-log-store-critique-B.md +353 -0
  116. package/docs/concepts/event-log-store-phase0-measurements.md +58 -0
  117. package/docs/concepts/event-log-store-proposal-A.md +365 -0
  118. package/docs/concepts/event-log-store-proposal-B.md +404 -0
  119. package/docs/concepts/event-log-store.md +928 -0
  120. package/docs/concepts/identity-model-proposal.md +371 -0
  121. package/docs/concepts/memory.md +5 -4
  122. package/docs/concepts/observer-protocol.md +361 -0
  123. package/docs/concepts/parallel-merge-protocol.md +71 -0
  124. package/docs/concepts/plans-and-claims.md +43 -0
  125. package/docs/concepts/skills.md +78 -0
  126. package/docs/concepts/workspace-bootstrapping.md +61 -0
  127. package/docs/integrations/agents.md +4 -4
  128. package/docs/integrations/cline.md +10 -11
  129. package/docs/integrations/codex.md +2 -2
  130. package/docs/integrations/continue.md +5 -5
  131. package/docs/integrations/copilot.md +14 -12
  132. package/docs/integrations/openclaw.md +7 -6
  133. package/docs/integrations/overview.md +7 -7
  134. package/docs/integrations/roo.md +3 -3
  135. package/docs/integrations/windsurf.md +6 -6
  136. package/docs/mcp-schema-changelog.md +29 -2
  137. package/docs/quickstart.md +48 -47
  138. package/docs/security.md +174 -15
  139. package/docs/storage.md +4 -2
  140. package/package.json +8 -6
package/README.md CHANGED
@@ -65,17 +65,17 @@ brainclaw is designed to sit alongside the coding agents teams are already using
65
65
  | Logo | Agent | Tier | What brainclaw configures |
66
66
  |---|---|---|---|
67
67
  | [![Claude Code](https://img.shields.io/badge/Claude_Code-111111?logo=anthropic&logoColor=white)](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** | A | MCP + CLAUDE.md + hooks + auto-approve + permissions + /brainclaw skill |
68
- | [![Codex](https://img.shields.io/badge/Codex-111111?logo=openai&logoColor=white)](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** | A | MCP + AGENTS.md + hooks + skills |
68
+ | [![Codex](https://img.shields.io/badge/Codex-111111?logo=openai&logoColor=white)](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** | A | MCP + AGENTS.md + skills |
69
69
  | [![Cursor](https://img.shields.io/badge/Cursor-1F2430?logo=cursor&logoColor=white)](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** | A | MCP (machine) + .cursor/rules/ + hooks + skills |
70
- | [![Windsurf](https://img.shields.io/badge/Windsurf-0B1220?logo=codeium&logoColor=white)](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** | A | MCP (machine) + .windsurfrules + hooks + skills |
71
- | [![Cline](https://img.shields.io/badge/Cline-0F766E?logoColor=white)](https://github.com/cline/cline) | **[Cline](https://github.com/cline/cline)** | A | MCP + auto-approve + .clinerules/ + hooks + skills |
70
+ | [![Windsurf](https://img.shields.io/badge/Windsurf-0B1220?logo=codeium&logoColor=white)](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** | A | MCP (machine) + .windsurfrules + .windsurf/rules/ |
71
+ | [![Cline](https://img.shields.io/badge/Cline-0F766E?logoColor=white)](https://github.com/cline/cline) | **[Cline](https://github.com/cline/cline)** | A | MCP + auto-approve + .clinerules/ |
72
72
  | [![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-181717?logo=githubcopilot&logoColor=white)](https://github.com/features/copilot) | **[GitHub Copilot](https://github.com/features/copilot)** | A | MCP + copilot-instructions.md + hooks + skills |
73
73
  | [![Roo](https://img.shields.io/badge/Roo-7C3AED?logoColor=white)](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** | B | MCP + auto-approve + .roo/rules/ |
74
74
  | [![Continue](https://img.shields.io/badge/Continue-2563EB?logoColor=white)](https://github.com/continuedev/continue) | **[Continue](https://github.com/continuedev/continue)** | B | MCP + .continue/rules/ |
75
75
  | [![OpenCode](https://img.shields.io/badge/OpenCode-0F172A?logoColor=white)](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** | B | MCP + AGENTS.md |
76
76
  | [![Gemini CLI](https://img.shields.io/badge/Gemini_CLI-1A73E8?logo=googlegemini&logoColor=white)](https://github.com/google-gemini/gemini-cli) | **[Antigravity / Gemini CLI](https://github.com/google-gemini/gemini-cli)** | B | MCP + GEMINI.md |
77
77
 
78
- **Tier A** = MCP + hooks + skills (context injected dynamically, lightweight instruction files). **Tier B** = MCP only, no hooks (richer static instruction files with architecture + top traps). Tier can degrade at runtime if integration surfaces are missing.
78
+ **Tier A** = strongest supported integration for that agent family (usually MCP plus native files, and hooks/skills where the agent exposes them). **Tier B** = MCP/native-file integration with fewer automation surfaces. Tier can degrade at runtime if integration surfaces are missing.
79
79
 
80
80
  ### Autonomous Agents
81
81
 
@@ -96,11 +96,11 @@ brainclaw is most effective today when one agent works at a time in a given chec
96
96
 
97
97
  ## Platform Support
98
98
 
99
- brainclaw declares support for Node.js 20+ in `package.json`, and CI actively exercises Node 20, 22, and 24 across the main Linux path (Windows runs on Node 24). Real-world support is still not perfectly even yet.
99
+ brainclaw declares support for Node.js 20+ in `package.json` (`engines.node = ">=20.0.0"`). CI actively exercises Node 22 (Active LTS) and Node 24 (current LTS) on Linux; Windows runs on Node 24. Node 20 still works as a minimum runtime but is no longer CI-verified — it reached EOL in April 2026 and was removed from GitHub-hosted runners. The recommended runtime is Node 22 LTS or Node 24 LTS. Real-world support is still not perfectly even yet.
100
100
 
101
101
  | Logo | Platform | Status today | Notes |
102
102
  |---|---|---|---|
103
- | [![Linux](https://img.shields.io/badge/Linux-111111?logo=linux&logoColor=white)](https://www.kernel.org/) | **[Linux](https://www.kernel.org/)** | Recommended | best-supported environment today; GitHub CI runs on Ubuntu with Node 20, 22, and 24 |
103
+ | [![Linux](https://img.shields.io/badge/Linux-111111?logo=linux&logoColor=white)](https://www.kernel.org/) | **[Linux](https://www.kernel.org/)** | Recommended | best-supported environment today; GitHub CI runs on Ubuntu with Node 22 and 24 |
104
104
  | [![macOS](https://img.shields.io/badge/macOS-000000?logo=apple&logoColor=white)](https://www.apple.com/macos/) | **[macOS](https://www.apple.com/macos/)** | Likely supported | Unix-like path and shell model should map well, but it is less exercised than Linux |
105
105
  | [![Windows](https://img.shields.io/badge/Windows-0078D4?logo=windows&logoColor=white)](https://www.microsoft.com/windows/) | **[Windows](https://www.microsoft.com/windows/)** | Supported with caveats | native support exists, but PATH, npm, SSH, and PowerShell quoting still create more friction than on Unix systems |
106
106
  | [![Windows + WSL2](https://img.shields.io/badge/Windows%20%2B%20WSL2-0078D4?logo=windows&logoColor=white)](https://learn.microsoft.com/windows/wsl/) | **[Windows + WSL2](https://learn.microsoft.com/windows/wsl/)** | Important, still maturing | Brainclaw detects this setup explicitly, but setup/install/store parity across Windows and WSL is not fully seamless yet |
@@ -232,7 +232,7 @@ Still sharp:
232
232
  1. **Same-checkout concurrent edits** — running two agents in the *same* working tree (no per-claim worktree) is still the wrong answer. Use the dispatch path (auto-worktree per claim) instead of raw concurrent CLI sessions.
233
233
  2. **Cross-machine sync** — federation across machines is on the roadmap, not in v1.x. Today brainclaw's store is local and one-machine-per-project.
234
234
  3. **Spawn-and-forget assumptions** — spawned workers don't always commit their work cleanly. The brief-ack file confirms the spawn started; in the worst case the coordinator harvests open changes.
235
- 4. **Live state for hook-less agents** — Tier B/C agents without lifecycle hooks (Cursor, Cline, Windsurf, Copilot, Continue, Kilocode, Mistral Vibe, Hermes) get live context via `.live.md` companions regenerated on session-end and handoff, not via real-time push.
235
+ 4. **Live state for hook-less agents** — supported hook-less file surfaces such as Cline, Windsurf, Continue, Antigravity/Gemini CLI, and Mistral Vibe can get live context via `.live.md` companions regenerated on session-end and handoff, not via real-time push.
236
236
 
237
237
  Recommended use today:
238
238
 
@@ -334,9 +334,10 @@ If you are integrating Brainclaw into an agent workflow, start with the agent-fa
334
334
  Contributor note: the commands below are for developing Brainclaw itself, not for normal agent usage inside a target repo.
335
335
 
336
336
  ```bash
337
- npm test # unit + smoke (fast path)
338
- npm run test:e2e # full suite
339
- npm run test:coverage # with coverage report
337
+ npm test # unit + smoke (fast path)
338
+ npm run test:e2e # end-to-end suite
339
+ npm run test:all # full suite
340
+ npm run test:coverage # with coverage report
340
341
  ```
341
342
 
342
343
  ---
@@ -459,7 +460,7 @@ For older releases (v0.x and the early v1.0 launch series), `git log` on `master
459
460
 
460
461
  ### v1.1.0
461
462
 
462
- - **Node 20+ baseline** (pln#485) — `engines.node` is now `>=20.0.0` (Node 18 reached EOL in April 2025). CI matrix runs Node 20, 22, and 24 on Linux; Windows on Node 24.
463
+ - **Node 20+ baseline** (pln#485) — `engines.node` is now `>=20.0.0` (Node 18 reached EOL in April 2025). CI matrix runs Node 22 and 24 on Linux; Windows on Node 24. Node 20 remains the minimum installable runtime but is no longer CI-verified.
463
464
  - **commander 13 → 14** (requires Node 20+).
464
465
  - **@types/node 22 → 24** (LTS-aligned).
465
466
 
Binary file
package/dist/cli.js CHANGED
@@ -63,7 +63,7 @@ import { runCheckConstraints } from './commands/check-constraints.js';
63
63
  import { runCheckPolicy } from './commands/check-policy.js';
64
64
  import { runCheckSecurity } from './commands/check-security.js';
65
65
  import { runSetupSecurity } from './commands/setup-security.js';
66
- import { runRegisterAgent } from './commands/register-agent.js';
66
+ import { runRegisterAgent, runRemoveAgent } from './commands/register-agent.js';
67
67
  import { runEnableAgent } from './commands/enable-agent.js';
68
68
  import { runVersion } from './commands/version.js';
69
69
  import { runReleaseNotes } from './commands/release-notes.js';
@@ -80,6 +80,7 @@ import { runExport, runRefresh } from './commands/export.js';
80
80
  import { runHooks } from './commands/hooks.js';
81
81
  import { runWatch } from './commands/watch.js';
82
82
  import { runDispatchAnalysis, runDispatch, runDispatchReview } from './commands/dispatch.js';
83
+ import { runDispatchWatch } from './commands/dispatch-watch.js';
83
84
  import { runInboxList, runInboxAck, runInboxArchive, runInboxSend, runInboxThread } from './commands/inbox.js';
84
85
  import { runMetrics } from './commands/metrics.js';
85
86
  import { runRollback } from './commands/rollback.js';
@@ -98,7 +99,7 @@ import { initLogLevel, logger } from './core/logger.js';
98
99
  import { resolveEffectiveCwd } from './core/store-resolution.js';
99
100
  import { resolveProjectCwd } from './core/cross-project.js';
100
101
  import { runSwitch } from './commands/switch.js';
101
- import { runWorktreeCreate, runWorktreeList, runWorktreeRemove, runWorktreePrune, runWorktreeClean, runWorktreeMerge } from './commands/worktree.js';
102
+ import { runWorktreeCreate, runWorktreeList, runWorktreeRemove, runWorktreePrune, runWorktreeClean, runWorktreeMerge, runWorktreeCheck } from './commands/worktree.js';
102
103
  import { runCheckEvents } from './commands/check-events.js';
103
104
  import { runDiscover } from './commands/discover.js';
104
105
  import { runMigrate } from './commands/migrate.js';
@@ -148,11 +149,72 @@ function parseLeadingGlobalOptions(argv) {
148
149
  }
149
150
  return result;
150
151
  }
152
+ function trailingGlobalOptionError(argv, actionCommand) {
153
+ let firstCommandIndex = -1;
154
+ for (let i = 0; i < argv.length; i++) {
155
+ const token = argv[i];
156
+ if (token === '--')
157
+ break;
158
+ if (!token.startsWith('-')) {
159
+ firstCommandIndex = i;
160
+ break;
161
+ }
162
+ if (token === '--cwd' || token === '--project')
163
+ i++;
164
+ }
165
+ if (firstCommandIndex < 0)
166
+ return undefined;
167
+ for (let i = firstCommandIndex + 1; i < argv.length; i++) {
168
+ const token = argv[i];
169
+ if (token === '--')
170
+ break;
171
+ const long = token.includes('=') ? token.slice(0, token.indexOf('=')) : token;
172
+ if (!['--cwd', '--project', '--verbose', '--debug'].includes(long))
173
+ continue;
174
+ const isLocalOption = actionCommand.options.some((option) => option.long === long);
175
+ if (!isLocalOption) {
176
+ const valueHint = token === long && (long === '--cwd' || long === '--project') ? ' <value>' : '';
177
+ return `Global option ${long} must appear before the subcommand. Use: brainclaw ${long}${valueHint} <command> ...`;
178
+ }
179
+ }
180
+ return undefined;
181
+ }
182
+ /**
183
+ * Resolve the (possibly nested) subcommand named in argv without parsing.
184
+ * Used to run the trailing-global-option guard BEFORE Commander parses:
185
+ * with positional options enabled, Commander would otherwise reject a
186
+ * trailing --cwd/--project as "unknown option" before any hook runs.
187
+ */
188
+ function findCommandFromArgv(argv) {
189
+ let current = program;
190
+ let found;
191
+ for (let i = 0; i < argv.length; i++) {
192
+ const token = argv[i];
193
+ if (token === '--')
194
+ break;
195
+ if (token.startsWith('-')) {
196
+ if (!found && (token === '--cwd' || token === '--project'))
197
+ i++;
198
+ continue;
199
+ }
200
+ const sub = current.commands.find((c) => c.name() === token || c.aliases().includes(token));
201
+ if (!sub)
202
+ break;
203
+ found = sub;
204
+ current = sub;
205
+ }
206
+ return found;
207
+ }
151
208
  function isCodevEnabled() {
152
209
  return process.env.BRAINCLAW_ENABLE_CODEV === '1';
153
210
  }
154
211
  program
155
212
  .name('brainclaw')
213
+ // Stop the program-level parser from consuming options that appear AFTER the
214
+ // subcommand: `instruction … --project auth` must reach the subcommand's own
215
+ // --project, not the global routing flag (which is leading-only by contract —
216
+ // see parseLeadingGlobalOptions + trailingGlobalOptionError).
217
+ .enablePositionalOptions()
156
218
  .description('Shared project memory for humans and coding agents.')
157
219
  .version(getInstalledBrainclawVersion())
158
220
  .option('--verbose', 'Show info-level log messages on stderr')
@@ -570,8 +632,10 @@ program
570
632
  .command('add-step <planId> <text>')
571
633
  .description('Add an optional step to a plan item')
572
634
  .option('--assign <assignee>', 'Assign this step to an agent or person')
635
+ .option('--estimate <minutes>', 'Step-level estimate (minutes, or a duration like "2h"/"30m")')
636
+ .option('--actual-effort <effort>', 'Step-level actual effort (e.g. "45m", "2h")')
573
637
  .action((planId, text, options) => {
574
- runAddStep(planId, text, { assignee: options.assign });
638
+ runAddStep(planId, text, { assignee: options.assign, estimatedEffort: options.estimate, actualEffort: options.actualEffort });
575
639
  });
576
640
  // --- complete-step ---
577
641
  program
@@ -583,10 +647,12 @@ program
583
647
  // --- update-step ---
584
648
  program
585
649
  .command('update-step <planId> <stepId>')
586
- .description('Update a plan step (status, text, assignee)')
650
+ .description('Update a plan step (status, text, assignee, effort)')
587
651
  .option('--status <status>', 'New status: todo, in_progress, testing, done, blocked')
588
652
  .option('--text <text>', 'Replace step description')
589
653
  .option('--assign <assignee>', 'Assign the step (empty string to unassign)')
654
+ .option('--estimate <minutes>', 'Step-level estimate (minutes, or a duration like "2h"/"30m")')
655
+ .option('--actual-effort <effort>', 'Step-level actual effort (e.g. "45m", "2h")')
590
656
  .action((planId, stepId, options) => {
591
657
  runUpdateStep(planId, stepId, options);
592
658
  });
@@ -603,8 +669,13 @@ program
603
669
  .description('Show estimation accuracy report for completed plans')
604
670
  .option('--agent <name>', 'Filter by agent/author name')
605
671
  .option('--json', 'Output as JSON')
672
+ .option('--outlier-threshold <minutes>', 'Drop plan-wallclock actuals over N minutes from the stats (default 1440 = 24h; 0 disables)')
606
673
  .action((options) => {
607
- runEstimationReport(options);
674
+ runEstimationReport({
675
+ agent: options.agent,
676
+ json: options.json,
677
+ outlierThresholdMinutes: options.outlierThreshold !== undefined ? Number(options.outlierThreshold) : undefined,
678
+ });
608
679
  });
609
680
  // --- update-plan ---
610
681
  program
@@ -681,6 +752,7 @@ program
681
752
  .option('--repair', 'Rebuild dist/ when the MCP runtime is missing or stale')
682
753
  .option('--after-migration', 'Run the v1.0 post-migration health check only (exits non-zero on any failure)')
683
754
  .option('--dispatch', 'Run dispatch-health diagnostic only: reconcile open agent_runs and report stuck/unverified/silent failures (pln#496 step stp_8c072d75)')
755
+ .option('--verify-journal', 'Phase-2 cutover gate (pln#565): rebuild state from the event journal and diff vs live projections; exits non-zero on any drift')
684
756
  .option('--spawn-check', 'Real spawn round-trip per installed agent before dispatch (pln#520 step 2): validates delivery + handshake on this host, exits non-zero on any installed-agent failure')
685
757
  .option('--spawn-check-timeout <ms>', 'Per-agent timeout for --spawn-check (default 15000)', parseInt)
686
758
  .action(async (options) => {
@@ -688,7 +760,7 @@ program
688
760
  await runDoctorSpawnCheck({ cwd: options.cwd, json: options.json, timeoutMs: options.spawnCheckTimeout });
689
761
  return;
690
762
  }
691
- runDoctor({ ...options, afterMigration: options.afterMigration, dispatch: options.dispatch });
763
+ runDoctor({ ...options, afterMigration: options.afterMigration, dispatch: options.dispatch, verifyJournal: options.verifyJournal });
692
764
  });
693
765
  // --- repair (Phase 4 Sprint 2 Lane C / pln#397) ---
694
766
  program
@@ -901,8 +973,14 @@ program
901
973
  .option('--generate-fingerprint', 'Generate or rotate a local public identity fingerprint for this agent')
902
974
  .option('--set-current', 'Set this identity as the current agent in config')
903
975
  .option('--curator', 'Register this agent as a curator (project owner with direct-write access)')
976
+ .option('--remove', 'Remove this identity instead of registering (guarded: debris identities only unless --force)')
977
+ .option('--force', 'With --remove: allow removing a non-debris identity')
904
978
  .option('--json', 'Output as JSON')
905
979
  .action((name, options) => {
980
+ if (options.remove) {
981
+ runRemoveAgent(name, { force: options.force, json: options.json });
982
+ return;
983
+ }
906
984
  runRegisterAgent(name, options);
907
985
  });
908
986
  // --- enable-agent ---
@@ -1015,6 +1093,8 @@ program
1015
1093
  .description('Harvest a worker LANE-RESULT.json from its worktree into the project (pass an assignment id, or --all)')
1016
1094
  .option('--all', 'Harvest every lane result found across worktrees')
1017
1095
  .option('--integrate', 'Worktree-as-contract (pln#534): commit the worktree diff on behalf of a sandboxed worker, lifecycle the assignment, and release the claim')
1096
+ .option('--orphaned', 'Recover a dead worker that left NO lane-result (pln#554): typecheck + commit the worktree on behalf, lifecycle, release. Never deletes or resets anything')
1097
+ .option('--base <ref>', 'Base ref for --orphaned commits-ahead comparison (default: master)')
1018
1098
  .option('--dry-run', 'Preview without writing events/markers')
1019
1099
  .option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
1020
1100
  .option('--json', 'Output as JSON')
@@ -1237,13 +1317,19 @@ program
1237
1317
  program
1238
1318
  .command('check-security')
1239
1319
  .description('Check supply chain security scores for packages via Socket.dev')
1240
- .requiredOption('--packages <names>', 'Comma-separated package names (e.g. "axios,express" or "axios@1.14.1")')
1320
+ .option('--packages <names>', 'Comma-separated package names (e.g. "axios,express" or "axios@1.14.1")')
1321
+ .option('--requirements <file>', 'Path to a pip-style requirements.txt to scan')
1322
+ .option('--lockfile <file>', 'Path to a package-lock.json (npm) to scan top-level deps')
1241
1323
  .option('--ecosystem <type>', 'Package ecosystem: npm or pypi', 'npm')
1324
+ .option('--mode <mode>', 'Override security mode: advisory or enforced (defaults to config)')
1242
1325
  .option('--json', 'Output as JSON')
1243
1326
  .action(async (options) => {
1244
1327
  await runCheckSecurity({
1245
1328
  packages: options.packages,
1329
+ requirements: options.requirements,
1330
+ lockfile: options.lockfile,
1246
1331
  ecosystem: options.ecosystem,
1332
+ mode: options.mode,
1247
1333
  json: options.json,
1248
1334
  });
1249
1335
  });
@@ -1339,7 +1425,7 @@ program
1339
1425
  .option('--reflect-handoff', 'Materialize an open handoff from git commits since session start')
1340
1426
  .option('--dispatch-review', 'When used with --reflect-handoff, auto-dispatch a code review if the handoff is reviewable')
1341
1427
  .option('--reviewer <name>', 'Explicit reviewer to route the reflected handoff review to')
1342
- .option('--reflect', 'Include structured reflection questions for the agent to answer')
1428
+ .option('--no-reflect', 'Suppress the dogfooding reflection prompt (project + your surfaces/skills/tools), shown by default')
1343
1429
  .option('--json', 'Output as JSON')
1344
1430
  .action(async (options) => {
1345
1431
  await runSessionEnd({
@@ -1388,7 +1474,7 @@ program
1388
1474
  program
1389
1475
  .command('export')
1390
1476
  .description('Export memory as instructions for IDE/AI tools')
1391
- .option('--format <format>', 'Format: copilot-instructions, cursor-rules, agents-md, claude-md, gemini-md, windsurf, cline, roo, continue')
1477
+ .option('--format <format>', 'Format: copilot-instructions, cursor-rules, agents-md, claude-md, gemini-md, windsurf, cline, roo, continue, openclaw, nanoclaw, nemoclaw, picoclaw, zeroclaw')
1392
1478
  .option('--detect', 'Auto-detect agent environment and write to its native file')
1393
1479
  .option('--all', 'Write all known agent instruction files at once (claude-md, agents-md, copilot-instructions, cursor-rules, etc.)')
1394
1480
  .option('--write', 'Write to canonical file path instead of stdout (when --format is given); local files are gitignored by default')
@@ -1466,8 +1552,8 @@ dispatchCmd
1466
1552
  .option('--spawn', 'Autonomously launch CLI agents with invoke templates')
1467
1553
  .option('--agent <name>', 'Dispatcher agent name')
1468
1554
  .option('--json', 'Output as JSON')
1469
- .action((options) => {
1470
- runDispatch({
1555
+ .action(async (options) => {
1556
+ await runDispatch({
1471
1557
  agents: options.agents,
1472
1558
  lanes: options.lanes,
1473
1559
  max: options.max,
@@ -1479,6 +1565,21 @@ dispatchCmd
1479
1565
  json: options.json,
1480
1566
  });
1481
1567
  });
1568
+ dispatchCmd
1569
+ .command('watch <target>')
1570
+ .description('Block until a dispatched worker reaches a terminal state (asgn_/clm_/run_ id) — sentinels, lane-result, committed-clean and worker-process-gone heuristics')
1571
+ .option('--interval <seconds>', 'Poll interval in seconds (default 60)', parseInt)
1572
+ .option('--timeout <minutes>', 'Give up after N minutes (default 90, exit code 2)', parseInt)
1573
+ .option('--base <ref>', 'Base ref for commits-ahead evidence (default master)')
1574
+ .option('--json', 'One JSON object per poll line')
1575
+ .action(async (target, options) => {
1576
+ await runDispatchWatch(target, {
1577
+ intervalSeconds: options.interval,
1578
+ timeoutMinutes: options.timeout,
1579
+ base: options.base,
1580
+ json: options.json,
1581
+ });
1582
+ });
1482
1583
  dispatchCmd
1483
1584
  .command('review')
1484
1585
  .description('Dispatch code reviews for completed handoffs')
@@ -1698,9 +1799,10 @@ program
1698
1799
  .command('migrate')
1699
1800
  .description('Migrate memory items between stores (e.g. promote machine-scoped items to user store)')
1700
1801
  .option('--promote-machine-items', 'Move items with scope:machine from project store to user store (~/.brainclaw/)')
1701
- .option('--dry-run', 'Show what would be moved without actually moving')
1802
+ .option('--enable-journal', 'Turn on the event journal (mode=dual) for this existing store and backfill it (pln#567)')
1803
+ .option('--dry-run', 'Show what would be done without writing')
1702
1804
  .action((options) => {
1703
- runMigrate({ promoteMachineItems: options.promoteMachineItems, dryRun: options.dryRun });
1805
+ runMigrate({ promoteMachineItems: options.promoteMachineItems, enableJournal: options.enableJournal, dryRun: options.dryRun });
1704
1806
  });
1705
1807
  program
1706
1808
  .command('switch [project]')
@@ -1781,6 +1883,15 @@ worktreeCmd
1781
1883
  const globalOpts = program.opts();
1782
1884
  runWorktreeMerge({ branch, message: options.message, dryRun: options.dryRun, cwd: globalOpts.cwd });
1783
1885
  });
1886
+ worktreeCmd
1887
+ .command('check')
1888
+ .description('Pre-merge conflict detection: which parallel lanes touch overlapping files, and who owns them (exit 3 if overlaps found)')
1889
+ .option('--base <ref>', 'Base ref each lane is diffed against (default: current branch)')
1890
+ .option('--json', 'Emit the full risk report as JSON')
1891
+ .action((options) => {
1892
+ const globalOpts = program.opts();
1893
+ runWorktreeCheck({ baseRef: options.base, json: options.json, cwd: globalOpts.cwd });
1894
+ });
1784
1895
  // --- federation cloud ---
1785
1896
  const federationCmd = program
1786
1897
  .command('federation')
@@ -2023,6 +2134,20 @@ program
2023
2134
  const globalOpts = program.opts();
2024
2135
  runRunProfile(profileName, { ...options, cwd: globalOpts.cwd });
2025
2136
  });
2137
+ {
2138
+ // Friendly trailing-global-option error must run before Commander parses:
2139
+ // with positional options enabled, Commander itself would reject a trailing
2140
+ // --cwd/--project as a bare "unknown option" otherwise.
2141
+ const argvTail = process.argv.slice(2);
2142
+ const guardCommand = findCommandFromArgv(argvTail);
2143
+ if (guardCommand) {
2144
+ const trailingError = trailingGlobalOptionError(argvTail, guardCommand);
2145
+ if (trailingError) {
2146
+ console.error(`Error: ${trailingError}`);
2147
+ process.exit(1);
2148
+ }
2149
+ }
2150
+ }
2026
2151
  program.parseAsync(process.argv).catch((err) => {
2027
2152
  console.error(err);
2028
2153
  process.exit(1);
@@ -5,7 +5,7 @@ export function runAddStep(planId, text, options = {}) {
5
5
  requireInitialized(process.cwd());
6
6
  validateCliInput(text);
7
7
  try {
8
- const result = addStep({ planId, text, assignee: options.assignee });
8
+ const result = addStep({ planId, text, assignee: options.assignee, estimatedEffort: options.estimatedEffort, actualEffort: options.actualEffort });
9
9
  console.log(`✔ Step added: [${result.stepId}] ${text}`);
10
10
  console.log(` Plan [${result.planId}] progress: ${result.doneSteps}/${result.totalSteps} steps done`);
11
11
  }
@@ -1,9 +1,8 @@
1
1
  import fs from 'node:fs';
2
- import readline from 'node:readline/promises';
3
- import { stdin as input, stdout as output } from 'node:process';
4
2
  import { memoryExists } from '../core/io.js';
5
3
  import { applyBootstrapImport, renderBootstrapInterview, renderBootstrapSummary, runBootstrapProfile, uninstallBootstrapImport, } from '../core/bootstrap.js';
6
4
  import { BootstrapInterviewAnswerSchema } from '../core/schema.js';
5
+ import { confirmAction } from './confirm.js';
7
6
  export async function runBootstrap(options = {}) {
8
7
  const cwd = options.cwd ?? process.cwd();
9
8
  if (!memoryExists(cwd)) {
@@ -92,28 +91,5 @@ function loadBootstrapInterviewAnswers(filepath) {
92
91
  }
93
92
  return parsed.map((entry) => BootstrapInterviewAnswerSchema.parse(entry));
94
93
  }
95
- async function confirmBootstrapAction(question, yes) {
96
- if (yes) {
97
- return;
98
- }
99
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
100
- console.error(`Error: ${question} Re-run with --yes in non-interactive mode.`);
101
- process.exit(1);
102
- }
103
- const rl = readline.createInterface({ input, output });
104
- try {
105
- const answer = await rl.question(`${question} [y/N] `);
106
- if (answer.trim().toLowerCase() !== 'y') {
107
- console.error('Cancelled.');
108
- process.exit(1);
109
- }
110
- }
111
- catch (error) {
112
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
113
- process.exit(1);
114
- }
115
- finally {
116
- rl.close();
117
- }
118
- }
94
+ const confirmBootstrapAction = confirmAction;
119
95
  //# sourceMappingURL=bootstrap.js.map
@@ -2,7 +2,9 @@ import { memoryExists, memoryPath } from '../core/io.js';
2
2
  import { loadConfig } from '../core/config.js';
3
3
  import { SecurityCache } from '../core/security-cache.js';
4
4
  import { querySocketScores } from '../core/socket-client.js';
5
- import { evaluateBatch, worstDecision } from '../core/security-scoring.js';
5
+ import { applyMode, evaluateBatch, worstDecision, } from '../core/security-scoring.js';
6
+ import { parsePackageSpec } from '../core/security-packages.js';
7
+ import { collectPackages } from '../core/security-extract.js';
6
8
  import { createTrap } from '../core/operations/memory-write.js';
7
9
  export async function handleCheckSecurity(args, cwd) {
8
10
  if (!memoryExists(cwd)) {
@@ -13,20 +15,35 @@ export async function handleCheckSecurity(args, cwd) {
13
15
  if (!preinstall?.enabled) {
14
16
  return { content: [{ type: 'text', text: 'Security preinstall checks are not enabled. Set security.preinstall.enabled: true in config.yaml.' }] };
15
17
  }
16
- const packagesStr = String(args.packages ?? '').trim();
17
- if (!packagesStr) {
18
- return { content: [{ type: 'text', text: 'Error: missing required argument: packages' }] };
19
- }
20
18
  const ecosystem = (String(args.ecosystem ?? 'npm').trim());
21
- const packageNames = packagesStr.split(',').map(p => p.trim()).filter(Boolean);
19
+ const modeArg = args.mode ? String(args.mode).trim() : undefined;
20
+ const effectiveMode = (modeArg === 'enforced' || modeArg === 'advisory')
21
+ ? modeArg
22
+ : (preinstall.mode ?? 'advisory');
23
+ let packageSpecs;
24
+ try {
25
+ packageSpecs = collectPackages({
26
+ packages: args.packages ? String(args.packages) : undefined,
27
+ requirements: args.requirements ? String(args.requirements) : undefined,
28
+ lockfile: args.lockfile ? String(args.lockfile) : undefined,
29
+ defaultEcosystem: ecosystem,
30
+ });
31
+ }
32
+ catch (err) {
33
+ const msg = err instanceof Error ? err.message : String(err);
34
+ return { content: [{ type: 'text', text: `Error: ${msg}` }] };
35
+ }
36
+ if (packageSpecs.length === 0) {
37
+ return { content: [{ type: 'text', text: 'Error: no packages specified (pass --packages, --requirements, or --lockfile).' }] };
38
+ }
22
39
  // Build cache
23
40
  const cachePath = memoryPath('security/cache.json', cwd);
24
41
  const cache = new SecurityCache(cachePath, preinstall.cache_ttl_hours);
25
42
  // Separate cached vs uncached
26
43
  const queries = [];
27
44
  const cachedScores = [];
28
- for (const name of packageNames) {
29
- const [depname, version] = parsePackageName(name);
45
+ for (const spec of packageSpecs) {
46
+ const { depname, version } = parsePackageSpec(spec);
30
47
  const cached = cache.get(ecosystem, depname, version);
31
48
  if (cached) {
32
49
  cachedScores.push(cached);
@@ -55,25 +72,34 @@ export async function handleCheckSecurity(args, cwd) {
55
72
  const allScores = [...cachedScores, ...fetchedScores];
56
73
  if (allScores.length === 0 && fetchError) {
57
74
  const fallback = preinstall.fallback_on_error;
75
+ const fallbackVerdict = fallback === 'block' ? 'block' : fallback === 'warn' ? 'warn' : 'pass';
76
+ const effective = applyMode(fallbackVerdict, effectiveMode);
58
77
  return {
59
- content: [{ type: 'text', text: `Socket MCP error: ${fetchError}\nFallback policy: ${fallback}` }],
60
- structuredContent: { error: fetchError, fallback, decision: fallback === 'block' ? 'block' : fallback === 'warn' ? 'warn' : 'pass' },
78
+ content: [{ type: 'text', text: `Socket MCP error: ${fetchError}\nFallback policy: ${fallback} (mode=${effectiveMode}) → ${effective.toUpperCase()}` }],
79
+ structuredContent: {
80
+ error: fetchError,
81
+ fallback,
82
+ mode: effectiveMode,
83
+ decision: fallbackVerdict,
84
+ effective_decision: effective,
85
+ },
61
86
  };
62
87
  }
63
88
  const verdicts = evaluateBatch(allScores, preinstall);
64
- const worst = worstDecision(verdicts);
89
+ const intrinsic = worstDecision(verdicts);
90
+ const effective = applyMode(intrinsic, effectiveMode);
65
91
  // Build text output
66
92
  const lines = [];
67
93
  if (fetchError) {
68
- lines.push(`\u26A0 Socket MCP partial error: ${fetchError} (${cachedScores.length} results from cache)`);
94
+ lines.push(`⚠ Socket MCP partial error: ${fetchError} (${cachedScores.length} results from cache)`);
69
95
  lines.push('');
70
96
  }
71
97
  for (const v of verdicts) {
72
- const icon = v.decision === 'pass' ? '\u2705' : v.decision === 'warn' ? '\u26A0\uFE0F' : '\uD83D\uDED1';
73
- lines.push(`${icon} ${v.ecosystem}/${v.package}@${v.version} \u2014 composite=${v.composite} [${v.decision.toUpperCase()}]`);
98
+ const icon = v.decision === 'pass' ? '' : v.decision === 'warn' ? '⚠️' : '🛑';
99
+ lines.push(`${icon} ${v.ecosystem}/${v.package}@${v.version} composite=${v.composite} [${v.decision.toUpperCase()}]`);
74
100
  lines.push(` SC=${v.scores.supplyChain} vuln=${v.scores.vulnerability} qual=${v.scores.quality} maint=${v.scores.maintenance} lic=${v.scores.license}`);
75
101
  for (const r of v.reasons) {
76
- lines.push(` \u2192 ${r}`);
102
+ lines.push(` ${r}`);
77
103
  }
78
104
  }
79
105
  // Auto-create traps for WARN and BLOCK verdicts
@@ -102,7 +128,12 @@ export async function handleCheckSecurity(args, cwd) {
102
128
  lines.push(`Created ${trapsCreated.length} security trap(s): ${trapsCreated.join(', ')}`);
103
129
  }
104
130
  lines.push('');
105
- lines.push(`Overall decision: ${worst.toUpperCase()}`);
131
+ if (intrinsic !== effective) {
132
+ lines.push(`Verdict: ${intrinsic.toUpperCase()} — downgraded to ${effective.toUpperCase()} by mode=${effectiveMode}.`);
133
+ }
134
+ else {
135
+ lines.push(`Verdict: ${intrinsic.toUpperCase()} (mode=${effectiveMode})`);
136
+ }
106
137
  return {
107
138
  content: [{ type: 'text', text: lines.join('\n') }],
108
139
  structuredContent: {
@@ -121,25 +152,11 @@ export async function handleCheckSecurity(args, cwd) {
121
152
  license: v.scores.license,
122
153
  },
123
154
  })),
124
- decision: worst,
155
+ decision: intrinsic,
156
+ effective_decision: effective,
157
+ mode: effectiveMode,
125
158
  ...(fetchError ? { fetch_error: fetchError } : {}),
126
159
  },
127
160
  };
128
161
  }
129
- function parsePackageName(name) {
130
- // Handle scoped packages: @scope/pkg@version
131
- if (name.startsWith('@')) {
132
- const lastAt = name.lastIndexOf('@', name.length - 1);
133
- if (lastAt > 0) {
134
- return [name.slice(0, lastAt), name.slice(lastAt + 1)];
135
- }
136
- return [name, 'latest'];
137
- }
138
- // Regular: pkg@version
139
- if (name.includes('@')) {
140
- const [depname, version] = name.split('@');
141
- return [depname, version || 'latest'];
142
- }
143
- return [name, 'latest'];
144
- }
145
162
  //# sourceMappingURL=check-security-mcp.js.map