@aiwg/cli 2026.10.1 → 2026.10.4

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 (361) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/capability-matrix.yaml +270 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +3183 -24
  4. package/bin/aiwg.mjs +135 -47
  5. package/dist/src/a2a/codecs.js +9 -0
  6. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  7. package/dist/src/artifacts/browser-export.js +6 -0
  8. package/dist/src/artifacts/checksum-manifest.js +1 -0
  9. package/dist/src/artifacts/citation-parser.js +173 -92
  10. package/dist/src/artifacts/citation-table-schema.js +59 -0
  11. package/dist/src/artifacts/cli.js +57 -18
  12. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  13. package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
  14. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  15. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  16. package/dist/src/artifacts/discovery-eval.js +6 -4
  17. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  18. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  19. package/dist/src/artifacts/framework-capabilities.js +171 -0
  20. package/dist/src/artifacts/framework-index-paths.js +73 -0
  21. package/dist/src/artifacts/framework-source-digest.js +75 -0
  22. package/dist/src/artifacts/index-builder.js +285 -131
  23. package/dist/src/artifacts/index-files.js +132 -18
  24. package/dist/src/artifacts/index-preparation.js +65 -0
  25. package/dist/src/artifacts/index-refresh.js +576 -0
  26. package/dist/src/artifacts/index-worker.mjs +9 -0
  27. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  28. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  29. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  30. package/dist/src/artifacts/query-engine.js +410 -88
  31. package/dist/src/artifacts/relations-builder.js +168 -0
  32. package/dist/src/artifacts/relations.js +148 -0
  33. package/dist/src/artifacts/repair.js +9 -4
  34. package/dist/src/artifacts/stats.js +5 -2
  35. package/dist/src/artifacts/types.js +30 -10
  36. package/dist/src/artifacts/watcher.js +1 -1
  37. package/dist/src/auth/credential-store.js +2 -1
  38. package/dist/src/catalog/cli.d.mts +5 -0
  39. package/dist/src/channel/manager.d.mts +20 -0
  40. package/dist/src/channel/manager.mjs +78 -12
  41. package/dist/src/cli/child-process-result.js +40 -0
  42. package/dist/src/cli/command-log.js +2 -3
  43. package/dist/src/cli/env.js +38 -16
  44. package/dist/src/cli/flag-parsing.js +236 -0
  45. package/dist/src/cli/handlers/artifacts.js +3 -0
  46. package/dist/src/cli/handlers/auth.js +3 -0
  47. package/dist/src/cli/handlers/decision.js +218 -5
  48. package/dist/src/cli/handlers/diagnose.js +13 -5
  49. package/dist/src/cli/handlers/effect.js +4 -2
  50. package/dist/src/cli/handlers/evidence.js +3 -0
  51. package/dist/src/cli/handlers/feedback.js +14 -5
  52. package/dist/src/cli/handlers/help.js +1 -0
  53. package/dist/src/cli/handlers/index.js +3 -1
  54. package/dist/src/cli/handlers/init.js +248 -30
  55. package/dist/src/cli/handlers/lint.js +5 -0
  56. package/dist/src/cli/handlers/mc.js +23 -0
  57. package/dist/src/cli/handlers/models.js +169 -10
  58. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  59. package/dist/src/cli/handlers/ralph.js +3 -0
  60. package/dist/src/cli/handlers/refresh.js +28 -0
  61. package/dist/src/cli/handlers/regenerate.js +14 -0
  62. package/dist/src/cli/handlers/resource-versions.js +3 -0
  63. package/dist/src/cli/handlers/rules.js +195 -0
  64. package/dist/src/cli/handlers/run.js +4 -0
  65. package/dist/src/cli/handlers/runtime-info.js +25 -11
  66. package/dist/src/cli/handlers/script-runner.js +19 -8
  67. package/dist/src/cli/handlers/session.js +406 -62
  68. package/dist/src/cli/handlers/sessions.js +243 -76
  69. package/dist/src/cli/handlers/setup-manifest.js +474 -79
  70. package/dist/src/cli/handlers/setup.js +180 -39
  71. package/dist/src/cli/handlers/steward.js +11 -0
  72. package/dist/src/cli/handlers/subcommands.js +60 -3
  73. package/dist/src/cli/handlers/team.js +18 -8
  74. package/dist/src/cli/handlers/uhp.js +3 -0
  75. package/dist/src/cli/handlers/use.js +307 -55
  76. package/dist/src/cli/handlers/utilities.js +125 -26
  77. package/dist/src/cli/handlers/version.js +8 -0
  78. package/dist/src/cli/prompt-utils.js +72 -0
  79. package/dist/src/cli/provider-resolution.js +11 -0
  80. package/dist/src/cli/router.js +15 -1
  81. package/dist/src/cli/services/deployment-verification.js +19 -9
  82. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  83. package/dist/src/cli/skill-usage.js +2 -3
  84. package/dist/src/config/aiwg-config.js +349 -35
  85. package/dist/src/config/cli.js +256 -23
  86. package/dist/src/config/decision-config.js +106 -0
  87. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  88. package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
  89. package/dist/src/config/gitignore.js +50 -0
  90. package/dist/src/config/index-graph-overrides.js +36 -0
  91. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  92. package/dist/src/config/project-artifacts-health.mjs +14 -3
  93. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  94. package/dist/src/config/user-config-dir.d.mts +9 -0
  95. package/dist/src/config/user-config.js +38 -11
  96. package/dist/src/config/user-registry.js +23 -15
  97. package/dist/src/config/user-state.js +7 -0
  98. package/dist/src/decision/adapters/jev.js +75 -27
  99. package/dist/src/decision/ask.js +203 -43
  100. package/dist/src/decision/context-live-qualification.js +2 -2
  101. package/dist/src/decision/driver.js +14 -7
  102. package/dist/src/decision/ensemble/aggregate.js +23 -10
  103. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  104. package/dist/src/decision/entry.js +25 -6
  105. package/dist/src/decision/graph-live-qualification.js +1 -1
  106. package/dist/src/decision/graph-run.js +9 -0
  107. package/dist/src/decision/heldout/collector.js +15 -14
  108. package/dist/src/decision/heldout/contract.js +30 -7
  109. package/dist/src/decision/index.js +1 -0
  110. package/dist/src/decision/interop.js +60 -8
  111. package/dist/src/decision/jev-credentials.js +15 -1
  112. package/dist/src/decision/ledger.js +942 -0
  113. package/dist/src/decision/offload-activation.js +137 -0
  114. package/dist/src/decision/offload.js +550 -0
  115. package/dist/src/decision/qualification/load.js +4 -6
  116. package/dist/src/decision/savings.js +28 -0
  117. package/dist/src/decision/validate.js +6 -3
  118. package/dist/src/effects/dispatch-verifier.js +252 -0
  119. package/dist/src/effects/index.js +1 -0
  120. package/dist/src/effects/types.js +5 -1
  121. package/dist/src/effects/verifiers/git.js +2 -1
  122. package/dist/src/effects/verifiers/tracker.js +26 -14
  123. package/dist/src/extensions/commands/definitions.js +19 -0
  124. package/dist/src/extensions/project-local-remove.js +36 -1
  125. package/dist/src/features/installer.js +2 -2
  126. package/dist/src/flow/graph-metadata.js +5 -0
  127. package/dist/src/gates/providers/loader.js +64 -18
  128. package/dist/src/installation/manager-command.d.mts +18 -0
  129. package/dist/src/installation/manager-command.mjs +15 -3
  130. package/dist/src/installation/manager.d.mts +49 -0
  131. package/dist/src/installation/manager.mjs +95 -5
  132. package/dist/src/issues/cli.js +4 -4
  133. package/dist/src/issues/local.js +24 -5
  134. package/dist/src/jobs/runner.js +1 -1
  135. package/dist/src/lint/cli.js +27 -1
  136. package/dist/src/lint/loader.js +2 -1
  137. package/dist/src/lint/reporters.js +25 -1
  138. package/dist/src/lint/rule-activation.js +43 -0
  139. package/dist/src/lint/runner.js +137 -62
  140. package/dist/src/mcp/adapters/codex-runtime.js +366 -56
  141. package/dist/src/mcp/claude-install.d.mts +4 -0
  142. package/dist/src/mcp/claude-install.mjs +41 -0
  143. package/dist/src/mcp/cli.d.mts +1 -0
  144. package/dist/src/mcp/cli.mjs +620 -85
  145. package/dist/src/mcp/config-file.d.mts +28 -0
  146. package/dist/src/mcp/config-file.mjs +279 -0
  147. package/dist/src/mcp/config-layers.d.mts +36 -0
  148. package/dist/src/mcp/config-layers.mjs +348 -0
  149. package/dist/src/mcp/credentials.d.mts +51 -0
  150. package/dist/src/mcp/credentials.mjs +484 -0
  151. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  152. package/dist/src/mcp/grok-build-config.mjs +17 -27
  153. package/dist/src/mcp/helpers.mjs +109 -4
  154. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  155. package/dist/src/mcp/muse-native-config.mjs +1 -0
  156. package/dist/src/mcp/omp-config.d.mts +4 -0
  157. package/dist/src/mcp/omp-config.mjs +50 -29
  158. package/dist/src/mcp/profiles.js +97 -23
  159. package/dist/src/mcp/profiles.mjs +99 -27
  160. package/dist/src/mcp/registry.js +248 -131
  161. package/dist/src/mcp/registry.mjs +169 -100
  162. package/dist/src/mcp/server-config.d.mts +3 -0
  163. package/dist/src/mcp/server-config.mjs +89 -0
  164. package/dist/src/mcp/server.mjs +127 -82
  165. package/dist/src/mcp/test-simple.mjs +2 -1
  166. package/dist/src/mcp/toml-editor.d.mts +4 -0
  167. package/dist/src/mcp/toml-string.d.mts +1 -0
  168. package/dist/src/mcp/toml-string.mjs +9 -0
  169. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  170. package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
  171. package/dist/src/mcp/tool-filters.d.mts +46 -0
  172. package/dist/src/mcp/tool-filters.mjs +357 -0
  173. package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
  174. package/dist/src/mcp/tools/decision.mjs +22 -3
  175. package/dist/src/mcp/tools/discovery.mjs +62 -55
  176. package/dist/src/mcp/tools/orchestration.mjs +50 -2
  177. package/dist/src/mcp/tools/subsystems.mjs +39 -8
  178. package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
  179. package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
  180. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
  181. package/dist/src/memory/project-registry.js +2 -2
  182. package/dist/src/mission-protocol/codecs.js +647 -1
  183. package/dist/src/mission-protocol/types.js +110 -0
  184. package/dist/src/mission-protocol/worker-status.js +2 -0
  185. package/dist/src/models/config-loader.js +76 -14
  186. package/dist/src/models/grok-build-models.js +93 -41
  187. package/dist/src/models/model-capabilities.v1.json +3183 -24
  188. package/dist/src/models/model-discovery.js +931 -67
  189. package/dist/src/models/provider-policy.js +719 -11
  190. package/dist/src/models/qualification-digest.d.mts +2 -0
  191. package/dist/src/models/qualification-digest.mjs +17 -0
  192. package/dist/src/models/resolver.js +261 -0
  193. package/dist/src/models/worker-direct-readiness.js +550 -0
  194. package/dist/src/models/worker-dispatch-offer.js +169 -0
  195. package/dist/src/models/worker-health-readiness.js +144 -0
  196. package/dist/src/models/worker-installation.d.mts +39 -0
  197. package/dist/src/models/worker-installation.mjs +986 -0
  198. package/dist/src/models/wrapper-route.js +18 -0
  199. package/dist/src/ops/registry.js +9 -14
  200. package/dist/src/plugins/standalone-packager.js +4 -3
  201. package/dist/src/process/bounded-child-process.d.mts +84 -0
  202. package/dist/src/process/bounded-child-process.mjs +430 -0
  203. package/dist/src/process/cli-command.d.mts +16 -0
  204. package/dist/src/process/cli-command.mjs +75 -0
  205. package/dist/src/process/signal-child.d.mts +6 -0
  206. package/dist/src/process/signal-child.mjs +7 -0
  207. package/dist/src/providers/capability-matrix.js +8 -0
  208. package/dist/src/providers/capability-matrix.yaml +270 -0
  209. package/dist/src/providers/deployed-skill-roots.js +20 -0
  210. package/dist/src/providers/grok-build-acp.js +3 -2
  211. package/dist/src/providers/grok-build-headless.js +53 -23
  212. package/dist/src/providers/grok-build-inspect.js +1 -0
  213. package/dist/src/providers/omp-agent.d.mts +8 -0
  214. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  215. package/dist/src/providers/omp-paths.d.mts +13 -0
  216. package/dist/src/providers/provider-definitions.js +9 -1
  217. package/dist/src/providers/provider-definitions.mjs +9 -2
  218. package/dist/src/providers/skill-ownership.d.mts +9 -0
  219. package/dist/src/providers/skill-ownership.mjs +88 -0
  220. package/dist/src/rules/activation.d.mts +21 -0
  221. package/dist/src/rules/activation.mjs +62 -0
  222. package/dist/src/rules/inline-budget.d.mts +26 -0
  223. package/dist/src/rules/inline-budget.mjs +66 -0
  224. package/dist/src/rules/inventory.js +129 -0
  225. package/dist/src/rules/pin-state.js +73 -0
  226. package/dist/src/rules/pins.d.mts +14 -0
  227. package/dist/src/rules/pins.mjs +112 -0
  228. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  229. package/dist/src/serve/executor-registry.js +16 -24
  230. package/dist/src/serve/fleet-mission-conductor.js +9 -0
  231. package/dist/src/serve/mission-conductor.js +47 -12
  232. package/dist/src/serve/shared-host-scheduler.js +2 -0
  233. package/dist/src/serve/worker-invocation.js +558 -0
  234. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  235. package/dist/src/serve/worker-stdin-frame.mjs +107 -0
  236. package/dist/src/sessions/adapter-registry.js +49 -0
  237. package/dist/src/sessions/adapters/claude.js +39 -54
  238. package/dist/src/sessions/adapters/codex.js +82 -67
  239. package/dist/src/sessions/adapters/copilot.js +4 -4
  240. package/dist/src/sessions/adapters/cursor.js +40 -57
  241. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  242. package/dist/src/sessions/adapters/factory.js +31 -27
  243. package/dist/src/sessions/adapters/generic.js +43 -9
  244. package/dist/src/sessions/adapters/grok-build.js +11 -11
  245. package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
  246. package/dist/src/sessions/adapters/grokbot.js +434 -24
  247. package/dist/src/sessions/adapters/hermes.js +27 -29
  248. package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
  249. package/dist/src/sessions/adapters/muse.js +200 -104
  250. package/dist/src/sessions/adapters/omp.js +28 -14
  251. package/dist/src/sessions/adapters/openclaw.js +89 -79
  252. package/dist/src/sessions/adapters/opencode.js +27 -24
  253. package/dist/src/sessions/adapters/openhuman.js +18 -60
  254. package/dist/src/sessions/adapters/pi.js +30 -14
  255. package/dist/src/sessions/adapters/warp.js +3 -3
  256. package/dist/src/sessions/adapters/windsurf.js +19 -48
  257. package/dist/src/sessions/batch-contracts.js +25 -5
  258. package/dist/src/sessions/batch-import.js +72 -51
  259. package/dist/src/sessions/candidates.js +47 -11
  260. package/dist/src/sessions/contracts.js +75 -2
  261. package/dist/src/sessions/coverage-preflight.js +134 -0
  262. package/dist/src/sessions/discovery.js +3 -0
  263. package/dist/src/sessions/export-plan.js +4 -3
  264. package/dist/src/sessions/grok-build-cli.js +67 -5
  265. package/dist/src/sessions/import-exclusions.js +72 -0
  266. package/dist/src/sessions/import-lease.js +34 -18
  267. package/dist/src/sessions/import-limits.js +37 -0
  268. package/dist/src/sessions/importer.js +84 -46
  269. package/dist/src/sessions/index.js +2 -0
  270. package/dist/src/sessions/lifecycle.js +45 -0
  271. package/dist/src/sessions/policy.js +133 -25
  272. package/dist/src/sessions/readers.js +106 -53
  273. package/dist/src/sessions/refresh.js +106 -0
  274. package/dist/src/sessions/repository.js +199 -63
  275. package/dist/src/sessions/workspace-discovery.js +315 -88
  276. package/dist/src/sessions/workspace-matching.js +107 -0
  277. package/dist/src/skills/adapters/clawhub.js +10 -8
  278. package/dist/src/skills/adapters/openclaw.js +10 -7
  279. package/dist/src/skills/importer.js +1 -0
  280. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  281. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  282. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  283. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  284. package/dist/src/smiths/context-pipeline/parallelism-section.js +195 -14
  285. package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
  286. package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
  287. package/dist/src/smiths/mcpsmith/example.js +1 -1
  288. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  289. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  290. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +85 -31
  291. package/dist/src/storage/backends/fs.js +18 -76
  292. package/dist/src/storage/backends/logseq.js +68 -7
  293. package/dist/src/storage/backends/obsidian.js +1 -0
  294. package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
  295. package/dist/src/testing/generators/test-code-generator.js +57 -30
  296. package/dist/src/tracker/forge-http.js +2 -1
  297. package/dist/src/uhp/client.js +11 -1
  298. package/dist/src/uhp/mission.js +5 -0
  299. package/dist/src/update/checker.d.mts +2 -0
  300. package/dist/src/update/checker.mjs +5 -5
  301. package/dist/src/update/notifier.mjs +54 -2
  302. package/dist/src/update/service.d.mts +22 -0
  303. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  304. package/dist/src/utils/contained-filesystem.mjs +683 -0
  305. package/dist/src/utils/temporary-directory.js +36 -0
  306. package/dist/src/writing/example-generator.js +1 -1
  307. package/dist/src/writing/fidelity.js +18 -1
  308. package/package.json +32 -2
  309. package/schemas/mission-v1.schema.json +2429 -0
  310. package/schemas/models/model-catalog.v1.schema.json +301 -0
  311. package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
  312. package/schemas/models/model-policy.v1.schema.json +14 -0
  313. package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
  314. package/tools/_resolve-impl.mjs +39 -2
  315. package/tools/agents/deploy-agents.mjs +64 -23
  316. package/tools/agents/providers/base.mjs +153 -166
  317. package/tools/agents/providers/claude.mjs +8 -24
  318. package/tools/agents/providers/codex.mjs +18 -11
  319. package/tools/agents/providers/cursor.mjs +19 -18
  320. package/tools/agents/providers/grok-build.mjs +3 -1
  321. package/tools/agents/providers/grokbot.mjs +11 -12
  322. package/tools/agents/providers/hermes.mjs +62 -49
  323. package/tools/agents/providers/omp.mjs +5 -2
  324. package/tools/agents/providers/openclaw-translator.mjs +19 -5
  325. package/tools/agents/providers/openclaw.mjs +9 -4
  326. package/tools/agents/providers/pi.mjs +5 -0
  327. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  328. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  329. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  330. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  331. package/tools/agents/providers/warp.mjs +20 -26
  332. package/tools/cli/decision-offload-report.mjs +40 -0
  333. package/tools/cli/workspace-status.mjs +862 -0
  334. package/tools/plugin/package-plugins.mjs +186 -8
  335. package/tools/plugin/plugin-links.mjs +114 -0
  336. package/tools/providers/antigravity-transport.mjs +3 -1
  337. package/tools/providers/deepseek-harness-transport.mjs +360 -0
  338. package/tools/providers/omp-transport.mjs +194 -0
  339. package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
  340. package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
  341. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  342. package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
  343. package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
  344. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  345. package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
  346. package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
  347. package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
  348. package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
  349. package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
  350. package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
  351. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  352. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  353. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  354. package/tools/ralph-external/lib/worker-health.mjs +439 -0
  355. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  356. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  357. package/tools/ralph-external/process-monitor.mjs +1017 -0
  358. package/tools/ralph-external/session-launcher.mjs +5057 -0
  359. package/tools/scripts/resource-preflight.mjs +541 -0
  360. package/tools/skills/deploy-skills-codex.mjs +2 -1
  361. package/vscode-extension/schemas/aiwg.config.v1.json +2916 -0
