@aiwg/cli 2026.10.3 → 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 (279) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/model-capabilities.v1.json +96 -4
  3. package/bin/aiwg.mjs +50 -27
  4. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  5. package/dist/src/artifacts/browser-export.js +6 -0
  6. package/dist/src/artifacts/checksum-manifest.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +173 -92
  8. package/dist/src/artifacts/citation-table-schema.js +59 -0
  9. package/dist/src/artifacts/cli.js +57 -18
  10. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  11. package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
  12. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  13. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  14. package/dist/src/artifacts/discovery-eval.js +6 -4
  15. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  16. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  17. package/dist/src/artifacts/framework-capabilities.js +171 -0
  18. package/dist/src/artifacts/framework-index-paths.js +73 -0
  19. package/dist/src/artifacts/framework-source-digest.js +75 -0
  20. package/dist/src/artifacts/index-builder.js +285 -131
  21. package/dist/src/artifacts/index-files.js +132 -18
  22. package/dist/src/artifacts/index-preparation.js +65 -0
  23. package/dist/src/artifacts/index-refresh.js +576 -0
  24. package/dist/src/artifacts/index-worker.mjs +9 -0
  25. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  26. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  27. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  28. package/dist/src/artifacts/query-engine.js +410 -88
  29. package/dist/src/artifacts/relations-builder.js +168 -0
  30. package/dist/src/artifacts/relations.js +148 -0
  31. package/dist/src/artifacts/stats.js +5 -2
  32. package/dist/src/artifacts/types.js +30 -10
  33. package/dist/src/artifacts/watcher.js +1 -1
  34. package/dist/src/auth/credential-store.js +2 -1
  35. package/dist/src/catalog/cli.d.mts +5 -0
  36. package/dist/src/channel/manager.d.mts +20 -0
  37. package/dist/src/channel/manager.mjs +19 -2
  38. package/dist/src/cli/command-log.js +2 -3
  39. package/dist/src/cli/handlers/artifacts.js +3 -0
  40. package/dist/src/cli/handlers/auth.js +3 -0
  41. package/dist/src/cli/handlers/decision.js +19 -12
  42. package/dist/src/cli/handlers/diagnose.js +4 -3
  43. package/dist/src/cli/handlers/effect.js +4 -2
  44. package/dist/src/cli/handlers/evidence.js +3 -0
  45. package/dist/src/cli/handlers/help.js +1 -0
  46. package/dist/src/cli/handlers/index.js +3 -1
  47. package/dist/src/cli/handlers/init.js +4 -1
  48. package/dist/src/cli/handlers/lint.js +5 -0
  49. package/dist/src/cli/handlers/models.js +104 -8
  50. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  51. package/dist/src/cli/handlers/refresh.js +12 -0
  52. package/dist/src/cli/handlers/resource-versions.js +3 -0
  53. package/dist/src/cli/handlers/rules.js +195 -0
  54. package/dist/src/cli/handlers/run.js +4 -0
  55. package/dist/src/cli/handlers/runtime-info.js +1 -2
  56. package/dist/src/cli/handlers/script-runner.js +3 -2
  57. package/dist/src/cli/handlers/session.js +19 -9
  58. package/dist/src/cli/handlers/sessions.js +98 -37
  59. package/dist/src/cli/handlers/setup-manifest.js +39 -44
  60. package/dist/src/cli/handlers/setup.js +41 -0
  61. package/dist/src/cli/handlers/steward.js +11 -0
  62. package/dist/src/cli/handlers/subcommands.js +57 -2
  63. package/dist/src/cli/handlers/uhp.js +3 -0
  64. package/dist/src/cli/handlers/use.js +294 -55
  65. package/dist/src/cli/handlers/utilities.js +26 -10
  66. package/dist/src/cli/handlers/version.js +8 -0
  67. package/dist/src/cli/router.js +15 -1
  68. package/dist/src/cli/services/deployment-verification.js +19 -9
  69. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  70. package/dist/src/cli/skill-usage.js +2 -3
  71. package/dist/src/config/aiwg-config.js +87 -37
  72. package/dist/src/config/cli.js +111 -5
  73. package/dist/src/config/decision-config.js +106 -0
  74. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  75. package/dist/src/config/gitignore.js +50 -0
  76. package/dist/src/config/index-graph-overrides.js +36 -0
  77. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  78. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  79. package/dist/src/config/user-config-dir.d.mts +9 -0
  80. package/dist/src/config/user-config.js +2 -1
  81. package/dist/src/config/user-registry.js +23 -15
  82. package/dist/src/config/user-state.js +7 -0
  83. package/dist/src/decision/ask.js +203 -43
  84. package/dist/src/decision/context-live-qualification.js +2 -2
  85. package/dist/src/decision/driver.js +13 -8
  86. package/dist/src/decision/ensemble/aggregate.js +23 -10
  87. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  88. package/dist/src/decision/entry.js +2 -15
  89. package/dist/src/decision/graph-live-qualification.js +1 -1
  90. package/dist/src/decision/graph-run.js +9 -0
  91. package/dist/src/decision/heldout/collector.js +5 -13
  92. package/dist/src/decision/heldout/contract.js +30 -7
  93. package/dist/src/decision/jev-credentials.js +15 -1
  94. package/dist/src/decision/ledger.js +131 -28
  95. package/dist/src/decision/offload-activation.js +137 -0
  96. package/dist/src/decision/offload.js +550 -0
  97. package/dist/src/decision/qualification/load.js +4 -6
  98. package/dist/src/decision/savings.js +28 -0
  99. package/dist/src/effects/types.js +5 -1
  100. package/dist/src/effects/verifiers/git.js +2 -1
  101. package/dist/src/effects/verifiers/tracker.js +26 -14
  102. package/dist/src/extensions/commands/definitions.js +19 -0
  103. package/dist/src/extensions/project-local-remove.js +36 -1
  104. package/dist/src/gates/providers/loader.js +61 -17
  105. package/dist/src/installation/manager-command.d.mts +18 -0
  106. package/dist/src/installation/manager-command.mjs +14 -2
  107. package/dist/src/installation/manager.d.mts +49 -0
  108. package/dist/src/installation/manager.mjs +78 -3
  109. package/dist/src/issues/cli.js +4 -4
  110. package/dist/src/issues/local.js +24 -5
  111. package/dist/src/jobs/runner.js +1 -1
  112. package/dist/src/lint/cli.js +27 -1
  113. package/dist/src/lint/loader.js +2 -1
  114. package/dist/src/lint/reporters.js +25 -1
  115. package/dist/src/lint/rule-activation.js +43 -0
  116. package/dist/src/lint/runner.js +137 -62
  117. package/dist/src/mcp/adapters/codex-runtime.js +7 -1
  118. package/dist/src/mcp/claude-install.d.mts +4 -0
  119. package/dist/src/mcp/claude-install.mjs +41 -0
  120. package/dist/src/mcp/cli.d.mts +1 -0
  121. package/dist/src/mcp/cli.mjs +36 -11
  122. package/dist/src/mcp/config-file.d.mts +28 -0
  123. package/dist/src/mcp/config-layers.d.mts +36 -0
  124. package/dist/src/mcp/credentials.d.mts +51 -0
  125. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  126. package/dist/src/mcp/grok-build-config.mjs +1 -1
  127. package/dist/src/mcp/helpers.mjs +3 -2
  128. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  129. package/dist/src/mcp/muse-native-config.mjs +1 -0
  130. package/dist/src/mcp/omp-config.d.mts +4 -0
  131. package/dist/src/mcp/registry.js +3 -93
  132. package/dist/src/mcp/registry.mjs +3 -87
  133. package/dist/src/mcp/server-config.d.mts +3 -0
  134. package/dist/src/mcp/server-config.mjs +89 -0
  135. package/dist/src/mcp/server.mjs +37 -375
  136. package/dist/src/mcp/test-simple.mjs +2 -1
  137. package/dist/src/mcp/toml-editor.d.mts +4 -0
  138. package/dist/src/mcp/toml-string.d.mts +1 -0
  139. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  140. package/dist/src/mcp/tool-filters.d.mts +46 -0
  141. package/dist/src/mcp/tools/decision.mjs +12 -11
  142. package/dist/src/mcp/tools/discovery.mjs +62 -55
  143. package/dist/src/mcp/tools/orchestration.mjs +12 -2
  144. package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
  145. package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
  146. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
  147. package/dist/src/mission-protocol/codecs.js +8 -0
  148. package/dist/src/models/config-loader.js +7 -2
  149. package/dist/src/models/model-capabilities.v1.json +96 -4
  150. package/dist/src/models/model-discovery.js +76 -11
  151. package/dist/src/models/provider-policy.js +101 -7
  152. package/dist/src/models/qualification-digest.d.mts +2 -0
  153. package/dist/src/models/resolver.js +12 -10
  154. package/dist/src/models/worker-direct-readiness.js +65 -44
  155. package/dist/src/models/worker-dispatch-offer.js +3 -3
  156. package/dist/src/models/worker-health-readiness.js +16 -3
  157. package/dist/src/models/worker-installation.d.mts +39 -0
  158. package/dist/src/models/worker-installation.mjs +135 -28
  159. package/dist/src/process/bounded-child-process.d.mts +84 -0
  160. package/dist/src/process/bounded-child-process.mjs +43 -0
  161. package/dist/src/process/cli-command.d.mts +16 -0
  162. package/dist/src/process/signal-child.d.mts +6 -0
  163. package/dist/src/process/signal-child.mjs +7 -0
  164. package/dist/src/providers/deployed-skill-roots.js +20 -0
  165. package/dist/src/providers/grok-build-acp.js +3 -2
  166. package/dist/src/providers/grok-build-headless.js +12 -4
  167. package/dist/src/providers/grok-build-inspect.js +1 -0
  168. package/dist/src/providers/omp-agent.d.mts +8 -0
  169. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  170. package/dist/src/providers/omp-paths.d.mts +13 -0
  171. package/dist/src/providers/skill-ownership.d.mts +9 -0
  172. package/dist/src/providers/skill-ownership.mjs +88 -0
  173. package/dist/src/rules/activation.d.mts +21 -0
  174. package/dist/src/rules/activation.mjs +62 -0
  175. package/dist/src/rules/inline-budget.d.mts +26 -0
  176. package/dist/src/rules/inline-budget.mjs +66 -0
  177. package/dist/src/rules/inventory.js +129 -0
  178. package/dist/src/rules/pin-state.js +73 -0
  179. package/dist/src/rules/pins.d.mts +14 -0
  180. package/dist/src/rules/pins.mjs +112 -0
  181. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  182. package/dist/src/serve/mission-conductor.js +38 -12
  183. package/dist/src/serve/worker-invocation.js +30 -10
  184. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  185. package/dist/src/sessions/adapter-registry.js +23 -19
  186. package/dist/src/sessions/adapters/claude.js +39 -54
  187. package/dist/src/sessions/adapters/codex.js +82 -67
  188. package/dist/src/sessions/adapters/copilot.js +4 -4
  189. package/dist/src/sessions/adapters/cursor.js +40 -57
  190. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  191. package/dist/src/sessions/adapters/factory.js +31 -27
  192. package/dist/src/sessions/adapters/generic.js +10 -10
  193. package/dist/src/sessions/adapters/grok-build.js +11 -11
  194. package/dist/src/sessions/adapters/grokbot.js +24 -13
  195. package/dist/src/sessions/adapters/hermes.js +27 -29
  196. package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
  197. package/dist/src/sessions/adapters/muse.js +150 -93
  198. package/dist/src/sessions/adapters/omp.js +28 -14
  199. package/dist/src/sessions/adapters/openclaw.js +89 -79
  200. package/dist/src/sessions/adapters/opencode.js +27 -24
  201. package/dist/src/sessions/adapters/openhuman.js +18 -60
  202. package/dist/src/sessions/adapters/pi.js +29 -14
  203. package/dist/src/sessions/adapters/warp.js +3 -3
  204. package/dist/src/sessions/adapters/windsurf.js +19 -48
  205. package/dist/src/sessions/batch-contracts.js +16 -8
  206. package/dist/src/sessions/batch-import.js +51 -11
  207. package/dist/src/sessions/contracts.js +40 -2
  208. package/dist/src/sessions/coverage-preflight.js +1 -1
  209. package/dist/src/sessions/export-plan.js +4 -3
  210. package/dist/src/sessions/import-exclusions.js +72 -0
  211. package/dist/src/sessions/import-lease.js +34 -18
  212. package/dist/src/sessions/import-limits.js +37 -0
  213. package/dist/src/sessions/importer.js +60 -38
  214. package/dist/src/sessions/lifecycle.js +45 -0
  215. package/dist/src/sessions/policy.js +133 -25
  216. package/dist/src/sessions/readers.js +106 -53
  217. package/dist/src/sessions/refresh.js +106 -0
  218. package/dist/src/sessions/repository.js +147 -52
  219. package/dist/src/sessions/workspace-discovery.js +221 -56
  220. package/dist/src/sessions/workspace-matching.js +107 -0
  221. package/dist/src/skills/importer.js +1 -0
  222. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  223. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  224. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  225. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  226. package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
  227. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  228. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  229. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
  230. package/dist/src/storage/backends/fs.js +18 -76
  231. package/dist/src/storage/backends/obsidian.js +1 -0
  232. package/dist/src/tracker/forge-http.js +2 -1
  233. package/dist/src/update/checker.d.mts +2 -0
  234. package/dist/src/update/service.d.mts +22 -0
  235. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  236. package/dist/src/utils/contained-filesystem.mjs +683 -0
  237. package/dist/src/utils/temporary-directory.js +36 -0
  238. package/dist/src/writing/example-generator.js +1 -1
  239. package/package.json +15 -2
  240. package/schemas/models/model-catalog.v1.schema.json +1 -1
  241. package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
  242. package/tools/_resolve-impl.mjs +13 -15
  243. package/tools/agents/deploy-agents.mjs +49 -21
  244. package/tools/agents/providers/base.mjs +104 -134
  245. package/tools/agents/providers/claude.mjs +8 -24
  246. package/tools/agents/providers/codex.mjs +18 -11
  247. package/tools/agents/providers/cursor.mjs +19 -18
  248. package/tools/agents/providers/grok-build.mjs +3 -1
  249. package/tools/agents/providers/hermes.mjs +57 -47
  250. package/tools/agents/providers/openclaw.mjs +9 -4
  251. package/tools/agents/providers/pi.mjs +5 -0
  252. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  253. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  254. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  255. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  256. package/tools/agents/providers/warp.mjs +20 -26
  257. package/tools/cli/decision-offload-report.mjs +40 -0
  258. package/tools/cli/workspace-status.mjs +862 -0
  259. package/tools/plugin/package-plugins.mjs +186 -8
  260. package/tools/plugin/plugin-links.mjs +114 -0
  261. package/tools/providers/antigravity-transport.mjs +3 -1
  262. package/tools/providers/deepseek-harness-transport.mjs +6 -4
  263. package/tools/providers/omp-transport.mjs +4 -2
  264. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  265. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  266. package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
  267. package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
  268. package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
  269. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  270. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  271. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  272. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  273. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  274. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  275. package/tools/ralph-external/process-monitor.mjs +1017 -0
  276. package/tools/ralph-external/session-launcher.mjs +5057 -0
  277. package/tools/scripts/resource-preflight.mjs +541 -0
  278. package/tools/skills/deploy-skills-codex.mjs +2 -1
  279. package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
