@jterrats/open-orchestra 0.3.1 → 0.4.1

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 (92) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/autonomous-phase-lifecycle.d.ts +25 -0
  3. package/dist/autonomous-phase-lifecycle.js +194 -0
  4. package/dist/autonomous-phase-lifecycle.js.map +1 -0
  5. package/dist/autonomous-run-state.d.ts +6 -0
  6. package/dist/autonomous-run-state.js +91 -0
  7. package/dist/autonomous-run-state.js.map +1 -0
  8. package/dist/autonomous-run-store.d.ts +12 -0
  9. package/dist/autonomous-run-store.js +64 -0
  10. package/dist/autonomous-run-store.js.map +1 -0
  11. package/dist/autonomous-workflow-constants.d.ts +6 -0
  12. package/dist/autonomous-workflow-constants.js +36 -0
  13. package/dist/autonomous-workflow-constants.js.map +1 -0
  14. package/dist/autonomous-workflow.d.ts +6 -45
  15. package/dist/autonomous-workflow.js +4 -385
  16. package/dist/autonomous-workflow.js.map +1 -1
  17. package/dist/benchmark.d.ts +2 -1
  18. package/dist/benchmark.js +70 -0
  19. package/dist/benchmark.js.map +1 -1
  20. package/dist/cli.js +31 -2
  21. package/dist/cli.js.map +1 -1
  22. package/dist/command-utils.d.ts +6 -0
  23. package/dist/command-utils.js +19 -0
  24. package/dist/command-utils.js.map +1 -0
  25. package/dist/commands.d.ts +8 -40
  26. package/dist/commands.js +59 -803
  27. package/dist/commands.js.map +1 -1
  28. package/dist/constants.js +14 -0
  29. package/dist/constants.js.map +1 -1
  30. package/dist/defaults.d.ts +17 -0
  31. package/dist/defaults.js +17 -0
  32. package/dist/defaults.js.map +1 -1
  33. package/dist/instruction-commands.d.ts +5 -0
  34. package/dist/instruction-commands.js +98 -0
  35. package/dist/instruction-commands.js.map +1 -0
  36. package/dist/metrics-commands.d.ts +3 -0
  37. package/dist/metrics-commands.js +114 -0
  38. package/dist/metrics-commands.js.map +1 -0
  39. package/dist/model-commands.d.ts +13 -0
  40. package/dist/model-commands.js +199 -0
  41. package/dist/model-commands.js.map +1 -0
  42. package/dist/model-providers.d.ts +26 -1
  43. package/dist/model-providers.js +257 -1
  44. package/dist/model-providers.js.map +1 -1
  45. package/dist/notifications.d.ts +31 -0
  46. package/dist/notifications.js +165 -0
  47. package/dist/notifications.js.map +1 -0
  48. package/dist/phase-executor.d.ts +18 -0
  49. package/dist/phase-executor.js +218 -0
  50. package/dist/phase-executor.js.map +1 -0
  51. package/dist/release-commands.d.ts +10 -0
  52. package/dist/release-commands.js +116 -0
  53. package/dist/release-commands.js.map +1 -1
  54. package/dist/runtime-adapters.d.ts +5 -1
  55. package/dist/runtime-adapters.js +27 -0
  56. package/dist/runtime-adapters.js.map +1 -1
  57. package/dist/runtime-commands.d.ts +9 -0
  58. package/dist/runtime-commands.js +156 -0
  59. package/dist/runtime-commands.js.map +1 -0
  60. package/dist/runtime-execution-adapters.d.ts +2 -0
  61. package/dist/runtime-execution-adapters.js +163 -0
  62. package/dist/runtime-execution-adapters.js.map +1 -0
  63. package/dist/runtime-execution-renderer.d.ts +10 -0
  64. package/dist/runtime-execution-renderer.js +110 -0
  65. package/dist/runtime-execution-renderer.js.map +1 -0
  66. package/dist/runtime-execution.d.ts +27 -0
  67. package/dist/runtime-execution.js +147 -0
  68. package/dist/runtime-execution.js.map +1 -0
  69. package/dist/skills-commands.d.ts +9 -0
  70. package/dist/skills-commands.js +130 -0
  71. package/dist/skills-commands.js.map +1 -0
  72. package/dist/sprint-commands.d.ts +5 -0
  73. package/dist/sprint-commands.js +120 -0
  74. package/dist/sprint-commands.js.map +1 -0
  75. package/dist/sprint-metrics.d.ts +9 -0
  76. package/dist/sprint-metrics.js +203 -0
  77. package/dist/sprint-metrics.js.map +1 -0
  78. package/dist/telemetry-commands.d.ts +7 -0
  79. package/dist/telemetry-commands.js +82 -0
  80. package/dist/telemetry-commands.js.map +1 -0
  81. package/dist/tool-commands.d.ts +3 -0
  82. package/dist/tool-commands.js +67 -0
  83. package/dist/tool-commands.js.map +1 -0
  84. package/dist/types.d.ts +177 -0
  85. package/dist/types.js.map +1 -1
  86. package/dist/workflow-services.d.ts +3 -1
  87. package/dist/workflow-services.js +16 -6
  88. package/dist/workflow-services.js.map +1 -1
  89. package/docs/autonomous-workflow.md +28 -1
  90. package/docs/benchmark.md +18 -0
  91. package/docs/runtime-llm-flow.md +80 -9
  92. package/package.json +1 -1
package/dist/commands.js CHANGED
@@ -1,29 +1,23 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
1
  import { initWorkspace } from "./workspace.js";
4
2
  import { requireArg } from "./args.js";
5
3
  import { AUTONOMOUS_PHASE_SEQUENCE, autonomousRunsPath, checkArchitectSizing, closePhase, createAutonomousRun, initPhase, listAutonomousRuns, markRunDone, markRunFailed, readAutonomousRun, resumePhaseIndex, suspendPhaseForClarification, resumePhaseFromClarification, } from "./autonomous-workflow.js";
6
4
  import { listClarifications, openClarification, answerClarification, } from "./clarification.js";
7
- import { computeBenchmark, recordEstimate, summarizeBenchmark, } from "./benchmark.js";
8
- import { computeSprintBurndown, renderBurndownAscii } from "./burndown.js";
9
- import { SIZING_LABELS } from "./types.js";
10
- import { resolveWorkspaceWritePath } from "./fs-utils.js";
11
- 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";
5
+ import { executePhaseWithLlm } from "./phase-executor.js";
6
+ import { addEvidence, addPlaywrightEvidence, addTask, checkReadiness, checkTaskDependencies, claimLock, createHandoff, evaluateWorkflowGate, executeNextReadyTask, executePlanWithBudgetPreflight, executeReadyTaskBatch, generateExecutionPlan, generatePlaywrightTestPlan, generatePullRequestSummary, generateTaskGraphPlan, getWorkflowStatus, getWorkflowSummary, getTaskContext, getWorkflowConfig, listEvidence, listDecisions, listLocks, listReviews, listRoles, listTasks, recordReview, recordDecision, releaseLock, updateTask, } from "./workflow-services.js";
12
7
  import { validateWorkspace } from "./workspace-validator.js";
