brainclaw 1.5.4 → 1.6.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 (60) hide show
  1. package/README.md +52 -28
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +159 -12
  4. package/dist/commands/assignment-resource.js +182 -0
  5. package/dist/commands/bootstrap-loop.js +206 -0
  6. package/dist/commands/init.js +158 -22
  7. package/dist/commands/loop.js +156 -0
  8. package/dist/commands/loops-handlers.js +110 -55
  9. package/dist/commands/mcp-read-handlers.js +45 -4
  10. package/dist/commands/mcp.js +628 -205
  11. package/dist/commands/questions.js +180 -0
  12. package/dist/commands/reply.js +190 -0
  13. package/dist/commands/session-end.js +105 -3
  14. package/dist/commands/session-start.js +32 -53
  15. package/dist/commands/setup.js +87 -48
  16. package/dist/commands/switch.js +21 -1
  17. package/dist/core/agentrun-reconciler.js +65 -0
  18. package/dist/core/agentruns.js +10 -0
  19. package/dist/core/assignments.js +29 -10
  20. package/dist/core/claims.js +29 -0
  21. package/dist/core/context.js +1 -1
  22. package/dist/core/coordination.js +1 -1
  23. package/dist/core/dispatch-status.js +219 -0
  24. package/dist/core/entity-operations.js +166 -10
  25. package/dist/core/entity-registry.js +11 -10
  26. package/dist/core/execution-adapters.js +38 -2
  27. package/dist/core/facade-schema.js +55 -0
  28. package/dist/core/federation-cloud.js +27 -12
  29. package/dist/core/federation-materialize.js +57 -0
  30. package/dist/core/instruction-templates.js +2 -0
  31. package/dist/core/loops/bootstrap-acquire.js +195 -0
  32. package/dist/core/loops/facade-schema.js +68 -1
  33. package/dist/core/loops/hooks/bootstrap-write.js +144 -0
  34. package/dist/core/loops/hooks/notify-operator.js +148 -0
  35. package/dist/core/loops/hooks/survey-source-reader.js +256 -0
  36. package/dist/core/loops/index.js +8 -2
  37. package/dist/core/loops/next-expected.js +63 -0
  38. package/dist/core/loops/presets/bootstrap.js +75 -0
  39. package/dist/core/loops/presets/index.js +16 -0
  40. package/dist/core/loops/store.js +224 -4
  41. package/dist/core/loops/types.js +346 -1
  42. package/dist/core/loops/verbs.js +739 -6
  43. package/dist/core/schema.js +31 -2
  44. package/dist/core/state.js +62 -0
  45. package/dist/core/store-resolution.js +26 -16
  46. package/dist/facts.js +7 -5
  47. package/dist/facts.json +6 -4
  48. package/docs/cli.md +115 -30
  49. package/docs/concepts/dispatch-lifecycle.md +228 -0
  50. package/docs/concepts/loop-engine.md +55 -0
  51. package/docs/concepts/multi-agent-workflows.md +167 -166
  52. package/docs/concepts/troubleshooting.md +10 -2
  53. package/docs/integrations/agents.md +14 -14
  54. package/docs/integrations/codex.md +15 -12
  55. package/docs/integrations/mcp.md +10 -4
  56. package/docs/integrations/overview.md +11 -0
  57. package/docs/playbooks/productivity/index.md +3 -3
  58. package/docs/quickstart-existing-project.md +48 -28
  59. package/docs/quickstart.md +42 -28
  60. package/package.json +1 -1
package/README.md CHANGED
@@ -108,28 +108,52 @@ If you want the least surprising setup today, use Linux first. If you are on Win
108
108
 
109
109
  ---
110
110
 
