@jterrats/open-orchestra 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.
Files changed (93) hide show
  1. package/AGENTS.md +151 -0
  2. package/CLAUDE.md +157 -0
  3. package/README.md +60 -0
  4. package/bin/orchestra.js +8 -0
  5. package/dist/args.d.ts +3 -0
  6. package/dist/args.js +30 -0
  7. package/dist/args.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +190 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands.d.ts +44 -0
  12. package/dist/commands.js +883 -0
  13. package/dist/commands.js.map +1 -0
  14. package/dist/constants.d.ts +15 -0
  15. package/dist/constants.js +69 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/defaults.d.ts +72 -0
  18. package/dist/defaults.js +694 -0
  19. package/dist/defaults.js.map +1 -0
  20. package/dist/fs-utils.d.ts +8 -0
  21. package/dist/fs-utils.js +35 -0
  22. package/dist/fs-utils.js.map +1 -0
  23. package/dist/model-providers.d.ts +19 -0
  24. package/dist/model-providers.js +78 -0
  25. package/dist/model-providers.js.map +1 -0
  26. package/dist/types.d.ts +550 -0
  27. package/dist/types.js +2 -0
  28. package/dist/types.js.map +1 -0
  29. package/dist/validation.d.ts +10 -0
  30. package/dist/validation.js +163 -0
  31. package/dist/validation.js.map +1 -0
  32. package/dist/web-api.d.ts +16 -0
  33. package/dist/web-api.js +220 -0
  34. package/dist/web-api.js.map +1 -0
  35. package/dist/web-chart-contracts.d.ts +13 -0
  36. package/dist/web-chart-contracts.js +13 -0
  37. package/dist/web-chart-contracts.js.map +1 -0
  38. package/dist/web-console.d.ts +1 -0
  39. package/dist/web-console.js +232 -0
  40. package/dist/web-console.js.map +1 -0
  41. package/dist/web-evidence.d.ts +25 -0
  42. package/dist/web-evidence.js +67 -0
  43. package/dist/web-evidence.js.map +1 -0
  44. package/dist/web-playwright.d.ts +3 -0
  45. package/dist/web-playwright.js +14 -0
  46. package/dist/web-playwright.js.map +1 -0
  47. package/dist/web-roles.d.ts +33 -0
  48. package/dist/web-roles.js +70 -0
  49. package/dist/web-roles.js.map +1 -0
  50. package/dist/workflow-gates.d.ts +7 -0
  51. package/dist/workflow-gates.js +291 -0
  52. package/dist/workflow-gates.js.map +1 -0
  53. package/dist/workflow-services.d.ts +56 -0
  54. package/dist/workflow-services.js +1240 -0
  55. package/dist/workflow-services.js.map +1 -0
  56. package/dist/workspace-validator.d.ts +6 -0
  57. package/dist/workspace-validator.js +189 -0
  58. package/dist/workspace-validator.js.map +1 -0
  59. package/dist/workspace.d.ts +10 -0
  60. package/dist/workspace.js +72 -0
  61. package/dist/workspace.js.map +1 -0
  62. package/docs/multi-agent-orchestrator-backlog.md +445 -0
  63. package/docs/multi-agent-orchestrator-sprint-1.md +433 -0
  64. package/docs/orchestra-mvp.md +176 -0
  65. package/package.json +63 -0
  66. package/rules/agent-collaboration.mdc +58 -0
  67. package/rules/agent-roles.mdc +105 -0
  68. package/rules/ai-assisted-development.mdc +31 -0
  69. package/rules/api-design.mdc +31 -0
  70. package/rules/architecture-decisions.mdc +27 -0
  71. package/rules/code-review-engineering.mdc +34 -0
  72. package/rules/concurrency-async.mdc +32 -0
  73. package/rules/configuration-management.mdc +31 -0
  74. package/rules/data-modeling-domain.mdc +31 -0
  75. package/rules/delivery-quality-gates.mdc +40 -0
  76. package/rules/dependency-management.mdc +31 -0
  77. package/rules/devops-tooling.mdc +55 -0
  78. package/rules/documentation-standards.mdc +26 -0
  79. package/rules/dry-clean-code.mdc +30 -0
  80. package/rules/error-handling.mdc +28 -0
  81. package/rules/frontend-engineering.mdc +32 -0
  82. package/rules/git-discipline.mdc +39 -0
  83. package/rules/infra-data-encryption.mdc +81 -0
  84. package/rules/performance-reliability.mdc +32 -0
  85. package/rules/readiness-done.mdc +32 -0
  86. package/rules/release-rollback.mdc +32 -0
  87. package/rules/rule-composition.mdc +28 -0
  88. package/rules/security-guardrails.mdc +37 -0
  89. package/rules/solid-architecture.mdc +32 -0
  90. package/rules/static-analysis-githooks.mdc +32 -0
  91. package/rules/testing-discipline.mdc +42 -0
  92. package/rules/ux-ui-product-experience.mdc +51 -0
  93. package/rules/work-intake-sequencing.mdc +39 -0