13
- import { addAgentLesson, listAgentLessons, listSkills, planSkillsForTask, promoteAgentLessons, readSourceOfTruth, recordSkillPlan, recordSkillRender, renderSkills, validateSkills, } from "./skills.js";
14
8
  import { getWebServerAddress, startWebApiServer } from "./web-api.js";
15
9
  import { decideTaskDelegation } from "./delegation-decision.js";
16
- import { lintMermaidDiagram, recordDiagramLintEvidence, } from "./diagram-validation.js";
17
- import { evaluateMcpOAuthProxy, } from "./mcp-oauth-proxy.js";
18
- import { detectStaleInstructionFilesFromManifest, resolveInstructionImportsFromFile, updateManagedInstructionFile, } from "./instruction-updates.js";
19
- import { applyInstructionUpdatesFromManifest } from "./instruction-apply.js";
20
- import { renderSubagentProtocol, upsertSubagentProtocolBlock, } from "./subagent-protocol.js";
21
10
  import { listCollaborationFlows, recommendCollaborationFlow, } from "./collaboration-flows.js";
22
11
  import { listWorkflowTemplates, renderWorkflowTemplates, selectWorkflowTemplates, validateWorkflowTemplates, } from "./workflow-templates.js";
23
- import { listCommandManifest } from "./command-manifest.js";
24
- import { listRuntimeAdapters, parseRuntimeTarget } from "./runtime-adapters.js";
25
- import { renderRuntimeBootstrap, upsertRuntimeBootstrapBlock, } from "./runtime-bootstrap.js";
26
- import { disableTelemetryConsent, enableTelemetryConsent, exportTelemetryDataset, exportTelemetryEvalDataset, getTelemetryConsent, parseTelemetryLevel, requiresSensitiveTelemetryOptIn, submitTelemetryDataset, } from "./telemetry.js";
12
+ import { parseRuntimeTarget } from "./runtime-adapters.js";
13
+ export { instructionsApplyCommand, instructionsBlockCommand, instructionsImportsCommand, instructionsStaleCommand, } from "./instruction-commands.js";
14
+ export { benchmarkCommand, estimateCommand } from "./metrics-commands.js";
15
+ export { burndownCommand, calibrationCommand, sprintCommand, velocityCommand, } from "./sprint-commands.js";
16
+ export { approvalsApproveCommand, approvalsListCommand, approvalsRejectCommand, approvalsShowCommand, budgetCheckCommand, configShowCommand, modelCompleteFakeCommand, modelProvidersCommand, modelProvenanceAddCommand, modelProvenanceListCommand, modelSetRoleCommand, usageCommand, } from "./model-commands.js";
17
+ export { commandsManifestCommand, protocolBlockCommand, protocolRenderCommand, runtimeAdaptersCommand, runtimeBootstrapCommand, runtimeBriefCommand, runtimeDelegatePlanCommand, runtimeHandoffCommand, } from "./runtime-commands.js";
18
+ export { lessonsAddCommand, lessonsListCommand, lessonsPromoteCommand, skillsListCommand, skillsPlanCommand, skillsRenderCommand, skillsValidateCommand, sourcesListCommand, } from "./skills-commands.js";
19
+ export { diagramsLintCommand, mcpOAuthProxyEvaluateCommand, } from "./tool-commands.js";
20
+ export { telemetryDisableCommand, telemetryEnableCommand, telemetryEvalDatasetCommand, telemetryExportCommand, telemetryStatusCommand, telemetrySubmitCommand, } from "./telemetry-commands.js";
27
21
  import { buildPrBody, createPullRequest } from "./github.js";
28
22
  export async function initCommand(options, io) {
29
23
  const root = stringOption(options["target-dir"]) ?? process.cwd();
@@ -347,56 +341,6 @@ export async function summaryCommand(options, io) {
347
341
  }
348
342
  io.log(renderSummaryMarkdown(summary));
349
343
  }
350
- export async function usageCommand(options, io) {
351
- const report = await getUsageReport(stringOption(options.task));
352
- if (options.json) {
353
- io.log(JSON.stringify(report, null, 2));
354
- return;
355
- }
356
- io.log(renderUsageReport(report));
357
- }
358
- export async function budgetCheckCommand(options, io) {
359
- const result = await checkUsageBudget(stringOption(options.task));
360
- if (options.json) {
361
- io.log(JSON.stringify(result, null, 2));
362
- }
363
- else {
364
- io.log(renderBudgetCheck(result));
365
- }
366
- if (!result.passed) {
367
- throw new Error("budget check failed");
368
- }
369
- }
370
- export async function approvalsListCommand(options, io) {
371
- const approvals = await listApprovals(stringOption(options.task));
372
- if (options.json) {
373
- io.log(JSON.stringify(approvals, null, 2));
374
- return;
375
- }
376
- if (approvals.length === 0) {
377
- io.log("No approvals");
378
- return;
379
- }
380
- for (const approval of approvals) {
381
- io.log(`${approval.id} [${approval.status}] ${approval.taskId ?? "no-task"} ${approval.artifact}`);
382
- }
383
- }
384
- export async function approvalsShowCommand(options, io) {
385
- const approval = await showApproval(requireArg(options, "id"));
386
- if (options.json) {
387
- io.log(JSON.stringify(approval, null, 2));
388
- return;
389
- }
390
- io.log(approval.content);
391
- }
392
- export async function approvalsApproveCommand(options, io) {
393
- const approval = await approveApproval(parseApprovalDecision(options));
394
- io.log(`Approved ${approval.id}`);
395
- }
396
- export async function approvalsRejectCommand(options, io) {
397
- const approval = await rejectApproval(parseApprovalDecision(options));
398
- io.log(`Rejected ${approval.id}`);
399
- }
400
344
  export async function prSummaryCommand(options, io) {
401
345
  const summary = await generatePullRequestSummary(requireArg(options, "task"));
402
346
  if (options.json) {
@@ -458,254 +402,6 @@ export async function playwrightEvidenceCommand(options, io) {
458
402
  }));
459
403
  io.log(`Created ${artifact}`);
460
404
  }