@@ -202,8 +202,7 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
202
202
  const { RuntimeDiscovery } = await import('../../smiths/toolsmith/runtime-discovery.mjs');
203
203
  const discovery = new RuntimeDiscovery();
204
204
  if (hasDiscover) {
205
- // Full discovery
206
- const catalog = await discovery.discover();
205
+ const catalog = await discovery.discover({ force: args.includes('--force') });
207
206
  if (hasJson) {
208
207
  console.log(JSON.stringify(catalog, null, 2));
209
208
  }
@@ -8,6 +8,7 @@
8
8
  * @source @src/cli/router.ts
9
9
  * @issue #33
10
10
  */
11
+ import { signalChild } from '../../process/signal-child.mjs';
11
12
  import { spawn } from 'child_process';
12
13
  import path from 'path';
13
14
  /**
@@ -73,12 +74,12 @@ export class DefaultScriptRunner {
73
74
  // does not exit within the deadline. Both kill() calls are wrapped
74
75
  // because they can throw ESRCH on an already-exited child.
75
76
  try {
76
- child.kill('SIGTERM');
77
+ signalChild(child, 'SIGTERM');
77
78
  }
78
79
  catch { /* already exited */ }
79
80
  killEscalationId = setTimeout(() => {
80
81
  try {
81
- child.kill('SIGKILL');
82
+ signalChild(child, 'SIGKILL');
82
83
  }
83
84
  catch { /* already exited */ }
84
85
  }, killDeadlineMs);