111
- ## Get Started
112
-
113
- ### 1. Install
114
-
115
- ```bash
116
- npm install -g brainclaw
117
- ```
118
-
119
- ### 2. Initialize a project
120
-
121
- ```bash
122
- cd your-project
123
- brainclaw init
124
- ```
125
-
126
- This creates `.brainclaw/` in your repo, detects your coding agent, writes MCP config and instruction files, and sets up session hooks. It takes about 10 seconds.
127
-
128
- ### 3. Restart your agent
129
-
130
- Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
131
-
132
- ### 4. Start working
111
+ ## Get Started
112
+
113
+ ### 1. Let your coding agent lead
114
+
115
+ The smoothest first-run path is agent-first:
116
+
117
+ 1. ask your coding agent to inspect the package and explain what brainclaw does
118
+ 2. ask it to install brainclaw and initialize or join the project you're working on
119
+ 3. use the CLI yourself when you need an explicit operator or fallback path
120
+
121
+ If you want to drive setup manually, use the steps below.
122
+
123
+ ### 2. Install
124
+
125
+ ```bash
126
+ npm install -g brainclaw
127
+ ```
128
+
129
+ ### 3. Bootstrap this machine
130
+
131
+ ```bash
132
+ brainclaw setup-machine --yes
133
+ ```
134
+
135
+ This detects the installed agents on the current machine, writes the machine-level MCP and user config Brainclaw manages, and does **not** scan or initialize repositories.
136
+
137
+ ### 4. Initialize or refresh the current project
138
+
139
+ ```bash
140
+ cd your-project
141
+ brainclaw init
142
+ ```
143
+
144
+ `brainclaw init` is now safe to rerun. It creates `.brainclaw/` when the project is new, or refreshes the managed Brainclaw and agent integration files when the project already has memory.
145
+
146
+ If you are explicitly adding another agent to an existing Brainclaw project, use:
147
+
148
+ ```bash
149
+ brainclaw enable-agent <agent-name>
150
+ ```
151
+
152
+ ### 5. Restart your agent
153
+
154
+ Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
155
+
156
+ ### 6. Start working
133
157
 
134
158
  Pick one of the canonical entry points depending on what you're doing:
135
159
 
@@ -161,7 +185,7 @@ For agents without MCP (e.g. Copilot reads `.github/copilot-instructions.md`), r
161
185
  brainclaw export --detect --write
162
186
  ```
163
187
 
164
- ### 5. Verify it works
188
+ ### 7. Verify it works
165
189
 
166
190
  ```bash
167
191
  brainclaw status # see active sessions, claims, plans
@@ -172,11 +196,11 @@ brainclaw agent-board # see what each agent is doing
172
196
 
173
197
  To configure brainclaw for all your repos and agents at once:
174
198
 
175
- ```bash
176
- brainclaw setup --yes
177
- ```
178
-
179
- This scans your projects, detects installed agents (Claude Code, Codex, Cursor, Copilot, Cline, Mistral Vibe, etc.), and writes MCP configs for each.
199
+ ```bash
200
+ brainclaw setup --yes
201
+ ```
202
+
203
+ This is the broader multi-repo wizard. It bootstraps the machine, scans your project roots, and initializes selected repositories in one pass.
180
204
 
181
205
  ### Existing projects
182
206
 
Binary file
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import path from 'node:path';
3
3
  import { Command } from 'commander';
4
4
  import { runInit } from './commands/init.js';
5
- import { runSetup } from './commands/setup.js';
5
+ import { runSetup, runSetupMachine } from './commands/setup.js';
6
6
  import { runUpgrade } from './commands/upgrade.js';
7
7
  import { patchAllMcpConfigs } from './core/agent-files.js';
8
8
  import { runReconcile } from './commands/reconcile.js';
@@ -47,6 +47,7 @@ import { cleanupStaleCandidates } from './core/candidates.js';
47
47
  import { runListClaims } from './commands/list-claims.js';
48
48
  import { runReleaseClaim } from './commands/release-claim.js';
49
49
  import { runClaimResource } from './commands/claim-resource.js';
50
+ import { runAssignmentResource } from './commands/assignment-resource.js';
50
51
  import { runMemoryCommand } from './commands/memory.js';
51
52
  import { runReleaseClaims } from './commands/release-claims.js';
52
53
  import { runAgentBoard } from './commands/agent-board.js';
@@ -104,6 +105,8 @@ import { runMigrate } from './commands/migrate.js';
104
105
  import { runRunProfile } from './commands/run-profile.js';
105
106
  import { runCompact } from './commands/compact.js';
106
107
  import { runHarvestCandidates } from './commands/harvest.js';
108
+ import { runQuestionsCommand } from './commands/questions.js';
109
+ import { runReplyCommand } from './commands/reply.js';
107
110
  import { requireRegisteredAgentIdentity } from './core/agent-registry.js';
108
111
  const program = new Command();