@@ -0,0 +1,883 @@
1
+ import { initWorkspace } from "./workspace.js";
2
+ import { requireArg } from "./args.js";
3
+ import { addEvidence, addPlaywrightEvidence, addTask, approveApproval, checkUsageBudget, checkReadiness, checkTaskDependencies, claimLock, completeWithProviderFallback, createHandoff, evaluateWorkflowGate, executeNextReadyTask, executePlanWithBudgetPreflight, executeReadyTaskBatch, generateExecutionPlan, generatePlaywrightTestPlan, generatePullRequestSummary, generateTaskGraphPlan, getUsageReport, getWorkflowStatus, getWorkflowSummary, getTaskContext, getWorkflowConfig, listEvidence, listConfiguredModelProviders, listDecisions, listLocks, listApprovals, listReviews, listRoles, listTasks, rejectApproval, recordReview, recordModelProvenance, recordDecision, releaseLock, setRoleModelProvider, showApproval, updateTask, listModelProvenance, } from "./workflow-services.js";
4
+ import { validateWorkspace } from "./workspace-validator.js";
5
+ import { getWebServerAddress, startWebApiServer } from "./web-api.js";
6
+ export async function initCommand(options, io) {
7
+ const base = await initWorkspace({ force: Boolean(options.force) });
8
+ io.log(`Initialized ${base}`);
9
+ io.log("Next: add tasks to .agent-workflow/tasks.json and run orchestra status");
10
+ }
11
+ export async function statusCommand(options, io) {
12
+ const status = await getWorkflowStatus();
13
+ if (options.json) {
14
+ io.log(JSON.stringify(status, null, 2));
15
+ return;
16
+ }
17
+ io.log(`Tasks: ${status.tasks.total}`);
18
+ for (const [name, count] of Object.entries(status.tasks.byStatus)) {
19
+ io.log(` ${name}: ${count}`);
20
+ }
21
+ io.log(`Locks: ${status.locks.total}`);
22
+ if (status.tasks.blocked.length > 0) {
23
+ io.log("Blocked:");
24
+ for (const task of status.tasks.blocked) {
25
+ io.log(` ${task.id}: ${task.reason}`);
26
+ }
27
+ }
28
+ }
29
+ export async function validateCommand(options, io) {
30
+ const report = await validateWorkspace();
31
+ if (options.json) {
32
+ io.log(JSON.stringify(report, null, 2));
33
+ }
34
+ else {
35
+ io.log(report.valid ? "Workspace valid" : "Workspace invalid");
36
+ for (const error of report.errors) {
37
+ io.log(`ERROR: ${error}`);
38
+ }
39
+ for (const warning of report.warnings) {
40
+ io.log(`WARN: ${warning}`);
41
+ }
42
+ }
43
+ if (!report.valid) {
44
+ throw new Error("workspace validation failed");
45
+ }
46
+ }
47
+ export async function webCommand(options, io) {
48
+ const serverOptions = {};
49
+ const host = stringOption(options.host);
50
+ if (host) {
51
+ serverOptions.host = host;
52
+ }
53
+ if (options.port) {
54
+ serverOptions.port = numberOption(options.port, 3717);
55
+ }
56
+ const server = await startWebApiServer(serverOptions);
57
+ io.log(`Open Orchestra web API listening at ${getWebServerAddress(server)}`);
58
+ }
59
+ export async function taskAddCommand(options, io) {
60
+ const input = removeUndefined({
61
+ id: requireArg(options, "id"),
62
+ title: requireArg(options, "title"),
63
+ ownerRole: requireArg(options, "owner"),
64
+ dependencies: parseCsv(options.depends),
65
+ backlogItem: stringOption(options.backlog),
66
+ goal: stringOption(options.goal),
67
+ scope: stringOption(options.scope),
68
+ acceptanceCriteria: parseCsv(options.acceptance),
69
+ assumptions: parseCsv(options.assumptions),
70
+ risks: parseCsv(options.risks),
71
+ paths: parseCsv(options.paths),
72
+ testStrategy: stringOption(options["test-strategy"]),
73
+ architectureApproval: parseArchitectureApproval(options),
74
+ qaGate: parseQaGate(options),
75
+ riskGate: parseRiskGate(options),
76
+ });
77
+ const task = await addTask(input);
78
+ io.log(`Added task ${task.id}`);
79
+ }
80
+ export async function taskListCommand(options, io) {
81
+ const tasks = await listTasks();
82
+ if (options.json) {
83
+ io.log(JSON.stringify(tasks, null, 2));
84
+ return;
85
+ }
86
+ if (tasks.length === 0) {
87
+ io.log("No tasks");
88
+ return;
89
+ }
90
+ for (const task of tasks) {
91
+ io.log(`${task.id} [${task.status}] ${task.title} (${task.ownerRole})`);
92
+ }
93
+ }
94
+ export async function rolesListCommand(options, io) {
95
+ const roles = await listRoles();
96
+ if (options.json) {
97
+ io.log(JSON.stringify(roles, null, 2));
98
+ return;
99
+ }
100
+ if (roles.length === 0) {
101
+ io.log("No roles");
102
+ return;
103
+ }
104
+ for (const role of roles) {
105
+ const gates = role.gateParticipation?.join(", ") || "none";
106
+ io.log(`${role.id} - ${role.name} (gates: ${gates})`);
107
+ }
108
+ }
109
+ export async function taskUpdateCommand(options, io) {
110
+ const id = requireArg(options, "id");
111
+ await updateTask(removeUndefined({
112
+ id,
113
+ status: stringOption(options.status),
114
+ blockedReason: stringOption(options["blocked-reason"]),
115
+ }));
116
+ io.log(`Updated task ${id}`);
117
+ }
118
+ export async function taskDepsCommand(options, io) {
119
+ const report = await checkTaskDependencies(requireArg(options, "id"));
120
+ if (options.json) {
121
+ io.log(JSON.stringify(report, null, 2));
122
+ return;
123
+ }
124
+ io.log(renderDependencyReport(report));
125
+ }
126
+ export async function graphPlanCommand(options, io) {
127
+ const plan = await generateTaskGraphPlan();
128
+ if (options.json) {
129
+ io.log(JSON.stringify(plan, null, 2));
130
+ return;
131
+ }
132
+ io.log(renderTaskGraphPlan(plan));
133
+ }
134
+ export async function graphRunNextCommand(options, io) {
135
+ const run = await executeNextReadyTask();
136
+ if (options.json) {
137
+ io.log(JSON.stringify(run, null, 2));
138
+ return;
139
+ }
140
+ io.log(renderExecutionRunMarkdown(run));
141
+ }
142
+ export async function graphRunReadyCommand(options, io) {
143
+ const batch = await executeReadyTaskBatch();
144
+ if (options.json) {
145
+ io.log(JSON.stringify(batch, null, 2));
146
+ return;
147
+ }
148
+ io.log(renderTaskGraphBatchRun(batch));
149
+ if (batch.failure) {
150
+ throw new Error(`graph batch failed: ${batch.failure.taskId}`);
151
+ }
152
+ }
153
+ export async function lockListCommand(options, io) {
154
+ const locks = await listLocks();
155
+ if (options.json) {
156
+ io.log(JSON.stringify(locks, null, 2));
157
+ return;
158
+ }
159
+ if (locks.length === 0) {
160
+ io.log("No locks");
161
+ return;
162
+ }
163
+ for (const lock of locks) {
164
+ io.log(`${lock.id} ${lock.path} (${lock.taskId}, ${lock.ownerRole})`);
165
+ }
166
+ }
167
+ export async function lockClaimCommand(options, io) {
168
+ const lock = await claimLock(removeUndefined({
169
+ id: stringOption(options.id) ?? `lock-${Date.now()}`,
170
+ taskId: requireArg(options, "task"),
171
+ ownerRole: requireArg(options, "role"),
172
+ path: requireArg(options, "path"),
173
+ reason: requireArg(options, "reason"),
174
+ expiresAt: stringOption(options.expires),
175
+ }));
176
+ io.log(`Claimed lock ${lock.id}`);
177
+ }
178
+ export async function lockReleaseCommand(options, io) {
179
+ const id = requireArg(options, "id");
180
+ await releaseLock(id);
181
+ io.log(`Released lock ${id}`);
182
+ }
183
+ export async function readinessCommand(options, io) {
184
+ const taskId = requireArg(options, "task");
185
+ const { task, report } = await checkReadiness(taskId);
186
+ if (options.markdown) {
187
+ io.log(renderReadinessMarkdown(task, report));
188
+ return;
189
+ }
190
+ io.log(JSON.stringify(report, null, 2));
191
+ }
192
+ export async function gateCommand(options, io) {
193
+ const gateId = requireArg(options, "gate");
194
+ const taskId = requireArg(options, "task");
195
+ const result = await evaluateWorkflowGate(gateId, taskId);
196
+ if (options.markdown) {
197
+ io.log(renderGateMarkdown(result));
198
+ return;
199
+ }
200
+ io.log(JSON.stringify(result, null, 2));
201
+ }
202
+ export async function handoffCommand(options, io) {
203
+ const { artifact } = await createHandoff(removeUndefined({
204
+ task: requireArg(options, "task"),
205
+ from: requireArg(options, "from"),
206
+ to: requireArg(options, "to"),
207
+ changed: requireArg(options, "changed"),
208
+ behavior: requireArg(options, "behavior"),
209
+ tests: requireArg(options, "tests"),
210
+ commands: requireArg(options, "commands"),
211
+ status: stringOption(options.status),
212
+ gaps: stringOption(options.gaps),
213
+ risks: stringOption(options.risks),
214
+ playwright: stringOption(options.playwright),
215
+ }));
216
+ io.log(`Created ${artifact}`);
217
+ }
218
+ export async function reviewCommand(options, io) {
219
+ const input = {
220
+ task: requireArg(options, "task"),
221
+ role: requireArg(options, "role"),
222
+ result: requireArg(options, "result"),
223
+ severity: stringOption(options.severity) ?? "info",
224
+ findings: requireArg(options, "findings"),
225
+ recommendation: requireArg(options, "recommendation"),
226
+ };
227
+ const { artifact } = await recordReview(input);
228
+ io.log(`Created ${artifact}`);
229
+ }
230
+ export async function decisionAddCommand(options, io) {
231
+ const decision = await recordDecision({
232
+ task: requireArg(options, "task"),
233
+ title: requireArg(options, "title"),
234
+ context: requireArg(options, "context"),
235
+ decision: requireArg(options, "decision"),
236
+ consequences: requireArg(options, "consequences"),
237
+ status: (stringOption(options.status) ?? "accepted"),
238
+ owner: requireArg(options, "owner"),
239
+ });
240
+ io.log(`Created ${decision.artifact}`);
241
+ }
242
+ export async function decisionListCommand(options, io) {
243
+ const decisions = await listDecisions(stringOption(options.task));
244
+ if (options.json) {
245
+ io.log(JSON.stringify(decisions, null, 2));
246
+ return;
247
+ }
248
+ if (decisions.length === 0) {
249
+ io.log("No decisions");
250
+ return;
251
+ }
252
+ for (const decision of decisions) {
253
+ io.log(`${decision.taskId ?? "no-task"} ${decision.actor}: ${decision.summary}`);
254
+ }
255
+ }
256
+ export async function reviewListCommand(options, io) {
257
+ const reviews = await listReviews(stringOption(options.task));
258
+ if (options.json) {
259
+ io.log(JSON.stringify(reviews, null, 2));
260
+ return;
261
+ }
262
+ if (reviews.length === 0) {
263
+ io.log("No reviews");
264
+ return;
265
+ }
266
+ for (const review of reviews) {
267
+ const result = String(review.metadata.result ?? "unknown");
268
+ const severity = String(review.metadata.severity ?? "unknown");
269
+ io.log(`${review.taskId ?? "no-task"} ${review.actor} ${result} (${severity})`);
270
+ }
271
+ }
272
+ export async function evidenceCommand(options, io) {
273
+ const input = removeUndefined({
274
+ task: requireArg(options, "task"),
275
+ role: requireArg(options, "role"),
276
+ type: requireArg(options, "type"),
277
+ summary: requireArg(options, "summary"),
278
+ path: options.path,
279
+ command: options.command,
280
+ exitCode: options["exit-code"],
281
+ });
282
+ const { artifact } = await addEvidence(input);
283
+ io.log(`Created ${artifact}`);
284
+ }
285
+ export async function evidenceListCommand(options, io) {
286
+ const evidence = await listEvidence(stringOption(options.task));
287
+ if (options.json) {
288
+ io.log(JSON.stringify(evidence, null, 2));
289
+ return;
290
+ }
291
+ if (evidence.length === 0) {
292
+ io.log("No evidence");
293
+ return;
294
+ }
295
+ for (const item of evidence) {
296
+ const type = String(item.metadata.type ?? "unknown");
297
+ io.log(`${item.taskId ?? "no-task"} ${item.actor} ${type}: ${item.summary}`);
298
+ }
299
+ }
300
+ export async function summaryCommand(options, io) {
301
+ const summary = await getWorkflowSummary();
302
+ if (options.json) {
303
+ io.log(JSON.stringify(summary, null, 2));
304
+ return;
305
+ }
306
+ io.log(renderSummaryMarkdown(summary));
307
+ }
308
+ export async function usageCommand(options, io) {
309
+ const report = await getUsageReport(stringOption(options.task));
310
+ if (options.json) {
311
+ io.log(JSON.stringify(report, null, 2));
312
+ return;
313
+ }
314
+ io.log(renderUsageReport(report));
315
+ }
316
+ export async function budgetCheckCommand(options, io) {
317
+ const result = await checkUsageBudget(stringOption(options.task));
318
+ if (options.json) {
319
+ io.log(JSON.stringify(result, null, 2));
320
+ }
321
+ else {
322
+ io.log(renderBudgetCheck(result));
323
+ }
324
+ if (!result.passed) {
325
+ throw new Error("budget check failed");
326
+ }
327
+ }
328
+ export async function approvalsListCommand(options, io) {
329
+ const approvals = await listApprovals(stringOption(options.task));
330
+ if (options.json) {
331
+ io.log(JSON.stringify(approvals, null, 2));
332
+ return;
333
+ }
334
+ if (approvals.length === 0) {
335
+ io.log("No approvals");
336
+ return;
337
+ }
338
+ for (const approval of approvals) {
339
+ io.log(`${approval.id} [${approval.status}] ${approval.taskId ?? "no-task"} ${approval.artifact}`);
340
+ }
341
+ }
342
+ export async function approvalsShowCommand(options, io) {
343
+ const approval = await showApproval(requireArg(options, "id"));
344
+ if (options.json) {
345
+ io.log(JSON.stringify(approval, null, 2));
346
+ return;
347
+ }
348
+ io.log(approval.content);
349
+ }
350
+ export async function approvalsApproveCommand(options, io) {
351
+ const approval = await approveApproval(parseApprovalDecision(options));
352
+ io.log(`Approved ${approval.id}`);
353
+ }
354
+ export async function approvalsRejectCommand(options, io) {
355
+ const approval = await rejectApproval(parseApprovalDecision(options));
356
+ io.log(`Rejected ${approval.id}`);
357
+ }
358
+ export async function prSummaryCommand(options, io) {
359
+ const summary = await generatePullRequestSummary(requireArg(options, "task"));
360
+ if (options.json) {
361
+ io.log(JSON.stringify(summary, null, 2));
362
+ return;
363
+ }
364
+ io.log(renderPullRequestSummaryMarkdown(summary));
365
+ }
366
+ export async function contextCommand(options, io) {
367
+ const context = await getTaskContext(requireArg(options, "task"));
368
+ if (options.json) {
369
+ io.log(JSON.stringify(context, null, 2));
370
+ return;
371
+ }
372
+ io.log(renderTaskContextMarkdown(context));
373
+ }
374
+ export async function planCommand(options, io) {
375
+ const plan = await generateExecutionPlan(requireArg(options, "task"));
376
+ if (options.json) {
377
+ io.log(JSON.stringify(plan, null, 2));
378
+ return;
379
+ }
380
+ io.log(renderExecutionPlanMarkdown(plan));
381
+ }
382
+ export async function runCommand(options, io) {
383
+ const taskId = requireArg(options, "task");
384
+ const run = await executePlanWithBudgetPreflight(taskId, process.cwd(), parseBudgetEscalationDecision(options));
385
+ if (options.json) {
386
+ io.log(JSON.stringify(run, null, 2));
387
+ return;
388
+ }
389
+ io.log(renderExecutionRunMarkdown(run));
390
+ }
391
+ export async function playwrightPlanCommand(options, io) {
392
+ const plan = await generatePlaywrightTestPlan(requireArg(options, "task"));
393
+ if (options.json) {
394
+ io.log(JSON.stringify(plan, null, 2));
395
+ return;
396
+ }
397
+ io.log(renderPlaywrightPlanMarkdown(plan));
398
+ }
399
+ export async function playwrightEvidenceCommand(options, io) {
400
+ const { artifact } = await addPlaywrightEvidence(removeUndefined({
401
+ task: requireArg(options, "task"),
402
+ kind: requireArg(options, "kind"),
403
+ path: requireArg(options, "path"),
404
+ summary: requireArg(options, "summary"),
405
+ runId: stringOption(options["run-id"]),
406
+ }));
407
+ io.log(`Created ${artifact}`);
408
+ }
409
+ export async function configShowCommand(options, io) {
410
+ const config = await getWorkflowConfig();
411
+ if (options.json) {
412
+ io.log(JSON.stringify(config, null, 2));
413
+ return;
414
+ }
415
+ io.log(`Tools: ${Object.keys(config.tools ?? {}).length}`);
416
+ io.log(`Role routes: ${Object.keys(config.providers?.byRole ?? {}).length}`);
417
+ }
418
+ export async function modelProvidersCommand(options, io) {
419
+ const providers = await listConfiguredModelProviders();
420
+ if (options.json) {
421
+ io.log(JSON.stringify(providers, null, 2));
422
+ return;
423
+ }
424
+ if (providers.length === 0) {
425
+ io.log("No configured model providers");
426
+ return;
427
+ }
428
+ for (const provider of providers) {
429
+ io.log(`${provider.scope}: ${provider.provider}/${provider.model} (${provider.timeoutMs}ms)`);
430
+ }
431
+ }
432
+ export async function modelSetRoleCommand(options, io) {
433
+ const role = requireArg(options, "role");
434
+ const routing = parseProviderRouting(options);
435
+ await setRoleModelProvider(role, routing);
436
+ io.log(`Configured model provider for ${role}`);
437
+ }
438
+ export async function modelProvenanceAddCommand(options, io) {
439
+ const record = await recordModelProvenance({
440
+ task: requireArg(options, "task"),
441
+ role: requireArg(options, "role"),
442
+ provider: requireArg(options, "provider"),
443
+ model: requireArg(options, "model"),
444
+ promptId: requireArg(options, "prompt-id"),
445
+ responseId: requireArg(options, "response-id"),
446
+ inputTokens: numberOption(options["input-tokens"], 0),
447
+ outputTokens: numberOption(options["output-tokens"], 0),
448
+ estimatedCostUsd: numberOption(options["estimated-cost-usd"], 0),
449
+ finishReason: requireArg(options, "finish-reason"),
450
+ });
451
+ io.log(`Recorded model provenance ${record.responseId}`);
452
+ }
453
+ export async function modelProvenanceListCommand(options, io) {
454
+ const records = await listModelProvenance(stringOption(options.task));
455
+ if (options.json) {
456
+ io.log(JSON.stringify(records, null, 2));
457
+ return;
458
+ }
459
+ if (records.length === 0) {
460
+ io.log("No model provenance");
461
+ return;
462
+ }
463
+ for (const record of records) {
464
+ io.log(`${record.task} ${record.role} ${record.provider}/${record.model} ${record.responseId}`);
465
+ }
466
+ }
467
+ export async function modelCompleteFakeCommand(options, io) {
468
+ const result = await completeWithProviderFallback({
469
+ provider: requireArg(options, "provider"),
470
+ model: requireArg(options, "model"),
471
+ fallbacks: parseCsv(options.fallbacks),
472
+ maxTokens: 0,
473
+ maxCostUsd: 0,
474
+ timeoutMs: 30000,
475
+ retries: 0,
476
+ requiredCapabilities: [],
477
+ }, requireArg(options, "prompt"), removeUndefined({
478
+ failingProviders: parseCsv(options["fail-provider"]),
479
+ taskId: stringOption(options.task),
480
+ role: stringOption(options.role) ?? "parent",
481
+ }));
482
+ if (options.json) {
483
+ io.log(JSON.stringify(result, null, 2));
484
+ return;
485
+ }
486
+ io.log(`Fake completion used ${result.provider}/${result.model} fallback=${String(result.fallbackUsed)}`);
487
+ }
488
+ function parseCsv(value) {
489
+ if (typeof value !== "string" || value.trim() === "") {
490
+ return [];
491
+ }
492
+ return value
493
+ .split(",")
494
+ .map((item) => item.trim())
495
+ .filter(Boolean);
496
+ }
497
+ function stringOption(value) {
498
+ return typeof value === "string" && value.trim() !== "" ? value : undefined;
499
+ }
500
+ function removeUndefined(value) {
501
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
502
+ }
503
+ function parseArchitectureApproval(options) {
504
+ const approval = removeUndefined({
505
+ proposal: stringOption(options["architecture-proposal"]),
506
+ userApproved: options["user-approved"] ? true : undefined,
507
+ approvedBy: stringOption(options["architecture-approved-by"]),
508
+ approvedAt: stringOption(options["architecture-approved-at"]),
509
+ });
510
+ return Object.keys(approval).length > 0 ? approval : undefined;
511
+ }
512
+ function parseQaGate(options) {
513
+ const qaGate = removeUndefined({
514
+ plan: stringOption(options["qa-plan"]),
515
+ executionStatus: stringOption(options["qa-execution-status"]),
516
+ deferredRationale: stringOption(options["qa-deferred-rationale"]),
517
+ deferredOwner: stringOption(options["qa-deferred-owner"]),
518
+ });
519
+ return Object.keys(qaGate).length > 0 ? qaGate : undefined;
520
+ }
521
+ function parseRiskGate(options) {
522
+ const impactAreas = parseCsv(options["risk-areas"]);
523
+ const riskGate = removeUndefined({
524
+ impactAreas: impactAreas.length > 0 ? impactAreas : undefined,
525
+ acceptance: parseRiskAcceptance(options),
526
+ });
527
+ return Object.keys(riskGate).length > 0 ? riskGate : undefined;
528
+ }
529
+ function parseRiskAcceptance(options) {
530
+ const acceptance = removeUndefined({
531
+ acceptedBy: stringOption(options["risk-accepted-by"]),
532
+ rationale: stringOption(options["risk-acceptance-rationale"]),
533
+ });
534
+ return Object.keys(acceptance).length > 0 ? acceptance : undefined;
535
+ }
536
+ function parseProviderRouting(options) {
537
+ return {
538
+ provider: requireArg(options, "provider"),
539
+ model: requireArg(options, "model"),
540
+ fallbacks: parseCsv(options.fallbacks),
541
+ maxTokens: numberOption(options["max-tokens"], 0),
542
+ maxCostUsd: numberOption(options["max-cost-usd"], 0),
543
+ timeoutMs: numberOption(options["timeout-ms"], 30000),
544
+ retries: numberOption(options.retries, 0),
545
+ requiredCapabilities: parseCsv(options.capabilities),
546
+ };
547
+ }
548
+ function parseBudgetEscalationDecision(options) {
549
+ if (options["approve-budget-fallback"]) {
550
+ return removeUndefined({
551
+ approved: true,
552
+ approver: stringOption(options.approver),
553
+ rationale: stringOption(options.rationale),
554
+ });
555
+ }
556
+ if (options["reject-budget-fallback"]) {
557
+ return removeUndefined({
558
+ approved: false,
559
+ approver: stringOption(options.approver),
560
+ rationale: stringOption(options.rationale),
561
+ });
562
+ }
563
+ return undefined;
564
+ }
565
+ function parseApprovalDecision(options) {
566
+ return {
567
+ id: requireArg(options, "id"),
568
+ approver: requireArg(options, "approver"),
569
+ rationale: requireArg(options, "rationale"),
570
+ };
571
+ }
572
+ function numberOption(value, fallback) {
573
+ if (typeof value !== "string" || value.trim() === "") {
574
+ return fallback;
575
+ }
576
+ const parsed = Number(value);
577
+ if (!Number.isFinite(parsed)) {
578
+ throw new Error(`invalid number: ${value}`);
579
+ }
580
+ return parsed;
581
+ }
582
+ function renderReadinessMarkdown(task, report) {
583
+ const status = report.isReady ? "passed" : "blocked";
584
+ const missing = report.missing.length > 0
585
+ ? report.missing.map((field) => `- ${field}`).join("\n")
586
+ : "- none";
587
+ return [
588
+ `# Readiness ${task.id}`,
589
+ "",
590
+ `Status: ${status}`,
591
+ "",
592
+ "Missing fields:",
593
+ missing,
594
+ "",
595
+ ].join("\n");
596
+ }
597
+ function renderGateMarkdown(report) {
598
+ const status = report.passed ? "passed" : "blocked";
599
+ const missing = report.missing.length > 0
600
+ ? report.missing.map((field) => `- ${field}`).join("\n")
601
+ : "- none";
602
+ return [
603
+ `# Gate ${report.gateId}`,
604
+ "",
605
+ `Status: ${status}`,
606
+ "",
607
+ "Missing fields:",
608
+ missing,
609
+ "",
610
+ ].join("\n");
611
+ }
612
+ function renderDependencyReport(report) {
613
+ return [
614
+ `Dependencies for ${report.taskId}: ${report.isSatisfied ? "satisfied" : "blocked"}`,
615
+ ...(report.dependencies.length === 0
616
+ ? ["- none"]
617
+ : report.dependencies.map((dependency) => `- ${dependency.id}: ${dependency.status} (${dependency.isComplete ? "complete" : "incomplete"})`)),
618
+ ].join("\n");
619
+ }
620
+ function renderTaskGraphPlan(plan) {
621
+ return [
622
+ "Task graph plan",
623
+ "",
624
+ "Ready:",
625
+ ...taskGraphReadyLines(plan.ready),
626
+ "",
627
+ "Blocked:",
628
+ ...taskGraphBlockedLines(plan.blocked),
629
+ "",
630
+ "Locked:",
631
+ ...taskGraphLockedLines(plan.locked),
632
+ "",
633
+ "Complete:",
634
+ ...taskGraphReadyLines(plan.complete),
635
+ ].join("\n");
636
+ }
637
+ function renderTaskGraphBatchRun(batch) {
638
+ return [
639
+ "Task graph batch run",
640
+ `- Selected: ${batch.selectedTaskIds.join(", ")}`,
641
+ `- Completed runs: ${batch.runs.length}`,
642
+ `- Locked: ${batch.locked.map((task) => task.id).join(", ") || "none"}`,
643
+ `- Artifact: ${batch.artifact ?? "none"}`,
644
+ ...(batch.failure
645
+ ? [`- Failure: ${batch.failure.taskId}: ${batch.failure.error}`]
646
+ : ["- Failure: none"]),
647
+ ].join("\n");
648
+ }
649
+ function taskGraphReadyLines(items) {
650
+ return items.length === 0
651
+ ? ["- none"]
652
+ : items.map((item) => `- ${item.id} [${item.status}] ${item.title} (${item.ownerRole})`);
653
+ }
654
+ function taskGraphBlockedLines(items) {
655
+ return items.length === 0
656
+ ? ["- none"]
657
+ : items.map((item) => {
658
+ const blockers = item.incomplete
659
+ .map((dependency) => `${dependency.id}:${dependency.status}`)
660
+ .join(", ");
661
+ return `- ${item.id} [${item.status}] ${item.title} blocked by ${blockers}`;
662
+ });
663
+ }
664
+ function taskGraphLockedLines(items) {
665
+ return items.length === 0
666
+ ? ["- none"]
667
+ : items.map((item) => {
668
+ const locks = item.locks
669
+ .map((lock) => lock.conflictPath
670
+ ? `${lock.id}:${lock.path}->${lock.conflictPath}`
671
+ : `${lock.id}:${lock.path}`)
672
+ .join(", ");
673
+ return `- ${item.id} [${item.status}] ${item.title} locked by ${locks}`;
674
+ });
675
+ }
676
+ function renderSummaryMarkdown(summary) {
677
+ return [
678
+ "# Agent Workflow Summary",
679
+ "",
680
+ `- Tasks: ${summary.tasks.length}`,
681
+ `- Locks: ${summary.locks.length}`,
682
+ `- Reviews: ${summary.reviews.length}`,
683
+ `- Evidence: ${summary.evidence.length}`,
684
+ `- Events: ${summary.eventCount}`,
685
+ "",
686
+ "## Tasks",
687
+ ...(summary.tasks.length === 0
688
+ ? ["- none"]
689
+ : summary.tasks.map((task) => `- ${task.id} [${task.status}] ${task.title} (${task.ownerRole})`)),
690
+ "",
691
+ "## Blockers",
692
+ ...(summary.blockers.length === 0
693
+ ? ["- none"]
694
+ : summary.blockers.map((task) => `- ${task.id}: ${task.blockedReason ?? "not specified"}`)),
695
+ "",
696
+ "## Reviews",
697
+ ...(summary.reviews.length === 0
698
+ ? ["- none"]
699
+ : summary.reviews.map((event) => `- ${event.taskId ?? "no-task"} ${event.actor}: ${String(event.metadata.result ?? "unknown")}`)),
700
+ "",
701
+ "## Evidence",
702
+ ...(summary.evidence.length === 0
703
+ ? ["- none"]
704
+ : summary.evidence.map((event) => `- ${event.taskId ?? "no-task"} ${event.actor}: ${event.summary}`)),
705
+ "",
706
+ ].join("\n");
707
+ }
708
+ function renderUsageReport(report) {
709
+ return [
710
+ `Usage${report.taskId ? ` for ${report.taskId}` : ""}`,
711
+ `- Requests: ${report.totals.requests}`,
712
+ `- Input tokens: ${report.totals.inputTokens}`,
713
+ `- Output tokens: ${report.totals.outputTokens}`,
714
+ `- Total tokens: ${report.totals.totalTokens}`,
715
+ `- Estimated cost USD: ${report.totals.estimatedCostUsd}`,
716
+ "",
717
+ "By role:",
718
+ ...usageLines(report.byRole),
719
+ "",
720
+ "By provider:",
721
+ ...usageLines(report.byProvider),
722
+ ].join("\n");
723
+ }
724
+ function renderBudgetCheck(result) {
725
+ return [
726
+ `Budget check${result.taskId ? ` for ${result.taskId}` : ""}: ${result.passed ? "passed" : "failed"}`,
727
+ `- Applied budgets: ${result.appliedBudgets.length > 0
728
+ ? result.appliedBudgets.join(", ")
729
+ : "none"}`,
730
+ `- Requests: ${result.usage.totals.requests}`,
731
+ `- Total tokens: ${result.usage.totals.totalTokens}`,
732
+ `- Estimated cost USD: ${result.usage.totals.estimatedCostUsd}`,
733
+ "",
734
+ "Violations:",
735
+ ...(result.violations.length === 0
736
+ ? ["- none"]
737
+ : result.violations.map((violation) => `- ${violation.scope} ${violation.metric}: ${violation.actual} > ${violation.limit}`)),
738
+ ].join("\n");
739
+ }
740
+ function usageLines(items) {
741
+ return items.length === 0
742
+ ? ["- none"]
743
+ : items.map((item) => `- ${item.key}: ${item.requests} requests, ${item.totalTokens} tokens, $${item.estimatedCostUsd}`);
744
+ }
745
+ function renderPullRequestSummaryMarkdown(summary) {
746
+ return [
747
+ `# PR Summary: ${summary.task.id}`,
748
+ "",
749
+ `## Task`,
750
+ `- Title: ${summary.task.title}`,
751
+ `- Owner: ${summary.task.ownerRole}`,
752
+ `- Status: ${summary.task.status}`,
753
+ `- Goal: ${summary.task.goal ?? "not specified"}`,
754
+ `- Scope: ${summary.task.scope ?? "not specified"}`,
755
+ "",
756
+ "## Acceptance Criteria",
757
+ ...listOrNone(summary.task.acceptanceCriteria),
758
+ "",
759
+ "## Risks",
760
+ ...listOrNone(summary.risks),
761
+ "",
762
+ "## Gates",
763
+ ...eventLines(summary.gates),
764
+ "",
765
+ "## Evidence",
766
+ ...eventLines(summary.evidence),
767
+ "",
768
+ "## Reviews",
769
+ ...eventLines(summary.reviews),
770
+ "",
771
+ "## Handoffs",
772
+ ...eventLines(summary.handoffs),
773
+ "",
774
+ "## Active Locks",
775
+ ...(summary.locks.length === 0
776
+ ? ["- none"]
777
+ : summary.locks.map((lock) => `- ${lock.id}: ${lock.path}`)),
778
+ "",
779
+ "## Rollout",
780
+ `- ${summary.rollout}`,
781
+ "",
782
+ "## Rollback",
783
+ `- ${summary.rollback}`,
784
+ "",
785
+ ].join("\n");
786
+ }
787
+ function renderTaskContextMarkdown(context) {
788
+ return [
789
+ `# Task Context: ${context.task.id}`,
790
+ "",
791
+ `- Title: ${context.task.title}`,
792
+ `- Owner: ${context.task.ownerRole}`,
793
+ `- Status: ${context.task.status}`,
794
+ `- Dependencies: ${context.dependencies.isSatisfied ? "satisfied" : "blocked"}`,
795
+ `- Locks: ${context.locks.length}`,
796
+ `- Risks: ${context.risks.length}`,
797
+ "",
798
+ "## Decisions",
799
+ ...eventLines(context.decisions),
800
+ "",
801
+ "## Handoffs",
802
+ ...eventLines(context.handoffs),
803
+ "",
804
+ "## Reviews",
805
+ ...eventLines(context.reviews),
806
+ "",
807
+ "## Evidence",
808
+ ...eventLines(context.evidence),
809
+ "",
810
+ "## Gates",
811
+ ...eventLines(context.gates),
812
+ "",
813
+ "## Model Provenance",
814
+ ...(context.modelProvenance.length === 0
815
+ ? ["- none"]
816
+ : context.modelProvenance.map((record) => `- ${record.role}: ${record.provider}/${record.model} ${record.responseId}`)),
817
+ "",
818
+ ].join("\n");
819
+ }
820
+ function renderExecutionPlanMarkdown(plan) {
821
+ return [
822
+ `# Execution Plan: ${plan.taskId}`,
823
+ "",
824
+ ...plan.steps.map((step) => {
825
+ const gate = step.gate ? ` gate=${step.gate}` : "";
826
+ const dependsOn = step.dependsOn.length > 0
827
+ ? ` dependsOn=${step.dependsOn.join(",")}`
828
+ : "";
829
+ return `- ${step.id}: ${step.role}${gate}${dependsOn} - ${step.action}`;
830
+ }),
831
+ "",
832
+ ].join("\n");
833
+ }
834
+ function renderExecutionRunMarkdown(run) {
835
+ return [
836
+ `# Execution Run: ${run.taskId}`,
837
+ "",
838
+ `- Provider: ${run.provider}`,
839
+ `- Model: ${run.model}`,
840
+ "",
841
+ ...run.steps.map((step) => `- ${step.id}: ${step.role} ${step.status} ${step.responseId ?? step.error ?? ""}`.trim()),
842
+ "",
843
+ ].join("\n");
844
+ }
845
+ function renderPlaywrightPlanMarkdown(plan) {
846
+ return [
847
+ `# Playwright Test Plan: ${plan.taskId}`,
848
+ "",
849
+ `- Title: ${plan.title}`,
850
+ `- Target user: ${plan.targetUser}`,
851
+ "",
852
+ "## Scenarios",
853
+ ...plan.scenarios.flatMap((scenario) => [
854
+ `### ${scenario.name}`,
855
+ `- Source: ${scenario.source}`,
856
+ `- Page object: ${scenario.pageObject}`,
857
+ `- Selectors: ${scenario.selectors.join("; ")}`,
858
+ `- Assertions: ${scenario.assertions.join("; ")}`,
859
+ `- Evidence: ${scenario.evidence.join(", ")}`,
860
+ "",
861
+ ]),
862
+ "## Fixtures",
863
+ ...listOrNone(plan.fixtures),
864
+ "",
865
+ "## Notes",
866
+ ...listOrNone(plan.notes),
867
+ "",
868
+ ].join("\n");
869
+ }
870
+ function eventLines(events) {
871
+ return events.length === 0
872
+ ? ["- none"]
873
+ : events.map((event) => {
874
+ const artifacts = event.artifacts?.join(", ") ?? "no artifacts";
875
+ return `- ${event.type}: ${event.summary} (${artifacts})`;
876
+ });
877
+ }
878
+ function listOrNone(items) {
879
+ return items && items.length > 0
880
+ ? items.map((item) => `- ${item}`)
881
+ : ["- none"];
882
+ }
883
+ //# sourceMappingURL=commands.js.map