@@ -4,8 +4,13 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { parseDocument, stringify } from 'yaml';
6
6
  import { findPackageRoot } from '../find-package-root.js';
7
+ import { isInteractive } from '../env.js';
8
+ import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
7
9
  import { sha256 } from '../../security/artifact-trust.js';
8
10
  import { artifactVerifyHandler } from './artifact-verify.js';
11
+ import { requireFlagValue, MissingFlagValueError, collectFlagValues, hasPositionalOrSwitch, argsTerminatorIndex, valueTokenIndexes } from '../flag-parsing.js';
12
+ import { exitCodeForSpawnResult } from '../child-process-result.js';
13
+ import { askString, createPromptInterface } from '../prompt-utils.js';
9
14
  const SETUP_SCHEMA_REL = 'agentic/code/addons/agentic-installer/schemas/v1/setup-manifest.schema.json';
10
15
  const GENERATE_HELP = `aiwg setup-generate - generate a starter setup.aiwg.io/v1 SetupManifest
11
16
 
@@ -67,50 +72,40 @@ Safety:
67
72
  Mutating execution refuses to run without explicit confirmation.
68
73
  Provider-orchestrated manifests are never handed to an agent unless their
69
74
  exact bytes have status verified under the explicit trust policy.
75
+
76
+ Headless input steps (#3062):
77
+ A 'type: ask' step resolves its declared 'params' the same way a 'script'
78
+ step's env vars do (--param/--params-file/env/default); if every declared
79
+ param is already resolved it runs headless; otherwise a terminal prompts
80
+ for missing values. Without a terminal it refuses and names the missing params. A step marked
81
+ 'interactive: true' runs an arbitrary script whose questions cannot be
82
+ enumerated; without a terminal it is refused outright. Every selected
83
+ step (including nested 'platform-route' steps) is preflighted for both
84
+ conditions before any step executes — never partway through.
70
85
  `;