@@ -32,10 +32,9 @@
32
32
  */
33
33
  import { spawnSync } from 'child_process';
34
34
  import { readFile } from 'node:fs/promises';
35
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
36
- import { tmpdir } from 'node:os';
35
+ import { mkdtempSync, rmSync, writeFileSync, realpathSync, statSync } from 'node:fs';
37
36
  import { join } from 'node:path';
38
- import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
37
+ import { boundedSpawn, boundedSpawnSync } from '../../process/bounded-child-process.mjs';
39
38
  import { existsSync } from 'node:fs';
40
39
  import { ensureRuntimeHome, writeProfileConfig, launchWithProfile } from '../../mcp/adapters/codex-runtime.js';
41
40
  import { getFrameworkRoot } from '../../channel/manager.mjs';
@@ -314,8 +313,8 @@ function buildHeadlessLaunch(provider, binaryArgs, prompt) {
314
313
  }
315
314
  // File transport (opencode): the prompt never touches argv or stdin — it
316
315
  // is written to a private temp file only this process can read, passed
317
- // by path. `mkdtemp()` under the system temp directory (never a custom
318
- // location — #3062 second review, finding 10) already gives the
316
+ // by path. `mkdtemp()` under the independently verified system temp
317
+ // directory gives the
319
318
  // directory itself a private, unpredictable name owned by this process;
320
319
  // the file within it additionally gets an explicit `0600` mode via an
321
320
  // exclusive create (`flag: 'wx'` — fails rather than silently