109
112
  function collect(value, previous) {
@@ -161,7 +164,7 @@ program
161
164
  initLogLevel({ verbose: root.verbose, debug: root.debug });
162
165
  // Skip effective cwd resolution for commands that create the store
163
166
  const cmdName = actionCommand.name();
164
- const skipResolution = cmdName === 'init' || cmdName === 'setup';
167
+ const skipResolution = cmdName === 'init' || cmdName === 'setup' || cmdName === 'setup-machine';
165
168
  // pln#359 phase 1c — `--project <name>` resolves a linked project to an
166
169
  // absolute path via resolveProjectCwd, then feeds the same chdir flow
167
170
  // as --cwd. Mutually exclusive with --cwd to avoid ambiguity.
@@ -193,17 +196,19 @@ program
193
196
  logger.info(`Cleaned ${removed} orphan lock/tmp file(s) in ${memoryDir()}`);
194
197
  }
195
198
  }
196
- else if (explicitCwd) {
197
- // For init/setup, still respect explicit --cwd / --project but nothing else
199
+ else if (explicitCwd && cmdName !== 'init') {
200
+ // For setup commands, still respect explicit --cwd / --project but nothing else.
201
+ // init receives cwd through runInit options so scaffold writers do not
202
+ // depend on changing process.cwd().
198
203
  process.chdir(path.resolve(explicitCwd));
199
204
  }
200
205
  });
201
206
  // --- init ---
202
207
  program
203
208
  .command('init')
204
- .description('Initialize project memory in .brainclaw/ storage directory')
209
+ .description('Initialize or refresh project memory in .brainclaw/ storage directory')
205
210
  .option('-y, --yes', 'Skip interactive wizard and use defaults')
206
- .option('--force', 'Overwrite existing project memory directory')
211
+ .option('--force', 'Rebuild managed Brainclaw config and generated files from defaults')
207
212
  .option('--compact', 'Enable compact markdown mode')
208
213
  .option('--topology <mode>', 'Topology mode: embedded, sidecar, local-only')
209
214
  .option('--project-mode <mode>', 'Project mode: single-project, multi-project, auto')
@@ -211,13 +216,18 @@ program
211
216
  .option('--no-analyze-repo', 'Skip repository analysis when suggesting a project mode')
212
217
  .option('--no-ai-scan', 'Skip AI surface scan during init')
213
218
  .option('--scan', 'Scan subdirectories for service boundaries and suggest init targets')
219
+ .option('--cwd <path>', 'Override working directory for init scaffolding (parity with other CLI commands)')
214
220
  .action(async (options) => {
215
- await runInit(options);
221
+ // pln#515 step 1: commander binds --cwd to the program-level option even
222
+ // when it appears after `init`, so resolve via program.opts() and feed
223
+ // runInit's existing options.cwd plumb.
224
+ const programCwd = program.opts().cwd;
225
+ await runInit({ ...options, cwd: options.cwd ?? programCwd });
216
226
  });
217
227
  // --- setup ---
218
228
  program
219
229
  .command('setup')
220
- .description('Interactive onboarding wizard — global agent install + multi-repo init')
230
+ .description('Interactive onboarding wizard — machine bootstrap plus multi-repo init')
221
231
  .option('--roots <paths>', 'Comma-separated root directories to scan (skips interactive prompt)')
222
232
  .option('--agents <agents>', 'Agents to configure: all, detected, or comma-separated names')
223
233
  .option('--repos <mode>', 'Repo selection: all, current, or comma-separated numbers')
@@ -225,6 +235,15 @@ program
225
235
  .action(async (options) => {
226
236
  await runSetup(options);
227
237
  });
238
+ // --- setup-machine ---
239
+ program
240
+ .command('setup-machine')
241
+ .description('Machine-only onboarding — detect/configure agents and MCP without scanning or initializing repositories')
242
+ .option('--agents <agents>', 'Agents to configure: all, detected, or comma-separated names')
243
+ .option('-y, --yes', 'Accept all defaults non-interactively')
244
+ .action(async (options) => {
245
+ await runSetupMachine(options);
246
+ });
228
247
  // --- memory-log ---
229
248
  program
230
249
  .command('memory-log')
@@ -1039,6 +1058,26 @@ program
1039
1058
  .action((subcommand, args, options) => {
1040
1059
  runClaimResource(subcommand, args, { ...options, planStatus: options.planStatus, localOnly: options.localOnly });
1041
1060
  });
