@jterrats/open-orchestra 0.4.0 → 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 (69) hide show
  1. package/CHANGELOG.md +21 -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 -394
  16. package/dist/autonomous-workflow.js.map +1 -1
  17. package/dist/cli.js +13 -1
  18. package/dist/cli.js.map +1 -1
  19. package/dist/command-utils.d.ts +6 -0
  20. package/dist/command-utils.js +19 -0
  21. package/dist/command-utils.js.map +1 -0
  22. package/dist/commands.d.ts +8 -43
  23. package/dist/commands.js +13 -868
  24. package/dist/commands.js.map +1 -1
  25. package/dist/constants.js +9 -0
  26. package/dist/constants.js.map +1 -1
  27. package/dist/defaults.d.ts +11 -0
  28. package/dist/defaults.js +11 -0
  29. package/dist/defaults.js.map +1 -1
  30. package/dist/instruction-commands.d.ts +5 -0
  31. package/dist/instruction-commands.js +98 -0
  32. package/dist/instruction-commands.js.map +1 -0
  33. package/dist/metrics-commands.d.ts +3 -0
  34. package/dist/metrics-commands.js +114 -0
  35. package/dist/metrics-commands.js.map +1 -0
  36. package/dist/model-commands.d.ts +13 -0
  37. package/dist/model-commands.js +199 -0
  38. package/dist/model-commands.js.map +1 -0
  39. package/dist/runtime-adapters.d.ts +5 -1
  40. package/dist/runtime-adapters.js +27 -0
  41. package/dist/runtime-adapters.js.map +1 -1
  42. package/dist/runtime-commands.d.ts +9 -0
  43. package/dist/runtime-commands.js +156 -0
  44. package/dist/runtime-commands.js.map +1 -0
  45. package/dist/runtime-execution-adapters.d.ts +2 -0
  46. package/dist/runtime-execution-adapters.js +163 -0
  47. package/dist/runtime-execution-adapters.js.map +1 -0
  48. package/dist/runtime-execution-renderer.d.ts +10 -0
  49. package/dist/runtime-execution-renderer.js +110 -0
  50. package/dist/runtime-execution-renderer.js.map +1 -0
  51. package/dist/runtime-execution.d.ts +27 -0
  52. package/dist/runtime-execution.js +147 -0
  53. package/dist/runtime-execution.js.map +1 -0
  54. package/dist/skills-commands.d.ts +9 -0
  55. package/dist/skills-commands.js +130 -0
  56. package/dist/skills-commands.js.map +1 -0
  57. package/dist/sprint-commands.d.ts +5 -0
  58. package/dist/sprint-commands.js +120 -0
  59. package/dist/sprint-commands.js.map +1 -0
  60. package/dist/telemetry-commands.d.ts +7 -0
  61. package/dist/telemetry-commands.js +82 -0
  62. package/dist/telemetry-commands.js.map +1 -0
  63. package/dist/tool-commands.d.ts +3 -0
  64. package/dist/tool-commands.js +67 -0
  65. package/dist/tool-commands.js.map +1 -0
  66. package/dist/types.d.ts +79 -0
  67. package/dist/types.js.map +1 -1
  68. package/docs/runtime-llm-flow.md +46 -0
  69. package/package.json +1 -1
package/dist/commands.js CHANGED
@@ -1,31 +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, computeVelocity, recordEstimate, summarizeBenchmark, } from "./benchmark.js";
8
- import { computeSprintBurndown, renderBurndownAscii } from "./burndown.js";
9
- import { calibrationReport, closeSprint, startSprint, velocityTrend, } from "./sprint-metrics.js";
10
5
  import { executePhaseWithLlm } from "./phase-executor.js";
11
- import { SIZING_LABELS } from "./types.js";
12
- import { resolveWorkspaceWritePath } from "./fs-utils.js";
13
- 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";
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";
14
7
  import { validateWorkspace } from "./workspace-validator.js";
15
- import { addAgentLesson, listAgentLessons, listSkills, planSkillsForTask, promoteAgentLessons, readSourceOfTruth, recordSkillPlan, recordSkillRender, renderSkills, validateSkills, } from "./skills.js";
16
8
  import { getWebServerAddress, startWebApiServer } from "./web-api.js";
17
9
  import { decideTaskDelegation } from "./delegation-decision.js";
18
- import { lintMermaidDiagram, recordDiagramLintEvidence, } from "./diagram-validation.js";
19
- import { evaluateMcpOAuthProxy, } from "./mcp-oauth-proxy.js";
20
- import { detectStaleInstructionFilesFromManifest, resolveInstructionImportsFromFile, updateManagedInstructionFile, } from "./instruction-updates.js";
21
- import { applyInstructionUpdatesFromManifest } from "./instruction-apply.js";
22
- import { renderSubagentProtocol, upsertSubagentProtocolBlock, } from "./subagent-protocol.js";
23
10
  import { listCollaborationFlows, recommendCollaborationFlow, } from "./collaboration-flows.js";
24
11
  import { listWorkflowTemplates, renderWorkflowTemplates, selectWorkflowTemplates, validateWorkflowTemplates, } from "./workflow-templates.js";
25
- import { listCommandManifest } from "./command-manifest.js";
26
- import { listRuntimeAdapters, parseRuntimeTarget } from "./runtime-adapters.js";
27
- import { renderRuntimeBootstrap, upsertRuntimeBootstrapBlock, } from "./runtime-bootstrap.js";
28
- 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";
29
21
  import { buildPrBody, createPullRequest } from "./github.js";
30
22
  export async function initCommand(options, io) {
31
23
  const root = stringOption(options["target-dir"]) ?? process.cwd();
@@ -349,56 +341,6 @@ export async function summaryCommand(options, io) {
349
341
  }
350
342
  io.log(renderSummaryMarkdown(summary));
351
343
  }