86
+ /**
87
+ * A value-taking flag present without a usable value is a usage error, not
88
+ * "not given" (#3062 review item 11) — delegates to the shared strict
89
+ * parser. Callers (the three handlers below) catch `MissingFlagValueError`
90
+ * and report it before any preflight check, spawn, or write.
91
+ * `precedence: 'first'` matches this command's own pre-#3062 behavior (a
92
+ * hand-rolled loop that returned on the first match), unchanged by the
93
+ * shared parser's second-round fix (#3062 second review, finding 4).
94
+ */
71
95
  function flagValue(args, name) {
72
- for (let i = 0; i < args.length; i += 1) {
73
- if (args[i] === name) {
74
- const value = args[i + 1];
75
- return value && !value.startsWith('--') ? value : undefined;
76
- }
77
- }
78
- return undefined;
96
+ return requireFlagValue(args, {}, name);
79
97
  }
98
+ const VALUE_FLAGS = [
99
+ '--manifest', '--schema', '--platform', '--distro', '--params-file', '--param', '--set',
100
+ '--step', '--skip', '--type', '--output', '--name', '--attestation', '--policy', '--state',
101
+ '--asset-type', '--namespace', '--channel',
102
+ ];
80
103
  function hasFlag(args, ...names) {
81
- return args.some((arg) => names.includes(arg));
104
+ return names.some(name => hasPositionalOrSwitch(args, name, VALUE_FLAGS));
82
105
  }
