brainclaw 1.14.0 → 1.16.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.
- package/README.md +16 -263
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli/register-capture.js +209 -0
- package/dist/cli/register-code-map.js +19 -0
- package/dist/cli/register-coordination.js +472 -0
- package/dist/cli/register-federation.js +258 -0
- package/dist/cli/register-lifecycle.js +436 -0
- package/dist/cli/register-memory-context.js +502 -0
- package/dist/cli/register-planning.js +167 -0
- package/dist/cli/register-review.js +149 -0
- package/dist/cli/shared.js +5 -0
- package/dist/cli.js +212 -2015
- package/dist/commands/dispatch-watch.js +25 -2
- package/dist/commands/harvest.js +31 -6
- package/dist/commands/mcp-catalog.js +1438 -0
- package/dist/commands/mcp-contract.js +33 -0
- package/dist/commands/mcp-presentation.js +27 -0
- package/dist/commands/mcp-read-handlers.js +72 -36
- package/dist/commands/mcp-write-admin.js +328 -0
- package/dist/commands/mcp-write-claims.js +864 -0
- package/dist/commands/mcp-write-coordination.js +1825 -0
- package/dist/commands/mcp-write-entities.js +620 -0
- package/dist/commands/mcp-write-memory.js +451 -0
- package/dist/commands/mcp-write-sequences.js +116 -0
- package/dist/commands/mcp-write-support.js +367 -0
- package/dist/commands/mcp.js +261 -5570
- package/dist/commands/update-handoff.js +28 -42
- package/dist/core/agent-capability.js +31 -14
- package/dist/core/agent-files.js +1 -1
- package/dist/core/agent-registry.js +51 -3
- package/dist/core/claims.js +18 -0
- package/dist/core/coordination.js +5 -2
- package/dist/core/cross-project.js +35 -1
- package/dist/core/dispatcher.js +34 -20
- package/dist/core/entity-operations.js +335 -12
- package/dist/core/entity-registry.js +72 -9
- package/dist/core/execution.js +28 -4
- package/dist/core/facade-schema.js +30 -4
- package/dist/core/federation-cloud.js +142 -11
- package/dist/core/federation-outbox.js +292 -0
- package/dist/core/federation-signing.js +115 -0
- package/dist/core/handoff-review.js +35 -0
- package/dist/core/io.js +6 -0
- package/dist/core/protocol-tool-policy.js +113 -0
- package/dist/core/review-loop-close.js +115 -0
- package/dist/core/schema.js +25 -2
- package/dist/core/security-detectors.js +35 -6
- package/dist/core/security.js +32 -12
- package/dist/core/worktree.js +98 -9
- package/dist/facts.js +13 -11
- package/dist/facts.json +12 -10
- package/docs/PROTOCOL.md +7 -3
- package/docs/concepts/coordinator-runbook.md +3 -0
- package/docs/concepts/dispatch-lifecycle.md +4 -4
- package/docs/concepts/loop-engine.md +3 -1
- package/docs/concepts/troubleshooting.md +1 -1
- package/docs/integrations/codex.md +3 -3
- package/docs/integrations/overview.md +1 -1
- package/docs/mcp-schema-changelog.md +153 -2
- package/docs/playbooks/orchestration.md +1 -1
- package/docs/product/entity-model-audit.md +3 -2
- package/docs/security.md +22 -1
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -1,120 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { Command } from 'commander';
|
|
4
|
-
import { runInit } from './commands/init.js';
|
|
5
|
-
import { runSetup, runSetupMachine } from './commands/setup.js';
|
|
6
|
-
import { runUpgrade } from './commands/upgrade.js';
|
|
7
|
-
import { patchAllMcpConfigs } from './core/agent-files.js';
|
|
8
|
-
import { runReconcile } from './commands/reconcile.js';
|
|
9
|
-
import { getMemoryLog, rollbackMemory, hasMemoryRepo } from './core/memory-git.js';
|
|
10
|
-
import { buildMachineProfile, saveMachineProfile, loadMachineProfile, renderMachineProfileSummary } from './core/machine-profile.js';
|
|
11
|
-
import { buildAgentInventory, saveAgentInventory, loadAgentInventory, renderAgentInventorySummary } from './core/agent-inventory.js';
|
|
12
|
-
import { scanAndRegister, scanProject, upsertProject, loadGlobalRegistry, renderGlobalRegistrySummary } from './core/global-registry.js';
|
|
13
|
-
import { runStatus } from './commands/status.js';
|
|
14
|
-
import { runDecision } from './commands/decision.js';
|
|
15
|
-
import { runConstraint } from './commands/constraint.js';
|
|
16
|
-
import { runTrap } from './commands/trap.js';
|
|
17
|
-
import { runHandoff } from './commands/handoff.js';
|
|
18
|
-
import { runListPlans } from './commands/list-plans.js';
|
|
19
|
-
import { runUpdatePlan } from './commands/update-plan.js';
|
|
20
|
-
import { runDeletePlan } from './commands/delete-plan.js';
|
|
21
|
-
import { runPlanResource } from './commands/plan-resource.js';
|
|
22
|
-
import { runCodeMap } from './commands/code-map.js';
|
|
23
|
-
import { runMove } from './commands/move.js';
|
|
24
|
-
import { runSequenceResource } from './commands/sequence.js';
|
|
25
|
-
import { runAddStep } from './commands/add-step.js';
|
|
26
|
-
import { runDeleteStep } from './commands/delete-step.js';
|
|
27
|
-
import { runUpdateStep } from './commands/update-step.js';
|
|
28
|
-
import { runEstimationReport } from './commands/estimation-report.js';
|
|
29
|
-
import { runCompleteStep } from './commands/complete-step.js';
|
|
30
|
-
import { runUpdateHandoff } from './commands/update-handoff.js';
|
|
31
|
-
import { runInstruction } from './commands/instruction.js';
|
|
32
|
-
import { runListAgents } from './commands/list-agents.js';
|
|
33
|
-
import { runSurfaceTaskResource } from './commands/surface-task-resource.js';
|
|
34
|
-
import { runListInstructions } from './commands/list-instructions.js';
|
|
35
|
-
import { runDoctor, runDoctorSpawnCheck } from './commands/doctor.js';
|
|
36
|
-
import { runRepair } from './commands/repair.js';
|
|
37
|
-
import { runStale } from './commands/stale.js';
|
|
38
|
-
import { runRebuild } from './commands/rebuild.js';
|
|
39
|
-
import { runReflect } from './commands/reflect.js';
|
|
40
|
-
import { runReflectRuntimeNote } from './commands/reflect-runtime-note.js';
|
|
41
|
-
import { runReview } from './commands/review.js';
|
|
42
|
-
import { runShowCandidate } from './commands/show-candidate.js';
|
|
43
|
-
import { runStarCandidate } from './commands/star-candidate.js';
|
|
44
|
-
import { runUseCandidate } from './commands/use-candidate.js';
|
|
45
|
-
import { runAccept } from './commands/accept.js';
|
|
46
|
-
import { runReject } from './commands/reject.js';
|
|
47
|
-
import { runPruneCandidates } from './commands/prune-candidates.js';
|
|
48
|
-
import { cleanupStaleCandidates } from './core/candidates.js';
|
|
49
|
-
import { runListClaims } from './commands/list-claims.js';
|
|
50
|
-
import { runReleaseClaim } from './commands/release-claim.js';
|
|
51
|
-
import { runClaimResource } from './commands/claim-resource.js';
|
|
52
|
-
import { runAssignmentResource } from './commands/assignment-resource.js';
|
|
53
|
-
import { runMemoryCommand } from './commands/memory.js';
|
|
54
|
-
import { runReleaseClaims } from './commands/release-claims.js';
|
|
55
|
-
import { runAgentBoard } from './commands/agent-board.js';
|
|
56
|
-
import { runRuntimeNote } from './commands/runtime-note.js';
|
|
57
|
-
import { runRuntimeStatus } from './commands/runtime-status.js';
|
|
58
|
-
import { runSync } from './commands/sync.js';
|
|
59
|
-
import { runContext } from './commands/context.js';
|
|
60
|
-
import { runBootstrap } from './commands/bootstrap.js';
|
|
61
|
-
import { runEnv } from './commands/env.js';
|
|
62
|
-
import { runAdapterOpenclawImport } from './commands/adapter-openclaw-import.js';
|
|
63
|
-
import { runInstallHooks } from './commands/install-hooks.js';
|
|
64
|
-
import { runCheckConstraints } from './commands/check-constraints.js';
|
|
65
|
-
import { runCheckPolicy } from './commands/check-policy.js';
|
|
66
|
-
import { runCheckSecurity } from './commands/check-security.js';
|
|
67
|
-
import { runSetupSecurity } from './commands/setup-security.js';
|
|
68
|
-
import { runRegisterAgent, runRemoveAgent } from './commands/register-agent.js';
|
|
69
|
-
import { runEnableAgent } from './commands/enable-agent.js';
|
|
70
|
-
import { runVersion } from './commands/version.js';
|
|
71
|
-
import { runReleaseNotes } from './commands/release-notes.js';
|
|
72
|
-
import { runDiff } from './commands/changes.js';
|
|
73
|
-
import { runPrune } from './commands/prune.js';
|
|
74
|
-
import { runMcp } from './commands/mcp.js';
|
|
75
|
-
import { runSetTrust } from './commands/set-trust.js';
|
|
76
|
-
import { runSessionStart } from './commands/session-start.js';
|
|
77
|
-
import { runSessionEnd } from './commands/session-end.js';
|
|
78
|
-
import { runWhoami } from './commands/whoami.js';
|
|
79
|
-
import { runUsage } from './commands/usage.js';
|
|
80
|
-
import { runSearch } from './commands/search.js';
|
|
81
|
-
import { runExport, runRefresh } from './commands/export.js';
|
|
82
|
-
import { runHooks } from './commands/hooks.js';
|
|
83
|
-
import { runWatch } from './commands/watch.js';
|
|
84
|
-
import { runDispatchAnalysis, runDispatch, runDispatchReview } from './commands/dispatch.js';
|
|
85
|
-
import { runDispatchWatch } from './commands/dispatch-watch.js';
|
|
86
|
-
import { runInboxList, runInboxAck, runInboxArchive, runInboxSend, runInboxThread } from './commands/inbox.js';
|
|
87
|
-
import { runMetrics } from './commands/metrics.js';
|
|
88
|
-
import { runRollback } from './commands/rollback.js';
|
|
89
|
-
import { runPull } from './commands/pull.js';
|
|
90
|
-
import { runPush } from './commands/push.js';
|
|
91
|
-
import { runAuditCommand } from './commands/audit.js';
|
|
92
|
-
import { runHistory } from './commands/history.js';
|
|
93
|
-
import { runContextDiff } from './commands/context-diff.js';
|
|
94
|
-
import { runCapability } from './commands/capability.js';
|
|
95
|
-
import { runLink } from './commands/link.js';
|
|
96
|
-
import { runTool } from './commands/tool.js';
|
|
97
|
-
import { runExplore } from './commands/explore.js';
|
|
98
5
|
import { getInstalledBrainclawVersion } from './core/brainclaw-version.js';
|
|
99
|
-
import { cleanOrphanFiles, memoryDir
|
|
6
|
+
import { cleanOrphanFiles, memoryDir } from './core/io.js';
|
|
100
7
|
import { initLogLevel, logger } from './core/logger.js';
|
|
101
8
|
import { resolveEffectiveCwd } from './core/store-resolution.js';
|
|
102
9
|
import { resolveProjectCwd } from './core/cross-project.js';
|
|
103
|
-
import {
|
|
104
|
-
import {
|
|
105
|
-
import {
|
|
106
|
-
import {
|
|
107
|
-
import {
|
|
108
|
-
import {
|
|
109
|
-
import {
|
|
110
|
-
import {
|
|
111
|
-
import { runQuestionsCommand } from './commands/questions.js';
|
|
112
|
-
import { runReplyCommand } from './commands/reply.js';
|
|
113
|
-
import { requireRegisteredAgentIdentity } from './core/agent-registry.js';
|
|
10
|
+
import { registerLifecycleCommands } from './cli/register-lifecycle.js';
|
|
11
|
+
import { registerCaptureCommands } from './cli/register-capture.js';
|
|
12
|
+
import { registerPlanningCommands } from './cli/register-planning.js';
|
|
13
|
+
import { registerCoordinationCommands } from './cli/register-coordination.js';
|
|
14
|
+
import { registerReviewCommands } from './cli/register-review.js';
|
|
15
|
+
import { registerMemoryContextCommands } from './cli/register-memory-context.js';
|
|
16
|
+
import { registerFederationCommands } from './cli/register-federation.js';
|
|
17
|
+
import { registerCodeMapCommands } from './cli/register-code-map.js';
|
|
114
18
|
const program = new Command();
|
|
115
|
-
function collect(value, previous) {
|
|
116
|
-
return [...previous, value];
|
|
117
|
-
}
|
|
118
19
|
function parseLeadingGlobalOptions(argv) {
|
|
119
20
|
const result = {};
|
|
120
21
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -207,9 +108,6 @@ function findCommandFromArgv(argv) {
|
|
|
207
108
|
}
|
|
208
109
|
return found;
|
|
209
110
|
}
|
|
210
|
-
function isCodevEnabled() {
|
|
211
|
-
return process.env.BRAINCLAW_ENABLE_CODEV === '1';
|
|
212
|
-
}
|
|
213
111
|
program
|
|
214
112
|
.name('brainclaw')
|
|
215
113
|
// Stop the program-level parser from consuming options that appear AFTER the
|
|
@@ -267,1914 +165,213 @@ program
|
|
|
267
165
|
process.chdir(path.resolve(explicitCwd));
|
|
268
166
|
}
|
|
269
167
|
});
|
|
270
|
-
|
|
271
|
-
program
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
168
|
+
registerLifecycleCommands(program);
|
|
169
|
+
registerCaptureCommands(program);
|
|
170
|
+
registerPlanningCommands(program);
|
|
171
|
+
registerCoordinationCommands(program);
|
|
172
|
+
registerReviewCommands(program);
|
|
173
|
+
registerMemoryContextCommands(program);
|
|
174
|
+
registerFederationCommands(program);
|
|
175
|
+
registerCodeMapCommands(program);
|
|
176
|
+
// ─── Command-order shim (pln#622 PR5) ────────────────────────────────────────
|
|
177
|
+
// Commander renders `--help` in registration order. The per-family register
|
|
178
|
+
// modules above would otherwise reorder the help output relative to the
|
|
179
|
+
// pre-split monolithic cli.ts — a visible surface change for a pure refactor.
|
|
180
|
+
// This manifest freezes the pre-split order (git show of the monolith); the
|
|
181
|
+
// stable sort below restores it on the live registry. Any NEW registration not
|
|
182
|
+
// listed here is appended at the end in its relative registration order —
|
|
183
|
+
// changing that (or editing this manifest) is an explicit product decision,
|
|
184
|
+
// not a refactor side effect.
|
|
185
|
+
const ORIGINAL_COMMAND_ORDER = [
|
|
186
|
+
'init',
|
|
187
|
+
'setup',
|
|
188
|
+
'setup-machine',
|
|
189
|
+
'memory-log',
|
|
190
|
+
'memory-rollback',
|
|
191
|
+
'upgrade',
|
|
192
|
+
'patch-configs',
|
|
193
|
+
'machine-profile',
|
|
194
|
+
'agent-inventory',
|
|
195
|
+
'projects',
|
|
196
|
+
'decision',
|
|
197
|
+
'constraint',
|
|
198
|
+
'trap',
|
|
199
|
+
'handoff',
|
|
200
|
+
'status',
|
|
201
|
+
'plan',
|
|
202
|
+
'code-map',
|
|
203
|
+
'move',
|
|
204
|
+
'list-plans',
|
|
205
|
+
'sequence',
|
|
206
|
+
'add-step',
|
|
207
|
+
'complete-step',
|
|
208
|
+
'update-step',
|
|
209
|
+
'delete-step',
|
|
210
|
+
'estimation-report',
|
|
211
|
+
'update-plan',
|
|
212
|
+
'surface-task',
|
|
213
|
+
'delete-plan',
|
|
214
|
+
'update-handoff',
|
|
215
|
+
'doctor',
|
|
216
|
+
'repair',
|
|
217
|
+
'stale',
|
|
218
|
+
'version',
|
|
219
|
+
'release-notes',
|
|
220
|
+
'uninstall',
|
|
221
|
+
'rebuild',
|
|
222
|
+
'reflect',
|
|
223
|
+
'reflect-runtime-note',
|
|
224
|
+
'context',
|
|
225
|
+
'bootstrap',
|
|
226
|
+
'env',
|
|
227
|
+
'memory',
|
|
228
|
+
'instruction',
|
|
229
|
+
'list-instructions',
|
|
230
|
+
'register-agent',
|
|
231
|
+
'enable-agent',
|
|
232
|
+
'list-agents',
|
|
233
|
+
'review',
|
|
234
|
+
'show-candidate',
|
|
235
|
+
'star-candidate',
|
|
236
|
+
'use-candidate',
|
|
237
|
+
'accept',
|
|
238
|
+
'adapter-openclaw-import',
|
|
239
|
+
'reject',
|
|
240
|
+
'harvest-candidates',
|
|
241
|
+
'harvest',
|
|
242
|
+
'prune-candidates',
|
|
243
|
+
'cleanup-candidates',
|
|
244
|
+
'claim',
|
|
245
|
+
'assignment',
|
|
246
|
+
'list-claims',
|
|
247
|
+
'release-claim',
|
|
248
|
+
'release-claims',
|
|
249
|
+
'agent-board',
|
|
250
|
+
'runtime-note',
|
|
251
|
+
'note',
|
|
252
|
+
'runtime-status',
|
|
253
|
+
'sync',
|
|
254
|
+
'check-constraints',
|
|
255
|
+
'check-policy',
|
|
256
|
+
'check-security',
|
|
257
|
+
'setup-security',
|
|
258
|
+
'install-hooks',
|
|
259
|
+
'diff',
|
|
260
|
+
'prune',
|
|
261
|
+
'compact',
|
|
262
|
+
'mcp',
|
|
263
|
+
'set-trust',
|
|
264
|
+
'session-start',
|
|
265
|
+
'session-end',
|
|
266
|
+
'whoami',
|
|
267
|
+
'usage',
|
|
268
|
+
'search',
|
|
269
|
+
'export',
|
|
270
|
+
'refresh',
|
|
271
|
+
'reconcile',
|
|
272
|
+
'hooks',
|
|
273
|
+
'watch',
|
|
274
|
+
'dispatch',
|
|
275
|
+
'inbox',
|
|
276
|
+
'check-events',
|
|
277
|
+
'metrics',
|
|
278
|
+
'rollback',
|
|
279
|
+
'pull',
|
|
280
|
+
'push',
|
|
281
|
+
'audit',
|
|
282
|
+
'history',
|
|
283
|
+
'context-diff',
|
|
284
|
+
'capability',
|
|
285
|
+
'link',
|
|
286
|
+
'tool',
|
|
287
|
+
'explore',
|
|
288
|
+
'discover',
|
|
289
|
+
'migrate',
|
|
290
|
+
'switch',
|
|
291
|
+
'who',
|
|
292
|
+
'worktree',
|
|
293
|
+
'federation',
|
|
294
|
+
// Gated by BRAINCLAW_ENABLE_CODEV: listed here so the order is right in both
|
|
295
|
+
// modes — when not registered they are simply absent from the live array and
|
|
296
|
+
// the sort skips them naturally.
|
|
297
|
+
'codev',
|
|
298
|
+
'codev-metrics',
|
|
299
|
+
'questions',
|
|
300
|
+
'bootstrap-loop',
|
|
301
|
+
'loop',
|
|
302
|
+
'reply',
|
|
303
|
+
'run',
|
|
304
|
+
];
|
|
305
|
+
{
|
|
306
|
+
const rank = new Map(ORIGINAL_COMMAND_ORDER.map((name, index) => [name, index]));
|
|
307
|
+
// Localized cast: Commander types `commands` as readonly, but reordering the
|
|
308
|
+
// live array is exactly the point of this shim. Array.prototype.sort is
|
|
309
|
+
// stable (ES2019+), so unlisted commands keep their relative registration
|
|
310
|
+
// order after every listed one.
|
|
311
|
+
program.commands.sort((a, b) => (rank.get(a.name()) ?? Number.MAX_SAFE_INTEGER) -
|
|
312
|
+
(rank.get(b.name()) ?? Number.MAX_SAFE_INTEGER));
|
|
313
|
+
}
|
|
314
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
315
|
+
// CAMPAIGN SCAFFOLDING (pln#622 PR0c) — TEMPORARY, removed at end of campaign.
|
|
316
|
+
// When BRAINCLAW_DUMP_REGISTRY is set, emit a normalized JSON snapshot of
|
|
317
|
+
// the fully-built Commander registry and exit BEFORE parsing: value '1'
|
|
318
|
+
// prints to stdout (manual debugging); any other value is a FILE PATH the
|
|
319
|
+
// JSON is written to — module side effects can interleave writes on stdout
|
|
320
|
+
// (observed on Linux CI: corrupted JSON mid-stream), a file write is not
|
|
321
|
+
// subject to that race. This
|
|
322
|
+
// adds zero visible CLI surface: no new command, option, or help text — it is
|
|
323
|
+
// reachable only through an env var that regular users never set. It exists
|
|
324
|
+
// solely so tests/unit/cli-registry-snapshot.test.ts can freeze the command
|
|
325
|
+
// surface while cli.ts is decomposed (PR1→PR5); the branch goes away in PR6.
|
|
326
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
327
|
+
if (process.env.BRAINCLAW_DUMP_REGISTRY) {
|
|
328
|
+
// Codepoint comparison (NOT localeCompare) so the committed snapshot is
|
|
329
|
+
// byte-identical across machines/locales.
|
|
330
|
+
const byCodepoint = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
331
|
+
const commands = [];
|
|
332
|
+
const walk = (cmd, prefix) => {
|
|
333
|
+
const pathTokens = [...prefix, cmd.name()];
|
|
334
|
+
commands.push({
|
|
335
|
+
aliases: [...cmd.aliases()].sort(byCodepoint),
|
|
336
|
+
arguments: cmd.registeredArguments.map((arg) => ({
|
|
337
|
+
name: arg.name(),
|
|
338
|
+
required: arg.required,
|
|
339
|
+
variadic: arg.variadic,
|
|
340
|
+
})),
|
|
341
|
+
options: cmd.options
|
|
342
|
+
.map((opt) => ({
|
|
343
|
+
...(opt.defaultValue !== undefined ? { defaultValue: opt.defaultValue } : {}),
|
|
344
|
+
flags: opt.flags,
|
|
345
|
+
long: opt.long ?? null,
|
|
346
|
+
mandatory: opt.mandatory,
|
|
347
|
+
negate: opt.negate,
|
|
348
|
+
short: opt.short ?? null,
|
|
349
|
+
valueOptional: opt.optional,
|
|
350
|
+
valueRequired: opt.required,
|
|
351
|
+
variadic: opt.variadic,
|
|
352
|
+
}))
|
|
353
|
+
.sort((a, b) => byCodepoint(a.flags, b.flags)),
|
|
354
|
+
path: pathTokens.join(' '),
|
|
345
355
|
});
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
else {
|
|
360
|
-
console.error(`Error: failed to rollback to '${ref}'. Check memory-log for valid refs.`);
|
|
361
|
-
process.exit(1);
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
// --- upgrade ---
|
|
365
|
-
program
|
|
366
|
-
.command('upgrade')
|
|
367
|
-
.description('Upgrade project memory structure and refresh managed workspace agent files without losing data')
|
|
368
|
-
.option('--json', 'Output as JSON')
|
|
369
|
-
.option('--dry-run', 'Show what would be done without making changes')
|
|
370
|
-
.option('--self-update', 'Check for a newer brainclaw package version and install it before upgrading memory')
|
|
371
|
-
.option('--to <version>', 'One-shot target schema version (e.g. --to=1.0). Real runs require a backup.')
|
|
372
|
-
.option('--backup', 'Create a timestamped backup of .brainclaw/ before any write (always on for --to runs)')
|
|
373
|
-
.option('--no-backup', 'Disable the automatic backup for housekeeping-only upgrade runs')
|
|
374
|
-
.option('--rollback', 'Restore the most recent backup, park the current live store, exit')
|
|
375
|
-
.option('--yes', 'Skip interactive confirmations (reserved for later prompt additions)')
|
|
376
|
-
.action((options) => {
|
|
377
|
-
runUpgrade({
|
|
378
|
-
json: options.json,
|
|
379
|
-
dryRun: options.dryRun,
|
|
380
|
-
selfUpdate: options.selfUpdate,
|
|
381
|
-
to: options.to,
|
|
382
|
-
backup: options.backup,
|
|
383
|
-
rollback: options.rollback,
|
|
384
|
-
yes: options.yes,
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
// --- patch-configs ---
|
|
388
|
-
program
|
|
389
|
-
.command('patch-configs')
|
|
390
|
-
.description('Patch all MCP config files to use the current brainclaw binary path')
|
|
391
|
-
.option('--json', 'Output as JSON')
|
|
392
|
-
.action((options) => {
|
|
393
|
-
const cwd = process.env.BRAINCLAW_CWD ?? process.cwd();
|
|
394
|
-
const results = patchAllMcpConfigs(cwd);
|
|
395
|
-
if (options.json) {
|
|
396
|
-
console.log(JSON.stringify(results, null, 2));
|
|
397
|
-
}
|
|
398
|
-
else if (results.length === 0) {
|
|
399
|
-
console.log('✔ All MCP configs are already up to date.');
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
for (const r of results) {
|
|
403
|
-
const tag = r.created ? 'created' : 'updated';
|
|
404
|
-
console.log(`✔ ${r.filePath} (${tag}) — ${r.label}`);
|
|
405
|
-
}
|
|
406
|
-
console.log(`\n${results.length} MCP config(s) patched.`);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
// --- machine-profile ---
|
|
410
|
-
program
|
|
411
|
-
.command('machine-profile')
|
|
412
|
-
.description('Detect and persist machine capabilities (OS, shells, git users, SSH keys, toolchains, WSL, AI surfaces)')
|
|
413
|
-
.option('--refresh', 'Force regeneration even if profile exists')
|
|
414
|
-
.option('--json', 'Output as JSON')
|
|
415
|
-
.action(async (options) => {
|
|
416
|
-
const existing = loadMachineProfile();
|
|
417
|
-
if (existing && !options.refresh) {
|
|
418
|
-
if (options.json) {
|
|
419
|
-
console.log(JSON.stringify(existing, null, 2));
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
console.log(renderMachineProfileSummary(existing));
|
|
423
|
-
console.log('\nUse --refresh to regenerate.');
|
|
424
|
-
}
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
console.log('Detecting machine capabilities...');
|
|
428
|
-
const profile = buildMachineProfile();
|
|
429
|
-
const filePath = saveMachineProfile(profile);
|
|
430
|
-
if (options.json) {
|
|
431
|
-
console.log(JSON.stringify(profile, null, 2));
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
console.log(renderMachineProfileSummary(profile));
|
|
435
|
-
console.log(`\n✔ Profile saved to ${filePath}`);
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
// --- agent-inventory ---
|
|
439
|
-
program
|
|
440
|
-
.command('agent-inventory')
|
|
441
|
-
.description('Detect all installed AI coding agents and their capabilities')
|
|
442
|
-
.option('--refresh', 'Force regeneration even if inventory exists')
|
|
443
|
-
.option('--json', 'Output as JSON')
|
|
444
|
-
.action(async (options) => {
|
|
445
|
-
const existing = loadAgentInventory();
|
|
446
|
-
if (existing && !options.refresh) {
|
|
447
|
-
if (options.json) {
|
|
448
|
-
console.log(JSON.stringify(existing, null, 2));
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
console.log(renderAgentInventorySummary(existing));
|
|
452
|
-
console.log('\nUse --refresh to regenerate.');
|
|
453
|
-
}
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
console.log('Detecting installed agents...');
|
|
457
|
-
const inventory = buildAgentInventory();
|
|
458
|
-
const filePath = saveAgentInventory(inventory);
|
|
459
|
-
if (options.json) {
|
|
460
|
-
console.log(JSON.stringify(inventory, null, 2));
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
console.log(renderAgentInventorySummary(inventory));
|
|
464
|
-
console.log(`\n✔ Inventory saved to ${filePath}`);
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
// --- projects ---
|
|
468
|
-
program
|
|
469
|
-
.command('projects')
|
|
470
|
-
.description('List all brainclaw-initialized projects on this machine')
|
|
471
|
-
.option('--scan <roots>', 'Comma-separated directories to scan for projects')
|
|
472
|
-
.option('--register', 'Register the current project in the global registry')
|
|
473
|
-
.option('--json', 'Output as JSON')
|
|
474
|
-
.action(async (options) => {
|
|
475
|
-
if (options.register) {
|
|
476
|
-
const entry = scanProject(process.cwd());
|
|
477
|
-
if (!entry) {
|
|
478
|
-
console.error('No brainclaw project found in current directory. Run brainclaw init first.');
|
|
479
|
-
process.exit(1);
|
|
480
|
-
}
|
|
481
|
-
upsertProject(entry);
|
|
482
|
-
console.log(`✔ Registered ${entry.project_name} (${entry.project_id})`);
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
if (options.scan) {
|
|
486
|
-
const roots = options.scan.split(',').map((r) => r.trim());
|
|
487
|
-
console.log(`Scanning ${roots.join(', ')}...`);
|
|
488
|
-
const registry = scanAndRegister(roots);
|
|
489
|
-
if (options.json) {
|
|
490
|
-
console.log(JSON.stringify(registry, null, 2));
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
console.log(renderGlobalRegistrySummary(registry));
|
|
494
|
-
}
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
// Default: show existing registry
|
|
498
|
-
const registry = loadGlobalRegistry();
|
|
499
|
-
if (!registry || registry.projects.length === 0) {
|
|
500
|
-
console.log('No projects registered. Use --scan <roots> or --register to add projects.');
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
if (options.json) {
|
|
504
|
-
console.log(JSON.stringify(registry, null, 2));
|
|
356
|
+
for (const sub of cmd.commands)
|
|
357
|
+
walk(sub, pathTokens);
|
|
358
|
+
};
|
|
359
|
+
walk(program, []);
|
|
360
|
+
// topLevelOrder captures the LIVE registration order (what `--help` renders,
|
|
361
|
+
// after the command-order shim above) before the path sort below erases it.
|
|
362
|
+
// The snapshot test asserts it still matches the pre-split monolith order.
|
|
363
|
+
const topLevelOrder = program.commands.map((cmd) => cmd.name());
|
|
364
|
+
commands.sort((a, b) => byCodepoint(a.path, b.path));
|
|
365
|
+
const dump = { commands, topLevelOrder };
|
|
366
|
+
const dumpTarget = process.env.BRAINCLAW_DUMP_REGISTRY;
|
|
367
|
+
if (dumpTarget === '1') {
|
|
368
|
+
console.log(JSON.stringify(dump, null, 2));
|
|
505
369
|
}
|
|
506
370
|
else {
|
|
507
|
-
|
|
371
|
+
fs.writeFileSync(dumpTarget, JSON.stringify(dump, null, 2), 'utf-8');
|
|
508
372
|
}
|
|
509
|
-
|
|
510
|
-
// --- decision ---
|
|
511
|
-
program
|
|
512
|
-
.command('decision <text>')
|
|
513
|
-
.description('Add a recent decision')
|
|
514
|
-
.option('--outcome <outcome>', 'Outcome: approved, rejected, deferred, pending')
|
|
515
|
-
.option('--tag <tags...>', 'Tags for this decision')
|
|
516
|
-
.option('--path <paths...>', 'Related file paths')
|
|
517
|
-
.option('--author <author>', 'Author name')
|
|
518
|
-
.option('--plan <id>', 'Optional linked plan item ID')
|
|
519
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace, user')
|
|
520
|
-
.action((text, options) => {
|
|
521
|
-
runDecision(text, options);
|
|
522
|
-
});
|
|
523
|
-
// --- constraint ---
|
|
524
|
-
program
|
|
525
|
-
.command('constraint <text>')
|
|
526
|
-
.description('Add an active constraint')
|
|
527
|
-
.option('--category <category>', 'Category: architecture, performance, security, reliability, compatibility, process, other')
|
|
528
|
-
.option('--tag <tags...>', 'Tags for this constraint')
|
|
529
|
-
.option('--path <paths...>', 'Related file paths')
|
|
530
|
-
.option('--author <author>', 'Author name')
|
|
531
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace, user')
|
|
532
|
-
.action((text, options) => {
|
|
533
|
-
runConstraint(text, options);
|
|
534
|
-
});
|
|
535
|
-
// --- trap ---
|
|
536
|
-
program
|
|
537
|
-
.command('trap <text>')
|
|
538
|
-
.description('Add a known trap')
|
|
539
|
-
.option('--status <status>', 'Status: active, resolved, expired', 'active')
|
|
540
|
-
.option('--severity <severity>', 'Severity: low, medium, high', 'medium')
|
|
541
|
-
.option('--visibility <visibility>', 'Visibility: shared, machine, private', 'shared')
|
|
542
|
-
.option('--host <host>', 'Optional host identifier override for machine/private traps')
|
|
543
|
-
.option('--tag <tags...>', 'Tags for this trap')
|
|
544
|
-
.option('--path <paths...>', 'Related file paths')
|
|
545
|
-
.option('--author <author>', 'Author name')
|
|
546
|
-
.option('--ttl <duration>', 'Time-to-live: 30m, 2h, 7d (trap auto-expires after this duration)')
|
|
547
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace, user')
|
|
548
|
-
.action((text, options) => {
|
|
549
|
-
runTrap(text, options);
|
|
550
|
-
});
|
|
551
|
-
// --- handoff ---
|
|
552
|
-
program
|
|
553
|
-
.command('handoff <text>')
|
|
554
|
-
.description('Create a handoff')
|
|
555
|
-
.requiredOption('--from <from>', 'Source of the handoff')
|
|
556
|
-
.requiredOption('--to <to>', 'Destination of the handoff')
|
|
557
|
-
.option('--tag <tags...>', 'Tags for this handoff')
|
|
558
|
-
.option('--path <paths...>', 'Related file paths')
|
|
559
|
-
.option('--project <project>', 'Optional project namespace')
|
|
560
|
-
.option('--plan <id>', 'Optional linked plan item ID')
|
|
561
|
-
.option('--author <author>', 'Author name')
|
|
562
|
-
.option('--capture-diff', 'Capture current git diff into the handoff snapshot')
|
|
563
|
-
.option('--files <files...>', 'Files touched in this handoff')
|
|
564
|
-
.option('--pre-condition <conditions...>', 'Pre-conditions for the receiving agent')
|
|
565
|
-
.option('--post-condition <conditions...>', 'Post-conditions the receiving agent must satisfy')
|
|
566
|
-
.option('--test <tests...>', 'Tests the receiving agent should verify')
|
|
567
|
-
.option('--linked-plan <plans...>', 'Linked plan IDs')
|
|
568
|
-
.action((text, options) => {
|
|
569
|
-
runHandoff(text, options);
|
|
570
|
-
});
|
|
571
|
-
// --- status ---
|
|
572
|
-
program
|
|
573
|
-
.command('status')
|
|
574
|
-
.description('Show project memory status')
|
|
575
|
-
.option('--json', 'Output as JSON')
|
|
576
|
-
.option('--markdown', 'Output as Markdown')
|
|
577
|
-
.action((options) => {
|
|
578
|
-
runStatus(options);
|
|
579
|
-
});
|
|
580
|
-
// --- plan ---
|
|
581
|
-
program
|
|
582
|
-
.command('plan <subcommand> [args...]')
|
|
583
|
-
.description('Manage shared plan items (create, list, update, delete)')
|
|
584
|
-
.option('--json', 'Output as JSON for list')
|
|
585
|
-
.option('--all', 'Include done and dropped plan items in list')
|
|
586
|
-
.option('--type <type>', 'Plan type or filter: feat, fix, chore, spike, doc')
|
|
587
|
-
.option('--status <status>', 'Status filter/update: todo, in_progress, blocked, done, dropped')
|
|
588
|
-
.option('--priority <priority>', 'Priority: low, medium, high', 'medium')
|
|
589
|
-
.option('--assignee <assignee>', 'Assignee for this plan item')
|
|
590
|
-
.option('--project <project>', 'Optional project namespace')
|
|
591
|
-
.option('--tag <tags...>', 'Tags for this plan item')
|
|
592
|
-
.option('--path <paths...>', 'Related file paths')
|
|
593
|
-
.option('--depends-on <ids...>', 'Dependency IDs for this plan item')
|
|
594
|
-
.option('--author <author>', 'Author name')
|
|
595
|
-
.option('--estimate <minutes>', 'Estimated effort in minutes (positive integer, e.g. --estimate 30)')
|
|
596
|
-
.option('--actual-effort <effort>', 'Actual effort spent (e.g. "20min", "1h30m")')
|
|
597
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace, user')
|
|
598
|
-
.option('--recursive', 'Include plans from descendant brainclaw projects (for list)')
|
|
599
|
-
.option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
|
|
600
|
-
.action((subcommand, args, options) => {
|
|
601
|
-
runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort, localOnly: options.localOnly });
|
|
602
|
-
});
|
|
603
|
-
// --- code-map ---
|
|
604
|
-
program
|
|
605
|
-
.command('code-map <subcommand> [args...]')
|
|
606
|
-
.description('Query the per-project Code Map (status, refresh, find, brief)')
|
|
607
|
-
.option('--json', 'Output as JSON')
|
|
608
|
-
.option('--all', 'For refresh: enumerate all supported files (full refresh)')
|
|
609
|
-
.option('--changed', 'For refresh: only changed files (default)')
|
|
610
|
-
.option('--cascade', 'For refresh/status in a multi-project workspace: cascade across every nested project (each gets its own store; the root store is scoped to files no child owns)')
|
|
611
|
-
.option('--limit <n>', 'Max results for find/brief', (v) => parseInt(v, 10))
|
|
612
|
-
.action((subcommand, args, options) => {
|
|
613
|
-
void runCodeMap(subcommand, args, options).catch((err) => {
|
|
614
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
615
|
-
process.exit(1);
|
|
616
|
-
});
|
|
617
|
-
});
|
|
618
|
-
// --- move (cross-project relocation, pln#595) ---
|
|
619
|
-
program
|
|
620
|
-
.command('move <entity> <id>')
|
|
621
|
-
.description('Relocate a brainclaw item to another project, id-preserving (multi-project workspaces). Relocatable: plan, decision, constraint, trap, handoff, sequence.')
|
|
622
|
-
.requiredOption('--to <project>', 'Target project (name, path, or basename)')
|
|
623
|
-
.option('--from <project>', 'Source project (defaults to the current project)')
|
|
624
|
-
.option('--force', 'Move even if an active claim references the item')
|
|
625
|
-
.option('--json', 'Output as JSON')
|
|
626
|
-
.action((entity, id, options) => runMove(entity, id, options));
|
|
627
|
-
// --- list-plans ---
|
|
628
|
-
program
|
|
629
|
-
.command('list-plans')
|
|
630
|
-
.description('List shared plan items')
|
|
631
|
-
.option('--json', 'Output as JSON')
|
|
632
|
-
.option('--status <status>', 'Status: todo, in_progress, blocked, done, dropped')
|
|
633
|
-
.option('--type <type>', 'Filter by type: feat, fix, chore, spike, doc')
|
|
634
|
-
.option('--assignee <assignee>', 'Filter by assignee')
|
|
635
|
-
.option('--project <project>', 'Filter by project namespace')
|
|
636
|
-
.option('--all', 'Include done and dropped plan items')
|
|
637
|
-
.option('--recursive', 'Include plans from descendant brainclaw projects')
|
|
638
|
-
.option('--local-only', 'Read from local store only (skip parent stores in chain)')
|
|
639
|
-
.action((options) => {
|
|
640
|
-
runListPlans({ ...options, localOnly: options.localOnly });
|
|
641
|
-
});
|
|
642
|
-
program
|
|
643
|
-
.command('sequence <subcommand> [args...]')
|
|
644
|
-
.description('Manage coordination sequences (create, list, show, update)')
|
|
645
|
-
.option('--json', 'Output as JSON')
|
|
646
|
-
.option('--description <text>', 'Optional sequence description')
|
|
647
|
-
.option('--status <status>', 'Sequence status: draft, active, archived')
|
|
648
|
-
.option('--owner <owner>', 'Optional sequence owner')
|
|
649
|
-
.option('--items <json>', 'Sequence items JSON array')
|
|
650
|
-
.option('--name <name>', 'Optional sequence name for update')
|
|
651
|
-
.option('--tag <tags...>', 'Tags for this sequence')
|
|
652
|
-
.option('--author <author>', 'Author name')
|
|
653
|
-
.action((subcommand, args, options) => {
|
|
654
|
-
runSequenceResource(subcommand, args, options);
|
|
655
|
-
});
|
|
656
|
-
// --- add-step ---
|
|
657
|
-
program
|
|
658
|
-
.command('add-step <planId> <text>')
|
|
659
|
-
.description('Add an optional step to a plan item')
|
|
660
|
-
.option('--assign <assignee>', 'Assign this step to an agent or person')
|
|
661
|
-
.option('--estimate <minutes>', 'Step-level estimate (minutes, or a duration like "2h"/"30m")')
|
|
662
|
-
.option('--actual-effort <effort>', 'Step-level actual effort (e.g. "45m", "2h")')
|
|
663
|
-
.action((planId, text, options) => {
|
|
664
|
-
runAddStep(planId, text, { assignee: options.assign, estimatedEffort: options.estimate, actualEffort: options.actualEffort });
|
|
665
|
-
});
|
|
666
|
-
// --- complete-step ---
|
|
667
|
-
program
|
|
668
|
-
.command('complete-step <planId> <stepId>')
|
|
669
|
-
.description('Mark a plan step as done')
|
|
670
|
-
.action((planId, stepId) => {
|
|
671
|
-
runCompleteStep(planId, stepId);
|
|
672
|
-
});
|
|
673
|
-
// --- update-step ---
|
|
674
|
-
program
|
|
675
|
-
.command('update-step <planId> <stepId>')
|
|
676
|
-
.description('Update a plan step (status, text, assignee, effort)')
|
|
677
|
-
.option('--status <status>', 'New status: todo, in_progress, testing, done, blocked')
|
|
678
|
-
.option('--text <text>', 'Replace step description')
|
|
679
|
-
.option('--assign <assignee>', 'Assign the step (empty string to unassign)')
|
|
680
|
-
.option('--estimate <minutes>', 'Step-level estimate (minutes, or a duration like "2h"/"30m")')
|
|
681
|
-
.option('--actual-effort <effort>', 'Step-level actual effort (e.g. "45m", "2h")')
|
|
682
|
-
.action((planId, stepId, options) => {
|
|
683
|
-
runUpdateStep(planId, stepId, options);
|
|
684
|
-
});
|
|
685
|
-
// --- delete-step ---
|
|
686
|
-
program
|
|
687
|
-
.command('delete-step <planId> <stepId>')
|
|
688
|
-
.description('Remove a step from a plan')
|
|
689
|
-
.action((planId, stepId) => {
|
|
690
|
-
runDeleteStep(planId, stepId);
|
|
691
|
-
});
|
|
692
|
-
// --- estimation-report ---
|
|
693
|
-
program
|
|
694
|
-
.command('estimation-report')
|
|
695
|
-
.description('Show estimation accuracy report for completed plans')
|
|
696
|
-
.option('--agent <name>', 'Filter by agent/author name')
|
|
697
|
-
.option('--json', 'Output as JSON')
|
|
698
|
-
.option('--outlier-threshold <minutes>', 'Drop plan-wallclock actuals over N minutes from the stats (default 1440 = 24h; 0 disables)')
|
|
699
|
-
.action((options) => {
|
|
700
|
-
runEstimationReport({
|
|
701
|
-
agent: options.agent,
|
|
702
|
-
json: options.json,
|
|
703
|
-
outlierThresholdMinutes: options.outlierThreshold !== undefined ? Number(options.outlierThreshold) : undefined,
|
|
704
|
-
});
|
|
705
|
-
});
|
|
706
|
-
// --- update-plan ---
|
|
707
|
-
program
|
|
708
|
-
.command('update-plan <id>')
|
|
709
|
-
.description('Update a shared plan item')
|
|
710
|
-
.option('--status <status>', 'Status: todo, in_progress, blocked, done, dropped')
|
|
711
|
-
.option('--assignee <assignee>', 'Assign a user or agent to this plan item')
|
|
712
|
-
.option('--project <project>', 'Set or change project namespace')
|
|
713
|
-
.option('--priority <priority>', 'Priority: low, medium, high')
|
|
714
|
-
.option('--actual-effort <effort>', 'Actual effort spent (e.g. "20min", "1h30m")')
|
|
715
|
-
.action((id, options) => {
|
|
716
|
-
runUpdatePlan(id, { ...options, actualEffort: options.actualEffort });
|
|
717
|
-
});
|
|
718
|
-
// --- surface-task ---
|
|
719
|
-
program
|
|
720
|
-
.command('surface-task <subcommand> [args...]')
|
|
721
|
-
.description('Manage queued tasks for desktop AI surfaces such as ChatGPT Desktop or Claude Desktop')
|
|
722
|
-
.option('--json', 'Output as JSON for list')
|
|
723
|
-
.option('--all', 'Include completed, cancelled, and failed tasks in list')
|
|
724
|
-
.option('--status <status>', 'Status filter/update: queued, in_progress, completed, cancelled, failed')
|
|
725
|
-
.option('--target <surface>', 'Target surface, e.g. chatgpt, claude, gemini')
|
|
726
|
-
.option('--kind <kind>', 'Task kind: visual_asset, draft, summary, analysis, research, custom')
|
|
727
|
-
.option('--instructions <text>', 'Detailed instructions for the target surface')
|
|
728
|
-
.option('--output <paths...>', 'Expected output paths')
|
|
729
|
-
.option('--result <text>', 'Optional result note when updating a task')
|
|
730
|
-
.option('--tag <tags...>', 'Tags for this task')
|
|
731
|
-
.option('--path <paths...>', 'Related file paths')
|
|
732
|
-
.option('--agent <agent>', 'Author agent name')
|
|
733
|
-
.option('--agent-id <agentId>', 'Author agent id')
|
|
734
|
-
.action((subcommand, args, options) => {
|
|
735
|
-
runSurfaceTaskResource(subcommand, args, {
|
|
736
|
-
...options,
|
|
737
|
-
agentId: options.agentId,
|
|
738
|
-
});
|
|
739
|
-
});
|
|
740
|
-
// --- delete-plan ---
|
|
741
|
-
program
|
|
742
|
-
.command('delete-plan <id>')
|
|
743
|
-
.description('Delete a shared plan item')
|
|
744
|
-
.action((id) => {
|
|
745
|
-
runDeletePlan(id);
|
|
746
|
-
});
|
|
747
|
-
// --- update-handoff ---
|
|
748
|
-
program
|
|
749
|
-
.command('update-handoff <id>')
|
|
750
|
-
.description('Update the status, recipient, or review state of a handoff')
|
|
751
|
-
.option('--status <status>', 'Status: open, accepted, closed')
|
|
752
|
-
.option('--to <agent>', 'Change the receiving agent')
|
|
753
|
-
.option('--narrative <text>', 'Update the narrative attached to the handoff')
|
|
754
|
-
.option('--reviewer <agent>', 'Set or override the assigned reviewer')
|
|
755
|
-
.option('--review-verdict <verdict>', 'Set review verdict: approve or request_changes')
|
|
756
|
-
.option('--reviewed-by <agent>', 'Set the reviewer identity that produced the verdict')
|
|
757
|
-
.option('--review-summary <text>', 'Attach a short review summary')
|
|
758
|
-
.option('--blocking-issue <text>', 'Add a blocking review issue (repeatable)', collect, [])
|
|
759
|
-
.option('--suggestion <text>', 'Add a non-blocking review suggestion (repeatable)', collect, [])
|
|
760
|
-
.action((id, options) => {
|
|
761
|
-
runUpdateHandoff(id, {
|
|
762
|
-
...options,
|
|
763
|
-
review_verdict: options.reviewVerdict,
|
|
764
|
-
reviewed_by: options.reviewedBy,
|
|
765
|
-
review_summary: options.reviewSummary,
|
|
766
|
-
blocking_issues: options.blockingIssue,
|
|
767
|
-
suggestions: options.suggestion,
|
|
768
|
-
});
|
|
769
|
-
});
|
|
770
|
-
// --- doctor ---
|
|
771
|
-
program
|
|
772
|
-
.command('doctor')
|
|
773
|
-
.description('Check memory health and security')
|
|
774
|
-
.option('--json', 'Output as JSON dashboard')
|
|
775
|
-
.option('--migration-check', 'Report versioned documents that need schema migration')
|
|
776
|
-
.option('--fix-agent-ignore', 'Add missing .gitignore entries for generated local Brainclaw agent files')
|
|
777
|
-
.option('--fix-hooks', 'Purge stale/broken/duplicate brainclaw session hooks across all Claude Code settings scopes (user + cwd) and rewrite the canonical ones')
|
|
778
|
-
.option('--fix', 'Fix auto-resolvable issues (e.g. drifting MCP configs)')
|
|
779
|
-
.option('--repair', 'Rebuild dist/ when the MCP runtime is missing or stale')
|
|
780
|
-
.option('--after-migration', 'Run the v1.0 post-migration health check only (exits non-zero on any failure)')
|
|
781
|
-
.option('--dispatch', 'Run dispatch-health diagnostic only: reconcile open agent_runs and report stuck/unverified/silent failures (pln#496 step stp_8c072d75)')
|
|
782
|
-
.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')
|
|
783
|
-
.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')
|
|
784
|
-
.option('--spawn-check-timeout <ms>', 'Per-agent timeout for --spawn-check (default 15000)', parseInt)
|
|
785
|
-
.option('--hygiene', 'Coordination-hygiene snapshot (pln#602): counts per family, park candidates, serve-count aging stats. Read-only.')
|
|
786
|
-
.action(async (options) => {
|
|
787
|
-
if (options.spawnCheck) {
|
|
788
|
-
await runDoctorSpawnCheck({ cwd: options.cwd, json: options.json, timeoutMs: options.spawnCheckTimeout });
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
runDoctor({ ...options, afterMigration: options.afterMigration, dispatch: options.dispatch, verifyJournal: options.verifyJournal, hygiene: options.hygiene });
|
|
792
|
-
});
|
|
793
|
-
// --- repair (Phase 4 Sprint 2 Lane C / pln#397) ---
|
|
794
|
-
program
|
|
795
|
-
.command('repair')
|
|
796
|
-
.description('Apply safe, non-destructive fixes for the repair candidates surfaced by doctor')
|
|
797
|
-
.option('--dry-run', 'Print the plan without executing anything')
|
|
798
|
-
.option('--include-unsafe', 'Also apply candidates flagged unsafe (preserves data but requires confirmation)')
|
|
799
|
-
.option('--json', 'Output as JSON')
|
|
800
|
-
.action((options) => {
|
|
801
|
-
runRepair({
|
|
802
|
-
dryRun: options.dryRun,
|
|
803
|
-
includeUnsafe: options.includeUnsafe,
|
|
804
|
-
json: options.json,
|
|
805
|
-
});
|
|
806
|
-
});
|
|
807
|
-
// --- stale (Phase 4 Sprint 1 Lane A / pln#390) ---
|
|
808
|
-
program
|
|
809
|
-
.command('stale [subcommand] [id]')
|
|
810
|
-
.description('List or resolve stale memory items (plans, traps, handoffs, candidates, runtime notes). Subcommands: list (default), resolve <id>.')
|
|
811
|
-
.option('--json', 'Output as JSON')
|
|
812
|
-
.action((subcommand, id, options) => {
|
|
813
|
-
runStale(subcommand, id, { json: options.json });
|
|
814
|
-
});
|
|
815
|
-
// --- version ---
|
|
816
|
-
program
|
|
817
|
-
.command('version')
|
|
818
|
-
.description('Show the installed brainclaw version and the project version policy')
|
|
819
|
-
.option('--check', 'Check the configured installable update source')
|
|
820
|
-
.option('--publish-local', 'Create/update the local installable .releases channel via npm pack')
|
|
821
|
-
.option('--release-notes <text>', 'Attach plain-text release notes to the generated local-pack manifest')
|
|
822
|
-
.option('--agent-release-notes <json>', 'Attach structured agent-first release notes (JSON) to the generated local-pack manifest')
|
|
823
|
-
.option('--auto-release-notes', 'Auto-generate agent-first release notes from git log (use with --publish-local)')
|
|
824
|
-
.option('--json', 'Output as JSON')
|
|
825
|
-
.action((options) => {
|
|
826
|
-
runVersion(options);
|
|
827
|
-
});
|
|
828
|
-
// --- release-notes ---
|
|
829
|
-
program
|
|
830
|
-
.command('release-notes')
|
|
831
|
-
.description('Show or generate agent-first release notes')
|
|
832
|
-
.option('--generate', 'Generate release notes from git log instead of showing configured ones')
|
|
833
|
-
.option('--since <ref>', 'Git ref to generate from (default: last version tag)')
|
|
834
|
-
.option('--json', 'Output as JSON')
|
|
835
|
-
.action((options) => {
|
|
836
|
-
runReleaseNotes(options);
|
|
837
|
-
});
|
|
838
|
-
// --- uninstall ---
|
|
839
|
-
import { runUninstall } from './commands/uninstall.js';
|
|
840
|
-
program
|
|
841
|
-
.command('uninstall')
|
|
842
|
-
.description('Remove brainclaw from a project and/or machine')
|
|
843
|
-
.option('--project', 'Remove brainclaw from the current project (.brainclaw/, agent files, configs)')
|
|
844
|
-
.option('--machine', 'Remove brainclaw global config (~/.brainclaw/)')
|
|
845
|
-
.option('-y, --yes', 'Skip confirmation prompts')
|
|
846
|
-
.action(async (options) => {
|
|
847
|
-
await runUninstall(options);
|
|
848
|
-
});
|
|
849
|
-
// --- rebuild ---
|
|
850
|
-
program
|
|
851
|
-
.command('rebuild')
|
|
852
|
-
.description('Rebuild project.md from canonical memory state')
|
|
853
|
-
.action(() => {
|
|
854
|
-
runRebuild();
|
|
855
|
-
});
|
|
856
|
-
// --- reflect ---
|
|
857
|
-
program
|
|
858
|
-
.command('reflect [text]')
|
|
859
|
-
.description('Create a memory candidate for review')
|
|
860
|
-
.option('--type <type>', 'Type: constraint, decision, trap, handoff')
|
|
861
|
-
.option('--batch <file>', 'Import runtime events from JSON file')
|
|
862
|
-
.option('--session <id>', 'Import runtime events by session id from .brainclaw/runtime/')
|
|
863
|
-
.option('--tag <tags...>', 'Tags')
|
|
864
|
-
.option('--author <author>', 'Author name')
|
|
865
|
-
.option('--source <source>', 'Source context (e.g. agent name)')
|
|
866
|
-
.option('--severity <severity>', 'Severity for traps: low, medium, high')
|
|
867
|
-
.option('--from <from>', 'Handoff source')
|
|
868
|
-
.option('--to <to>', 'Handoff destination')
|
|
869
|
-
.option('--path <paths...>', 'Related file paths')
|
|
870
|
-
.action((text, options) => {
|
|
871
|
-
runReflect(text, options);
|
|
872
|
-
});
|
|
873
|
-
// --- reflect-runtime-note ---
|
|
874
|
-
program
|
|
875
|
-
.command('reflect-runtime-note <id> [text]')
|
|
876
|
-
.description('Turn a visible runtime note into a shared review candidate')
|
|
877
|
-
.option('--type <type>', 'Type: constraint, decision, trap, handoff')
|
|
878
|
-
.option('--host <host>', 'Look up machine-local runtime note for a specific host')
|
|
879
|
-
.option('--all-hosts', 'Look up runtime notes across all hosts')
|
|
880
|
-
.option('--suggest', 'Show candidate type suggestions before or instead of creation')
|
|
881
|
-
.option('--json', 'Output suggestions as JSON when used with --suggest or without --type')
|
|
882
|
-
.option('--tag <tags...>', 'Additional tags to merge with the runtime note tags')
|
|
883
|
-
.option('--author <author>', 'Author name for the candidate')
|
|
884
|
-
.option('--source <source>', 'Source context for the candidate')
|
|
885
|
-
.option('--severity <severity>', 'Severity for traps: low, medium, high')
|
|
886
|
-
.option('--from <from>', 'Handoff source')
|
|
887
|
-
.option('--to <to>', 'Handoff destination')
|
|
888
|
-
.option('--path <paths...>', 'Related file paths')
|
|
889
|
-
.action((id, text, options) => {
|
|
890
|
-
runReflectRuntimeNote(id, text, options);
|
|
891
|
-
});
|
|
892
|
-
// --- context ---
|
|
893
|
-
program
|
|
894
|
-
.command('context')
|
|
895
|
-
.description('Generate compact context for agents')
|
|
896
|
-
.option('--for <target>', 'Task/path/goal to prioritize')
|
|
897
|
-
.option('--project <project>', 'Explicit project namespace for layered instructions')
|
|
898
|
-
.option('--agent <agent>', 'Agent name for agent-layer instructions')
|
|
899
|
-
.option('--host <host>', 'Include machine-local runtime notes for a specific host')
|
|
900
|
-
.option('--all-hosts', 'Include machine-local runtime notes from all hosts')
|
|
901
|
-
.option('--profile <profile>', 'Profile: dev, openclaw, ops, research')
|
|
902
|
-
.option('--include-pending', 'Include pending candidates')
|
|
903
|
-
.option('--max-items <count>', 'Limit number of items', parseInt)
|
|
904
|
-
.option('--max-chars <count>', 'Approximate output budget for selected item content', parseInt)
|
|
905
|
-
.option('--digest', 'Include a short deterministic digest ahead of the detailed context')
|
|
906
|
-
.option('--since-session <id>', 'Include a compact memory diff since the given session started')
|
|
907
|
-
.option('--no-bootstrap', 'Disable brownfield bootstrap fallback when canonical memory is sparse')
|
|
908
|
-
.option('--refresh-bootstrap', 'Refresh brownfield bootstrap profile before building context')
|
|
909
|
-
.option('--template', 'Output prompt-ready context template')
|
|
910
|
-
.option('--compact-template', 'Use compact template format (default for openclaw profile)')
|
|
911
|
-
.option('--explain', 'Show ranking reasons in human-readable output')
|
|
912
|
-
.option('--json', 'Output as JSON')
|
|
913
|
-
.action((options) => {
|
|
914
|
-
runContext(options);
|
|
915
|
-
});
|
|
916
|
-
// --- bootstrap ---
|
|
917
|
-
program
|
|
918
|
-
.command('bootstrap')
|
|
919
|
-
.description('Derive brownfield bootstrap signals and optionally import them into canonical memory')
|
|
920
|
-
.option('--for <target>', 'Target path or scope to tailor the bootstrap')
|
|
921
|
-
.option('--json', 'Output as JSON')
|
|
922
|
-
.option('--refresh', 'Force a fresh bootstrap scan instead of reusing the current profile')
|
|
923
|
-
.option('--interview', 'Render the adaptive interview prompts instead of the bootstrap summary')
|
|
924
|
-
.option('--audience <audience>', 'Target interview prompts for cli, ide_chat, or any')
|
|
925
|
-
.option('--answers-file <path>', 'Load structured bootstrap interview answers from a JSON file')
|
|
926
|
-
.option('--apply', 'Import the current bootstrap proposal into canonical memory')
|
|
927
|
-
.option('--uninstall', 'Deactivate the last bootstrap import managed by this workspace')
|
|
928
|
-
.option('-y, --yes', 'Skip confirmation prompts for apply/uninstall')
|
|
929
|
-
.action(async (options) => {
|
|
930
|
-
await runBootstrap(options);
|
|
931
|
-
});
|
|
932
|
-
// --- env ---
|
|
933
|
-
program
|
|
934
|
-
.command('env')
|
|
935
|
-
.description('Show the local execution context and optionally detected agent tooling')
|
|
936
|
-
.option('--json', 'Output as JSON')
|
|
937
|
-
.option('--agent-tooling', 'Include AGENTS.md, local skills, and local MCP inventory')
|
|
938
|
-
.action((options) => {
|
|
939
|
-
runEnv({ ...options, agentTooling: options.agentTooling });
|
|
940
|
-
});
|
|
941
|
-
// --- memory ---
|
|
942
|
-
program
|
|
943
|
-
.command('memory <subcommand> [args...]')
|
|
944
|
-
.description('Manage canonical memory items (create, list, update, delete)')
|
|
945
|
-
.option('--json', 'Output as JSON for list')
|
|
946
|
-
.option('--type <type>', 'Memory type/filter: decision, constraint, trap, handoff')
|
|
947
|
-
.option('--text <text>', 'Replacement text for memory update')
|
|
948
|
-
.option('--tag <tags...>', 'Tags')
|
|
949
|
-
.option('--path <paths...>', 'Related file paths')
|
|
950
|
-
.option('--author <author>', 'Author name')
|
|
951
|
-
.option('--outcome <outcome>', 'Decision outcome: approved, rejected, deferred, pending')
|
|
952
|
-
.option('--category <category>', 'Constraint category: architecture, performance, security, reliability, compatibility, process, other')
|
|
953
|
-
.option('--status <status>', 'Constraint, trap, or handoff status')
|
|
954
|
-
.option('--severity <severity>', 'Trap severity: low, medium, high')
|
|
955
|
-
.option('--project <project>', 'Optional project namespace')
|
|
956
|
-
.option('--plan <id>', 'Optional linked plan item ID')
|
|
957
|
-
.option('--from <from>', 'Handoff source')
|
|
958
|
-
.option('--to <to>', 'Handoff destination')
|
|
959
|
-
.option('--visibility <visibility>', 'Trap visibility: shared, machine, private', 'shared')
|
|
960
|
-
.option('--host <host>', 'Optional host identifier override for machine/private traps')
|
|
961
|
-
.option('--ttl <duration>', 'Time-to-live: 30m, 2h, 7d')
|
|
962
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace, user')
|
|
963
|
-
.action((subcommand, args, options) => {
|
|
964
|
-
runMemoryCommand(subcommand, args, options);
|
|
965
|
-
});
|
|
966
|
-
// --- instruction ---
|
|
967
|
-
program
|
|
968
|
-
.command('instruction <text>')
|
|
969
|
-
.description('Add a layered shared instruction')
|
|
970
|
-
.option('--layer <layer>', 'Instruction layer: global, project, agent', 'global')
|
|
971
|
-
.option('--project <project>', 'Project namespace when --layer project is used')
|
|
972
|
-
.option('--agent <agent>', 'Agent name when --layer agent is used')
|
|
973
|
-
.option('--tag <tags...>', 'Tags for this instruction')
|
|
974
|
-
.option('--author <author>', 'Author name')
|
|
975
|
-
.option('--supersedes <id>', 'Supersede an older instruction entry')
|
|
976
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace, user')
|
|
977
|
-
.action((text, options) => {
|
|
978
|
-
runInstruction(text, options);
|
|
979
|
-
});
|
|
980
|
-
// --- list-instructions ---
|
|
981
|
-
program
|
|
982
|
-
.command('list-instructions')
|
|
983
|
-
.description('List raw or resolved shared instructions')
|
|
984
|
-
.option('--json', 'Output as JSON')
|
|
985
|
-
.option('--layer <layer>', 'Filter by layer: global, project, agent')
|
|
986
|
-
.option('--project <project>', 'Project namespace filter')
|
|
987
|
-
.option('--agent <agent>', 'Agent name filter')
|
|
988
|
-
.option('--active', 'Only show active entries')
|
|
989
|
-
.option('--resolved', 'Resolve effective instructions for the given scope')
|
|
990
|
-
.option('--for <target>', 'Infer project namespace from target path when strategy=folder')
|
|
991
|
-
.action((options) => {
|
|
992
|
-
runListInstructions(options);
|
|
993
|
-
});
|
|
994
|
-
// --- register-agent ---
|
|
995
|
-
program
|
|
996
|
-
.command('register-agent <name>')
|
|
997
|
-
.description('Register an agent or human identity in project memory')
|
|
998
|
-
.option('--kind <kind>', 'Identity kind: agent, human, unknown', 'unknown')
|
|
999
|
-
.option('--capability <value>', 'Declare a capability on the agent profile (repeatable)', collect, [])
|
|
1000
|
-
.option('--replace-capabilities', 'Replace existing capabilities instead of merging')
|
|
1001
|
-
.option('--generate-fingerprint', 'Generate or rotate a local public identity fingerprint for this agent')
|
|
1002
|
-
.option('--set-current', 'Set this identity as the current agent in config')
|
|
1003
|
-
.option('--curator', 'Register this agent as a curator (project owner with direct-write access)')
|
|
1004
|
-
.option('--remove', 'Remove this identity instead of registering (guarded: debris identities only unless --force)')
|
|
1005
|
-
.option('--force', 'With --remove: allow removing a non-debris identity')
|
|
1006
|
-
.option('--json', 'Output as JSON')
|
|
1007
|
-
.action((name, options) => {
|
|
1008
|
-
if (options.remove) {
|
|
1009
|
-
runRemoveAgent(name, { force: options.force, json: options.json });
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1012
|
-
runRegisterAgent(name, options);
|
|
1013
|
-
});
|
|
1014
|
-
// --- enable-agent ---
|
|
1015
|
-
program
|
|
1016
|
-
.command('enable-agent <name>')
|
|
1017
|
-
.description('Activate a supported coding agent on an already initialized project')
|
|
1018
|
-
.option('--kind <kind>', 'Identity kind: agent, human, unknown', 'agent')
|
|
1019
|
-
.option('--context-profile <profile>', 'Default context profile: dev, dense, compact, copilot, quick, openclaw, ops, research')
|
|
1020
|
-
.option('--capability <value>', 'Declare a capability on the agent profile (repeatable)', collect, [])
|
|
1021
|
-
.option('--replace-capabilities', 'Replace existing capabilities instead of merging')
|
|
1022
|
-
.option('--generate-fingerprint', 'Generate or rotate a local public identity fingerprint for this agent')
|
|
1023
|
-
.option('--set-current', 'Set this identity as the current agent in config')
|
|
1024
|
-
.option('--json', 'Output as JSON')
|
|
1025
|
-
.action((name, options) => {
|
|
1026
|
-
runEnableAgent(name, options);
|
|
1027
|
-
});
|
|
1028
|
-
// --- list-agents ---
|
|
1029
|
-
program
|
|
1030
|
-
.command('list-agents')
|
|
1031
|
-
.description('List registered agent identities')
|
|
1032
|
-
.option('--json', 'Output as JSON')
|
|
1033
|
-
.option('--with-reputation', 'Include bounded reputation summaries when available')
|
|
1034
|
-
.action((options) => {
|
|
1035
|
-
runListAgents(options);
|
|
1036
|
-
});
|
|
1037
|
-
// --- review ---
|
|
1038
|
-
program
|
|
1039
|
-
.command('review')
|
|
1040
|
-
.description('List pending memory candidates')
|
|
1041
|
-
.option('--json', 'Output as JSON')
|
|
1042
|
-
.option('--type <type>', 'Filter by type')
|
|
1043
|
-
.option('--prioritized', 'Sort by review priority and show SLA')
|
|
1044
|
-
.option('--only-overdue', 'Show only candidates overdue review SLA')
|
|
1045
|
-
.option('--assignee <assignee>', 'Filter by assignee tag (assignee:<name>)')
|
|
1046
|
-
.option('--for-curator <curator>', 'Shortcut assignee filter for curator queue')
|
|
1047
|
-
.option('--take <count>', 'Limit number of reviewed items', parseInt)
|
|
1048
|
-
.option('--claim <curator>', 'Atomically assign selected candidates to a curator')
|
|
1049
|
-
.option('--auto', 'Auto-promote candidates meeting the configured score threshold')
|
|
1050
|
-
.option('--auto-by <actor>', 'Actor identity for auto-promotion (defaults to current user)')
|
|
1051
|
-
.action((options) => {
|
|
1052
|
-
runReview({ ...options, autoBy: options.autoBy });
|
|
1053
|
-
});
|
|
1054
|
-
// --- show-candidate ---
|
|
1055
|
-
program
|
|
1056
|
-
.command('show-candidate <id>')
|
|
1057
|
-
.description('Show details of a memory candidate')
|
|
1058
|
-
.option('--related', 'Show related candidates by shared tags/paths')
|
|
1059
|
-
.action((id, options) => {
|
|
1060
|
-
runShowCandidate(id, { related: options.related });
|
|
1061
|
-
});
|
|
1062
|
-
// --- star-candidate ---
|
|
1063
|
-
program
|
|
1064
|
-
.command('star-candidate <id>')
|
|
1065
|
-
.description('Add an adoption star to a pending candidate')
|
|
1066
|
-
.option('--by <actor>', 'Agent or person adding the star')
|
|
1067
|
-
.action((id, options) => {
|
|
1068
|
-
runStarCandidate(id, options);
|
|
1069
|
-
});
|
|
1070
|
-
// --- use-candidate ---
|
|
1071
|
-
program
|
|
1072
|
-
.command('use-candidate <id>')
|
|
1073
|
-
.description('Record that a pending candidate was reused in a concrete work context')
|
|
1074
|
-
.option('--by <actor>', 'Agent or person using the candidate')
|
|
1075
|
-
.requiredOption('--context <context>', 'Work context where the candidate was reused')
|
|
1076
|
-
.action((id, options) => {
|
|
1077
|
-
runUseCandidate(id, options);
|
|
1078
|
-
});
|
|
1079
|
-
// --- accept ---
|
|
1080
|
-
program
|
|
1081
|
-
.command('accept <id>')
|
|
1082
|
-
.description('Accept a candidate into canonical memory')
|
|
1083
|
-
.option('--by <reviewer>', 'Reviewer identity used for governance checks')
|
|
1084
|
-
.action((id, options) => {
|
|
1085
|
-
runAccept(id, options.by);
|
|
1086
|
-
});
|
|
1087
|
-
// --- adapter-openclaw-import ---
|
|
1088
|
-
program
|
|
1089
|
-
.command('adapter-openclaw-import [file]')
|
|
1090
|
-
.description('Import OpenClaw runtime events file as memory candidates')
|
|
1091
|
-
.option('--session <id>', 'Import runtime events from a session id in .brainclaw/runtime/')
|
|
1092
|
-
.option('--dry-run', 'Preview import without creating candidates')
|
|
1093
|
-
.option('--source <source>', 'Source label for imported candidates', 'openclaw')
|
|
1094
|
-
.option('--author <author>', 'Author for imported candidates')
|
|
1095
|
-
.action((file, options) => {
|
|
1096
|
-
runAdapterOpenclawImport(file, options);
|
|
1097
|
-
});
|
|
1098
|
-
// --- reject ---
|
|
1099
|
-
program
|
|
1100
|
-
.command('reject <id>')
|
|
1101
|
-
.description('Reject a memory candidate')
|
|
1102
|
-
.option('--by <reviewer>', 'Reviewer identity used for rejection attribution')
|
|
1103
|
-
.option('--reason <reason>', 'Reason for rejection')
|
|
1104
|
-
.action((id, options) => {
|
|
1105
|
-
runReject(id, options.reason, options.by);
|
|
1106
|
-
});
|
|
1107
|
-
// --- harvest-candidates ---
|
|
1108
|
-
program
|
|
1109
|
-
.command('harvest-candidates')
|
|
1110
|
-
.description('Harvest candidates from worktree inboxes into the main project store (codex sandbox bridge)')
|
|
1111
|
-
.option('--dry-run', 'Preview what would be imported without writing anything')
|
|
1112
|
-
.option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
|
|
1113
|
-
.option('--json', 'Output as JSON')
|
|
1114
|
-
.action((options) => {
|
|
1115
|
-
const globalOpts = program.opts();
|
|
1116
|
-
runHarvestCandidates({ ...options, cwd: globalOpts.cwd });
|
|
1117
|
-
});
|
|
1118
|
-
// --- harvest (lane results, pln#526) ---
|
|
1119
|
-
program
|
|
1120
|
-
.command('harvest [assignment_id]')
|
|
1121
|
-
.description('Harvest a worker LANE-RESULT.json from its worktree into the project (pass an assignment id, or --all)')
|
|
1122
|
-
.option('--all', 'Harvest every lane result found across worktrees')
|
|
1123
|
-
.option('--integrate', 'Worktree-as-contract (pln#534): commit the worktree diff on behalf of a sandboxed worker, lifecycle the assignment, and release the claim')
|
|
1124
|
-
.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')
|
|
1125
|
-
.option('--base <ref>', 'Base ref for --orphaned commits-ahead comparison (default: master)')
|
|
1126
|
-
.option('--dry-run', 'Preview without writing events/markers')
|
|
1127
|
-
.option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
|
|
1128
|
-
.option('--json', 'Output as JSON')
|
|
1129
|
-
.action((assignmentId, options) => {
|
|
1130
|
-
const globalOpts = program.opts();
|
|
1131
|
-
runHarvestLane(assignmentId, { ...options, cwd: globalOpts.cwd });
|
|
1132
|
-
});
|
|
1133
|
-
// --- prune-candidates ---
|
|
1134
|
-
program
|
|
1135
|
-
.command('prune-candidates')
|
|
1136
|
-
.description('Remove old rejected candidates')
|
|
1137
|
-
.option('--days <days>', 'Max age in days', parseInt)
|
|
1138
|
-
.option('--dry-run', 'Preview without deleting')
|
|
1139
|
-
.action((options) => {
|
|
1140
|
-
runPruneCandidates(options);
|
|
1141
|
-
});
|
|
1142
|
-
// --- cleanup-candidates ---
|
|
1143
|
-
program
|
|
1144
|
-
.command('cleanup-candidates')
|
|
1145
|
-
.description('Remove stale auto-generated pending candidates')
|
|
1146
|
-
.option('--max-age <days>', 'Max age in days before cleanup', parseInt)
|
|
1147
|
-
.option('--dry-run', 'Preview without deleting')
|
|
1148
|
-
.action((options) => {
|
|
1149
|
-
if (!memoryExists()) {
|
|
1150
|
-
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
1151
|
-
process.exit(1);
|
|
1152
|
-
}
|
|
1153
|
-
const maxAgeDays = options.maxAge ?? 30;
|
|
1154
|
-
const result = cleanupStaleCandidates({
|
|
1155
|
-
maxAgeDays,
|
|
1156
|
-
dryRun: options.dryRun,
|
|
1157
|
-
});
|
|
1158
|
-
if (result.matched === 0) {
|
|
1159
|
-
console.log(`No stale auto-generated candidates older than ${maxAgeDays} days found.`);
|
|
1160
|
-
return;
|
|
1161
|
-
}
|
|
1162
|
-
if (options.dryRun) {
|
|
1163
|
-
console.log(`Would remove ${result.matched} stale auto-generated candidate(s):`);
|
|
1164
|
-
for (const candidate of result.candidates) {
|
|
1165
|
-
console.log(` [${candidate.id}] ${candidate.text}`);
|
|
1166
|
-
}
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
console.log(`✔ Removed ${result.deleted} stale auto-generated candidate(s) older than ${maxAgeDays} days.`);
|
|
1170
|
-
});
|
|
1171
|
-
// --- claim ---
|
|
1172
|
-
program
|
|
1173
|
-
.command('claim <subcommand> [args...]')
|
|
1174
|
-
.description('Manage work claims (create, list, release)')
|
|
1175
|
-
.option('--agent <agent>', 'Agent or person name; defaults to the configured current agent')
|
|
1176
|
-
.option('--scope <scope>', 'Scope being claimed (e.g. file path, module)')
|
|
1177
|
-
.option('--project <project>', 'Optional project namespace for this claim')
|
|
1178
|
-
.option('--plan <id>', 'Optional linked plan item ID')
|
|
1179
|
-
.option('--ttl <duration>', 'Auto-expire after duration: 30m, 2h, 8h, 1d')
|
|
1180
|
-
.option('--all', 'Include released claims in list')
|
|
1181
|
-
.option('--json', 'Output as JSON for list')
|
|
1182
|
-
.option('--plan-status <status>', 'Optional linked plan status when releasing: todo, in_progress, blocked, done, dropped')
|
|
1183
|
-
.option('--coordinator-override', 'Trusted+ only: release a claim owned by another agent')
|
|
1184
|
-
.option('--store <target>', 'Target store level: local (default), repo, workspace')
|
|
1185
|
-
.option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
|
|
1186
|
-
.action((subcommand, args, options) => {
|
|
1187
|
-
runClaimResource(subcommand, args, {
|
|
1188
|
-
...options,
|
|
1189
|
-
planStatus: options.planStatus,
|
|
1190
|
-
coordinatorOverride: options.coordinatorOverride,
|
|
1191
|
-
localOnly: options.localOnly,
|
|
1192
|
-
});
|
|
1193
|
-
});
|
|
1194
|
-
// --- assignment ---
|
|
1195
|
-
program
|
|
1196
|
-
.command('assignment <subcommand> [args...]')
|
|
1197
|
-
.description('Manage work assignments (list, show, update, cancel)')
|
|
1198
|
-
.option('--json', 'Output as JSON for list/show')
|
|
1199
|
-
.option('--all', 'Include terminal assignments in list')
|
|
1200
|
-
.option('--status <status>', 'Status filter for list or target status for update')
|
|
1201
|
-
.option('--agent <agent>', 'Filter by agent name')
|
|
1202
|
-
.option('--claim <id>', 'Filter by linked claim ID')
|
|
1203
|
-
.option('--plan <id>', 'Filter by linked plan ID')
|
|
1204
|
-
.option('--sequence <id>', 'Filter by linked sequence ID')
|
|
1205
|
-
.option('--reason <text>', 'Optional status reason for update/cancel')
|
|
1206
|
-
.action((subcommand, args, options) => {
|
|
1207
|
-
runAssignmentResource(subcommand, args, {
|
|
1208
|
-
...options,
|
|
1209
|
-
claim: options.claim,
|
|
1210
|
-
plan: options.plan,
|
|
1211
|
-
sequence: options.sequence,
|
|
1212
|
-
});
|
|
1213
|
-
});
|
|
1214
|
-
// --- list-claims ---
|
|
1215
|
-
program
|
|
1216
|
-
.command('list-claims')
|
|
1217
|
-
.description('List work claims')
|
|
1218
|
-
.option('--json', 'Output as JSON')
|
|
1219
|
-
.option('--all', 'Include released claims')
|
|
1220
|
-
.option('--project <project>', 'Filter by project namespace')
|
|
1221
|
-
.option('--plan <id>', 'Filter by linked plan item')
|
|
1222
|
-
.option('--agent <agent>', 'Filter by agent name')
|
|
1223
|
-
.option('--local-only', 'Read from local store only (skip parent stores in chain)')
|
|
1224
|
-
.action((options) => {
|
|
1225
|
-
runListClaims({ ...options, localOnly: options.localOnly });
|
|
1226
|
-
});
|
|
1227
|
-
// --- release-claim ---
|
|
1228
|
-
program
|
|
1229
|
-
.command('release-claim <id>')
|
|
1230
|
-
.description('Release a work claim')
|
|
1231
|
-
.option('--plan-status <status>', 'Optional linked plan status: todo, in_progress, blocked, done, dropped')
|
|
1232
|
-
.option('--coordinator-override', 'Trusted+ only: release a claim owned by another agent')
|
|
1233
|
-
.action((id, options) => {
|
|
1234
|
-
runReleaseClaim(id, { ...options, coordinatorOverride: options.coordinatorOverride });
|
|
1235
|
-
});
|
|
1236
|
-
// --- release-claims ---
|
|
1237
|
-
program
|
|
1238
|
-
.command('release-claims')
|
|
1239
|
-
.description('Bulk-release claims whose scope overlaps with git-changed files')
|
|
1240
|
-
.option('--from-git-diff', 'Use ORIG_HEAD..HEAD diff to detect changed files (post-merge)')
|
|
1241
|
-
.option('--ref1 <ref>', 'First git ref (default: ORIG_HEAD)')
|
|
1242
|
-
.option('--ref2 <ref>', 'Second git ref (default: HEAD)')
|
|
1243
|
-
.action((options) => {
|
|
1244
|
-
runReleaseClaims({ fromGitDiff: options.fromGitDiff, ref1: options.ref1, ref2: options.ref2 });
|
|
1245
|
-
});
|
|
1246
|
-
// --- agent-board ---
|
|
1247
|
-
program
|
|
1248
|
-
.command('agent-board')
|
|
1249
|
-
.description('Show a coordination board for agents, plans, claims, handoffs, and instructions')
|
|
1250
|
-
.option('--agent <agent>', 'Filter by agent name')
|
|
1251
|
-
.option('--project <project>', 'Filter by project namespace')
|
|
1252
|
-
.option('--for <target>', 'Infer project from target path')
|
|
1253
|
-
.option('--host <host>', 'Include machine-local runtime notes for a specific host')
|
|
1254
|
-
.option('--all-hosts', 'Include machine-local runtime notes from all hosts')
|
|
1255
|
-
.option('--json', 'Output as JSON')
|
|
1256
|
-
.option('--with-reputation', 'Include bounded reputation summaries when available')
|
|
1257
|
-
.option('--capabilities', 'List all registered agents with their declared capabilities')
|
|
1258
|
-
.option('--suggest <query>', 'Suggest agents whose capabilities match a query string')
|
|
1259
|
-
.option('--include-session-meta', 'Include session_start/session_end runtime notes (hidden by default)')
|
|
1260
|
-
.option('--all-agents', 'Show unfiltered board (supervisor mode — all claims, all agents)')
|
|
1261
|
-
.action((options) => {
|
|
1262
|
-
runAgentBoard(options);
|
|
1263
|
-
});
|
|
1264
|
-
// --- runtime-note ---
|
|
1265
|
-
program
|
|
1266
|
-
.command('runtime-note <text>')
|
|
1267
|
-
.description('Add a runtime note for an agent')
|
|
1268
|
-
.option('--agent <agent>', 'Agent name; defaults to the configured current agent')
|
|
1269
|
-
.option('--project <project>', 'Optional project namespace')
|
|
1270
|
-
.option('--plan <id>', 'Optional linked plan item ID')
|
|
1271
|
-
.option('--visibility <visibility>', 'Visibility: shared, machine, private', 'shared')
|
|
1272
|
-
.option('--host <host>', 'Optional host identifier override for machine/private runtime notes')
|
|
1273
|
-
.option('--tag <tags...>', 'Tags')
|
|
1274
|
-
.option('--ttl <duration>', 'Time-to-live: 30m, 2h, 7d (note auto-expires after this duration)')
|
|
1275
|
-
.option('--auto-reflect', 'Attempt to turn this runtime note into durable memory immediately')
|
|
1276
|
-
.action((text, options) => {
|
|
1277
|
-
runRuntimeNote(text, { ...options, autoReflect: options.autoReflect });
|
|
1278
|
-
});
|
|
1279
|
-
// --- note create ---
|
|
1280
|
-
const noteCommand = program
|
|
1281
|
-
.command('note')
|
|
1282
|
-
.description('Manage runtime notes');
|
|
1283
|
-
noteCommand
|
|
1284
|
-
.command('create <text>')
|
|
1285
|
-
.description('Alias for runtime-note')
|
|
1286
|
-
.option('--agent <agent>', 'Agent name; defaults to the configured current agent')
|
|
1287
|
-
.option('--project <project>', 'Optional project namespace')
|
|
1288
|
-
.option('--plan <id>', 'Optional linked plan item ID')
|
|
1289
|
-
.option('--visibility <visibility>', 'Visibility: shared, machine, private', 'shared')
|
|
1290
|
-
.option('--host <host>', 'Optional host identifier override for machine/private runtime notes')
|
|
1291
|
-
.option('--tag <tags...>', 'Tags')
|
|
1292
|
-
.option('--ttl <duration>', 'Time-to-live: 30m, 2h, 7d (note auto-expires after this duration)')
|
|
1293
|
-
.option('--auto-reflect', 'Attempt to turn this runtime note into durable memory immediately')
|
|
1294
|
-
.action((text, options) => {
|
|
1295
|
-
runRuntimeNote(text, { ...options, autoReflect: options.autoReflect });
|
|
1296
|
-
});
|
|
1297
|
-
// --- runtime-status ---
|
|
1298
|
-
program
|
|
1299
|
-
.command('runtime-status')
|
|
1300
|
-
.description('Show runtime notes')
|
|
1301
|
-
.option('--agent <agent>', 'Filter by agent')
|
|
1302
|
-
.option('--plan <id>', 'Filter by linked plan item')
|
|
1303
|
-
.option('--visibility <visibility>', 'Visibility filter: shared, machine, private, all')
|
|
1304
|
-
.option('--host <host>', 'Include machine-local notes for a specific host')
|
|
1305
|
-
.option('--all-hosts', 'Include machine-local notes from all hosts')
|
|
1306
|
-
.option('--json', 'Output as JSON')
|
|
1307
|
-
.action((options) => {
|
|
1308
|
-
runRuntimeStatus(options);
|
|
1309
|
-
});
|
|
1310
|
-
// --- sync ---
|
|
1311
|
-
program
|
|
1312
|
-
.command('sync')
|
|
1313
|
-
.description('Summarize memory changes and optionally commit')
|
|
1314
|
-
.option('--commit', 'Create a local git commit')
|
|
1315
|
-
.option('--message <message>', 'Custom commit message')
|
|
1316
|
-
.option('--summary-only', 'Print summary only; skip git status and commit checks')
|
|
1317
|
-
.option('--scope <scope>', 'Scope: all, state, config, project, inbox, archive, claims, runtime, runtime-local, trap-local')
|
|
1318
|
-
.option('--include-machine-runtime', 'Include machine-local runtime memory in sync scope for all')
|
|
1319
|
-
.option('--remote', 'Pull + push from/to remote memory repo in one step')
|
|
1320
|
-
.action((options) => {
|
|
1321
|
-
runSync({ ...options, remote: options.remote });
|
|
1322
|
-
});
|
|
1323
|
-
// --- check-constraints ---
|
|
1324
|
-
program
|
|
1325
|
-
.command('check-constraints')
|
|
1326
|
-
.description('Check if staged files (or given files) violate active constraints')
|
|
1327
|
-
.option('--staged', 'Check git staged files (git diff --cached --name-only)')
|
|
1328
|
-
.option('--files <files...>', 'Explicit list of files to check')
|
|
1329
|
-
.option('--json', 'Output as JSON')
|
|
1330
|
-
.action((options) => {
|
|
1331
|
-
runCheckConstraints({ staged: options.staged, files: options.files, json: options.json });
|
|
1332
|
-
});
|
|
1333
|
-
// --- check-policy ---
|
|
1334
|
-
program
|
|
1335
|
-
.command('check-policy')
|
|
1336
|
-
.description('Pre-execution policy check: verify claims, constraints, traps and instructions for a scope')
|
|
1337
|
-
.requiredOption('--scope <path>', 'File or directory scope to check')
|
|
1338
|
-
.option('--agent <name>', 'Agent name to check claims for')
|
|
1339
|
-
.option('--agent-id <id>', 'Agent id to check claims for')
|
|
1340
|
-
.option('--action <action>', 'Intended action: edit, create, delete')
|
|
1341
|
-
.option('--json', 'Output as JSON')
|
|
1342
|
-
.action((options) => {
|
|
1343
|
-
runCheckPolicy({
|
|
1344
|
-
scope: options.scope,
|
|
1345
|
-
agent: options.agent,
|
|
1346
|
-
agentId: options.agentId,
|
|
1347
|
-
action: options.action,
|
|
1348
|
-
json: options.json,
|
|
1349
|
-
});
|
|
1350
|
-
});
|
|
1351
|
-
// --- check-security ---
|
|
1352
|
-
program
|
|
1353
|
-
.command('check-security')
|
|
1354
|
-
.description('Check supply chain security scores for packages via Socket.dev')
|
|
1355
|
-
.option('--packages <names>', 'Comma-separated package names (e.g. "axios,express" or "axios@1.14.1")')
|
|
1356
|
-
.option('--requirements <file>', 'Path to a pip-style requirements.txt to scan')
|
|
1357
|
-
.option('--lockfile <file>', 'Path to a package-lock.json (npm) to scan top-level deps')
|
|
1358
|
-
.option('--ecosystem <type>', 'Package ecosystem: npm or pypi', 'npm')
|
|
1359
|
-
.option('--mode <mode>', 'Override security mode: advisory or enforced (defaults to config)')
|
|
1360
|
-
.option('--json', 'Output as JSON')
|
|
1361
|
-
.action(async (options) => {
|
|
1362
|
-
await runCheckSecurity({
|
|
1363
|
-
packages: options.packages,
|
|
1364
|
-
requirements: options.requirements,
|
|
1365
|
-
lockfile: options.lockfile,
|
|
1366
|
-
ecosystem: options.ecosystem,
|
|
1367
|
-
mode: options.mode,
|
|
1368
|
-
json: options.json,
|
|
1369
|
-
});
|
|
1370
|
-
});
|
|
1371
|
-
// --- setup-security ---
|
|
1372
|
-
program
|
|
1373
|
-
.command('setup-security')
|
|
1374
|
-
.description('Enable supply chain security gate: generate wrapper scripts and configure preinstall checks')
|
|
1375
|
-
.option('--mode <mode>', 'Security mode: advisory (default) or enforced', 'advisory')
|
|
1376
|
-
.action((options) => {
|
|
1377
|
-
runSetupSecurity({ mode: options.mode });
|
|
1378
|
-
});
|
|
1379
|
-
// --- install-hooks ---
|
|
1380
|
-
program
|
|
1381
|
-
.command('install-hooks')
|
|
1382
|
-
.description('Install a Git pre-commit hook that blocks sensitive content in .brainclaw/ and checks active constraints')
|
|
1383
|
-
.option('--force', 'Overwrite existing pre-commit hook')
|
|
1384
|
-
.action((options) => {
|
|
1385
|
-
runInstallHooks(options);
|
|
1386
|
-
});
|
|
1387
|
-
// --- diff ---
|
|
1388
|
-
program
|
|
1389
|
-
.command('diff')
|
|
1390
|
-
.description('Show what changed in .brainclaw/ since a timestamp or last context read')
|
|
1391
|
-
.option('--since <timestamp>', 'ISO 8601 timestamp to diff from')
|
|
1392
|
-
.option('--json', 'Output as JSON')
|
|
1393
|
-
.action((options) => {
|
|
1394
|
-
runDiff(options);
|
|
1395
|
-
});
|
|
1396
|
-
// --- prune ---
|
|
1397
|
-
program
|
|
1398
|
-
.command('prune')
|
|
1399
|
-
.description('Prune expired constraints and stale memory')
|
|
1400
|
-
.option('--expired', 'Also prune expired runtime notes and traps')
|
|
1401
|
-
.option('--archive', 'Archive done plans and closed handoffs (>30 days) to cold storage JSONL')
|
|
1402
|
-
.option('--semantic', 'Detect near-duplicate clusters and stale items via semantic analysis')
|
|
1403
|
-
.option('--dry-run', 'Preview compaction without applying (use with --semantic)')
|
|
1404
|
-
.action((options) => {
|
|
1405
|
-
runPrune(options);
|
|
1406
|
-
});
|
|
1407
|
-
// --- compact ---
|
|
1408
|
-
program
|
|
1409
|
-
.command('compact')
|
|
1410
|
-
.description('LLM-driven semantic memory compaction — archive old items and get a summary template')
|
|
1411
|
-
.option('--assess', 'Show pressure assessment and compaction template without archiving')
|
|
1412
|
-
.option('--dry-run', 'Preview eligible items without archiving')
|
|
1413
|
-
.option('--max-items <n>', 'Maximum items to compact (default: 20)', parseInt)
|
|
1414
|
-
.option('--min-age <days>', 'Minimum age in days for eligibility (default: 7)', parseInt)
|
|
1415
|
-
.option('--no-dedup-handoffs', 'Skip deduplication of auto-generated session-end handoffs')
|
|
1416
|
-
.option('--no-purge-claims', 'Skip archival of released claims')
|
|
1417
|
-
.option('--no-purge-session-notes', 'Skip archival of session-lifecycle runtime_notes')
|
|
1418
|
-
.action((options) => {
|
|
1419
|
-
runCompact(options);
|
|
1420
|
-
});
|
|
1421
|
-
// --- mcp ---
|
|
1422
|
-
program
|
|
1423
|
-
.command('mcp')
|
|
1424
|
-
.description('Start the standalone MCP server over stdio')
|
|
1425
|
-
.action(() => {
|
|
1426
|
-
runMcp();
|
|
1427
|
-
});
|
|
1428
|
-
// --- set-trust ---
|
|
1429
|
-
program
|
|
1430
|
-
.command('set-trust <agent>')
|
|
1431
|
-
.description('Set the trust level for a registered agent or manage circuit-breaker state')
|
|
1432
|
-
.option('--level <level>', 'Trust level: observer, contributor, trusted, curator')
|
|
1433
|
-
.option('--reset-breaker', 'Reset circuit-breaker suspension for the agent (restores auto-promote)')
|
|
1434
|
-
.option('--json', 'Output as JSON')
|
|
1435
|
-
.action((agent, options) => {
|
|
1436
|
-
runSetTrust(agent, { level: options.level, resetBreaker: options.resetBreaker, json: options.json });
|
|
1437
|
-
});
|
|
1438
|
-
// --- session-start ---
|
|
1439
|
-
program
|
|
1440
|
-
.command('session-start')
|
|
1441
|
-
.description('Start a memory session and capture initial context')
|
|
1442
|
-
.option('--agent <agent>', 'Agent name (defaults to current configured agent)')
|
|
1443
|
-
.option('--context <path>', 'Context target path for initial hash capture')
|
|
1444
|
-
.option('--model <id>', 'Model identifier (e.g. claude-sonnet-4-6)')
|
|
1445
|
-
.option('--maintenance-mode <mode>', 'Maintenance mode: full (default) or fast')
|
|
1446
|
-
.option('--include-context', 'Output full project context after starting session (replaces separate context call)')
|
|
1447
|
-
.option('--hook', 'Hook mode: degrade to exit 0 + ~/.brainclaw/hook.log on failure (advisory session hooks)')
|
|
1448
|
-
.option('--json', 'Output as JSON')
|
|
1449
|
-
.action(async (options) => {
|
|
1450
|
-
await runSessionStart(options);
|
|
1451
|
-
});
|
|
1452
|
-
// --- session-end ---
|
|
1453
|
-
program
|
|
1454
|
-
.command('session-end')
|
|
1455
|
-
.description('End a memory session and optionally auto-reflect observations')
|
|
1456
|
-
.option('--session <id>', 'Session ID (defaults to BRAINCLAW_SESSION_ID env var)')
|
|
1457
|
-
.option('--agent <agent>', 'Agent name')
|
|
1458
|
-
.option('--summary <text>', 'Session summary text')
|
|
1459
|
-
.option('--auto-reflect', 'Auto-reflect session notes as pending candidates')
|
|
1460
|
-
.option('--auto-release', 'Auto-release any active claims at session end')
|
|
1461
|
-
.option('--reflect-handoff', 'Materialize an open handoff from git commits since session start')
|
|
1462
|
-
.option('--dispatch-review', 'When used with --reflect-handoff, auto-dispatch a code review if the handoff is reviewable')
|
|
1463
|
-
.option('--reviewer <name>', 'Explicit reviewer to route the reflected handoff review to')
|
|
1464
|
-
.option('--no-reflect', 'Suppress the dogfooding reflection prompt (project + your surfaces/skills/tools), shown by default')
|
|
1465
|
-
.option('--hook', 'Hook mode: degrade to exit 0 + ~/.brainclaw/hook.log on failure (advisory Stop hook)')
|
|
1466
|
-
.option('--json', 'Output as JSON')
|
|
1467
|
-
.action(async (options) => {
|
|
1468
|
-
await runSessionEnd({
|
|
1469
|
-
...options,
|
|
1470
|
-
autoReflect: options.autoReflect,
|
|
1471
|
-
autoRelease: options.autoRelease,
|
|
1472
|
-
reflectHandoff: options.reflectHandoff,
|
|
1473
|
-
dispatchReview: options.dispatchReview,
|
|
1474
|
-
reviewer: options.reviewer,
|
|
1475
|
-
reflect: options.reflect,
|
|
1476
|
-
hook: options.hook,
|
|
1477
|
-
});
|
|
1478
|
-
});
|
|
1479
|
-
// --- whoami ---
|
|
1480
|
-
program
|
|
1481
|
-
.command('whoami')
|
|
1482
|
-
.description('Show the current resolved agent identity and trust level')
|
|
1483
|
-
.option('--json', 'Output as JSON')
|
|
1484
|
-
.action((options) => {
|
|
1485
|
-
runWhoami(options);
|
|
1486
|
-
});
|
|
1487
|
-
// --- usage ---
|
|
1488
|
-
program
|
|
1489
|
-
.command('usage')
|
|
1490
|
-
.description('Show brainclaw context volume stats (tokens injected per agent/tool)')
|
|
1491
|
-
.option('--agent <name>', 'Filter by agent name')
|
|
1492
|
-
.option('--tool <name>', 'Filter by tool name')
|
|
1493
|
-
.option('--days <n>', 'Limit to last N days', parseInt)
|
|
1494
|
-
.option('--json', 'Output as JSON')
|
|
1495
|
-
.action((options) => {
|
|
1496
|
-
runUsage(options);
|
|
1497
|
-
});
|
|
1498
|
-
// --- search ---
|
|
1499
|
-
program
|
|
1500
|
-
.command('search <query>')
|
|
1501
|
-
.description('Full-text search across project memory')
|
|
1502
|
-
.option('--section <section>', 'Filter by section: constraints, decisions, traps, handoffs, plans')
|
|
1503
|
-
.option('--since <timestamp>', 'ISO timestamp filter')
|
|
1504
|
-
.option('--tag <tags...>', 'Filter by tags')
|
|
1505
|
-
.option('--pending', 'Include pending candidates')
|
|
1506
|
-
.option('--max-results <count>', 'Maximum results to return', parseInt)
|
|
1507
|
-
.option('--json', 'Output as JSON')
|
|
1508
|
-
.action((query, options) => {
|
|
1509
|
-
runSearch(query, options);
|
|
1510
|
-
});
|
|
1511
|
-
// --- export ---
|
|
1512
|
-
program
|
|
1513
|
-
.command('export')
|
|
1514
|
-
.description('Export memory as instructions for IDE/AI tools')
|
|
1515
|
-
.option('--format <format>', 'Format: copilot-instructions, cursor-rules, agents-md, claude-md, gemini-md, windsurf, cline, roo, continue, openclaw, nanoclaw, nemoclaw, picoclaw, zeroclaw')
|
|
1516
|
-
.option('--detect', 'Auto-detect agent environment and write to its native file')
|
|
1517
|
-
.option('--all', 'Write all known agent instruction files at once (claude-md, agents-md, copilot-instructions, cursor-rules, etc.)')
|
|
1518
|
-
.option('--write', 'Write to canonical file path instead of stdout (when --format is given); local files are gitignored by default')
|
|
1519
|
-
.option('--include-live', 'Also write the native live companion file when the target agent supports one')
|
|
1520
|
-
.option('--shared', 'Keep the main exported instruction file versionable instead of auto-ignoring it (companions remain local)')
|
|
1521
|
-
.option('--output <file>', 'Write to a specific file path instead of stdout')
|
|
1522
|
-
.option('--project <project>', 'Project namespace filter')
|
|
1523
|
-
.option('--agent <agent>', 'Agent name for agent-layer instructions')
|
|
1524
|
-
.action((options) => {
|
|
1525
|
-
runExport(options);
|
|
1526
|
-
});
|
|
1527
|
-
program
|
|
1528
|
-
.command('refresh')
|
|
1529
|
-
.description('Refresh live companion files with current state (plans, claims, traps, sequences). Gitignored, safe to run frequently.')
|
|
1530
|
-
.action(() => {
|
|
1531
|
-
runRefresh();
|
|
1532
|
-
});
|
|
1533
|
-
program
|
|
1534
|
-
.command('reconcile')
|
|
1535
|
-
.description('Refresh machine and workspace bootstrap state after updates or onboarding on complex installs')
|
|
1536
|
-
.option('--json', 'Output as JSON')
|
|
1537
|
-
.option('--dry-run', 'Preview the reconciliation plan without writing machine or bootstrap state')
|
|
1538
|
-
.option('--apply-bootstrap', 'Apply bootstrap suggestions across all selected stores after refresh')
|
|
1539
|
-
.option('-y, --yes', 'Skip confirmation prompts for multi-store bootstrap apply')
|
|
1540
|
-
.option('--skip-machine-profile', 'Skip machine-profile refresh')
|
|
1541
|
-
.option('--skip-agent-inventory', 'Skip agent-inventory refresh')
|
|
1542
|
-
.action(async (options) => {
|
|
1543
|
-
await runReconcile({
|
|
1544
|
-
json: options.json,
|
|
1545
|
-
dryRun: options.dryRun,
|
|
1546
|
-
applyBootstrap: options.applyBootstrap,
|
|
1547
|
-
yes: options.yes,
|
|
1548
|
-
skipMachineProfile: options.skipMachineProfile,
|
|
1549
|
-
skipAgentInventory: options.skipAgentInventory,
|
|
1550
|
-
});
|
|
1551
|
-
});
|
|
1552
|
-
// --- hooks ---
|
|
1553
|
-
program
|
|
1554
|
-
.command('hooks')
|
|
1555
|
-
.description('Write deterministic session-trigger hooks for Cursor, Windsurf, and Claude Code (PostToolUse event check)')
|
|
1556
|
-
.option('--target <target>', 'Which hooks to write: cursor, windsurf, claude-code, all (default: all)')
|
|
1557
|
-
.action((options) => {
|
|
1558
|
-
runHooks(options);
|
|
1559
|
-
});
|
|
1560
|
-
// --- watch ---
|
|
1561
|
-
program
|
|
1562
|
-
.command('watch')
|
|
1563
|
-
.description('Watch for memory changes and emit NDJSON events on stdout')
|
|
1564
|
-
.option('--interval <seconds>', 'Poll interval in seconds', parseInt)
|
|
1565
|
-
.option('--auto-claim', 'Auto-create advisory claims on first write to workspace files')
|
|
1566
|
-
.option('--agent <name>', 'Agent name for auto-claim')
|
|
1567
|
-
.action((options) => {
|
|
1568
|
-
runWatch({ ...options, autoClaim: options.autoClaim });
|
|
1569
|
-
});
|
|
1570
|
-
// --- dispatch ---
|
|
1571
|
-
const dispatchCmd = program
|
|
1572
|
-
.command('dispatch')
|
|
1573
|
-
.description('Local agent dispatcher — analyze lanes and assign work');
|
|
1574
|
-
dispatchCmd
|
|
1575
|
-
.command('analysis')
|
|
1576
|
-
.description('Analyze the active sequence: show ready, active, blocked, and done lanes')
|
|
1577
|
-
.option('--json', 'Output as JSON')
|
|
1578
|
-
.action((options) => {
|
|
1579
|
-
runDispatchAnalysis({ json: options.json });
|
|
1580
|
-
});
|
|
1581
|
-
dispatchCmd
|
|
1582
|
-
.command('run')
|
|
1583
|
-
.description('Run a dispatch cycle: assign ready lanes to available agents')
|
|
1584
|
-
.option('--agents <names>', 'Comma-separated list of agents to dispatch to')
|
|
1585
|
-
.option('--lanes <names>', 'Comma-separated list of lanes to dispatch')
|
|
1586
|
-
.option('--max <n>', 'Maximum assignments', parseInt)
|
|
1587
|
-
.option('--max-concurrency <n>', 'Opt-in cap on concurrent instances per host-binary (default: unlimited)', parseInt)
|
|
1588
|
-
.option('--model <name>', 'Model to run, decoupled from agent identity (e.g. --model sonnet)')
|
|
1589
|
-
.option('--dry', 'Preview assignments without sending messages')
|
|
1590
|
-
.option('--spawn', 'Autonomously launch CLI agents with invoke templates')
|
|
1591
|
-
.option('--agent <name>', 'Dispatcher agent name')
|
|
1592
|
-
.option('--json', 'Output as JSON')
|
|
1593
|
-
.action(async (options) => {
|
|
1594
|
-
await runDispatch({
|
|
1595
|
-
agents: options.agents,
|
|
1596
|
-
lanes: options.lanes,
|
|
1597
|
-
max: options.max,
|
|
1598
|
-
maxConcurrency: options.maxConcurrency,
|
|
1599
|
-
model: options.model,
|
|
1600
|
-
dry: options.dry,
|
|
1601
|
-
spawn: options.spawn,
|
|
1602
|
-
agent: options.agent,
|
|
1603
|
-
json: options.json,
|
|
1604
|
-
});
|
|
1605
|
-
});
|
|
1606
|
-
dispatchCmd
|
|
1607
|
-
.command('watch <target>')
|
|
1608
|
-
.description('Block until a dispatched worker reaches a terminal state (asgn_/clm_/run_ id) — sentinels, lane-result, committed-clean and worker-process-gone heuristics')
|
|
1609
|
-
.option('--interval <seconds>', 'Poll interval in seconds (default 60)', parseInt)
|
|
1610
|
-
.option('--timeout <minutes>', 'Give up after N minutes (default 90, exit code 2)', parseInt)
|
|
1611
|
-
.option('--base <ref>', 'Base ref for commits-ahead evidence (default master)')
|
|
1612
|
-
.option('--json', 'One JSON object per poll line')
|
|
1613
|
-
.action(async (target, options) => {
|
|
1614
|
-
await runDispatchWatch(target, {
|
|
1615
|
-
intervalSeconds: options.interval,
|
|
1616
|
-
timeoutMinutes: options.timeout,
|
|
1617
|
-
base: options.base,
|
|
1618
|
-
json: options.json,
|
|
1619
|
-
});
|
|
1620
|
-
});
|
|
1621
|
-
dispatchCmd
|
|
1622
|
-
.command('review')
|
|
1623
|
-
.description('Dispatch code reviews for completed handoffs')
|
|
1624
|
-
.option('--handoff <id>', 'Specific handoff ID to review')
|
|
1625
|
-
.option('--reviewer <name>', 'Specific reviewer agent')
|
|
1626
|
-
.option('--spawn', 'Launch the reviewer CLI agent')
|
|
1627
|
-
.option('--dry', 'Preview without sending')
|
|
1628
|
-
.option('--agent <name>', 'Dispatcher agent name')
|
|
1629
|
-
.option('--json', 'Output as JSON')
|
|
1630
|
-
.action((options) => {
|
|
1631
|
-
runDispatchReview({
|
|
1632
|
-
handoff: options.handoff,
|
|
1633
|
-
reviewer: options.reviewer,
|
|
1634
|
-
spawn: options.spawn,
|
|
1635
|
-
dry: options.dry,
|
|
1636
|
-
agent: options.agent,
|
|
1637
|
-
json: options.json,
|
|
1638
|
-
});
|
|
1639
|
-
});
|
|
1640
|
-
// --- inbox ---
|
|
1641
|
-
const inboxCmd = program
|
|
1642
|
-
.command('inbox')
|
|
1643
|
-
.description('Inter-agent messaging inbox');
|
|
1644
|
-
inboxCmd
|
|
1645
|
-
.command('list')
|
|
1646
|
-
.description('List inbox messages (default: pending only)')
|
|
1647
|
-
.option('--agent <name>', 'Agent name')
|
|
1648
|
-
.option('--status <status>', 'Filter by status: pending, read, acknowledged, archived')
|
|
1649
|
-
.option('--type <type>', 'Filter by type: assign, review, rfc, info, reply')
|
|
1650
|
-
.option('--thread <id>', 'Filter by thread ID')
|
|
1651
|
-
.option('--all', 'Show all messages, not just pending')
|
|
1652
|
-
.option('--json', 'Output as JSON')
|
|
1653
|
-
.option('--local-only', 'Read from local store only (skip parent stores in chain)')
|
|
1654
|
-
.action((options) => {
|
|
1655
|
-
runInboxList({ ...options, localOnly: options.localOnly });
|
|
1656
|
-
});
|
|
1657
|
-
inboxCmd
|
|
1658
|
-
.command('ack <id>')
|
|
1659
|
-
.description('Acknowledge a message')
|
|
1660
|
-
.option('--agent <name>', 'Agent name')
|
|
1661
|
-
.option('--json', 'Output as JSON')
|
|
1662
|
-
.action((id, options) => {
|
|
1663
|
-
runInboxAck(id, options);
|
|
1664
|
-
});
|
|
1665
|
-
inboxCmd
|
|
1666
|
-
.command('archive <id>')
|
|
1667
|
-
.description('Archive a message')
|
|
1668
|
-
.option('--agent <name>', 'Agent name')
|
|
1669
|
-
.option('--json', 'Output as JSON')
|
|
1670
|
-
.action((id, options) => {
|
|
1671
|
-
runInboxArchive(id, options);
|
|
1672
|
-
});
|
|
1673
|
-
inboxCmd
|
|
1674
|
-
.command('send <to> <text>')
|
|
1675
|
-
.description('Send a message to another agent')
|
|
1676
|
-
.option('--type <type>', 'Message type: assign, review, rfc, info, reply (default: info)')
|
|
1677
|
-
.option('--ref <id>', 'Reference to a plan, sequence, or other entity')
|
|
1678
|
-
.option('--scope <path>', 'File scope')
|
|
1679
|
-
.option('--thread <id>', 'Thread ID for conversations')
|
|
1680
|
-
.option('--ack', 'Require acknowledgment')
|
|
1681
|
-
.option('--agent <name>', 'Sender agent name')
|
|
1682
|
-
.option('--json', 'Output as JSON')
|
|
1683
|
-
.action((to, text, options) => {
|
|
1684
|
-
runInboxSend(to, text, options);
|
|
1685
|
-
});
|
|
1686
|
-
inboxCmd
|
|
1687
|
-
.command('thread <id>')
|
|
1688
|
-
.description('Show all messages in a thread')
|
|
1689
|
-
.option('--json', 'Output as JSON')
|
|
1690
|
-
.action((id, options) => {
|
|
1691
|
-
runInboxThread(id, options);
|
|
1692
|
-
});
|
|
1693
|
-
// --- check-events ---
|
|
1694
|
-
program
|
|
1695
|
-
.command('check-events')
|
|
1696
|
-
.description('Show unseen events from the event bus (events.jsonl) for the current agent')
|
|
1697
|
-
.option('--agent <name>', 'Agent name for cursor lookup (default: auto-detected)')
|
|
1698
|
-
.option('--json', 'Output as JSON')
|
|
1699
|
-
.action((options) => {
|
|
1700
|
-
runCheckEvents(options);
|
|
1701
|
-
});
|
|
1702
|
-
// --- metrics ---
|
|
1703
|
-
program
|
|
1704
|
-
.command('metrics')
|
|
1705
|
-
.description('Show memory health metrics dashboard')
|
|
1706
|
-
.option('--json', 'Output as JSON')
|
|
1707
|
-
.option('--since <timestamp>', 'Filter audit log stats from this ISO timestamp')
|
|
1708
|
-
.action((options) => {
|
|
1709
|
-
runMetrics(options);
|
|
1710
|
-
});
|
|
1711
|
-
// --- rollback ---
|
|
1712
|
-
program
|
|
1713
|
-
.command('rollback')
|
|
1714
|
-
.description('Restore a memory item to a previous state from audit log')
|
|
1715
|
-
.option('--audit-id <timestamp>', 'Audit log entry timestamp to roll back')
|
|
1716
|
-
.option('--item-id <id>', 'Memory item ID to roll back (uses most recent audit entry with before-state)')
|
|
1717
|
-
.option('--dry-run', 'Preview rollback without applying changes')
|
|
1718
|
-
.option('--json', 'Output as JSON')
|
|
1719
|
-
.action((options) => {
|
|
1720
|
-
runRollback({ auditId: options.auditId, itemId: options.itemId, dryRun: options.dryRun, json: options.json });
|
|
1721
|
-
});
|
|
1722
|
-
// --- pull ---
|
|
1723
|
-
program
|
|
1724
|
-
.command('pull')
|
|
1725
|
-
.description('Pull memory updates from a git remote')
|
|
1726
|
-
.option('--remote <remote>', 'Remote name (defaults to origin)')
|
|
1727
|
-
.option('--json', 'Output as JSON')
|
|
1728
|
-
.action((options) => {
|
|
1729
|
-
runPull(options);
|
|
1730
|
-
});
|
|
1731
|
-
// --- push ---
|
|
1732
|
-
program
|
|
1733
|
-
.command('push')
|
|
1734
|
-
.description('Push memory updates to a git remote')
|
|
1735
|
-
.option('--remote <remote>', 'Remote name (defaults to origin)')
|
|
1736
|
-
.option('--message <message>', 'Custom commit message')
|
|
1737
|
-
.option('--json', 'Output as JSON')
|
|
1738
|
-
.action((options) => {
|
|
1739
|
-
runPush(options);
|
|
1740
|
-
});
|
|
1741
|
-
// --- audit ---
|
|
1742
|
-
program
|
|
1743
|
-
.command('audit')
|
|
1744
|
-
.description('View the append-only audit log, or generate a governance posture report with --governance')
|
|
1745
|
-
.option('--since <date>', 'Show entries since this ISO date')
|
|
1746
|
-
.option('--actor <agent>', 'Filter by actor name or agent ID')
|
|
1747
|
-
.option('--action <action>', 'Filter by action type (create, accept, reject, etc.)')
|
|
1748
|
-
.option('--limit <n>', 'Show last N entries', parseInt)
|
|
1749
|
-
.option('--json', 'Output as JSON')
|
|
1750
|
-
.option('--governance', 'Generate a governance posture report (aggregated view of claims, constraints, traps, instructions)')
|
|
1751
|
-
.option('--scope <path>', 'Filter governance report by scope (used with --governance)')
|
|
1752
|
-
.action((options) => {
|
|
1753
|
-
runAuditCommand({ since: options.since, actor: options.actor, action: options.action, limit: options.limit, json: options.json, governance: options.governance, scope: options.scope });
|
|
1754
|
-
});
|
|
1755
|
-
// --- history ---
|
|
1756
|
-
program
|
|
1757
|
-
.command('history <id>')
|
|
1758
|
-
.description('Show full mutation history of a memory item from the audit log')
|
|
1759
|
-
.action((id) => {
|
|
1760
|
-
runHistory(id);
|
|
1761
|
-
});
|
|
1762
|
-
// --- context-diff ---
|
|
1763
|
-
program
|
|
1764
|
-
.command('context-diff')
|
|
1765
|
-
.description('Show what changed in memory since last context read, a session start, or a given timestamp')
|
|
1766
|
-
.option('--since <date>', 'Show changes since this ISO date')
|
|
1767
|
-
.option('--session <id>', 'Show changes since the start of this session')
|
|
1768
|
-
.option('--hook', 'Hook mode: exit 0 silently when there is no diff baseline (advisory session hooks)')
|
|
1769
|
-
.option('--json', 'Output as JSON')
|
|
1770
|
-
.action((options) => {
|
|
1771
|
-
runContextDiff({ since: options.since, session: options.session, json: options.json, hook: options.hook });
|
|
1772
|
-
});
|
|
1773
|
-
program
|
|
1774
|
-
.command('capability <subcommand> [args...]')
|
|
1775
|
-
.description('Manage project capabilities (list, add, describe)')
|
|
1776
|
-
.option('--tag <tag>', 'Tag for categorization (repeatable)', (val, prev) => [...(prev || []), val])
|
|
1777
|
-
.option('--author <name>', 'Author name')
|
|
1778
|
-
.option('--store <target>', 'Store level: local (default), repo, workspace, user')
|
|
1779
|
-
.action((subcommand, args, options) => {
|
|
1780
|
-
runCapability(subcommand, args, {
|
|
1781
|
-
tag: options.tag,
|
|
1782
|
-
author: options.author,
|
|
1783
|
-
store: options.store,
|
|
1784
|
-
});
|
|
1785
|
-
});
|
|
1786
|
-
program
|
|
1787
|
-
.command('link <subcommand> [args...]')
|
|
1788
|
-
.description('Manage cross-project federation links (add, list, remove)')
|
|
1789
|
-
.option('--name <slug>', 'Override the auto-derived link name')
|
|
1790
|
-
.option('--role <role>', 'Link role: publisher (push signals out) or subscriber (default)')
|
|
1791
|
-
.option('--channels <list>', 'Comma-separated allow-list of channels: candidate,handoff,runtime_note', (val) => val.split(',').map((s) => s.trim()).filter(Boolean))
|
|
1792
|
-
.option('--force', 'Replace an existing link of the same name/path')
|
|
1793
|
-
.option('--json', 'Output as JSON')
|
|
1794
|
-
.option('--store <target>', 'Store level: local (default), repo, workspace, user')
|
|
1795
|
-
.action((subcommand, args, options) => {
|
|
1796
|
-
runLink(subcommand, args, {
|
|
1797
|
-
name: options.name,
|
|
1798
|
-
role: options.role,
|
|
1799
|
-
channels: options.channels,
|
|
1800
|
-
force: options.force,
|
|
1801
|
-
json: options.json,
|
|
1802
|
-
store: options.store,
|
|
1803
|
-
});
|
|
1804
|
-
});
|
|
1805
|
-
program
|
|
1806
|
-
.command('tool <subcommand> [args...]')
|
|
1807
|
-
.description('Manage project tools (list, add, describe, search)')
|
|
1808
|
-
.option('--tag <tag>', 'Tag for categorization (repeatable)', (val, prev) => [...(prev || []), val])
|
|
1809
|
-
.option('--type <type>', 'Tool type: workflow, validator, generator, utility, explorer')
|
|
1810
|
-
.option('--author <name>', 'Author name')
|
|
1811
|
-
.option('--store <target>', 'Store level: local (default), repo, workspace, user')
|
|
1812
|
-
.action((subcommand, args, options) => {
|
|
1813
|
-
runTool(subcommand, args, {
|
|
1814
|
-
tag: options.tag,
|
|
1815
|
-
type: options.type,
|
|
1816
|
-
author: options.author,
|
|
1817
|
-
store: options.store,
|
|
1818
|
-
});
|
|
1819
|
-
});
|
|
1820
|
-
program
|
|
1821
|
-
.command('explore')
|
|
1822
|
-
.description('Explore project capabilities and available tools')
|
|
1823
|
-
.option('--query <q>', 'Search for specific capability or tool')
|
|
1824
|
-
.action((options) => {
|
|
1825
|
-
runExplore({ query: options.query });
|
|
1826
|
-
});
|
|
1827
|
-
// --- discover ---
|
|
1828
|
-
program
|
|
1829
|
-
.command('discover')
|
|
1830
|
-
.description('Scan workspace for MCP configs, instruction files, skills, hooks, and agent integrations')
|
|
1831
|
-
.option('--json', 'Output as JSON')
|
|
1832
|
-
.option('--no-save', 'Do not persist discovery profile to .brainclaw/discovery/')
|
|
1833
|
-
.action((options) => {
|
|
1834
|
-
runDiscover({ json: options.json, save: options.save });
|
|
1835
|
-
});
|
|
1836
|
-
// --- migrate ---
|
|
1837
|
-
program
|
|
1838
|
-
.command('migrate')
|
|
1839
|
-
.description('Migrate memory items between stores (e.g. promote machine-scoped items to user store)')
|
|
1840
|
-
.option('--promote-machine-items', 'Move items with scope:machine from project store to user store (~/.brainclaw/)')
|
|
1841
|
-
.option('--enable-journal', 'Turn on the event journal (mode=dual) for this existing store and backfill it (pln#567)')
|
|
1842
|
-
.option('--dry-run', 'Show what would be done without writing')
|
|
1843
|
-
.action((options) => {
|
|
1844
|
-
runMigrate({ promoteMachineItems: options.promoteMachineItems, enableJournal: options.enableJournal, dryRun: options.dryRun });
|
|
1845
|
-
});
|
|
1846
|
-
program
|
|
1847
|
-
.command('switch [project]')
|
|
1848
|
-
.description('Set the active project for subsequent commands (session-scoped by default)')
|
|
1849
|
-
.option('--list', 'List available projects in the workspace')
|
|
1850
|
-
.option('--clear', 'Clear the active project (revert to cwd)')
|
|
1851
|
-
.option('--global', 'Set/clear the SHARED workspace default for ALL agents (writes active-project.json). Without it, switch is session-scoped and isolated.')
|
|
1852
|
-
.option('--json', 'Output as JSON')
|
|
1853
|
-
.action((project, options) => {
|
|
1854
|
-
const globalOpts = options.parent?.parent ? program.opts() : {};
|
|
1855
|
-
runSwitch(project, {
|
|
1856
|
-
list: options.list,
|
|
1857
|
-
clear: options.clear,
|
|
1858
|
-
global: options.global,
|
|
1859
|
-
json: options.json,
|
|
1860
|
-
cwd: globalOpts.cwd,
|
|
1861
|
-
});
|
|
1862
|
-
});
|
|
1863
|
-
program
|
|
1864
|
-
.command('who')
|
|
1865
|
-
.description('Show active agent sessions on this workspace')
|
|
1866
|
-
.option('--json', 'Output as JSON')
|
|
1867
|
-
.option('--all', 'Include stale sessions')
|
|
1868
|
-
.option('--gc', 'Remove stale sessions')
|
|
1869
|
-
.option('--local-only', 'Read claims from local store only (skip parent stores in chain)')
|
|
1870
|
-
.action(async (options) => {
|
|
1871
|
-
const globalOpts = program.opts();
|
|
1872
|
-
const { runWho } = await import('./commands/who.js');
|
|
1873
|
-
runWho({ json: options.json, all: options.all, gc: options.gc, cwd: globalOpts.cwd, localOnly: options.localOnly });
|
|
1874
|
-
});
|
|
1875
|
-
const worktreeCmd = program
|
|
1876
|
-
.command('worktree')
|
|
1877
|
-
.description('Manage git worktrees for parallel agent isolation');
|
|
1878
|
-
worktreeCmd
|
|
1879
|
-
.command('create <branch>')
|
|
1880
|
-
.description('Create a linked git worktree for a given branch')
|
|
1881
|
-
.option('--session-id <id>', 'Associate this worktree with a brainclaw session')
|
|
1882
|
-
.option('--agent <name>', 'Associate this worktree with an agent name')
|
|
1883
|
-
.action((branch, options) => {
|
|
1884
|
-
const globalOpts = program.opts();
|
|
1885
|
-
runWorktreeCreate({ branch, sessionId: options.sessionId, agent: options.agent, cwd: globalOpts.cwd });
|
|
1886
|
-
});
|
|
1887
|
-
worktreeCmd
|
|
1888
|
-
.command('list')
|
|
1889
|
-
.description('List all git worktrees for this project')
|
|
1890
|
-
.action(() => {
|
|
1891
|
-
const globalOpts = program.opts();
|
|
1892
|
-
runWorktreeList({ cwd: globalOpts.cwd });
|
|
1893
|
-
});
|
|
1894
|
-
worktreeCmd
|
|
1895
|
-
.command('remove <path>')
|
|
1896
|
-
.description('Remove a linked git worktree')
|
|
1897
|
-
.option('--force', 'Force removal even with uncommitted changes')
|
|
1898
|
-
.action((worktreePath, options) => {
|
|
1899
|
-
const globalOpts = program.opts();
|
|
1900
|
-
runWorktreeRemove({ path: worktreePath, force: options.force, cwd: globalOpts.cwd });
|
|
1901
|
-
});
|
|
1902
|
-
worktreeCmd
|
|
1903
|
-
.command('prune')
|
|
1904
|
-
.description('Prune stale worktree administrative files')
|
|
1905
|
-
.action(() => {
|
|
1906
|
-
const globalOpts = program.opts();
|
|
1907
|
-
runWorktreePrune({ cwd: globalOpts.cwd });
|
|
1908
|
-
});
|
|
1909
|
-
worktreeCmd
|
|
1910
|
-
.command('clean')
|
|
1911
|
-
.description('Remove worktrees whose branch is fully merged and orphan worktree directories')
|
|
1912
|
-
.option('--force', 'Force removal even with uncommitted changes')
|
|
1913
|
-
.option('--dry-run', 'Show what would be removed without actually removing')
|
|
1914
|
-
.action((options) => {
|
|
1915
|
-
const globalOpts = program.opts();
|
|
1916
|
-
runWorktreeClean({ force: options.force, dryRun: options.dryRun, cwd: globalOpts.cwd });
|
|
1917
|
-
});
|
|
1918
|
-
worktreeCmd
|
|
1919
|
-
.command('merge <branch>')
|
|
1920
|
-
.description('Merge a worktree branch with auto-restoration of parasitic deletions')
|
|
1921
|
-
.option('-m, --message <message>', 'Merge commit message')
|
|
1922
|
-
.option('--dry-run', 'Show what would be merged without committing')
|
|
1923
|
-
.action((branch, options) => {
|
|
1924
|
-
const globalOpts = program.opts();
|
|
1925
|
-
runWorktreeMerge({ branch, message: options.message, dryRun: options.dryRun, cwd: globalOpts.cwd });
|
|
1926
|
-
});
|
|
1927
|
-
worktreeCmd
|
|
1928
|
-
.command('check')
|
|
1929
|
-
.description('Pre-merge conflict detection: which parallel lanes touch overlapping files, and who owns them (exit 3 if overlaps found)')
|
|
1930
|
-
.option('--base <ref>', 'Base ref each lane is diffed against (default: current branch)')
|
|
1931
|
-
.option('--json', 'Emit the full risk report as JSON')
|
|
1932
|
-
.action((options) => {
|
|
1933
|
-
const globalOpts = program.opts();
|
|
1934
|
-
runWorktreeCheck({ baseRef: options.base, json: options.json, cwd: globalOpts.cwd });
|
|
1935
|
-
});
|
|
1936
|
-
// --- federation cloud ---
|
|
1937
|
-
const federationCmd = program
|
|
1938
|
-
.command('federation')
|
|
1939
|
-
.description('Cloud federation — sync signals with app.brainclaw.dev');
|
|
1940
|
-
federationCmd
|
|
1941
|
-
.command('push <message>')
|
|
1942
|
-
.description('Push a test signal to the cloud')
|
|
1943
|
-
.option('--type <type>', 'Signal type', 'runtime_note')
|
|
1944
|
-
.option('--to-project <project>', 'Target project name')
|
|
1945
|
-
.option('--to-agent <agent>', 'Target agent name')
|
|
1946
|
-
.action(async (message, options) => {
|
|
1947
|
-
const { pushSignalToCloud, isCloudConfigured } = await import('./core/federation-cloud.js');
|
|
1948
|
-
const { createFederationMessage } = await import('./core/federation-message.js');
|
|
1949
|
-
const { loadConfig } = await import('./core/config.js');
|
|
1950
|
-
const { resolveCurrentAgentName } = await import('./core/agent-registry.js');
|
|
1951
|
-
if (!isCloudConfigured()) {
|
|
1952
|
-
console.error('Error: cloud not configured. Set BRAINCLAW_CLOUD_API_KEY env var.');
|
|
1953
|
-
process.exit(1);
|
|
1954
|
-
}
|
|
1955
|
-
const config = loadConfig();
|
|
1956
|
-
const agent = resolveCurrentAgentName() ?? 'unknown';
|
|
1957
|
-
const msg = createFederationMessage({
|
|
1958
|
-
version: 1,
|
|
1959
|
-
from: { project_name: config.project_name, project_path: process.cwd(), agent_name: agent },
|
|
1960
|
-
to: { project_name: options.toProject ?? 'broadcast', project_path: '' },
|
|
1961
|
-
type: options.type,
|
|
1962
|
-
payload: { text: message },
|
|
1963
|
-
});
|
|
1964
|
-
const ok = await pushSignalToCloud(msg);
|
|
1965
|
-
if (ok) {
|
|
1966
|
-
console.log(`✔ Signal pushed to cloud: [${msg.id}] ${message}`);
|
|
1967
|
-
}
|
|
1968
|
-
else {
|
|
1969
|
-
console.error('Error: failed to push signal to cloud.');
|
|
1970
|
-
process.exit(1);
|
|
1971
|
-
}
|
|
1972
|
-
});
|
|
1973
|
-
federationCmd
|
|
1974
|
-
.command('pull')
|
|
1975
|
-
.description('Pull signals from the cloud inbox')
|
|
1976
|
-
.option('--agent <name>', 'Agent name to pull for')
|
|
1977
|
-
.option('--since <date>', 'Only pull signals after this ISO date')
|
|
1978
|
-
.option('--limit <n>', 'Max signals to pull', '20')
|
|
1979
|
-
.action(async (options) => {
|
|
1980
|
-
const { pullSignalsFromCloud, isCloudConfigured } = await import('./core/federation-cloud.js');
|
|
1981
|
-
const { resolveCurrentAgentName } = await import('./core/agent-registry.js');
|
|
1982
|
-
if (!isCloudConfigured()) {
|
|
1983
|
-
console.error('Error: cloud not configured. Set BRAINCLAW_CLOUD_API_KEY env var.');
|
|
1984
|
-
process.exit(1);
|
|
1985
|
-
}
|
|
1986
|
-
const agent = options.agent ?? resolveCurrentAgentName() ?? 'unknown';
|
|
1987
|
-
const signals = await pullSignalsFromCloud(agent, {
|
|
1988
|
-
since: options.since,
|
|
1989
|
-
limit: parseInt(options.limit, 10),
|
|
1990
|
-
});
|
|
1991
|
-
if (signals.length === 0) {
|
|
1992
|
-
console.log('No signals in cloud inbox.');
|
|
1993
|
-
return;
|
|
1994
|
-
}
|
|
1995
|
-
console.log(`${signals.length} signal(s) from cloud:\n`);
|
|
1996
|
-
for (const s of signals) {
|
|
1997
|
-
const payload = typeof s.payload === 'object' && s.payload !== null ? s.payload.text ?? JSON.stringify(s.payload) : String(s.payload);
|
|
1998
|
-
console.log(` [${s.id}] ${s.type} from ${s.from.project_name}/${s.from.agent_name}`);
|
|
1999
|
-
console.log(` ${String(payload).slice(0, 120)}`);
|
|
2000
|
-
console.log(` ${s.created_at}\n`);
|
|
2001
|
-
}
|
|
2002
|
-
});
|
|
2003
|
-
federationCmd
|
|
2004
|
-
.command('status')
|
|
2005
|
-
.description('Check cloud federation configuration')
|
|
2006
|
-
.action(async () => {
|
|
2007
|
-
const { isCloudConfigured } = await import('./core/federation-cloud.js');
|
|
2008
|
-
const url = process.env.BRAINCLAW_CLOUD_URL ?? 'https://app.brainclaw.dev';
|
|
2009
|
-
console.log(`Cloud URL: ${url}`);
|
|
2010
|
-
console.log(`API Key: ${process.env.BRAINCLAW_CLOUD_API_KEY ? '***configured***' : 'NOT SET'}`);
|
|
2011
|
-
console.log(`Configured: ${isCloudConfigured() ? 'yes' : 'no'}`);
|
|
2012
|
-
if (isCloudConfigured()) {
|
|
2013
|
-
try {
|
|
2014
|
-
const res = await fetch(`${url}/api/v1/health`);
|
|
2015
|
-
const data = await res.json();
|
|
2016
|
-
console.log(`Cloud status: ${data.status} (v${data.version})`);
|
|
2017
|
-
}
|
|
2018
|
-
catch (e) {
|
|
2019
|
-
console.error(`Cloud unreachable: ${e.message}`);
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
});
|
|
2023
|
-
// --- codev (legacy experimental) ---
|
|
2024
|
-
if (isCodevEnabled()) {
|
|
2025
|
-
program
|
|
2026
|
-
.command('codev [topic]')
|
|
2027
|
-
.description('Experimental legacy ideation session using persona-based consultation')
|
|
2028
|
-
.option('--personas <tier>', 'Persona tier: tier1 (default), tier2, or list', 'tier1')
|
|
2029
|
-
.option('--checkpoint', 'Pause after clarification for human input')
|
|
2030
|
-
.option('--spawn', 'Spawn each consultant as an agent CLI instance')
|
|
2031
|
-
.option('--fresh', 'Clear cached responses before starting a new run')
|
|
2032
|
-
.option('--agents <list>', 'Comma-separated agent names for spawn (e.g. claude-code,codex,antigravity). Default: auto-detect')
|
|
2033
|
-
.option('--rounds <N>', 'Number of discussion rounds in spawn mode (default 3, min 2)', '3')
|
|
2034
|
-
.option('--target-duration <seconds>', 'Target duration per round indicated to agents (default 120)', '120')
|
|
2035
|
-
.option('--quorum <N>', 'Advance to next round after N agent responses (default: all)')
|
|
2036
|
-
.option('--model-map <map>', 'Per-persona model overrides, e.g. simplificateur:sonnet,stratege:opus')
|
|
2037
|
-
.option('--metrics', 'Display response timing metrics at end of session')
|
|
2038
|
-
.option('--json', 'Output as JSON')
|
|
2039
|
-
.action(async (topic, options) => {
|
|
2040
|
-
const globalOpts = program.opts();
|
|
2041
|
-
const { runCodev } = await import('./commands/codev.js');
|
|
2042
|
-
runCodev(topic, {
|
|
2043
|
-
...options,
|
|
2044
|
-
rounds: parseInt(options.rounds, 10),
|
|
2045
|
-
targetDuration: parseInt(options.targetDuration, 10),
|
|
2046
|
-
quorum: options.quorum != null ? parseInt(options.quorum, 10) : undefined,
|
|
2047
|
-
cwd: globalOpts.cwd,
|
|
2048
|
-
});
|
|
2049
|
-
});
|
|
2050
|
-
program
|
|
2051
|
-
.command('codev-metrics <thread>')
|
|
2052
|
-
.description('Show per-agent avg/p95 response metrics for an experimental CoDev thread')
|
|
2053
|
-
.option('--json', 'Output as JSON')
|
|
2054
|
-
.action(async (thread, options) => {
|
|
2055
|
-
const globalOpts = program.opts();
|
|
2056
|
-
const { runCodevMetrics } = await import('./commands/codev.js');
|
|
2057
|
-
runCodevMetrics(thread, { ...options, cwd: globalOpts.cwd });
|
|
2058
|
-
});
|
|
373
|
+
process.exit(0);
|
|
2059
374
|
}
|
|
2060
|
-
// --- questions (operator-question artifacts across loops; pln#508 step 4) ---
|
|
2061
|
-
program
|
|
2062
|
-
.command('questions')
|
|
2063
|
-
.description('List pending operator_question artifacts across loops in the current project')
|
|
2064
|
-
.option('--loop <loop_id>', 'Filter to a single loop')
|
|
2065
|
-
.option('--status <status>', 'Filter by status: awaiting (default), answered, timed_out', 'awaiting')
|
|
2066
|
-
.option('--mine', 'Filter to questions targeted at the current agent (v1 heuristic: humans see all awaiting)')
|
|
2067
|
-
.option('--json', 'Output as JSON')
|
|
2068
|
-
.action((options) => {
|
|
2069
|
-
const globalOpts = program.opts();
|
|
2070
|
-
const status = options.status;
|
|
2071
|
-
if (!['awaiting', 'answered', 'timed_out'].includes(status)) {
|
|
2072
|
-
console.error(`Error: --status must be one of awaiting|answered|timed_out (got "${options.status}")`);
|
|
2073
|
-
process.exit(1);
|
|
2074
|
-
}
|
|
2075
|
-
runQuestionsCommand({
|
|
2076
|
-
loop: options.loop,
|
|
2077
|
-
status,
|
|
2078
|
-
mine: options.mine,
|
|
2079
|
-
json: options.json,
|
|
2080
|
-
}, globalOpts.cwd);
|
|
2081
|
-
});
|
|
2082
|
-
// --- bootstrap-loop (open/join/status/cancel a bootstrap loop; pln#513 step 3) ---
|
|
2083
|
-
program
|
|
2084
|
-
.command('bootstrap-loop')
|
|
2085
|
-
.description('Open or join a bootstrap loop on the current project, or query its status')
|
|
2086
|
-
.option('--status', 'Report current state')
|
|
2087
|
-
.option('--cancel', 'Cancel the active bootstrap loop')
|
|
2088
|
-
.option('--yes', 'Skip confirmation prompts')
|
|
2089
|
-
.option('--json', 'Machine-readable output')
|
|
2090
|
-
.action(async (options) => {
|
|
2091
|
-
const globalOpts = program.opts();
|
|
2092
|
-
const { runBootstrapLoopCommand } = await import('./commands/bootstrap-loop.js');
|
|
2093
|
-
await runBootstrapLoopCommand(options, globalOpts.cwd);
|
|
2094
|
-
});
|
|
2095
|
-
// --- loop (drive loop turn verbs; pln#517 step 2) ---
|
|
2096
|
-
const loopCmd = program
|
|
2097
|
-
.command('loop')
|
|
2098
|
-
.description('Drive a loop turn (turn / complete-turn / advance / add-artifact)');
|
|
2099
|
-
loopCmd
|
|
2100
|
-
.command('turn <loop_id>')
|
|
2101
|
-
.description('Issue a turn assignment on a slot')
|
|
2102
|
-
.requiredOption('--slot <slot_id>', 'Target slot id (lsl_...)')
|
|
2103
|
-
.option('--input <text>', 'Free-form input passed to the slot')
|
|
2104
|
-
.option('--role <role>', 'Slot role (resolves the first non-done slot with that role)')
|
|
2105
|
-
.option('--assignment-id <id>', 'Dispatcher-provided assignment id to record on the slot')
|
|
2106
|
-
.option('--json', 'Machine-readable output')
|
|
2107
|
-
.action(async (loop_id, options) => {
|
|
2108
|
-
const globalOpts = program.opts();
|
|
2109
|
-
const { runLoopCommand } = await import('./commands/loop.js');
|
|
2110
|
-
await runLoopCommand('turn', { loop_id }, options, globalOpts.cwd);
|
|
2111
|
-
});
|
|
2112
|
-
loopCmd
|
|
2113
|
-
.command('complete-turn <loop_id>')
|
|
2114
|
-
.description('Complete a slot turn')
|
|
2115
|
-
.requiredOption('--slot <slot_id>', 'Target slot id (lsl_...)')
|
|
2116
|
-
.requiredOption('--outcome <outcome>', 'Turn outcome: done, failed, or cancelled')
|
|
2117
|
-
.option('--failure-reason <text>', 'Reason when outcome is failed')
|
|
2118
|
-
.option('--artifact <json>', 'JSON object payload for an artifact to attach')
|
|
2119
|
-
.option('--json', 'Machine-readable output')
|
|
2120
|
-
.action(async (loop_id, options) => {
|
|
2121
|
-
const globalOpts = program.opts();
|
|
2122
|
-
const { runLoopCommand } = await import('./commands/loop.js');
|
|
2123
|
-
await runLoopCommand('complete-turn', { loop_id }, options, globalOpts.cwd);
|
|
2124
|
-
});
|
|
2125
|
-
loopCmd
|
|
2126
|
-
.command('advance <loop_id>')
|
|
2127
|
-
.description('Advance a loop to its next phase')
|
|
2128
|
-
.option('--to-phase <name>', 'Explicit target phase')
|
|
2129
|
-
.option('--force', 'Bypass phase gate checks')
|
|
2130
|
-
.option('--reason <text>', 'Reason to record on the phase advance event')
|
|
2131
|
-
.option('--json', 'Machine-readable output')
|
|
2132
|
-
.action(async (loop_id, options) => {
|
|
2133
|
-
const globalOpts = program.opts();
|
|
2134
|
-
const { runLoopCommand } = await import('./commands/loop.js');
|
|
2135
|
-
await runLoopCommand('advance', { loop_id }, options, globalOpts.cwd);
|
|
2136
|
-
});
|
|
2137
|
-
loopCmd
|
|
2138
|
-
.command('add-artifact <loop_id>')
|
|
2139
|
-
.description('Attach an artifact to a loop')
|
|
2140
|
-
.requiredOption('--phase <phase>', 'Artifact phase')
|
|
2141
|
-
.requiredOption('--type <type>', 'Artifact type')
|
|
2142
|
-
.requiredOption('--body <json-or-text>', 'Artifact body as JSON or text')
|
|
2143
|
-
.option('--produced-by <agent>', 'Agent that produced the artifact')
|
|
2144
|
-
.option('--ref <ref>', 'JSON ref object, e.g. {"kind":"plan","id":"pln_..."}')
|
|
2145
|
-
.option('--json', 'Machine-readable output')
|
|
2146
|
-
.action(async (loop_id, options) => {
|
|
2147
|
-
const globalOpts = program.opts();
|
|
2148
|
-
const { runLoopCommand } = await import('./commands/loop.js');
|
|
2149
|
-
await runLoopCommand('add-artifact', { loop_id }, options, globalOpts.cwd);
|
|
2150
|
-
});
|
|
2151
|
-
// --- reply (provide_input to an operator_question; pln#508 step 4) ---
|
|
2152
|
-
program
|
|
2153
|
-
.command('reply <qst_id>')
|
|
2154
|
-
.description('Resolve an operator_question artifact (wraps bclaw_loop.provide_input)')
|
|
2155
|
-
.option('--answer <text>', 'Free-form answer text')
|
|
2156
|
-
.option('--choose <option_id>', 'Pick one of the question\'s structured options[].id')
|
|
2157
|
-
.option('--skip', 'Materialize the question\'s suggested_default')
|
|
2158
|
-
.option('--json', 'Output as JSON')
|
|
2159
|
-
.action((qstId, options) => {
|
|
2160
|
-
const globalOpts = program.opts();
|
|
2161
|
-
runReplyCommand(qstId, {
|
|
2162
|
-
answer: options.answer,
|
|
2163
|
-
choose: options.choose,
|
|
2164
|
-
skip: options.skip,
|
|
2165
|
-
json: options.json,
|
|
2166
|
-
}, globalOpts.cwd);
|
|
2167
|
-
});
|
|
2168
|
-
// --- run (agent profiles) ---
|
|
2169
|
-
program
|
|
2170
|
-
.command('run [profile-name]')
|
|
2171
|
-
.description('Run an agent profile (list profiles if no name given)')
|
|
2172
|
-
.option('--dry', 'Print the resolved command without executing')
|
|
2173
|
-
.option('--agent <agent>', 'Override the invoke template with a known agent')
|
|
2174
|
-
.action((profileName, options) => {
|
|
2175
|
-
const globalOpts = program.opts();
|
|
2176
|
-
runRunProfile(profileName, { ...options, cwd: globalOpts.cwd });
|
|
2177
|
-
});
|
|
2178
375
|
{
|
|
2179
376
|
// Friendly trailing-global-option error must run before Commander parses:
|
|
2180
377
|
// with positional options enabled, Commander itself would reject a trailing
|