352
- export async function usageCommand(options, io) {
353
- const report = await getUsageReport(stringOption(options.task));
354
- if (options.json) {
355
- io.log(JSON.stringify(report, null, 2));
356
- return;
357
- }
358
- io.log(renderUsageReport(report));
359
- }
360
- export async function budgetCheckCommand(options, io) {
361
- const result = await checkUsageBudget(stringOption(options.task));
362
- if (options.json) {
363
- io.log(JSON.stringify(result, null, 2));
364
- }
365
- else {
366
- io.log(renderBudgetCheck(result));
367
- }
368
- if (!result.passed) {
369
- throw new Error("budget check failed");
370
- }
371
- }
372
- export async function approvalsListCommand(options, io) {
373
- const approvals = await listApprovals(stringOption(options.task));
374
- if (options.json) {
375
- io.log(JSON.stringify(approvals, null, 2));
376
- return;
377
- }
378
- if (approvals.length === 0) {
379
- io.log("No approvals");
380
- return;
381
- }
382
- for (const approval of approvals) {
383
- io.log(`${approval.id} [${approval.status}] ${approval.taskId ?? "no-task"} ${approval.artifact}`);
384
- }
385
- }
386
- export async function approvalsShowCommand(options, io) {
387
- const approval = await showApproval(requireArg(options, "id"));
388
- if (options.json) {
389
- io.log(JSON.stringify(approval, null, 2));
390
- return;
391
- }
392
- io.log(approval.content);
393
- }
394
- export async function approvalsApproveCommand(options, io) {
395
- const approval = await approveApproval(parseApprovalDecision(options));
396
- io.log(`Approved ${approval.id}`);
397
- }
398
- export async function approvalsRejectCommand(options, io) {
399
- const approval = await rejectApproval(parseApprovalDecision(options));
400
- io.log(`Rejected ${approval.id}`);
401
- }
402
344
  export async function prSummaryCommand(options, io) {
403
345
  const summary = await generatePullRequestSummary(requireArg(options, "task"));
404
346
  if (options.json) {
@@ -460,254 +402,6 @@ export async function playwrightEvidenceCommand(options, io) {
460
402
  }));
461
403
  io.log(`Created ${artifact}`);
462
404
  }
