sdd-agent-platform 0.1.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.
@@ -0,0 +1,1499 @@
1
+ #!/usr/bin/env node
2
+ import { archiveRun, applyAiToolEntries, applySyncBack, createWorktreeLifecycle, createRun, doctor, evaluateLifecycleDecisionGate, evaluateGovernancePolicy, getProjectStatus, getDelegationStateMachine, getSddInstructions, initProject, inspectRun, inspectSddTask, inspectToolPluginContract, inspectToolCapability, inspectDelegationQueueItem, inspectSyncBack, inspectTaskGraph, inspectWavePlan, inspectWaveExecutor, inspectBackgroundExecutor, inspectArtifactResultIngestions, inspectWorktreeLifecycle, inspectWorkerAdapterContract, inspectWorktreeIsolation, inspectGovernancePolicy, keepWorktreeLifecycle, removeWorktreeLifecycle, listRuns, rebuildLocalRunIndex, inspectLocalRunIndex, queryLocalRunIndex, listToolPluginContracts, listToolCapabilities, listDelegationQueueItems, listWorkerAdapterContracts, ingestArtifactResult, parseSddBranch, readRunState, recordLifecycleDecision, renderDoctorReport, renderGoalVerifyResult, renderLifecycleDecisionGate, renderSddResultArtifactTemplate, renderSddInstructions, renderSingleTaskLoopResult, renderTaskGapReport, renderTaskInspect, renderTaskList, runGoalVerify, runSingleTaskLoop, runBackgroundExecutor, runWaveExecutor, SDD_VERSION, summarizeAiProjectionStatus, validateSddResultArtifact, } from '../../core/src/index.js';
3
+ async function main(args) {
4
+ const projectRoot = process.cwd();
5
+ const [command, subcommand, ...rest] = args;
6
+ if (!command || command === '--help' || command === '-h') {
7
+ return {
8
+ exitCode: 0,
9
+ output: helpText()
10
+ };
11
+ }
12
+ if (command === '--version' || command === '-v') {
13
+ return {
14
+ exitCode: 0,
15
+ output: SDD_VERSION
16
+ };
17
+ }
18
+ if (command === 'init') {
19
+ const initArgs = [subcommand, ...rest].filter(Boolean);
20
+ const force = initArgs.includes('--force');
21
+ const aiTool = readAiToolSelection(initArgs, true);
22
+ const branch = readOption(initArgs, '--branch') ?? 'master';
23
+ const scaffoldDocuments = !initArgs.includes('--no-scaffold-docs');
24
+ const result = await initProject(projectRoot, { force, aiTool, branch, scaffoldDocuments });
25
+ return {
26
+ exitCode: 0,
27
+ output: JSON.stringify({ command: 'init', ...result }, null, 2)
28
+ };
29
+ }
30
+ if (command === 'update') {
31
+ const updateArgs = [subcommand, ...rest].filter(Boolean);
32
+ const check = updateArgs.includes('--check');
33
+ const aiTool = readAiToolSelection(updateArgs, false);
34
+ const results = await applyAiToolEntries(projectRoot, { tool: aiTool, check });
35
+ const status = summarizeAiProjectionStatus(results);
36
+ return {
37
+ exitCode: status === 'FAIL' ? 1 : 0,
38
+ output: JSON.stringify({ command: 'update', check, status, aiTools: results }, null, 2)
39
+ };
40
+ }
41
+ if (command === 'instructions') {
42
+ const instructionArgs = [subcommand, ...rest].filter(Boolean);
43
+ const action = instructionArgs.find((item) => !item.startsWith('--')) ?? 'overview';
44
+ const payload = getSddInstructions(action);
45
+ const json = instructionArgs.includes('--json');
46
+ return {
47
+ exitCode: 0,
48
+ output: json ? JSON.stringify(payload, null, 2) : renderSddInstructions(payload)
49
+ };
50
+ }
51
+ if (command === 'doctor') {
52
+ const doctorArgs = [subcommand, ...rest].filter(Boolean);
53
+ if (doctorArgs.includes('--latest-only') && doctorArgs.includes('--all-runs')) {
54
+ return {
55
+ exitCode: 2,
56
+ error: 'Usage: sdd doctor [--latest-only] [--all-runs] (choose only one scope flag)'
57
+ };
58
+ }
59
+ const report = await doctor(projectRoot, {
60
+ latestOnly: doctorArgs.includes('--latest-only'),
61
+ allRuns: doctorArgs.includes('--all-runs')
62
+ });
63
+ return {
64
+ exitCode: report.status === 'FAIL' ? 1 : 0,
65
+ output: renderDoctorReport(report)
66
+ };
67
+ }
68
+ if (command === 'status') {
69
+ const statusArgs = [subcommand, ...rest].filter(Boolean);
70
+ const result = await getProjectStatus(projectRoot, { branch: readOption(statusArgs, '--branch') ?? 'master' });
71
+ const json = statusArgs.includes('--json');
72
+ return {
73
+ exitCode: result.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
74
+ output: json ? JSON.stringify(result, null, 2) : renderProjectStatus(result)
75
+ };
76
+ }
77
+ if (command === 'run' && subcommand === 'create') {
78
+ const state = await createRun(projectRoot);
79
+ return {
80
+ exitCode: 0,
81
+ output: JSON.stringify({ runId: state.runId, statePath: `.sdd/runs/${state.runId}/state.json`, eventLogPath: `.sdd/runs/${state.runId}/events.jsonl` }, null, 2)
82
+ };
83
+ }
84
+ if (command === 'run' && subcommand === 'status') {
85
+ const runId = rest[0];
86
+ if (!runId) {
87
+ return {
88
+ exitCode: 2,
89
+ error: 'Usage: sdd run status <run_id>'
90
+ };
91
+ }
92
+ const state = await readRunState(projectRoot, runId);
93
+ return {
94
+ exitCode: 0,
95
+ output: JSON.stringify({ runId: state.runId, status: state.status, phase: state.phase, currentTask: state.currentTask, updatedAt: state.updatedAt }, null, 2)
96
+ };
97
+ }
98
+ if (command === 'run' && subcommand === 'list') {
99
+ const runs = await listRuns(projectRoot);
100
+ const json = rest.includes('--json');
101
+ return {
102
+ exitCode: 0,
103
+ output: json ? JSON.stringify(runs, null, 2) : renderRunList(runs)
104
+ };
105
+ }
106
+ if (command === 'run' && subcommand === 'index') {
107
+ const action = rest[0];
108
+ const json = rest.includes('--json');
109
+ if (action === 'rebuild') {
110
+ const index = await rebuildLocalRunIndex(projectRoot);
111
+ return {
112
+ exitCode: 0,
113
+ output: json ? JSON.stringify(index, null, 2) : renderLocalRunIndex(index)
114
+ };
115
+ }
116
+ if (action === 'inspect') {
117
+ const inspection = await inspectLocalRunIndex(projectRoot);
118
+ return {
119
+ exitCode: inspection.valid ? 0 : 1,
120
+ output: json ? JSON.stringify(inspection, null, 2) : renderLocalRunIndexInspection(inspection)
121
+ };
122
+ }
123
+ if (action === 'query') {
124
+ const status = readRunStatus(rest, '--status');
125
+ if (readOption(rest, '--status') && !status) {
126
+ return {
127
+ exitCode: 2,
128
+ error: 'Usage: sdd run index query [--run <run_id>] [--task <task_id>] [--status created|running|completed|blocked|failed|archived] [--artifact <path>] [--json]'
129
+ };
130
+ }
131
+ const index = await queryLocalRunIndex(projectRoot, {
132
+ runId: readOption(rest, '--run') ?? undefined,
133
+ taskId: readOption(rest, '--task') ?? undefined,
134
+ status: status ?? undefined,
135
+ artifact: readOption(rest, '--artifact') ?? undefined
136
+ });
137
+ return {
138
+ exitCode: 0,
139
+ output: json ? JSON.stringify(index, null, 2) : renderLocalRunIndex(index)
140
+ };
141
+ }
142
+ return {
143
+ exitCode: 2,
144
+ error: 'Usage: sdd run index rebuild|inspect|query [options]'
145
+ };
146
+ }
147
+ if (command === 'run' && subcommand === 'inspect') {
148
+ const runId = rest[0];
149
+ if (!runId) {
150
+ return {
151
+ exitCode: 2,
152
+ error: 'Usage: sdd run inspect <run_id> [--json]'
153
+ };
154
+ }
155
+ const result = await inspectRun(projectRoot, runId);
156
+ const json = rest.includes('--json');
157
+ return {
158
+ exitCode: 0,
159
+ output: json ? JSON.stringify(result, null, 2) : renderRunInspection(result)
160
+ };
161
+ }
162
+ if (command === 'run' && subcommand === 'archive') {
163
+ const runId = rest[0];
164
+ if (!runId) {
165
+ return {
166
+ exitCode: 2,
167
+ error: 'Usage: sdd run archive <run_id> [--reason <text>]'
168
+ };
169
+ }
170
+ const state = await archiveRun(projectRoot, runId, { reason: readOption(rest, '--reason') ?? undefined });
171
+ return {
172
+ exitCode: 0,
173
+ output: JSON.stringify({ runId: state.runId, status: state.status, updatedAt: state.updatedAt }, null, 2)
174
+ };
175
+ }
176
+ if (command === 'sync-back' && subcommand === 'inspect') {
177
+ const runId = rest[0];
178
+ if (!runId) {
179
+ return {
180
+ exitCode: 2,
181
+ error: 'Usage: sdd sync-back inspect <run_id> [--branch <branch>] [--task <task_id>] [--json]'
182
+ };
183
+ }
184
+ const result = await inspectSyncBack(projectRoot, {
185
+ runId,
186
+ branch: readOption(rest, '--branch') ?? 'master',
187
+ taskId: readOption(rest, '--task') ?? undefined
188
+ });
189
+ const json = rest.includes('--json');
190
+ return {
191
+ exitCode: result.status === 'blocked' ? 1 : 0,
192
+ output: json ? JSON.stringify(result, null, 2) : renderSyncBackInspection(result)
193
+ };
194
+ }
195
+ if (command === 'sync-back' && subcommand === 'apply') {
196
+ const runId = rest[0];
197
+ if (!runId) {
198
+ return {
199
+ exitCode: 2,
200
+ error: 'Usage: sdd sync-back apply <run_id> [--branch <branch>] [--task <task_id>] [--approved] [--json]'
201
+ };
202
+ }
203
+ const result = await applySyncBack(projectRoot, {
204
+ runId,
205
+ branch: readOption(rest, '--branch') ?? 'master',
206
+ taskId: readOption(rest, '--task') ?? undefined,
207
+ approved: rest.includes('--approved')
208
+ });
209
+ const json = rest.includes('--json');
210
+ return {
211
+ exitCode: 0,
212
+ output: json ? JSON.stringify(result, null, 2) : renderSyncBackApplyResult(result)
213
+ };
214
+ }
215
+ if (command === 'tasks' && subcommand === 'format') {
216
+ return {
217
+ exitCode: 0,
218
+ output: taskFormatText()
219
+ };
220
+ }
221
+ if (command === 'tasks' && subcommand === 'list') {
222
+ const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
223
+ return {
224
+ exitCode: model.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
225
+ output: renderTaskList(model)
226
+ };
227
+ }
228
+ if (command === 'tasks' && subcommand === 'inspect') {
229
+ const taskId = rest.find((item) => !item.startsWith('--'));
230
+ if (!taskId) {
231
+ return {
232
+ exitCode: 2,
233
+ error: 'Usage: sdd tasks inspect <task_id> [--branch <branch>]'
234
+ };
235
+ }
236
+ const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
237
+ const result = inspectSddTask(model, taskId);
238
+ if (!result.task && result.gaps.length === 0) {
239
+ return {
240
+ exitCode: 1,
241
+ error: `Task not found: ${taskId}`
242
+ };
243
+ }
244
+ return {
245
+ exitCode: result.task === null || result.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
246
+ output: renderTaskInspect(result.task, result.gaps)
247
+ };
248
+ }
249
+ if (command === 'tasks' && subcommand === 'gaps') {
250
+ const model = await parseSddBranch(projectRoot, readOption(rest, '--branch') ?? 'master');
251
+ return {
252
+ exitCode: model.gaps.some((gap) => gap.severity === 'blocking') ? 1 : 0,
253
+ output: renderTaskGapReport(model)
254
+ };
255
+ }
256
+ if (command === 'lifecycle' && subcommand === 'decide') {
257
+ const result = evaluateLifecycleDecisionGate(readLifecycleSignalOptions(rest));
258
+ const runId = readOption(rest, '--run');
259
+ if (runId) {
260
+ await recordLifecycleDecision(projectRoot, runId, result.record);
261
+ }
262
+ const json = rest.includes('--json');
263
+ return {
264
+ exitCode: 0,
265
+ output: json ? JSON.stringify({ ...result, recordedRunId: runId ?? null }, null, 2) : `${renderLifecycleDecisionGate(result)}${runId ? `\nrecorded_run=${runId}` : ''}`
266
+ };
267
+ }
268
+ if (command === 'do' && subcommand === 'task') {
269
+ const taskId = rest.find((item) => !item.startsWith('--'));
270
+ if (!taskId) {
271
+ return {
272
+ exitCode: 2,
273
+ error: 'Usage: sdd do task <task_id> [--branch <branch>] [--run <run_id>] [--implement-artifact artifacts/path.md] [--review-artifact artifacts/path.md] [--debug-artifact artifacts/path.md] [--validation-artifact artifacts/path.md]'
274
+ };
275
+ }
276
+ const result = await runSingleTaskLoop(projectRoot, {
277
+ taskId,
278
+ branch: readOption(rest, '--branch') ?? 'master',
279
+ runId: readOption(rest, '--run') ?? undefined,
280
+ implementArtifact: readOption(rest, '--implement-artifact') ?? undefined,
281
+ reviewArtifact: readOption(rest, '--review-artifact') ?? undefined,
282
+ debugArtifact: readOption(rest, '--debug-artifact') ?? undefined,
283
+ validationArtifact: readOption(rest, '--validation-artifact') ?? undefined
284
+ });
285
+ return {
286
+ exitCode: result.status === 'completed' ? 0 : 1,
287
+ output: renderSingleTaskLoopResult(result)
288
+ };
289
+ }
290
+ if (command === 'verify' && subcommand === 'task') {
291
+ const taskId = rest.find((item) => !item.startsWith('--'));
292
+ const runId = readOption(rest, '--run');
293
+ if (!taskId || !runId) {
294
+ return {
295
+ exitCode: 2,
296
+ error: 'Usage: sdd verify task <task_id> --run <run_id> [--branch <branch>] [--review-artifact artifacts/path.md] [--validation-artifact artifacts/path.md]'
297
+ };
298
+ }
299
+ const result = await runGoalVerify(projectRoot, {
300
+ taskId,
301
+ runId,
302
+ branch: readOption(rest, '--branch') ?? 'master',
303
+ reviewArtifact: readOption(rest, '--review-artifact') ?? undefined,
304
+ validationArtifact: readOption(rest, '--validation-artifact') ?? undefined
305
+ });
306
+ return {
307
+ exitCode: result.status === 'PASS' ? 0 : 1,
308
+ output: renderGoalVerifyResult(result)
309
+ };
310
+ }
311
+ if (command === 'governance' && subcommand === 'inspect') {
312
+ const policy = await inspectGovernancePolicy(projectRoot);
313
+ return {
314
+ exitCode: 0,
315
+ output: rest.includes('--json') ? JSON.stringify(policy, null, 2) : renderGovernancePolicy(policy)
316
+ };
317
+ }
318
+ if (command === 'governance' && subcommand === 'evaluate') {
319
+ const operation = readGovernancePolicyOperation(rest[0]);
320
+ if (!operation) {
321
+ return {
322
+ exitCode: 2,
323
+ error: 'Usage: sdd governance evaluate background_executor|wave_executor|sync_back_apply|destructive_git|external_interaction|cleanup [--worker <adapter_id>] [--risk <tag>] [--approved] [--json]'
324
+ };
325
+ }
326
+ const decision = await evaluateGovernancePolicy(projectRoot, {
327
+ operation,
328
+ workerAdapterId: readOption(rest, '--worker') ?? undefined,
329
+ riskTags: readRepeatedOption(rest, '--risk'),
330
+ approved: rest.includes('--approved')
331
+ });
332
+ return {
333
+ exitCode: decision.allowed ? 0 : 1,
334
+ output: rest.includes('--json') ? JSON.stringify(decision, null, 2) : renderGovernancePolicyDecision(decision)
335
+ };
336
+ }
337
+ if (command === 'capabilities' && subcommand === 'list') {
338
+ const registry = await listToolCapabilities(projectRoot);
339
+ return {
340
+ exitCode: 0,
341
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderCapabilityList(registry.capabilities)
342
+ };
343
+ }
344
+ if (command === 'capabilities' && subcommand === 'inspect') {
345
+ const capabilityId = rest.find((item) => !item.startsWith('--'));
346
+ if (!capabilityId) {
347
+ return {
348
+ exitCode: 2,
349
+ error: 'Usage: sdd capabilities inspect <capability_id> [--json]'
350
+ };
351
+ }
352
+ const capability = await inspectToolCapability(projectRoot, capabilityId);
353
+ if (!capability) {
354
+ return {
355
+ exitCode: 1,
356
+ error: `Unknown capability: ${capabilityId}`
357
+ };
358
+ }
359
+ return {
360
+ exitCode: 0,
361
+ output: rest.includes('--json') ? JSON.stringify(capability, null, 2) : renderCapabilityInspect(capability)
362
+ };
363
+ }
364
+ if (command === 'plugins' && subcommand === 'list') {
365
+ const registry = await listToolPluginContracts(projectRoot);
366
+ return {
367
+ exitCode: 0,
368
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderPluginContractList(registry.contracts)
369
+ };
370
+ }
371
+ if (command === 'plugins' && subcommand === 'inspect') {
372
+ const pluginId = rest.find((item) => !item.startsWith('--'));
373
+ if (!pluginId) {
374
+ return {
375
+ exitCode: 2,
376
+ error: 'Usage: sdd plugins inspect <plugin_id> [--json]'
377
+ };
378
+ }
379
+ const contract = await inspectToolPluginContract(projectRoot, pluginId);
380
+ if (!contract) {
381
+ return {
382
+ exitCode: 1,
383
+ error: `Unknown plugin contract: ${pluginId}`
384
+ };
385
+ }
386
+ return {
387
+ exitCode: 0,
388
+ output: rest.includes('--json') ? JSON.stringify(contract, null, 2) : renderPluginContractInspect(contract)
389
+ };
390
+ }
391
+ if (command === 'queue' && subcommand === 'list') {
392
+ const snapshot = await listDelegationQueueItems(projectRoot, { runId: readOption(rest, '--run') ?? undefined });
393
+ return {
394
+ exitCode: 0,
395
+ output: rest.includes('--json') ? JSON.stringify(snapshot, null, 2) : renderDelegationQueueList(snapshot.items)
396
+ };
397
+ }
398
+ if (command === 'queue' && subcommand === 'inspect') {
399
+ const queueItemId = rest.find((item) => !item.startsWith('--'));
400
+ if (!queueItemId) {
401
+ return {
402
+ exitCode: 2,
403
+ error: 'Usage: sdd queue inspect <queue_item_id> [--json]'
404
+ };
405
+ }
406
+ const item = await inspectDelegationQueueItem(projectRoot, queueItemId);
407
+ if (!item) {
408
+ return {
409
+ exitCode: 1,
410
+ error: `Unknown queue item: ${queueItemId}`
411
+ };
412
+ }
413
+ return {
414
+ exitCode: 0,
415
+ output: rest.includes('--json') ? JSON.stringify(item, null, 2) : renderDelegationQueueInspect(item)
416
+ };
417
+ }
418
+ if (command === 'state-machine' && subcommand === 'inspect') {
419
+ const machine = getDelegationStateMachine();
420
+ return {
421
+ exitCode: 0,
422
+ output: rest.includes('--json') ? JSON.stringify(machine, null, 2) : renderDelegationStateMachineInspect(machine)
423
+ };
424
+ }
425
+ if (command === 'isolation' && subcommand === 'inspect') {
426
+ const taskId = rest.find((item) => !item.startsWith('--'));
427
+ if (!taskId) {
428
+ return {
429
+ exitCode: 2,
430
+ error: 'Usage: sdd isolation inspect <task_id> [--branch <branch>] [--capability <capability_id>] [--peer-task <task_id>] [--json]'
431
+ };
432
+ }
433
+ const decision = await inspectWorktreeIsolation(projectRoot, {
434
+ taskId,
435
+ branch: readOption(rest, '--branch') ?? 'master',
436
+ capabilityId: readOption(rest, '--capability') ?? undefined,
437
+ peerTaskIds: readRepeatedOptions(rest, '--peer-task')
438
+ });
439
+ return {
440
+ exitCode: decision.mode === 'blocked' ? 1 : 0,
441
+ output: rest.includes('--json') ? JSON.stringify(decision, null, 2) : renderWorktreeIsolationDecision(decision)
442
+ };
443
+ }
444
+ if (command === 'graph' && subcommand === 'inspect') {
445
+ const graph = await inspectTaskGraph(projectRoot, { branch: readOption(rest, '--branch') ?? 'master' });
446
+ return {
447
+ exitCode: graph.valid ? 0 : 1,
448
+ output: rest.includes('--json') ? JSON.stringify(graph, null, 2) : renderTaskGraphPlan(graph)
449
+ };
450
+ }
451
+ if (command === 'wave' && subcommand === 'inspect') {
452
+ const wavePlan = await inspectWavePlan(projectRoot, {
453
+ branch: readOption(rest, '--branch') ?? 'master',
454
+ capabilityId: readOption(rest, '--capability') ?? undefined
455
+ });
456
+ return {
457
+ exitCode: wavePlan.valid ? 0 : 1,
458
+ output: rest.includes('--json') ? JSON.stringify(wavePlan, null, 2) : renderWavePlan(wavePlan)
459
+ };
460
+ }
461
+ if (command === 'wave' && subcommand === 'run') {
462
+ const strategy = readWaveExecutorStrategy(rest, '--strategy');
463
+ if (!strategy) {
464
+ return {
465
+ exitCode: 2,
466
+ error: 'Usage: sdd wave run [--branch <branch>] [--run <run_id>] [--capability <id>] [--agent <agent>] [--worker <adapter_id>] [--strategy fast-stop|safe-continue] [--artifact <task_id:path>]... [--json]'
467
+ };
468
+ }
469
+ const result = await runWaveExecutor(projectRoot, {
470
+ branch: readOption(rest, '--branch') ?? 'master',
471
+ runId: readOption(rest, '--run') ?? undefined,
472
+ capabilityId: readOption(rest, '--capability') ?? undefined,
473
+ agent: readOption(rest, '--agent') ?? undefined,
474
+ workerAdapterId: readOption(rest, '--worker') ?? undefined,
475
+ strategy,
476
+ artifactPaths: readTaskArtifactOptions(rest)
477
+ });
478
+ return {
479
+ exitCode: result.status === 'completed' || result.status === 'claimed' ? 0 : 1,
480
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderWaveExecutorResult(result)
481
+ };
482
+ }
483
+ if (command === 'wave' && subcommand === 'executor') {
484
+ const runId = rest[0];
485
+ if (!runId) {
486
+ return {
487
+ exitCode: 2,
488
+ error: 'Usage: sdd wave executor <run_id> [--json]'
489
+ };
490
+ }
491
+ const inspection = await inspectWaveExecutor(projectRoot, runId);
492
+ return {
493
+ exitCode: inspection.valid ? 0 : 1,
494
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderWaveExecutorInspection(inspection)
495
+ };
496
+ }
497
+ if (command === 'background' && subcommand === 'run') {
498
+ const taskId = rest[0];
499
+ if (!taskId) {
500
+ return {
501
+ exitCode: 2,
502
+ error: 'Usage: sdd background run <task_id> [--run <run_id>] [--agent <agent>] [--worker <adapter_id>] [--artifact <path>] [--branch <branch>] [--json]'
503
+ };
504
+ }
505
+ const result = await runBackgroundExecutor(projectRoot, {
506
+ branch: readOption(rest, '--branch') ?? 'master',
507
+ runId: readOption(rest, '--run') ?? undefined,
508
+ taskId,
509
+ agent: readOption(rest, '--agent') ?? undefined,
510
+ workerAdapterId: readOption(rest, '--worker') ?? undefined,
511
+ artifactPath: readOption(rest, '--artifact') ?? undefined,
512
+ delegationId: readOption(rest, '--delegation') ?? undefined
513
+ });
514
+ return {
515
+ exitCode: result.status === 'blocked' || result.status === 'failed' ? 1 : 0,
516
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderBackgroundExecutorResult(result)
517
+ };
518
+ }
519
+ if (command === 'background' && subcommand === 'inspect') {
520
+ const runId = rest[0];
521
+ if (!runId) {
522
+ return {
523
+ exitCode: 2,
524
+ error: 'Usage: sdd background inspect <run_id> [--json]'
525
+ };
526
+ }
527
+ const inspection = await inspectBackgroundExecutor(projectRoot, runId);
528
+ return {
529
+ exitCode: inspection.valid ? 0 : 1,
530
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderBackgroundExecutorInspection(inspection)
531
+ };
532
+ }
533
+ if (command === 'worktree' && subcommand === 'create') {
534
+ const runId = rest[0];
535
+ const taskId = rest[1];
536
+ if (!runId || !taskId) {
537
+ return {
538
+ exitCode: 2,
539
+ error: 'Usage: sdd worktree create <run_id> <task_id> [--base <ref>] [--id <worktree_id>] [--json]'
540
+ };
541
+ }
542
+ const record = await createWorktreeLifecycle(projectRoot, runId, {
543
+ taskId,
544
+ baseRef: readOption(rest, '--base') ?? undefined,
545
+ worktreeId: readOption(rest, '--id') ?? undefined
546
+ });
547
+ return {
548
+ exitCode: 0,
549
+ output: rest.includes('--json') ? JSON.stringify(record, null, 2) : renderWorktreeLifecycleRecord('Worktree created', record)
550
+ };
551
+ }
552
+ if (command === 'worktree' && subcommand === 'inspect') {
553
+ const runId = rest[0];
554
+ if (!runId) {
555
+ return {
556
+ exitCode: 2,
557
+ error: 'Usage: sdd worktree inspect <run_id> [--json]'
558
+ };
559
+ }
560
+ const inspection = await inspectWorktreeLifecycle(projectRoot, runId);
561
+ return {
562
+ exitCode: inspection.valid ? 0 : 1,
563
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderWorktreeLifecycleInspection(inspection)
564
+ };
565
+ }
566
+ if (command === 'worktree' && subcommand === 'keep') {
567
+ const runId = rest[0];
568
+ const worktreeId = rest[1];
569
+ if (!runId || !worktreeId) {
570
+ return {
571
+ exitCode: 2,
572
+ error: 'Usage: sdd worktree keep <run_id> <worktree_id> [--reason <text>] [--json]'
573
+ };
574
+ }
575
+ const record = await keepWorktreeLifecycle(projectRoot, runId, worktreeId, { reason: readOption(rest, '--reason') ?? undefined });
576
+ return {
577
+ exitCode: 0,
578
+ output: rest.includes('--json') ? JSON.stringify(record, null, 2) : renderWorktreeLifecycleRecord('Worktree kept', record)
579
+ };
580
+ }
581
+ if (command === 'worktree' && subcommand === 'remove') {
582
+ const runId = rest[0];
583
+ const worktreeId = rest[1];
584
+ if (!runId || !worktreeId) {
585
+ return {
586
+ exitCode: 2,
587
+ error: 'Usage: sdd worktree remove <run_id> <worktree_id> [--json]'
588
+ };
589
+ }
590
+ const record = await removeWorktreeLifecycle(projectRoot, runId, worktreeId);
591
+ return {
592
+ exitCode: 0,
593
+ output: rest.includes('--json') ? JSON.stringify(record, null, 2) : renderWorktreeLifecycleRecord('Worktree removed', record)
594
+ };
595
+ }
596
+ if (command === 'workers' && subcommand === 'list') {
597
+ const registry = await listWorkerAdapterContracts(projectRoot);
598
+ return {
599
+ exitCode: 0,
600
+ output: rest.includes('--json') ? JSON.stringify(registry, null, 2) : renderWorkerAdapterList(registry.adapters)
601
+ };
602
+ }
603
+ if (command === 'workers' && subcommand === 'inspect') {
604
+ const adapterId = rest.find((item) => !item.startsWith('--'));
605
+ if (!adapterId) {
606
+ return {
607
+ exitCode: 2,
608
+ error: 'Usage: sdd workers inspect <adapter_id> [--json]'
609
+ };
610
+ }
611
+ const adapter = await inspectWorkerAdapterContract(projectRoot, adapterId);
612
+ if (!adapter) {
613
+ return {
614
+ exitCode: 1,
615
+ error: `Unknown worker adapter: ${adapterId}`
616
+ };
617
+ }
618
+ return {
619
+ exitCode: 0,
620
+ output: rest.includes('--json') ? JSON.stringify(adapter, null, 2) : renderWorkerAdapterInspect(adapter)
621
+ };
622
+ }
623
+ if (command === 'artifact' && subcommand === 'template') {
624
+ const artifactPath = rest.find((item) => !item.startsWith('--'));
625
+ const taskId = readOption(rest, '--task');
626
+ const agent = readOption(rest, '--agent');
627
+ if (!artifactPath || !taskId || !agent) {
628
+ return {
629
+ exitCode: 2,
630
+ error: 'Usage: sdd artifact template <artifacts/path.md> --task <task_id> --agent <agent> [--branch <branch>] [--status <status>]'
631
+ };
632
+ }
633
+ return {
634
+ exitCode: 0,
635
+ output: await renderSddResultArtifactTemplate(projectRoot, {
636
+ artifactPath,
637
+ taskId,
638
+ agent,
639
+ branch: readOption(rest, '--branch') ?? 'master',
640
+ status: readSddResultStatus(rest, '--status') ?? 'PASS'
641
+ })
642
+ };
643
+ }
644
+ if (command === 'artifact' && subcommand === 'validate') {
645
+ const runId = rest[0];
646
+ const artifactPath = rest[1];
647
+ if (!runId || !artifactPath) {
648
+ return {
649
+ exitCode: 2,
650
+ error: 'Usage: sdd artifact validate <run_id> <artifacts/path.md> [--task <task_id>] [--agent <agent>] [--json]'
651
+ };
652
+ }
653
+ const expectedTask = readOption(rest, '--task') ?? undefined;
654
+ const expectedAgent = readOption(rest, '--agent') ?? undefined;
655
+ const report = await validateSddResultArtifact(projectRoot, runId, artifactPath, {
656
+ expectedTask,
657
+ expectedAgent
658
+ });
659
+ return {
660
+ exitCode: report.valid ? 0 : 1,
661
+ output: rest.includes('--json') ? JSON.stringify(report, null, 2) : renderArtifactValidationReport(artifactPath, report, expectedTask, expectedAgent)
662
+ };
663
+ }
664
+ if (command === 'artifact' && subcommand === 'ingest') {
665
+ const runId = rest[0];
666
+ const delegationId = rest[1];
667
+ const artifactPath = rest[2];
668
+ if (!runId || !delegationId || !artifactPath) {
669
+ return {
670
+ exitCode: 2,
671
+ error: 'Usage: sdd artifact ingest <run_id> <delegation_id> <artifacts/path.md> [--json]'
672
+ };
673
+ }
674
+ const result = await ingestArtifactResult(projectRoot, runId, { delegationId, artifactPath });
675
+ return {
676
+ exitCode: result.valid ? 0 : 1,
677
+ output: rest.includes('--json') ? JSON.stringify(result, null, 2) : renderArtifactIngestionResult(result)
678
+ };
679
+ }
680
+ if (command === 'artifact' && subcommand === 'ingestions') {
681
+ const runId = rest[0];
682
+ if (!runId) {
683
+ return {
684
+ exitCode: 2,
685
+ error: 'Usage: sdd artifact ingestions <run_id> [--json]'
686
+ };
687
+ }
688
+ const inspection = await inspectArtifactResultIngestions(projectRoot, runId);
689
+ return {
690
+ exitCode: inspection.valid ? 0 : 1,
691
+ output: rest.includes('--json') ? JSON.stringify(inspection, null, 2) : renderArtifactIngestionInspection(inspection)
692
+ };
693
+ }
694
+ return {
695
+ exitCode: 2,
696
+ error: `Unknown command: ${args.join(' ')}\n\n${helpText()}`
697
+ };
698
+ }
699
+ function helpText() {
700
+ return `sdd Phase 2 platform CLI
701
+
702
+ Commands:
703
+ sdd --version Print CLI/core version
704
+ sdd init [--force] [--ai <mode>] [--branch <branch>] [--no-scaffold-docs]
705
+ Create .sdd config, starter SDD docs, and generated AI entries
706
+ sdd update [--check] [--ai <mode>] Refresh or check managed generated AI entries
707
+ sdd instructions [action] [--json] Print dynamic SDD instruction payload
708
+ sdd doctor [--latest-only] [--all-runs] Check config, run evidence, specs, and AI entry drift
709
+ sdd status [--branch <branch>] [--json] Show tasks, latest run, gaps, and recommended next command
710
+ sdd run create Create .sdd/runs/<run_id> with state/events/artifacts
711
+ sdd run status <run_id> Print current run status
712
+ sdd run list [--json] List recorded runs by updated time
713
+ sdd run index rebuild|inspect|query [options] Rebuild, inspect, or query Phase 3.13 local run index
714
+ sdd run inspect <run_id> [--json] Inspect run state, events, artifacts, validation, sync-back
715
+ sdd run archive <run_id> [--reason] Archive a run without deleting evidence
716
+ sdd sync-back inspect <run_id> [options] Inspect explicit proposal-to-tasks.md write-back readiness
717
+ sdd sync-back apply <run_id> [--approved] Apply verified sync-back proposal to tasks.md
718
+ sdd lifecycle decide [options] Evaluate lifecycle decision gate
719
+ sdd do task <task_id> [options] Run ingestion-aware task workflow over supplied artifacts
720
+ sdd verify task <task_id> --run <run_id> Run goal-level acceptance coverage verify
721
+ sdd tasks format Print canonical sdd-task fenced block format
722
+ sdd tasks list [--branch <branch>] Parse and list sdd-task blocks
723
+ sdd tasks inspect <task_id> [--branch] Inspect one parsed task
724
+ sdd tasks gaps [--branch <branch>] Render parser task gap report
725
+ sdd artifact template <path> [options] Print a valid sdd-result artifact template
726
+ sdd artifact validate <run_id> <path> Validate a run-relative sdd-result artifact
727
+ sdd artifact ingest <run_id> <delegation_id> <path>
728
+ sdd artifact ingestions <run_id> [--json] Inspect Phase 3.6 artifact ingestion ledger
729
+ sdd capabilities list [--json] List Phase 3.1 tool/capability declarations
730
+ sdd capabilities inspect <id> [--json] Inspect one capability declaration
731
+ sdd governance inspect|evaluate [options] Inspect or evaluate Phase 3.14 governance policy
732
+ sdd plugins list [--json] List Phase 3.2 plugin loading contracts
733
+ sdd plugins inspect <id> [--json] Inspect one plugin loading contract
734
+ sdd queue list [--run <run_id>] [--json] List Phase 3.3 delegation queue items
735
+ sdd queue inspect <id> [--json] Inspect one delegation queue item
736
+ sdd state-machine inspect [--json] Inspect Phase 3.4 delegation state machine
737
+ sdd workers list [--json] List Phase 3.5 worker adapter contracts
738
+ sdd workers inspect <id> [--json] Inspect one worker adapter contract
739
+ sdd isolation inspect <task_id> [options] Dry-run Phase 3.7 worktree isolation decision
740
+ sdd graph inspect [--branch <branch>] [--json] Inspect Phase 3.9 task dependency graph
741
+ sdd wave inspect [--branch <branch>] [--capability <id>] [--json] Inspect Phase 3.10 dependency wave plan
742
+ sdd wave run [options] Run Phase 3.12 planner-driven wave executor
743
+ sdd wave executor <run_id> [--json] Inspect Phase 3.12 wave executor evidence
744
+ sdd background run <task_id> [options] Claim one Phase 3.11 background delegation; ingest supplied artifact if provided
745
+ sdd background inspect <run_id> [--json] Inspect Phase 3.11 background executor evidence
746
+ sdd worktree create <run_id> <task_id> [options]
747
+ sdd worktree inspect <run_id> [--json] Inspect Phase 3.8 worktree lifecycle records
748
+ sdd worktree keep <run_id> <id> Mark a worktree retained for inspection
749
+ sdd worktree remove <run_id> <id> Remove a clean tracked worktree
750
+
751
+ AI options:
752
+ --ai auto Project Claude Code entries when supported
753
+ --ai claude-code Project Claude Code entries explicitly
754
+ --ai none Skip AI entry projection during init
755
+
756
+ Init options:
757
+ --branch <branch> Create starter docs under specs/<branch>; default master
758
+ --no-scaffold-docs Skip starter spec.md/plan.md/tasks.md creation
759
+
760
+ Doctor options:
761
+ --latest-only Inspect only the newest non-archived run evidence
762
+ --all-runs Inspect every run, including archived runs
763
+
764
+ Run index options:
765
+ --run <run_id> Filter local run index query by run id
766
+ --task <task_id> Filter local run index query by task id
767
+ --status <status> Filter query by run status
768
+ --artifact <path> Filter query by indexed artifact path
769
+ --json Print machine-readable local run index output
770
+
771
+ Artifact options:
772
+ --task <task_id> Expected artifact task id
773
+ --agent <agent> Expected producing agent name
774
+ --branch <branch> Branch used to copy validator Acceptance mapping
775
+ --status <status> Template status; default PASS
776
+ --json Print machine-readable validation result
777
+
778
+ Governance options:
779
+ --worker <adapter_id> Worker adapter to evaluate
780
+ --risk <tag> Risk tag to evaluate; repeatable
781
+ --approved Record explicit approval for confirmation-gated operations
782
+ --json Print machine-readable governance output
783
+
784
+ Wave executor options:
785
+ --branch <branch> Read specs/<branch>/tasks.md; default master
786
+ --run <run_id> Reuse an existing run; default creates one
787
+ --capability <id> Capability used for planner isolation decisions
788
+ --strategy fast-stop|safe-continue Stop on first task failure or finish current safe wave
789
+ --artifact <task_id:path> Supply task-specific run-relative artifact; repeatable
790
+
791
+ Instructions actions:
792
+ overview | init | doctor | update | spec | plan | tasks | do | verify | run-task | verify-task
793
+
794
+ Do task options:
795
+ --branch <branch> Read specs/<branch>/spec.md, plan.md, tasks.md
796
+ --run <run_id> Reuse an existing run instead of creating one
797
+ --implement-artifact <path> Run-relative implementer artifact, if available
798
+ --review-artifact <path> Run-relative reviewer artifact, required to complete
799
+ --debug-artifact <path> Optional single debugger artifact after review failure
800
+ --validation-artifact <path> Run-relative validator artifact, required to complete
801
+
802
+ Verify task options:
803
+ --branch <branch> Read specs/<branch>/spec.md, plan.md, tasks.md
804
+ --run <run_id> Required run id containing state/events/artifacts
805
+ --review-artifact <path> Optional reviewer artifact override
806
+ --validation-artifact <path> Optional validator artifact override
807
+
808
+ Lifecycle decide options:
809
+ --direct-safe High clarity, high confidence, reversible, cheap local validation
810
+ --risk <tag> Add risk tag; api/schema/database/security/state-machine/ci force gates
811
+ --contract <name> Mark affected API/schema/contract
812
+ --impact-confidence <high|medium|low> Set impact confidence
813
+ --acceptance <high|medium|low> Set acceptance clarity
814
+ --validation <clear|partial|unclear> Set validation clarity
815
+ --external-unknown Require research route
816
+ --architecture Require architecture/research route
817
+ --checkpoint Force human checkpoint
818
+ --permission <name> Mark permission/checkpoint requirement
819
+ --run <run_id> Record decision to existing run state/events
820
+ --json Print machine-readable result
821
+
822
+ Sync-back options:
823
+ --branch <branch> Read target specs/<branch>/tasks.md
824
+ --task <task_id> Override run currentTask when inspecting or applying
825
+ --json Print machine-readable result
826
+
827
+ Isolation options:
828
+ --branch <branch> Read specs/<branch>/tasks.md
829
+ --capability <capability_id> Capability side effect used for isolation decision
830
+ --peer-task <task_id> Compare affected_files overlap against peer task
831
+
832
+ Graph options:
833
+ --branch <branch> Read specs/<branch>/tasks.md
834
+
835
+ Wave options:
836
+ --branch <branch> Read specs/<branch>/tasks.md
837
+ --capability <capability_id> Capability side effect used for isolation decisions
838
+
839
+ Background options:
840
+ --branch <branch> Read specs/<branch>/tasks.md
841
+ --run <run_id> Reuse an existing run; default creates one
842
+ --agent <agent> Delegated agent name; default implementer
843
+ --worker <adapter_id> Worker adapter id; default sdd-cli-task-worker
844
+ --artifact <path> Run-relative sdd-result artifact to ingest
845
+ --delegation <delegation_id> Stable delegation id for retry/ingest
846
+ Worktree options:
847
+ --base <ref> Base ref for git worktree add; default HEAD
848
+ --id <worktree_id> Stable lifecycle id; default wt-<run>-<task>
849
+ --reason <text> Reason when keeping a worktree
850
+ `;
851
+ }
852
+ function renderProjectStatus(status) {
853
+ const lines = [`SDD status for ${status.branch}`];
854
+ lines.push(`documents spec=${status.documents.specExists} plan=${status.documents.planExists} tasks=${status.documents.tasksExists}`);
855
+ lines.push(`tasks total=${status.tasks.total} pending=${status.tasks.pending} in_progress=${status.tasks.inProgress} completed=${status.tasks.completed} blocked=${status.tasks.blocked} deferred=${status.tasks.deferred} unknown=${status.tasks.unknown} gaps=${status.tasks.gaps}`);
856
+ if (status.latestRun) {
857
+ lines.push(`latest_run ${status.latestRun.runId} status=${status.latestRun.status} phase=${status.latestRun.phase ?? 'n/a'} task=${status.latestRun.currentTask ?? 'n/a'} validation=${status.latestRun.validationStatus} sync_back=${status.latestRun.syncBackStatus}`);
858
+ }
859
+ else {
860
+ lines.push('latest_run none');
861
+ }
862
+ if (status.gaps.length > 0) {
863
+ lines.push('gaps');
864
+ for (const gap of status.gaps) {
865
+ lines.push(`- [${gap.severity}] ${gap.type} ${gap.taskId ?? 'document'} ${gap.field}: ${gap.message}`);
866
+ }
867
+ }
868
+ lines.push(`next ${status.recommendedNextCommand}`);
869
+ return lines.join('\n');
870
+ }
871
+ function renderRunList(runs) {
872
+ if (runs.length === 0) {
873
+ return 'No SDD runs found.';
874
+ }
875
+ const lines = ['SDD runs'];
876
+ for (const run of runs) {
877
+ lines.push(`${run.runId}\t${run.status}\tphase=${run.phase ?? 'n/a'}\ttask=${run.currentTask ?? 'n/a'}\tvalidation=${run.validationStatus}\tsync_back=${run.syncBackStatus}\tupdated=${run.updatedAt}`);
878
+ }
879
+ return lines.join('\n');
880
+ }
881
+ function renderLocalRunIndex(index) {
882
+ const lines = [`Local run index ${index.contract}`];
883
+ lines.push(`generated=${index.generatedAt} runs=${index.runs.length} tasks=${index.tasks.length} delegations=${index.delegations.length} artifacts=${index.artifacts.length} waves=${index.waves.length}`);
884
+ for (const run of index.runs) {
885
+ lines.push(`- ${run.runId}: ${run.status} phase=${run.phase ?? 'n/a'} task=${run.currentTask ?? 'n/a'} artifacts=${run.artifactCount} updated=${run.updatedAt}`);
886
+ }
887
+ return lines.join('\n');
888
+ }
889
+ function renderLocalRunIndexInspection(inspection) {
890
+ const lines = [`Local run index ${inspection.valid ? 'valid' : 'invalid'}`];
891
+ lines.push(`path=${inspection.indexPath} exists=${inspection.exists}`);
892
+ if (inspection.index) {
893
+ lines.push(`contract=${inspection.index.contract} runs=${inspection.index.runs.length} tasks=${inspection.index.tasks.length} delegations=${inspection.index.delegations.length} artifacts=${inspection.index.artifacts.length} waves=${inspection.index.waves.length}`);
894
+ }
895
+ if (inspection.issues.length > 0) {
896
+ lines.push('issues');
897
+ for (const issue of inspection.issues) {
898
+ lines.push(`- ${issue.field}: ${issue.message}`);
899
+ lines.push(` recommendation: ${issue.recommendation}`);
900
+ }
901
+ }
902
+ return lines.join('\n');
903
+ }
904
+ function renderGovernancePolicy(policy) {
905
+ const lines = [`Governance policy ${policy.version}`];
906
+ lines.push(`concurrency background=${policy.concurrency.maxBackgroundDelegations} wave=${policy.concurrency.maxWaveExecutors}`);
907
+ lines.push(`confirmation operations=${policy.manualConfirmation.operations.join(',')}`);
908
+ lines.push(`confirmation workers=${policy.manualConfirmation.workerAdapters.join(',') || 'none'}`);
909
+ lines.push(`confirmation risks=${policy.manualConfirmation.riskTags.join(',') || 'none'}`);
910
+ lines.push(`cleanup archive_only=${policy.cleanup.archiveOnly} delete_run_history=${policy.cleanup.deleteRunHistory}`);
911
+ lines.push(`retry reopen_terminal=${policy.retry.reopenTerminalDelegation} max_attempts=${policy.retry.maxAttemptsPerDelegation}`);
912
+ lines.push(`stop_conditions=${policy.stopConditions.join(',')}`);
913
+ return lines.join('\n');
914
+ }
915
+ function renderGovernancePolicyDecision(decision) {
916
+ const lines = [`Governance decision ${decision.status} for ${decision.operation}`];
917
+ lines.push(`version=${decision.version} allowed=${decision.allowed}`);
918
+ lines.push('reasons');
919
+ for (const reason of decision.reasons) {
920
+ lines.push(`- ${reason}`);
921
+ }
922
+ if (decision.issues.length > 0) {
923
+ lines.push('issues');
924
+ for (const issue of decision.issues) {
925
+ lines.push(`- ${issue.field}: ${issue.message}`);
926
+ lines.push(` recommendation: ${issue.recommendation}`);
927
+ }
928
+ }
929
+ return lines.join('\n');
930
+ }
931
+ function renderRunInspection(inspection) {
932
+ const lines = [`SDD run ${inspection.summary.runId}`];
933
+ lines.push(`status=${inspection.summary.status} phase=${inspection.summary.phase ?? 'n/a'} task=${inspection.summary.currentTask ?? 'n/a'} updated=${inspection.summary.updatedAt}`);
934
+ lines.push(`validation=${inspection.validation.status} evidence=${inspection.validation.evidence.join(',') || 'none'}`);
935
+ lines.push(`sync_back=${inspection.syncBack.status} proposal=${inspection.syncBack.proposalPath ?? 'none'}`);
936
+ lines.push(`artifacts=${inspection.artifacts.length}`);
937
+ for (const artifact of inspection.artifacts) {
938
+ lines.push(`- ${artifact.path} kind=${artifact.kind} task=${artifact.task ?? 'n/a'} agent=${artifact.agent ?? 'n/a'}`);
939
+ }
940
+ lines.push(`artifact_ingestions=${inspection.artifactIngestions.length}`);
941
+ for (const ingestion of inspection.artifactIngestions) {
942
+ lines.push(`- ${ingestion.delegationId} ${ingestion.status} artifact=${ingestion.artifactPath} result=${ingestion.resultStatus ?? 'n/a'} delegation=${ingestion.delegationStatus ?? 'n/a'}`);
943
+ }
944
+ lines.push(`events=${inspection.eventCount}`);
945
+ for (const event of inspection.recentEvents) {
946
+ lines.push(`- ${event.time} ${event.event}${event.summary ? `: ${event.summary}` : ''}`);
947
+ }
948
+ return lines.join('\n');
949
+ }
950
+ function renderSyncBackInspection(inspection) {
951
+ const lines = [`Sync-back ${inspection.status} for ${inspection.runId}/${inspection.taskId ?? 'n/a'}`];
952
+ lines.push(`branch=${inspection.branch}`);
953
+ lines.push(`proposal=${inspection.proposalPath ?? 'none'}`);
954
+ lines.push(`run_task_status=${inspection.runTaskStatus ?? 'n/a'} markdown_status=${inspection.markdownStatus ?? 'n/a'}`);
955
+ if (inspection.reasons.length > 0) {
956
+ lines.push('reasons');
957
+ for (const reason of inspection.reasons) {
958
+ lines.push(`- ${reason}`);
959
+ }
960
+ }
961
+ if (inspection.gaps.length > 0) {
962
+ lines.push('gaps');
963
+ for (const gap of inspection.gaps) {
964
+ lines.push(`- [${gap.severity}] ${gap.type} ${gap.taskId ?? 'document'} ${gap.field}: ${gap.message}`);
965
+ }
966
+ }
967
+ lines.push(`apply_policy=${inspection.applyPolicy.mode} approval_required=${inspection.applyPolicy.requiresApproval}`);
968
+ for (const reason of inspection.applyPolicy.reasons) {
969
+ lines.push(`- policy: ${reason}`);
970
+ }
971
+ if (inspection.status === 'ready') {
972
+ const approvedFlag = inspection.applyPolicy.requiresApproval ? ' --approved' : '';
973
+ lines.push(`next sdd sync-back apply ${inspection.runId} --branch ${inspection.branch} --task ${inspection.taskId}${approvedFlag}`);
974
+ }
975
+ return lines.join('\n');
976
+ }
977
+ function renderSyncBackApplyResult(result) {
978
+ const lines = [result.message];
979
+ lines.push(`tasks_path=${result.tasksPath}`);
980
+ lines.push(`applied=${result.applied}`);
981
+ lines.push(`sync_back=${result.inspection.status}`);
982
+ return lines.join('\n');
983
+ }
984
+ function renderCapabilityList(capabilities) {
985
+ const lines = ['SDD tool capabilities'];
986
+ for (const capability of capabilities) {
987
+ lines.push(`- ${capability.id} category=${capability.category} side_effect=${capability.sideEffect} default=${capability.defaultAvailable}`);
988
+ }
989
+ return lines.join('\n');
990
+ }
991
+ function renderCapabilityInspect(capability) {
992
+ const lines = [`Capability ${capability.id}`];
993
+ lines.push(`title=${capability.title}`);
994
+ lines.push(`category=${capability.category} side_effect=${capability.sideEffect} default=${capability.defaultAvailable}`);
995
+ lines.push(`summary=${capability.summary}`);
996
+ lines.push('allowed_stages');
997
+ for (const stage of capability.allowedStages) {
998
+ lines.push(`- ${stage}`);
999
+ }
1000
+ lines.push('required_evidence');
1001
+ for (const evidence of capability.requiredEvidence) {
1002
+ lines.push(`- ${evidence}`);
1003
+ }
1004
+ lines.push('forbidden_uses');
1005
+ for (const forbiddenUse of capability.forbiddenUses) {
1006
+ lines.push(`- ${forbiddenUse}`);
1007
+ }
1008
+ return lines.join('\n');
1009
+ }
1010
+ function renderPluginContractList(contracts) {
1011
+ const lines = ['SDD plugin loading contracts'];
1012
+ for (const contract of contracts) {
1013
+ lines.push(`- ${contract.id} capability=${contract.capabilityId} entry=${contract.entryKind} load_mode=${contract.loadMode}`);
1014
+ }
1015
+ return lines.join('\n');
1016
+ }
1017
+ function renderPluginContractInspect(contract) {
1018
+ const lines = [`Plugin contract ${contract.id}`];
1019
+ lines.push(`title=${contract.title}`);
1020
+ lines.push(`version=${contract.version} capability=${contract.capabilityId}`);
1021
+ lines.push(`entry=${contract.entryKind} load_mode=${contract.loadMode}`);
1022
+ lines.push(`asset_path=${contract.assetPath}`);
1023
+ lines.push(`checksum=${contract.checksum ?? 'none'}`);
1024
+ lines.push('required_evidence');
1025
+ for (const evidence of contract.requiredEvidence) {
1026
+ lines.push(`- ${evidence}`);
1027
+ }
1028
+ lines.push('forbidden_uses');
1029
+ for (const forbiddenUse of contract.forbiddenUses) {
1030
+ lines.push(`- ${forbiddenUse}`);
1031
+ }
1032
+ return lines.join('\n');
1033
+ }
1034
+ function renderDelegationQueueList(items) {
1035
+ const lines = ['SDD delegation queue items'];
1036
+ for (const item of items) {
1037
+ lines.push(`- ${item.id} task=${item.taskId} agent=${item.agent} status=${item.status} capability=${item.requestedCapabilityId}`);
1038
+ }
1039
+ return lines.join('\n');
1040
+ }
1041
+ function renderDelegationQueueInspect(item) {
1042
+ const lines = [`Queue item ${item.id}`];
1043
+ lines.push(`run=${item.runId} delegation=${item.delegationId}`);
1044
+ lines.push(`task=${item.taskId} agent=${item.agent} status=${item.status}`);
1045
+ lines.push(`capability=${item.requestedCapabilityId} source=${item.statusSource} run_mode=${item.runMode}`);
1046
+ lines.push(`dedupe_key=${item.dedupeKey}`);
1047
+ lines.push(`expected_artifact=${item.expectedArtifact}`);
1048
+ lines.push('required_evidence');
1049
+ for (const evidence of item.requiredEvidence) {
1050
+ lines.push(`- ${evidence}`);
1051
+ }
1052
+ return lines.join('\n');
1053
+ }
1054
+ function renderDelegationStateMachineInspect(machine) {
1055
+ const lines = [`Delegation state machine ${machine.version}`];
1056
+ lines.push(`statuses=${machine.statuses.join(',')}`);
1057
+ lines.push(`terminal_statuses=${machine.terminalStatuses.join(',')}`);
1058
+ lines.push('transitions');
1059
+ for (const transition of machine.transitions) {
1060
+ lines.push(`- ${transition.from} -> ${transition.to} event=${transition.event} terminal=${transition.terminal}`);
1061
+ }
1062
+ return lines.join('\n');
1063
+ }
1064
+ function renderWorkerAdapterList(adapters) {
1065
+ const lines = ['SDD worker adapter contracts'];
1066
+ for (const adapter of adapters) {
1067
+ lines.push(`- ${adapter.id} kind=${adapter.kind} capability=${adapter.capabilityId} plugin=${adapter.pluginContractId} side_effect=${adapter.sideEffect}`);
1068
+ }
1069
+ return lines.join('\n');
1070
+ }
1071
+ function renderWorkerAdapterInspect(adapter) {
1072
+ const lines = [`Worker adapter ${adapter.id}`];
1073
+ lines.push(`title=${adapter.title}`);
1074
+ lines.push(`version=${adapter.version} kind=${adapter.kind}`);
1075
+ lines.push(`capability=${adapter.capabilityId} plugin=${adapter.pluginContractId} side_effect=${adapter.sideEffect}`);
1076
+ lines.push(`state_machine=${adapter.input.stateMachineVersion}`);
1077
+ lines.push(`artifact_reference=${adapter.output.artifactReference}`);
1078
+ lines.push(`terminal_status=${adapter.output.terminalStatus.join(',')}`);
1079
+ lines.push(`exit_statuses=${adapter.output.exitStatuses.join(',')}`);
1080
+ lines.push(`permission_prompt=${adapter.permissionPrompt}`);
1081
+ lines.push('required_events');
1082
+ for (const event of adapter.output.requiredEvents) {
1083
+ lines.push(`- ${event}`);
1084
+ }
1085
+ lines.push('required_evidence');
1086
+ for (const evidence of adapter.requiredEvidence) {
1087
+ lines.push(`- ${evidence}`);
1088
+ }
1089
+ lines.push('forbidden_uses');
1090
+ for (const forbiddenUse of adapter.forbiddenUses) {
1091
+ lines.push(`- ${forbiddenUse}`);
1092
+ }
1093
+ return lines.join('\n');
1094
+ }
1095
+ function renderArtifactIngestionResult(result) {
1096
+ const lines = [`Artifact ingestion ${result.record.status}: ${result.record.artifactPath}`];
1097
+ lines.push(`delegation=${result.record.delegationId} duplicate=${result.duplicate} result=${result.record.resultStatus ?? 'n/a'} terminal=${result.record.delegationStatus ?? 'n/a'}`);
1098
+ if (result.record.issues.length > 0) {
1099
+ lines.push('issues');
1100
+ for (const issue of result.record.issues) {
1101
+ lines.push(`- ${issue.field}: ${issue.message}`);
1102
+ lines.push(` recommendation: ${issue.recommendation}`);
1103
+ }
1104
+ }
1105
+ if (result.record.gaps.length > 0) {
1106
+ lines.push('gaps');
1107
+ for (const gap of result.record.gaps) {
1108
+ lines.push(`- [${gap.severity}] ${gap.type} ${gap.field}: ${gap.message}`);
1109
+ }
1110
+ }
1111
+ return lines.join('\n');
1112
+ }
1113
+ function renderArtifactIngestionInspection(inspection) {
1114
+ const lines = [`Artifact ingestions ${inspection.valid ? 'valid' : 'invalid'} for ${inspection.runId}`];
1115
+ lines.push(`records=${inspection.records.length}`);
1116
+ for (const record of inspection.records) {
1117
+ lines.push(`- ${record.delegationId} ${record.status} artifact=${record.artifactPath} result=${record.resultStatus ?? 'n/a'} delegation=${record.delegationStatus ?? 'n/a'}`);
1118
+ }
1119
+ if (inspection.issues.length > 0) {
1120
+ lines.push('issues');
1121
+ for (const issue of inspection.issues) {
1122
+ lines.push(`- ${issue.field}: ${issue.message}`);
1123
+ lines.push(` recommendation: ${issue.recommendation}`);
1124
+ }
1125
+ }
1126
+ return lines.join('\n');
1127
+ }
1128
+ function renderBackgroundExecutorResult(result) {
1129
+ const lines = [`Background executor ${result.status} for ${result.taskId}`];
1130
+ lines.push(`version=${result.version}`);
1131
+ lines.push(`run=${result.runId} delegation=${result.delegationId ?? 'n/a'} queue=${result.queueItemId ?? 'n/a'} worker=${result.workerAdapterId}`);
1132
+ lines.push(`artifact=${result.artifactPath ?? 'pending'}`);
1133
+ lines.push(result.message);
1134
+ if (result.issues.length > 0) {
1135
+ lines.push('issues');
1136
+ for (const issue of result.issues) {
1137
+ lines.push(`- ${issue.field}: ${issue.message}`);
1138
+ lines.push(` recommendation: ${issue.recommendation}`);
1139
+ }
1140
+ }
1141
+ return lines.join('\n');
1142
+ }
1143
+ function renderBackgroundExecutorInspection(inspection) {
1144
+ const lines = [`Background executor ${inspection.valid ? 'valid' : 'invalid'} for ${inspection.runId}`];
1145
+ lines.push(`version=${inspection.version}`);
1146
+ lines.push(`delegations=${inspection.delegations.length} running=${inspection.runningDelegations} terminal=${inspection.terminalDelegations} ingestions=${inspection.artifactIngestions.length}`);
1147
+ for (const delegation of inspection.delegations) {
1148
+ lines.push(`- ${delegation.delegationId} ${delegation.status} task=${delegation.taskId} agent=${delegation.agent} artifact=${delegation.expectedArtifact}`);
1149
+ }
1150
+ if (inspection.issues.length > 0) {
1151
+ lines.push('issues');
1152
+ for (const issue of inspection.issues) {
1153
+ lines.push(`- ${issue.field}: ${issue.message}`);
1154
+ lines.push(` recommendation: ${issue.recommendation}`);
1155
+ }
1156
+ }
1157
+ return lines.join('\n');
1158
+ }
1159
+ function renderWaveExecutorResult(result) {
1160
+ const lines = [`Wave executor ${result.status} for ${result.branch}`];
1161
+ lines.push(`version=${result.version}`);
1162
+ lines.push(`run=${result.runId} strategy=${result.strategy} waves=${result.executedWaves}/${result.plannedWaves} tasks=${result.taskResults.length}`);
1163
+ lines.push(result.message);
1164
+ for (const task of result.taskResults) {
1165
+ lines.push(`- wave ${task.waveIndex} ${task.taskId}: ${task.result.status} delegation=${task.result.delegationId ?? 'n/a'} artifact=${task.result.artifactPath ?? 'pending'}`);
1166
+ }
1167
+ if (result.manualGates.length > 0) {
1168
+ lines.push('manual_gates');
1169
+ for (const gate of result.manualGates) {
1170
+ lines.push(`- ${gate.taskId}: ${gate.reasons.join(' | ')}`);
1171
+ }
1172
+ }
1173
+ if (result.blockedTasks.length > 0) {
1174
+ lines.push('blocked_tasks');
1175
+ for (const gate of result.blockedTasks) {
1176
+ lines.push(`- ${gate.taskId}: ${gate.reasons.join(' | ')}`);
1177
+ }
1178
+ }
1179
+ if (result.issues.length > 0) {
1180
+ lines.push('issues');
1181
+ for (const issue of result.issues) {
1182
+ lines.push(`- ${issue.field}: ${issue.message}`);
1183
+ lines.push(` recommendation: ${issue.recommendation}`);
1184
+ }
1185
+ }
1186
+ return lines.join('\n');
1187
+ }
1188
+ function renderWaveExecutorInspection(inspection) {
1189
+ const lines = [`Wave executor ${inspection.valid ? 'valid' : 'invalid'} for ${inspection.runId}`];
1190
+ lines.push(`version=${inspection.version}`);
1191
+ lines.push(`events=${inspection.waveEvents.length} delegations=${inspection.background.delegations.length} terminal=${inspection.background.terminalDelegations}`);
1192
+ for (const event of inspection.waveEvents) {
1193
+ lines.push(`- ${event.event}: ${event.summary ?? ''}`);
1194
+ }
1195
+ if (inspection.issues.length > 0) {
1196
+ lines.push('issues');
1197
+ for (const issue of inspection.issues) {
1198
+ lines.push(`- ${issue.field}: ${issue.message}`);
1199
+ lines.push(` recommendation: ${issue.recommendation}`);
1200
+ }
1201
+ }
1202
+ return lines.join('\n');
1203
+ }
1204
+ function renderWorktreeIsolationDecision(decision) {
1205
+ const lines = [`Worktree isolation ${decision.mode} for ${decision.taskId}`];
1206
+ lines.push(`version=${decision.version}`);
1207
+ lines.push(`safe_concurrency=${decision.safeConcurrency} capability=${decision.capabilityId} side_effect=${decision.capabilitySideEffect}`);
1208
+ lines.push(`affected_files=${decision.affectedFiles.length > 0 ? decision.affectedFiles.join(',') : 'none'}`);
1209
+ lines.push(`risk=${decision.risk.length > 0 ? decision.risk.join(',') : 'none'}`);
1210
+ if (decision.overlaps.length > 0) {
1211
+ lines.push('overlaps');
1212
+ for (const overlap of decision.overlaps) {
1213
+ lines.push(`- ${overlap.peerTaskId}: ${overlap.files.join(',')}`);
1214
+ }
1215
+ }
1216
+ lines.push('gates');
1217
+ for (const gate of decision.gates) {
1218
+ lines.push(`- ${gate.name} ${gate.passed ? 'PASS' : 'FAIL'}: ${gate.message}`);
1219
+ }
1220
+ lines.push('reasons');
1221
+ for (const reason of decision.reasons) {
1222
+ lines.push(`- ${reason}`);
1223
+ }
1224
+ return lines.join('\n');
1225
+ }
1226
+ function renderWorktreeLifecycleRecord(title, record) {
1227
+ return [
1228
+ `${title}: ${record.worktreeId}`,
1229
+ `status=${record.status} task=${record.taskId} branch=${record.branchName}`,
1230
+ `path=${record.worktreePath} base=${record.baseRef} dirty=${record.dirty}`,
1231
+ `kept=${record.keepReason ?? 'n/a'} removed=${record.removedAt ?? 'n/a'}`
1232
+ ].join('\n');
1233
+ }
1234
+ function renderWorktreeLifecycleInspection(inspection) {
1235
+ const lines = [`Worktree lifecycle ${inspection.valid ? 'valid' : 'invalid'} for ${inspection.runId}`];
1236
+ lines.push(`records=${inspection.records.length}`);
1237
+ for (const record of inspection.records) {
1238
+ lines.push(`- ${record.worktreeId} ${record.status} task=${record.taskId} path=${record.worktreePath} dirty=${record.dirty}`);
1239
+ }
1240
+ if (inspection.issues.length > 0) {
1241
+ lines.push('issues');
1242
+ for (const issue of inspection.issues) {
1243
+ lines.push(`- ${issue.field}: ${issue.message}`);
1244
+ lines.push(` recommendation: ${issue.recommendation}`);
1245
+ }
1246
+ }
1247
+ return lines.join('\n');
1248
+ }
1249
+ function renderTaskGraphPlan(graph) {
1250
+ const lines = [`Task graph ${graph.valid ? 'valid' : 'blocked'} for ${graph.branch}`];
1251
+ lines.push(`version=${graph.version}`);
1252
+ lines.push(`tasks=${graph.summary.tasks} dependencies=${graph.summary.dependencies} file_overlaps=${graph.summary.fileOverlaps}`);
1253
+ lines.push(`high_risk_tasks=${graph.summary.highRiskTasks.length > 0 ? graph.summary.highRiskTasks.join(',') : 'none'}`);
1254
+ lines.push(`validation=${graph.summary.validationCommands.length > 0 ? graph.summary.validationCommands.join(' | ') : 'none'}`);
1255
+ lines.push('nodes');
1256
+ for (const node of graph.nodes) {
1257
+ lines.push(`- ${node.taskId} status=${node.status} wave=${node.wave ?? 'n/a'} deps=${node.dependsOn.join(',') || 'none'} files=${node.affectedFiles.length}`);
1258
+ }
1259
+ if (graph.dependencyEdges.length > 0) {
1260
+ lines.push('dependency_edges');
1261
+ for (const edge of graph.dependencyEdges) {
1262
+ lines.push(`- ${edge.from} -> ${edge.to}`);
1263
+ }
1264
+ }
1265
+ if (graph.fileOverlapEdges.length > 0) {
1266
+ lines.push('file_overlap_edges');
1267
+ for (const edge of graph.fileOverlapEdges) {
1268
+ lines.push(`- ${edge.from} <-> ${edge.to}: ${edge.files.join(',')}`);
1269
+ }
1270
+ }
1271
+ if (graph.diagnostics.length > 0) {
1272
+ lines.push('diagnostics');
1273
+ for (const diagnostic of graph.diagnostics) {
1274
+ lines.push(`- [${diagnostic.severity}] ${diagnostic.taskId ?? 'document'} ${diagnostic.field}: ${diagnostic.message}`);
1275
+ lines.push(` recommendation: ${diagnostic.recommendation}`);
1276
+ }
1277
+ }
1278
+ return lines.join('\n');
1279
+ }
1280
+ function renderWavePlan(plan) {
1281
+ const lines = [`Wave plan ${plan.valid ? 'valid' : 'blocked'} for ${plan.branch}`];
1282
+ lines.push(`version=${plan.version}`);
1283
+ lines.push(`tasks=${plan.summary.tasks} waves=${plan.summary.waves} planned=${plan.summary.plannedTasks} manual=${plan.summary.manualTasks} blocked=${plan.summary.blockedTasks}`);
1284
+ if (plan.waves.length > 0) {
1285
+ lines.push('waves');
1286
+ for (const wave of plan.waves) {
1287
+ const tasks = wave.tasks.map((task) => `${task.taskId}(${task.isolationMode})`).join(', ');
1288
+ lines.push(`- wave ${wave.index}: ${tasks}`);
1289
+ }
1290
+ }
1291
+ if (plan.manualGates.length > 0) {
1292
+ lines.push('manual_gates');
1293
+ for (const gate of plan.manualGates) {
1294
+ lines.push(`- ${gate.taskId}: ${gate.reasons.join(' | ')}`);
1295
+ }
1296
+ }
1297
+ if (plan.blockedTasks.length > 0) {
1298
+ lines.push('blocked_tasks');
1299
+ for (const gate of plan.blockedTasks) {
1300
+ lines.push(`- ${gate.taskId}: ${gate.reasons.join(' | ')}`);
1301
+ }
1302
+ }
1303
+ if (plan.diagnostics.length > 0) {
1304
+ lines.push('diagnostics');
1305
+ for (const diagnostic of plan.diagnostics) {
1306
+ lines.push(`- [${diagnostic.severity}] ${diagnostic.taskId ?? 'document'} ${diagnostic.field}: ${diagnostic.message}`);
1307
+ lines.push(` recommendation: ${diagnostic.recommendation}`);
1308
+ }
1309
+ }
1310
+ return lines.join('\n');
1311
+ }
1312
+ function readOption(args, name) {
1313
+ const index = args.indexOf(name);
1314
+ if (index < 0) {
1315
+ return null;
1316
+ }
1317
+ return args[index + 1] ?? null;
1318
+ }
1319
+ function readWaveExecutorStrategy(args, name) {
1320
+ const value = readOption(args, name) ?? 'fast-stop';
1321
+ return value === 'fast-stop' || value === 'safe-continue' ? value : null;
1322
+ }
1323
+ function readRunStatus(args, name) {
1324
+ const value = readOption(args, name);
1325
+ return value === 'created' || value === 'running' || value === 'completed' || value === 'blocked' || value === 'failed' || value === 'archived' ? value : null;
1326
+ }
1327
+ function readGovernancePolicyOperation(value) {
1328
+ return value === 'background_executor' || value === 'wave_executor' || value === 'sync_back_apply' || value === 'destructive_git' || value === 'external_interaction' || value === 'cleanup' ? value : null;
1329
+ }
1330
+ function readRepeatedOption(args, name) {
1331
+ const values = [];
1332
+ for (let index = 0; index < args.length; index += 1) {
1333
+ if (args[index] === name && args[index + 1]) {
1334
+ values.push(args[index + 1]);
1335
+ }
1336
+ }
1337
+ return values;
1338
+ }
1339
+ function readTaskArtifactOptions(args) {
1340
+ const artifacts = {};
1341
+ for (let index = 0; index < args.length; index += 1) {
1342
+ if (args[index] !== '--artifact') {
1343
+ continue;
1344
+ }
1345
+ const value = args[index + 1];
1346
+ const separator = value?.indexOf(':') ?? -1;
1347
+ if (!value || separator <= 0) {
1348
+ continue;
1349
+ }
1350
+ artifacts[value.slice(0, separator)] = value.slice(separator + 1);
1351
+ }
1352
+ return artifacts;
1353
+ }
1354
+ function readSddResultStatus(args, name) {
1355
+ const value = readOption(args, name);
1356
+ return value === 'PASS' || value === 'PASS_WITH_GAPS' || value === 'FAIL' || value === 'BLOCKED' || value === 'TIMED_OUT' || value === 'CANCELLED' ? value : null;
1357
+ }
1358
+ function renderArtifactValidationReport(artifactPath, report, expectedTask, expectedAgent) {
1359
+ if (report.valid) {
1360
+ return `Artifact valid: ${artifactPath}`;
1361
+ }
1362
+ const lines = [`Artifact invalid: ${artifactPath}`];
1363
+ for (const issue of report.issues) {
1364
+ lines.push(`- ${issue.field}: ${issue.message}`);
1365
+ lines.push(` recommendation: ${issue.recommendation}`);
1366
+ }
1367
+ if (expectedTask && expectedAgent) {
1368
+ lines.push(`next sdd artifact template ${artifactPath} --task ${expectedTask} --agent ${expectedAgent}`);
1369
+ }
1370
+ return lines.join('\n');
1371
+ }
1372
+ function readAiToolSelection(args, allowNone) {
1373
+ const value = readOption(args, '--ai') ?? 'auto';
1374
+ if (value === 'auto' || value === 'claude-code' || (allowNone && value === 'none')) {
1375
+ return value;
1376
+ }
1377
+ throw new Error(`Unsupported --ai value: ${value}`);
1378
+ }
1379
+ function readLifecycleSignalOptions(args) {
1380
+ const directSafe = args.includes('--direct-safe');
1381
+ const riskTags = readRepeatedOptions(args, '--risk');
1382
+ const contracts = readRepeatedOptions(args, '--contract');
1383
+ const permissions = readRepeatedOptions(args, '--permission');
1384
+ return {
1385
+ intent_clarity: directSafe ? 'high' : readSignalClarity(args, '--intent') ?? 'medium',
1386
+ acceptance_clarity: directSafe ? 'high' : readSignalClarity(args, '--acceptance') ?? 'medium',
1387
+ estimated_change_size: directSafe ? 'tiny' : readEstimatedChangeSize(args, '--size') ?? 'small',
1388
+ task_count_estimate: Number(readOption(args, '--tasks') ?? (directSafe ? '1' : '1')),
1389
+ file_count_estimate: Number(readOption(args, '--files') ?? (directSafe ? '1' : '1')),
1390
+ affected_layers: readRepeatedOptions(args, '--layer'),
1391
+ affected_contracts: contracts,
1392
+ dependency_fanout: readDependencyFanout(args, '--fanout') ?? 'local',
1393
+ impact_confidence: directSafe ? 'high' : readImpactConfidence(args, '--impact-confidence') ?? 'medium',
1394
+ risk_tags: riskTags,
1395
+ reversibility: directSafe ? 'reversible' : readReversibility(args, '--reversibility') ?? 'unknown',
1396
+ validation_clarity: directSafe ? 'clear' : readValidationClarity(args, '--validation') ?? 'partial',
1397
+ validation_available: directSafe || args.includes('--validation-available'),
1398
+ validation_cost: directSafe ? 'cheap' : readValidationCost(args, '--validation-cost') ?? 'unknown',
1399
+ policy_hits: readRepeatedOptions(args, '--policy'),
1400
+ permission_required: permissions,
1401
+ requires_agents: args.includes('--requires-agents'),
1402
+ handoff_count: Number(readOption(args, '--handoffs') ?? '0'),
1403
+ artifact_dependency: args.includes('--artifact-dependency'),
1404
+ runtime_recovery_need: args.includes('--runtime-recovery'),
1405
+ orchestration_uncertainty: directSafe ? 'low' : readOrchestrationUncertainty(args, '--orchestration') ?? 'medium',
1406
+ human_checkpoint_required: args.includes('--checkpoint'),
1407
+ approval_reason: readRepeatedOptions(args, '--approval-reason'),
1408
+ source_artifacts: readRepeatedOptions(args, '--source-artifact'),
1409
+ can_scout_impact: !args.includes('--cannot-scout-impact'),
1410
+ architecture_decision_required: args.includes('--architecture'),
1411
+ external_unknown: args.includes('--external-unknown')
1412
+ };
1413
+ }
1414
+ function readRepeatedOptions(args, name) {
1415
+ const values = [];
1416
+ for (let index = 0; index < args.length; index += 1) {
1417
+ if (args[index] === name && args[index + 1]) {
1418
+ values.push(args[index + 1]);
1419
+ index += 1;
1420
+ }
1421
+ }
1422
+ return values;
1423
+ }
1424
+ function readSignalClarity(args, name) {
1425
+ const value = readOption(args, name);
1426
+ return value === 'high' || value === 'medium' || value === 'low' ? value : null;
1427
+ }
1428
+ function readEstimatedChangeSize(args, name) {
1429
+ const value = readOption(args, name);
1430
+ return value === 'tiny' || value === 'small' || value === 'medium' || value === 'large' ? value : null;
1431
+ }
1432
+ function readImpactConfidence(args, name) {
1433
+ return readSignalClarity(args, name);
1434
+ }
1435
+ function readValidationClarity(args, name) {
1436
+ const value = readOption(args, name);
1437
+ return value === 'clear' || value === 'partial' || value === 'unclear' ? value : null;
1438
+ }
1439
+ function readValidationCost(args, name) {
1440
+ const value = readOption(args, name);
1441
+ return value === 'cheap' || value === 'moderate' || value === 'expensive' || value === 'unknown' ? value : null;
1442
+ }
1443
+ function readDependencyFanout(args, name) {
1444
+ const value = readOption(args, name);
1445
+ return value === 'none' || value === 'local' || value === 'multi_component' || value === 'unknown' ? value : null;
1446
+ }
1447
+ function readReversibility(args, name) {
1448
+ const value = readOption(args, name);
1449
+ return value === 'reversible' || value === 'irreversible' || value === 'unknown' ? value : null;
1450
+ }
1451
+ function readOrchestrationUncertainty(args, name) {
1452
+ const value = readOption(args, name);
1453
+ return value === 'low' || value === 'medium' || value === 'high' ? value : null;
1454
+ }
1455
+ function taskFormatText() {
1456
+ return `# Canonical sdd-task format
1457
+
1458
+ \`\`\`sdd-task
1459
+ id: T1
1460
+ status: pending
1461
+ wave: 1
1462
+ depends_on: []
1463
+ affected_files:
1464
+ - path/to/file
1465
+ validation:
1466
+ - command string
1467
+ risk: []
1468
+ \`\`\`
1469
+
1470
+ Companion sections such as #### Boundary, #### Acceptance, and #### Implementation Notes must stay outside the fenced sdd-task metadata block. Keep only metadata inside the fence.
1471
+
1472
+ #### Boundary
1473
+
1474
+ Allowed implementation scope. Explicitly list forbidden scope when relevant.
1475
+
1476
+ #### Acceptance
1477
+
1478
+ - Verifiable acceptance item.
1479
+
1480
+ #### Implementation Notes
1481
+
1482
+ Reserved for approved sync-back notes and artifact links.
1483
+ `;
1484
+ }
1485
+ main(process.argv.slice(2))
1486
+ .then((result) => {
1487
+ if (result.output) {
1488
+ console.log(result.output);
1489
+ }
1490
+ if (result.error) {
1491
+ console.error(result.error);
1492
+ }
1493
+ process.exitCode = result.exitCode;
1494
+ })
1495
+ .catch((error) => {
1496
+ console.error(error instanceof Error ? error.message : String(error));
1497
+ process.exitCode = 1;
1498
+ });
1499
+ //# sourceMappingURL=main.js.map