cool-workflow 0.1.98 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +2 -0
  139. package/docs/cli-mcp-parity.7.md +280 -219
  140. package/docs/contract-migration-tooling.7.md +2 -0
  141. package/docs/control-plane-scheduling.7.md +2 -0
  142. package/docs/durable-state-and-locking.7.md +2 -0
  143. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  144. package/docs/execution-backends.7.md +2 -0
  145. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  146. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  147. package/docs/multi-agent-operator-ux.7.md +2 -0
  148. package/docs/node-snapshot-diff-replay.7.md +2 -0
  149. package/docs/observability-cost-accounting.7.md +2 -0
  150. package/docs/project-index.md +132 -71
  151. package/docs/real-execution-backends.7.md +2 -0
  152. package/docs/release-and-migration.7.md +2 -0
  153. package/docs/release-tooling.7.md +2 -0
  154. package/docs/run-registry-control-plane.7.md +2 -0
  155. package/docs/run-retention-reclamation.7.md +23 -0
  156. package/docs/state-explosion-management.7.md +2 -0
  157. package/docs/team-collaboration.7.md +2 -0
  158. package/docs/web-desktop-workbench.7.md +2 -0
  159. package/manifest/plugin.manifest.json +1 -1
  160. package/manifest/source-context-profiles.json +9 -13
  161. package/package.json +1 -1
  162. package/scripts/agents/cw-attest-wrap.js +2 -2
  163. package/scripts/bump-version.js +4 -3
  164. package/scripts/canonical-apps.js +4 -4
  165. package/scripts/dogfood-architecture-review.js +2 -3
  166. package/scripts/dogfood-release.js +3 -3
  167. package/scripts/gen-parity-doc.js +15 -2
  168. package/scripts/golden-path.js +4 -4
  169. package/scripts/onramp-check.js +1 -1
  170. package/scripts/parity-check.js +38 -21
  171. package/scripts/release-flow.js +2 -2
  172. package/scripts/sync-project-index.js +51 -27
  173. package/scripts/validate-run-state-schema.js +2 -2
  174. package/scripts/version-sync-check.js +30 -30
  175. package/dist/candidate-scoring.js +0 -729
  176. package/dist/capability-core.js +0 -1189
  177. package/dist/capability-registry.js +0 -885
  178. package/dist/cli/command-surface.js +0 -494
  179. package/dist/cli/format.js +0 -56
  180. package/dist/cli/handlers/audit.js +0 -82
  181. package/dist/cli/handlers/blackboard.js +0 -81
  182. package/dist/cli/handlers/candidate.js +0 -40
  183. package/dist/cli/handlers/clones.js +0 -34
  184. package/dist/cli/handlers/collaboration.js +0 -61
  185. package/dist/cli/handlers/eval.js +0 -40
  186. package/dist/cli/handlers/ledger.js +0 -169
  187. package/dist/cli/handlers/maintenance.js +0 -107
  188. package/dist/cli/handlers/multi-agent.js +0 -165
  189. package/dist/cli/handlers/node.js +0 -41
  190. package/dist/cli/handlers/operational.js +0 -155
  191. package/dist/cli/handlers/operator.js +0 -146
  192. package/dist/cli/handlers/registry.js +0 -68
  193. package/dist/cli/handlers/run.js +0 -153
  194. package/dist/cli/handlers/scheduling.js +0 -132
  195. package/dist/cli/handlers/workbench.js +0 -41
  196. package/dist/cli/handlers/worker.js +0 -45
  197. package/dist/cli/run-summary.js +0 -45
  198. package/dist/clones.js +0 -162
  199. package/dist/collaboration.js +0 -726
  200. package/dist/commit.js +0 -592
  201. package/dist/compare.js +0 -18
  202. package/dist/coordinator/classify.js +0 -45
  203. package/dist/coordinator/paths.js +0 -42
  204. package/dist/coordinator/util.js +0 -126
  205. package/dist/coordinator.js +0 -990
  206. package/dist/daemon.js +0 -44
  207. package/dist/dispatch.js +0 -250
  208. package/dist/drive.js +0 -864
  209. package/dist/error-feedback.js +0 -419
  210. package/dist/evidence-grounding.js +0 -184
  211. package/dist/execution-backend/agent.js +0 -354
  212. package/dist/execution-backend/probes.js +0 -112
  213. package/dist/execution-backend/util.js +0 -47
  214. package/dist/execution-backend.js +0 -961
  215. package/dist/gates.js +0 -48
  216. package/dist/harness.js +0 -61
  217. package/dist/ledger.js +0 -313
  218. package/dist/loop-expansion.js +0 -60
  219. package/dist/mcp/tool-call.js +0 -470
  220. package/dist/mcp/tool-definitions.js +0 -1066
  221. package/dist/mcp-surface.js +0 -30
  222. package/dist/multi-agent/graph.js +0 -84
  223. package/dist/multi-agent/helpers.js +0 -141
  224. package/dist/multi-agent/ids.js +0 -20
  225. package/dist/multi-agent/paths.js +0 -22
  226. package/dist/multi-agent-eval/normalize.js +0 -51
  227. package/dist/multi-agent-eval.js +0 -678
  228. package/dist/multi-agent-host.js +0 -777
  229. package/dist/multi-agent.js +0 -984
  230. package/dist/node-projection.js +0 -59
  231. package/dist/node-snapshot.js +0 -260
  232. package/dist/operator-ux.js +0 -631
  233. package/dist/orchestrator/app-operations.js +0 -211
  234. package/dist/orchestrator/audit-operations.js +0 -182
  235. package/dist/orchestrator/candidate-operations.js +0 -117
  236. package/dist/orchestrator/cli-options.js +0 -294
  237. package/dist/orchestrator/collaboration-operations.js +0 -86
  238. package/dist/orchestrator/feedback-operations.js +0 -81
  239. package/dist/orchestrator/host-operations.js +0 -78
  240. package/dist/orchestrator/lifecycle-operations.js +0 -650
  241. package/dist/orchestrator/migration-operations.js +0 -44
  242. package/dist/orchestrator/multi-agent-operations.js +0 -362
  243. package/dist/orchestrator/topology-operations.js +0 -84
  244. package/dist/orchestrator.js +0 -925
  245. package/dist/pipeline-runner.js +0 -285
  246. package/dist/reclamation/hash.js +0 -72
  247. package/dist/reclamation.js +0 -812
  248. package/dist/reporter.js +0 -67
  249. package/dist/run-export.js +0 -815
  250. package/dist/run-registry/derive.js +0 -175
  251. package/dist/run-registry/format.js +0 -124
  252. package/dist/run-registry/gc.js +0 -251
  253. package/dist/run-registry/policy.js +0 -16
  254. package/dist/run-registry/queue.js +0 -115
  255. package/dist/run-registry.js +0 -850
  256. package/dist/run-state-schema.js +0 -68
  257. package/dist/scheduling.js +0 -184
  258. package/dist/state-explosion.js +0 -1014
  259. package/dist/state.js +0 -367
  260. package/dist/telemetry-ledger.js +0 -196
  261. package/dist/topology.js +0 -565
  262. package/dist/triggers.js +0 -184
  263. package/dist/trust-audit.js +0 -644
  264. package/dist/types/blackboard.js +0 -2
  265. package/dist/types/candidate.js +0 -2
  266. package/dist/types/collaboration.js +0 -2
  267. package/dist/types/core.js +0 -2
  268. package/dist/types/drive.js +0 -10
  269. package/dist/types/error-feedback.js +0 -2
  270. package/dist/types/evidence-reasoning.js +0 -2
  271. package/dist/types/execution-backend.js +0 -2
  272. package/dist/types/multi-agent.js +0 -2
  273. package/dist/types/observability.js +0 -2
  274. package/dist/types/pipeline.js +0 -2
  275. package/dist/types/reclamation.js +0 -8
  276. package/dist/types/report-bundle.js +0 -6
  277. package/dist/types/result.js +0 -2
  278. package/dist/types/run-registry.js +0 -2
  279. package/dist/types/run.js +0 -2
  280. package/dist/types/sandbox.js +0 -2
  281. package/dist/types/schedule.js +0 -2
  282. package/dist/types/state-node.js +0 -2
  283. package/dist/types/topology.js +0 -2
  284. package/dist/types/trust.js +0 -2
  285. package/dist/types/workbench.js +0 -2
  286. package/dist/types/worker.js +0 -2
  287. package/dist/types/workflow-app.js +0 -2
  288. package/dist/types.js +0 -44
  289. package/dist/util/fingerprint.js +0 -19
  290. package/dist/util/fingerprint.test.js +0 -27
  291. package/dist/verifier.js +0 -78
  292. package/dist/version.js +0 -8
  293. package/dist/workbench-host.js +0 -192
  294. package/dist/workbench.js +0 -192
  295. package/dist/worker-accept/acceptance.js +0 -114
  296. package/dist/worker-accept/blackboard-fanout.js +0 -80
  297. package/dist/worker-accept/blackboard-linkage.js +0 -19
  298. package/dist/worker-accept/context.js +0 -2
  299. package/dist/worker-accept/telemetry-ledger.js +0 -126
  300. package/dist/worker-accept/validation.js +0 -77
  301. package/dist/worker-accept/verifier-completion.js +0 -73
  302. package/dist/worker-isolation/helpers.js +0 -51
  303. package/dist/worker-isolation/paths.js +0 -46
  304. package/dist/worker-isolation.js +0 -656
  305. package/dist/workflow-api.js +0 -131
  306. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,650 @@