461
- export async function configShowCommand(options, io) {
462
- const config = await getWorkflowConfig();
463
- if (options.json) {
464
- io.log(JSON.stringify(config, null, 2));
465
- return;
466
- }
467
- io.log(`Tools: ${Object.keys(config.tools ?? {}).length}`);
468
- io.log(`Role routes: ${Object.keys(config.providers?.byRole ?? {}).length}`);
469
- }
470
- export async function modelProvidersCommand(options, io) {
471
- const providers = await listConfiguredModelProviders();
472
- if (options.json) {
473
- io.log(JSON.stringify(providers, null, 2));
474
- return;
475
- }
476
- if (providers.length === 0) {
477
- io.log("No configured model providers");
478
- return;
479
- }
480
- for (const provider of providers) {
481
- io.log(`${provider.scope}: ${provider.provider}/${provider.model} (${provider.timeoutMs}ms)`);
482
- }
483
- }
484
- export async function modelSetRoleCommand(options, io) {
485
- const role = requireArg(options, "role");
486
- const routing = parseProviderRouting(options);
487
- await setRoleModelProvider(role, routing);
488
- io.log(`Configured model provider for ${role}`);
489
- }
490
- export async function modelProvenanceAddCommand(options, io) {
491
- const record = await recordModelProvenance({
492
- task: requireArg(options, "task"),
493
- role: requireArg(options, "role"),
494
- provider: requireArg(options, "provider"),
495
- model: requireArg(options, "model"),
496
- promptId: requireArg(options, "prompt-id"),
497
- responseId: requireArg(options, "response-id"),
498
- inputTokens: numberOption(options["input-tokens"], 0),
499
- outputTokens: numberOption(options["output-tokens"], 0),
500
- estimatedCostUsd: numberOption(options["estimated-cost-usd"], 0),
501
- finishReason: requireArg(options, "finish-reason"),
502
- });
503
- io.log(`Recorded model provenance ${record.responseId}`);
504
- }
505
- export async function modelProvenanceListCommand(options, io) {
506
- const records = await listModelProvenance(stringOption(options.task));
507
- if (options.json) {
508
- io.log(JSON.stringify(records, null, 2));
509
- return;
510
- }
511
- if (records.length === 0) {
512
- io.log("No model provenance");
513
- return;
514
- }
515
- for (const record of records) {
516
- io.log(`${record.task} ${record.role} ${record.provider}/${record.model} ${record.responseId}`);
517
- }
518
- }
519
- export async function modelCompleteFakeCommand(options, io) {
520
- const result = await completeWithProviderFallback({
521
- provider: requireArg(options, "provider"),
522
- model: requireArg(options, "model"),
523
- fallbacks: parseCsv(options.fallbacks),
524
- maxTokens: 0,
525
- maxCostUsd: 0,
526
- timeoutMs: 30000,
527
- retries: 0,
528
- requiredCapabilities: [],
529
- }, requireArg(options, "prompt"), removeUndefined({
530
- failingProviders: parseCsv(options["fail-provider"]),
531
- taskId: stringOption(options.task),
532
- role: stringOption(options.role) ?? "parent",
533
- }));
534
- if (options.json) {
535
- io.log(JSON.stringify(result, null, 2));
536
- return;
537
- }
538
- io.log(`Fake completion used ${result.provider}/${result.model} fallback=${String(result.fallbackUsed)}`);
539
- }
540
- export async function telemetryStatusCommand(options, io) {
541
- const telemetry = await getTelemetryConsent();
542
- if (options.json) {
543
- io.log(JSON.stringify(telemetry, null, 2));
544
- return;
545
- }
546
- io.log("Telemetry: " + (telemetry.enabled ? "enabled" : "off"));
547
- io.log("Level: " + telemetry.level);
548
- io.log("Policy: " + telemetry.policyVersion);
549
- }
550
- export async function telemetryEnableCommand(options, io) {
551
- const level = parseTelemetryLevel(stringOption(options.level) ?? "metadata");
552
- if (requiresSensitiveTelemetryOptIn(level) && !options["confirm-sensitive"]) {
553
- throw new Error("prompt-sample and eval-dataset telemetry require --confirm-sensitive");
554
- }
555
- const telemetry = await enableTelemetryConsent({
556
- level,
557
- actor: stringOption(options.actor) ?? "user",
558
- });
559
- if (options.json) {
560
- io.log(JSON.stringify(telemetry, null, 2));
561
- return;
562
- }
563
- io.log("Telemetry enabled at level " + telemetry.level);
564
- }
565
- export async function telemetryExportCommand(options, io) {
566
- const result = await exportTelemetryDataset({
567
- dryRun: Boolean(options["dry-run"]),
568
- });
569
- if (options.json) {
570
- io.log(JSON.stringify(result, null, 2));
571
- return;
572
- }
573
- io.log((result.dryRun ? "Telemetry export dry run" : "Telemetry exported") +
574
- ": " +
575
- result.recordCount +
576
- " record(s)");
577
- if (result.file) {
578
- io.log("File: " + result.file);
579
- }
580
- }
581
- export async function telemetrySubmitCommand(options, io) {
582
- const result = await submitTelemetryDataset({
583
- file: requireArg(options, "file"),
584
- endpoint: requireArg(options, "endpoint"),
585
- });
586
- if (options.json) {
587
- io.log(JSON.stringify(result, null, 2));
588
- return;
589
- }
590
- io.log("Telemetry submission recorded: " + result.fileHash);
591
- }
592
- export async function telemetryEvalDatasetCommand(options, io) {
593
- const result = await exportTelemetryEvalDataset({
594
- dryRun: Boolean(options["dry-run"]),
595
- });
596
- if (options.json) {
597
- io.log(JSON.stringify(result, null, 2));
598
- return;
599
- }
600
- io.log((result.dryRun ? "Eval dataset dry run" : "Eval dataset exported") +
601
- ": " +
602
- result.recordCount +
603
- " record(s)");
604
- if (result.file) {
605
- io.log("File: " + result.file);
606
- }
607
- }
608
- export async function telemetryDisableCommand(options, io) {
609
- const telemetry = await disableTelemetryConsent({
610
- actor: stringOption(options.actor) ?? "user",
611
- });
612
- if (options.json) {
613
- io.log(JSON.stringify(telemetry, null, 2));
614
- return;
615
- }
616
- io.log("Telemetry disabled");
617
- }
618
- export async function instructionsApplyCommand(options, io) {
619
- const report = await applyInstructionUpdatesFromManifest({
620
- manifestPath: requireArg(options, "manifest"),
621
- check: Boolean(options.check),
622
- dryRun: Boolean(options["dry-run"]),
623
- force: Boolean(options.force),
624
- });
625
- if (options.json) {
626
- io.log(JSON.stringify(report, null, 2));
627
- return;
628
- }
629
- io.log("Instruction update " +
630
- report.mode +
631
- ": " +
632
- report.totals.changed +
633
- " changed, " +
634
- report.totals.unchanged +
635
- " unchanged, " +
636
- report.totals.blocked +
637
- " blocked");
638
- for (const item of report.items) {
639
- io.log(item.status +
640
- " " +
641
- item.filePath +
642
- "#" +
643
- item.blockId +
644
- " - " +
645
- item.reason);
646
- }
647
- }
648
- export async function instructionsStaleCommand(options, io) {
649
- const report = await detectStaleInstructionFilesFromManifest({
650
- manifestPath: requireArg(options, "manifest"),
651
- });
652
- if (options.json) {
653
- io.log(JSON.stringify(report, null, 2));
654
- return;
655
- }
656
- io.log("Instruction file status:");
657
- for (const item of report.items) {
658
- io.log(item.status +
659
- " " +
660
- item.filePath +
661
- "#" +
662
- item.blockId +
663
- " - " +
664
- item.reason);
665
- }
666
- }
667
- export async function instructionsBlockCommand(options, io) {
668
- const result = await updateManagedInstructionFile({
669
- filePath: resolveWorkspaceWritePath(process.cwd(), requireArg(options, "file")),
670
- contentPath: resolveWorkspaceWritePath(process.cwd(), requireArg(options, "content-file")),
671
- input: {
672
- blockId: requireArg(options, "block"),
673
- generator: stringOption(options.generator) ?? "open-orchestra",
674
- version: stringOption(options.version) ?? "1",
675
- target: parseInstructionTarget(stringOption(options.target) ?? "generic"),
676
- sourceManifest: stringOption(options["source-manifest"]) ?? "local",
677
- },
678
- check: Boolean(options.check),
679
- dryRun: Boolean(options["dry-run"]),
680
- force: Boolean(options.force),
681
- });
682
- if (options.json) {
683
- io.log(JSON.stringify(result, null, 2));
684
- return;
685
- }
686
- if (result.drift) {
687
- io.log("Drift detected for " + requireArg(options, "block"));
688
- return;
689
- }
690
- io.log(result.changed
691
- ? "Instruction block changed"
692
- : "Instruction block unchanged");
693
- }
694
- export async function instructionsImportsCommand(options, io) {
695
- const result = await resolveInstructionImportsFromFile({
696
- registryPath: requireArg(options, "registry"),
697
- entryId: requireArg(options, "entry"),
698
- target: parseInstructionTarget(stringOption(options.target) ?? "generic"),
699
- });
700
- if (options.json) {
701
- io.log(JSON.stringify(result, null, 2));
702
- return;
703
- }
704
- io.log(result.content);
705
- }
706
- function parseInstructionTarget(value) {
707
- return parseRuntimeTarget(value);
708
- }
709
405
  export async function collaborationFlowsCommand(options, io) {
710
406
  const flows = listCollaborationFlows();
711
407
  if (options.json) {
@@ -786,283 +482,6 @@ export async function workflowTemplateRenderCommand(options, io) {
786
482
  }
787
483
  io.log(rendered.content);
788
484
  }
789
- export async function commandsManifestCommand(options, io) {
790
- const manifest = listCommandManifest();
791
- if (options.json) {
792
- io.log(JSON.stringify(manifest, null, 2));
793
- return;
794
- }
795
- for (const command of manifest) {
796
- io.log(command.command + " - " + command.summary);
797
- }
798
- }
799
- export async function runtimeAdaptersCommand(options, io) {
800
- const adapters = listRuntimeAdapters();
801
- if (options.json) {
802
- io.log(JSON.stringify(adapters, null, 2));
803
- return;
804
- }
805
- for (const adapter of adapters) {
806
- io.log(adapter.target +
807
- " - " +
808
- adapter.label +
809
- " (" +
810
- adapter.defaultInstructionFiles.join(", ") +
811
- ")");
812
- }
813
- }
814
- export async function runtimeBootstrapCommand(options, io) {
815
- const target = parseSkillRenderTarget(stringOption(options.target) ?? "generic");
816
- const bootstrap = renderRuntimeBootstrap(target);
817
- const filePath = stringOption(options.file);
818
- if (filePath) {
819
- const resolvedFilePath = resolveWorkspaceWritePath(process.cwd(), path.normalize(filePath));
820
- const existing = await readFile(resolvedFilePath, "utf8").catch(() => "");
821
- const result = upsertRuntimeBootstrapBlock(existing, bootstrap, {
822
- force: Boolean(options.force),
823
- });
824
- if (!options.check && !options["dry-run"] && !result.drift) {
825
- await writeFile(resolvedFilePath, result.content);
826
- }
827
- if (options.json) {
828
- io.log(JSON.stringify(result, null, 2));
829
- return;
830
- }
831
- io.log(result.changed
832
- ? "Runtime bootstrap changed"
833
- : "Runtime bootstrap unchanged");
834
- return;
835
- }
836
- if (options.json) {
837
- io.log(JSON.stringify(bootstrap, null, 2));
838
- return;
839
- }
840
- io.log(bootstrap.content);
841
- }
842
- export async function protocolRenderCommand(options, io) {
843
- const protocol = await renderSubagentProtocol(removeUndefined({
844
- target: parseSkillRenderTarget(stringOption(options.target) ?? "generic"),
845
- taskId: stringOption(options.task),
846
- }));
847
- if (options.json) {
848
- io.log(JSON.stringify(protocol, null, 2));
849
- return;
850
- }
851
- io.log(protocol.content);
852
- }
853
- export async function protocolBlockCommand(options, io) {
854
- const filePath = requireArg(options, "file");
855
- const protocol = await renderSubagentProtocol(removeUndefined({
856
- target: parseSkillRenderTarget(stringOption(options.target) ?? "generic"),
857
- taskId: stringOption(options.task),
858
- }));
859
- const existing = await readFile(filePath, "utf8").catch(() => "");
860
- const result = upsertSubagentProtocolBlock(existing, protocol, {
861
- force: Boolean(options.force),
862
- });
863
- const shouldWrite = !options.check && !options["dry-run"] && !result.drift && result.changed;
864
- if (shouldWrite) {
865
- await writeFile(filePath, result.content);
866
- }
867
- if (options.json) {
868
- io.log(JSON.stringify(result, null, 2));
869
- return;
870
- }
871
- if (result.drift) {
872
- io.log("Drift detected for subagent protocol block");
873
- return;
874
- }
875
- io.log(result.changed
876
- ? "Subagent protocol block changed"
877
- : "Subagent protocol block unchanged");
878
- }
879
- export async function diagramsLintCommand(options, io) {
880
- const result = await lintMermaidDiagram({
881
- filePath: requireArg(options, "file"),
882
- });
883
- const evidence = stringOption(options.task)
884
- ? await recordDiagramLintEvidence({
885
- taskId: stringOption(options.task) ?? "",
886
- result,
887
- })
888
- : undefined;
889
- const output = removeUndefined({ result, evidence });
890
- if (options.json) {
891
- io.log(JSON.stringify(output, null, 2));
892
- }
893
- else {
894
- io.log(result.valid ? "Mermaid diagram valid" : "Mermaid diagram invalid");
895
- if (result.installHint) {
896
- io.log(result.installHint);
897
- }
898
- if (evidence) {
899
- io.log("Created " + evidence.artifact);
900
- }
901
- }
902
- if (!result.valid) {
903
- throw new Error("diagram lint failed");
904
- }
905
- }
906
- export async function mcpOAuthProxyEvaluateCommand(options, io) {
907
- const evaluation = evaluateMcpOAuthProxy(removeUndefined({
908
- enabled: Boolean(options.enable),
909
- mode: parseMcpProxyMode(stringOption(options.mode) ?? "stdio-proxy"),
910
- serverUrl: requireArg(options, "server-url"),
911
- tokenStorage: parseMcpSecretStorage(stringOption(options["token-storage"]) ?? "keychain"),
912
- tokenPath: stringOption(options["token-path"]),
913
- refreshWindowSeconds: Number(stringOption(options["refresh-window"]) ?? 300),
914
- approvedBy: options.approve
915
- ? (stringOption(options.approver) ?? "local-user")
916
- : undefined,
917
- }));
918
- if (options.json) {
919
- io.log(JSON.stringify(evaluation, null, 2));
920
- return;
921
- }
922
- io.log(evaluation.approved
923
- ? "MCP proxy plan approved"
924
- : "MCP proxy plan has risks");
925
- for (const risk of evaluation.risks) {
926
- io.log("RISK: " + risk);
927
- }
928
- }
929
- function parseMcpProxyMode(value) {
930
- if (["stdio-proxy", "direct-http", "tool-native-oauth"].includes(value)) {
931
- return value;
932
- }
933
- throw new Error("unknown MCP proxy mode: " + value);
934
- }
935
- function parseMcpSecretStorage(value) {
936
- if (["keychain", "libsecret", "windows-credential", "secure-file"].includes(value)) {
937
- return value;
938
- }
939
- throw new Error("unknown MCP secret storage: " + value);
940
- }
941
- export async function skillsValidateCommand(options, io) {
942
- const report = await validateSkills();
943
- if (options.json) {
944
- io.log(JSON.stringify(report, null, 2));
945
- }
946
- else {
947
- io.log(report.valid ? "Skills valid" : "Skills invalid");
948
- for (const error of report.errors) {
949
- io.log(`ERROR: ${error}`);
950
- }
951
- for (const warning of report.warnings) {
952
- io.log(`WARN: ${warning}`);
953
- }
954
- }
955
- if (!report.valid) {
956
- throw new Error("skills validation failed");
957
- }
958
- }
959
- export async function sourcesListCommand(options, io) {
960
- const sources = await readSourceOfTruth();
961
- if (options.json) {
962
- io.log(JSON.stringify(sources, null, 2));
963
- return;
964
- }
965
- for (const source of sources) {
966
- io.log(`${source.id ?? "unknown"} - ${source.name ?? "Unnamed source"}`);
967
- io.log(` ${(source.locations ?? []).join(", ")}`);
968
- }
969
- }
970
- export async function lessonsListCommand(options, io) {
971
- const lessons = await listAgentLessons();
972
- if (options.json) {
973
- io.log(JSON.stringify(lessons, null, 2));
974
- return;
975
- }
976
- if (lessons.length === 0) {
977
- io.log("No lessons");
978
- return;
979
- }
980
- for (const lesson of lessons) {
981
- io.log(`${lesson.timestamp} ${lesson.operation}: ${lesson.errorSignature}`);
982
- }
983
- }
984
- export async function lessonsAddCommand(options, io) {
985
- const lesson = await addAgentLesson(removeUndefined({
986
- taskId: stringOption(options.task),
987
- actor: stringOption(options.actor) ?? "parent",
988
- operation: requireArg(options, "operation"),
989
- failedAction: requireArg(options, "failed-action"),
990
- errorSignature: requireArg(options, "error-signature"),
991
- rootCause: requireArg(options, "root-cause"),
992
- fix: requireArg(options, "fix"),
993
- prevention: requireArg(options, "prevention"),
994
- appliesTo: parseCsv(options["applies-to"]),
995
- verifiedBy: parseCsv(options["verified-by"]),
996
- }));
997
- if (options.json) {
998
- io.log(JSON.stringify(lesson, null, 2));
999
- return;
1000
- }
1001
- io.log(`Recorded lesson ${lesson.errorSignature}`);
1002
- }
1003
- export async function lessonsPromoteCommand(options, io) {
1004
- const result = await promoteAgentLessons(removeUndefined({
1005
- to: parsePromotionTarget(stringOption(options.to) ?? "doc"),
1006
- filter: stringOption(options.filter),
1007
- }));
1008
- if (options.json) {
1009
- io.log(JSON.stringify(result, null, 2));
1010
- return;
1011
- }
1012
- io.log(`Created ${result.artifact}`);
1013
- io.log(`Promoted lessons: ${result.lessons.length}`);
1014
- }
1015
- function parsePromotionTarget(value) {
1016
- if (["skill", "rule", "doc"].includes(value)) {
1017
- return value;
1018
- }
1019
- throw new Error(`unknown promotion target: ${value}`);
1020
- }
1021
- export async function skillsListCommand(options, io) {
1022
- const skills = listSkills();
1023
- if (options.json) {
1024
- io.log(JSON.stringify(skills, null, 2));
1025
- return;
1026
- }
1027
- for (const skill of skills) {
1028
- io.log(`${skill.id} - ${skill.name} (${skill.loadBudget})`);
1029
- io.log(` ${skill.summary}`);
1030
- io.log(` sources: ${skill.sourceGroups.join(", ")}`);
1031
- }
1032
- }
1033
- export async function skillsPlanCommand(options, io) {
1034
- const plan = await planSkillsForTask(requireArg(options, "task"));
1035
- await recordSkillPlan(plan);
1036
- if (options.json) {
1037
- io.log(JSON.stringify(plan, null, 2));
1038
- return;
1039
- }
1040
- io.log(`Skills for ${plan.taskId}:`);
1041
- for (const item of plan.selected) {
1042
- io.log(` ${item.skill.id} (score ${item.score})`);
1043
- for (const reason of item.rationale) {
1044
- io.log(` - ${reason}`);
1045
- }
1046
- }
1047
- io.log(`Source groups: ${plan.sourceGroups.join(", ")}`);
1048
- }
1049
- export async function skillsRenderCommand(options, io) {
1050
- const target = parseSkillRenderTarget(stringOption(options.target) ?? "generic");
1051
- const rendered = await renderSkills({
1052
- target,
1053
- taskId: stringOption(options.task),
1054
- skillIds: parseCsv(options.skills),
1055
- });
1056
- await recordSkillRender(rendered);
1057
- if (options.json) {
1058
- io.log(JSON.stringify(rendered, null, 2));
1059
- return;
1060
- }
1061
- io.log(rendered.content);
1062
- }
1063
- function parseSkillRenderTarget(value) {
1064
- return parseRuntimeTarget(value);
1065
- }
1066
485
  // --- Autonomous workflow commands ---
1067
486
  const GATE_MODES = ["none", "phase", "all"];
1068
487
  export async function workflowRunCommand(options, io) {
@@ -1104,11 +523,15 @@ export async function workflowRunCommand(options, io) {
1104
523
  io.log(JSON.stringify(result, null, 2));
1105
524
  return;
1106
525
  }
1107
- io.log(run.status === "done"
1108
- ? `Workflow complete [run=${run.id}]`
1109
- : run.status === "paused"
1110
- ? `Workflow paused — approve gate review then run: orchestra workflow run --task ${taskId} --resume ${run.id}`
1111
- : `Workflow ${run.status} [run=${run.id}]`);
526
+ if (run.status === "done") {
527
+ io.log(`Workflow complete [run=${run.id}]`);
528
+ }
529
+ else if (run.status === "paused") {
530
+ io.log(`Workflow paused — approve gate review then run: orchestra workflow run --task ${taskId} --resume ${run.id}`);
531
+ }
532
+ else {
533
+ io.log(`Workflow ${run.status} [run=${run.id}]`);
534
+ }
1112
535
  }
1113
536
  export async function workflowRunListCommand(options, io) {
1114
537
  const cwd = process.cwd();
@@ -1245,7 +668,30 @@ async function executePhases(cwd, run, startIndex, io) {
1245
668
  else if (existing.status === "awaiting_clarification") {
1246
669
  io.log(`↺ ${def.phase} (${def.role}) resuming after clarification`);
1247
670
  }
1248
- // Architect sizing gate — always enforced regardless of --gates mode
671
+ // QA loop guard
672
+ if (def.phase === "qa" && current.qaIterations >= current.maxIterations) {
673
+ io.log(`✗ QA failed after ${current.maxIterations} iterations — workflow blocked`);
674
+ current = await markRunFailed(cwd, current, `Max QA iterations (${current.maxIterations}) reached`);
675
+ return current;
676
+ }
677
+ const llmResult = await executePhaseWithLlm({
678
+ root: cwd,
679
+ run: current,
680
+ phase: def,
681
+ phaseIndex: i,
682
+ }).catch(async (error) => {
683
+ const message = error instanceof Error ? error.message : String(error);
684
+ io.log(`✗ ${def.phase} provider execution failed: ${message}`);
685
+ current = await markRunFailed(cwd, current, message);
686
+ return null;
687
+ });
688
+ if (!llmResult)
689
+ return current;
690
+ if (llmResult.mode === "llm") {
691
+ io.log(` ✓ llm artifact (${llmResult.artifact})`);
692
+ }
693
+ // Architect sizing gate — always enforced regardless of --gates mode.
694
+ // In LLM mode, the architect phase records the sizing decision before this check.
1249
695
  if (def.phase === "architect") {
1250
696
  const sizing = await checkArchitectSizing(cwd, current.taskId);
1251
697
  if (!sizing.found) {
@@ -1258,27 +704,25 @@ async function executePhases(cwd, run, startIndex, io) {
1258
704
  }
1259
705
  io.log(` ✓ sizing=${sizing.sizing}${sizing.points !== undefined ? ` (${sizing.points} pts)` : ""}`);
1260
706
  }
1261
- // QA loop guard
1262
- if (def.phase === "qa" && current.qaIterations >= current.maxIterations) {
1263
- io.log(`✗ QA failed after ${current.maxIterations} iterations — workflow blocked`);
1264
- current = await markRunFailed(cwd, current, `Max QA iterations (${current.maxIterations}) reached`);
1265
- return current;
1266
- }
1267
- // Determine outcome — QA phase can fail and route back to developer
1268
- // When LLM execution lands (ORCH-019), this will be the actual QA verdict.
1269
- // For now the outcome is always done; qa_fail is exercised via tests/simulation.
1270
- const outcome = {
1271
- kind: "done",
1272
- notes: def.summary,
1273
- };
707
+ const outcome = llmResult.outcome;
1274
708
  const result = await closePhase(cwd, current, i, outcome);
1275
709
  current = result.run;
1276
710
  if (result.handoffArtifact) {
1277
711
  io.log(` ✓ handoff → ${AUTONOMOUS_PHASE_SEQUENCE[i + 1]?.role ?? "end"} (${result.handoffArtifact})`);
1278
712
  }
1279
713
  if (result.reviewArtifact) {
1280
- io.log(` ⏸ gate ${def.phase}→${AUTONOMOUS_PHASE_SEQUENCE[i + 1]?.phase} — review: ${result.reviewArtifact}`);
714
+ const nextPhase = AUTONOMOUS_PHASE_SEQUENCE[i + 1]?.phase;
715
+ io.log(` ⏸ gate ${def.phase}→${nextPhase} — review: ${result.reviewArtifact}`);
1281
716
  io.log(` Approve: orchestra workflow run --task ${current.taskId} --resume ${current.id}`);
717
+ io.log(``);
718
+ io.log(`╔══ GATE PAUSE: ${def.phase.toUpperCase()} → ${nextPhase?.toUpperCase()} ══════════════════════`);
719
+ io.log(`║ Run: ${current.id}`);
720
+ io.log(`║ Task: ${current.taskId}`);
721
+ io.log(`║ Review: ${result.reviewArtifact}`);
722
+ io.log(`║`);
723
+ io.log(`║ To approve, run:`);
724
+ io.log(`║ orchestra workflow run --task ${current.taskId} --resume ${current.id}`);
725
+ io.log(`╚══════════════════════════════════════════════════════════════`);
1282
726
  return current;
1283
727
  }
1284
728
  // QA failure — loop back to developer with findings as context
@@ -1347,141 +791,6 @@ async function workflowDryRun(options, io, taskId, gates, maxIterations) {
1347
791
  }, null, 2));
1348
792
  }
1349
793
  }
