cool-workflow 0.1.85 → 0.1.86

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 (58) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +47 -333
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/architecture-review-fast/workflow.js +2 -0
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/capability-core.js +131 -3
  12. package/dist/capability-registry.js +84 -50
  13. package/dist/cli/command-surface.js +3 -0
  14. package/dist/doctor.js +37 -1
  15. package/dist/mcp/tool-call.js +428 -0
  16. package/dist/mcp/tool-definitions.js +1027 -0
  17. package/dist/mcp-surface.js +5 -1442
  18. package/dist/onramp.js +421 -0
  19. package/dist/orchestrator.js +20 -15
  20. package/dist/version.js +1 -1
  21. package/dist/workbench-host.js +1 -8
  22. package/docs/agent-delegation-drive.7.md +21 -2
  23. package/docs/cli-mcp-parity.7.md +29 -4
  24. package/docs/contract-migration-tooling.7.md +2 -0
  25. package/docs/control-plane-scheduling.7.md +2 -0
  26. package/docs/dogfood-one-real-repo.7.md +9 -1
  27. package/docs/durable-state-and-locking.7.md +2 -0
  28. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  29. package/docs/execution-backends.7.md +2 -0
  30. package/docs/getting-started.md +40 -2
  31. package/docs/index.md +51 -38
  32. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  33. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  34. package/docs/multi-agent-operator-ux.7.md +2 -0
  35. package/docs/node-snapshot-diff-replay.7.md +2 -0
  36. package/docs/observability-cost-accounting.7.md +2 -0
  37. package/docs/project-index.md +18 -7
  38. package/docs/real-execution-backends.7.md +2 -0
  39. package/docs/release-and-migration.7.md +3 -1
  40. package/docs/release-history.md +342 -0
  41. package/docs/release-tooling.7.md +16 -0
  42. package/docs/run-registry-control-plane.7.md +2 -0
  43. package/docs/run-retention-reclamation.7.md +2 -0
  44. package/docs/state-explosion-management.7.md +2 -0
  45. package/docs/team-collaboration.7.md +2 -0
  46. package/docs/web-desktop-workbench.7.md +2 -0
  47. package/manifest/plugin.manifest.json +1 -1
  48. package/manifest/source-context-profiles.json +1 -1
  49. package/package.json +2 -1
  50. package/scripts/architecture-review-fast.js +44 -2
  51. package/scripts/canonical-apps.js +4 -4
  52. package/scripts/dogfood-release.js +55 -133
  53. package/scripts/golden-path.js +4 -4
  54. package/scripts/onramp-check.js +47 -0
  55. package/scripts/parity-check.js +740 -2
  56. package/scripts/release-check.js +2 -0
  57. package/scripts/source-context.js +31 -5
  58. package/scripts/version-sync-check.js +4 -2