1061
+ // --- assignment ---
1062
+ program
1063
+ .command('assignment <subcommand> [args...]')
1064
+ .description('Manage work assignments (list, show, update, cancel)')
1065
+ .option('--json', 'Output as JSON for list/show')
1066
+ .option('--all', 'Include terminal assignments in list')
1067
+ .option('--status <status>', 'Status filter for list or target status for update')
1068
+ .option('--agent <agent>', 'Filter by agent name')
1069
+ .option('--claim <id>', 'Filter by linked claim ID')
1070
+ .option('--plan <id>', 'Filter by linked plan ID')
1071
+ .option('--sequence <id>', 'Filter by linked sequence ID')
1072
+ .option('--reason <text>', 'Optional status reason for update/cancel')
1073
+ .action((subcommand, args, options) => {
1074
+ runAssignmentResource(subcommand, args, {
1075
+ ...options,
1076
+ claim: options.claim,
1077
+ plan: options.plan,
1078
+ sequence: options.sequence,
1079
+ });
1080
+ });
1042
1081
  // --- list-claims ---
1043
1082
  program
1044
1083
  .command('list-claims')
@@ -1266,8 +1305,8 @@ program
1266
1305
  .option('--maintenance-mode <mode>', 'Maintenance mode: full (default) or fast')
1267
1306
  .option('--include-context', 'Output full project context after starting session (replaces separate context call)')
1268
1307
  .option('--json', 'Output as JSON')