83
106
  function positionalManifest(args) {
84
- const valueFlags = new Set([
85
- '--manifest',
86
- '--schema',
87
- '--platform',
88
- '--distro',
89
- '--params-file',
90
- '--param',
91
- '--set',
92
- '--step',
93
- '--skip',
94
- '--type',
95
- '--output',
96
- '--name',
97
- '--attestation',
98
- '--policy',
99
- '--state',
100
- '--asset-type',
101
- '--namespace',
102
- '--channel',
103
- ]);
104
- for (let i = 0; i < args.length; i += 1) {
105
- const arg = args[i];
106
- if (valueFlags.has(arg)) {
107
- i += 1;
108
- continue;
109
- }
110
- if (!arg.startsWith('-'))
111
- return arg;
112
- }
113
- return undefined;
107
+ const consumed = valueTokenIndexes(args, VALUE_FLAGS);
108
+ return args.slice(0, argsTerminatorIndex(args)).find((arg, index) => !consumed.has(index) && !arg.startsWith('-'));
114
109
  }
115
110
  function resolveSchemaPath(frameworkRoot, cwd, override) {
116
111
  if (override)
@@ -238,6 +233,83 @@ function allSteps(manifest) {
238
233
  function existingRelative(manifestDir, rel) {
239
234
  return !!rel && existsSync(path.resolve(manifestDir, rel));
240
235
  }
236
+ /**
237
+ * Detect `on_fail` reference cycles among recovery procedures themselves —
238
+ * go/no-go review blocker 3. A step's `on_fail` can name any recovery
239
+ * procedure, including (structurally, per the shared `SetupStep` type) a
240
+ * step *inside* a recovery procedure's own `steps`. A recovery procedure
241
+ * that (directly, or through another recovery procedure) names itself used
242
+ * to make the old, eager preflight recurse into it forever —
243
+ * `RangeError: Maximum call stack size exceeded`, reproduced in memory by
244
+ * the review.
245
+ *
246
+ * This is a one-time, bounded graph walk over `manifest.spec.recovery[]`
247
+ * only (at most `recovery.length` nodes) — a visited/"on the current path"
248
+ * marker per node means each node is fully explored exactly once, so this
249
+ * terminates and never re-enters a node already being explored; it reports
250
+ * one finding per distinct cycle instead of throwing. An `on_fail` that
251
+ * does not resolve to a known recovery id is reported separately by the
252
+ * existing unresolved-recovery check (`installerConsistencyChecks` above)
253
+ * and is not itself a cycle.
254
+ *
255
+ * This graph check is independent of, and does not change, how `on_fail`
256
+ * references are actually used (`collectPreflightBlockers()`/`runStep()`
257
+ * below): execution never follows an `on_fail` reference more than once
258
+ * (a failing recovery step reports failure directly, it does not itself
259
+ * trigger further recovery), so neither the main-sequence preflight nor a
260
+ * selected recovery's own preflight recurses through `on_fail` at all —
261
+ * see `collectPreflightBlockers()`'s own doc comment. This cycle check
262
+ * exists for the case the type system permits but execution never
263
+ * exercises: a recovery procedure's own steps declaring `on_fail`.
264
+ */
265
+ function detectRecoveryReferenceCycles(manifest) {
266
+ const recoveries = manifest.spec.recovery ?? [];
267
+ if (recoveries.length === 0)
268
+ return [];
269
+ const onFailTargets = new Map();
270
+ for (const recovery of recoveries) {
271
+ const targets = [];
272
+ const collect = (steps) => {
273
+ steps?.forEach((step) => {
274
+ if (step.on_fail && step.on_fail !== 'recover')
275
+ targets.push(step.on_fail);
276
+ step.routes?.forEach((route) => collect(route.steps));
277
+ });
278
+ };
279
+ collect(recovery.steps);
280
+ onFailTargets.set(recovery.id, targets);
281
+ }
282
+ const findings = [];
283
+ const reportedCycles = new Set();
284
+ const state = new Map();
285
+ const visit = (id, path) => {
286
+ if (state.get(id) === 'done')
287
+ return;
288
+ if (state.get(id) === 'on-path') {
289
+ const cycle = [...path.slice(path.indexOf(id)), id];
290
+ const key = [...new Set(cycle)].sort().join('\u0000');
291
+ if (!reportedCycles.has(key)) {
292
+ reportedCycles.add(key);
293
+ findings.push({
294
+ severity: 'error',
295
+ path: '/spec/recovery',
296
+ rule: 'recoveryCycle',
297
+ message: `recovery reference cycle: ${cycle.join(' -> ')}`,
298
+ });
299
+ }
300
+ return;
301
+ }
302
+ state.set(id, 'on-path');
303
+ for (const next of onFailTargets.get(id) ?? []) {
304
+ if (onFailTargets.has(next))
305
+ visit(next, [...path, id]);
306
+ }
307
+ state.set(id, 'done');
308
+ };
309
+ for (const recovery of recoveries)
310
+ visit(recovery.id, []);
311
+ return findings;
312
+ }
241
313
  function installerConsistencyChecks(manifest, manifestDir) {
242
314
  if (!manifest)
243
315
  return [];
@@ -262,6 +334,8 @@ function installerConsistencyChecks(manifest, manifestDir) {
262
334
  findings.push({ severity: 'error', path: `/spec/steps/${index}/on_fail`, rule: 'on_fail', message: `unresolved recovery '${step.on_fail}'` });
263
335
  }
264
336
  }
337
+ // Go/no-go review blocker 3.
338
+ findings.push(...detectRecoveryReferenceCycles(manifest));
265
339
  for (const { step, pointer } of allSteps(manifest)) {
266
340
  if (step.type === 'script' && !existingRelative(manifestDir, step.script)) {
267
341
  findings.push({ severity: 'error', path: `${pointer}/script`, rule: 'scriptExists', message: `script not found: ${step.script ?? '(missing)'}` });
@@ -374,15 +448,12 @@ function renderValidationText(result) {
374
448
  }
375
449
  function parseParamValues(args) {
376
450
  const values = {};
377
- for (let i = 0; i < args.length; i += 1) {
378
- if (args[i] !== '--param' && args[i] !== '--set')
379
- continue;
380
- const raw = args[i + 1] ?? '';
451
+ for (const raw of collectFlagValues(args, {}, '--param', '--set')) {
381
452
  const eq = raw.indexOf('=');
382
- if (eq <= 0)
383
- throw new Error(`${args[i]} requires KEY=VALUE`);
453
+ if (eq <= 0 || !raw.slice(0, eq).trim())
454
+ throw new MissingFlagValueError('--param KEY=VALUE');
455
+ // An empty parameter value is supported (e.g. explicitly clearing a default).
384
456
  values[raw.slice(0, eq)] = raw.slice(eq + 1);
385
- i += 1;
386
457
  }
387
458
  return values;
388
459
  }
@@ -602,13 +673,41 @@ function planLines(manifest, manifestPath, target, params, steps) {
602
673
  });
603
674
  return lines.join('\n') + '\n';
604
675
  }
605
- function commandResult(status) {
606
- const code = status === null ? 1 : status;
607
- return { ok: code === 0, status: code };
676
+ /**
677
+ * #3062 review item 7: a `null` status already falls through to `code = 1`
678
+ * (not success); this also surfaces the bounded helper's `error`/`signal`
679
+ * diagnostic (e.g. the enriched timeout message naming the bound and
680
+ * override variable) instead of silently dropping it.
681
+ */
682
+ function commandResult(result) {
683
+ const outcome = exitCodeForSpawnResult(result);
684
+ if (outcome.message)
685
+ console.error(`[setup] ${outcome.message}`);
686
+ const code = result.status === null ? 1 : result.status;
687
+ return { ok: code === 0 && !result.error && !result.signal, status: code };
608
688
  }
689
+ /**
690
+ * Run one manifest-author-defined shell command (#3062).
691
+ *
692
+ * Without a terminal, a command that tries to prompt must fail fast instead
693
+ * of hanging forever: bounded (long tier, stdin ignored). With a terminal,
694
+ * unchanged — inherited stdio, unbounded, so a human can actually answer a
695
+ * prompt the command issues.
696
+ */
609
697
  function runShell(command, cwd, env) {
698
+ if (!isInteractive()) {
699
+ const result = boundedSpawnSync(command, [], 'long', {
700
+ cwd, env, shell: true, stdio: ['ignore', 'inherit', 'inherit'],
701
+ });
702
+ return commandResult(result);
703
+ }
704
+ // sync-spawn: interactive manifest-shell-command — runs an arbitrary
705
+ // manifest-author-defined shell command with inherited stdio; it may
706
+ // itself prompt (see tools/testing/sync-spawn-allowlist.json). Reached
707
+ // only when isInteractive() is true (#3062) — the no-terminal path above
708
+ // uses the bounded helper instead. Not a candidate for a bounded timeout.
610
709
  const result = spawnSync(command, { cwd, env, shell: true, stdio: 'inherit' });
611
- return commandResult(result.status);
710
+ return commandResult(result);
612
711
  }
613
712
  function isDestructiveScript(filePath) {
614
713
  const name = path.basename(filePath);
@@ -619,9 +718,180 @@ function isDestructiveScript(filePath) {
619
718
  const text = readFileSync(filePath, 'utf8');
620
719
  return /\brm\s+-rf\b|\b(cp|mv)\s+-f\b|>\s*\S+/.test(text);
621
720
  }
622
- function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recovery = false) {
721
+ /**
722
+ * Param names a `type: ask` step resolves (#3062 review item 10). Reuses
723
+ * the same `params` field `type: script` steps already use to pass env
724
+ * vars — there is no separate "ask step input" field in the schema, and
725
+ * this is the same mechanism, just read by the `ask` branch below instead
726
+ * of passed to a script.
727
+ */
728
+ function askStepParams(step) {
729
+ return step.params ?? [];
730
+ }
731
+ /** Which of an `ask` step's declared params are not yet resolved (missing from `--param`/params-file/env/default). */
732
+ function missingAskParams(step, params) {
733
+ return askStepParams(step).filter((name) => params[name] === undefined);
734
+ }
735
+ /**
736
+ * Preflight every step in `steps` (recursively into `platform-route` nested
737
+ * steps only) for conditions that would block it from running at all, and
738
+ * return one message per blocker — never a partial list halted at the
739
+ * first step (#3062 review item 13, and the second review's finding 7:
740
+ * refusal must happen before ANY step executes).
741
+ *
742
+ * Used for two distinct call sites, both passing only the steps that are
743
+ * actually about to run:
744
+ *
745
+ * 1. The main selected sequence, up front, before any of it runs
746
+ * (`runSetupManifest()`'s own preflight call, below).
747
+ * 2. A *specific* recovery procedure's own steps, called again at the
748
+ * moment a failed step selects it — completely, before that
749
+ * recovery's first step runs (go/no-go review blocker 3).
750
+ *
751
+ * This function never itself follows a step's `on_fail` into another
752
+ * recovery procedure, in either case — go/no-go review blocker 3's
753
+ * "preflight is too eager" finding: the previous version always recursed
754
+ * into every selected step's `on_fail` recovery during the main, up-front
755
+ * preflight, which (a) refused a manifest whose main sequence would
756
+ * succeed outright, solely because its *unused* recovery contained an
757
+ * unsupported step, and (b) had no cycle protection, so a recovery whose
758
+ * own steps' `on_fail` pointed back into a recovery already being
759
+ * preflighted recursed without bound (`RangeError: Maximum call stack
760
+ * size exceeded`, reproduced by the review). Not recursing into `on_fail`
761
+ * here also matches what execution itself does: a failing step triggers
762
+ * its *one* named recovery exactly once; a failing step *within* that
763
+ * recovery reports failure directly rather than following a further
764
+ * `on_fail` — so preflight never needs to follow one either. (A
765
+ * recovery-to-recovery `on_fail` reference cycle in the manifest itself is
766
+ * still caught — as a graph check over `manifest.spec.recovery[]`, not a
767
+ * recursive preflight — by `detectRecoveryReferenceCycles()` during
768
+ * manifest validation.)
769
+ *
770
+ * - `interactive: true` without a terminal AND without every declared
771
+ * `params` entry resolved: with every declared param supplied, the
772
+ * step runs headless instead (review item 10; second review finding
773
+ * 5b — the step itself is trusted not to need to prompt once its
774
+ * declared inputs are present). With no declared params at all
775
+ * ("undeclared interaction" — the step's questions cannot be
776
+ * enumerated or supplied via `--param`) this always requires a
777
+ * terminal.
778
+ * - `type: ask` with no declared `params`: always refused — unlike
779
+ * `interactive: true`, this CLI itself owns the prompting for `ask`
780
+ * steps (there is no separate script to hand a terminal to), and
781
+ * with nothing declared there is no destination to store an answer
782
+ * in. This is one of the two agreed, permanent exceptions (the other
783
+ * is the contributor sync-upstream strategy choice).
784
+ * - `type: ask` with declared params not all resolved, and no
785
+ * terminal: refused, naming the missing names so `--param` can
786
+ * supply them. With a terminal, this is *not* a blocker — the
787
+ * missing ones are prompted for when the step actually runs (second
788
+ * review finding 5b).
789
+ * - `type: agentic`/`type: chain`: `runStep()` refuses both
790
+ * unconditionally; preflighted the same way so an earlier, unrelated
791
+ * step is never executed ahead of this unconditional refusal (second
792
+ * review finding 7).
793
+ */
794
+ function collectPreflightBlockers(steps, manifest, targetOs, params, terminalPresent) {
795
+ const blockers = [];
796
+ for (const step of steps) {
797
+ if (!stepPlatformMatches(targetOs, step))
798
+ continue;
799
+ if (step.interactive) {
800
+ const names = askStepParams(step);
801
+ const missing = missingAskParams(step, params);
802
+ const fullySupplied = names.length > 0 && missing.length === 0;
803
+ if (!fullySupplied && !terminalPresent) {
804
+ blockers.push(names.length === 0
805
+ ? `step '${step.id}' is marked interactive (its prompts cannot be enumerated or supplied via --param) and requires a terminal`
806
+ : `step '${step.id}' is marked interactive and is missing required param(s): ${missing.join(', ')} (supply with --param <name>=<value>, or run with a terminal attached)`);
807
+ }
808
+ }
809
+ if (step.type === 'ask') {
810
+ const names = askStepParams(step);
811
+ if (names.length === 0) {
812
+ blockers.push(`ask step '${step.id}' declares no params to resolve via --param; interactive capture for ask ` +
813
+ "steps is not implemented — declare 'params' on the step and supply them with --param <name>=<value>");
814
+ }
815
+ else {
816
+ const missing = missingAskParams(step, params);
817
+ if (missing.length > 0 && !terminalPresent) {
818
+ blockers.push(`ask step '${step.id}' is missing required param(s): ${missing.join(', ')} (supply with --param ` +
819
+ '<name>=<value>, or run with a terminal attached to be prompted for them)');
820
+ }
821
+ }
822
+ }
823
+ if (step.type === 'agentic' || step.type === 'chain') {
824
+ blockers.push(`${step.type} step '${step.id}' is not supported by deterministic CLI execution and is always refused`);
825
+ }
826
+ if (step.type === 'platform-route') {
827
+ const route = step.routes?.find((item) => item.platform === targetOs);
828
+ if (route)
829
+ blockers.push(...collectPreflightBlockers(route.steps, manifest, targetOs, params, terminalPresent));
830
+ }
831
+ }
832
+ return blockers;
833
+ }
834
+ /**
835
+ * Preflight a *specific* recovery procedure's own steps — go/no-go review
836
+ * blocker 3. Called at the moment a failed step selects it, completely,
837
+ * before any of its steps run. Returns `undefined` when the recovery id
838
+ * does not resolve to a known procedure (the schema/consistency check
839
+ * already reports that as a manifest error; this function's caller treats
840
+ * an unresolved id as "no recovery available", same as before).
841
+ */
842
+ function findRecoveryProcedure(manifest, recoveryId) {
843
+ return manifest.spec.recovery?.find((item) => item.id === recoveryId);
844
+ }
845
+ /**
846
+ * Prompt interactively for each of `missingNames`, one per declared `ask`-
847
+ * step param still unresolved (#3062 second review, finding 5b: declared
848
+ * ask inputs missing AT a terminal are prompted for, not refused — this
849
+ * is reachable only when `isInteractive()` is true, so it is always safe
850
+ * to open a prompt interface here).
851
+ */
852
+ async function promptForAskStep(step, missingNames) {
853
+ const rl = createPromptInterface();
854
+ const answers = {};
855
+ try {
856
+ if (step.message)
857
+ console.log(`[setup] ${step.message}`);
858
+ for (const name of missingNames) {
859
+ const answer = await askString(rl, ` ${name}: `, '', undefined);
860
+ if (answer)
861
+ answers[name] = answer;
862
+ }
863
+ }
864
+ finally {
865
+ rl.close();
866
+ }
867
+ return answers;
868
+ }
869
+ async function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recovery = false) {
623
870
  if (!stepPlatformMatches(targetOs, step))
624
871
  return { ok: true, status: 0 };
872
+ // A step marked `interactive: true` (#3062) declares that it prompts
873
+ // directly. For a top-level selected step this condition is already
874
+ // preflighted before any step runs (review item 13; second review
875
+ // finding 7); this remains the only gate for a recovery step, which
876
+ // isn't known until a step actually fails. With every declared `params`
877
+ // entry resolved, the step is trusted not to need to prompt and runs
878
+ // headless below like any other step (second review finding 5b — the
879
+ // bug this fixes: it used to be refused here even when fully supplied).
880
+ // With no declared params at all ("undeclared interaction") or some
881
+ // still missing, a terminal is required.
882
+ if (step.interactive) {
883
+ const names = askStepParams(step);
884
+ const missing = missingAskParams(step, params);
885
+ const fullySupplied = names.length > 0 && missing.length === 0;
886
+ if (!fullySupplied && !isInteractive()) {
887
+ console.error(names.length === 0
888
+ ? `setup-run: step '${step.id}' is marked interactive (its prompts cannot be enumerated or supplied ` +
889
+ 'via --param) and requires a terminal; re-run from an interactive shell.'
890
+ : `setup-run: step '${step.id}' is marked interactive and is missing required param(s): ` +
891
+ `${missing.join(', ')}; supply with --param <name>=<value>, or re-run from an interactive shell.`);
892
+ return { ok: false, status: 2, failedStep: step.id };
893
+ }
894
+ }
625
895
  const env = { ...process.env, ...params };
626
896
  console.log(`[setup] Step ${step.id} (${step.type})`);
627
897
  if (step.type === 'script') {
@@ -634,8 +904,19 @@ function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recov
634
904
  console.error(`setup-run: refusing destructive step '${step.id}' without --yes/--confirm`);
635
905
  return { ok: false, status: 2, failedStep: step.id };
636
906
  }
637
- const result = spawnSync(scriptPath, [], { cwd: manifestDir, env, stdio: 'inherit' });
638
- const ran = commandResult(result.status);
907
+ // Without a terminal, a script that tries to prompt must fail fast
908
+ // instead of hanging forever: bounded (long tier, stdin ignored). With
909
+ // a terminal, unchanged (#3062).
910
+ const result = isInteractive()
911
+ // sync-spawn: interactive manifest-script-step — runs an arbitrary
912
+ // manifest-author-defined script file with inherited stdio; it may
913
+ // itself prompt (see tools/testing/sync-spawn-allowlist.json). Reached
914
+ // only when isInteractive() is true (#3062) — the no-terminal path
915
+ // uses the bounded helper instead. Not a candidate for a bounded
916
+ // timeout.
917
+ ? spawnSync(scriptPath, [], { cwd: manifestDir, env, stdio: 'inherit' })
918
+ : boundedSpawnSync(scriptPath, [], 'long', { cwd: manifestDir, env, stdio: ['ignore', 'inherit', 'inherit'] });
919
+ const ran = commandResult(result);
639
920
  if (!ran.ok)
640
921
  return { ...ran, failedStep: step.id };
641
922
  const verifies = Array.isArray(step.verify) ? step.verify : step.verify ? [step.verify] : [];
@@ -677,13 +958,51 @@ function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recov
677
958
  return { ok: false, status: 2, failedStep: step.id };
678
959
  }
679
960
  if (step.type === 'ask') {
680
- console.error(`setup-run: ask step '${step.id}' requires interactive input; use params before execution`);
681
- return { ok: false, status: 2, failedStep: step.id };
961
+ // #3062 review item 10: a top-level selected step with all its declared
962
+ // params already resolved is already caught as *not* blocking by
963
+ // `collectPreflightBlockers` and runs headless here; this remains the
964
+ // gate for a recovery-path `ask` step, which isn't preflighted.
965
+ const names = askStepParams(step);
966
+ if (names.length === 0) {
967
+ // Agreed, permanent exception (second review finding 5b): this CLI
968
+ // owns the prompting for `ask` steps directly (there is no separate
969
+ // script to hand a terminal to), and with nothing declared there is
970
+ // no destination to store an answer in — always refused, with or
971
+ // without a terminal.
972
+ console.error(`setup-run: ask step '${step.id}' declares no params to resolve via --param; interactive capture ` +
973
+ "for ask steps is not implemented — declare 'params' on the step and supply them with --param " +
974
+ '<name>=<value>.');
975
+ return { ok: false, status: 2, failedStep: step.id };
976
+ }
977
+ const missing = missingAskParams(step, params);
978
+ if (missing.length === 0) {
979
+ console.log(` ask: ${step.message ?? step.id} -> answered via --param (${names.join(', ')})`);
980
+ return { ok: true, status: 0 };
981
+ }
982
+ if (!isInteractive()) {
983
+ console.error(`setup-run: ask step '${step.id}' is missing required param(s): ${missing.join(', ')}; supply with ` +
984
+ '--param <name>=<value>, or re-run from an interactive shell to be prompted for them.');
985
+ return { ok: false, status: 2, failedStep: step.id };
986
+ }
987
+ // #3062 second review, finding 5b: declared ask inputs missing AT a
988
+ // terminal are prompted for, not refused.
989
+ const answers = await promptForAskStep(step, missing);
990
+ const stillMissing = missing.filter((name) => !answers[name]);
991
+ if (stillMissing.length > 0) {
992
+ console.error(`setup-run: ask step '${step.id}' did not receive a value for: ${stillMissing.join(', ')}.`);
993
+ return { ok: false, status: 2, failedStep: step.id };
994
+ }
995
+ // Merged into the same object reference the caller's loop (and every
996
+ // later step) reads from, so a later step sees the interactively-
997
+ // captured answer exactly like a `--param`-supplied one.
998
+ Object.assign(params, answers);
999
+ console.log(` ask: ${step.message ?? step.id} -> answered interactively (${missing.join(', ')})`);
1000
+ return { ok: true, status: 0 };
682
1001
  }
683
1002
  if (step.type === 'platform-route') {
684
1003
  const route = step.routes?.find((item) => item.platform === targetOs);
685
1004
  for (const nested of route?.steps ?? []) {
686
- const result = runStep(nested, manifest, manifestDir, params, targetOs, confirmed);
1005
+ const result = await runStep(nested, manifest, manifestDir, params, targetOs, confirmed);
687
1006
  if (!result.ok)
688
1007
  return result;
689
1008
  }
@@ -691,7 +1010,7 @@ function runStep(step, manifest, manifestDir, params, targetOs, confirmed, recov
691
1010
  }
692
1011
  return { ok: true, status: 0 };
693
1012
  }
694
- export function runSetupManifest(options) {
1013
+ export async function runSetupManifest(options) {
695
1014
  const validation = validateSetupManifest({
696
1015
  cwd: options.cwd,
697
1016
  frameworkRoot: options.frameworkRoot,
@@ -722,10 +1041,21 @@ export function runSetupManifest(options) {
722
1041
  return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };
723
1042
  }
724
1043
  const params = resolveParams(manifest, options);
725
- if (params.missing.length) {
1044
+ const steps = selectedSteps(manifest, options);
1045
+ const askSteps = [];
1046
+ const collectAsks = (selected) => {
1047
+ for (const step of selected) {
1048
+ if (step.type === 'ask')
1049
+ askSteps.push(step);
1050
+ if (step.type === 'platform-route')
1051
+ collectAsks(step.routes?.find(route => route.platform === target.os)?.steps ?? []);
1052
+ }
1053
+ };
1054
+ collectAsks(steps);
1055
+ const promptable = new Set(isInteractive() ? askSteps.flatMap(askStepParams) : []);
1056
+ if (params.missing.some(name => !promptable.has(name))) {
726
1057
  return { exitCode: 1, message: `setup-run: required params missing before execution: ${params.missing.join(', ')}` };
727
1058
  }
728
- const steps = selectedSteps(manifest, options);
729
1059
  if (options.step && steps.length === 0)
730
1060
  return { exitCode: 1, message: `setup-run: step '${options.step}' not found` };
731
1061
  if (options.dryRun) {
@@ -735,15 +1065,55 @@ export function runSetupManifest(options) {
735
1065
  if (!options.yes) {
736
1066
  return { exitCode: 2, message: 'setup-run: mutating execution requires --yes or --confirm after reviewing the plan with --dry-run' };
737
1067
  }
1068
+ // #3062 review item 13: preflight every selected step (recursively into
1069
+ // `platform-route` nesting) before executing any of them — the previous
1070
+ // behavior checked `interactive`/`ask` conditions inside the per-step
1071
+ // loop below, so an earlier normal step could already perform work
1072
+ // before a later step's refusal fired.
1073
+ const blockers = collectPreflightBlockers(steps, manifest, target.os, params.values, isInteractive());
1074
+ if (blockers.length > 0) {
1075
+ for (const blocker of blockers)
1076
+ console.error(`setup-run: ${blocker}.`);
1077
+ return {
1078
+ exitCode: 2,
1079
+ message: `setup-run: refusing before executing any step:\n${blockers.map((b) => ` - ${b}`).join('\n')}`,
1080
+ };
1081
+ }
1082
+ // Resolve required ask inputs before any scripts can consume them.
1083
+ for (const step of askSteps) {
1084
+ const missing = missingAskParams(step, params.values).filter(name => params.missing.includes(name));
1085
+ if (!missing.length)
1086
+ continue;
1087
+ Object.assign(params.values, await promptForAskStep(step, missing));
1088
+ }
1089
+ const checked = resolveParams(manifest, { ...options, paramValues: { ...options.paramValues, ...params.values } });
1090
+ if (checked.missing.length)
1091
+ return { exitCode: 1, message: `setup-run: required params missing before execution: ${checked.missing.join(', ')}` };
1092
+ Object.assign(params.values, checked.values);
738
1093
  for (const step of steps) {
739
- const result = runStep(step, manifest, validation.manifestDir, params.values, target.os, options.yes);
1094
+ const result = await runStep(step, manifest, validation.manifestDir, params.values, target.os, options.yes);
740
1095
  if (!result.ok) {
741
1096
  if (result.failedStep && step.on_fail) {
742
- const recovery = manifest.spec.recovery?.find((item) => item.id === step.on_fail);
1097
+ const recovery = findRecoveryProcedure(manifest, step.on_fail);
743
1098
  if (recovery) {
1099
+ // Go/no-go review blocker 3: preflight the *selected* recovery's
1100
+ // own steps completely, now that a failure has actually chosen
1101
+ // it — not when the manifest was first loaded (that preflighted
1102
+ // the main sequence only, above) — and before any of its steps
1103
+ // run. Refuses with zero recovery steps executed, same
1104
+ // all-blockers-at-once shape as the main-sequence preflight.
1105
+ const recoveryBlockers = collectPreflightBlockers(recovery.steps, manifest, target.os, params.values, isInteractive());
1106
+ if (recoveryBlockers.length > 0) {
1107
+ for (const blocker of recoveryBlockers)
1108
+ console.error(`setup-run: [recovery '${recovery.id}'] ${blocker}.`);
1109
+ return {
1110
+ exitCode: 2,
1111
+ message: `setup-run: recovery '${recovery.id}' (for failed step '${result.failedStep}') refusing before executing any recovery step:\n${recoveryBlockers.map((b) => ` - ${b}`).join('\n')}`,
1112
+ };
1113
+ }
744
1114
  console.error(`[setup] recovery '${recovery.id}' available for failed step '${result.failedStep}'`);
745
1115
  for (const recoveryStep of recovery.steps) {
746
- const recoveryResult = runStep(recoveryStep, manifest, validation.manifestDir, params.values, target.os, options.yes, true);
1116
+ const recoveryResult = await runStep(recoveryStep, manifest, validation.manifestDir, params.values, target.os, options.yes, true);
747
1117
  if (!recoveryResult.ok)
748
1118
  return { exitCode: recoveryResult.status || 1, message: `setup-run: recovery '${recovery.id}' failed` };
749
1119
  }
@@ -771,11 +1141,22 @@ export const setupValidateHandler = {
771
1141
  return { exitCode: 0 };
772
1142
  }
773
1143
  const strict = hasFlag(ctx.args, '--strict');
1144
+ let manifestPath;
1145
+ let schemaPath;
1146
+ try {
1147
+ manifestPath = flagValue(ctx.args, '--manifest') ?? positionalManifest(ctx.args);
1148
+ schemaPath = flagValue(ctx.args, '--schema');
1149
+ }
1150
+ catch (err) {
1151
+ if (err instanceof MissingFlagValueError)
1152
+ return { exitCode: 2, message: `${err.message}.` };
1153
+ throw err;
1154
+ }
774
1155
  const result = validateSetupManifest({
775
1156
  cwd: ctx.cwd,
776
1157
  frameworkRoot: ctx.frameworkRoot,
777
- manifestPath: flagValue(ctx.args, '--manifest') ?? positionalManifest(ctx.args),
778
- schemaPath: flagValue(ctx.args, '--schema'),
1158
+ manifestPath,
1159
+ schemaPath,
779
1160
  strict,
780
1161
  });
781
1162
  const payload = {
@@ -807,8 +1188,9 @@ export const setupGenerateHandler = {
807
1188
  }
808
1189
  catch (error) {
809
1190
  return {
810
- exitCode: 1,
811
- message: error instanceof Error ? error.message : String(error),
1191
+ // #3062 review item 11: a missing flag value is a usage error.
1192
+ exitCode: error instanceof MissingFlagValueError ? 2 : 1,
1193
+ message: error instanceof Error ? `${error.message}.` : String(error),
812
1194
  };
813
1195
  }
814
1196
  },
@@ -824,21 +1206,34 @@ export const setupRunHandler = {
824
1206
  process.stdout.write(RUN_HELP);
825
1207
  return { exitCode: 0 };
826
1208
  }
827
- const options = parseRunOptions(ctx);
828
- const manifestPath = options.manifestPath ?? 'setup.manifest.yaml';
1209
+ let options;
1210
+ let policyFlag;
1211
+ let verificationArgs = [];
1212
+ try {
1213
+ options = parseRunOptions(ctx);
1214
+ policyFlag = flagValue(ctx.args, '--policy');
1215
+ if (policyFlag) {
1216
+ const manifestPathForVerification = options.manifestPath ?? 'setup.manifest.yaml';
1217
+ verificationArgs = [
1218
+ manifestPathForVerification,
1219
+ '--attestation', flagValue(ctx.args, '--attestation') ?? `${manifestPathForVerification}.aiwg-attestation.json`,
1220
+ '--policy', policyFlag,
1221
+ '--asset-type', flagValue(ctx.args, '--asset-type') ?? 'setup-manifest',
1222
+ '--namespace', flagValue(ctx.args, '--namespace') ?? 'aiwg',
1223
+ '--channel', flagValue(ctx.args, '--channel') ?? 'stable',
1224
+ '--json',
1225
+ ...(flagValue(ctx.args, '--state') ? ['--state', flagValue(ctx.args, '--state')] : []),
1226
+ ...(hasFlag(ctx.args, '--offline') ? ['--offline'] : []),
1227
+ ];
1228
+ }
1229
+ }
1230
+ catch (err) {
1231
+ if (err instanceof MissingFlagValueError)
1232
+ return { exitCode: 2, message: `setup-run: ${err.message}.` };
1233
+ throw err;
1234
+ }
829
1235
  let artifactVerification;
830
- if (flagValue(ctx.args, '--policy')) {
831
- const verificationArgs = [
832
- manifestPath,
833
- '--attestation', flagValue(ctx.args, '--attestation') ?? `${manifestPath}.aiwg-attestation.json`,
834
- '--policy', flagValue(ctx.args, '--policy'),
835
- '--asset-type', flagValue(ctx.args, '--asset-type') ?? 'setup-manifest',
836
- '--namespace', flagValue(ctx.args, '--namespace') ?? 'aiwg',
837
- '--channel', flagValue(ctx.args, '--channel') ?? 'stable',
838
- '--json',
839
- ...(flagValue(ctx.args, '--state') ? ['--state', flagValue(ctx.args, '--state')] : []),
840
- ...(hasFlag(ctx.args, '--offline') ? ['--offline'] : []),
841
- ];
1236
+ if (policyFlag) {
842
1237
  const verification = await artifactVerifyHandler.execute({ ...ctx, args: verificationArgs, rawArgs: verificationArgs });
843
1238
  try {
844
1239
  artifactVerification = JSON.parse(verification.message ?? '');