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.
Files changed (63) hide show
  1. package/README.md +16 -263
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli/register-capture.js +209 -0
  4. package/dist/cli/register-code-map.js +19 -0
  5. package/dist/cli/register-coordination.js +472 -0
  6. package/dist/cli/register-federation.js +258 -0
  7. package/dist/cli/register-lifecycle.js +436 -0
  8. package/dist/cli/register-memory-context.js +502 -0
  9. package/dist/cli/register-planning.js +167 -0
  10. package/dist/cli/register-review.js +149 -0
  11. package/dist/cli/shared.js +5 -0
  12. package/dist/cli.js +212 -2015
  13. package/dist/commands/dispatch-watch.js +25 -2
  14. package/dist/commands/harvest.js +31 -6
  15. package/dist/commands/mcp-catalog.js +1438 -0
  16. package/dist/commands/mcp-contract.js +33 -0
  17. package/dist/commands/mcp-presentation.js +27 -0
  18. package/dist/commands/mcp-read-handlers.js +72 -36
  19. package/dist/commands/mcp-write-admin.js +328 -0
  20. package/dist/commands/mcp-write-claims.js +864 -0
  21. package/dist/commands/mcp-write-coordination.js +1825 -0
  22. package/dist/commands/mcp-write-entities.js +620 -0
  23. package/dist/commands/mcp-write-memory.js +451 -0
  24. package/dist/commands/mcp-write-sequences.js +116 -0
  25. package/dist/commands/mcp-write-support.js +367 -0
  26. package/dist/commands/mcp.js +261 -5570
  27. package/dist/commands/update-handoff.js +28 -42
  28. package/dist/core/agent-capability.js +31 -14
  29. package/dist/core/agent-files.js +1 -1
  30. package/dist/core/agent-registry.js +51 -3
  31. package/dist/core/claims.js +18 -0
  32. package/dist/core/coordination.js +5 -2
  33. package/dist/core/cross-project.js +35 -1
  34. package/dist/core/dispatcher.js +34 -20
  35. package/dist/core/entity-operations.js +335 -12
  36. package/dist/core/entity-registry.js +72 -9
  37. package/dist/core/execution.js +28 -4
  38. package/dist/core/facade-schema.js +30 -4
  39. package/dist/core/federation-cloud.js +142 -11
  40. package/dist/core/federation-outbox.js +292 -0
  41. package/dist/core/federation-signing.js +115 -0
  42. package/dist/core/handoff-review.js +35 -0
  43. package/dist/core/io.js +6 -0
  44. package/dist/core/protocol-tool-policy.js +113 -0
  45. package/dist/core/review-loop-close.js +115 -0
  46. package/dist/core/schema.js +25 -2
  47. package/dist/core/security-detectors.js +35 -6
  48. package/dist/core/security.js +32 -12
  49. package/dist/core/worktree.js +98 -9
  50. package/dist/facts.js +13 -11
  51. package/dist/facts.json +12 -10
  52. package/docs/PROTOCOL.md +7 -3
  53. package/docs/concepts/coordinator-runbook.md +3 -0
  54. package/docs/concepts/dispatch-lifecycle.md +4 -4
  55. package/docs/concepts/loop-engine.md +3 -1
  56. package/docs/concepts/troubleshooting.md +1 -1
  57. package/docs/integrations/codex.md +3 -3
  58. package/docs/integrations/overview.md +1 -1
  59. package/docs/mcp-schema-changelog.md +153 -2
  60. package/docs/playbooks/orchestration.md +1 -1
  61. package/docs/product/entity-model-audit.md +3 -2
  62. package/docs/security.md +22 -1
  63. package/package.json +3 -1