@@ -179,8 +179,8 @@ function compactOperatorStatus(status) {
179
179
  // functions so `cw <cmd> --json` is byte-identical to `cw_<tool>`. Each accepts
180
180
  // the raw CLI options OR the raw MCP arguments and normalizes them identically,
181
181
  // then calls the single RunRegistry method. The registry is constructed from the
182
- // same resolved cwd on both surfaces (CLI: --cwd|process.cwd(); MCP chdir'd to
183
- // args.cwd), so repo/home roots line up.
182
+ // same resolved cwd on both surfaces (CLI: --cwd|process.cwd(); MCP passes a
183
+ // resolved cwd and scopes the runner), so repo/home roots line up.
184
184
  function runRegistryFor(args, planner) {
185
185
  return new run_registry_1.RunRegistry(String(args.cwd || process.cwd()), planner);
186
186
  }
@@ -545,6 +545,8 @@ exports.QUICKSTART_DEFAULT_APP = "architecture-review";
545
545
  function quickstart(runner, args) {
546
546
  const appId = String(args.appId || args.app || args.workflowId || exports.QUICKSTART_DEFAULT_APP);
547
547
  const agentConfigured = Boolean((0, agent_config_1.resolveAgentConfig)(args).command || (0, agent_config_1.resolveAgentConfig)(args).endpoint);
548
+ if (isTrue(args.check))
549
+ return quickstartCheck(runner, appId, args, agentConfigured);
548
550
  // `--resume`: a discoverability flag over the existing continuation. With no
549
551
  // `--run`, advance exactly ONE step (reuse the `--once` path) and print a
550
552
  // copy-pasteable continue line; with `--run <id>`, continue that run to
@@ -623,7 +625,7 @@ function quickstart(runner, args) {
623
625
  }
624
626
  else if (result.status === "in-progress") {
625
627
  hint = resume
626
- ? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume`
628
+ ? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume${wantsBundle ? " --bundle" : ""}`
627
629
  : `one step advanced (--once) — continue: cw quickstart ${appId} --run ${result.runId} --once`;
628
630
  }
629
631
  // --bundle on a run that didn't complete is a NO-OP, not silence: tell the operator
@@ -655,6 +657,132 @@ function quickstart(runner, args) {
655
657
  ...(bundle ? { bundle } : {})
656
658
  };
657
659
  }
660
+ function quickstartCheck(runner, appId, args, agentConfigured) {
661
+ const base = invocationCwd(args);
662
+ const repoArg = optionalString(args.repo) || base;
663
+ const repo = node_path_1.default.resolve(base, repoArg);
664
+ const checks = [];
665
+ try {
666
+ runner.showApp(appId);
667
+ checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
668
+ }
669
+ catch (error) {
670
+ checks.push({
671
+ name: "app",
672
+ status: "blocked",
673
+ detail: `Workflow app ${appId} is not available.`,
674
+ fix: "Run `cw app list` and choose one of the listed app ids."
675
+ });
676
+ }
677
+ let repoReadable = false;
678
+ let repoStateWritable = false;
679
+ try {
680
+ const stat = node_fs_1.default.statSync(repo);
681
+ repoReadable = stat.isDirectory();
682
+ if (!repoReadable)
683
+ throw new Error("not a directory");
684
+ node_fs_1.default.accessSync(repo, node_fs_1.default.constants.R_OK);
685
+ checks.push({ name: "repo", status: "ok", detail: `Repository path is readable (${repo}).` });
686
+ }
687
+ catch (error) {
688
+ checks.push({
689
+ name: "repo",
690
+ status: "blocked",
691
+ detail: `Repository path is not readable (${repo}).`,
692
+ fix: "Pass --repo PATH for a readable repository directory."
693
+ });
694
+ }
695
+ try {
696
+ const cwDir = node_path_1.default.join(repo, ".cw");
697
+ node_fs_1.default.accessSync(node_fs_1.default.existsSync(cwDir) ? cwDir : repo, node_fs_1.default.constants.W_OK);
698
+ repoStateWritable = repoReadable;
699
+ checks.push({ name: "repo-state", status: "ok", detail: "Run state location is writable." });
700
+ }
701
+ catch (error) {
702
+ checks.push({
703
+ name: "repo-state",
704
+ status: "blocked",
705
+ detail: "Run state location is not writable.",
706
+ fix: "Use a writable repo, fix directory permissions, or pass --repo to a writable checkout."
707
+ });
708
+ }
709
+ if (optionalString(args.question)) {
710
+ checks.push({ name: "question", status: "ok", detail: "Question is set." });
711
+ }
712
+ else {
713
+ checks.push({
714
+ name: "question",
715
+ status: "blocked",
716
+ detail: "Question is missing.",
717
+ fix: "Pass --question TEXT."
718
+ });
719
+ }
720
+ if (agentConfigured) {
721
+ checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
722
+ }
723
+ else {
724
+ checks.push({
725
+ name: "agent",
726
+ status: "blocked",
727
+ detail: "No agent backend is configured.",
728
+ fix: "Pass --agent-command \"claude -p\", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude."
729
+ });
730
+ }
731
+ if (flag(args.bundle) === true) {
732
+ const trustKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey || args.pubkey) || process.env.CW_AGENT_ATTEST_PUBKEY;
733
+ if (trustKey) {
734
+ checks.push({ name: "bundle-trust-key", status: "ok", detail: "Bundle trust public key is configured." });
735
+ }
736
+ else if (Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs)) {
737
+ checks.push({
738
+ name: "bundle-trust-key",
739
+ status: "blocked",
740
+ detail: "Strict signature verification needs a public trust key.",
741
+ fix: "Pass --with-trust-key PATH or set $CW_AGENT_ATTEST_PUBKEY."
742
+ });
743
+ }
744
+ else {
745
+ checks.push({
746
+ name: "bundle-trust-key",
747
+ status: "warn",
748
+ detail: "No public trust key is configured; unsigned or unkeyed bundles may verify with reduced signature proof.",
749
+ fix: "Pass --with-trust-key PATH to embed the public key."
750
+ });
751
+ }
752
+ }
753
+ const ok = checks.every((check) => check.status !== "blocked") && repoStateWritable;
754
+ return {
755
+ schemaVersion: 1,
756
+ mode: "check",
757
+ ok,
758
+ appId,
759
+ repo,
760
+ checks,
761
+ nextCommand: quickstartNextCommand(appId, repo, args)
762
+ };
763
+ }
764
+ function quickstartNextCommand(appId, repo, args) {
765
+ const parts = ["cw", "quickstart", shellWord(appId), "--repo", shellWord(repo)];
766
+ const question = optionalString(args.question);
767
+ if (question)
768
+ parts.push("--question", shellWord(question));
769
+ const command = optionalString(args.agentCommand || args["agent-command"]);
770
+ if (command)
771
+ parts.push("--agent-command", shellWord(command));
772
+ if (flag(args.bundle) === true)
773
+ parts.push("--bundle");
774
+ const trustKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey);
775
+ if (trustKey)
776
+ parts.push("--with-trust-key", shellWord(trustKey));
777
+ if (Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs))
778
+ parts.push("--strict-signatures");
779
+ return parts.join(" ");
780
+ }
781
+ function shellWord(value) {
782
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
783
+ return value;
784
+ return `'${value.replace(/'/g, "'\\''")}'`;
785
+ }
658
786
  /** Read-only, deterministic projection of the effective agent config (secret-stripped). */
