@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
@@ -1,26 +1,42 @@
1
- import { spawnSync } from 'child_process';
1
+ import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
2
2
  import { existsSync } from 'fs';
3
3
  import { emptyConfig, FORCE_PUSH_POLICY_ALIAS_NOTE, normalizeForcePushPolicy as normalizeForcePushPolicyShared, getConfigPath, getProjectDir, readAiwgConfig, resolveRemoteProvider, VALID_PROVIDERS, writeAiwgConfig, } from '../../config/aiwg-config.js';
4
4
  import { AiwgError, EXIT_CODES } from '../errors.js';
5
- import { askChoice, askString, askYesNo, createPromptInterface } from '../prompt-utils.js';
5
+ import { askChoice, askConsent, askString, askYesNo, createPromptInterface } from '../prompt-utils.js';
6
6
  import * as ui from '../ui.js';
7
7
  import { projectAiwgPath } from '../../config/project-artifacts.js';
8
8
  import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
9
+ import { isInteractive } from '../env.js';
10
+ import { requireFlagValue as strictFlagValue, MissingFlagValueError } from '../flag-parsing.js';
11
+ import { detectInstalledWorkerClis, evaluateWorkerDispatchOffer, workerDispatchOfferHintWithArg, workerDispatchOfferQuestion, WORKER_DISPATCH_ARG_VALUES, } from '../../models/worker-dispatch-offer.js';
9
12
  const DELIVERY_MODES = ['pr-required', 'feature-branch', 'direct'];
10
13
  const FORCE_PUSH_POLICIES = ['never', 'own-branch-only', 'allowed'];
11
14
  const ISSUE_PROVIDERS = ['gitea', 'github', 'local'];
12
15
  const SIGNING_FORMATS = ['openpgp', 'ssh', 'x509'];
13
16
  const TRACKER_VIA = ['tea', 'gh', 'mcp', 'api'];