1350
- // --- Estimate & Benchmark commands ---
1351
- const CONFIDENCE_LEVELS = ["low", "medium", "high"];
1352
- export async function estimateCommand(options, io) {
1353
- const cwd = process.cwd();
1354
- const taskId = requireArg(options, "task");
1355
- const sizingRaw = requireArg(options, "sizing");
1356
- const soloDays = numberOption(options["solo-days"], 0);
1357
- const aiDays = numberOption(options["ai-unguided-days"], 0);
1358
- const confidence = (stringOption(options.confidence) ??
1359
- "medium");
1360
- const declaredBy = stringOption(options["declared-by"]) ?? "pm";
1361
- if (!SIZING_LABELS.includes(sizingRaw)) {
1362
- throw new Error(`--sizing must be one of: ${SIZING_LABELS.join(", ")}`);
1363
- }
1364
- if (!CONFIDENCE_LEVELS.includes(confidence)) {
1365
- throw new Error(`--confidence must be one of: ${CONFIDENCE_LEVELS.join(", ")}`);
1366
- }
1367
- if (soloDays <= 0)
1368
- throw new Error(`--solo-days must be > 0`);
1369
- if (aiDays <= 0)
1370
- throw new Error(`--ai-unguided-days must be > 0`);
1371
- const record = await recordEstimate(cwd, {
1372
- taskId,
1373
- sizingLabel: sizingRaw,
1374
- soloEstimateDays: soloDays,
1375
- aiUnguidedEstimateDays: aiDays,
1376
- confidence,
1377
- declaredBy,
1378
- });
1379
- if (options.json) {
1380
- io.log(JSON.stringify(record, null, 2));
1381
- return;
1382
- }
1383
- io.log(`Estimate recorded for ${taskId} [${record.id}]`);
1384
- io.log(` Sizing: ${record.sizingLabel}`);
1385
- io.log(` Solo: ${record.soloEstimateDays}d`);
1386
- io.log(` AI-unguided: ${record.aiUnguidedEstimateDays}d`);
1387
- io.log(` Confidence: ${record.confidence}`);
1388
- io.log(` Declared by: ${record.declaredBy}`);
1389
- }
1390
- export async function benchmarkCommand(options, io) {
1391
- const cwd = process.cwd();
1392
- if (options.summary) {
1393
- const summary = await summarizeBenchmark(cwd);
1394
- if (options.json) {
1395
- io.log(JSON.stringify(summary, null, 2));
1396
- return;
1397
- }
1398
- if (summary.stories.length === 0) {
1399
- io.log("No estimates recorded yet. Use: orchestra estimate --task <id> ...");
1400
- return;
1401
- }
1402
- const header = `${"Story".padEnd(14)} ${"Size".padEnd(4)} ${"Solo".padEnd(6)} ${"AI".padEnd(6)} ${"Actual".padEnd(8)} ${"vs Solo".padEnd(8)} ${"vs AI".padEnd(8)} ${"QA".padEnd(4)} ${"Rev".padEnd(4)} ${"Blk".padEnd(4)} ${"Ev".padEnd(4)} ${"Les"}`;
1403
- io.log(header);
1404
- io.log("─".repeat(header.length));
1405
- for (const s of summary.stories) {
1406
- const actual = s.actualDays !== null ? `${s.actualDays}d` : "pending";
1407
- const vsSolo = s.vsSoloPct !== null
1408
- ? `${s.vsSoloPct > 0 ? "+" : ""}${s.vsSoloPct}%`
1409
- : "—";
1410
- const vsAi = s.vsAiUnguidedPct !== null
1411
- ? `${s.vsAiUnguidedPct > 0 ? "+" : ""}${s.vsAiUnguidedPct}%`
1412
- : "—";
1413
- io.log(`${s.taskId.padEnd(14)} ${s.sizingLabel.padEnd(4)} ${`${s.soloEstimateDays}d`.padEnd(6)} ${`${s.aiUnguidedEstimateDays}d`.padEnd(6)} ${actual.padEnd(8)} ${vsSolo.padEnd(8)} ${vsAi.padEnd(8)} ${String(s.qaIterations).padEnd(4)} ${String(s.quality.reviewCount).padEnd(4)} ${String(s.quality.blockingReviews).padEnd(4)} ${String(s.quality.evidenceCount).padEnd(4)} ${s.quality.lessonCount}`);
1414
- }
1415
- if (summary.totalWithActuals > 0) {
1416
- io.log("");
1417
- io.log(`Avg savings vs solo: ${summary.avgVsSoloPct}%`);
1418
- io.log(`Avg savings vs AI-unguided: ${summary.avgVsAiUnguidedPct}%`);
1419
- io.log(`Stories with actuals: ${summary.totalWithActuals}/${summary.stories.length}`);
1420
- }
1421
- return;
1422
- }
1423
- const taskId = requireArg(options, "task");
1424
- const result = await computeBenchmark(cwd, taskId);
1425
- if (options.json) {
1426
- io.log(JSON.stringify(result, null, 2));
1427
- return;
1428
- }
1429
- io.log(`Benchmark: ${taskId} [${result.status}]`);
1430
- io.log(` Sizing: ${result.sizingLabel}`);
1431
- io.log(` Solo: ${result.soloEstimateDays}d (declared)`);
1432
- io.log(` AI-unguided: ${result.aiUnguidedEstimateDays}d (declared)`);
1433
- io.log(` Actual: ${result.actualDays !== null ? `${result.actualDays}d` : "pending — run not complete"}`);
1434
- if (result.vsSoloPct !== null) {
1435
- const sign = result.vsSoloPct > 0 ? "+" : "";
1436
- io.log(` vs Solo: ${sign}${result.vsSoloPct}%`);
1437
- io.log(` vs AI: ${result.vsAiUnguidedPct !== null ? `${result.vsAiUnguidedPct > 0 ? "+" : ""}${result.vsAiUnguidedPct}%` : "—"}`);
1438
- }
1439
- io.log(` QA loops: ${result.qaIterations}`);
1440
- io.log(` Reviews: ${result.quality.reviewCount} (${result.quality.blockingReviews} blocking)`);
1441
- io.log(` Evidence: ${result.quality.evidenceCount} artifacts`);
1442
- io.log(` Gate blocks: ${result.quality.gateBlockCount}`);
1443
- io.log(` Lessons: ${result.quality.lessonCount}`);
1444
- if (result.quality.totalInputTokens > 0 ||
1445
- result.quality.totalOutputTokens > 0) {
1446
- io.log(` Tokens: ${result.quality.totalInputTokens}in / ${result.quality.totalOutputTokens}out`);
1447
- io.log(` Cost: $${result.quality.estimatedCostUsd}`);
1448
- }
1449
- }
1450
- // --- Burndown command ---
1451
- export async function burndownCommand(options, io) {
1452
- const cwd = process.cwd();
1453
- const sprintCsv = requireArg(options, "sprint");
1454
- const sprintTaskIds = sprintCsv
1455
- .split(",")
1456
- .map((s) => s.trim())
1457
- .filter(Boolean);
1458
- if (sprintTaskIds.length === 0)
1459
- throw new Error(`--sprint requires at least one task id`);
1460
- const series = await computeSprintBurndown(cwd, sprintTaskIds);
1461
- if (options.json) {
1462
- io.log(JSON.stringify(series, null, 2));
1463
- return;
1464
- }
1465
- for (const w of series.warnings)
1466
- io.log(`⚠ ${w}`);
1467
- if (series.totalPoints === 0) {
1468
- io.log("No points to chart — record estimates first with: orchestra estimate --task <id> ...");
1469
- return;
1470
- }
1471
- io.log(`Sprint burndown total=${series.totalPoints} pts tasks=${series.sprintTaskIds.length}`);
1472
- io.log("");
1473
- io.log(renderBurndownAscii(series));
1474
- io.log("");
1475
- io.log("Task breakdown:");
1476
- for (const t of series.taskBreakdown) {
1477
- const arch = t.architectPoints !== null ? `arch=${t.architectPoints}` : "arch=—";
1478
- const dev = t.developerPoints !== null ? `dev=${t.developerPoints}` : "dev=—";
1479
- const done = t.completedAt
1480
- ? `done ${t.completedAt.slice(0, 10)}`
1481
- : "pending";
1482
- io.log(` ${t.taskId.padEnd(14)} ${arch.padEnd(10)} ${dev.padEnd(10)} resolved=${t.resolvedPoints} ${done}`);
1483
- }
1484
- }
1485
794
  function parseRuntimeTargetOptions(options) {
1486
795
  const targetValues = [
1487
796
  ...parseCsv(options.target),
@@ -1489,6 +798,9 @@ function parseRuntimeTargetOptions(options) {
1489
798
  ];
1490
799
  return [...new Set(targetValues)].map((target) => parseRuntimeTarget(target));
1491
800
  }
801
+ function parseSkillRenderTarget(value) {
802
+ return parseRuntimeTarget(value);
803
+ }
1492
804
  function parseCsv(value) {
1493
805
  if (typeof value !== "string" || value.trim() === "") {
1494
806
  return [];
@@ -1537,18 +849,6 @@ function parseRiskAcceptance(options) {
1537
849
  });
1538
850
  return Object.keys(acceptance).length > 0 ? acceptance : undefined;
1539
851
  }
1540
- function parseProviderRouting(options) {
1541
- return {
1542
- provider: requireArg(options, "provider"),
1543
- model: requireArg(options, "model"),
1544
- fallbacks: parseCsv(options.fallbacks),
1545
- maxTokens: numberOption(options["max-tokens"], 0),
1546
- maxCostUsd: numberOption(options["max-cost-usd"], 0),
1547
- timeoutMs: numberOption(options["timeout-ms"], 30000),
1548
- retries: numberOption(options.retries, 0),
1549
- requiredCapabilities: parseCsv(options.capabilities),
1550
- };
1551
- }
1552
852
  function parseBudgetEscalationDecision(options) {
1553
853
  if (options["approve-budget-fallback"]) {
1554
854
  return removeUndefined({
@@ -1566,13 +866,6 @@ function parseBudgetEscalationDecision(options) {
1566
866
  }
1567
867
  return undefined;
1568
868
  }
1569
- function parseApprovalDecision(options) {
1570
- return {
1571
- id: requireArg(options, "id"),
1572
- approver: requireArg(options, "approver"),
1573
- rationale: requireArg(options, "rationale"),
1574
- };
1575
- }
1576
869
  function numberOption(value, fallback) {
1577
870
  if (typeof value !== "string" || value.trim() === "") {
1578
871
  return fallback;
@@ -1709,43 +1002,6 @@ function renderSummaryMarkdown(summary) {
1709
1002
  "",
1710
1003
  ].join("\n");
1711
1004
  }
1712
- function renderUsageReport(report) {
1713
- return [
1714
- `Usage${report.taskId ? ` for ${report.taskId}` : ""}`,
1715
- `- Requests: ${report.totals.requests}`,
1716
- `- Input tokens: ${report.totals.inputTokens}`,
1717
- `- Output tokens: ${report.totals.outputTokens}`,
1718
- `- Total tokens: ${report.totals.totalTokens}`,
1719
- `- Estimated cost USD: ${report.totals.estimatedCostUsd}`,
1720
- "",
1721
- "By role:",
1722
- ...usageLines(report.byRole),
1723
- "",
1724
- "By provider:",
1725
- ...usageLines(report.byProvider),
1726
- ].join("\n");
1727
- }
1728
- function renderBudgetCheck(result) {
1729
- return [
1730
- `Budget check${result.taskId ? ` for ${result.taskId}` : ""}: ${result.passed ? "passed" : "failed"}`,
1731
- `- Applied budgets: ${result.appliedBudgets.length > 0
1732
- ? result.appliedBudgets.join(", ")
1733
- : "none"}`,
1734
- `- Requests: ${result.usage.totals.requests}`,
1735
- `- Total tokens: ${result.usage.totals.totalTokens}`,
1736
- `- Estimated cost USD: ${result.usage.totals.estimatedCostUsd}`,
1737
- "",
1738
- "Violations:",
1739
- ...(result.violations.length === 0
1740
- ? ["- none"]
1741
- : result.violations.map((violation) => `- ${violation.scope} ${violation.metric}: ${violation.actual} > ${violation.limit}`)),
1742
- ].join("\n");
1743
- }
1744
- function usageLines(items) {
1745
- return items.length === 0
1746
- ? ["- none"]
1747
- : items.map((item) => `- ${item.key}: ${item.requests} requests, ${item.totalTokens} tokens, $${item.estimatedCostUsd}`);
1748
- }
1749
1005
  function renderPullRequestSummaryMarkdown(summary) {
1750
1006
  return [
1751
1007
  `# PR Summary: ${summary.task.id}`,