1
+ "use strict";
2
+ // shell/workflow-app-loader.ts — the real workflow-app loader + discovery.
3
+ //
4
+ // See core/workflow-apps/app-schema.ts's file header for the validation
5
+ // half this file calls into. This file `require()`s real bundled apps'
6
+ // `app.json` + `workflow.js` (the SAME files
7
+ // plugins/cool-workflow/apps/<id>/ ships today, plus the legacy
8
+ // `workflows/<name>.workflow.js` factory files) and interprets the REAL
9
+ // manifest — it does not hard-code any specific app's behavior.
10
+ //
11
+ // Bundled app root resolution: `CW_APPS_DIR` env override (used by
12
+ // conformance/tests to point at a fixture tree), else the real
13
+ // `plugins/cool-workflow/apps` directory shipped alongside this build
14
+ // (walked up from this file's own location), else `<cwd>/apps`. The
15
+ // legacy `workflows/` root sits alongside `apps/` (one level up), same
16
+ // walk-up resolution, `CW_WORKFLOWS_DIR` override.
17
+ //
18
+ // MILESTONE 12 additions on top of the milestone 6+7 minimal loader
19
+ // (`loadWorkflowApp`, kept byte-for-byte — `shell/drive.ts` and
20
+ // `shell/pipeline-cli.ts` call it directly and must keep working):
21
+ // - `listWorkflowApps()` / `listWorkflows()` — full discovery over BOTH
22
+ // `apps/*/app.json` and legacy `workflows/*.workflow.js`, sorted by
23
+ // id then source path, fail-closed on a duplicate app id.
24
+ // - `loadWorkflowAppRecordById()` — the full LoadedWorkflowAppRecord
25
+ // (source kind, legacy flag, compatibility) `app show`/`app validate`
26
+ // need, vs. the minimal `LoadedWorkflowApp` `plan`/`drive` use.
27
+ // - `showWorkflowApp()` / `validateWorkflowAppTarget()` / `initApp()` /
28
+ // `packageApp()` — the `cw app show|validate|init|package` bodies.
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
41
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
42
+ }) : function(o, v) {
43
+ o["default"] = v;
44
+ });
45
+ var __importStar = (this && this.__importStar) || (function () {
46
+ var ownKeys = function(o) {
47
+ ownKeys = Object.getOwnPropertyNames || function (o) {
48
+ var ar = [];
49
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
50
+ return ar;
51
+ };
52
+ return ownKeys(o);
53
+ };
54
+ return function (mod) {
55
+ if (mod && mod.__esModule) return mod;
56
+ var result = {};
57
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
58
+ __setModuleDefault(result, mod);
59
+ return result;
60
+ };
61
+ })();
62
+ Object.defineProperty(exports, "__esModule", { value: true });
63
+ exports.WorkflowAppNotFoundError = void 0;
64
+ exports.loadWorkflowApp = loadWorkflowApp;
65
+ exports.listWorkflowAppRecords = listWorkflowAppRecords;
66
+ exports.loadWorkflowAppRecordById = loadWorkflowAppRecordById;
67
+ exports.listWorkflowApps = listWorkflowApps;
68
+ exports.listWorkflowsShallow = listWorkflowsShallow;
69
+ exports.showWorkflowApp = showWorkflowApp;
70
+ exports.validateWorkflowAppTarget = validateWorkflowAppTarget;
71
+ exports.initWorkflowApp = initWorkflowApp;
72
+ exports.packageWorkflowApp = packageWorkflowApp;
73
+ const fs = __importStar(require("node:fs"));
74
+ const path = __importStar(require("node:path"));
75
+ const app_schema_1 = require("../core/workflow-apps/app-schema");
76
+ const sandbox_profile_1 = require("./sandbox-profile");
77
+ const version_1 = require("../core/version");
78
+ class WorkflowAppNotFoundError extends Error {
79
+ constructor(appId) {
80
+ super(`Workflow app not found: ${appId}`);
81
+ this.name = "WorkflowAppNotFoundError";
82
+ }
83
+ }
84
+ exports.WorkflowAppNotFoundError = WorkflowAppNotFoundError;
85
+ function walkUpFor(...tail) {
86
+ const roots = [];
87
+ let dir = __dirname;
88
+ for (let i = 0; i < 8; i++) {
89
+ roots.push(path.join(dir, "plugins", "cool-workflow", ...tail));
90
+ const parent = path.dirname(dir);
91
+ if (parent === dir)
92
+ break;
93
+ dir = parent;
94
+ }
95
+ return roots;
96
+ }
97
+ /** Walk up from this file's own location and, at each ancestor that looks
98
+ * like the package root (has a package.json), yield `<pkgRoot>/<tail>`.
99
+ * On a published/globally-installed package the bundled `apps/`+`workflows/`
100
+ * sit at the PACKAGE ROOT (`<pkg>/apps`), NOT under a nested
101
+ * `plugins/cool-workflow/` segment — walkUpFor above only ever builds the
102
+ * nested monorepo path, so a global install found 0 apps. This restores the
103
+ * old build's resolvePluginRoot behavior (find the dir with package.json,
104
+ * then read `<pluginRoot>/apps`). The loader lives at
105
+ * `<pkg>/dist/shell/workflow-app-loader.js`, so `<pkg>` is two hops up. */
106
+ function walkUpForPackageRoot(...tail) {
107
+ const roots = [];
108
+ let dir = __dirname;
109
+ for (let i = 0; i < 8; i++) {
110
+ if (fs.existsSync(path.join(dir, "package.json"))) {
111
+ roots.push(path.join(dir, ...tail));
112
+ }
113
+ const parent = path.dirname(dir);
114
+ if (parent === dir)
115
+ break;
116
+ dir = parent;
117
+ }
118
+ return roots;
119
+ }
120
+ function candidateAppsRoots() {
121
+ const roots = [];
122
+ if (process.env.CW_APPS_DIR)
123
+ roots.push(path.resolve(process.env.CW_APPS_DIR));
124
+ // Walk up from this file's own location looking for a sibling
125
+ // plugins/cool-workflow/apps tree (the real bundled apps ship there in
126
+ // this monorepo checkout).
127
+ roots.push(...walkUpFor("apps"));
128
+ // ...and for the package-root `<pkg>/apps` tree an npm-installed (global or
129
+ // local) package ships. Placed before the cwd fallback so bundled apps win.
130
+ roots.push(...walkUpForPackageRoot("apps"));
131
+ roots.push(path.join(process.cwd(), "apps"));
132
+ return roots;
133
+ }
134
+ function candidateWorkflowsRoots() {
135
+ const roots = [];
136
+ if (process.env.CW_WORKFLOWS_DIR)
137
+ roots.push(path.resolve(process.env.CW_WORKFLOWS_DIR));
138
+ roots.push(...walkUpFor("workflows"));
139
+ roots.push(...walkUpForPackageRoot("workflows"));
140
+ roots.push(path.join(process.cwd(), "workflows"));
141
+ return roots;
142
+ }
143
+ function findAppDir(appId) {
144
+ for (const root of candidateAppsRoots()) {
145
+ const dir = path.join(root, appId);
146
+ if (fs.existsSync(path.join(dir, "app.json")))
147
+ return dir;
148
+ }
149
+ return undefined;
150
+ }
151
+ function validationContext() {
152
+ return { bundledSandboxProfileIds: (0, sandbox_profile_1.bundledSandboxProfileIds)(), currentCoolWorkflowVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION };
153
+ }
154
+ function authorNameOf(author) {
155
+ if (typeof author === "string")
156
+ return author;
157
+ if (author && typeof author === "object" && typeof author.name === "string")
158
+ return author.name;
159
+ return undefined;
160
+ }
161
+ /** Convert a discovery record (from listWorkflowAppRecords) into the minimal
162
+ * LoadedWorkflowApp shape `plan`/`drive` consume. Used as the fallback path
163
+ * in loadWorkflowApp so a legacy `<name>.workflow.js` wrapper — which `cw
164
+ * list`/`cw app list` already resolve as a record but which has no
165
+ * `apps/<id>/app.json` directory — can also be planned by id. Without this,
166
+ * `cw plan legacy-research-synthesis` died "Workflow app not found" even
167
+ * though `cw list` shows it. */
168
+ function loadedAppFromRecord(record) {
169
+ const workflowDefinition = record.app.workflow;
170
+ return {
171
+ id: record.app.id,
172
+ title: record.app.title,
173
+ summary: record.app.summary || workflowDefinition.summary || "",
174
+ version: record.app.version,
175
+ author: authorNameOf(record.app.author),
176
+ workflow: workflowDefinition,
177
+ sandboxProfiles: record.app.sandboxProfiles || workflowDefinition.sandboxProfiles || [],
178
+ sourcePath: sourcePathOf(record),
179
+ compatibility: record.app.compatibility,
180
+ metadata: record.app.metadata,
181
+ };
182
+ }
183
+ /** Load one real bundled workflow app by id: reads its `app.json`
184
+ * manifest, then `require()`s its declared `workflow.entrypoint` factory
185
+ * file (the same `({workflow, phase, parallel, agent, artifact, input})
186
+ * => workflow({...})` shape the old build's workflow-api.ts factories
187
+ * produce), and returns the interpreted WorkflowDefinition. A legacy
188
+ * workflow-file id (no `apps/<id>/app.json`) resolves through the record
189
+ * fallback below. */
190
+ function loadWorkflowApp(appId) {
191
+ const dir = findAppDir(appId);
192
+ if (!dir) {
193
+ // No `apps/<id>/app.json` directory: fall back to the full discovery set
194
+ // (app directories + legacy `<name>.workflow.js` wrappers) so a legacy
195
+ // workflow-file app id resolves for plan/drive the same way `cw list`
196
+ // resolves it. loadWorkflowAppRecordById throws WorkflowAppNotFoundError
197
+ // when nothing matches, preserving the not-found contract.
198
+ return loadedAppFromRecord(loadWorkflowAppRecordById(appId));
199
+ }
200
+ const manifestPath = path.join(dir, "app.json");
201
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
202
+ const entrypointPath = path.resolve(dir, manifest.workflow.entrypoint);
203
+ // Workflow apps are plain runtime JavaScript, not TypeScript.
204
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
205
+ const rawExport = require(entrypointPath);
206
+ const selected = manifest.workflow.exportName && rawExport && typeof rawExport === "object" ? rawExport[manifest.workflow.exportName] : rawExport;
207
+ if (typeof selected !== "function") {
208
+ throw new Error(`Workflow app entrypoint must export a factory function: ${entrypointPath}`);
209
+ }
210
+ const definition = selected({
211
+ ...(0, app_schema_1.createWorkflowApi)(),
212
+ parallel: app_schema_1.parallel,
213
+ phase: app_schema_1.phase,
214
+ workflow: app_schema_1.workflow,
215
+ agent: app_schema_1.agent,
216
+ artifact: app_schema_1.artifact,
217
+ input: app_schema_1.input,
218
+ });
219
+ return {
220
+ id: manifest.id,
221
+ title: manifest.title,
222
+ summary: manifest.summary || definition.summary || "",
223
+ version: manifest.version,
224
+ author: authorNameOf(manifest.author),
225
+ workflow: definition,
226
+ sandboxProfiles: manifest.sandboxProfiles || definition.sandboxProfiles || [],
227
+ sourcePath: manifestPath,
228
+ // Thread the manifest's compatibility window + metadata (incl. domain)
229
+ // into the loaded app so workflowAppRunMetadata can stamp them onto
230
+ // run.workflow.app — this is what lets report.md label a research-domain
231
+ // run's source line "Source:" instead of "Repository:".
232
+ compatibility: manifest.compatibility,
233
+ metadata: manifest.metadata,
234
+ };
235
+ }
236
+ // ---------------------------------------------------------------------
237
+ // Full discovery: apps/*/app.json + legacy workflows/*.workflow.js
238
+ // (MILESTONE 12). Byte-exact in spirit to
239
+ // src/orchestrator/app-operations.ts's loadWorkflowApps/listApps/showApp/
240
+ // validateApp/initApp/packageApp.
241
+ // ---------------------------------------------------------------------
242
+ const FACTORY_API = { ...(0, app_schema_1.createWorkflowApi)(), parallel: app_schema_1.parallel, phase: app_schema_1.phase, loop: app_schema_1.loop, workflow: app_schema_1.workflow, agent: app_schema_1.agent, artifact: app_schema_1.artifact, subWorkflow: app_schema_1.subWorkflow, input: app_schema_1.input };
243
+ function materializeFactoryExport(entrypointPath, exportName) {
244
+ if (!fs.existsSync(entrypointPath)) {
245
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app entrypoint", [
246
+ { code: "workflow-app-entrypoint-not-found", message: `Workflow app entrypoint does not exist: ${entrypointPath}`, path: entrypointPath },
247
+ ]);
248
+ }
249
+ // Workflow apps are plain runtime JavaScript, not TypeScript.
250
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
251
+ const rawExport = require(entrypointPath);
252
+ const selected = exportName && rawExport && typeof rawExport === "object" ? rawExport[exportName] : rawExport;
253
+ if (exportName && selected === undefined) {
254
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app entrypoint", [
255
+ { code: "workflow-app-entrypoint-export", message: `Workflow app entrypoint does not export ${exportName}`, path: entrypointPath },
256
+ ]);
257
+ }
258
+ if (typeof selected === "function") {
259
+ return selected(FACTORY_API);
260
+ }
261
+ return selected;
262
+ }
263
+ function isWorkflowDefinitionShape(value) {
264
+ return Boolean(value &&
265
+ typeof value === "object" &&
266
+ !Array.isArray(value) &&
267
+ typeof value.id === "string" &&
268
+ typeof value.title === "string" &&
269
+ Array.isArray(value.phases));
270
+ }
271
+ function isWorkflowAppDefinitionShape(value) {
272
+ return Boolean(value && typeof value === "object" && value.schemaVersion === 1 && "workflow" in value);
273
+ }
274
+ function extractFactoryExport(value, file) {
275
+ if (isWorkflowAppDefinitionShape(value) && isWorkflowDefinitionShape(value.workflow)) {
276
+ return { app: value, workflow: value.workflow };
277
+ }
278
+ if (isWorkflowDefinitionShape(value)) {
279
+ return { workflow: value };
280
+ }
281
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app entrypoint", [
282
+ { code: "workflow-app-entrypoint-export", message: "Workflow app entrypoint must export a workflow definition, workflow app, or factory", path: file },
283
+ ]);
284
+ }
285
+ function collectWorkflowSandboxProfiles(definition) {
286
+ const profiles = new Set();
287
+ for (const phaseDefinition of definition.phases) {
288
+ for (const taskDefinition of phaseDefinition.tasks) {
289
+ if (taskDefinition.sandboxProfileId)
290
+ profiles.add(taskDefinition.sandboxProfileId);
291
+ }
292
+ }
293
+ return [...profiles].sort();
294
+ }
295
+ function createLegacyWorkflowApp(definition, source) {
296
+ return {
297
+ schemaVersion: 1,
298
+ id: definition.id,
299
+ title: definition.title,
300
+ summary: definition.summary || "",
301
+ version: "0.0.0",
302
+ workflow: definition,
303
+ inputs: definition.inputs || [],
304
+ sandboxProfiles: definition.sandboxProfiles || collectWorkflowSandboxProfiles(definition),
305
+ compatibility: { maxVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION, notes: "Compatibility wrapper for legacy .workflow.js factory files." },
306
+ metadata: { legacyWorkflow: true, sourcePath: source.path },
307
+ };
308
+ }
309
+ /** Loads one workflow app from a legacy `<name>.workflow.js` entrypoint
310
+ * (no `app.json`). Ported from `loadWorkflowAppFromEntrypoint`. */
311
+ function loadWorkflowAppFromEntrypoint(file) {
312
+ const entrypointPath = path.resolve(file);
313
+ const materialized = materializeFactoryExport(entrypointPath);
314
+ const loaded = extractFactoryExport(materialized, entrypointPath);
315
+ const source = { kind: "workflow-file", path: entrypointPath, entrypointPath };
316
+ if (loaded.app) {
317
+ const app = { ...loaded.app, workflow: loaded.workflow };
318
+ assertValid(app, { appPath: entrypointPath });
319
+ return { app, source, legacy: false };
320
+ }
321
+ const app = createLegacyWorkflowApp(loaded.workflow, source);
322
+ assertValid(app, { appPath: entrypointPath });
323
+ return { app, source, legacy: true };
324
+ }
325
+ function validateEntrypointAppMatchesManifest(entrypointApp, manifest, manifestPath) {
326
+ const issues = [];
327
+ for (const key of ["schemaVersion", "id", "title", "version"]) {
328
+ if (entrypointApp[key] !== manifest[key]) {
329
+ issues.push({ code: "workflow-app-manifest-mismatch", message: `Entrypoint app ${key} must match manifest ${key}`, path: `${manifestPath}.${key}` });
330
+ }
331
+ }
332
+ if (issues.length)
333
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", issues);
334
+ }
335
+ function assertValid(app, options) {
336
+ const result = (0, app_schema_1.validateWorkflowApp)(app, validationContext(), options);
337
+ if (!result.valid)
338
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app", result.issues);
339
+ }
340
+ /** Loads one workflow app from an `app.json` manifest path. Ported from
341
+ * `loadWorkflowAppFromManifest`. */
342
+ function loadWorkflowAppFromManifest(manifestPath) {
343
+ const absoluteManifestPath = path.resolve(manifestPath);
344
+ if (!fs.existsSync(absoluteManifestPath)) {
345
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", [
346
+ { code: "workflow-app-manifest-not-found", message: `Workflow app manifest does not exist: ${absoluteManifestPath}`, path: absoluteManifestPath },
347
+ ]);
348
+ }
349
+ let manifest;
350
+ try {
351
+ manifest = JSON.parse(fs.readFileSync(absoluteManifestPath, "utf8"));
352
+ }
353
+ catch (error) {
354
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", [
355
+ {
356
+ code: "workflow-app-manifest-json",
357
+ message: `Workflow app manifest is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
358
+ path: absoluteManifestPath,
359
+ },
360
+ ]);
361
+ }
362
+ const manifestValidation = (0, app_schema_1.validateWorkflowApp)(manifest, validationContext(), { appPath: absoluteManifestPath });
363
+ if (!manifestValidation.valid) {
364
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", manifestValidation.issues);
365
+ }
366
+ const workflowField = manifest.workflow;
367
+ const isEntrypoint = Boolean(workflowField && typeof workflowField === "object" && "entrypoint" in workflowField && !("phases" in workflowField));
368
+ if (!isEntrypoint) {
369
+ throw new app_schema_1.WorkflowAppValidationError("Invalid workflow app manifest", [
370
+ { code: "workflow-app-entrypoint", message: "Manifest workflow must be an entrypoint object", path: `${absoluteManifestPath}.workflow` },
371
+ ]);
372
+ }
373
+ const entrypointPath = path.resolve(path.dirname(absoluteManifestPath), manifest.workflow.entrypoint);
374
+ const materialized = materializeFactoryExport(entrypointPath, manifest.workflow.exportName);
375
+ const loaded = extractFactoryExport(materialized, entrypointPath);
376
+ const workflowDefinition = loaded.workflow;
377
+ const source = {
378
+ kind: path.basename(absoluteManifestPath) === "app.json" ? "app-directory" : "app-manifest",
379
+ path: path.dirname(absoluteManifestPath),
380
+ manifestPath: absoluteManifestPath,
381
+ entrypointPath,
382
+ };
383
+ if (loaded.app) {
384
+ validateEntrypointAppMatchesManifest(loaded.app, manifest, absoluteManifestPath);
385
+ }
386
+ const app = { ...manifest, workflow: workflowDefinition };
387
+ assertValid(app, { appPath: absoluteManifestPath, loadedWorkflow: workflowDefinition });
388
+ return { app, source, legacy: false };
389
+ }
390
+ function loadWorkflowFiles(workflowsDir) {
391
+ if (!fs.existsSync(workflowsDir))
392
+ return [];
393
+ return fs
394
+ .readdirSync(workflowsDir)
395
+ .filter((file) => file.endsWith(".workflow.js"))
396
+ .sort()
397
+ .map((file) => path.join(workflowsDir, file));
398
+ }
399
+ function loadAppManifestFiles(appsDir) {
400
+ if (!fs.existsSync(appsDir))
401
+ return [];
402
+ return fs
403
+ .readdirSync(appsDir, { withFileTypes: true })
404
+ .filter((entry) => entry.isDirectory())
405
+ .map((entry) => path.join(appsDir, entry.name, "app.json"))
406
+ .filter((file) => fs.existsSync(file))
407
+ .sort();
408
+ }
409
+ function sourcePathOf(record) {
410
+ return record.source.manifestPath || record.source.entrypointPath || record.source.path;
411
+ }
412
+ /** Full discovery over every real bundled app + legacy workflow file: one
413
+ * root each, walked up from this file's own location (or `CW_APPS_DIR`/
414
+ * `CW_WORKFLOWS_DIR`). Sorted by app id then source path; a duplicate id
415
+ * across BOTH roots is a fail-closed error, matching
416
+ * `loadWorkflowApps` in the old build. */
417
+ function listWorkflowAppRecords() {
418
+ const appsDir = candidateAppsRoots().find((root) => fs.existsSync(root)) || candidateAppsRoots()[0];
419
+ const workflowsDir = candidateWorkflowsRoots().find((root) => fs.existsSync(root)) || candidateWorkflowsRoots()[0];
420
+ const records = [
421
+ ...loadWorkflowFiles(workflowsDir).map((file) => loadWorkflowAppFromEntrypoint(file)),
422
+ ...loadAppManifestFiles(appsDir).map((file) => loadWorkflowAppFromManifest(file)),
423
+ ].sort((left, right) => {
424
+ const byId = left.app.id.localeCompare(right.app.id);
425
+ if (byId)
426
+ return byId;
427
+ return sourcePathOf(left).localeCompare(sourcePathOf(right));
428
+ });
429
+ const seen = new Map();
430
+ for (const record of records) {
431
+ const previous = seen.get(record.app.id);
432
+ if (previous) {
433
+ throw new Error(`Duplicate workflow app id ${record.app.id}: ${sourcePathOf(previous)} and ${sourcePathOf(record)}`);
434
+ }
435
+ seen.set(record.app.id, record);
436
+ }
437
+ return records;
438
+ }
439
+ function loadWorkflowAppRecordById(appId) {
440
+ const record = listWorkflowAppRecords().find((candidate) => candidate.app.id === appId);
441
+ if (!record)
442
+ throw new WorkflowAppNotFoundError(appId);
443
+ return record;
444
+ }
445
+ /** Resolves a `cw app validate <path-or-id>` / `cw app show <path-or-id>`
446
+ * target: an existing directory/`app.json`/other-file path wins over an
447
+ * id lookup, matching `loadWorkflowAppTarget` in the old build. */
448
+ function loadWorkflowAppRecordTarget(target) {
449
+ if (!target)
450
+ throw new Error("Missing workflow app path or id");
451
+ const resolved = path.resolve(target);
452
+ if (fs.existsSync(resolved)) {
453
+ const stat = fs.statSync(resolved);
454
+ if (stat.isDirectory())
455
+ return loadWorkflowAppFromManifest(path.join(resolved, "app.json"));
456
+ if (path.basename(resolved) === "app.json" || resolved.endsWith(".json"))
457
+ return loadWorkflowAppFromManifest(resolved);
458
+ return loadWorkflowAppFromEntrypoint(resolved);
459
+ }
460
+ return loadWorkflowAppRecordById(target);
461
+ }
462
+ function summarizeWorkflowAppRecord(record) {
463
+ const workflowDefinition = record.app.workflow;
464
+ const sandboxProfiles = record.app.sandboxProfiles || workflowDefinition.sandboxProfiles || [];
465
+ return {
466
+ id: record.app.id,
467
+ title: record.app.title,
468
+ summary: record.app.summary || workflowDefinition.summary || "",
469
+ version: record.app.version,
470
+ author: record.app.author,
471
+ file: sourcePathOf(record),
472
+ sourceKind: record.source.kind,
473
+ legacy: record.legacy,
474
+ compatible: (0, app_schema_1.isWorkflowAppCompatible)(record.app, validationContext()),
475
+ inputs: record.app.inputs || workflowDefinition.inputs || [],
476
+ sandboxProfiles,
477
+ phases: workflowDefinition.phases.map((phaseDefinition) => ({ id: phaseDefinition.id, name: phaseDefinition.name, taskCount: phaseDefinition.tasks.length })),
478
+ taskCount: workflowDefinition.phases.reduce((total, phaseDefinition) => total + phaseDefinition.tasks.length, 0),
479
+ };
480
+ }
481
+ /** `cw app list` / `cw_app_list`. Key order matches `WorkflowAppSummary`;
482
+ * a legacy app's `author` key is OMITTED (never present, never null) —
483
+ * callers must build the JSON object with the same conditional-spread
484
+ * discipline used here. */
485
+ function listWorkflowApps() {
486
+ return listWorkflowAppRecords().map((record) => summaryToJson(summarizeWorkflowAppRecord(record)));
487
+ }
488
+ function summaryToJson(summary) {
489
+ const { author, ...rest } = summary;
490
+ return {
491
+ id: rest.id,
492
+ title: rest.title,
493
+ summary: rest.summary,
494
+ version: rest.version,
495
+ ...(author !== undefined ? { author } : {}),
496
+ file: rest.file,
497
+ sourceKind: rest.sourceKind,
498
+ legacy: rest.legacy,
499
+ compatible: rest.compatible,
500
+ inputs: rest.inputs,
501
+ sandboxProfiles: rest.sandboxProfiles,
502
+ phases: rest.phases,
503
+ taskCount: rest.taskCount,
504
+ };
505
+ }
506
+ /** `cw list` / `cw_list` — the lighter {id,title,summary,file} view over
507
+ * the SAME discovered app set. */
508
+ function listWorkflowsShallow() {
509
+ return listWorkflowAppRecords().map((record) => {
510
+ const summary = summarizeWorkflowAppRecord(record);
511
+ return { id: summary.id, title: summary.title, summary: summary.summary, file: summary.file };
512
+ });
513
+ }
514
+ /** `cw app show <id>` / `cw_app_show`. Throws `WorkflowAppNotFoundError`
515
+ * on an unknown id (`recoveryHint` in cli/entry.ts turns this into the
516
+ * `Try: cw app list` tip). */
517
+ function showWorkflowApp(appId) {
518
+ const record = loadWorkflowAppRecordById(appId);
519
+ const summary = summarizeWorkflowAppRecord(record);
520
+ return {
521
+ ...summaryToJson(summary),
522
+ source: record.source,
523
+ app: {
524
+ schemaVersion: record.app.schemaVersion,
525
+ id: record.app.id,
526
+ title: record.app.title,
527
+ summary: record.app.summary || "",
528
+ version: record.app.version,
529
+ author: record.app.author,
530
+ inputs: record.app.inputs || record.app.workflow.inputs,
531
+ sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles || [],
532
+ compatibility: record.app.compatibility,
533
+ metadata: record.app.metadata || {},
534
+ },
535
+ workflow: {
536
+ id: record.app.workflow.id,
537
+ title: record.app.workflow.title,
538
+ summary: record.app.workflow.summary || "",
539
+ limits: record.app.workflow.limits,
540
+ inputs: record.app.workflow.inputs,
541
+ sandboxProfiles: record.app.workflow.sandboxProfiles || [],
542
+ phases: record.app.workflow.phases.map((phaseDefinition) => ({
543
+ id: phaseDefinition.id,
544
+ name: phaseDefinition.name,
545
+ status: phaseDefinition.status,
546
+ tasks: phaseDefinition.tasks.map((taskDefinition) => ({
547
+ id: taskDefinition.id,
548
+ kind: taskDefinition.kind,
549
+ requiresEvidence: Boolean(taskDefinition.requiresEvidence),
550
+ sandboxProfileId: taskDefinition.sandboxProfileId,
551
+ })),
552
+ })),
553
+ },
554
+ };
555
+ }
556
+ /** `cw app validate <path-or-id>` / `cw_app_validate`. Never throws — an
557
+ * unresolvable target gives `{valid:false, appId:target, appPath:target,
558
+ * issues}`, matching `validateApp` in the old build. */
559
+ function validateWorkflowAppTarget(target) {
560
+ const resolvedTarget = path.resolve(target);
561
+ try {
562
+ const record = loadWorkflowAppRecordTarget(target);
563
+ const result = (0, app_schema_1.validateWorkflowApp)(record.app, validationContext(), { appPath: sourcePathOf(record) });
564
+ return { ...result, summary: summarizeWorkflowAppRecord(record) };
565
+ }
566
+ catch (error) {
567
+ return { valid: false, appId: target, appPath: resolvedTarget, issues: (0, app_schema_1.validationIssuesFromError)(error) };
568
+ }
569
+ }
570
+ const SYSTEM_DIR_PATTERN = /^\/(etc|bin|sbin|usr|Library|System|Applications|boot|dev|proc|sys|root|var\/log|var\/run)\//;
571
+ function titleize(value) {
572
+ return value
573
+ .split("-")
574
+ .filter(Boolean)
575
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
576
+ .join(" ");
577
+ }
578
+ function renderManifestTemplate(id, title) {
579
+ return `${JSON.stringify({
580
+ schemaVersion: 1,
581
+ id,
582
+ title,
583
+ summary: "Describe what this workflow app does.",
584
+ version: "0.1.0",
585
+ author: "COOLWHITE LLC",
586
+ inputs: [{ name: "question", type: "string", required: true, description: "Question or task this workflow should answer." }],
587
+ sandboxProfiles: ["readonly"],
588
+ compatibility: { minVersion: "0.1.9" },
589
+ workflow: { entrypoint: "workflow.js" },
590
+ }, null, 2)}\n`;
591
+ }
592
+ function renderEntrypointTemplate(id, title) {
593
+ 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`;
594
+ }
595
+ /** `cw app init <id>` / `cw_app_init`. Writes `app.json` + `workflow.js`
596
+ * from the templates, refusing system directories and (without
597
+ * `--force`) an existing app. Ported from `initApp`. */
598
+ function initWorkflowApp(appId, options = {}) {
599
+ const id = (0, app_schema_1.slugify)(appId);
600
+ if (!id)
601
+ throw new Error("App id must include at least one letter or digit");
602
+ const title = String(options.title || titleize(id));
603
+ const appsDir = candidateAppsRoots().find((root) => fs.existsSync(root)) || candidateAppsRoots()[0];
604
+ const destinationDir = path.resolve(String(options.directory || options.output || path.join(appsDir, id)));
605
+ if (SYSTEM_DIR_PATTERN.test(destinationDir)) {
606
+ throw new Error(`Refusing to create app in a system directory: ${destinationDir}`);
607
+ }
608
+ const manifestPath = path.join(destinationDir, "app.json");
609
+ const entrypointPath = path.join(destinationDir, "workflow.js");
610
+ if (!options.force && (fs.existsSync(manifestPath) || fs.existsSync(entrypointPath))) {
611
+ throw new Error(`Refusing to overwrite existing workflow app: ${destinationDir}`);
612
+ }
613
+ fs.mkdirSync(destinationDir, { recursive: true });
614
+ fs.writeFileSync(manifestPath, renderManifestTemplate(id, title), "utf8");
615
+ fs.writeFileSync(entrypointPath, renderEntrypointTemplate(id, title), "utf8");
616
+ const validation = validateWorkflowAppTarget(manifestPath);
617
+ if (!validation.valid) {
618
+ throw new app_schema_1.WorkflowAppValidationError("Generated workflow app is invalid", validation.issues);
619
+ }
620
+ return { id, manifestPath, entrypointPath };
621
+ }
622
+ /** `cw app package <id>` / `cw_app_package`. Ported from `packageApp`. The
623
+ * default package path is anchored under the caller's explicit `cwd`
624
+ * (`options.cwd`) so the CLI/MCP surfaces never write into the process cwd
625
+ * when a different working directory was requested — and never chdir. */
626
+ function packageWorkflowApp(appId, options = {}, cwd) {
627
+ const base = cwd || (typeof options.cwd === "string" && options.cwd.trim() ? path.resolve(String(options.cwd)) : process.cwd());
628
+ const record = loadWorkflowAppRecordById(appId);
629
+ const destination = path.resolve(base, String(options.output || path.join(".cw", "packages", `${record.app.id}-${record.app.version}.cwapp.json`)));
630
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
631
+ const payload = {
632
+ schemaVersion: 1,
633
+ app: {
634
+ schemaVersion: 1,
635
+ id: record.app.id,
636
+ title: record.app.title,
637
+ summary: record.app.summary || record.app.workflow.summary || "",
638
+ version: record.app.version,
639
+ author: record.app.author,
640
+ compatibility: record.app.compatibility,
641
+ sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles,
642
+ source: record.source,
643
+ metadata: record.app.metadata,
644
+ },
645
+ workflow: record.app.workflow,
646
+ packagedAt: new Date().toISOString(),
647
+ };
648
+ fs.writeFileSync(destination, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
649
+ return { id: record.app.id, version: record.app.version, path: destination };
650
+ }
@@ -413,3 +413,5 @@ The one-command `cw -q` headline now routes the question and defaults the repo t
413
413
  0.1.97
414
414
 
415
415
  0.1.98
416
+
417
+ 0.2.0