@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,9 +4,21 @@
4
4
  * Implements `aiwg init` — the first-run wizard that creates
5
5
  * `.aiwg/aiwg.config` for the current project.
6
6
  *
7
- * Prompts:
8
- * 1. Which AI provider toolchains does this project target?
9
- * 2. Would you like to add any scripts? (optional)
7
+ * Prompts, and the argument that answers each one directly (#3062):
8
+ * 1. Which AI provider toolchains does this project target? → `--providers <list>`
9
+ * 2. Would you like to add default scripts (deploy/doctor/sync)? → `--default-scripts true|false`
10
+ * 3. Add a custom script (name + command)? → `--script <name>=<command>` (repeatable;
11
+ * implies yes) or `--custom-scripts true|false` (answers the question directly
12
+ * without supplying a script; `false` declines it outright)
13
+ * 4. Enable direct worker dispatch? → `--worker-dispatch enable|decline`
14
+ *
15
+ * With a terminal attached, any of these given directly skips that
16
+ * question — only genuinely missing values are still asked. Without one,
17
+ * omitted inputs fall back to their documented defaults (`claude`, default
18
+ * scripts on) rather than erroring, since those defaults are always safe to
19
+ * apply headless. `--non-interactive`/`--yes` force the non-interactive
20
+ * path even with a terminal attached. `--dry-run` never writes anything,
21
+ * including the baseline config itself.
10
22
  *
11
23
  * Runs automatically on first `aiwg use` when no config exists.
12
24
  * Can also be run explicitly to reconfigure.
@@ -16,10 +28,14 @@
16
28
  import readline from 'readline';
17
29
  import { readAiwgConfig, writeAiwgConfig, emptyConfig, populateDeployedTo, VALID_PROVIDERS, getConfigPath, } from '../../config/aiwg-config.js';
18
30
  import * as ui from '../ui.js';
19
- import { askString as sharedAskString, askYesNo as sharedAskYesNo } from '../prompt-utils.js';
31
+ import { askString as sharedAskString, askYesNo as sharedAskYesNo, askConsent } from '../prompt-utils.js';
20
32
  import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
21
33
  import { writeNormalizedAiwgMd } from '../../smiths/context-pipeline/finalization.js';
22
34
  import { ensureWorkspaceContext } from '../../smiths/context-pipeline/workspace-context.js';
35
+ import { detectInstalledWorkerClis, evaluateWorkerDispatchOffer, workerDispatchOfferQuestion, workerDispatchOfferHintWithArg, parseWorkerDispatchArg, } from '../../models/worker-dispatch-offer.js';
36
+ import { EXIT_CODES } from '../errors.js';
37
+ import { isInteractive } from '../env.js';
38
+ import { requireFlagValue, collectFlagValues, parseBooleanFlagValue, parseListFlagValue, parseKeyValueEntry, MissingFlagValueError, } from '../flag-parsing.js';
23
39
  const PROVIDER_LABELS = {
24
40
  antigravity: 'Google Antigravity .agents/',
25
41
  claude: 'Claude Code .claude/',
@@ -76,6 +92,89 @@ async function askProviders(rl, signal) {
76
92
  }
77
93
  return selected.length > 0 ? selected : ['claude'];
78
94
  }
95
+ /**
96
+ * Non-interactive / --yes / no-TTY runs never prompt and never touch the
97
+ * config (#3050 item 2) — they only print one line naming the enable
98
+ * command, when two or more worker CLIs are detected and dispatch is not
99
+ * already on or declined. The hint also names `--worker-dispatch` (#3062):
100
+ * that argument answers the same question headlessly.
101
+ */
102
+ function renderWorkerDispatchHintIfApplicable(parallelism, setup) {
103
+ const offer = evaluateWorkerDispatchOffer(parallelism, setup, detectInstalledWorkerClis());
104
+ if (offer.shouldOffer)
105
+ ui.dim(` ${workerDispatchOfferHintWithArg(offer.detected)}`);
106
+ }
107
+ function parseInitArgInputs(args) {
108
+ // #3062 second review, finding 4: `precedence: 'first'` matches this
109
+ // command's pre-existing (and, for `--providers`/`--default-scripts`/
110
+ // `--script`/`--custom-scripts`, only) convention — see
111
+ // `parseInitArgInputs`'s module-level callers for why `--worker-dispatch`
112
+ // specifically needs `rejectEmpty` (an explicit empty choice is never
113
+ // meaningful and must not silently fall through to "not given").
114
+ const workerDispatchArg = parseWorkerDispatchArg(requireFlagValue(args, { rejectEmpty: true }, '--worker-dispatch'));
115
+ const rawProviders = parseListFlagValue(requireFlagValue(args, {}, '--providers'));
116
+ const providersArg = rawProviders?.map((entry) => {
117
+ const canonical = normalizeProviderDefinitionId(entry);
118
+ if (!canonical || canonical === 'generic' || !VALID_PROVIDERS.includes(canonical)) {
119
+ throw new Error(`--providers: unknown provider '${entry}' (valid: ${VALID_PROVIDERS.join(', ')})`);
120
+ }
121
+ return canonical;
122
+ });
123
+ if (providersArg && providersArg.length === 0) {
124
+ throw new Error('--providers requires at least one provider name');
125
+ }
126
+ const defaultScriptsArg = parseBooleanFlagValue(requireFlagValue(args, {}, '--default-scripts'), '--default-scripts');
127
+ const customScriptsArg = parseBooleanFlagValue(requireFlagValue(args, {}, '--custom-scripts'), '--custom-scripts');
128
+ const scriptPairs = {};
129
+ for (const entry of collectFlagValues(args, {}, '--script')) {
130
+ const [name, command] = parseKeyValueEntry(entry, '--script');
131
+ scriptPairs[name] = command;
132
+ }
133
+ return { workerDispatchArg, providersArg, defaultScriptsArg, customScriptsArg, scriptPairs };
134
+ }
135
+ /** One-line description of what an explicit `--worker-dispatch` choice would write. */
136
+ function describeWorkerDispatchWrite(arg) {
137
+ return arg === 'enable'
138
+ ? 'enable parallelism.worker_dispatch'
139
+ : 'record a worker-dispatch decline (setup.worker_dispatch_offer)';
140
+ }
141
+ /**
142
+ * Resolve an explicit `--worker-dispatch` choice into a decision to apply to
143
+ * a brand-new config (#3062). Under `--dry-run`, writes nothing — reports
144
+ * what would be written instead and returns `undefined` so the caller never
145
+ * applies it to the config it builds.
146
+ */
147
+ function resolveWorkerDispatchDecisionForNewConfig(arg, dryRun) {
148
+ if (dryRun) {
149
+ ui.dim(` Would ${describeWorkerDispatchWrite(arg)} (--dry-run writes nothing).`);
150
+ return undefined;
151
+ }
152
+ return { kind: arg };
153
+ }
154
+ /**
155
+ * Interactive-only: asks the one consent question (default/unanswered is
156
+ * no, strict `y`/`yes` parsing only) when two or more worker CLIs are
157
+ * detected and dispatch is not already on or declined. Returns the
158
+ * decision to apply, or `undefined` when there is nothing to offer, or
159
+ * when the prompt timed out / stdin closed without an answer — only an
160
+ * actually-answered "no" is recorded as a persistent decline (#3050
161
+ * review item 4).
162
+ */
163
+ async function askWorkerDispatchOffer(rl, parallelism, setup, signal) {
164
+ const offer = evaluateWorkerDispatchOffer(parallelism, setup, detectInstalledWorkerClis());
165
+ if (!offer.shouldOffer)
166
+ return undefined;
167
+ console.log('');
168
+ const outcome = await askConsent(rl, workerDispatchOfferQuestion(offer.detected), undefined, signal);
169
+ if (outcome.kind !== 'answered')
170
+ return undefined;
171
+ if (outcome.value) {
172
+ ui.success(' Will enable parallelism.worker_dispatch.');
173
+ return { kind: 'enable' };
174
+ }
175
+ ui.dim(' Noted — enable later with `aiwg config set --project parallelism.worker_dispatch true`.');
176
+ return { kind: 'decline' };
177
+ }
79
178
  export const initHandler = {
80
179
  id: 'init',
81
180
  name: 'Init',
@@ -84,16 +183,34 @@ export const initHandler = {
84
183
  aliases: [],
85
184
  async execute(ctx) {
86
185
  const force = ctx.args.includes('--force');
87
- // Treat a missing stdin TTY as non-interactive so piped/backgrounded
88
- // invocations never attempt to create a readline interface. This is a
89
- // single authoritative check — downstream code MUST NOT re-derive
186
+ // `--non-interactive`/`--yes` are this command's own explicit flags for
187
+ // the one shared interactive decision (#3062 review item 12) — honored
188
+ // ahead of (and regardless of) the environment-only checks `isInteractive`
189
+ // otherwise applies (TTY, TERM, CI). Downstream code MUST NOT re-derive
90
190
  // interactive-ness from process.stdin.isTTY again.
91
- const nonInteractive = ctx.args.includes('--non-interactive') ||
92
- ctx.args.includes('--yes') ||
93
- !process.stdin.isTTY ||
94
- !process.stdout.isTTY ||
95
- process.env['CI'] === 'true';
191
+ const explicitNonInteractive = ctx.args.includes('--non-interactive') || ctx.args.includes('--yes');
192
+ const nonInteractive = !isInteractive(explicitNonInteractive);
96
193
  const projectDir = ctx.cwd;
194
+ // `--worker-dispatch enable|decline` answers the worker-dispatch offer
195
+ // directly (#3062), with or without a terminal, and is honored even when
196
+ // fewer than two worker CLIs are detected — an explicit choice overrides
197
+ // the presence-only detection gate. `--providers`/`--default-scripts`/
198
+ // `--script` answer the remaining wizard inputs the same way (review
199
+ // item 10). Every flag is parsed strictly (review item 11): present
200
+ // without a usable value is a usage error, raised here before any
201
+ // preflight check, read, or write.
202
+ let workerDispatchArg;
203
+ let providersArg;
204
+ let defaultScriptsArg;
205
+ let customScriptsArg;
206
+ let scriptPairs;
207
+ try {
208
+ ({ workerDispatchArg, providersArg, defaultScriptsArg, customScriptsArg, scriptPairs } = parseInitArgInputs(ctx.args));
209
+ }
210
+ catch (err) {
211
+ const message = err instanceof MissingFlagValueError ? `${err.message}.` : err instanceof Error ? err.message : String(err);
212
+ return { exitCode: EXIT_CODES.USAGE, message };
213
+ }
97
214
  ui.blank();
98
215
  console.log(` ${ui.brandMark()} ${ui.bold('AIWG Baseline Config')}`);
99
216
  ui.rule();
@@ -101,7 +218,10 @@ export const initHandler = {
101
218
  const existing = await readAiwgConfig(projectDir);
102
219
  if (existing && !force) {
103
220
  ui.info(`Config already exists: ${getConfigPath(projectDir)}`);
104
- await writeNormalizedAiwgMd(projectDir);
221
+ // #3062 review item 18: `--dry-run` must write nothing at all, not
222
+ // just skip the worker-dispatch increment below.
223
+ if (!ctx.dryRun)
224
+ await writeNormalizedAiwgMd(projectDir);
105
225
  console.log(` Providers: ${existing.providers.join(', ')}`);
106
226
  const scriptCount = Object.keys(existing.scripts).length;
107
227
  if (scriptCount > 0) {
@@ -111,22 +231,72 @@ export const initHandler = {
111
231
  ui.dim(' Ask your AIWG agent to review repo, tracker, delivery, signing, and provider policy.');
112
232
  ui.dim(' Agents may use `aiwg setup project --dry-run` as the CLI helper for that review.');
113
233
  ui.dim(' Run `aiwg init --force` only when you need to recreate the baseline config.');
234
+ // #3050: guide users to direct worker dispatch from the setup step they
235
+ // already run, instead of leaving it undiscoverable. `--dry-run` must
236
+ // never prompt and never write (review item 1) — it degrades to the
237
+ // same hint-only behavior as non-interactive runs.
238
+ // #3062: an explicit `--worker-dispatch` argument always answers the
239
+ // question directly (bypassing the ask/hint entirely) except under
240
+ // `--dry-run`, which still writes nothing — it reports what would be
241
+ // written instead.
242
+ if (workerDispatchArg) {
243
+ if (ctx.dryRun) {
244
+ ui.dim(` Would ${describeWorkerDispatchWrite(workerDispatchArg)} (--dry-run writes nothing).`);
245
+ }
246
+ else if (workerDispatchArg === 'enable') {
247
+ await writeAiwgConfig(projectDir, { ...existing, parallelism: { ...existing.parallelism, worker_dispatch: true } });
248
+ ui.success(' Enabled parallelism.worker_dispatch. Next: aiwg doctor --probe-workers --qualify.');
249
+ }
250
+ else {
251
+ await writeAiwgConfig(projectDir, { ...existing, setup: { ...existing.setup, worker_dispatch_offer: 'declined' } });
252
+ ui.success(' Recorded worker-dispatch decline (setup.worker_dispatch_offer).');
253
+ }
254
+ }
255
+ else if (nonInteractive || ctx.dryRun) {
256
+ renderWorkerDispatchHintIfApplicable(existing.parallelism, existing.setup);
257
+ }
258
+ else {
259
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
260
+ try {
261
+ const decision = await askWorkerDispatchOffer(rl, existing.parallelism, existing.setup, ctx.signal);
262
+ if (decision?.kind === 'enable') {
263
+ await writeAiwgConfig(projectDir, { ...existing, parallelism: { ...existing.parallelism, worker_dispatch: true } });
264
+ ui.info('Next: aiwg doctor --probe-workers --qualify.');
265
+ }
266
+ else if (decision?.kind === 'decline') {
267
+ await writeAiwgConfig(projectDir, { ...existing, setup: { ...existing.setup, worker_dispatch_offer: 'declined' } });
268
+ }
269
+ }
270
+ finally {
271
+ rl.close();
272
+ }
273
+ }
114
274
  ui.blank();
115
275
  return { exitCode: 0 };
116
276
  }
117
277
  let providers;
118
278
  let scripts = {};
279
+ let workerDispatchDecision;
280
+ const DEFAULT_SCRIPTS = { deploy: 'aiwg use all', doctor: 'aiwg doctor', sync: 'aiwg sync' };
119
281
  if (nonInteractive) {
120
- // Non-interactive: use defaults
121
- providers = ['claude'];
122
- scripts = {
123
- deploy: 'aiwg use all',
124
- doctor: 'aiwg doctor',
125
- sync: 'aiwg sync',
126
- };
282
+ // Non-interactive: an explicit `--providers`/`--default-scripts`
283
+ // answers the corresponding wizard input directly (review item 10);
284
+ // anything not supplied keeps its prior default.
285
+ providers = providersArg ?? ['claude'];
286
+ const includeDefaultScripts = defaultScriptsArg ?? true;
287
+ scripts = { ...(includeDefaultScripts ? DEFAULT_SCRIPTS : {}), ...scriptPairs };
288
+ if (workerDispatchArg) {
289
+ workerDispatchDecision = resolveWorkerDispatchDecisionForNewConfig(workerDispatchArg, ctx.dryRun);
290
+ }
291
+ else {
292
+ renderWorkerDispatchHintIfApplicable(undefined, undefined);
293
+ }
127
294
  }
128
295
  else {
129
- // Interactive wizard
296
+ // Interactive wizard — skip a prompt whenever its argument already
297
+ // answered it (review item 10/12: "with a terminal we can skip steps
298
+ // for any params provided and only provide interaction when we are
299
+ // missing a value").
130
300
  const rl = readline.createInterface({
131
301
  input: process.stdin,
132
302
  output: process.stdout,
@@ -134,16 +304,29 @@ export const initHandler = {
134
304
  try {
135
305
  // Thread ctx.signal through every prompt so Ctrl-C during the wizard
136
306
  // cancels cleanly instead of being absorbed by readline (POC #926).
137
- providers = await askProviders(rl, ctx.signal);
307
+ providers = providersArg ?? await askProviders(rl, ctx.signal);
138
308
  console.log('');
139
- const addScripts = await askYesNo(rl, ' Add default scripts (deploy, doctor, sync)? [Y/n]: ', true, ctx.signal);
140
- if (addScripts) {
141
- scripts = {
142
- deploy: 'aiwg use all',
143
- doctor: 'aiwg doctor',
144
- sync: 'aiwg sync',
145
- };
146
- const addCustom = await askYesNo(rl, ' Add a custom script? [y/N]: ', false, ctx.signal);
309
+ const includeDefaultScripts = defaultScriptsArg !== undefined
310
+ ? defaultScriptsArg
311
+ : await askYesNo(rl, ' Add default scripts (deploy, doctor, sync)? [Y/n]: ', true, ctx.signal);
312
+ scripts = includeDefaultScripts ? { ...DEFAULT_SCRIPTS } : {};
313
+ if (Object.keys(scriptPairs).length > 0) {
314
+ // `--script` already answered "add a custom script?" (implicit
315
+ // yes) and its name/command sub-prompts — never ask any of the
316
+ // three (#3062 second review, finding 5a).
317
+ scripts = { ...scripts, ...scriptPairs };
318
+ }
319
+ else {
320
+ // `--custom-scripts true|false` answers "add a custom script?"
321
+ // directly when `--script` itself was not given — an explicit
322
+ // decline (`false`) skips the question entirely, same as every
323
+ // other already-supplied input; `true` still asks for the
324
+ // name/command, since those were not supplied either.
325
+ const addCustom = customScriptsArg !== undefined
326
+ ? customScriptsArg
327
+ : includeDefaultScripts
328
+ ? await askYesNo(rl, ' Add a custom script? [y/N]: ', false, ctx.signal)
329
+ : false;
147
330
  if (addCustom) {
148
331
  const name = await askString(rl, ' Script name: ', '', ctx.signal);
149
332
  const cmd = await askString(rl, ' Command: ', '', ctx.signal);
@@ -152,6 +335,21 @@ export const initHandler = {
152
335
  }
153
336
  }
154
337
  }
338
+ // #3050: offer direct worker dispatch inside the same wizard — but
339
+ // `--dry-run` must never prompt or write anything from the offer
340
+ // (review item 1), so it degrades to the hint instead. An explicit
341
+ // `--worker-dispatch` argument (#3062) always answers the question
342
+ // directly instead of asking or hinting, still subject to `--dry-run`
343
+ // writing nothing.
344
+ if (workerDispatchArg) {
345
+ workerDispatchDecision = resolveWorkerDispatchDecisionForNewConfig(workerDispatchArg, ctx.dryRun);
346
+ }
347
+ else if (ctx.dryRun) {
348
+ renderWorkerDispatchHintIfApplicable(undefined, undefined);
349
+ }
350
+ else {
351
+ workerDispatchDecision = await askWorkerDispatchOffer(rl, undefined, undefined, ctx.signal);
352
+ }
155
353
  }
156
354
  finally {
157
355
  rl.close();
@@ -160,10 +358,30 @@ export const initHandler = {
160
358
  // Build config
161
359
  let config = emptyConfig(providers);
162
360
  config.scripts = scripts;
361
+ if (workerDispatchDecision?.kind === 'enable') {
362
+ config.parallelism = { ...config.parallelism, worker_dispatch: true };
363
+ }
364
+ else if (workerDispatchDecision?.kind === 'decline') {
365
+ config.setup = { ...config.setup, worker_dispatch_offer: 'declined' };
366
+ }
367
+ // #3062 review item 18: `--dry-run` writes nothing at all — not even
368
+ // the baseline config — rather than only skipping the worker-dispatch
369
+ // increment. Report what would be written instead.
370
+ if (ctx.dryRun) {
371
+ ui.blank();
372
+ ui.dim(` Would create ${getConfigPath(projectDir)} (--dry-run writes nothing).`);
373
+ ui.dim(` Providers: ${providers.join(', ')}`);
374
+ if (Object.keys(scripts).length > 0)
375
+ ui.dim(` Scripts: ${Object.keys(scripts).join(', ')}`);
376
+ ui.blank();
377
+ return { exitCode: 0 };
378
+ }
163
379
  // Populate deployedTo from disk for entries with empty deployedTo (#721)
164
380
  config = await populateDeployedTo(config, projectDir);
165
381
  // Write
166
382
  await writeAiwgConfig(projectDir, config);
383
+ if (workerDispatchDecision?.kind === 'enable')
384
+ ui.info('Next: aiwg doctor --probe-workers --qualify.');
167
385
  const normalizedAiwgMdPath = await writeNormalizedAiwgMd(projectDir);
168
386
  const workspace = await ensureWorkspaceContext(projectDir);
169
387
  ui.blank();
@@ -25,6 +25,11 @@ export const lintHandler = {
25
25
  description: 'Lint AIWG artifacts against declarative rule sets',
26
26
  category: 'utility',
27
27
  aliases: ['-lint', '--lint'],
28
+ async help() {
29
+ const { main } = await import('../../lint/cli.js');
30
+ await main(['--help']);
31
+ return { exitCode: 0 };
32
+ },
28
33
  async execute(ctx) {
29
34
  try {
30
35
  const { main } = await import('../../lint/cli.js');
@@ -15,6 +15,7 @@ import * as ui from '../ui.js';
15
15
  import { promises as fs } from 'node:fs';
16
16
  import { join } from 'node:path';
17
17
  import { createHash, randomBytes } from 'node:crypto';
18
+ import { assertMissionWorkerHandoff } from '../../mission-protocol/codecs.js';
18
19
  // ── Constants ────────────────────────────────────────────────
19
20
  const MC_ROOT = '.aiwg/ralph-external/mc';
20
21
  const SESSIONS_DIR = join(MC_ROOT, 'sessions');
@@ -378,6 +379,18 @@ async function mcDispatch(ctx) {
378
379
  ui.error(sessionId ? `Session not found: ${sessionId}` : 'No active session. Run `aiwg mc start` first.');
379
380
  return { exitCode: 1 };
380
381
  }
382
+ // mission-dispatch accepts only the frozen objective envelope. Worker
383
+ // bindings can arrive through its existing session, so inspect that payload.
384
+ try {
385
+ assertMissionWorkerHandoff(selected, 'mc-cli');
386
+ for (const mission of selected.missions)
387
+ assertMissionWorkerHandoff(mission, 'mc-cli');
388
+ }
389
+ catch (error) {
390
+ const message = error instanceof Error ? error.message : String(error);
391
+ ui.error(message);
392
+ return { exitCode: 1, message };
393
+ }
381
394
  // #1361: Check project-level parallelism cap. Active missions = running or
382
395
  // queued; if at cap, warn but still queue (FIFO behavior — the mission goes
383
396
  // into the session as 'queued' and will run when a slot frees up).
@@ -560,6 +573,16 @@ async function mcRun(ctx) {
560
573
  return { exitCode: 1 };
561
574
  }
562
575
  const queued = session.missions.filter((m) => m.status === 'queued');
576
+ try {
577
+ assertMissionWorkerHandoff(session, 'mc-cli');
578
+ for (const mission of queued)
579
+ assertMissionWorkerHandoff(mission, 'mc-cli');
580
+ }
581
+ catch (error) {
582
+ const message = error instanceof Error ? error.message : String(error);
583
+ ui.error(message);
584
+ return { exitCode: 1, message };
585
+ }
563
586
  if (queued.length === 0) {
564
587
  ui.info(`No queued missions in session ${session.id}. Run \`aiwg mc dispatch ${session.id} "<objective>"\` to add one.`);
565
588
  return { exitCode: 0 };
@@ -4,16 +4,114 @@
4
4
  */
5
5
  import { promises as fs } from 'node:fs';
6
6
  import os from 'node:os';
7
+ import { isDeepStrictEqual } from 'node:util';
7
8
  import path from 'node:path';
8
9
  import { glob } from 'glob';
9
10
  import { load as loadYaml } from 'js-yaml';
10
- import { compileModelPolicy, validateCanonicalModelPolicy, validateUserProjectModelConfig, } from '../../models/provider-policy.js';
11
+ import { compileModelPolicy, validateCanonicalModelPolicy, validateUserProjectModelConfig, loadProviderModelCapabilities, WorkerPolicyError, } from '../../models/provider-policy.js';
11
12
  import { resolveGrokBuildRoleModel } from '../../models/grok-build-models.js';
13
+ import { readAiwgConfig, emptyConfig } from '../../config/aiwg-config.js';
14
+ import { ConfigLoader } from '../../models/config-loader.js';
15
+ import { loadRecordedWorkerInstallations, normalizeWorkerVersionBanner } from '../../models/worker-installation.mjs';
16
+ import { parseWorkerCommandArgs, resolveWorkerInvocation, workerInvocationDiagnostic } from '../../serve/worker-invocation.js';
17
+ /** Compare selection facts without observation times or approval/provenance metadata. */
18
+ function approvalFacts(row) {
19
+ if (!row.worker)
20
+ return null;
21
+ const { observedAt, verifiedAt, firstObservedAt, fetchedAt, retiredAt, evidenceDigest, provenance, qualification, ...facts } = row.worker;
22
+ return { id: row.id, apiModelId: row.apiModelId, llmProvider: row.llmProvider,
23
+ hidden: row.hidden, reasoningEfforts: row.reasoningEfforts, facts };
24
+ }
25
+ /**
26
+ * #2892: exported so the MCP worker-invoke bridge can resolve a direct worker
27
+ * plan in-process (same snapshot/driver construction as `aiwg models resolve`),
28
+ * without shelling out to the CLI. No new loader is introduced; this is the
29
+ * CLI's own existing default `ModelsWorkerServices.load`.
30
+ */
31
+ export async function loadWorkerInputs(ctx) {
32
+ const config = await readAiwgConfig(ctx.cwd) ?? emptyConfig();
33
+ const loader = new ConfigLoader(ctx.cwd);
34
+ const models = await loader.load(config.parallelism);
35
+ const catalog = await effectiveCatalog(ctx, ctx.cwd, false, parseArgs(['resolve']));
36
+ const registry = loadProviderModelCapabilities();
37
+ // Let the resolver explain the dispatch gate before requiring host qualification (#3070).
38
+ if (!config.parallelism?.worker_dispatch) {
39
+ return { snapshot: { config, models, modelDigests: loader.getInputDigests(), catalog,
40
+ registry, installations: {} }, drivers: {} };
41
+ }
42
+ const { registry: qualifiedRegistry, installations, drivers, observations } = await loadRecordedWorkerInstallations({
43
+ registry, cwd: ctx.cwd, policy: models.defaults.workerSelection, parallelism: config.parallelism,
44
+ });
45
+ catalog.discovery ??= { source: 'static', fetchedAt: new Date().toISOString(), providers: {} };
46
+ catalog.discovery.instances = { ...catalog.discovery.instances };
47
+ for (const [cli, approved] of Object.entries(observations)) {
48
+ const cached = catalog.discovery.instances[cli] ?? catalog.discovery.providers[approved.provider];
49
+ // Discovery stores the CLI banner; qualification stores its parsed version.
50
+ const invocation = drivers[cli]?.qualification.adapter.invocation;
51
+ const versionPattern = invocation?.kind === 'process' ? invocation.versionPattern : undefined;
52
+ const version = (value) => {
53
+ if (value === undefined)
54
+ return value;
55
+ try {
56
+ return normalizeWorkerVersionBanner(value, versionPattern);
57
+ }
58
+ catch {
59
+ return value;
60
+ } // A changed or invalid banner must still fail closed.
61
+ };
62
+ const current = cached ? { ...cached, runtimeVersion: version(cached.runtimeVersion),
63
+ accountBinding: cached.accountBinding ?? cached.accountScope,
64
+ models: cached.models.map(row => row.worker ? { ...row,
65
+ worker: { ...row.worker, runtimeVersion: version(row.worker.runtimeVersion) } } : row) } : undefined;
66
+ // Stale discovery must not replace a newer approved observation.
67
+ if (!current || Date.parse(approved.observedAt) > Date.parse(current.observedAt)) {
68
+ catalog.discovery.instances[cli] = approved;
69
+ continue;
70
+ }
71
+ catalog.discovery.instances[cli] = { ...current, models: current.models.map(row => {
72
+ const prior = approved.models.find(model => model.id === row.id);
73
+ if (!row.worker || !prior?.worker)
74
+ return row;
75
+ if (Date.parse(prior.worker.observedAt) > Date.parse(row.worker.observedAt))
76
+ return prior;
77
+ // New retirement/conflict/visibility evidence always requires requalification.
78
+ const safe = !row.hidden && row.worker.visibility !== 'hidden'
79
+ && row.worker.lifecycle !== 'retired' && !row.worker.conflicts?.length;
80
+ return safe && isDeepStrictEqual(approvalFacts(row), approvalFacts(prior))
81
+ ? { ...row, worker: { ...row.worker, qualification: prior.worker.qualification } } : row;
82
+ }) };
83
+ }
84
+ return { snapshot: { config, models, modelDigests: loader.getInputDigests(), catalog,
85
+ registry: qualifiedRegistry, installations }, drivers };
86
+ }
87
+ async function resolveDirect(ctx, services) {
88
+ let shell = false;
89
+ try {
90
+ const request = parseWorkerCommandArgs(ctx.args.slice(1));
91
+ shell = request.format === 'shell';
92
+ const { snapshot, drivers } = await services.load(ctx);
93
+ const plan = await resolveWorkerInvocation(snapshot, request, drivers, services.now?.());
94
+ const runnable = plan.invocation?.readiness === 'launchable';
95
+ if (shell && !runnable)
96
+ console.error(JSON.stringify({ status: 'blocked', diagnostics: plan.diagnostics }, null, 2));
97
+ else if (shell && plan.invocation?.transport === 'shell')
98
+ print(plan.invocation.shellCommand, false);
99
+ else
100
+ print(plan, true);
101
+ return { exitCode: runnable ? 0 : 2 };
102
+ }
103
+ catch (error) {
104
+ const diagnostic = { status: 'blocked', diagnostics: [workerInvocationDiagnostic(error, error instanceof WorkerPolicyError
105
+ && error.code === 'OG_ADAPTER_UNQUALIFIED' ? '/parallelism/clis' : '/request')] };
106
+ if (shell)
107
+ console.error(JSON.stringify(diagnostic, null, 2));
108
+ else
109
+ print(diagnostic, true);
110
+ return { exitCode: 2 };
111
+ }
112
+ }
12
113
  const TIERS = new Set(['economy', 'standard', 'premium', 'max-quality']);
13
- const PROVIDERS = new Set([
14
- 'antigravity', 'claude', 'codex', 'copilot', 'cursor', 'deepseek-harness', 'factory', 'grok-build', 'grokbot', 'hermes',
15
- 'opencode', 'openclaw', 'openhuman', 'omp', 'pi', 'warp', 'windsurf',
16
- ]);
114
+ const PROVIDERS = new Set(Object.keys(loadProviderModelCapabilities().providers));
17
115
  function parseArgs(args) {
18
116
  const [subcommand = 'help', ...rest] = args;
19
117
  const flags = new Map();
@@ -222,18 +320,75 @@ function replaceFrontmatter(raw, item, tier) {
222
320
  }
223
321
  return `---\n${frontmatter}\n---\n${match[2]}`;
224
322
  }
225
- async function execute(ctx) {
323
+ const MODELS_HELP = `Usage: aiwg models <subcommand> [options]
324
+
325
+ Subcommands:
326
+ list List discovered agent and skill model policies
327
+ audit Compile selected policies and report diagnostics
328
+ resolve Resolve artifact policies, or a direct worker invocation
329
+ validate Validate selected artifact policies (not worker configuration)
330
+ sources Show the effective model catalog without network refresh
331
+ refresh Refresh model discovery (may access the network)
332
+ set-default <TIER> Write defaults.tier to models.json
333
+ set --tier <TIER> Update selected agent/skill frontmatter; requires a selector or --all
334
+ migrate Add canonical role/tier hints to legacy skills
335
+
336
+ Artifact selectors and options:
337
+ --agent <name> --skill <name> --glob <pattern> --role <role>
338
+ --tier <tier> --framework <name> --all
339
+ --provider <provider> Provider for artifact compilation (default: claude)
340
+ --model <id> Explicit Grok Build model for artifact resolve
341
+ --target <directory> Artifact/project root (default: current directory)
342
+ --scope project|user set-default destination (default: project)
343
+ --dry-run Preview mutations without writing
344
+ --json Emit JSON
345
+ --drift Include catalog drift for sources/refresh
346
+ --url <url> Remote catalog URL for refresh
347
+ --help, -h Show this reference
348
+ TIER: economy | standard | premium | max-quality
349
+
350
+ Direct worker resolution (plans an invocation; does not launch it):
351
+ aiwg models resolve --role <role> --task <class> --group <group>
352
+ --brief <file> --workdir <tree> --final-output <file>
353
+ [--transport shell|mcp] [--format json|shell] [--authorize-premium]
354
+ --role efficiency | coding | reasoning
355
+ --task Task class from defaults.workerSelection.taskClasses
356
+ Built-ins: inventory, implementation, architecture, review
357
+ --group Group from parallelism.groups and the role's guidance
358
+ --brief Absolute path to the task brief
359
+ --workdir Absolute path to the worker tree
360
+ --final-output Absolute path for the final result
361
+ --transport Must match group execution transport (default: shell)
362
+ --format JSON plan or POSIX shell command (default: json)
363
+ --json Alias for --format json
364
+ --authorize-premium Authorize premium for this resolution only
365
+ Agents require explicit human confirmation of escalation
366
+
367
+ Worker groups live in .aiwg/aiwg.config; role guidance lives in project-root models.json.
368
+ Configure both, then run aiwg doctor --probe-workers --qualify.
369
+ See docs/getting-started/worker-dispatch.md for a complete example.`;
370
+ async function execute(ctx, services = { load: loadWorkerInputs }) {
371
+ if (ctx.args.length === 0 || ctx.args[0] === 'help' || ctx.args.includes('--help') || ctx.args.includes('-h')) {
372
+ print(MODELS_HELP, false);
373
+ return { exitCode: 0 };
374
+ }
375
+ // Preserve artifact/native resolve when direct-only flags are absent.
376
+ if (ctx.args[0] === 'resolve' && ctx.args.slice(1).some(arg => /^--(?:task|group|brief|workdir|final-output|transport|format|authorize-premium)(?:=|$)/.test(arg))) {
377
+ return resolveDirect(ctx, services);
378
+ }
226
379
  const parsed = parseArgs(ctx.args);
380
+ if (parsed.subcommand === 'resolve') {
381
+ const supported = new Set(['agent', 'skill', 'glob', 'role', 'tier', 'framework', 'all', 'target', 'provider', 'model', 'json', 'help', 'dry-run']);
382
+ for (const key of parsed.flags.keys())
383
+ if (!supported.has(key))
384
+ return { exitCode: 2, message: `Unsupported models resolve flag: --${key}` };
385
+ }
227
386
  const json = parsed.flags.has('json');
228
387
  const dryRun = parsed.flags.has('dry-run') || ctx.dryRun === true;
229
388
  const target = path.resolve(flagString(parsed, 'target') ?? ctx.cwd);
230
389
  const provider = flagString(parsed, 'provider') ?? 'claude';
231
390
  if (!PROVIDERS.has(provider))
232
391
  return { exitCode: 2, message: `Unknown provider: ${provider}` };
233
- if (parsed.subcommand === 'help' || parsed.flags.has('help')) {
234
- print('Usage: aiwg models <audit|list|resolve|sources|refresh|set-default|set|validate|migrate> [selectors] [--provider P] [--dry-run] [--json]', json);
235
- return { exitCode: 0 };
236
- }
237
392
  if (parsed.subcommand === 'sources' || parsed.subcommand === 'refresh') {
238
393
  const { diffModelCatalog } = await import('../../models/model-discovery.js');
239
394
  const catalog = await effectiveCatalog(ctx, target, parsed.subcommand === 'refresh', parsed, provider);
@@ -378,6 +533,10 @@ export const modelsHandler = {
378
533
  description: 'Audit, resolve, validate, and safely update model policy',
379
534
  category: 'catalog',
380
535
  aliases: [],
536
+ help: async () => ({ exitCode: 0, message: MODELS_HELP, rawOutput: true }),
381
537
  execute,
382
538
  };
539
+ export function createModelsHandler(services) {
540
+ return { ...modelsHandler, execute: ctx => execute(ctx, services) };
541
+ }
383
542
  //# sourceMappingURL=models.js.map
@@ -296,6 +296,8 @@ export function saveLauncherRegistry(projectRoot, registry) {
296
296
  * Check if a process is still running
297
297
  */
298
298
  export function isProcessAlive(pid) {
299
+ if (!Number.isSafeInteger(pid) || pid <= 0)
300
+ return false;
299
301
  try {
300
302
  process.kill(pid, 0);
301
303
  return true;
@@ -336,6 +336,9 @@ export class RalphStatusHandler {
336
336
  category = 'ralph';
337
337
  aliases = ['agent-loop-status', 'ralph-status'];
338
338
  async execute(ctx) {
339
+ if (ctx.args.includes('--workers')) {
340
+ return createScriptRunner(ctx.frameworkRoot).run('tools/ralph-external/index.mjs', ['--status', ...ctx.args]);
341
+ }
339
342
  const showAll = ctx.args.includes('--all') || ctx.args.includes('-a');
340
343
  const statuses = getLoopStatuses(process.cwd());
341
344
  if (statuses.length === 0) {