17
+ /**
18
+ * A value-taking flag present without a usable value is a usage error, not
19
+ * "not given" (#3062 review item 11) — delegates to the shared strict
20
+ * parser, translating its `MissingFlagValueError` into this file's own
21
+ * `AiwgError`/`EXIT_CODES.USAGE` convention. `precedence: 'first'` matches
22
+ * this command's own pre-#3062 behavior (a hand-rolled loop that returned
23
+ * on the first match) — unchanged by the shared parser's second-round fix
24
+ * (#3062 second review, finding 4).
25
+ */
14
26
  function flagValue(args, ...names) {
15
- for (let i = 0; i < args.length; i += 1) {
16
- if (names.includes(args[i] ?? '')) {
17
- const value = args[i + 1];
18
- if (!value || value.startsWith('--'))
19
- return undefined;
20
- return value;
27
+ try {
28
+ return strictFlagValue(args, {}, ...names);
29
+ }
30
+ catch (err) {
31
+ if (err instanceof MissingFlagValueError) {
32
+ throw new AiwgError({
33
+ code: 'ERR_USAGE_MISSING_FLAG_VALUE',
34
+ message: `${err.message}.`,
35
+ exitCode: EXIT_CODES.USAGE,
36
+ });
21
37
  }
38
+ throw err;
22
39
  }
23
- return undefined;
24
40
  }
25
41
  function boolFlag(args, name) {
26
42
  return args.includes(name);
@@ -92,6 +108,10 @@ export function parseSetupProjectOptions(ctx) {
92
108
  trackerActorVia: parseEnum(flagValue(args, '--tracker-actor-via'), TRACKER_VIA, '--tracker-actor-via'),
93
109
  customerTrackerActorLogin: flagValue(args, '--customer-tracker-actor-login'),
94
110
  customerTrackerActorVia: parseEnum(flagValue(args, '--customer-tracker-actor-via'), TRACKER_VIA, '--customer-tracker-actor-via'),
111
+ // #3062: answers the worker-dispatch question directly, with or without
112
+ // a terminal, and suppresses the interactive question below (see
113
+ // `SetupProjectOptions.workerDispatchChoice`'s doc comment).
114
+ workerDispatchChoice: parseEnum(flagValue(args, '--worker-dispatch'), WORKER_DISPATCH_ARG_VALUES, '--worker-dispatch'),
95
115
  providers: (() => {
96
116
  const providers = parseStringList(flagValue(args, '--providers'));
97
117
  return providers ? normalizeProviders(providers) : undefined;
@@ -99,7 +119,7 @@ export function parseSetupProjectOptions(ctx) {
99
119
  };
100
120
  }
101
121
  export function detectGitRemotes(projectDir) {
102
- const result = spawnSync('git', ['-C', projectDir, 'remote', '-v'], {
122
+ const result = boundedSpawnSync('git', ['-C', projectDir, 'remote', '-v'], 'quick', {
103
123
  encoding: 'utf-8',
104
124
  stdio: ['ignore', 'pipe', 'ignore'],
105
125
  });
@@ -122,21 +142,21 @@ export function detectGitRemotes(projectDir) {
122
142
  return [...byName.values()];
123
143
  }
124
144
  function detectDefaultBranch(projectDir) {
125
- const symbolic = spawnSync('git', ['-C', projectDir, 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], {
145
+ const symbolic = boundedSpawnSync('git', ['-C', projectDir, 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], 'quick', {
126
146
  encoding: 'utf-8',
127
147
  stdio: ['ignore', 'pipe', 'ignore'],
128
148
  });
129
149
  const fromOrigin = symbolic.stdout.trim().replace(/^origin\//, '');
130
150
  if (symbolic.status === 0 && fromOrigin)
131
151
  return fromOrigin;
132
- const branch = spawnSync('git', ['-C', projectDir, 'branch', '--show-current'], {
152
+ const branch = boundedSpawnSync('git', ['-C', projectDir, 'branch', '--show-current'], 'quick', {
133
153
  encoding: 'utf-8',
134
154
  stdio: ['ignore', 'pipe', 'ignore'],
135
155
  });
136
156
  return branch.stdout.trim() || 'main';
137
157
  }
138
158
  function gitConfig(projectDir, key) {
139
- const result = spawnSync('git', ['-C', projectDir, 'config', '--get', key], {
159
+ const result = boundedSpawnSync('git', ['-C', projectDir, 'config', '--get', key], 'quick', {
140
160
  encoding: 'utf-8',
141
161
  stdio: ['ignore', 'pipe', 'ignore'],
142
162
  });
@@ -321,6 +341,19 @@ export async function buildSetupProjectPlan(options) {
321
341
  }
322
342
  base.remotes = remotesConfig;
323
343
  base.delivery = delivery;
344
+ // #3050: apply an already-made worker-dispatch opt-in choice (from the
345
+ // interactive wizard), then recompute whether any surface should still
346
+ // offer/hint it — a choice just applied here always clears the offer.
347
+ // The decline is a setup/onboarding preference, not worker policy, so it
348
+ // lives in `setup`, not in the frozen `parallelism` contract fragment
349
+ // (B-OG-29).
350
+ if (options.workerDispatchChoice === 'enable') {
351
+ base.parallelism = { ...base.parallelism, worker_dispatch: true };
352
+ }
353
+ else if (options.workerDispatchChoice === 'decline') {
354
+ base.setup = { ...base.setup, worker_dispatch_offer: 'declined' };
355
+ }
356
+ const workerDispatchOffer = evaluateWorkerDispatchOffer(base.parallelism, base.setup, detectInstalledWorkerClis());
324
357
  if (primaryRemote?.provider === 'unknown' && issueProvider !== 'local') {
325
358
  warnings.push(`Remote '${primary}' is self-hosted or unknown; provider '${issueProvider}' was selected explicitly/defaulted.`);
326
359
  }
@@ -347,9 +380,10 @@ export async function buildSetupProjectPlan(options) {
347
380
  issueProvider,
348
381
  warnings,
349
382
  diff: formatPreviewDiff(existing, base),
383
+ workerDispatchOffer,
350
384
  };
351
385
  }
352
- async function applyInteractivePrompts(plan, rl, signal) {
386
+ export async function applyInteractivePrompts(plan, rl, signal, supplied = {}) {
353
387
  const remoteNames = plan.remotes.map(r => r.name);
354
388
  const current = plan.next;
355
389
  console.log('');
@@ -360,28 +394,59 @@ async function applyInteractivePrompts(plan, rl, signal) {
360
394
  }
361
395
  if (plan.remotes.length === 0)
362
396
  console.log(' (none)');
363
- const primary = remoteNames.length > 0
397
+ // #3062 review item 10: a value already supplied by flag is never
398
+ // re-asked — only the fields `supplied` leaves `undefined` prompt here.
399
+ // `??`'s right-hand side is only evaluated when the left is nullish, so
400
+ // a supplied value skips the corresponding `ask*` call entirely.
401
+ const primary = supplied.primary ?? (remoteNames.length > 0
364
402
  ? await askChoice(rl, `Primary remote [${current.remotes?.primary}]: `, remoteNames, current.remotes?.primary, signal)
365
- : await askString(rl, `Primary remote [${current.remotes?.primary}]: `, current.remotes?.primary ?? 'origin', signal);
366
- const issueProvider = await askChoice(rl, `Issue provider (${ISSUE_PROVIDERS.join('/')}): `, ISSUE_PROVIDERS, plan.issueProvider, signal);
403
+ : await askString(rl, `Primary remote [${current.remotes?.primary}]: `, current.remotes?.primary ?? 'origin', signal));
404
+ const issueProvider = supplied.issueProvider
405
+ ?? await askChoice(rl, `Issue provider (${ISSUE_PROVIDERS.join('/')}): `, ISSUE_PROVIDERS, plan.issueProvider, signal);
367
406
  const issueTracker = issueProvider === 'local'
368
407
  ? 'local'
369
- : remoteNames.length > 0
408
+ : supplied.issueTracker ?? (remoteNames.length > 0
370
409
  ? await askChoice(rl, `Issue tracker remote [${current.remotes?.issue_tracker}]: `, remoteNames, current.remotes?.issue_tracker, signal)
371
- : await askString(rl, `Issue tracker remote [${current.remotes?.issue_tracker}]: `, current.remotes?.issue_tracker ?? primary, signal);
372
- const ci = remoteNames.length > 0
410
+ : await askString(rl, `Issue tracker remote [${current.remotes?.issue_tracker}]: `, current.remotes?.issue_tracker ?? primary, signal));
411
+ const ci = supplied.ci ?? (remoteNames.length > 0
373
412
  ? await askChoice(rl, `CI remote [${current.remotes?.ci}]: `, remoteNames, current.remotes?.ci, signal)
374
- : await askString(rl, `CI remote [${current.remotes?.ci}]: `, current.remotes?.ci ?? primary, signal);
375
- const deliveryMode = await askChoice(rl, `Delivery mode (${DELIVERY_MODES.join('/')}): `, DELIVERY_MODES, current.delivery?.mode, signal);
376
- const defaultBranch = await askString(rl, `Default branch [${current.delivery?.default_branch}]: `, current.delivery?.default_branch ?? 'main', signal);
377
- const requireCiGreen = await askYesNo(rl, 'Require CI green before done? [Y/n]: ', current.delivery?.require_ci_green ?? true, signal);
378
- const autoCloseIssues = await askYesNo(rl, 'Auto-close linked issues from delivery? [Y/n]: ', current.delivery?.auto_close_issues ?? true, signal);
379
- const forcePushPolicy = await askChoice(rl, `Force-push policy (${FORCE_PUSH_POLICIES.join('/')}): `, FORCE_PUSH_POLICIES, current.delivery?.force_push_policy, signal);
380
- const requireSignedCommits = await askYesNo(rl, 'Require signed delivery commits? [y/N]: ', current.delivery?.require_signed_commits ?? false, signal);
381
- const providersRaw = await askString(rl, `Providers [${current.providers.join(',')}]: `, current.providers.join(','), signal);
413
+ : await askString(rl, `CI remote [${current.remotes?.ci}]: `, current.remotes?.ci ?? primary, signal));
414
+ const deliveryMode = supplied.deliveryMode
415
+ ?? await askChoice(rl, `Delivery mode (${DELIVERY_MODES.join('/')}): `, DELIVERY_MODES, current.delivery?.mode, signal);
416
+ const defaultBranch = supplied.defaultBranch
417
+ ?? await askString(rl, `Default branch [${current.delivery?.default_branch}]: `, current.delivery?.default_branch ?? 'main', signal);
418
+ const requireCiGreen = supplied.requireCiGreen
419
+ ?? await askYesNo(rl, 'Require CI green before done? [Y/n]: ', current.delivery?.require_ci_green ?? true, signal);
420
+ const autoCloseIssues = supplied.autoCloseIssues
421
+ ?? await askYesNo(rl, 'Auto-close linked issues from delivery? [Y/n]: ', current.delivery?.auto_close_issues ?? true, signal);
422
+ const forcePushPolicy = supplied.forcePushPolicy
423
+ ?? await askChoice(rl, `Force-push policy (${FORCE_PUSH_POLICIES.join('/')}): `, FORCE_PUSH_POLICIES, current.delivery?.force_push_policy, signal);
424
+ const requireSignedCommits = supplied.requireSignedCommits
425
+ ?? await askYesNo(rl, 'Require signed delivery commits? [y/N]: ', current.delivery?.require_signed_commits ?? false, signal);
426
+ const providers = supplied.providers
427
+ ?? parseProviders(await askString(rl, `Providers [${current.providers.join(',')}]: `, current.providers.join(','), signal), current.providers);
428
+ // #3050: offer direct worker dispatch, only when there is something to
429
+ // offer (two or more worker CLIs detected, dispatch not already on or
430
+ // declined), and only in a context where the answer could actually be
431
+ // acted on: CI and dry-run never prompt (review item 5) — an unattended
432
+ // CI run could otherwise stall on a prompt timeout, and a dry-run would
433
+ // ask a question whose answer is never written. Both cases fall back to
434
+ // the informational hint at the end of `execute()` instead (via
435
+ // `plan.workerDispatchOffer.shouldOffer` staying true on the rebuilt plan).
436
+ // Strict/distinguishable consent (review items 2 and 4): only an actually
437
+ // answered "no" is recorded as a persistent decline. An explicit
438
+ // `--worker-dispatch` already answers this directly (#3062) — skip the
439
+ // question the same way as every other supplied field.
440
+ let workerDispatchChoice = supplied.workerDispatchChoice;
441
+ if (!workerDispatchChoice && plan.workerDispatchOffer.shouldOffer && process.env.CI !== 'true' && !supplied.dryRun) {
442
+ console.log('');
443
+ const outcome = await askConsent(rl, workerDispatchOfferQuestion(plan.workerDispatchOffer.detected), undefined, signal);
444
+ if (outcome.kind === 'answered')
445
+ workerDispatchChoice = outcome.value ? 'enable' : 'decline';
446
+ }
382
447
  return {
383
448
  projectDir: plan.projectDir,
384
- providers: parseProviders(providersRaw, current.providers),
449
+ providers,
385
450
  primary,
386
451
  issueProvider,
387
452
  issueTracker,
@@ -392,6 +457,7 @@ async function applyInteractivePrompts(plan, rl, signal) {
392
457
  autoCloseIssues,
393
458
  forcePushPolicy,
394
459
  requireSignedCommits,
460
+ ...(workerDispatchChoice ? { workerDispatchChoice } : {}),
395
461
  };
396
462
  }
397
463
  function printPlan(plan) {
@@ -410,6 +476,45 @@ export const setupHandler = {
410
476
  description: 'CLI helper for agent-led repository policy and tracker setup',
411
477
  category: 'project',
412
478
  aliases: [],
479
+ async help() {
480
+ return {
481
+ exitCode: 0,
482
+ rawOutput: true,
483
+ message: `Usage: aiwg setup project [options]
484
+
485
+ Options:
486
+ --target <dir> Project directory
487
+ --yes Confirm writes without prompting
488
+ --dry-run Preview the setup plan
489
+ --non-interactive Disable prompts
490
+ --json Emit the plan as JSON
491
+ --primary <remote> Primary repository remote
492
+ --issue-tracker <remote> Internal issue tracker remote
493
+ --customer-issue-tracker <remote> Customer issue tracker remote
494
+ --ci <remote> CI remote
495
+ --issue-provider <provider> Internal tracker provider
496
+ --customer-issue-provider <provider> Customer tracker provider
497
+ --delivery-mode <mode> Delivery policy
498
+ --default-branch <branch> Default branch
499
+ --require-ci-green <boolean> Require passing CI
500
+ --auto-close-issues <boolean> Close delivered issues
501
+ --issue-comment-on-cycle <boolean> Report issue cycles
502
+ --force-push-policy <policy> Force-push policy
503
+ --require-signed-commits <boolean> Require signed commits
504
+ --committer-name <name> Committer identity
505
+ --committer-email <email> Committer email
506
+ --signing-format <format> Signing format
507
+ --signing-key <key> Signing key
508
+ --signing-enforce <commits|tags|all> Signing enforcement
509
+ --tracker-actor-login <login> Internal tracker actor
510
+ --tracker-actor-via <via> Internal tracker access method
511
+ --customer-tracker-actor-login <login> Customer tracker actor
512
+ --customer-tracker-actor-via <via> Customer tracker access method
513
+ --worker-dispatch <choice> Worker dispatch policy
514
+ --providers <ids> Comma-separated provider IDs
515
+ `,
516
+ };
517
+ },
413
518
  async execute(ctx) {
414
519
  const [subcommand] = ctx.args;
415
520
  if (subcommand && subcommand !== 'project' && !subcommand.startsWith('--')) {
@@ -420,12 +525,32 @@ export const setupHandler = {
420
525
  }
421
526
  const args = subcommand === 'project' ? ctx.args.slice(1) : ctx.args;
422
527
  const options = parseSetupProjectOptions({ ...ctx, args });
528
+ // #3062 review item 12: the one shared interactive decision (TTY, TERM,
529
+ // CI, DEBIAN_FRONTEND) — this used to check only `process.stdin.isTTY &&
530
+ // process.stdout.isTTY`, so a CI job with a pseudo-terminal could still
531
+ // reach these prompts.
532
+ const canPrompt = isInteractive(options.nonInteractive);
533
+ // #3062 second review, finding 6: refuse before any child process runs
534
+ // — `buildSetupProjectPlan()` below makes four `git` calls on its own,
535
+ // and this exact refusal used to happen only after building (and
536
+ // printing) the full plan. A write that can never be confirmed (no
537
+ // `--yes`, no terminal, not a dry run) is known from `options` alone.
538
+ if (!options.dryRun && !options.yes && !canPrompt) {
539
+ throw new AiwgError({
540
+ code: 'ERR_NON_INTERACTIVE_CONFIRMATION_REQUIRED',
541
+ message: 'Refusing to write project setup in a non-interactive context without --yes.',
542
+ hint: 'Re-run with `aiwg setup project --yes` or use `--dry-run` to preview only.',
543
+ exitCode: EXIT_CODES.USAGE,
544
+ });
545
+ }
423
546
  let plan = await buildSetupProjectPlan(options);
424
- const canPrompt = !options.nonInteractive && process.stdin.isTTY && process.stdout.isTTY;
425
547
  if (!options.yes && canPrompt) {
426
548
  const rl = createPromptInterface();
427
549
  try {
428
- const prompted = await applyInteractivePrompts(plan, rl, ctx.signal);
550
+ // #3062 review item 10: every field already supplied by flag is
551
+ // passed straight through so it is never re-asked — only genuinely
552
+ // missing ones prompt.
553
+ const prompted = await applyInteractivePrompts(plan, rl, ctx.signal, options);
429
554
  plan = await buildSetupProjectPlan({ ...options, ...prompted });
430
555
  }
431
556
  finally {
@@ -445,6 +570,19 @@ export const setupHandler = {
445
570
  exitCode: EXIT_CODES.CONFIG,
446
571
  });
447
572
  }
573
+ // #3050: non-interactive/--yes/CI/dry-run runs never prompt — the
574
+ // question above already applied a choice when it was asked (which
575
+ // clears `shouldOffer` on the rebuilt plan), so `shouldOffer` is only
576
+ // ever still true here when nothing asked. In `--json` mode the offer
577
+ // is structured data on the JSON object, never a separate
578
+ // human-readable line — a stray print after the JSON would make
579
+ // `setup project --json` unparseable (review item 6).
580
+ // #3062: an explicit `--worker-dispatch` argument clears `shouldOffer`
581
+ // the same way an answered prompt does, so `applied_choice` (alongside
582
+ // the top-level `dry_run` flag) is how `--json --dry-run` reports what
583
+ // the argument would have written — `previewBlocks()` deliberately
584
+ // excludes `parallelism`/`setup` (an unrelated, pre-existing contract),
585
+ // so this field carries that information instead of widening it.
448
586
  if (options.json) {
449
587
  console.log(JSON.stringify({
450
588
  project_dir: plan.projectDir,
@@ -453,21 +591,24 @@ export const setupHandler = {
453
591
  issue_provider: plan.issueProvider,
454
592
  warnings: plan.warnings,
455
593
  config: previewBlocks(plan.next),
594
+ worker_dispatch_offer: plan.workerDispatchOffer.shouldOffer
595
+ ? { should_offer: true, detected: plan.workerDispatchOffer.detected,
596
+ hint: workerDispatchOfferHintWithArg(plan.workerDispatchOffer.detected) }
597
+ : { should_offer: false, ...(options.workerDispatchChoice ? { applied_choice: options.workerDispatchChoice } : {}) },
456
598
  }, null, 2));
457
599
  }
458
600
  else {
459
601
  printPlan(plan);
602
+ if (options.workerDispatchChoice === 'enable')
603
+ ui.info('Next: aiwg doctor --probe-workers --qualify.');
604
+ if (plan.workerDispatchOffer.shouldOffer) {
605
+ ui.info(workerDispatchOfferHintWithArg(plan.workerDispatchOffer.detected));
606
+ }
460
607
  }
461
608
  if (options.dryRun)
462
609
  return { exitCode: 0 };
463
- if (!options.yes && !canPrompt) {
464
- throw new AiwgError({
465
- code: 'ERR_NON_INTERACTIVE_CONFIRMATION_REQUIRED',
466
- message: 'Refusing to write project setup in a non-interactive context without --yes.',
467
- hint: 'Re-run with `aiwg setup project --yes` or use `--dry-run` to preview only.',
468
- exitCode: EXIT_CODES.USAGE,
469
- });
470
- }
610
+ // The `!options.yes && !canPrompt` refusal above (review finding 6)
611
+ // already covers this exact condition before any git call ran.
471
612
  if (!options.yes && canPrompt) {
472
613
  const rl = createPromptInterface();
473
614
  try {
@@ -204,6 +204,7 @@ async function handleSteward(args, ctx) {
204
204
  aiwg steward — Provider capability awareness and command routing
205
205
 
206
206
  Usage:
207
+ aiwg steward decision-offload [--json] Offline advisory readiness and adoption report
207
208
  aiwg steward capabilities --provider <name> Capabilities for a specific provider
208
209
  aiwg steward capabilities --feature <name> Provider support matrix for a feature
209
210
  aiwg steward capabilities --all Full matrix (all providers x features)
@@ -231,6 +232,16 @@ async function handleSteward(args, ctx) {
231
232
  `);
232
233
  return;
233
234
  }
235
+ if (subcommand === 'decision-offload') {
236
+ const unknown = args.slice(1).filter(arg => arg !== '--json');
237
+ if (unknown.length)
238
+ throw new AiwgError({ code: 'ERR_USAGE_UNKNOWN_ARGUMENT',
239
+ message: `Unknown decision-offload argument: ${unknown[0]}`, exitCode: EXIT_CODES.USAGE });
240
+ const { buildDecisionOffloadSnapshot } = await import('../../decision/offload.js');
241
+ const decisionOffload = await buildDecisionOffloadSnapshot({ projectRoot: ctx?.cwd ?? process.cwd(), frameworkRoot: ctx?.frameworkRoot });
242
+ console.log(JSON.stringify(args.includes('--json') ? { decisionOffload } : decisionOffload, null, 2));
243
+ return;
244
+ }
234
245
  if (subcommand === 'permissions') {
235
246
  const operation = args[1];
236
247
  // `<namespace> --help` must reach the same usage block bare invocation prints (#2533).
@@ -21,6 +21,8 @@ import { sessionHandler } from "./session.js";
21
21
  import { feedbackHandler } from "./feedback.js";
22
22
  import { handlerResultFromError } from "../errors.js";
23
23
  import { getProjectDir } from "../../config/aiwg-config.js";
24
+ import { deployedSkillRoots } from "../../providers/deployed-skill-roots.js";
25
+ import { releaseSkillOwnership } from "../../providers/skill-ownership.mjs";
24
26
  import { formatDeployedWorkspaceSignalPlan, readWorkspaceSignalPlan } from "../workspace-signals.js";
25
27
  import * as path from "node:path";
26
28
  import * as fsSync from "node:fs";
@@ -32,6 +34,9 @@ export const quickrefHandler = {
32
34
  description: 'Generate and deploy an always-visible project quickref',
33
35
  category: 'project',
34
36
  aliases: [],
37
+ async help() {
38
+ return { exitCode: 0, message: 'Usage: aiwg quickref generate|deploy --project [--provider <id>] [--dry-run]', rawOutput: true };
39
+ },
35
40
  async execute(ctx) {
36
41
  const action = ctx.args.find(arg => !arg.startsWith('-')) ?? 'generate';
37
42
  if (!ctx.args.includes('--project')) {
@@ -147,6 +152,11 @@ export const mcpHandler = {
147
152
  description: "AIWG MCP server commands (serve, install, add, remove, update, list, inject, info)",
148
153
  category: "mcp",
149
154
  aliases: ["mcp", "aiwg-mcp"],
155
+ async help() {
156
+ const { main } = await import("../../mcp/cli.mjs");
157
+ await main(["--help"]);
158
+ return { exitCode: 0 };
159
+ },
150
160
  async execute(ctx) {
151
161
  try {
152
162
  // Dynamic import to avoid loading MCP dependencies unless needed
@@ -647,7 +657,15 @@ async function removeUserScopeDeploy(args) {
647
657
  continue;
648
658
  }
649
659
  try {
650
- await fs.rm(target, { recursive: true, force: true });
660
+ if (type === 'skills') {
661
+ const { resolveFrameworkDir } = await import('./use.js');
662
+ const released = releaseSkillOwnership(target, { frameworkSlug: resolveFrameworkDir(positional) ?? positional });
663
+ if (released === null)
664
+ continue;
665
+ }
666
+ else {
667
+ await fs.rm(target, { recursive: true, force: true });
668
+ }
651
669
  deletedHere++;
652
670
  totalDeleted++;
653
671
  }
@@ -699,7 +717,9 @@ export const removeHandler = {
699
717
  // adapter validates receipt hashes and preserves operator modifications.
700
718
  const nativeRemovalProvider = parseRemoveProvider(ctx.args);
701
719
  if (firstRemovePositional(ctx.args) === 'omp' && nativeRemovalProvider.provider === 'omp') {
702
- const { uninstall } = await import('../../../tools/agents/providers/omp.mjs');
720
+ // Loaded from the package root, not by a path relative to this file: the
721
+ // compiled handler sits one directory deeper than its source.
722
+ const { uninstall } = (await import(pathToFileURL(path.join(await getFrameworkRoot(), 'tools/agents/providers/omp.mjs')).href));
703
723
  const { getProjectDir } = await import('../../config/aiwg-config.js');
704
724
  const dryRun = ctx.args.includes('--dry-run');
705
725
  const scope = ctx.args.includes('--user') || isScopeUser(ctx.args) ? 'user' : 'project';
@@ -768,6 +788,33 @@ export const removeHandler = {
768
788
  const projectDir = getProjectDir({ cwd: ctx.cwd }, ctx.args);
769
789
  const config = await readAiwgConfig(projectDir);
770
790
  const entry = config?.installed?.[positionalArg];
791
+ if (config && entry && entry.source !== 'project-local') {
792
+ const fs = await import('node:fs/promises');
793
+ const { resolveFrameworkDir } = await import('./use.js');
794
+ const frameworkSlug = resolveFrameworkDir(positionalArg) ?? positionalArg;
795
+ const dryRun = ctx.args.includes('--dry-run');
796
+ const providers = Object.keys(entry.deployedTo).filter(p => !providerParse.provider || p === providerParse.provider);
797
+ if (providerParse.provider && providers.length === 0) {
798
+ return { exitCode: 1, message: `Error: '${positionalArg}' is not deployed for provider '${providerParse.provider}'.` };
799
+ }
800
+ let released = 0;
801
+ for (const provider of providers) {
802
+ for (const root of deployedSkillRoots(projectDir, provider)) {
803
+ for (const name of await fs.readdir(root).catch(() => [])) {
804
+ if (releaseSkillOwnership(path.join(root, name), { frameworkSlug }, { dryRun }) !== null)
805
+ released++;
806
+ }
807
+ }
808
+ if (!dryRun && !ctx.args.includes('--keep-registry'))
809
+ delete entry.deployedTo[provider];
810
+ }
811
+ if (!dryRun && !ctx.args.includes('--keep-registry')) {
812
+ if (Object.keys(entry.deployedTo).length === 0)
813
+ delete config.installed[positionalArg];
814
+ await writeAiwgConfig(projectDir, config);
815
+ }
816
+ return { exitCode: 0, message: `${dryRun ? 'Would release' : 'Released'} ${released} skill ownership receipt(s); operator files preserved.` };
817
+ }
771
818
  if (config && entry?.source === 'project-local') {
772
819
  const force = ctx.args.includes('--force');
773
820
  const dryRun = ctx.args.includes('--dry-run');
@@ -1300,12 +1347,17 @@ export const indexHandler = {
1300
1347
  description: "Artifact index commands (build, query, deps, stats)",
1301
1348
  category: "index",
1302
1349
  aliases: [],
1350
+ async help() {
1351
+ const { printIndexUsage } = await import("../../artifacts/cli.js");
1352
+ printIndexUsage();
1353
+ return { exitCode: 0 };
1354
+ },
1303
1355
  async execute(ctx) {
1304
1356
  try {
1305
1357
  const { main } = await import("../../artifacts/cli.js");
1306
1358
  await main(ctx.args);
1307
1359
  return {
1308
- exitCode: 0,
1360
+ exitCode: Number(process.exitCode ?? 0),
1309
1361
  };
1310
1362
  }
1311
1363
  catch (error) {
@@ -1324,6 +1376,11 @@ export const corpusHandler = {
1324
1376
  description: "Research-corpus tools (radar-init, radar-status, radar-report)",
1325
1377
  category: "index",
1326
1378
  aliases: [],
1379
+ async help() {
1380
+ const { corpusMain } = await import("../../artifacts/corpus-tools/cli.js");
1381
+ await corpusMain(["--help"]);
1382
+ return { exitCode: 0 };
1383
+ },
1327
1384
  async execute(ctx) {
1328
1385
  try {
1329
1386
  const { corpusMain } = await import("../../artifacts/corpus-tools/cli.js");
@@ -16,15 +16,25 @@ import { join, basename, resolve } from 'node:path';
16
16
  import { pathToFileURL } from 'node:url';
17
17
  import { projectAiwgPath } from '../../config/project-artifacts.js';
18
18
  import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
19
+ import { isSessionProviderResolution, resolveActiveProvider } from '../provider-resolution.js';
19
20
  // ── Provider Detection ───────────────────────────────────────
20
- function detectProvider(args) {
21
+ async function detectProvider(args, cwd) {
21
22
  const providerFlag = parseFlag(args, '--provider');
22
23
  if (providerFlag)
23
24
  return normalizeProviderDefinitionId(providerFlag) ?? providerFlag;
24
- // Claude Code sets CLAUDE_CODE_VERSION; API key presence is secondary heuristic
25
- const isClaudeCode = process.env.CLAUDE_CODE_VERSION !== undefined ||
26
- process.env.ANTHROPIC_API_KEY !== undefined;
27
- return isClaudeCode ? 'claude' : 'unknown';
25
+ // Resolved through the same shared provider-resolution path `runtime-info`
26
+ // and `steward capabilities` use (`resolveActiveProvider` -> the `claude`
27
+ // provider's `detection.env` markers in provider-definitions.ts) so none
28
+ // of them disagree about which provider the current session is running
29
+ // under (#2623, #2627, #3054). The ad-hoc `CLAUDE_CODE_VERSION`/
30
+ // `ANTHROPIC_API_KEY` check this replaced was stale (neither is set by
31
+ // current Claude Code) and `ANTHROPIC_API_KEY` was a false positive for
32
+ // any non-Claude session with that key set.
33
+ // Only a session signal counts. Native team dispatch needs the command to
34
+ // be running inside that harness; project configuration alone does not
35
+ // establish that.
36
+ const resolution = await resolveActiveProvider({ cwd });
37
+ return isSessionProviderResolution(resolution) ? (resolution.provider ?? 'unknown') : 'unknown';
28
38
  }
29
39
  function isNativeTeamsProvider(provider) {
30
40
  // Only Claude Code has native agent team support
@@ -154,7 +164,7 @@ async function teamRunOmp(ctx) {
154
164
  }
155
165
  }
156
166
  async function teamRun(ctx) {
157
- if (['omp', 'oh-my-pi'].includes(detectProvider(ctx.args)))
167
+ if (['omp', 'oh-my-pi'].includes(await detectProvider(ctx.args, ctx.cwd)))
158
168
  return teamRunOmp(ctx);
159
169
  const positional = getPositionalArgs(ctx.args);
160
170
  const slug = positional[0];
@@ -162,7 +172,7 @@ async function teamRun(ctx) {
162
172
  ui.error('Usage: aiwg team run <name> [--provider <p>] [--objective "<text>"]');
163
173
  return { exitCode: 1 };
164
174
  }
165
- const provider = detectProvider(ctx.args);
175
+ const provider = await detectProvider(ctx.args, ctx.cwd);
166
176
  const objective = parseFlag(ctx.args, '--objective') || `Execute ${slug} team workflow`;
167
177
  const team = await loadTeam(slug, ctx.frameworkRoot, ctx.cwd);
168
178
  if (!team) {
@@ -221,7 +231,7 @@ async function teamRun(ctx) {
221
231
  // ── Subcommand: list ─────────────────────────────────────────
222
232
  async function teamList(ctx) {
223
233
  const json = hasFlag(ctx.args, '--json');
224
- const provider = detectProvider(ctx.args);
234
+ const provider = await detectProvider(ctx.args, ctx.cwd);
225
235
  const teams = await listAllTeams(ctx.frameworkRoot, ctx.cwd);
226
236
  if (json) {
227
237
  console.log(JSON.stringify(teams.map(t => ({
@@ -76,6 +76,9 @@ export const uhpHandler = {
76
76
  description: 'Inspect and smoke-test an explicitly selected experimental UHP endpoint profile',
77
77
  category: 'toolsmith',
78
78
  aliases: [],
79
+ async help() {
80
+ return { exitCode: 0, message: usage(), rawOutput: true };
81
+ },
79
82
  async execute(ctx) {
80
83
  try {
81
84
  return await executeUhp(ctx);