659
787
  function backendAgentConfigShow(args) {
660
788
  return (0, agent_config_1.agentConfigShow)(args);
@@ -31,11 +31,13 @@ exports.mcpCapabilityForId = mcpCapabilityForId;
31
31
  exports.mcpRequiredArgsForTool = mcpRequiredArgsForTool;
32
32
  exports.mcpToolDefinition = mcpToolDefinition;
33
33
  exports.declaredCliTokens = declaredCliTokens;
34
+ exports.declaredCliHelpTokens = declaredCliHelpTokens;
34
35
  exports.requiresReason = requiresReason;
35
36
  exports.isPayloadProbeOptOut = isPayloadProbeOptOut;
36
37
  exports.payloadIdenticalCapabilities = payloadIdenticalCapabilities;
37
38
  exports.payloadProbeTargets = payloadProbeTargets;
38
39
  exports.deferredPayloadProbeCapabilities = deferredPayloadProbeCapabilities;
40
+ exports.buildPayloadProbePlan = buildPayloadProbePlan;
39
41
  exports.payloadProbePlan = payloadProbePlan;
40
42
  exports.buildParityReport = buildParityReport;
41
43
  // ---------------------------------------------------------------------------
@@ -351,7 +353,7 @@ const BUILTIN_CAPABILITIES = [
351
353
  { capability: "run.drive.step", summary: "Drive a run by delegating each worker to the agent backend (plan->dispatch->fulfill->accept->commit; --once for one step).", entry: "runDrive", surface: "both", cli: { path: ["run", "drive"], caseTokens: ["run", "drive"], jsonMode: "default" }, mcp: { tool: "cw_run_drive_step" }, payloadIdentical: false, reason: "Mutating: advances the run by spawning the external agent per worker and recording attested output — not a read probe. CLI (--drive/--step) and MCP route through the same drive() core." },
352
354
  {
353
355
  capability: "quickstart",
354
- summary: "ONE-COMMAND quickstart: plan(app, default architecture-review) -> run --drive -> report in a single invocation (--preview for a read-only dry run; --bundle [--with-trust-key K] seals a completed run into a self-verified portable bundle).",
356
+ summary: "ONE-COMMAND quickstart: --check preflights without writes; otherwise plan(app, default architecture-review) -> run --drive -> report in a single invocation (--preview for a read-only dry run; --bundle [--with-trust-key K] seals a completed run into a self-verified portable bundle).",
355
357
  entry: "quickstart",
356
358
  surface: "cli-only",
357
359
  cli: { path: ["quickstart"], caseTokens: ["quickstart", "audit-run"], jsonMode: "default" },
@@ -458,34 +460,63 @@ const RUN_PAYLOAD_PROBE_CAPABILITIES = [
458
460
  "gc.plan",
459
461
  "gc.verify"
460
462
  ];
463
+ const SCENARIO_PAYLOAD_PROBE_CAPABILITIES = [
464
+ "plan",
465
+ "app.show",
466
+ "app.validate",
467
+ "app.package",
468
+ "topology.show",
469
+ "topology.validate",
470
+ "topology.apply",
471
+ "topology.summary",
472
+ "topology.graph",
473
+ "summary.refresh",
474
+ "summary.show",
475
+ "sandbox.show",
476
+ "sandbox.validate",
477
+ "sandbox.choose",
478
+ "sandbox.resolve",
479
+ "approve",
480
+ "reject",
481
+ "comment.add",
482
+ "handoff",
483
+ "review.policy",
484
+ "worker.list",
485
+ "worker.show",
486
+ "worker.manifest",
487
+ "worker.output",
488
+ "worker.fail",
489
+ "worker.validate",
490
+ "candidate.list",
491
+ "candidate.show",
492
+ "candidate.register",
493
+ "candidate.score",
494
+ "candidate.rank",
495
+ "candidate.select",
496
+ "candidate.reject",
497
+ "feedback.list",
498
+ "feedback.show",
499
+ "feedback.collect",
500
+ "feedback.task",
501
+ "feedback.resolve",
502
+ "node.show",
503
+ "node.snapshot",
504
+ "node.diff",
505
+ "node.replay",
506
+ "node.replay.verify"
507
+ ];
461
508
  const PAYLOAD_PROBE_DEFERRED_GROUPS = [
462
509
  {
463
510
  reason: "Not safe for the deterministic bootstrap parity probe yet: this capability needs extra target ids/files, mutates durable state, depends on external state, or needs a dedicated fixture beyond cwd/runId.",
464
511
  capabilities: [
465
512
  "init",
466
- "plan",
467
513
  "dispatch",
468
514
  "result",
469
- "app.show",
470
- "app.validate",
471
515
  "app.init",
472
- "app.package",
473
516
  "app.run",
474
- "node.show",
475
- "node.snapshot",
476
- "node.diff",
477
- "node.replay",
478
- "node.replay.verify",
479
517
  "migration.list",
480
518
  "migration.check",
481
519
  "migration.prove",
482
- "topology.show",
483
- "topology.validate",
484
- "topology.apply",
485
- "topology.summary",
486
- "topology.graph",
487
- "summary.refresh",
488
- "summary.show",
489
520
  "multi-agent.run",
490
521
  "multi-agent.status",
491
522
  "multi-agent.step",
@@ -542,30 +573,8 @@ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
542
573
  "audit.judge",
543
574
  "audit.attest",
544
575
  "audit.decision",
545
- "sandbox.show",
546
- "sandbox.validate",
547
- "sandbox.choose",
548
- "sandbox.resolve",
549
576
  "backend.show",
550
577
  "backend.probe",
551
- "worker.list",
552
- "worker.show",
553
- "worker.manifest",
554
- "worker.output",
555
- "worker.fail",
556
- "worker.validate",
557
- "candidate.list",
558
- "candidate.show",
559
- "candidate.register",
560
- "candidate.score",
561
- "candidate.rank",
562
- "candidate.select",
563
- "candidate.reject",
564
- "feedback.list",
565
- "feedback.show",
566
- "feedback.collect",
567
- "feedback.task",
568
- "feedback.resolve",
569
578
  "schedule.create",
570
579
  "schedule.list",
571
580
  "schedule.delete",
@@ -607,12 +616,7 @@ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
607
616
  "sched.policy.show",
608
617
  "sched.policy.set",
609
618
  "telemetry.verify",
610
- "history",
611
- "approve",
612
- "reject",
613
- "comment.add",
614
- "handoff",
615
- "review.policy"
619
+ "history"
616
620
  ]
617
621
  }
618
622
  ];
@@ -665,6 +669,24 @@ function declaredCliTokens() {
665
669
  }
666
670
  return [...tokens].sort();
667
671
  }
672
+ /** The top-level CLI commands that should be visible in `cw help`. Subcommands
673
+ * are intentionally collapsed to their first token; aliases such as audit-run
674
+ * stay visible. */
675
+ function declaredCliHelpTokens() {
676
+ const tokens = new Set();
677
+ for (const cap of exports.CAPABILITY_REGISTRY) {
678
+ if (!cap.cli)
679
+ continue;
680
+ const subcommandTokens = new Set(cap.cli.path.slice(1));
681
+ tokens.add(cap.cli.path[0]);
682
+ for (const token of cap.cli.caseTokens || []) {
683
+ if (!subcommandTokens.has(token))
684
+ tokens.add(token);
685
+ }
686
+ }
687
+ tokens.delete("help");
688
+ return [...tokens].sort();
689
+ }
668
690
  /** Whether a descriptor MUST carry a reason (surface-specific or divergent). */
669
691
  function requiresReason(cap) {
670
692
  if (cap.surface !== "both")
@@ -699,27 +721,31 @@ function payloadIdenticalCapabilities() {
699
721
  function payloadProbeTargets() {
700
722
  return [
701
723
  ...GLOBAL_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "global" })),
702
- ...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" }))
724
+ ...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" })),
725
+ ...SCENARIO_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "scenario" }))
703
726
  ];