@@ -0,0 +1,167 @@
1
+ import { runPlanResource } from '../commands/plan-resource.js';
2
+ import { runMove } from '../commands/move.js';
3
+ import { runListPlans } from '../commands/list-plans.js';
4
+ import { runSequenceResource } from '../commands/sequence.js';
5
+ import { runAddStep } from '../commands/add-step.js';
6
+ import { runCompleteStep } from '../commands/complete-step.js';
7
+ import { runUpdateStep } from '../commands/update-step.js';
8
+ import { runDeleteStep } from '../commands/delete-step.js';
9
+ import { runEstimationReport } from '../commands/estimation-report.js';
10
+ import { runUpdatePlan } from '../commands/update-plan.js';
11
+ import { runSurfaceTaskResource } from '../commands/surface-task-resource.js';
12
+ import { runDeletePlan } from '../commands/delete-plan.js';
13
+ export function registerPlanningCommands(program) {
14
+ // --- plan ---
15
+ program
16
+ .command('plan <subcommand> [args...]')
17
+ .description('Manage shared plan items (create, list, update, delete)')
18
+ .option('--json', 'Output as JSON for list')
19
+ .option('--all', 'Include done and dropped plan items in list')
20
+ .option('--type <type>', 'Plan type or filter: feat, fix, chore, spike, doc')
21
+ .option('--status <status>', 'Status filter/update: todo, in_progress, blocked, done, dropped')
22
+ .option('--priority <priority>', 'Priority: low, medium, high', 'medium')
23
+ .option('--assignee <assignee>', 'Assignee for this plan item')
24
+ .option('--project <project>', 'Optional project namespace')
25
+ .option('--tag <tags...>', 'Tags for this plan item')
26
+ .option('--path <paths...>', 'Related file paths')
27
+ .option('--depends-on <ids...>', 'Dependency IDs for this plan item')
28
+ .option('--author <author>', 'Author name')
29
+ .option('--estimate <minutes>', 'Estimated effort in minutes (positive integer, e.g. --estimate 30)')
30
+ .option('--actual-effort <effort>', 'Actual effort spent (e.g. "20min", "1h30m")')
31
+ .option('--store <target>', 'Target store level: local (default), repo, workspace, user')
32
+ .option('--recursive', 'Include plans from descendant brainclaw projects (for list)')
33
+ .option('--local-only', 'Read from local store only for list (skip parent stores in chain)')
34
+ .action((subcommand, args, options) => {
35
+ runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort, localOnly: options.localOnly });
36
+ });
37
+ // --- move (cross-project relocation, pln#595) ---
38
+ program
39
+ .command('move <entity> <id>')
40
+ .description('Relocate a brainclaw item to another project, id-preserving (multi-project workspaces). Relocatable: plan, decision, constraint, trap, handoff, sequence.')
41
+ .requiredOption('--to <project>', 'Target project (name, path, or basename)')
42
+ .option('--from <project>', 'Source project (defaults to the current project)')
43
+ .option('--force', 'Move even if an active claim references the item')
44
+ .option('--json', 'Output as JSON')
45
+ .action((entity, id, options) => runMove(entity, id, options));
46
+ // --- list-plans ---
47
+ program
48
+ .command('list-plans')
49
+ .description('List shared plan items')
50
+ .option('--json', 'Output as JSON')
51
+ .option('--status <status>', 'Status: todo, in_progress, blocked, done, dropped')
52
+ .option('--type <type>', 'Filter by type: feat, fix, chore, spike, doc')
53
+ .option('--assignee <assignee>', 'Filter by assignee')
54
+ .option('--project <project>', 'Filter by project namespace')
55
+ .option('--all', 'Include done and dropped plan items')
56
+ .option('--recursive', 'Include plans from descendant brainclaw projects')
57
+ .option('--local-only', 'Read from local store only (skip parent stores in chain)')
58
+ .action((options) => {
59
+ runListPlans({ ...options, localOnly: options.localOnly });
60
+ });
61
+ program
62
+ .command('sequence <subcommand> [args...]')
63
+ .description('Manage coordination sequences (create, list, show, update)')
64
+ .option('--json', 'Output as JSON')
65
+ .option('--description <text>', 'Optional sequence description')
66
+ .option('--status <status>', 'Sequence status: draft, active, archived')
67
+ .option('--owner <owner>', 'Optional sequence owner')
68
+ .option('--items <json>', 'Sequence items JSON array')
69
+ .option('--name <name>', 'Optional sequence name for update')
70
+ .option('--tag <tags...>', 'Tags for this sequence')
71
+ .option('--author <author>', 'Author name')
72
+ .action((subcommand, args, options) => {
73
+ runSequenceResource(subcommand, args, options);
74
+ });
75
+ // --- add-step ---
76
+ program
77
+ .command('add-step <planId> <text>')
78
+ .description('Add an optional step to a plan item')
79
+ .option('--assign <assignee>', 'Assign this step to an agent or person')
80
+ .option('--estimate <minutes>', 'Step-level estimate (minutes, or a duration like "2h"/"30m")')
81
+ .option('--actual-effort <effort>', 'Step-level actual effort (e.g. "45m", "2h")')
82
+ .action((planId, text, options) => {
83
+ runAddStep(planId, text, { assignee: options.assign, estimatedEffort: options.estimate, actualEffort: options.actualEffort });
84
+ });
85
+ // --- complete-step ---
86
+ program
87
+ .command('complete-step <planId> <stepId>')
88
+ .description('Mark a plan step as done')
89
+ .action((planId, stepId) => {
90
+ runCompleteStep(planId, stepId);
91
+ });
92
+ // --- update-step ---
93
+ program
94
+ .command('update-step <planId> <stepId>')
95
+ .description('Update a plan step (status, text, assignee, effort)')
96
+ .option('--status <status>', 'New status: todo, in_progress, testing, done, blocked')
97
+ .option('--text <text>', 'Replace step description')
98
+ .option('--assign <assignee>', 'Assign the step (empty string to unassign)')
99
+ .option('--estimate <minutes>', 'Step-level estimate (minutes, or a duration like "2h"/"30m")')
100
+ .option('--actual-effort <effort>', 'Step-level actual effort (e.g. "45m", "2h")')
101
+ .action((planId, stepId, options) => {
102
+ runUpdateStep(planId, stepId, options);
103
+ });
104
+ // --- delete-step ---
105
+ program
106
+ .command('delete-step <planId> <stepId>')
107
+ .description('Remove a step from a plan')
108
+ .action((planId, stepId) => {
109
+ runDeleteStep(planId, stepId);
110
+ });
111
+ // --- estimation-report ---
112
+ program
113
+ .command('estimation-report')
114
+ .description('Show estimation accuracy report for completed plans')
115
+ .option('--agent <name>', 'Filter by agent/author name')
116
+ .option('--json', 'Output as JSON')
117
+ .option('--outlier-threshold <minutes>', 'Drop plan-wallclock actuals over N minutes from the stats (default 1440 = 24h; 0 disables)')
118
+ .action((options) => {
119
+ runEstimationReport({
120
+ agent: options.agent,
121
+ json: options.json,
122
+ outlierThresholdMinutes: options.outlierThreshold !== undefined ? Number(options.outlierThreshold) : undefined,
123
+ });
124
+ });
125
+ // --- update-plan ---
126
+ program
127
+ .command('update-plan <id>')
128
+ .description('Update a shared plan item')
129
+ .option('--status <status>', 'Status: todo, in_progress, blocked, done, dropped')
130
+ .option('--assignee <assignee>', 'Assign a user or agent to this plan item')
131
+ .option('--project <project>', 'Set or change project namespace')
132
+ .option('--priority <priority>', 'Priority: low, medium, high')
133
+ .option('--actual-effort <effort>', 'Actual effort spent (e.g. "20min", "1h30m")')
134
+ .action((id, options) => {
135
+ runUpdatePlan(id, { ...options, actualEffort: options.actualEffort });
136
+ });
137
+ // --- surface-task ---
138
+ program
139
+ .command('surface-task <subcommand> [args...]')
140
+ .description('Manage queued tasks for desktop AI surfaces such as ChatGPT Desktop or Claude Desktop')
141
+ .option('--json', 'Output as JSON for list')
142
+ .option('--all', 'Include completed, cancelled, and failed tasks in list')
143
+ .option('--status <status>', 'Status filter/update: queued, in_progress, completed, cancelled, failed')
144
+ .option('--target <surface>', 'Target surface, e.g. chatgpt, claude, gemini')
145
+ .option('--kind <kind>', 'Task kind: visual_asset, draft, summary, analysis, research, custom')
146
+ .option('--instructions <text>', 'Detailed instructions for the target surface')
147
+ .option('--output <paths...>', 'Expected output paths')
148
+ .option('--result <text>', 'Optional result note when updating a task')
149
+ .option('--tag <tags...>', 'Tags for this task')
150
+ .option('--path <paths...>', 'Related file paths')
151
+ .option('--agent <agent>', 'Author agent name')
152
+ .option('--agent-id <agentId>', 'Author agent id')
153
+ .action((subcommand, args, options) => {
154
+ runSurfaceTaskResource(subcommand, args, {
155
+ ...options,
156
+ agentId: options.agentId,
157
+ });
158
+ });
159
+ // --- delete-plan ---
160
+ program
161
+ .command('delete-plan <id>')
162
+ .description('Delete a shared plan item')
163
+ .action((id) => {
164
+ runDeletePlan(id);
165
+ });
166
+ }
167
+ //# sourceMappingURL=register-planning.js.map
@@ -0,0 +1,149 @@
1
+ import { runReview } from '../commands/review.js';
2
+ import { runShowCandidate } from '../commands/show-candidate.js';
3
+ import { runStarCandidate } from '../commands/star-candidate.js';
4
+ import { runUseCandidate } from '../commands/use-candidate.js';
5
+ import { runAccept } from '../commands/accept.js';
6
+ import { runAdapterOpenclawImport } from '../commands/adapter-openclaw-import.js';
7
+ import { runReject } from '../commands/reject.js';
8
+ import { runHarvestCandidates, runHarvestLane } from '../commands/harvest.js';
9
+ import { runPruneCandidates } from '../commands/prune-candidates.js';
10
+ import { cleanupStaleCandidates } from '../core/candidates.js';
11
+ import { memoryExists } from '../core/io.js';
12
+ import { collect } from './shared.js';
13
+ export function registerReviewCommands(program) {
14
+ // --- review ---
15
+ program
16
+ .command('review')
17
+ .description('List pending memory candidates')
18
+ .option('--json', 'Output as JSON')
19
+ .option('--type <type>', 'Filter by type')
20
+ .option('--prioritized', 'Sort by review priority and show SLA')
21
+ .option('--only-overdue', 'Show only candidates overdue review SLA')
22
+ .option('--assignee <assignee>', 'Filter by assignee tag (assignee:<name>)')
23
+ .option('--for-curator <curator>', 'Shortcut assignee filter for curator queue')
24
+ .option('--take <count>', 'Limit number of reviewed items', parseInt)
25
+ .option('--claim <curator>', 'Atomically assign selected candidates to a curator')
26
+ .option('--auto', 'Auto-promote candidates meeting the configured score threshold')
27
+ .option('--auto-by <actor>', 'Actor identity for auto-promotion (defaults to current user)')
28
+ .action((options) => {
29
+ runReview({ ...options, autoBy: options.autoBy });
30
+ });
31
+ // --- show-candidate ---
32
+ program
33
+ .command('show-candidate <id>')
34
+ .description('Show details of a memory candidate')
35
+ .option('--related', 'Show related candidates by shared tags/paths')
36
+ .action((id, options) => {
37
+ runShowCandidate(id, { related: options.related });
38
+ });
39
+ // --- star-candidate ---
40
+ program
41
+ .command('star-candidate <id>')
42
+ .description('Add an adoption star to a pending candidate')
43
+ .option('--by <actor>', 'Agent or person adding the star')
44
+ .action((id, options) => {
45
+ runStarCandidate(id, options);
46
+ });
47
+ // --- use-candidate ---
48
+ program
49
+ .command('use-candidate <id>')
50
+ .description('Record that a pending candidate was reused in a concrete work context')
51
+ .option('--by <actor>', 'Agent or person using the candidate')
52
+ .requiredOption('--context <context>', 'Work context where the candidate was reused')
53
+ .action((id, options) => {
54
+ runUseCandidate(id, options);
55
+ });
56
+ // --- accept ---
57
+ program
58
+ .command('accept <id>')
59
+ .description('Accept a candidate into canonical memory')
60
+ .option('--by <reviewer>', 'Reviewer identity used for governance checks')
61
+ .action((id, options) => {
62
+ runAccept(id, options.by);
63
+ });
64
+ // --- adapter-openclaw-import ---
65
+ program
66
+ .command('adapter-openclaw-import [file]')
67
+ .description('Import OpenClaw runtime events file as memory candidates')
68
+ .option('--session <id>', 'Import runtime events from a session id in .brainclaw/runtime/')
69
+ .option('--dry-run', 'Preview import without creating candidates')
70
+ .option('--source <source>', 'Source label for imported candidates', 'openclaw')
71
+ .option('--author <author>', 'Author for imported candidates')
72
+ .action((file, options) => {
73
+ runAdapterOpenclawImport(file, options);
74
+ });
75
+ // --- reject ---
76
+ program
77
+ .command('reject <id>')
78
+ .description('Reject a memory candidate')
79
+ .option('--by <reviewer>', 'Reviewer identity used for rejection attribution')
80
+ .option('--reason <reason>', 'Reason for rejection')
81
+ .action((id, options) => {
82
+ runReject(id, options.reason, options.by);
83
+ });
84
+ // --- harvest-candidates ---
85
+ program
86
+ .command('harvest-candidates')
87
+ .description('Harvest candidates from worktree inboxes into the main project store (codex sandbox bridge)')
88
+ .option('--dry-run', 'Preview what would be imported without writing anything')
89
+ .option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
90
+ .option('--json', 'Output as JSON')
91
+ .action((options) => {
92
+ const globalOpts = program.opts();
93
+ runHarvestCandidates({ ...options, cwd: globalOpts.cwd });
94
+ });
95
+ // --- harvest (lane results, pln#526) ---
96
+ program
97
+ .command('harvest [assignment_id]')
98
+ .description('Harvest a worker LANE-RESULT.json from its worktree into the project (pass an assignment id, or --all)')
99
+ .option('--all', 'Harvest every lane result found across worktrees')
100
+ .option('--integrate', 'Worktree-as-contract (pln#534): commit the worktree diff on behalf of a sandboxed worker, lifecycle the assignment, and release the claim')
101
+ .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')
102
+ .option('--base <ref>', 'Base ref for --orphaned commits-ahead comparison (default: master)')
103
+ .option('--dry-run', 'Preview without writing events/markers')
104
+ .option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
105
+ .option('--json', 'Output as JSON')
106
+ .action((assignmentId, options) => {
107
+ const globalOpts = program.opts();
108
+ runHarvestLane(assignmentId, { ...options, cwd: globalOpts.cwd });
109
+ });
110
+ // --- prune-candidates ---
111
+ program
112
+ .command('prune-candidates')
113
+ .description('Remove old rejected candidates')
114
+ .option('--days <days>', 'Max age in days', parseInt)
115
+ .option('--dry-run', 'Preview without deleting')
116
+ .action((options) => {
117
+ runPruneCandidates(options);
118
+ });
119
+ // --- cleanup-candidates ---
120
+ program
121
+ .command('cleanup-candidates')
122
+ .description('Remove stale auto-generated pending candidates')
123
+ .option('--max-age <days>', 'Max age in days before cleanup', parseInt)
124
+ .option('--dry-run', 'Preview without deleting')
125
+ .action((options) => {
126
+ if (!memoryExists()) {
127
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
128
+ process.exit(1);
129
+ }
130
+ const maxAgeDays = options.maxAge ?? 30;
131
+ const result = cleanupStaleCandidates({
132
+ maxAgeDays,
133
+ dryRun: options.dryRun,
134
+ });
135
+ if (result.matched === 0) {
136
+ console.log(`No stale auto-generated candidates older than ${maxAgeDays} days found.`);
137
+ return;
138
+ }
139
+ if (options.dryRun) {
140
+ console.log(`Would remove ${result.matched} stale auto-generated candidate(s):`);
141
+ for (const candidate of result.candidates) {
142
+ console.log(` [${candidate.id}] ${candidate.text}`);
143
+ }
144
+ return;
145
+ }
146
+ console.log(`✔ Removed ${result.deleted} stale auto-generated candidate(s) older than ${maxAgeDays} days.`);
147
+ });
148
+ }
149
+ //# sourceMappingURL=register-review.js.map
@@ -0,0 +1,5 @@
1
+ // pln#622 PR5 — helpers shared across CLI register modules.
2
+ export function collect(value, previous) {
3
+ return [...previous, value];
4
+ }
5
+ //# sourceMappingURL=shared.js.map