@@ -328,7 +327,15 @@ function buildHeadlessLaunch(provider, binaryArgs, prompt) {
328
327
  // `launchProvider()`, keyed off `plan.tempDir`) handles the ordinary
329
328
  // "launch ran, now clean up" path; this one exists only for the write
330
329
  // itself failing before a `plan` is ever returned to that caller.
331
- const tempDir = mkdtempSync(join(tmpdir(), 'aiwg-session-prompt-'));
330
+ // os.tmpdir() trusts TMPDIR/TEMP/TMP, which can point into the project.
331
+ // Select and validate the OS location independently of those overrides.
332
+ const systemTemp = realpathSync(process.platform === 'win32' ? join(process.env.SystemRoot ?? 'C:/Windows', 'Temp') : '/tmp');
333
+ const info = statSync(systemTemp);
334
+ if (!info.isDirectory() || (process.platform !== 'win32'
335
+ && (info.uid !== 0 && info.uid !== process.getuid?.() || (info.mode & 0o022) !== 0 && (info.mode & 0o1000) === 0))) {
336
+ throw new Error('Unsafe system temporary directory for session prompt');
337
+ }
338
+ const tempDir = mkdtempSync(join(systemTemp, 'aiwg-session-prompt-'));
332
339
  try {
333
340
  const promptPath = join(tempDir, 'prompt.txt');
334
341
  writeFileSync(promptPath, prompt, { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
@@ -355,7 +362,7 @@ function buildHeadlessLaunch(provider, binaryArgs, prompt) {
355
362
  * and without a terminal, this refuses before spawning anything that would
356
363
  * hang.
357
364
  */
358
- function launchProvider(provider, mcpInjected, mcpConfigPath, profile, settingsPath, prompt, interactive) {
365
+ async function launchProvider(provider, mcpInjected, mcpConfigPath, profile, settingsPath, prompt, interactive) {
359
366
  const cfg = getProviderConfig(provider);
360
367
  if (!isSpawnableProvider(provider)) {
361
368
  // Not spawnable — a prompt can never be delivered to a binary that does
@@ -424,11 +431,14 @@ ${cfg.guidanceMessage ?? `Open ${cfg.name} in your project directory to begin.`}
424
431
  // 8); stdin-transport providers get it via `input` (never inherited
425
432
  // from the parent), file-transport providers get it via a private
426
433
  // temp file path already baked into `plan.args`.
427
- const result = boundedSpawnSync(cfg.binary, plan.args, 'long', {
434
+ const launchOptions = {
428
435
  stdio: plan.input !== undefined ? ['pipe', 'inherit', 'inherit'] : ['ignore', 'inherit', 'inherit'],
429
436
  env: process.env,
430
437
  ...(plan.input !== undefined ? { input: plan.input } : {}),
431
- });
438
+ };
439
+ const result = plan.tempDir
440
+ ? await boundedSpawn(cfg.binary, plan.args, 'long', { ...launchOptions, stdio: ['ignore', 'inherit', 'inherit'] }, () => rmSync(plan.tempDir, { recursive: true, force: true }))
441
+ : boundedSpawnSync(cfg.binary, plan.args, 'long', launchOptions);
432
442
  const { exitCode, message } = exitCodeForSpawnResult(result, cfg.binary);
433
443
  return message ? { exitCode, message } : { exitCode };
434
444
  }
@@ -3,7 +3,10 @@ import { randomUUID } from 'node:crypto';
3
3
  import { basename, dirname, isAbsolute, join, resolve, } from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
5
  import { CLAUDE_ADAPTER_VERSION, ClaudeSessionAdapter, CODEX_ADAPTER_VERSION, CodexSessionAdapter, COPILOT_ADAPTER_VERSION, CopilotSessionAdapter, CURSOR_ADAPTER_VERSION, CursorSessionAdapter, FACTORY_ADAPTER_VERSION, FactorySessionAdapter, HERMES_ADAPTER_VERSION, HermesSessionAdapter, OPENCODE_ADAPTER_VERSION, OpenCodeSessionAdapter, OPENCLAW_ADAPTER_VERSION, OpenClawSessionAdapter, OPENHUMAN_ADAPTER_VERSION, OpenHumanSessionAdapter, GROKBOT_ADAPTER_VERSION, GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS, GrokbotSessionAdapter, GROK_BUILD_ADAPTER_VERSION, GROK_BUILD_CLI_EXPORT_LOCATOR_CLASS, GrokBuildSessionAdapter, grokBuildList, grokBuildSearch, grokBuildExport, grokHeadlessSessionIdFromFile, MUSE_ADAPTER_VERSION, MuseSessionAdapter, PI_ADAPTER_VERSION, PiSessionAdapter, DEEPSEEK_HARNESS_ADAPTER_VERSION, DeepSeekHarnessSessionAdapter, OmpSessionAdapter, OMP_ADAPTER_VERSION, WARP_ADAPTER_VERSION, WarpSessionAdapter, DEVIN_DESKTOP_ADAPTER_VERSION, DevinDesktopSessionAdapter, CandidateExtractionService, GENERIC_ADAPTER_VERSION, GenericSessionInterchangeAdapter, IncrementalSessionImporter, ImportLeaseContentionError, FilesystemMemoryDestination, FilesystemPromotionDispositionCoordinator, MemoryPromotionGateway, SESSION_CONTRACT_VERSION, SessionContractError, SessionRepository, SessionSourceSchema, StructuralCandidateExtractor, resolveMemoryConsumerManifest, assertSessionProviderId, acquireImportLease, defaultDiscoveryManifestPath, discoverWorkspaceHistories, createSessionAdapterRegistry, coveragePreflight, expectedSessionProviders, deriveSessionTimeline, importDiscoveryManifest, previewDiscoveryImport, publicDiscoveryManifest, readDiscoveryManifest, redactSourceLocator, sha256, parseTimelineGap, writeDiscoveryManifest, buildSessionExportPlan, reverifySessionExportPlan, buildSessionAiwgFortemiIndexExport, recoverAiwgFortemiIndexFromFullV1Shard, embedAttachmentsInFullV1Shard, recoverEmbeddedAttachments, SESSION_EXPORT_PLAN_SCHEMA_VERSION, } from '../../sessions/index.js';
6
+ import { resolveImportExclusions } from '../../sessions/import-exclusions.js';
7
+ import { resolveImportLimits } from '../../sessions/import-limits.js';
6
8
  import { classifyGrokbotBlobName } from '../../sessions/adapters/grokbot-locator.js';
9
+ import { readAiwgConfig } from '../../config/aiwg-config.js';
7
10
  const JSON_CONTRACT_VERSION = '1.0.0';
8
11
  async function createLineMemoryPromotionDestination(projectRoot, manifestPath) {
9
12
  const modulePath = resolve(dirname(manifestPath), 'commands', 'line-memory.mjs');
@@ -48,7 +51,7 @@ Commands:
48
51
  promote <id> <version> Preview promotion; use --confirm to write
49
52
  tag <session-id> <tag> Add a catalog tag
50
53
  relocate <source-id> <file> Update AIWG source-location metadata
51
- reindex Rebuild catalog indexes
54
+ reindex --workspace <path-or-id> | --all-workspaces Rebuild catalog indexes
52
55
  delete <session-id> Preview deletion; use --confirm to tombstone
53
56
  restore <session-id> Restore a reversible catalog tombstone
54
57
  purge <session-id> Preview terminal AIWG-copy purge
@@ -60,6 +63,7 @@ Commands:
60
63
  Options:
61
64
  --json Emit the versioned JSON contract
62
65
  --dry-run Preview a mutation without changing state
66
+ --all-workspaces Reindex every workspace in the selected catalog (reindex only)
63
67
  --db <path> Select a catalog; relative paths resolve from cwd
64
68
  --workspace <path-or-id> Select the workspace within that catalog for reads
65
69
  (default: current project, matching cwd, or sole catalog workspace)
@@ -69,7 +73,7 @@ Options:
69
73
  --expected-providers <ids> Comma-separated authorized scope, independent of runtime inventory
70
74
  --manifest <path> Override the discovery manifest path
71
75
  --provider-home <path> Override the provider home root (testing/portable homes)
72
- --codex-root <path> Explicitly authorize a shared Codex sessions/export root
76
+ --codex-root <path> Authorize a shared Codex sessions/export root (repeatable)
73
77
  --omp-root <path> Explicitly authorize an OMP profile sessions root
74
78
  --muse-root <path> Explicitly authorize a Muse Code sessions root
75
79
  --dsh-root <path> Explicitly authorize a DeepSeek Harness sessions root
@@ -78,6 +82,10 @@ Options:
78
82
  (default: grok on PATH, then $GROK_HOME/bin/grok)
79
83
  --confirm, --yes Confirm a persistent discovered batch import
80
84
  --lock-wait-ms <n> Maximum import-lease wait (default 5000)
85
+ --exclude <id-or-glob> Skip a session ID or absolute path glob (repeatable); adds to sessions.import.exclude and AIWG_SESSIONS_IMPORT_EXCLUDE
86
+ --max-record-bytes <n> Import record limit (default 67108864); overrides AIWG_SESSIONS_IMPORT_MAX_RECORD_BYTES and sessions.import.max_record_bytes
87
+ --max-source-bytes <n> Import source limit (default 1073741824, 1 GiB; 0 unlimited); overrides AIWG_SESSIONS_IMPORT_MAX_SOURCE_BYTES and sessions.import.max_source_bytes
88
+ Import limit precedence: CLI flag > env var > workspace config > default.
81
89
  --inactivity-threshold <duration> Historical inactivity threshold (default 24h)
82
90
  --min-coverage <0..1> Fail list/report commands below a coverage ratio
83
91
  --provider <id> Filter list or select an import adapter
@@ -110,9 +118,9 @@ Analytics / forensics:
110
118
  --group-by tool|session|provider, --limit <1..5000>
111
119
  --authorize-forensics Required for each authorized forensic invocation
112
120
  --markdown Render a sanitized forensic timeline table`;
113
- function printHelp(ctx, exitCode = EXIT.ok) {
121
+ async function printHelp(ctx, exitCode = EXIT.ok) {
114
122
  if (ctx.args.includes('--json'))
115
- emit(envelope('sessions.help', 'ok', { usage: HELP }, null));
123
+ await emit(envelope('sessions.help', 'ok', { usage: HELP }, null));
116
124
  else
117
125
  console.log(HELP);
118
126
  return { exitCode };
@@ -136,7 +144,7 @@ export const sessionsHandler = {
136
144
  const normalized = normalizeError(error);
137
145
  const output = envelope('sessions', 'error', null, normalized.error);
138
146
  if (json)
139
- emit(output);
147
+ await emit(output);
140
148
  else
141
149
  console.error(`${normalized.error.code}: ${normalized.error.message}`);
142
150
  return { exitCode: normalized.exitCode, message: normalized.error.message };
@@ -148,7 +156,7 @@ export const sessionsHandler = {
148
156
  try {
149
157
  const result = await executeCommand(ctx, parsed);
150
158
  if (json)
151
- emit(result.envelope);
159
+ await emit(result.envelope);
152
160
  else
153
161
  printHuman(result.envelope);
154
162
  return { exitCode: result.exitCode };
@@ -157,7 +165,7 @@ export const sessionsHandler = {
157
165
  const normalized = normalizeError(error);
158
166
  const output = envelope(`sessions.${parsed.command}`, 'error', null, normalized.error);
159
167
  if (json)
160
- emit(output);
168
+ await emit(output);
161
169
  else
162
170
  console.error(`${normalized.error.code}: ${normalized.error.message}`);
163
171
  return { exitCode: normalized.exitCode, message: normalized.error.message };
@@ -590,11 +598,22 @@ async function executeCommand(ctx, args) {
590
598
  return ok(command, { sourceId, redactedLocator });
591
599
  }
592
600
  case 'reindex': {
593
- const { workspaceId } = authorizationContext(ctx, args, command);
601
+ const allWorkspaces = args.flags.has('--all-workspaces');
602
+ if (allWorkspaces && args.values.has('--workspace')) {
603
+ throw new CliError('INVALID_ARGUMENT', '--all-workspaces cannot be combined with --workspace', EXIT.usage);
604
+ }
605
+ const workspaceId = allWorkspaces
606
+ ? undefined
607
+ : authorizationContext(ctx, args, command).workspaceId;
608
+ const data = {
609
+ operation: 'reindex',
610
+ allWorkspaces,
611
+ workspaceIds: allWorkspaces ? repository.listWorkspaceIds() : [workspaceId],
612
+ };
594
613
  if (isDryRun(ctx, args))
595
- return preview(command, { operation: 'reindex' });
614
+ return preview(command, data);
596
615
  repository.reindex(workspaceId);
597
- return ok(command, { operation: 'reindex' });
616
+ return ok(command, data);
598
617
  }
599
618
  case 'audit': {
600
619
  const { workspaceId } = readAuthorizationContext(ctx, args, command, repository);
@@ -721,6 +740,7 @@ async function executeCommand(ctx, args) {
721
740
  }
722
741
  }
723
742
  async function importSource(ctx, args) {
743
+ const limits = await configuredImportLimits(ctx, args);
724
744
  const input = resolve(ctx.cwd, requiredPositional(args, 0, 'file'));
725
745
  const providerInput = args.values.get('--provider') ?? 'generic';
726
746
  const provider = assertSessionProviderId(providerInput);
@@ -753,37 +773,37 @@ async function importSource(ctx, args) {
753
773
  const isDsh = provider === 'deepseek-harness';
754
774
  const isWarp = provider === 'warp';
755
775
  const isDevinDesktop = provider === 'devin-desktop';
756
- const adapter = isDsh ? new DeepSeekHarnessSessionAdapter() : isOmp ? new OmpSessionAdapter() : isClaude
757
- ? new ClaudeSessionAdapter()
776
+ const adapter = isDsh ? new DeepSeekHarnessSessionAdapter(limits) : isOmp ? new OmpSessionAdapter(limits) : isClaude
777
+ ? new ClaudeSessionAdapter(limits)
758
778
  : isCodex
759
- ? new CodexSessionAdapter()
779
+ ? new CodexSessionAdapter(limits)
760
780
  : isCopilot
761
- ? new CopilotSessionAdapter()
781
+ ? new CopilotSessionAdapter(limits)
762
782
  : isCursor
763
- ? new CursorSessionAdapter()
783
+ ? new CursorSessionAdapter(limits)
764
784
  : isFactory
765
- ? new FactorySessionAdapter()
785
+ ? new FactorySessionAdapter(limits)
766
786
  : isHermes
767
- ? new HermesSessionAdapter()
787
+ ? new HermesSessionAdapter(limits)
768
788
  : isOpenCode
769
- ? new OpenCodeSessionAdapter()
789
+ ? new OpenCodeSessionAdapter(limits)
770
790
  : isOpenClaw
771
- ? new OpenClawSessionAdapter()
791
+ ? new OpenClawSessionAdapter(limits)
772
792
  : isOpenHuman
773
- ? new OpenHumanSessionAdapter()
793
+ ? new OpenHumanSessionAdapter(limits)
774
794
  : isGrokbot
775
- ? new GrokbotSessionAdapter()
795
+ ? new GrokbotSessionAdapter(limits)
776
796
  : isGrokBuild
777
- ? new GrokBuildSessionAdapter()
797
+ ? new GrokBuildSessionAdapter(limits)
778
798
  : isMuse
779
- ? new MuseSessionAdapter()
799
+ ? new MuseSessionAdapter(limits)
780
800
  : isPi
781
- ? new PiSessionAdapter()
801
+ ? new PiSessionAdapter(limits)
782
802
  : isWarp
783
- ? new WarpSessionAdapter()
803
+ ? new WarpSessionAdapter(limits)
784
804
  : isDevinDesktop
785
- ? new DevinDesktopSessionAdapter()
786
- : new GenericSessionInterchangeAdapter();
805
+ ? new DevinDesktopSessionAdapter(limits)
806
+ : new GenericSessionInterchangeAdapter(limits);
787
807
  const locatorClass = isDsh ? 'deepseek-harness-session-v2-jsonl' : isOmp ? 'omp-session-v3-jsonl' : isClaude
788
808
  ? claudeLocatorClass(input)
789
809
  : isCodex
@@ -932,8 +952,9 @@ async function importSource(ctx, args) {
932
952
  try {
933
953
  const repository = openRepository(ctx, args);
934
954
  try {
955
+ const interruptedRuns = repository.interruptRunningBatchImports();
935
956
  const receipts = await new IncrementalSessionImporter(repository).import({
936
- source, selectedSource, adapter, workspaceId, policyVersion: '1.0.0',
957
+ source, selectedSource, adapter, workspaceId, policyVersion: '1.0.0', limits,
937
958
  inactivityThresholdMs: inactivityThreshold(args.values.get('--inactivity-threshold')),
938
959
  });
939
960
  for (const warning of workspaceReport.warnings)
@@ -942,6 +963,7 @@ async function importSource(ctx, args) {
942
963
  sourceId,
943
964
  ...workspaceReport,
944
965
  receipts,
966
+ interruptedRuns,
945
967
  totals: {
946
968
  sessionsInserted: receipts.reduce((sum, item) => sum + item.sessionsInserted, 0),
947
969
  eventsInserted: receipts.reduce((sum, item) => sum + item.eventsInserted, 0),
@@ -1234,8 +1256,15 @@ async function exportUnpack(ctx, args, command) {
1234
1256
  }
1235
1257
  async function discoverWorkspace(ctx, args) {
1236
1258
  const workspace = resolve(ctx.cwd, requiredValue(args, '--workspace'));
1259
+ const config = await readAiwgConfig(workspace);
1260
+ const canonicalWorkspace = realpathSync(workspace);
1261
+ const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(canonicalWorkspace));
1262
+ const exclude = resolveImportExclusions(config?.sessions?.import, args.exclude);
1263
+ const previous = exclude.length > 0 && existsSync(manifestPath) ? await readDiscoveryManifest(manifestPath) : undefined;
1237
1264
  const manifest = await discoverWorkspaceHistories({
1238
1265
  workspace,
1266
+ knownSources: previous?.workspaceId === canonicalWorkspace ? previous.sources : undefined,
1267
+ exclude,
1239
1268
  expectedProviders: requestedProviders(args),
1240
1269
  providerHome: args.values.has('--provider-home')
1241
1270
  ? resolve(ctx.cwd, args.values.get('--provider-home'))
@@ -1248,11 +1277,11 @@ async function discoverWorkspace(ctx, args) {
1248
1277
  ? resolve(ctx.cwd, args.values.get('--dsh-root')) : undefined,
1249
1278
  grokbotRoot: args.values.has('--grokbot-root')
1250
1279
  ? resolve(ctx.cwd, args.values.get('--grokbot-root')) : undefined,
1251
- codexRoot: args.values.has('--codex-root')
1252
- ? resolve(ctx.cwd, args.values.get('--codex-root'))
1253
- : undefined,
1280
+ codexRoots: [
1281
+ ...(config?.sessions?.codex_roots ?? []).map((root) => resolve(workspace, root)),
1282
+ ...args.codexRoots.map((root) => resolve(ctx.cwd, root)),
1283
+ ],
1254
1284
  });
1255
- const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(manifest.workspacePath));
1256
1285
  const data = {
1257
1286
  manifest: publicDiscoveryManifest(manifest),
1258
1287
  manifestPath: redactSourceLocator(manifestPath),
@@ -1267,6 +1296,9 @@ async function discoverWorkspace(ctx, args) {
1267
1296
  async function importDiscovered(ctx, args) {
1268
1297
  const workspace = resolve(ctx.cwd, requiredValue(args, '--workspace'));
1269
1298
  const canonicalWorkspace = realpathSync(workspace);
1299
+ const limits = await configuredImportLimits(ctx, args, canonicalWorkspace);
1300
+ const config = await readAiwgConfig(canonicalWorkspace);
1301
+ const exclude = resolveImportExclusions(config?.sessions?.import, args.exclude);
1270
1302
  const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(canonicalWorkspace));
1271
1303
  const manifest = await readDiscoveryManifest(manifestPath);
1272
1304
  if (manifest.workspaceId !== canonicalWorkspace) {
@@ -1289,7 +1321,7 @@ async function importDiscovered(ctx, args) {
1289
1321
  }
1290
1322
  return preview('import-discovered', {
1291
1323
  manifest: publicDiscoveryManifest(manifest),
1292
- receipt: previewDiscoveryImport(manifest, existing),
1324
+ receipt: previewDiscoveryImport(manifest, existing, new Date(), exclude),
1293
1325
  confirmationRequired: !isDryRun(ctx, args),
1294
1326
  wouldPersist: false,
1295
1327
  });
@@ -1300,12 +1332,16 @@ async function importDiscovered(ctx, args) {
1300
1332
  try {
1301
1333
  const repository = openRepository(ctx, args);
1302
1334
  try {
1335
+ const interruptedRuns = repository.interruptRunningBatchImports();
1303
1336
  const receipt = await importDiscoveryManifest({
1304
1337
  manifest,
1305
1338
  repository,
1339
+ limits,
1340
+ exclude,
1306
1341
  signal: ctx.signal,
1307
1342
  inactivityThresholdMs: inactivityThreshold(args.values.get('--inactivity-threshold')),
1308
1343
  });
1344
+ receipt.interruptedRuns = interruptedRuns;
1309
1345
  return ok('import-discovered', {
1310
1346
  manifestId: manifest.manifestId,
1311
1347
  receipt,
@@ -1648,9 +1684,25 @@ function catalogScope(ctx, args, repository, workspaceId) {
1648
1684
  : null,
1649
1685
  };
1650
1686
  }
1687
+ async function configuredImportLimits(ctx, args, workspace) {
1688
+ const selectedWorkspace = workspace ?? (args.values.has('--workspace')
1689
+ ? resolve(ctx.cwd, args.values.get('--workspace')) : ctx.cwd);
1690
+ try {
1691
+ const config = await readAiwgConfig(selectedWorkspace);
1692
+ return resolveImportLimits(config?.sessions?.import, {
1693
+ maxRecordBytes: args.values.get('--max-record-bytes'),
1694
+ maxSourceBytes: args.values.get('--max-source-bytes'),
1695
+ });
1696
+ }
1697
+ catch (error) {
1698
+ throw new CliError('INVALID_ARGUMENT', error instanceof Error ? error.message : String(error), EXIT.usage);
1699
+ }
1700
+ }
1651
1701
  function parseArgs(argv) {
1652
1702
  const flags = new Set();
1653
1703
  const values = new Map();
1704
+ const codexRoots = [];
1705
+ const exclude = [];
1654
1706
  const positionals = [];
1655
1707
  const valueFlags = new Set([
1656
1708
  '--db', '--provider', '--workspace', '--tag', '--limit', '--cursor', '--source-id',
@@ -1659,7 +1711,7 @@ function parseArgs(argv) {
1659
1711
  '--state', '--reviewer', '--reason', '--policy-version', '--min-confidence',
1660
1712
  '--consumer', '--actor-class', '--reason-code', '--dependent-action', '--basis',
1661
1713
  '--manifest', '--provider-home', '--codex-root', '--omp-root', '--muse-root', '--dsh-root', '--grokbot-root', '--lock-wait-ms', '--min-coverage', '--gap',
1662
- '--inactivity-threshold', '--expected-providers',
1714
+ '--inactivity-threshold', '--expected-providers', '--exclude', '--max-record-bytes', '--max-source-bytes',
1663
1715
  '--control-events',
1664
1716
  '--session', '--status', '--actor', '--group-by',
1665
1717
  '--out', '--plan', '--input', '--shard-name', '--max-attachment-bytes', '--format', '--grok-bin',
@@ -1673,6 +1725,10 @@ function parseArgs(argv) {
1673
1725
  throw new CliError('MISSING_OPTION_VALUE', `missing value for ${arg}`, EXIT.usage);
1674
1726
  }
1675
1727
  values.set(arg, value);
1728
+ if (arg === '--codex-root')
1729
+ codexRoots.push(value);
1730
+ if (arg === '--exclude')
1731
+ exclude.push(value);
1676
1732
  index += 1;
1677
1733
  }
1678
1734
  else if (arg.startsWith('-')) {
@@ -1685,7 +1741,7 @@ function parseArgs(argv) {
1685
1741
  positionals.push(arg);
1686
1742
  }
1687
1743
  }
1688
- return { command, positionals, flags, values };
1744
+ return { command, positionals, flags, values, codexRoots, exclude };
1689
1745
  }
1690
1746
  function requestedProviders(args) {
1691
1747
  const value = args.values.get('--expected-providers');
@@ -1961,8 +2017,13 @@ function preview(command, data) {
1961
2017
  function envelope(command, status, data, error) {
1962
2018
  return { contractVersion: JSON_CONTRACT_VERSION, command, status, data, error };
1963
2019
  }
1964
- function emit(value) {
2020
+ async function emit(value) {
1965
2021
  console.log(JSON.stringify(value, null, 2));
2022
+ // The launcher and router may force exit after the handler returns. A write
2023
+ // callback waits for the JSON already queued on stdout, including on pipes.
2024
+ await new Promise((resolve, reject) => {
2025
+ process.stdout.write('', (error) => error ? reject(error) : resolve());
2026
+ });
1966
2027
  }
1967
2028
  function printHuman(value) {
1968
2029
  if (value.status === 'preview')
@@ -2015,7 +2076,7 @@ function normalizeError(error) {
2015
2076
  }
2016
2077
  if (error instanceof SessionContractError) {
2017
2078
  const exitCode = error.code === 'UNSUPPORTED_OPERATION' ? EXIT.unsupported : EXIT.contract;
2018
- return { error: { code: error.code, message: error.message }, exitCode };
2079
+ return { error: { code: error.code, message: error.message, ...(error.detail ? { details: error.detail } : {}) }, exitCode };
2019
2080
  }
2020
2081
  if (error instanceof ImportLeaseContentionError) {
2021
2082
  return {
@@ -8,7 +8,7 @@ import { isInteractive } from '../env.js';
8
8
  import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
9
9
  import { sha256 } from '../../security/artifact-trust.js';
10
10
  import { artifactVerifyHandler } from './artifact-verify.js';
11
- import { requireFlagValue, MissingFlagValueError } from '../flag-parsing.js';
11
+ import { requireFlagValue, MissingFlagValueError, collectFlagValues, hasPositionalOrSwitch, argsTerminatorIndex, valueTokenIndexes } from '../flag-parsing.js';
12
12
  import { exitCodeForSpawnResult } from '../child-process-result.js';
13
13
  import { askString, createPromptInterface } from '../prompt-utils.js';
14
14
  const SETUP_SCHEMA_REL = 'agentic/code/addons/agentic-installer/schemas/v1/setup-manifest.schema.json';
@@ -76,9 +76,8 @@ Safety:
76
76
  Headless input steps (#3062):
77
77
  A 'type: ask' step resolves its declared 'params' the same way a 'script'
78
78
  step's env vars do (--param/--params-file/env/default); if every declared
79
- param is already resolved it runs headless, otherwise it is refused,
80
- naming the missing param names — this CLI has no interactive-capture
81
- mechanism for 'ask' steps, with or without a terminal. A step marked
79
+ param is already resolved it runs headless; otherwise a terminal prompts
80
+ for missing values. Without a terminal it refuses and names the missing params. A step marked
82
81
  'interactive: true' runs an arbitrary script whose questions cannot be
83
82
  enumerated; without a terminal it is refused outright. Every selected
84
83
  step (including nested 'platform-route' steps) is preflighted for both
@@ -96,40 +95,17 @@ Headless input steps (#3062):
96
95
  function flagValue(args, name) {
97
96
  return requireFlagValue(args, {}, name);
98
97
  }
98
+ const VALUE_FLAGS = [
99
+ '--manifest', '--schema', '--platform', '--distro', '--params-file', '--param', '--set',
100
+ '--step', '--skip', '--type', '--output', '--name', '--attestation', '--policy', '--state',
101
+ '--asset-type', '--namespace', '--channel',
102
+ ];
99
103
  function hasFlag(args, ...names) {
100
- return args.some((arg) => names.includes(arg));
104
+ return names.some(name => hasPositionalOrSwitch(args, name, VALUE_FLAGS));
101
105
  }
102
106
  function positionalManifest(args) {
103
- const valueFlags = new Set([
104
- '--manifest',
105
- '--schema',
106
- '--platform',
107
- '--distro',
108
- '--params-file',
109
- '--param',
110
- '--set',
111
- '--step',
112
- '--skip',
113
- '--type',
114
- '--output',
115
- '--name',
116
- '--attestation',
117
- '--policy',
118
- '--state',
119
- '--asset-type',
120
- '--namespace',
121
- '--channel',
122
- ]);
123
- for (let i = 0; i < args.length; i += 1) {
124
- const arg = args[i];
125
- if (valueFlags.has(arg)) {
126
- i += 1;
127
- continue;
128
- }
129
- if (!arg.startsWith('-'))
130
- return arg;
131
- }
132
- return undefined;
107
+ const consumed = valueTokenIndexes(args, VALUE_FLAGS);
108
+ return args.slice(0, argsTerminatorIndex(args)).find((arg, index) => !consumed.has(index) && !arg.startsWith('-'));
133
109
  }
134
110
  function resolveSchemaPath(frameworkRoot, cwd, override) {
135
111
  if (override)
@@ -472,15 +448,12 @@ function renderValidationText(result) {
472
448
  }
473
449
  function parseParamValues(args) {
474
450
  const values = {};
475
- for (let i = 0; i < args.length; i += 1) {
476
- if (args[i] !== '--param' && args[i] !== '--set')
477
- continue;
478
- const raw = args[i + 1] ?? '';
451
+ for (const raw of collectFlagValues(args, {}, '--param', '--set')) {
479
452
  const eq = raw.indexOf('=');
480
- if (eq <= 0)
481
- throw new Error(`${args[i]} requires KEY=VALUE`);
453
+ if (eq <= 0 || !raw.slice(0, eq).trim())
454
+ throw new MissingFlagValueError('--param KEY=VALUE');
455
+ // An empty parameter value is supported (e.g. explicitly clearing a default).
482
456
  values[raw.slice(0, eq)] = raw.slice(eq + 1);
483
- i += 1;
484
457
  }
485
458
  return values;
486
459
  }
@@ -1068,10 +1041,21 @@ export async function runSetupManifest(options) {
1068
1041
  return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };
1069
1042
  }
1070
1043
  const params = resolveParams(manifest, options);
1071
- if (params.missing.length) {
1044
+ const steps = selectedSteps(manifest, options);
1045
+ const askSteps = [];
1046
+ const collectAsks = (selected) => {
1047
+ for (const step of selected) {
1048
+ if (step.type === 'ask')
1049
+ askSteps.push(step);
1050
+ if (step.type === 'platform-route')
1051
+ collectAsks(step.routes?.find(route => route.platform === target.os)?.steps ?? []);
1052
+ }
1053
+ };
1054
+ collectAsks(steps);
1055
+ const promptable = new Set(isInteractive() ? askSteps.flatMap(askStepParams) : []);
1056
+ if (params.missing.some(name => !promptable.has(name))) {
1072
1057
  return { exitCode: 1, message: `setup-run: required params missing before execution: ${params.missing.join(', ')}` };
1073
1058
  }
1074
- const steps = selectedSteps(manifest, options);
1075
1059
  if (options.step && steps.length === 0)
1076
1060
  return { exitCode: 1, message: `setup-run: step '${options.step}' not found` };
1077
1061
  if (options.dryRun) {
@@ -1095,6 +1079,17 @@ export async function runSetupManifest(options) {
1095
1079
  message: `setup-run: refusing before executing any step:\n${blockers.map((b) => ` - ${b}`).join('\n')}`,
1096
1080
  };
1097
1081
  }
1082
+ // Resolve required ask inputs before any scripts can consume them.
1083
+ for (const step of askSteps) {
1084
+ const missing = missingAskParams(step, params.values).filter(name => params.missing.includes(name));
1085
+ if (!missing.length)
1086
+ continue;
1087
+ Object.assign(params.values, await promptForAskStep(step, missing));
1088
+ }
1089
+ const checked = resolveParams(manifest, { ...options, paramValues: { ...options.paramValues, ...params.values } });
1090
+ if (checked.missing.length)
1091
+ return { exitCode: 1, message: `setup-run: required params missing before execution: ${checked.missing.join(', ')}` };
1092
+ Object.assign(params.values, checked.values);
1098
1093
  for (const step of steps) {
1099
1094
  const result = await runStep(step, manifest, validation.manifestDir, params.values, target.os, options.yes);
1100
1095
  if (!result.ok) {
@@ -476,6 +476,45 @@ export const setupHandler = {
476
476
  description: 'CLI helper for agent-led repository policy and tracker setup',
477
477
  category: 'project',
478
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
+ },
479
518
  async execute(ctx) {
480
519
  const [subcommand] = ctx.args;
481
520
  if (subcommand && subcommand !== 'project' && !subcommand.startsWith('--')) {
@@ -560,6 +599,8 @@ export const setupHandler = {
560
599
  }
561
600
  else {
562
601
  printPlan(plan);
602
+ if (options.workerDispatchChoice === 'enable')
603
+ ui.info('Next: aiwg doctor --probe-workers --qualify.');
563
604
  if (plan.workerDispatchOffer.shouldOffer) {
564
605
  ui.info(workerDispatchOfferHintWithArg(plan.workerDispatchOffer.detected));
565
606
  }
@@ -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).