704
727
  }
705
728
  function deferredPayloadProbeCapabilities() {
706
729
  return PAYLOAD_PROBE_DEFERRED_GROUPS.flatMap((group) => group.capabilities.map((capability) => ({ capability, reason: group.reason })));
707
730
  }
708
- function payloadProbePlan() {
731
+ function buildPayloadProbePlan(targets, deferred) {
709
732
  const candidateIds = new Set(payloadIdenticalCapabilities().map((cap) => cap.capability));
710
733
  const counts = new Map();
711
- const classified = [...payloadProbeTargets().map((entry) => entry.capability), ...deferredPayloadProbeCapabilities().map((entry) => entry.capability)];
734
+ const classified = [...targets.map((entry) => entry.capability), ...deferred.map((entry) => entry.capability)];
712
735
  for (const capability of classified)
713
736
  counts.set(capability, (counts.get(capability) || 0) + 1);
714
737
  const classifiedIds = new Set(classified);
715
738
  return {
716
- targets: payloadProbeTargets(),
717
- deferred: deferredPayloadProbeCapabilities(),
739
+ targets,
740
+ deferred,
718
741
  unclassified: [...candidateIds].filter((capability) => !classifiedIds.has(capability)).sort(),
719
742
  duplicateClassifications: [...counts.entries()].filter(([, count]) => count > 1).map(([capability]) => capability).sort(),
720
743
  invalidClassifications: [...classifiedIds].filter((capability) => !candidateIds.has(capability)).sort()
721
744
  };
722
745
  }
746
+ function payloadProbePlan() {
747
+ return buildPayloadProbePlan(payloadProbeTargets(), deferredPayloadProbeCapabilities());
748
+ }
723
749
  function lintRegistry() {
724
750
  const issues = [];
725
751
  const seenCaps = new Set();
@@ -755,10 +781,14 @@ function buildParityReport(input) {
755
781
  const actualTools = new Set(input.mcpTools);
756
782
  const declaredTokens = new Set(declaredCliTokens());
757
783
  const actualTokens = new Set(input.cliTokens);
784
+ const declaredHelpTokens = new Set(declaredCliHelpTokens());
785
+ const actualHelpTokens = new Set(input.helpTokens || []);
758
786
  const missingMcpTools = [...declaredTools].filter((tool) => !actualTools.has(tool)).sort();
759
787
  const undeclaredMcpTools = [...actualTools].filter((tool) => !declaredTools.has(tool)).sort();
760
788
  const missingCliTokens = [...declaredTokens].filter((token) => !actualTokens.has(token)).sort();
761
789
  const undeclaredCliTokens = [...actualTokens].filter((token) => !declaredTokens.has(token)).sort();
790
+ const helpMissingCliTokens = input.helpTokens ? [...declaredHelpTokens].filter((token) => !actualHelpTokens.has(token)).sort() : [];
791
+ const helpUndeclaredCliTokens = input.helpTokens ? [...actualHelpTokens].filter((token) => !declaredHelpTokens.has(token)).sort() : [];
762
792
  const reasonlessExceptions = exports.CAPABILITY_REGISTRY.filter((cap) => requiresReason(cap) && !(cap.reason && cap.reason.trim()))
763
793
  .map((cap) => cap.capability)
764
794
  .sort();
@@ -768,6 +798,8 @@ function buildParityReport(input) {
768
798
  undeclaredMcpTools.length === 0 &&
769
799
  missingCliTokens.length === 0 &&
770
800
  undeclaredCliTokens.length === 0 &&
801
+ helpMissingCliTokens.length === 0 &&
802
+ helpUndeclaredCliTokens.length === 0 &&
771
803
  reasonlessExceptions.length === 0 &&
772
804
  payloadPlan.unclassified.length === 0 &&
773
805
  payloadPlan.duplicateClassifications.length === 0 &&
@@ -780,6 +812,8 @@ function buildParityReport(input) {
780
812
  undeclaredMcpTools,
781
813
  missingCliTokens,
782
814
  undeclaredCliTokens,
815
+ helpMissingCliTokens,
816
+ helpUndeclaredCliTokens,
783
817
  reasonlessExceptions,
784
818
  payloadProbeUnclassified: payloadPlan.unclassified,
785
819
  payloadProbeDuplicateClassifications: payloadPlan.duplicateClassifications,
@@ -93,6 +93,9 @@ async function runCli(argv = process.argv.slice(2)) {
93
93
  const runId = optionalArg(args.options.run) || optionalArg(args.options.runId);
94
94
  const qs = (0, capability_core_1.quickstart)(runner, { ...args.options, ...(appId ? { appId } : {}), ...(runId ? { runId } : {}) });
95
95
  printJson(qs);
96
+ if (qs.mode === "check" && qs.ok === false) {
97
+ process.exitCode = 1;
98
+ }
96
99
  // Fail closed: if --bundle produced an artifact that does not self-verify, exit
97
100
  // non-zero so `cw quickstart ... --bundle && send-to-client` cannot ship a report
98
101
  // whose bundle a client could not verify. Mirrors `report bundle`.
package/dist/doctor.js CHANGED
@@ -25,6 +25,7 @@ const node_os_1 = __importDefault(require("node:os"));
25
25
  const node_path_1 = __importDefault(require("node:path"));
26
26
  const node_child_process_1 = require("node:child_process");
27
27
  const agent_config_1 = require("./agent-config");
28
+ const onramp_1 = require("./onramp");
28
29
  /** Resolve a bare binary name against $PATH (or accept an explicit path). Returns
29
30
  * the resolved path, or undefined when not found. No spawning. */
30
31
  function whichBinary(bin, env) {
@@ -136,7 +137,15 @@ function runDoctor(args = {}, env = process.env, cwd = process.cwd()) {
136
137
  ? "ready — all checks passed"
137
138
  : `ready, with ${warns} warning${warns === 1 ? "" : "s"}`
138
139
  : `${fails} blocking problem${fails === 1 ? "" : "s"} found`;
139
- return { schemaVersion: 1, ok, checks, summary };
140
+ return {
141
+ schemaVersion: 1,
142
+ ok,
143
+ checks,
144
+ summary,
145
+ ...((0, onramp_1.optionEnabled)(args.onramp)
146
+ ? { onramp: (0, onramp_1.buildDoctorOnramp)({ cwd, env, changedFrom: typeof args["changed-from"] === "string" ? args["changed-from"] : undefined }) }
147
+ : {})
148
+ };
140
149
  }
141
150
  /** Human rendering (TTY/default). `--json` callers use the report object directly. */
142
151
  function formatDoctorReport(report) {
@@ -149,5 +158,32 @@ function formatDoctorReport(report) {
149
158
  }
150
159
  lines.push("");
151
160
  lines.push(`${report.ok ? "✓" : "✗"} ${report.summary}`);
161
+ if (report.onramp) {
162
+ lines.push("");
163
+ lines.push("Onramp");
164
+ lines.push(` ${report.onramp.summary}`);
165
+ if (report.onramp.recommendedChecks) {
166
+ lines.push("");
167
+ lines.push(" Recommended Checks");
168
+ for (const command of report.onramp.recommendedChecks.commands)
169
+ lines.push(` - ${command}`);
170
+ }
171
+ if (report.onramp.contract && !report.onramp.contract.ok) {
172
+ lines.push("");
173
+ lines.push(" Contract Issues");
174
+ for (const issue of report.onramp.contract.issues) {
175
+ lines.push(` - ${issue.code}: ${issue.detail}`);
176
+ lines.push(` fix: ${issue.fix}`);
177
+ }
178
+ }
179
+ for (const section of report.onramp.sections) {
180
+ lines.push("");
181
+ lines.push(` ${section.title}: ${section.summary}`);
182
+ for (const action of section.actions) {
183
+ lines.push(` - ${action.command}`);
184
+ lines.push(` ${action.reason}`);
185
+ }
186
+ }
187
+ }
152
188
  return lines.join("\n");
153
189
  }