463
- export async function configShowCommand(options, io) {
464
- const config = await getWorkflowConfig();
465
- if (options.json) {
466
- io.log(JSON.stringify(config, null, 2));
467
- return;
468
- }
469
- io.log(`Tools: ${Object.keys(config.tools ?? {}).length}`);
470
- io.log(`Role routes: ${Object.keys(config.providers?.byRole ?? {}).length}`);
471
- }
472
- export async function modelProvidersCommand(options, io) {
473
- const providers = await listConfiguredModelProviders();
474
- if (options.json) {
475
- io.log(JSON.stringify(providers, null, 2));
476
- return;
477
- }
478
- if (providers.length === 0) {
479
- io.log("No configured model providers");
480
- return;
481
- }
482
- for (const provider of providers) {
483
- io.log(`${provider.scope}: ${provider.provider}/${provider.model} (${provider.timeoutMs}ms)`);
484
- }
485
- }
486
- export async function modelSetRoleCommand(options, io) {
487
- const role = requireArg(options, "role");
488
- const routing = parseProviderRouting(options);
489
- await setRoleModelProvider(role, routing);
490
- io.log(`Configured model provider for ${role}`);
491
- }
492
- export async function modelProvenanceAddCommand(options, io) {
493
- const record = await recordModelProvenance({
494
- task: requireArg(options, "task"),
495
- role: requireArg(options, "role"),
496
- provider: requireArg(options, "provider"),
497
- model: requireArg(options, "model"),
498
- promptId: requireArg(options, "prompt-id"),
499
- responseId: requireArg(options, "response-id"),
500
- inputTokens: numberOption(options["input-tokens"], 0),
501
- outputTokens: numberOption(options["output-tokens"], 0),
502
- estimatedCostUsd: numberOption(options["estimated-cost-usd"], 0),
503
- finishReason: requireArg(options, "finish-reason"),
504
- });
505
- io.log(`Recorded model provenance ${record.responseId}`);
506
- }
507
- export async function modelProvenanceListCommand(options, io) {
508
- const records = await listModelProvenance(stringOption(options.task));
509
- if (options.json) {
510
- io.log(JSON.stringify(records, null, 2));
511
- return;
512
- }
513
- if (records.length === 0) {
514
- io.log("No model provenance");
515
- return;
516
- }
517
- for (const record of records) {
518
- io.log(`${record.task} ${record.role} ${record.provider}/${record.model} ${record.responseId}`);
519
- }
520
- }
521
- export async function modelCompleteFakeCommand(options, io) {
522
- const result = await completeWithProviderFallback({
523
- provider: requireArg(options, "provider"),
524
- model: requireArg(options, "model"),
525
- fallbacks: parseCsv(options.fallbacks),
526
- maxTokens: 0,
527
- maxCostUsd: 0,
528
- timeoutMs: 30000,
529
- retries: 0,
530
- requiredCapabilities: [],
531
- }, requireArg(options, "prompt"), removeUndefined({
532
- failingProviders: parseCsv(options["fail-provider"]),
533
- taskId: stringOption(options.task),
534
- role: stringOption(options.role) ?? "parent",
535
- }));
536
- if (options.json) {
537
- io.log(JSON.stringify(result, null, 2));
538
- return;
539
- }
540
- io.log(`Fake completion used ${result.provider}/${result.model} fallback=${String(result.fallbackUsed)}`);
541
- }
542
- export async function telemetryStatusCommand(options, io) {
543
- const telemetry = await getTelemetryConsent();
544
- if (options.json) {
545
- io.log(JSON.stringify(telemetry, null, 2));
546
- return;
547
- }
548
- io.log("Telemetry: " + (telemetry.enabled ? "enabled" : "off"));
549
- io.log("Level: " + telemetry.level);
550
- io.log("Policy: " + telemetry.policyVersion);
551
- }
552
- export async function telemetryEnableCommand(options, io) {
553
- const level = parseTelemetryLevel(stringOption(options.level) ?? "metadata");
554
- if (requiresSensitiveTelemetryOptIn(level) && !options["confirm-sensitive"]) {
555
- throw new Error("prompt-sample and eval-dataset telemetry require --confirm-sensitive");
556
- }
557
- const telemetry = await enableTelemetryConsent({
558
- level,
559
- actor: stringOption(options.actor) ?? "user",
560
- });
561
- if (options.json) {
562
- io.log(JSON.stringify(telemetry, null, 2));
563
- return;
564
- }
565
- io.log("Telemetry enabled at level " + telemetry.level);
566
- }
567
- export async function telemetryExportCommand(options, io) {
568
- const result = await exportTelemetryDataset({
569
- dryRun: Boolean(options["dry-run"]),
570
- });
571
- if (options.json) {
572
- io.log(JSON.stringify(result, null, 2));
573
- return;
574
- }
575
- io.log((result.dryRun ? "Telemetry export dry run" : "Telemetry exported") +
576
- ": " +
577
- result.recordCount +
578
- " record(s)");
579
- if (result.file) {
580
- io.log("File: " + result.file);
581
- }
582
- }
583
- export async function telemetrySubmitCommand(options, io) {
584
- const result = await submitTelemetryDataset({
585
- file: requireArg(options, "file"),
586
- endpoint: requireArg(options, "endpoint"),
587
- });
588
- if (options.json) {
589
- io.log(JSON.stringify(result, null, 2));
590
- return;
591
- }
592
- io.log("Telemetry submission recorded: " + result.fileHash);
593
- }
594
- export async function telemetryEvalDatasetCommand(options, io) {
595
- const result = await exportTelemetryEvalDataset({
596
- dryRun: Boolean(options["dry-run"]),
597
- });
598
- if (options.json) {
599
- io.log(JSON.stringify(result, null, 2));
600
- return;
601
- }
602
- io.log((result.dryRun ? "Eval dataset dry run" : "Eval dataset exported") +
603
- ": " +
604
- result.recordCount +
605
- " record(s)");
606
- if (result.file) {
607
- io.log("File: " + result.file);
608
- }
609
- }
610
- export async function telemetryDisableCommand(options, io) {
611
- const telemetry = await disableTelemetryConsent({
612
- actor: stringOption(options.actor) ?? "user",
613
- });
614
- if (options.json) {
615
- io.log(JSON.stringify(telemetry, null, 2));
616
- return;
617
- }
618
- io.log("Telemetry disabled");
619
- }
620
- export async function instructionsApplyCommand(options, io) {
621
- const report = await applyInstructionUpdatesFromManifest({
622
- manifestPath: requireArg(options, "manifest"),
623
- check: Boolean(options.check),
624
- dryRun: Boolean(options["dry-run"]),
625
- force: Boolean(options.force),
626
- });
627
- if (options.json) {
628
- io.log(JSON.stringify(report, null, 2));
629
- return;
630
- }
631
- io.log("Instruction update " +
632
- report.mode +
633
- ": " +
634
- report.totals.changed +
635
- " changed, " +
636
- report.totals.unchanged +
637
- " unchanged, " +
638
- report.totals.blocked +
639
- " blocked");
640
- for (const item of report.items) {
641
- io.log(item.status +
642
- " " +
643
- item.filePath +
644
- "#" +
645
- item.blockId +
646
- " - " +
647
- item.reason);
648
- }
649
- }
650
- export async function instructionsStaleCommand(options, io) {
651
- const report = await detectStaleInstructionFilesFromManifest({
652
- manifestPath: requireArg(options, "manifest"),
653
- });
654
- if (options.json) {
655
- io.log(JSON.stringify(report, null, 2));
656
- return;
657
- }
658
- io.log("Instruction file status:");
659
- for (const item of report.items) {
660
- io.log(item.status +
661
- " " +
662
- item.filePath +
663
- "#" +
664
- item.blockId +
665
- " - " +
666
- item.reason);
667
- }
668
- }
669
- export async function instructionsBlockCommand(options, io) {
670
- const result = await updateManagedInstructionFile({
671
- filePath: resolveWorkspaceWritePath(process.cwd(), requireArg(options, "file")),
672
- contentPath: resolveWorkspaceWritePath(process.cwd(), requireArg(options, "content-file")),
673
- input: {
674
- blockId: requireArg(options, "block"),
675
- generator: stringOption(options.generator) ?? "open-orchestra",
676
- version: stringOption(options.version) ?? "1",
677
- target: parseInstructionTarget(stringOption(options.target) ?? "generic"),
678
- sourceManifest: stringOption(options["source-manifest"]) ?? "local",
679
- },
680
- check: Boolean(options.check),
681
- dryRun: Boolean(options["dry-run"]),
682
- force: Boolean(options.force),
683
- });
684
- if (options.json) {
685
- io.log(JSON.stringify(result, null, 2));
686
- return;
687
- }
688
- if (result.drift) {
689
- io.log("Drift detected for " + requireArg(options, "block"));
690
- return;
691
- }
692
- io.log(result.changed
693
- ? "Instruction block changed"
694
- : "Instruction block unchanged");
695
- }
696
- export async function instructionsImportsCommand(options, io) {
697
- const result = await resolveInstructionImportsFromFile({
698
- registryPath: requireArg(options, "registry"),
699
- entryId: requireArg(options, "entry"),
700
- target: parseInstructionTarget(stringOption(options.target) ?? "generic"),
701
- });
702
- if (options.json) {
703
- io.log(JSON.stringify(result, null, 2));
704
- return;
705
- }
706
- io.log(result.content);
707
- }
708
- function parseInstructionTarget(value) {
709
- return parseRuntimeTarget(value);
710
- }
711
405
  export async function collaborationFlowsCommand(options, io) {
712
406
  const flows = listCollaborationFlows();
713
407
  if (options.json) {
@@ -788,283 +482,6 @@ export async function workflowTemplateRenderCommand(options, io) {
788
482
  }
789
483
  io.log(rendered.content);
790
484
  }
791
- export async function commandsManifestCommand(options, io) {
792
- const manifest = listCommandManifest();
793
- if (options.json) {
794
- io.log(JSON.stringify(manifest, null, 2));
795
- return;
796
- }
797
- for (const command of manifest) {
798
- io.log(command.command + " - " + command.summary);
799
- }
800
- }
801
- export async function runtimeAdaptersCommand(options, io) {
802
- const adapters = listRuntimeAdapters();
803
- if (options.json) {
804
- io.log(JSON.stringify(adapters, null, 2));
805
- return;
806
- }
807
- for (const adapter of adapters) {
808
- io.log(adapter.target +
809
- " - " +
810
- adapter.label +
811
- " (" +
812
- adapter.defaultInstructionFiles.join(", ") +
813
- ")");
814
- }
815
- }
816
- export async function runtimeBootstrapCommand(options, io) {
817
- const target = parseSkillRenderTarget(stringOption(options.target) ?? "generic");
818
- const bootstrap = renderRuntimeBootstrap(target);
819
- const filePath = stringOption(options.file);
820
- if (filePath) {
821
- const resolvedFilePath = resolveWorkspaceWritePath(process.cwd(), path.normalize(filePath));
822
- const existing = await readFile(resolvedFilePath, "utf8").catch(() => "");
823
- const result = upsertRuntimeBootstrapBlock(existing, bootstrap, {
824
- force: Boolean(options.force),
825
- });
826
- if (!options.check && !options["dry-run"] && !result.drift) {
827
- await writeFile(resolvedFilePath, result.content);
828
- }
829
- if (options.json) {
830
- io.log(JSON.stringify(result, null, 2));
831
- return;
832
- }
833
- io.log(result.changed
834
- ? "Runtime bootstrap changed"
835
- : "Runtime bootstrap unchanged");
836
- return;
837
- }
838
- if (options.json) {
839
- io.log(JSON.stringify(bootstrap, null, 2));
840
- return;
841
- }
842
- io.log(bootstrap.content);
843
- }
844
- export async function protocolRenderCommand(options, io) {
845
- const protocol = await renderSubagentProtocol(removeUndefined({
846
- target: parseSkillRenderTarget(stringOption(options.target) ?? "generic"),
847
- taskId: stringOption(options.task),
848
- }));
849
- if (options.json) {
850
- io.log(JSON.stringify(protocol, null, 2));
851
- return;
852
- }
853
- io.log(protocol.content);
854
- }
855
- export async function protocolBlockCommand(options, io) {
856
- const filePath = requireArg(options, "file");
857
- const protocol = await renderSubagentProtocol(removeUndefined({
858
- target: parseSkillRenderTarget(stringOption(options.target) ?? "generic"),
859
- taskId: stringOption(options.task),
860
- }));
861
- const existing = await readFile(filePath, "utf8").catch(() => "");
862
- const result = upsertSubagentProtocolBlock(existing, protocol, {
863
- force: Boolean(options.force),
864
- });
865
- const shouldWrite = !options.check && !options["dry-run"] && !result.drift && result.changed;
866
- if (shouldWrite) {
867
- await writeFile(filePath, result.content);
868
- }
869
- if (options.json) {
870
- io.log(JSON.stringify(result, null, 2));
871
- return;
872
- }
873
- if (result.drift) {
874
- io.log("Drift detected for subagent protocol block");
875
- return;
876
- }
877
- io.log(result.changed
878
- ? "Subagent protocol block changed"
879
- : "Subagent protocol block unchanged");
880
- }
881
- export async function diagramsLintCommand(options, io) {
882
- const result = await lintMermaidDiagram({
883
- filePath: requireArg(options, "file"),
884
- });
885
- const evidence = stringOption(options.task)
886
- ? await recordDiagramLintEvidence({
887
- taskId: stringOption(options.task) ?? "",
888
- result,
889
- })
890
- : undefined;
891
- const output = removeUndefined({ result, evidence });
892
- if (options.json) {
893
- io.log(JSON.stringify(output, null, 2));
894
- }
895
- else {
896
- io.log(result.valid ? "Mermaid diagram valid" : "Mermaid diagram invalid");
897
- if (result.installHint) {
898
- io.log(result.installHint);
899
- }
900
- if (evidence) {
901
- io.log("Created " + evidence.artifact);
902
- }
903
- }
904
- if (!result.valid) {
905
- throw new Error("diagram lint failed");
906
- }
907
- }
908
- export async function mcpOAuthProxyEvaluateCommand(options, io) {
909
- const evaluation = evaluateMcpOAuthProxy(removeUndefined({
910
- enabled: Boolean(options.enable),
911
- mode: parseMcpProxyMode(stringOption(options.mode) ?? "stdio-proxy"),
912
- serverUrl: requireArg(options, "server-url"),
913
- tokenStorage: parseMcpSecretStorage(stringOption(options["token-storage"]) ?? "keychain"),
914
- tokenPath: stringOption(options["token-path"]),
915
- refreshWindowSeconds: Number(stringOption(options["refresh-window"]) ?? 300),
916
- approvedBy: options.approve
917
- ? (stringOption(options.approver) ?? "local-user")
918
- : undefined,
919
- }));
920
- if (options.json) {
921
- io.log(JSON.stringify(evaluation, null, 2));
922
- return;
923
- }
924
- io.log(evaluation.approved
925
- ? "MCP proxy plan approved"
926
- : "MCP proxy plan has risks");
927
- for (const risk of evaluation.risks) {
928
- io.log("RISK: " + risk);
929
- }
930
- }
931
- function parseMcpProxyMode(value) {
932
- if (["stdio-proxy", "direct-http", "tool-native-oauth"].includes(value)) {
933
- return value;
934
- }
935
- throw new Error("unknown MCP proxy mode: " + value);
936
- }
937
- function parseMcpSecretStorage(value) {
938
- if (["keychain", "libsecret", "windows-credential", "secure-file"].includes(value)) {
939
- return value;
940
- }
941
- throw new Error("unknown MCP secret storage: " + value);
942
- }
943
- export async function skillsValidateCommand(options, io) {
944
- const report = await validateSkills();
945
- if (options.json) {
946
- io.log(JSON.stringify(report, null, 2));
947
- }
948
- else {
949
- io.log(report.valid ? "Skills valid" : "Skills invalid");
950
- for (const error of report.errors) {
951
- io.log(`ERROR: ${error}`);
952
- }
953
- for (const warning of report.warnings) {
954
- io.log(`WARN: ${warning}`);
955
- }
956
- }
957
- if (!report.valid) {
958
- throw new Error("skills validation failed");
959
- }
960
- }
961
- export async function sourcesListCommand(options, io) {
962
- const sources = await readSourceOfTruth();
963
- if (options.json) {
964
- io.log(JSON.stringify(sources, null, 2));
965
- return;
966
- }
967
- for (const source of sources) {
968
- io.log(`${source.id ?? "unknown"} - ${source.name ?? "Unnamed source"}`);
969
- io.log(` ${(source.locations ?? []).join(", ")}`);
970
- }
971
- }
972
- export async function lessonsListCommand(options, io) {
973
- const lessons = await listAgentLessons();
974
- if (options.json) {
975
- io.log(JSON.stringify(lessons, null, 2));
976
- return;
977
- }
978
- if (lessons.length === 0) {
979
- io.log("No lessons");
980
- return;
981
- }
982
- for (const lesson of lessons) {
983
- io.log(`${lesson.timestamp} ${lesson.operation}: ${lesson.errorSignature}`);
984
- }
985
- }
986
- export async function lessonsAddCommand(options, io) {
987
- const lesson = await addAgentLesson(removeUndefined({
988
- taskId: stringOption(options.task),
989
- actor: stringOption(options.actor) ?? "parent",
990
- operation: requireArg(options, "operation"),
991
- failedAction: requireArg(options, "failed-action"),
992
- errorSignature: requireArg(options, "error-signature"),
993
- rootCause: requireArg(options, "root-cause"),
994
- fix: requireArg(options, "fix"),
995
- prevention: requireArg(options, "prevention"),
996
- appliesTo: parseCsv(options["applies-to"]),
997
- verifiedBy: parseCsv(options["verified-by"]),
998
- }));
999
- if (options.json) {
1000
- io.log(JSON.stringify(lesson, null, 2));
1001
- return;
1002
- }
1003
- io.log(`Recorded lesson ${lesson.errorSignature}`);
1004
- }
1005
- export async function lessonsPromoteCommand(options, io) {
1006
- const result = await promoteAgentLessons(removeUndefined({
1007
- to: parsePromotionTarget(stringOption(options.to) ?? "doc"),
1008
- filter: stringOption(options.filter),
1009
- }));
1010
- if (options.json) {
1011
- io.log(JSON.stringify(result, null, 2));
1012
- return;
1013
- }
1014
- io.log(`Created ${result.artifact}`);
1015
- io.log(`Promoted lessons: ${result.lessons.length}`);
1016
- }
1017
- function parsePromotionTarget(value) {
1018
- if (["skill", "rule", "doc"].includes(value)) {
1019
- return value;
1020
- }
1021
- throw new Error(`unknown promotion target: ${value}`);
1022
- }
1023
- export async function skillsListCommand(options, io) {
1024
- const skills = listSkills();
1025
- if (options.json) {
1026
- io.log(JSON.stringify(skills, null, 2));
1027
- return;
1028
- }
1029
- for (const skill of skills) {
1030
- io.log(`${skill.id} - ${skill.name} (${skill.loadBudget})`);
1031
- io.log(` ${skill.summary}`);
1032
- io.log(` sources: ${skill.sourceGroups.join(", ")}`);
1033
- }
1034
- }
1035
- export async function skillsPlanCommand(options, io) {
1036
- const plan = await planSkillsForTask(requireArg(options, "task"));
1037
- await recordSkillPlan(plan);
1038
- if (options.json) {
1039
- io.log(JSON.stringify(plan, null, 2));
1040
- return;
1041
- }
1042
- io.log(`Skills for ${plan.taskId}:`);
1043
- for (const item of plan.selected) {
1044
- io.log(` ${item.skill.id} (score ${item.score})`);
1045
- for (const reason of item.rationale) {
1046
- io.log(` - ${reason}`);
1047
- }
1048
- }
1049
- io.log(`Source groups: ${plan.sourceGroups.join(", ")}`);
1050
- }
1051
- export async function skillsRenderCommand(options, io) {
1052
- const target = parseSkillRenderTarget(stringOption(options.target) ?? "generic");
1053
- const rendered = await renderSkills({
1054
- target,
1055
- taskId: stringOption(options.task),
1056
- skillIds: parseCsv(options.skills),
1057
- });
1058
- await recordSkillRender(rendered);
1059
- if (options.json) {
1060
- io.log(JSON.stringify(rendered, null, 2));
1061
- return;
1062
- }
1063
- io.log(rendered.content);
1064
- }
1065
- function parseSkillRenderTarget(value) {
1066
- return parseRuntimeTarget(value);
1067
- }
1068
485
  // --- Autonomous workflow commands ---
1069
486
  const GATE_MODES = ["none", "phase", "all"];
1070
487
  export async function workflowRunCommand(options, io) {
@@ -1374,225 +791,6 @@ async function workflowDryRun(options, io, taskId, gates, maxIterations) {
1374
791
  }, null, 2));