1269
- .action((options) => {
1270
- runSessionStart(options);
1308
+ .action(async (options) => {
1309
+ await runSessionStart(options);
1271
1310
  });
1272
1311
  // --- session-end ---
1273
1312
  program
@@ -1283,8 +1322,8 @@ program
1283
1322
  .option('--reviewer <name>', 'Explicit reviewer to route the reflected handoff review to')
1284
1323
  .option('--reflect', 'Include structured reflection questions for the agent to answer')
1285
1324
  .option('--json', 'Output as JSON')
1286
- .action((options) => {
1287
- runSessionEnd({
1325
+ .action(async (options) => {
1326
+ await runSessionEnd({
1288
1327
  ...options,
1289
1328
  autoReflect: options.autoReflect,
1290
1329
  autoRelease: options.autoRelease,
@@ -1843,6 +1882,114 @@ if (isCodevEnabled()) {
1843
1882
  runCodevMetrics(thread, { ...options, cwd: globalOpts.cwd });
1844
1883
  });
1845
1884
  }
1885
+ // --- questions (operator-question artifacts across loops; pln#508 step 4) ---
1886
+ program
1887
+ .command('questions')
1888
+ .description('List pending operator_question artifacts across loops in the current project')
1889
+ .option('--loop <loop_id>', 'Filter to a single loop')
1890
+ .option('--status <status>', 'Filter by status: awaiting (default), answered, timed_out', 'awaiting')
1891
+ .option('--mine', 'Filter to questions targeted at the current agent (v1 heuristic: humans see all awaiting)')
1892
+ .option('--json', 'Output as JSON')
1893
+ .action((options) => {
1894
+ const globalOpts = program.opts();
1895
+ const status = options.status;
1896
+ if (!['awaiting', 'answered', 'timed_out'].includes(status)) {
1897
+ console.error(`Error: --status must be one of awaiting|answered|timed_out (got "${options.status}")`);
1898
+ process.exit(1);
1899
+ }
1900
+ runQuestionsCommand({
1901
+ loop: options.loop,
1902
+ status,
1903
+ mine: options.mine,
1904
+ json: options.json,
1905
+ }, globalOpts.cwd);
1906
+ });
1907
+ // --- bootstrap-loop (open/join/status/cancel a bootstrap loop; pln#513 step 3) ---
1908
+ program
1909
+ .command('bootstrap-loop')
1910
+ .description('Open or join a bootstrap loop on the current project, or query its status')
1911
+ .option('--status', 'Report current state')
1912
+ .option('--cancel', 'Cancel the active bootstrap loop')
1913
+ .option('--yes', 'Skip confirmation prompts')
1914
+ .option('--json', 'Machine-readable output')
1915
+ .action(async (options) => {
1916
+ const globalOpts = program.opts();
1917
+ const { runBootstrapLoopCommand } = await import('./commands/bootstrap-loop.js');
1918
+ await runBootstrapLoopCommand(options, globalOpts.cwd);
1919
+ });
1920
+ // --- loop (drive loop turn verbs; pln#517 step 2) ---
1921
+ const loopCmd = program
1922
+ .command('loop')
1923
+ .description('Drive a loop turn (turn / complete-turn / advance / add-artifact)');
1924
+ loopCmd
1925
+ .command('turn <loop_id>')
1926
+ .description('Issue a turn assignment on a slot')
1927
+ .requiredOption('--slot <slot_id>', 'Target slot id (lsl_...)')
1928
+ .option('--input <text>', 'Free-form input passed to the slot')
1929
+ .option('--role <role>', 'Slot role (resolves the first non-done slot with that role)')
1930
+ .option('--assignment-id <id>', 'Dispatcher-provided assignment id to record on the slot')
1931
+ .option('--json', 'Machine-readable output')
1932
+ .action(async (loop_id, options) => {
1933
+ const globalOpts = program.opts();
1934
+ const { runLoopCommand } = await import('./commands/loop.js');
1935
+ await runLoopCommand('turn', { loop_id }, options, globalOpts.cwd);
1936
+ });
1937
+ loopCmd
1938
+ .command('complete-turn <loop_id>')
1939
+ .description('Complete a slot turn')
1940
+ .requiredOption('--slot <slot_id>', 'Target slot id (lsl_...)')
1941
+ .requiredOption('--outcome <outcome>', 'Turn outcome: done, failed, or cancelled')
1942
+ .option('--failure-reason <text>', 'Reason when outcome is failed')
1943
+ .option('--artifact <json>', 'JSON object payload for an artifact to attach')
1944
+ .option('--json', 'Machine-readable output')
1945
+ .action(async (loop_id, options) => {
1946
+ const globalOpts = program.opts();
1947
+ const { runLoopCommand } = await import('./commands/loop.js');
1948
+ await runLoopCommand('complete-turn', { loop_id }, options, globalOpts.cwd);
1949
+ });
1950
+ loopCmd
1951
+ .command('advance <loop_id>')
1952
+ .description('Advance a loop to its next phase')
1953
+ .option('--to-phase <name>', 'Explicit target phase')
1954
+ .option('--force', 'Bypass phase gate checks')
1955
+ .option('--reason <text>', 'Reason to record on the phase advance event')
1956
+ .option('--json', 'Machine-readable output')
1957
+ .action(async (loop_id, options) => {
1958
+ const globalOpts = program.opts();
1959
+ const { runLoopCommand } = await import('./commands/loop.js');
1960
+ await runLoopCommand('advance', { loop_id }, options, globalOpts.cwd);
1961
+ });
1962
+ loopCmd
1963
+ .command('add-artifact <loop_id>')
1964
+ .description('Attach an artifact to a loop')
1965
+ .requiredOption('--phase <phase>', 'Artifact phase')
1966
+ .requiredOption('--type <type>', 'Artifact type')
1967
+ .requiredOption('--body <json-or-text>', 'Artifact body as JSON or text')
1968
+ .option('--produced-by <agent>', 'Agent that produced the artifact')
1969
+ .option('--ref <ref>', 'JSON ref object, e.g. {"kind":"plan","id":"pln_..."}')
1970
+ .option('--json', 'Machine-readable output')
1971
+ .action(async (loop_id, options) => {
1972
+ const globalOpts = program.opts();
1973
+ const { runLoopCommand } = await import('./commands/loop.js');
1974
+ await runLoopCommand('add-artifact', { loop_id }, options, globalOpts.cwd);
1975
+ });
1976
+ // --- reply (provide_input to an operator_question; pln#508 step 4) ---
1977
+ program
1978
+ .command('reply <qst_id>')
1979
+ .description('Resolve an operator_question artifact (wraps bclaw_loop.provide_input)')
1980
+ .option('--answer <text>', 'Free-form answer text')
1981
+ .option('--choose <option_id>', 'Pick one of the question\'s structured options[].id')
1982
+ .option('--skip', 'Materialize the question\'s suggested_default')
1983
+ .option('--json', 'Output as JSON')
1984
+ .action((qstId, options) => {
1985
+ const globalOpts = program.opts();
1986
+ runReplyCommand(qstId, {
1987
+ answer: options.answer,
1988
+ choose: options.choose,
1989
+ skip: options.skip,
1990
+ json: options.json,
1991
+ }, globalOpts.cwd);
1992
+ });
1846
1993
  // --- run (agent profiles) ---
1847
1994
  program
1848
1995
  .command('run [profile-name]')
@@ -0,0 +1,182 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { listAssignments, loadAssignment, transitionAssignment } from '../core/assignments.js';
3
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
4
+ import { AssignmentStatusSchema } from '../core/schema.js';
5
+ const TERMINAL_STATUSES = new Set(['completed', 'cancelled', 'expired', 'rerouted']);
6
+ const KNOWN_SUBCOMMANDS = new Set(['list', 'ls', 'show', 'get', 'update', 'cancel']);
7
+ export function runAssignmentResource(subcommand, args, options = {}) {
8
+ const normalized = subcommand.trim().toLowerCase();
9
+ if (normalized === 'list' || normalized === 'ls') {
10
+ runListAssignmentsCommand(options);
11
+ return;
12
+ }
13
+ if (normalized === 'show' || normalized === 'get') {
14
+ const id = args[0];
15
+ if (!id) {
16
+ console.error(`Error: assignment ${normalized} requires <id>.`);
17
+ process.exit(1);
18
+ }
19
+ runShowAssignmentCommand(id, options);
20
+ return;
21
+ }
22
+ if (normalized === 'update') {
23
+ const id = args[0];
24
+ if (!id) {
25
+ console.error('Error: assignment update requires <id>.');
26
+ process.exit(1);
27
+ }
28
+ const status = options.status;
29
+ if (!status) {
30
+ console.error('Error: assignment update requires --status <status>.');
31
+ process.exit(1);
32
+ }
33
+ runTransitionAssignmentCommand(id, status, options);
34
+ return;
35
+ }
36
+ if (normalized === 'cancel') {
37
+ const id = args[0];
38
+ if (!id) {
39
+ console.error('Error: assignment cancel requires <id>.');
40
+ process.exit(1);
41
+ }
42
+ runTransitionAssignmentCommand(id, 'cancelled', options);
43
+ return;
44
+ }
45
+ if (normalized.startsWith('asgn_') || KNOWN_SUBCOMMANDS.has(normalized)) {
46
+ console.error(`Error: unknown assignment subcommand "${subcommand}".`);
47
+ console.error(' Available: list, show, get, update, cancel');
48
+ process.exit(1);
49
+ }
50
+ console.error('Error: missing assignment subcommand.');
51
+ console.error(' Available: list, show, get, update, cancel');
52
+ process.exit(1);
53
+ }
54
+ function runListAssignmentsCommand(options) {
55
+ ensureInitialized(options.cwd);
56
+ const requestedStatus = parseAssignmentStatus(options.status);
57
+ let assignments = listAssignments(options.cwd, {
58
+ status: requestedStatus,
59
+ agent: options.agent,
60
+ claim_id: options.claim,
61
+ plan_id: options.plan,
62
+ sequence_id: options.sequence,
63
+ });
64
+ if (!options.all && !requestedStatus) {
65
+ assignments = assignments.filter((assignment) => !TERMINAL_STATUSES.has(assignment.status));
66
+ }
67
+ if (options.json) {
68
+ console.log(JSON.stringify(assignments, null, 2));
69
+ return;
70
+ }
71
+ if (assignments.length === 0) {
72
+ console.log(options.all ? 'No assignments.' : 'No active assignments.');
73
+ return;
74
+ }
75
+ console.log(`${assignments.length} ${options.all ? 'assignment(s)' : 'active assignment(s)'}:`);
76
+ console.log('');
77
+ for (const assignment of assignments) {
78
+ const extras = [];
79
+ if (assignment.plan_id)
80
+ extras.push(`plan ${assignment.plan_id}`);
81
+ if (assignment.claim_id)
82
+ extras.push(`claim ${assignment.claim_id}`);
83
+ if (assignment.sequence_id)
84
+ extras.push(`sequence ${assignment.sequence_id}`);
85
+ if (assignment.worktree_path)
86
+ extras.push(`worktree ${assignment.worktree_path}`);
87
+ const suffix = extras.length > 0 ? ` [${extras.join(', ')}]` : '';
88
+ console.log(` [${assignment.id}] ${assignment.agent} (${assignment.status}) -> ${assignment.scope}: ${assignment.description}${suffix}`);
89
+ }
90
+ }
91
+ function runShowAssignmentCommand(id, options) {
92
+ ensureInitialized(options.cwd);
93
+ const assignment = requireAssignment(id, options.cwd);
94
+ if (options.json) {
95
+ console.log(JSON.stringify(assignment, null, 2));
96
+ return;
97
+ }
98
+ console.log(`Assignment: ${assignment.id}`);
99
+ console.log(` Agent: ${assignment.agent}`);
100
+ console.log(` Status: ${assignment.status}`);
101
+ console.log(` Scope: ${assignment.scope}`);
102
+ console.log(` Description: ${assignment.description}`);
103
+ console.log(` Claim: ${assignment.claim_id}`);
104
+ if (assignment.message_id)
105
+ console.log(` Message: ${assignment.message_id}`);
106
+ if (assignment.plan_id)
107
+ console.log(` Plan: ${assignment.plan_id}`);
108
+ if (assignment.sequence_id)
109
+ console.log(` Sequence: ${assignment.sequence_id}`);
110
+ if (assignment.session_id)
111
+ console.log(` Session: ${assignment.session_id}`);
112
+ if (assignment.status_reason)
113
+ console.log(` Reason: ${assignment.status_reason}`);
114
+ if (assignment.worktree_path)
115
+ console.log(` Worktree: ${assignment.worktree_path}`);
116
+ console.log(` Created: ${assignment.created_at}`);
117
+ if (assignment.updated_at)
118
+ console.log(` Updated: ${assignment.updated_at}`);
119
+ if (assignment.offered_at)
120
+ console.log(` Offered: ${assignment.offered_at}`);
121
+ if (assignment.accepted_at)
122
+ console.log(` Accepted: ${assignment.accepted_at}`);
123
+ if (assignment.started_at)
124
+ console.log(` Started: ${assignment.started_at}`);
125
+ if (assignment.completed_at)
126
+ console.log(` Completed: ${assignment.completed_at}`);
127
+ if (assignment.cancelled_at)
128
+ console.log(` Cancelled: ${assignment.cancelled_at}`);
129
+ if (assignment.failed_at)
130
+ console.log(` Failed: ${assignment.failed_at}`);
131
+ if (assignment.blocked_at)
132
+ console.log(` Blocked: ${assignment.blocked_at}`);
133
+ if (assignment.timed_out_at)
134
+ console.log(` Timed out: ${assignment.timed_out_at}`);
135
+ if (assignment.expired_at)
136
+ console.log(` Expired: ${assignment.expired_at}`);
137
+ if (assignment.rerouted_at)
138
+ console.log(` Rerouted: ${assignment.rerouted_at}`);
139
+ }
140
+ function runTransitionAssignmentCommand(id, statusInput, options) {
141
+ ensureInitialized(options.cwd);
142
+ const nextStatus = parseAssignmentStatus(statusInput, 'status');
143
+ const actor = resolveCurrentAgentName(options.cwd);
144
+ try {
145
+ const result = transitionAssignment(id, nextStatus, {
146
+ actor,
147
+ status_reason: options.reason,
148
+ }, options.cwd);
149
+ const verb = nextStatus === 'cancelled' ? 'cancelled' : 'updated';
150
+ console.log(`✔ Assignment ${verb}: [${result.assignment.id}] ${result.previous_status} -> ${result.assignment.status}`);
151
+ }
152
+ catch (error) {
153
+ console.error(`Error: ${error.message}`);
154
+ process.exit(1);
155
+ }
156
+ }
157
+ function ensureInitialized(cwd) {
158
+ if (!memoryExists(cwd)) {
159
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
160
+ process.exit(1);
161
+ }
162
+ }
163
+ function requireAssignment(id, cwd) {
164
+ const assignment = loadAssignment(id, cwd);
165
+ if (!assignment) {
166
+ console.error(`Error: assignment not found: ${id}`);
167
+ process.exit(1);
168
+ }
169
+ return assignment;
170
+ }
171
+ function parseAssignmentStatus(value, label = 'filter') {
172
+ if (value === undefined) {
173
+ return undefined;
174
+ }
175
+ const parsed = AssignmentStatusSchema.safeParse(value);
176
+ if (!parsed.success) {
177
+ console.error(`Error: invalid ${label} '${value}'. Expected one of: ${AssignmentStatusSchema.options.join(', ')}`);
178
+ process.exit(1);
179
+ }
180
+ return parsed.data;
181
+ }
182
+ //# sourceMappingURL=assignment-resource.js.map