cool-workflow 0.2.3 → 0.2.5
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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.gemini-plugin/mcp.json +10 -0
- package/.gemini-plugin/plugin.json +40 -0
- package/.opencode-plugin/mcp.json +10 -0
- package/.opencode-plugin/plugin.json +40 -0
- package/README.md +202 -48
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +33 -4
- package/dist/cli/entry.js +11 -19
- package/dist/cli/global-flags.js +113 -0
- package/dist/cli/io.js +6 -20
- package/dist/cli/parseargv.js +7 -1
- package/dist/core/capability-data.js +339 -10
- package/dist/core/format/help.js +80 -4
- package/dist/core/format/recovery-hint.js +32 -0
- package/dist/core/multi-agent/collaboration.js +35 -6
- package/dist/core/multi-agent/runtime.js +7 -0
- package/dist/core/multi-agent/trust-policy.js +7 -1
- package/dist/core/pipeline/contract.js +7 -0
- package/dist/core/pipeline/error-feedback.js +2 -2
- package/dist/core/trust/evidence-grounding.js +13 -1
- package/dist/core/util/cli-args.js +55 -0
- package/dist/core/version.js +1 -1
- package/dist/mcp/dispatch.js +22 -2
- package/dist/mcp/server.js +124 -13
- package/dist/mcp-server.js +20 -0
- package/dist/shell/commit.js +8 -2
- package/dist/shell/coordinator-io.js +73 -1
- package/dist/shell/drive.js +129 -64
- package/dist/shell/error-feedback-io.js +6 -0
- package/dist/shell/execution-backend/agent.js +205 -24
- package/dist/shell/execution-backend/container.js +44 -10
- package/dist/shell/execution-backend/local.js +47 -10
- package/dist/shell/fs-atomic.js +93 -12
- package/dist/shell/ledger-cli.js +9 -2
- package/dist/shell/multi-agent-cli.js +5 -1
- package/dist/shell/onramp.js +48 -5
- package/dist/shell/pipeline-cli.js +20 -2
- package/dist/shell/pipeline.js +2 -1
- package/dist/shell/reclamation-io.js +76 -7
- package/dist/shell/registry-cli.js +4 -0
- package/dist/shell/run-export.js +52 -4
- package/dist/shell/run-registry-io.js +1 -0
- package/dist/shell/run-store.js +156 -0
- package/dist/shell/scheduler-io.js +101 -10
- package/dist/shell/telemetry-ledger-io.js +36 -24
- package/dist/shell/trust-audit.js +104 -10
- package/dist/shell/workbench-host.js +121 -10
- package/dist/shell/workbench.js +79 -5
- package/dist/shell/worker-isolation.js +1 -1
- package/dist/shell/workflow-app-loader.js +67 -1
- package/dist/wiring/capability-table/basics.js +7 -2
- package/dist/wiring/capability-table/exec-backend.js +42 -24
- package/dist/wiring/capability-table/multi-agent.js +69 -69
- package/dist/wiring/capability-table/pipeline.js +60 -28
- package/dist/wiring/capability-table/registry-core.js +29 -6
- package/dist/wiring/capability-table/reporting.js +45 -39
- package/dist/wiring/capability-table/scheduling-registry.js +66 -60
- package/dist/wiring/capability-table/state.js +34 -34
- package/dist/wiring/capability-table/trust-ledger.js +66 -24
- package/dist/wiring/capability-table/workflow-apps.js +15 -15
- package/docs/agent-delegation-drive.7.md +17 -0
- package/docs/cli-mcp-parity.7.md +43 -13
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +38 -0
- package/docs/cross-agent-ledger.7.md +20 -8
- package/docs/durable-state-and-locking.7.md +4 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
- package/docs/execution-backends.7.md +4 -0
- package/docs/fix.7.md +4 -4
- package/docs/getting-started.md +40 -32
- package/docs/index.md +17 -0
- package/docs/launch/demo.tape +4 -3
- package/docs/mcp-app-surface.7.md +6 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
- package/docs/multi-agent-eval-replay-harness.7.md +4 -0
- package/docs/multi-agent-operator-ux.7.md +4 -0
- package/docs/node-snapshot-diff-replay.7.md +4 -0
- package/docs/observability-cost-accounting.7.md +4 -0
- package/docs/project-index.md +43 -9
- package/docs/real-execution-backends.7.md +4 -0
- package/docs/release-and-migration.7.md +4 -0
- package/docs/release-tooling.7.md +22 -0
- package/docs/routine.7.md +22 -0
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +28 -3
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/trust-audit-anchor.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +26 -2
- package/docs/workflow-app-framework.7.md +31 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +4 -3
- package/package.json +5 -2
- package/scripts/agents/agent-adapter-core.js +26 -0
- package/scripts/agents/claude-p-agent.js +4 -1
- package/scripts/agents/codex-agent.js +4 -0
- package/scripts/agents/cw-attest-wrap.js +1 -1
- package/scripts/agents/gemini-agent.js +4 -0
- package/scripts/agents/opencode-agent.js +5 -0
- package/scripts/block-unapproved-tag.js +160 -0
- package/scripts/bump-version.js +27 -10
- package/scripts/canonical-apps.js +4 -4
- package/scripts/children/batch-delegate-child.js +72 -4
- package/scripts/children/http-batch-delegate-child.js +132 -0
- package/scripts/children/http-delegate-child.js +8 -0
- package/scripts/dogfood-release.js +2 -2
- package/scripts/fake-date-for-reproduction.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/lang-policy-check.js +82 -0
- package/scripts/purity-baseline.json +0 -30
- package/scripts/purity-gate.js +16 -3
- package/scripts/release-check.js +2 -1
- package/scripts/release-flow.js +264 -41
- package/scripts/release-gate.js +184 -0
- package/scripts/release-oneclick.js +438 -0
- package/scripts/release-tags.js +47 -0
- package/scripts/verdict-keygen.js +2 -2
- package/scripts/verify-bump-reproduction.js +193 -0
- package/scripts/verify-verdict-signature.js +1 -1
- package/scripts/version-sync-check.js +39 -22
- package/ui/workbench/app.css +41 -9
- package/ui/workbench/app.js +160 -27
- package/ui/workbench/index.html +2 -2
- package/docs/agent-framework.md +0 -177
- package/docs/designs/handoff-ledger.md +0 -145
- package/docs/dogfood/resume-drive-real-agent-2026-06-14.md +0 -40
- package/docs/launch/launch-kit.md +0 -195
- package/docs/launch/pre-launch-checklist.md +0 -53
- package/docs/readme-v0.1.87-full.md +0 -301
- package/docs/routines.md +0 -101
- package/docs/scheduled-tasks.md +0 -80
- package/scripts/agents/claude-p-agent.sh +0 -9
- package/scripts/block-unapproved-tag.sh +0 -60
- package/scripts/release-gate.sh +0 -94
- package/scripts/verify-bump-reproduction.sh +0 -148
- package/scripts/verify-container-selfref.js +0 -64
|
@@ -141,14 +141,37 @@ function candidateWorkflowsRoots() {
|
|
|
141
141
|
roots.push(path.join(process.cwd(), "workflows"));
|
|
142
142
|
return roots;
|
|
143
143
|
}
|
|
144
|
+
/** Whether `candidate` resolves to `root` itself or a real descendant of it
|
|
145
|
+
* (not an ancestor, sibling, or anywhere reached only via `..` segments). */
|
|
146
|
+
function isWithinRoot(root, candidate) {
|
|
147
|
+
const relative = path.relative(root, candidate);
|
|
148
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
149
|
+
}
|
|
144
150
|
function findAppDir(appId) {
|
|
145
151
|
for (const root of candidateAppsRoots()) {
|
|
146
152
|
const dir = path.join(root, appId);
|
|
153
|
+
// appId is caller-controlled (MCP app.run, `cw plan`/`run --drive`, a
|
|
154
|
+
// sub-workflow's spec.appId). Without this check a traversal id like
|
|
155
|
+
// "../../../tmp/evil-app" walks path.join right out of every trusted
|
|
156
|
+
// root and loadWorkflowApp below require()s whatever app.json it finds
|
|
157
|
+
// there with NO manifest/entrypoint validation — the fast path this
|
|
158
|
+
// function feeds skips validateWorkflowApp entirely (see loadWorkflowApp).
|
|
159
|
+
if (!isWithinRoot(root, dir))
|
|
160
|
+
continue;
|
|
147
161
|
if (fs.existsSync(path.join(dir, "app.json")))
|
|
148
162
|
return dir;
|
|
149
163
|
}
|
|
150
164
|
return undefined;
|
|
151
165
|
}
|
|
166
|
+
/** Every root candidateAppsRoots()/candidateWorkflowsRoots() would search:
|
|
167
|
+
* bundled apps/workflows, an installed package's apps/workflows, an
|
|
168
|
+
* operator-set CW_APPS_DIR/CW_WORKFLOWS_DIR, and the caller's cwd/apps +
|
|
169
|
+
* cwd/workflows (the "cw app init" default — must stay unwarned, it is
|
|
170
|
+
* the normal flow for a user's own local apps). */
|
|
171
|
+
function isTrustedAppSourcePath(resolvedPath) {
|
|
172
|
+
const roots = [...candidateAppsRoots(), ...candidateWorkflowsRoots()];
|
|
173
|
+
return roots.some((root) => isWithinRoot(path.resolve(root), resolvedPath));
|
|
174
|
+
}
|
|
152
175
|
function validationContext() {
|
|
153
176
|
return { bundledSandboxProfileIds: (0, sandbox_profile_1.bundledSandboxProfileIds)(), currentCoolWorkflowVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION };
|
|
154
177
|
}
|
|
@@ -451,6 +474,31 @@ function loadWorkflowAppRecordTarget(target) {
|
|
|
451
474
|
throw new Error("Missing workflow app path or id");
|
|
452
475
|
const resolved = path.resolve(target);
|
|
453
476
|
if (fs.existsSync(resolved)) {
|
|
477
|
+
// `validate`/`show` on a real path is the one loader entrypoint whose
|
|
478
|
+
// whole point is to let a caller inspect an app BEFORE deciding to
|
|
479
|
+
// trust it — but inspecting it means require()-ing its workflow.js
|
|
480
|
+
// (validateWorkflowApp needs the returned WorkflowDefinition, which
|
|
481
|
+
// only exists after the factory runs). A path outside every root CW
|
|
482
|
+
// already trusts (bundled apps, an installed package, CW_APPS_DIR, or
|
|
483
|
+
// the caller's own cwd/apps from `cw app init`) gets arbitrary code
|
|
484
|
+
// executed by "validate", with no OS-level containment — a warning
|
|
485
|
+
// printed after that require() call would be too late to matter, so
|
|
486
|
+
// this fails closed instead: refuse by default, and only proceed
|
|
487
|
+
// (still with a visible warning) when the caller explicitly opts in.
|
|
488
|
+
// Mirrors the existing --allow-unattested precedent in
|
|
489
|
+
// worker-isolation.ts: unsafe-but-explicit, never silent.
|
|
490
|
+
if (!isTrustedAppSourcePath(resolved)) {
|
|
491
|
+
if (!process.env.CW_ALLOW_EXTERNAL_APP_CODE) {
|
|
492
|
+
throw new app_schema_1.WorkflowAppValidationError("Untrusted workflow app source", [
|
|
493
|
+
{
|
|
494
|
+
code: "workflow-app-untrusted-source",
|
|
495
|
+
message: `Refusing to load workflow app code outside CW's trusted app roots: ${resolved}. Its workflow.js would run as ordinary Node.js code with full host privileges — CW does not sandbox app code, only delegated agent workers. Set CW_ALLOW_EXTERNAL_APP_CODE=1 to load and execute it anyway.`,
|
|
496
|
+
path: resolved,
|
|
497
|
+
},
|
|
498
|
+
]);
|
|
499
|
+
}
|
|
500
|
+
process.stderr.write(`cw: loading external workflow app code from ${resolved} — its workflow.js runs as ordinary Node.js code with full host privileges, not sandboxed.\n`);
|
|
501
|
+
}
|
|
454
502
|
const stat = fs.statSync(resolved);
|
|
455
503
|
if (stat.isDirectory())
|
|
456
504
|
return loadWorkflowAppFromManifest(path.join(resolved, "app.json"));
|
|
@@ -593,6 +641,24 @@ function renderManifestTemplate(id, title) {
|
|
|
593
641
|
function renderEntrypointTemplate(id, title) {
|
|
594
642
|
return `module.exports = ({ workflow, phase, agent, artifact, input }) => {\n const inputs = [\n input("question", { type: "string", required: true, description: "Question or task this workflow should answer." })\n ];\n\n return workflow({\n id: ${JSON.stringify(id)},\n title: ${JSON.stringify(title)},\n summary: "Describe what this workflow app does.",\n limits: {\n maxAgents: 8,\n maxConcurrentAgents: 4\n },\n inputs,\n sandboxProfiles: ["readonly"],\n phases: [\n phase("Map", [\n agent("map:context", "Map the task context, constraints, and evidence needed for {{question}}.", { sandboxProfileId: "readonly" })\n ]),\n phase("Assess", [\n agent("assess:risks", "Assess risks, tradeoffs, and unknowns for {{question}}.", { sandboxProfileId: "readonly" })\n ]),\n phase("Synthesize", [\n artifact("synthesis:report", "Synthesize the final answer for {{question}}.", { requiresEvidence: true, sandboxProfileId: "readonly" })\n ])\n ]\n });\n};\n`;
|
|
595
643
|
}
|
|
644
|
+
/** Validates a manifest CW itself just wrote to disk (from `initWorkflowApp`
|
|
645
|
+
* below) — deliberately bypasses `loadWorkflowAppRecordTarget`'s
|
|
646
|
+
* untrusted-source gate. That gate exists for `cw app validate <path>`,
|
|
647
|
+
* where the caller is inspecting code someone else wrote before deciding
|
|
648
|
+
* whether to trust it; `app init --directory <anywhere>` is the opposite
|
|
649
|
+
* case (the caller is authoring new code, from CW's own template, in a
|
|
650
|
+
* location they chose on purpose) and must keep working regardless of
|
|
651
|
+
* where `--directory` points. */
|
|
652
|
+
function validateGeneratedManifest(manifestPath) {
|
|
653
|
+
try {
|
|
654
|
+
const record = loadWorkflowAppFromManifest(manifestPath);
|
|
655
|
+
const result = (0, app_schema_1.validateWorkflowApp)(record.app, validationContext(), { appPath: sourcePathOf(record) });
|
|
656
|
+
return { ...result, summary: summarizeWorkflowAppRecord(record) };
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
return { valid: false, appId: manifestPath, appPath: path.resolve(manifestPath), issues: (0, app_schema_1.validationIssuesFromError)(error) };
|
|
660
|
+
}
|
|
661
|
+
}
|
|
596
662
|
/** `cw app init <id>` / `cw_app_init`. Writes `app.json` + `workflow.js`
|
|
597
663
|
* from the templates, refusing system directories and (without
|
|
598
664
|
* `--force`) an existing app. Ported from `initApp`. */
|
|
@@ -614,7 +680,7 @@ function initWorkflowApp(appId, options = {}) {
|
|
|
614
680
|
fs.mkdirSync(destinationDir, { recursive: true });
|
|
615
681
|
fs.writeFileSync(manifestPath, renderManifestTemplate(id, title), "utf8");
|
|
616
682
|
fs.writeFileSync(entrypointPath, renderEntrypointTemplate(id, title), "utf8");
|
|
617
|
-
const validation =
|
|
683
|
+
const validation = validateGeneratedManifest(manifestPath);
|
|
618
684
|
if (!validation.valid) {
|
|
619
685
|
throw new app_schema_1.WorkflowAppValidationError("Generated workflow app is invalid", validation.issues);
|
|
620
686
|
}
|
|
@@ -18,7 +18,7 @@ const registry_core_1 = require("./registry-core");
|
|
|
18
18
|
const version_1 = require("../../core/version");
|
|
19
19
|
const help_1 = require("../../core/format/help");
|
|
20
20
|
const completion_1 = require("../../core/format/completion");
|
|
21
|
-
const
|
|
21
|
+
const cli_args_1 = require("../../core/util/cli-args");
|
|
22
22
|
// This whole module is required unconditionally at startup for EVERY
|
|
23
23
|
// command (see wiring/capability-table/index.ts) — a top-level import of
|
|
24
24
|
// `shell/workflow-app-loader` here means even `cw --version` pays its
|
|
@@ -38,7 +38,7 @@ function loadWorkflowAppLoader() {
|
|
|
38
38
|
path: ["completion"],
|
|
39
39
|
jsonMode: "human",
|
|
40
40
|
handler: (args) => {
|
|
41
|
-
const shell = (0,
|
|
41
|
+
const shell = (0, cli_args_1.optionalArg)(args.positionals[0]);
|
|
42
42
|
if (!shell)
|
|
43
43
|
throw new Error('Missing shell name.\n Try: cw completion bash|zsh|fish');
|
|
44
44
|
return { text: (0, completion_1.formatCompletionScript)(shell) };
|
|
@@ -78,6 +78,11 @@ function loadWorkflowAppLoader() {
|
|
|
78
78
|
path: ["list"],
|
|
79
79
|
jsonMode: "default",
|
|
80
80
|
handler: () => ({ json: (0, registry_core_1.listBundledWorkflows)() }),
|
|
81
|
+
// UI/UX fix: a person at a real terminal used to get the raw JSON array.
|
|
82
|
+
// On a TTY (and only there — piped bytes stay the exact same JSON, see
|
|
83
|
+
// cli/dispatch.ts's shouldRenderHuman) render one "<id> — <title>" line
|
|
84
|
+
// per workflow plus the next-step footer.
|
|
85
|
+
humanRender: help_1.formatWorkflowList,
|
|
81
86
|
});
|
|
82
87
|
(0, registry_core_1.attachCliBinding)("status", {
|
|
83
88
|
path: ["status"],
|
|
@@ -16,23 +16,26 @@ const registry_core_1 = require("./registry-core");
|
|
|
16
16
|
// their milestone-2 placeholder `mcp.handler` with the real body, exactly
|
|
17
17
|
// as milestones 3/4 did for their own rows.
|
|
18
18
|
// ---------------------------------------------------------------------
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
19
|
+
const cli_args_1 = require("../../core/util/cli-args");
|
|
20
|
+
// This slice is required unconditionally at startup for every command; every
|
|
21
|
+
// shell import below is lazy (require()'d only inside the handler that
|
|
22
|
+
// actually uses it), so a command that never touches doctor/fix/sandbox/
|
|
23
|
+
// backend/app.run never pays any of these shell modules' require cost.
|
|
24
24
|
function loadDoctor() {
|
|
25
25
|
return require("../../shell/doctor");
|
|
26
26
|
}
|
|
27
27
|
function loadAppRunCli() {
|
|
28
28
|
return require("../../shell/app-run-cli");
|
|
29
29
|
}
|
|
30
|
+
function loadExecBackendCli() {
|
|
31
|
+
return require("../../shell/exec-backend-cli");
|
|
32
|
+
}
|
|
30
33
|
(0, registry_core_1.attachCliBinding)("sandbox.list", {
|
|
31
34
|
path: ["sandbox", "list"],
|
|
32
35
|
jsonMode: "default",
|
|
33
|
-
handler: (args) => ({ json: (
|
|
36
|
+
handler: (args) => ({ json: loadExecBackendCli().listSandboxProfilesCli(args.options) }),
|
|
34
37
|
});
|
|
35
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.list").mcp.handler = (args) => (
|
|
38
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.list").mcp.handler = (args) => loadExecBackendCli().listSandboxProfilesCli(args);
|
|
36
39
|
// GAP #24: cw_sandbox_choose / cw_sandbox_resolve + cw_app_run were declared
|
|
37
40
|
// MCP-only rows with the notYetImplemented placeholder handler. Wire them to
|
|
38
41
|
// the ported shell bodies (both are MCP-only in the old build — no CLI path).
|
|
@@ -42,18 +45,18 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.run").mcp.handler = (args) => lo
|
|
|
42
45
|
(0, registry_core_1.attachCliBinding)("sandbox.show", {
|
|
43
46
|
path: ["sandbox", "show"],
|
|
44
47
|
jsonMode: "default",
|
|
45
|
-
handler: (args) => ({ json: (
|
|
48
|
+
handler: (args) => ({ json: loadExecBackendCli().showSandboxProfileCli((0, cli_args_1.required)(args.positionals[0], "profile id"), args.options) }),
|
|
46
49
|
});
|
|
47
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.show").mcp.handler = (args) => (
|
|
50
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.show").mcp.handler = (args) => loadExecBackendCli().showSandboxProfileCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.profileId), "profile id"), args);
|
|
48
51
|
(0, registry_core_1.attachCliBinding)("sandbox.validate", {
|
|
49
52
|
path: ["sandbox", "validate"],
|
|
50
53
|
jsonMode: "default",
|
|
51
54
|
handler: (args) => {
|
|
52
|
-
const result = (
|
|
55
|
+
const result = loadExecBackendCli().validateSandboxProfileCli((0, cli_args_1.required)(args.positionals[0], "profile file"), args.options);
|
|
53
56
|
return { json: result, exitCode: result.valid ? undefined : 1 };
|
|
54
57
|
},
|
|
55
58
|
});
|
|
56
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.validate").mcp.handler = (args) => (
|
|
59
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.validate").mcp.handler = (args) => loadExecBackendCli().validateSandboxProfileCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.profileFile), "profile file"), args);
|
|
57
60
|
// PARITY: `sandbox.choose`/`sandbox.resolve` are BOTH-surface capabilities
|
|
58
61
|
// per SPEC/mcp.md (old build cli.path ["sandbox","choose"]/["sandbox",
|
|
59
62
|
// "resolve"]) — they were left MCP-only at GAP #24 (see the comment
|
|
@@ -74,21 +77,21 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.validate").mcp.handler = (ar
|
|
|
74
77
|
(0, registry_core_1.attachCliBinding)("backend.list", {
|
|
75
78
|
path: ["backend", "list"],
|
|
76
79
|
jsonMode: "default",
|
|
77
|
-
handler: () => ({ json: (
|
|
80
|
+
handler: () => ({ json: loadExecBackendCli().listBackendsCli() }),
|
|
78
81
|
});
|
|
79
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.list").mcp.handler = () => (
|
|
82
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.list").mcp.handler = () => loadExecBackendCli().listBackendsCli();
|
|
80
83
|
(0, registry_core_1.attachCliBinding)("backend.show", {
|
|
81
84
|
path: ["backend", "show"],
|
|
82
85
|
jsonMode: "default",
|
|
83
|
-
handler: (args) => ({ json: (
|
|
86
|
+
handler: (args) => ({ json: loadExecBackendCli().showBackendCli((0, cli_args_1.required)(args.positionals[0], "backend id")) }),
|
|
84
87
|
});
|
|
85
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.show").mcp.handler = (args) => (
|
|
88
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.show").mcp.handler = (args) => loadExecBackendCli().showBackendCli((0, cli_args_1.required)((0, cli_args_1.optionalArg)(args.backendId), "backend id"));
|
|
86
89
|
(0, registry_core_1.attachCliBinding)("backend.probe", {
|
|
87
90
|
path: ["backend", "probe"],
|
|
88
91
|
jsonMode: "default",
|
|
89
|
-
handler: (args) => ({ json: (
|
|
92
|
+
handler: (args) => ({ json: loadExecBackendCli().probeBackendCli(args.positionals[0], args.options) }),
|
|
90
93
|
});
|
|
91
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.probe").mcp.handler = (args) => (
|
|
94
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.probe").mcp.handler = (args) => loadExecBackendCli().probeBackendCli((0, cli_args_1.optionalArg)(args.backendId), args);
|
|
92
95
|
// `backend agent config [show]` = read-only; `backend agent config set
|
|
93
96
|
// ...` = mutating. CLI path is ["backend", "agent"] (2 tokens, matching
|
|
94
97
|
// dispatchTable's supported path lengths); the remaining positionals
|
|
@@ -102,8 +105,8 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.probe").mcp.handler = (args)
|
|
|
102
105
|
handler: (args) => {
|
|
103
106
|
const action = args.positionals[1];
|
|
104
107
|
if (action === "set")
|
|
105
|
-
return { json: (
|
|
106
|
-
return { json: (
|
|
108
|
+
return { json: loadExecBackendCli().backendAgentConfigSet(args.options) };
|
|
109
|
+
return { json: loadExecBackendCli().backendAgentConfigShow(args.options) };
|
|
107
110
|
},
|
|
108
111
|
});
|
|
109
112
|
// `backend.agent.config.set` shares the SAME dispatch path/handler as
|
|
@@ -124,12 +127,12 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.probe").mcp.handler = (args)
|
|
|
124
127
|
handler: (args) => {
|
|
125
128
|
const action = args.positionals[1];
|
|
126
129
|
if (action === "set")
|
|
127
|
-
return { json: (
|
|
128
|
-
return { json: (
|
|
130
|
+
return { json: loadExecBackendCli().backendAgentConfigSet(args.options) };
|
|
131
|
+
return { json: loadExecBackendCli().backendAgentConfigShow(args.options) };
|
|
129
132
|
},
|
|
130
133
|
});
|
|
131
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.show").mcp.handler = (args) => (
|
|
132
|
-
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.set").mcp.handler = (args) => (
|
|
134
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.show").mcp.handler = (args) => loadExecBackendCli().backendAgentConfigShow(args);
|
|
135
|
+
registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.set").mcp.handler = (args) => loadExecBackendCli().backendAgentConfigSet(args);
|
|
133
136
|
// PARITY: `backend.agent.config.set` mutates $CW_HOME/agent-config.json
|
|
134
137
|
// (secret-stripped) before returning the effective config; both surfaces
|
|
135
138
|
// perform the same write, so it is a documented opt-out from the
|
|
@@ -151,9 +154,24 @@ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.set").reason =
|
|
|
151
154
|
// renderer only appends "\n" when the text does NOT already end in
|
|
152
155
|
// one, so a bare `formatDoctorFixes(report)` here would silently
|
|
153
156
|
// drop the old build's trailing blank line.
|
|
154
|
-
const text = (0,
|
|
157
|
+
const text = (0, cli_args_1.wantsJson)(args.options) ? undefined : args.options.fix ? `${doctor.formatDoctorFixes(report)}\n` : doctor.formatDoctorReport(report);
|
|
155
158
|
return { json: report, text, exitCode: report.ok ? undefined : 1 };
|
|
156
159
|
},
|
|
160
|
+
// UI/UX fix: `cw help doctor` used to list only the one-line summary
|
|
161
|
+
// above, so a first-run user had no way to learn these flags without
|
|
162
|
+
// reading source. Real flag names verified against this handler and
|
|
163
|
+
// the `runDoctor(args.options, ...)` call it makes (shell/doctor.ts).
|
|
164
|
+
flags: [
|
|
165
|
+
{ name: "--onramp", summary: "Run the extra onramp checks (3-step quick start)." },
|
|
166
|
+
{ name: "--fix", summary: "Print fix commands instead of the full report." },
|
|
167
|
+
{ name: "--changed-from REF", summary: "Check the onramp only for the change since REF." },
|
|
168
|
+
{ name: "--json", summary: "Print the report as JSON." },
|
|
169
|
+
],
|
|
170
|
+
// The handler above and runDoctor (shell/doctor.ts) read exactly
|
|
171
|
+
// onramp/fix/changed-from/json plus the shared globals (cwd, format)
|
|
172
|
+
// — the list above is complete, so the dispatcher may warn about an
|
|
173
|
+
// unknown flag (TTY-only; see cli/global-flags.ts).
|
|
174
|
+
flagsComplete: true,
|
|
157
175
|
}, "Environment diagnostics are inherently local to the CLI host — Node version, $PATH, $CW_HOME/cwd writability. An MCP client diagnosing the server process's environment is not meaningful; agents already receive the same readiness facts in their typed results (e.g. status: blocked, agentConfigured). Inspired by `brew doctor`.");
|
|
158
176
|
(0, registry_core_1.addCliOnlyCapability)("fix", "Print consolidated fix commands for CW setup issues.", {
|
|
159
177
|
path: ["fix"],
|