1375
792
  }
1376
793
  }
1377
- // --- Estimate & Benchmark commands ---
1378
- const CONFIDENCE_LEVELS = ["low", "medium", "high"];
1379
- export async function estimateCommand(options, io) {
1380
- const cwd = process.cwd();
1381
- const taskId = requireArg(options, "task");
1382
- const sizingRaw = requireArg(options, "sizing");
1383
- const soloDays = numberOption(options["solo-days"], 0);
1384
- const aiDays = numberOption(options["ai-unguided-days"], 0);
1385
- const confidence = (stringOption(options.confidence) ??
1386
- "medium");
1387
- const declaredBy = stringOption(options["declared-by"]) ?? "pm";
1388
- if (!SIZING_LABELS.includes(sizingRaw)) {
1389
- throw new Error(`--sizing must be one of: ${SIZING_LABELS.join(", ")}`);
1390
- }
1391
- if (!CONFIDENCE_LEVELS.includes(confidence)) {
1392
- throw new Error(`--confidence must be one of: ${CONFIDENCE_LEVELS.join(", ")}`);
1393
- }
1394
- if (soloDays <= 0)
1395
- throw new Error(`--solo-days must be > 0`);
1396
- if (aiDays <= 0)
1397
- throw new Error(`--ai-unguided-days must be > 0`);
1398
- const record = await recordEstimate(cwd, {
1399
- taskId,
1400
- sizingLabel: sizingRaw,
1401
- soloEstimateDays: soloDays,
1402
- aiUnguidedEstimateDays: aiDays,
1403
- confidence,
1404
- declaredBy,
1405
- });
1406
- if (options.json) {
1407
- io.log(JSON.stringify(record, null, 2));
1408
- return;
1409
- }
1410
- io.log(`Estimate recorded for ${taskId} [${record.id}]`);
1411
- io.log(` Sizing: ${record.sizingLabel}`);
1412
- io.log(` Solo: ${record.soloEstimateDays}d`);
1413
- io.log(` AI-unguided: ${record.aiUnguidedEstimateDays}d`);
1414
- io.log(` Confidence: ${record.confidence}`);
1415
- io.log(` Declared by: ${record.declaredBy}`);
1416
- }
1417
- export async function benchmarkCommand(options, io) {
1418
- const cwd = process.cwd();
1419
- if (options.summary) {
1420
- const summary = await summarizeBenchmark(cwd);
1421
- if (options.json) {
1422
- io.log(JSON.stringify(summary, null, 2));
1423
- return;
1424
- }
1425
- if (summary.stories.length === 0) {
1426
- io.log("No estimates recorded yet. Use: orchestra estimate --task <id> ...");
1427
- return;
1428
- }
1429
- 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"}`;
1430
- io.log(header);
1431
- io.log("─".repeat(header.length));
1432
- for (const s of summary.stories) {
1433
- const actual = s.actualDays !== null ? `${s.actualDays}d` : "pending";
1434
- const vsSolo = s.vsSoloPct !== null
1435
- ? `${s.vsSoloPct > 0 ? "+" : ""}${s.vsSoloPct}%`
1436
- : "—";
1437
- const vsAi = s.vsAiUnguidedPct !== null
1438
- ? `${s.vsAiUnguidedPct > 0 ? "+" : ""}${s.vsAiUnguidedPct}%`
1439
- : "—";
1440
- 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}`);
1441
- }
1442
- if (summary.totalWithActuals > 0) {
1443
- io.log("");
1444
- io.log(`Avg savings vs solo: ${summary.avgVsSoloPct}%`);
1445
- io.log(`Avg savings vs AI-unguided: ${summary.avgVsAiUnguidedPct}%`);
1446
- io.log(`Stories with actuals: ${summary.totalWithActuals}/${summary.stories.length}`);
1447
- }
1448
- return;
1449
- }
1450
- const taskId = requireArg(options, "task");
1451
- const result = await computeBenchmark(cwd, taskId);
1452
- if (options.json) {
1453
- io.log(JSON.stringify(result, null, 2));
1454
- return;
1455
- }
1456
- io.log(`Benchmark: ${taskId} [${result.status}]`);
1457
- io.log(` Sizing: ${result.sizingLabel}`);
1458
- io.log(` Solo: ${result.soloEstimateDays}d (declared)`);
1459
- io.log(` AI-unguided: ${result.aiUnguidedEstimateDays}d (declared)`);
1460
- io.log(` Actual: ${result.actualDays !== null ? `${result.actualDays}d` : "pending — run not complete"}`);
1461
- if (result.vsSoloPct !== null) {
1462
- const sign = result.vsSoloPct > 0 ? "+" : "";
1463
- io.log(` vs Solo: ${sign}${result.vsSoloPct}%`);
1464
- io.log(` vs AI: ${result.vsAiUnguidedPct !== null ? `${result.vsAiUnguidedPct > 0 ? "+" : ""}${result.vsAiUnguidedPct}%` : "—"}`);
1465
- }
1466
- io.log(` QA loops: ${result.qaIterations}`);
1467
- io.log(` Reviews: ${result.quality.reviewCount} (${result.quality.blockingReviews} blocking)`);
1468
- io.log(` Evidence: ${result.quality.evidenceCount} artifacts`);
1469
- io.log(` Gate blocks: ${result.quality.gateBlockCount}`);
1470
- io.log(` Lessons: ${result.quality.lessonCount}`);
1471
- if (result.quality.totalInputTokens > 0 ||
1472
- result.quality.totalOutputTokens > 0) {
1473
- io.log(` Tokens: ${result.quality.totalInputTokens}in / ${result.quality.totalOutputTokens}out`);
1474
- io.log(` Cost: $${result.quality.estimatedCostUsd}`);
1475
- }
1476
- }
1477
- // --- Velocity command ---
1478
- export async function velocityCommand(options, io) {
1479
- const cwd = process.cwd();
1480
- if (typeof options.sprints === "string") {
1481
- const limit = Number(options.sprints);
1482
- if (!Number.isInteger(limit) || limit <= 0) {
1483
- throw new Error("--sprints must be a positive integer");
1484
- }
1485
- const report = await velocityTrend(cwd, limit);
1486
- if (options.json) {
1487
- io.log(JSON.stringify(report, null, 2));
1488
- return;
1489
- }
1490
- io.log(`Sprint Velocity Report`);
1491
- io.log(` Trend: ${report.trend}`);
1492
- for (const sprint of report.sprints) {
1493
- io.log(` ${sprint.sprintId} ${sprint.storiesCompleted} stories ${sprint.pointsCompleted} pts`);
1494
- }
1495
- return;
1496
- }
1497
- const report = await computeVelocity(cwd);
1498
- if (options.json) {
1499
- io.log(JSON.stringify(report, null, 2));
1500
- return;
1501
- }
1502
- io.log(`Velocity Report`);
1503
- io.log(` Stories completed: ${report.totalStoriesCompleted}`);
1504
- io.log(` Points completed: ${report.totalPointsCompleted}`);
1505
- io.log(` Avg actual days: ${report.avgActualDaysPerStory !== null ? report.avgActualDaysPerStory + "d" : "n/a"}`);
1506
- io.log(` vs Solo estimate: ${report.avgVsSoloPct !== null ? report.avgVsSoloPct + "%" : "n/a"}`);
1507
- io.log(` vs AI estimate: ${report.avgVsAiUnguidedPct !== null ? report.avgVsAiUnguidedPct + "%" : "n/a"}`);
1508
- io.log(` Trend: ${report.trend}`);
1509
- if (report.weeks.length > 0) {
1510
- io.log(`\nWeekly breakdown:`);
1511
- for (const week of report.weeks) {
1512
- io.log(` ${week.week} ${week.storiesCompleted} stories ${week.pointsCompleted} pts`);
1513
- }
1514
- }
1515
- }
1516
- // --- Sprint command ---
1517
- export async function sprintCommand(subcommand, options, io) {
1518
- const cwd = process.cwd();
1519
- if (subcommand === "start") {
1520
- const id = requireArg(options, "id");
1521
- const taskIds = requireArg(options, "tasks")
1522
- .split(",")
1523
- .map((taskId) => taskId.trim())
1524
- .filter(Boolean);
1525
- const sprint = await startSprint(cwd, id, taskIds);
1526
- if (options.json) {
1527
- io.log(JSON.stringify(sprint, null, 2));
1528
- return;
1529
- }
1530
- io.log(`Sprint started: ${sprint.id} (${sprint.taskIds.length} tasks)`);
1531
- return;
1532
- }
1533
- if (subcommand === "close") {
1534
- const id = requireArg(options, "id");
1535
- const sprint = await closeSprint(cwd, id);
1536
- if (options.json) {
1537
- io.log(JSON.stringify(sprint, null, 2));
1538
- return;
1539
- }
1540
- io.log(`Sprint closed: ${sprint.id}`);
1541
- return;
1542
- }
1543
- throw new Error(`unknown sprint command: ${subcommand ?? ""}`.trim());
1544
- }
1545
- // --- Calibration command ---
1546
- export async function calibrationCommand(options, io) {
1547
- const report = await calibrationReport(process.cwd());
1548
- const roleFilter = typeof options.role === "string" ? options.role : null;
1549
- const filtered = roleFilter
1550
- ? { roles: report.roles.filter((role) => role.role === roleFilter) }
1551
- : report;
1552
- if (options.json) {
1553
- io.log(JSON.stringify(filtered, null, 2));
1554
- return;
1555
- }
1556
- io.log(`Calibration Report`);
1557
- for (const role of filtered.roles) {
1558
- io.log(` ${role.role} stories=${role.stories} declared=${role.avgDeclaredDays}d actual=${role.avgActualDays}d error=${role.avgErrorPct}% bias=${role.bias}`);
1559
- }
1560
- }
1561
- // --- Burndown command ---
1562
- export async function burndownCommand(options, io) {
1563
- const cwd = process.cwd();
1564
- const sprintCsv = requireArg(options, "sprint");
1565
- const sprintTaskIds = sprintCsv
1566
- .split(",")
1567
- .map((s) => s.trim())
1568
- .filter(Boolean);
1569
- if (sprintTaskIds.length === 0)
1570
- throw new Error(`--sprint requires at least one task id`);
1571
- const series = await computeSprintBurndown(cwd, sprintTaskIds);
1572
- if (options.json) {
1573
- io.log(JSON.stringify(series, null, 2));
1574
- return;
1575
- }
1576
- for (const w of series.warnings)
1577
- io.log(`⚠ ${w}`);
1578
- if (series.totalPoints === 0) {
1579
- io.log("No points to chart — record estimates first with: orchestra estimate --task <id> ...");
1580
- return;
1581
- }
1582
- io.log(`Sprint burndown total=${series.totalPoints} pts tasks=${series.sprintTaskIds.length}`);
1583
- io.log("");
1584
- io.log(renderBurndownAscii(series));
1585
- io.log("");
1586
- io.log("Task breakdown:");
1587
- for (const t of series.taskBreakdown) {
1588
- const arch = t.architectPoints !== null ? `arch=${t.architectPoints}` : "arch=—";
1589
- const dev = t.developerPoints !== null ? `dev=${t.developerPoints}` : "dev=—";
1590
- const done = t.completedAt
1591
- ? `done ${t.completedAt.slice(0, 10)}`
1592
- : "pending";
1593
- io.log(` ${t.taskId.padEnd(14)} ${arch.padEnd(10)} ${dev.padEnd(10)} resolved=${t.resolvedPoints} ${done}`);
1594
- }
1595
- }
1596
794
  function parseRuntimeTargetOptions(options) {
1597
795
  const targetValues = [
1598
796
  ...parseCsv(options.target),
@@ -1600,6 +798,9 @@ function parseRuntimeTargetOptions(options) {
1600
798
  ];
1601
799
  return [...new Set(targetValues)].map((target) => parseRuntimeTarget(target));
1602
800
  }
801
+ function parseSkillRenderTarget(value) {
802
+ return parseRuntimeTarget(value);
803
+ }
1603
804
  function parseCsv(value) {
1604
805
  if (typeof value !== "string" || value.trim() === "") {
1605
806
  return [];
@@ -1648,18 +849,6 @@ function parseRiskAcceptance(options) {
1648
849
  });
1649
850
  return Object.keys(acceptance).length > 0 ? acceptance : undefined;
1650
851
  }
1651
- function parseProviderRouting(options) {
1652
- return {
1653
- provider: requireArg(options, "provider"),
1654
- model: requireArg(options, "model"),
1655
- fallbacks: parseCsv(options.fallbacks),
1656
- maxTokens: numberOption(options["max-tokens"], 0),
1657
- maxCostUsd: numberOption(options["max-cost-usd"], 0),
1658
- timeoutMs: numberOption(options["timeout-ms"], 30000),
1659
- retries: numberOption(options.retries, 0),
1660
- requiredCapabilities: parseCsv(options.capabilities),
1661
- };
1662
- }
1663
852
  function parseBudgetEscalationDecision(options) {
1664
853
  if (options["approve-budget-fallback"]) {
1665
854
  return removeUndefined({
@@ -1677,13 +866,6 @@ function parseBudgetEscalationDecision(options) {
1677
866
  }
1678
867
  return undefined;
1679
868
  }
1680
- function parseApprovalDecision(options) {
1681
- return {
1682
- id: requireArg(options, "id"),
1683
- approver: requireArg(options, "approver"),
1684
- rationale: requireArg(options, "rationale"),
1685
- };
1686
- }
1687
869
  function numberOption(value, fallback) {
1688
870
  if (typeof value !== "string" || value.trim() === "") {
1689
871
  return fallback;
@@ -1820,43 +1002,6 @@ function renderSummaryMarkdown(summary) {
1820
1002
  "",
1821
1003
  ].join("\n");
1822
1004
  }
1823
- function renderUsageReport(report) {
1824
- return [
1825
- `Usage${report.taskId ? ` for ${report.taskId}` : ""}`,
1826
- `- Requests: ${report.totals.requests}`,
1827
- `- Input tokens: ${report.totals.inputTokens}`,
1828
- `- Output tokens: ${report.totals.outputTokens}`,
1829
- `- Total tokens: ${report.totals.totalTokens}`,
1830
- `- Estimated cost USD: ${report.totals.estimatedCostUsd}`,
1831
- "",
1832
- "By role:",
1833
- ...usageLines(report.byRole),
1834
- "",
1835
- "By provider:",
1836
- ...usageLines(report.byProvider),
1837
- ].join("\n");
1838
- }
1839
- function renderBudgetCheck(result) {
1840
- return [
1841
- `Budget check${result.taskId ? ` for ${result.taskId}` : ""}: ${result.passed ? "passed" : "failed"}`,
1842
- `- Applied budgets: ${result.appliedBudgets.length > 0
1843
- ? result.appliedBudgets.join(", ")
1844
- : "none"}`,
1845
- `- Requests: ${result.usage.totals.requests}`,
1846
- `- Total tokens: ${result.usage.totals.totalTokens}`,
1847
- `- Estimated cost USD: ${result.usage.totals.estimatedCostUsd}`,
1848
- "",
1849
- "Violations:",
1850
- ...(result.violations.length === 0
1851
- ? ["- none"]
1852
- : result.violations.map((violation) => `- ${violation.scope} ${violation.metric}: ${violation.actual} > ${violation.limit}`)),
1853
- ].join("\n");
1854
- }
1855
- function usageLines(items) {
1856
- return items.length === 0
1857
- ? ["- none"]
1858
- : items.map((item) => `- ${item.key}: ${item.requests} requests, ${item.totalTokens} tokens, $${item.estimatedCostUsd}`);
1859
- }
1860
1005
  function renderPullRequestSummaryMarkdown(summary) {
1861
1006
  return [
1862
1007
  `# PR Summary: ${summary.task.id}`,