@aiwg/cli 2026.10.5 → 2026.10.7

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 (263) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +7 -3
  6. package/dist/src/a2a/agent-card.js +11 -0
  7. package/dist/src/api/index.d.ts +1 -0
  8. package/dist/src/api/index.js +1 -0
  9. package/dist/src/artifacts/browser-export.js +1 -0
  10. package/dist/src/artifacts/citation-parser.js +8 -1
  11. package/dist/src/artifacts/cli.js +57 -6
  12. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  13. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  14. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  15. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  16. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  17. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  18. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  19. package/dist/src/artifacts/graph-query.js +18 -8
  20. package/dist/src/artifacts/index-adapters.js +339 -0
  21. package/dist/src/artifacts/index-builder.js +143 -14
  22. package/dist/src/artifacts/index-files.js +15 -4
  23. package/dist/src/artifacts/index-preparation.js +2 -0
  24. package/dist/src/artifacts/index-reader.js +44 -0
  25. package/dist/src/artifacts/index-refresh.js +114 -55
  26. package/dist/src/artifacts/index-status.js +20 -5
  27. package/dist/src/artifacts/memory-maintenance.js +503 -0
  28. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  29. package/dist/src/artifacts/query-engine.js +263 -108
  30. package/dist/src/artifacts/relations-builder.js +2 -2
  31. package/dist/src/artifacts/source-graph.js +2 -2
  32. package/dist/src/artifacts/types.js +9 -1
  33. package/dist/src/auth/client.js +5 -1
  34. package/dist/src/auth/credential-store.js +5 -1
  35. package/dist/src/auth/sensitive-input.js +151 -0
  36. package/dist/src/channel/manager.mjs +3 -0
  37. package/dist/src/cli/handlers/agentcard.js +47 -25
  38. package/dist/src/cli/handlers/auth.js +22 -2
  39. package/dist/src/cli/handlers/decision.js +22 -3
  40. package/dist/src/cli/handlers/effect.js +4 -84
  41. package/dist/src/cli/handlers/guided-setup.js +470 -0
  42. package/dist/src/cli/handlers/help.js +2 -2
  43. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  44. package/dist/src/cli/handlers/index.js +4 -2
  45. package/dist/src/cli/handlers/init.js +41 -6
  46. package/dist/src/cli/handlers/install.js +6 -0
  47. package/dist/src/cli/handlers/local-executor.js +5 -3
  48. package/dist/src/cli/handlers/marketplace.js +15 -4
  49. package/dist/src/cli/handlers/mc.js +509 -53
  50. package/dist/src/cli/handlers/models.js +32 -2
  51. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  52. package/dist/src/cli/handlers/rules.js +1 -1
  53. package/dist/src/cli/handlers/script-runner.js +73 -2
  54. package/dist/src/cli/handlers/serve.js +5 -3
  55. package/dist/src/cli/handlers/sessions.js +1 -0
  56. package/dist/src/cli/handlers/setup.js +432 -1
  57. package/dist/src/cli/handlers/skill-lint.js +1 -1
  58. package/dist/src/cli/handlers/subcommands.js +118 -8
  59. package/dist/src/cli/handlers/use.js +335 -107
  60. package/dist/src/cli/handlers/workspace.js +3 -1
  61. package/dist/src/cli/prompt-utils.js +81 -0
  62. package/dist/src/cli/router.js +16 -1
  63. package/dist/src/config/aiwg-config.js +42 -0
  64. package/dist/src/config/cli.js +7 -3
  65. package/dist/src/config/decision-config.js +14 -1
  66. package/dist/src/config/gitignore.js +4 -0
  67. package/dist/src/config/native-sandbox-warnings.js +25 -0
  68. package/dist/src/config/workspace.js +2 -1
  69. package/dist/src/contributors/validation.js +6 -5
  70. package/dist/src/decision/adapters/jev.js +20 -31
  71. package/dist/src/decision/admission.js +7 -3
  72. package/dist/src/decision/ask.js +6 -1
  73. package/dist/src/decision/benchmark.js +482 -0
  74. package/dist/src/decision/ledger.js +22 -3
  75. package/dist/src/decision/pricing.js +147 -0
  76. package/dist/src/decision/qualification/gate-evidence.js +23 -18
  77. package/dist/src/decision/qualification/load-staged.js +576 -0
  78. package/dist/src/decision/qualification/load.js +109 -33
  79. package/dist/src/decision/qualification/runner.js +12 -5
  80. package/dist/src/decision/review/service.js +6 -4
  81. package/dist/src/decision/review/validate.js +23 -2
  82. package/dist/src/decision/savings.js +8 -3
  83. package/dist/src/discovery/index.js +492 -0
  84. package/dist/src/discovery/interview.js +238 -0
  85. package/dist/src/discovery/plan-apply.js +567 -0
  86. package/dist/src/discovery/plan-store.js +171 -0
  87. package/dist/src/discovery/plan-types.js +2 -0
  88. package/dist/src/discovery/plan-validation.js +139 -0
  89. package/dist/src/discovery/types.js +2 -0
  90. package/dist/src/effects/host.js +91 -0
  91. package/dist/src/extensions/commands/definitions.js +49 -9
  92. package/dist/src/extensions/manifest.js +71 -44
  93. package/dist/src/extensions/project-local-discovery.js +1 -1
  94. package/dist/src/extensions/project-local-gitignore.js +2 -0
  95. package/dist/src/extensions/project-quickref.js +18 -17
  96. package/dist/src/extensions/validation.js +32 -29
  97. package/dist/src/housekeeper/daemon-config.js +148 -0
  98. package/dist/src/housekeeper/ipc.js +84 -0
  99. package/dist/src/housekeeper/lifecycle.js +148 -0
  100. package/dist/src/housekeeper/platform.js +35 -0
  101. package/dist/src/housekeeper/status.js +70 -0
  102. package/dist/src/housekeeper/units.js +114 -0
  103. package/dist/src/issues/cli.js +16 -5
  104. package/dist/src/issues/live.js +44 -12
  105. package/dist/src/jobs/flow.js +22 -21
  106. package/dist/src/lint/rule-activation.js +20 -5
  107. package/dist/src/lint/runner.js +1 -1
  108. package/dist/src/marketplace/exchange.js +18 -6
  109. package/dist/src/marketplace/provenance.js +3 -2
  110. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  111. package/dist/src/mcp/cli.mjs +22 -96
  112. package/dist/src/mcp/config-file.mjs +6 -2
  113. package/dist/src/mcp/config-layers.mjs +23 -7
  114. package/dist/src/mcp/grok-build-config.mjs +2 -1
  115. package/dist/src/mcp/muse-native-config.mjs +2 -1
  116. package/dist/src/mcp/profiles.js +18 -31
  117. package/dist/src/mcp/profiles.mjs +18 -32
  118. package/dist/src/mcp/registry.js +7 -27
  119. package/dist/src/mcp/registry.mjs +7 -28
  120. package/dist/src/mcp/server.mjs +34 -1
  121. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  122. package/dist/src/mcp/tools/command-run.mjs +3 -2
  123. package/dist/src/mcp/tools/decision.mjs +49 -48
  124. package/dist/src/mcp/tools/discovery.mjs +46 -21
  125. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  126. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  127. package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
  128. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  129. package/dist/src/memory/audit.js +719 -0
  130. package/dist/src/memory/canonical-context.js +392 -13
  131. package/dist/src/memory/canonical-maintenance.js +95 -0
  132. package/dist/src/memory/cli-result.js +22 -0
  133. package/dist/src/memory/cli.js +176 -0
  134. package/dist/src/memory/context-pack.js +57 -6
  135. package/dist/src/memory/cost.js +512 -0
  136. package/dist/src/memory/firewall-classification.d.mts +7 -0
  137. package/dist/src/memory/firewall-classification.mjs +32 -0
  138. package/dist/src/memory/index.js +8 -0
  139. package/dist/src/memory/line-maintenance.js +68 -0
  140. package/dist/src/memory/maintenance-cli.js +286 -0
  141. package/dist/src/memory/maintenance-contract.js +136 -0
  142. package/dist/src/memory/maintenance-index.js +18 -0
  143. package/dist/src/memory/maintenance-owner.js +2 -0
  144. package/dist/src/memory/maintenance-record.js +138 -0
  145. package/dist/src/memory/maintenance.js +254 -0
  146. package/dist/src/memory/project-registry.js +2 -1
  147. package/dist/src/memory/provider-memory-adapter.js +154 -0
  148. package/dist/src/memory/recommend-cli.js +83 -0
  149. package/dist/src/memory/recommend.js +269 -0
  150. package/dist/src/memory/scheduled.js +81 -0
  151. package/dist/src/memory/taxonomy.js +44 -0
  152. package/dist/src/mission-protocol/codecs.js +19 -9
  153. package/dist/src/mission-protocol/worker-status.js +91 -1
  154. package/dist/src/models/config-loader.js +11 -1
  155. package/dist/src/models/model-capabilities.v1.json +74 -50
  156. package/dist/src/models/provider-policy.js +125 -62
  157. package/dist/src/models/resolver.js +45 -8
  158. package/dist/src/models/worker-installation.d.mts +8 -1
  159. package/dist/src/models/worker-installation.mjs +231 -38
  160. package/dist/src/models/wrapper-route.js +1 -1
  161. package/dist/src/output-modes/registry.js +8 -7
  162. package/dist/src/packages/adapters/git.js +99 -23
  163. package/dist/src/packages/adapters/gitea.js +8 -27
  164. package/dist/src/packages/adapters/github.js +7 -25
  165. package/dist/src/packages/registry.js +4 -4
  166. package/dist/src/plugin/metadata-validator.js +1 -1
  167. package/dist/src/process/cli-command.d.mts +5 -0
  168. package/dist/src/process/cli-command.mjs +42 -0
  169. package/dist/src/providers/deploy-version.d.mts +1 -0
  170. package/dist/src/providers/deploy-version.mjs +17 -0
  171. package/dist/src/providers/provider-definitions.js +19 -16
  172. package/dist/src/providers/skill-ownership.d.mts +11 -2
  173. package/dist/src/providers/skill-ownership.mjs +35 -17
  174. package/dist/src/rules/activation.d.mts +11 -2
  175. package/dist/src/rules/activation.mjs +82 -6
  176. package/dist/src/rules/inventory.js +1 -1
  177. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  178. package/dist/src/schema/zod-validation.d.mts +6 -0
  179. package/dist/src/schema/zod-validation.mjs +50 -0
  180. package/dist/src/serve/executor-registry.js +297 -20
  181. package/dist/src/serve/executor-token.d.mts +1 -0
  182. package/dist/src/serve/executor-token.mjs +8 -0
  183. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  184. package/dist/src/serve/worker-invocation.js +124 -15
  185. package/dist/src/sessions/adapters/claude.js +19 -18
  186. package/dist/src/sessions/adapters/codex.js +10 -9
  187. package/dist/src/sessions/adapters/copilot.js +14 -13
  188. package/dist/src/sessions/adapters/cursor.js +19 -18
  189. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  190. package/dist/src/sessions/adapters/factory.js +11 -10
  191. package/dist/src/sessions/adapters/generic.js +32 -23
  192. package/dist/src/sessions/adapters/grokbot.js +35 -35
  193. package/dist/src/sessions/adapters/hermes.js +20 -19
  194. package/dist/src/sessions/adapters/muse.js +50 -49
  195. package/dist/src/sessions/adapters/omp.js +5 -4
  196. package/dist/src/sessions/adapters/openclaw.js +30 -29
  197. package/dist/src/sessions/adapters/opencode.js +26 -26
  198. package/dist/src/sessions/adapters/openhuman.js +24 -23
  199. package/dist/src/sessions/adapters/pi.js +5 -4
  200. package/dist/src/sessions/adapters/windsurf.js +8 -8
  201. package/dist/src/sessions/analytics.js +10 -9
  202. package/dist/src/sessions/candidates.js +7 -6
  203. package/dist/src/sessions/contracts.js +63 -53
  204. package/dist/src/sessions/output-registration.js +11 -10
  205. package/dist/src/sessions/workspace-discovery.js +3 -4
  206. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  207. package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
  208. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  209. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  210. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  211. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  212. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  213. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  214. package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
  215. package/dist/src/tracker/project-inputs.js +2 -0
  216. package/dist/src/update/checker.d.mts +4 -0
  217. package/dist/src/update/checker.mjs +14 -0
  218. package/dist/src/update/notifier.mjs +2 -4
  219. package/dist/src/writing/exemplar-selection.js +6 -6
  220. package/dist/src/writing/voice-evaluation.js +24 -23
  221. package/dist/src/writing/writer-migration.js +14 -13
  222. package/dist/src/writing/writer-profile.js +19 -18
  223. package/dist/src/writing/writing-brief.js +18 -17
  224. package/dist/src/writing/writing-receipt.js +47 -46
  225. package/package.json +5 -2
  226. package/schemas/decision/DecisionReview.schema.json +78 -1
  227. package/schemas/mission-v1.schema.json +3 -0
  228. package/tools/agents/deploy-agents.mjs +67 -39
  229. package/tools/agents/providers/antigravity.mjs +6 -1
  230. package/tools/agents/providers/base.mjs +24 -14
  231. package/tools/agents/providers/claude.mjs +6 -3
  232. package/tools/agents/providers/codex.mjs +54 -57
  233. package/tools/agents/providers/context-write.mjs +28 -0
  234. package/tools/agents/providers/copilot.mjs +13 -4
  235. package/tools/agents/providers/cursor.mjs +1 -1
  236. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  237. package/tools/agents/providers/factory.mjs +7 -1
  238. package/tools/agents/providers/grok-build.mjs +18 -2
  239. package/tools/agents/providers/grokbot.mjs +1 -1
  240. package/tools/agents/providers/hermes.mjs +12 -15
  241. package/tools/agents/providers/muse-hooks.mjs +21 -14
  242. package/tools/agents/providers/muse.mjs +7 -2
  243. package/tools/agents/providers/omp.mjs +31 -5
  244. package/tools/agents/providers/opencode.mjs +14 -5
  245. package/tools/agents/providers/openhuman.mjs +1 -1
  246. package/tools/agents/providers/pi.mjs +1 -1
  247. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  248. package/tools/agents/providers/rule-delivery.mjs +289 -80
  249. package/tools/agents/providers/warp.mjs +4 -1
  250. package/tools/agents/providers/windsurf.mjs +13 -3
  251. package/tools/cli/workspace-status.mjs +5 -0
  252. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  253. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  254. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  255. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  256. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  257. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  258. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  259. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  260. package/tools/ralph-external/process-monitor.mjs +49 -8
  261. package/tools/ralph-external/session-launcher.mjs +405 -35
  262. package/tools/skills/deploy-skills-codex.mjs +11 -7
  263. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -0,0 +1,238 @@
1
+ import { evaluateWorkerDispatchOffer, knownWorkerCliExecutables } from '../models/worker-dispatch-offer.js';
2
+ import { createHash } from 'node:crypto';
3
+ import { isAbsolute, join, posix } from 'node:path';
4
+ import { isDeepStrictEqual } from 'node:util';
5
+ import { getProviderDefinition, normalizeProviderDefinitionId, resolveProviderPathValue } from '../providers/provider-definitions.js';
6
+ import { assertPlanRedacted, validateSetupPlan } from './plan-validation.js';
7
+ export const INTERVIEW_BLOCKS = ['targets', 'intent', 'tools', 'locations', 'capabilities'];
8
+ export const INSTALL_METHOD_EXPLANATION = 'Installing the aiwg CLI globally with npm updates it for every project; global provider config (--scope user) only changes one provider\'s user-level files.';
9
+ const hash = (value) => createHash('sha256').update(value).digest('hex');
10
+ /** Roots come from discovery, which owns realpath canonicalization. No filesystem access here. */
11
+ export function workspaceSlug(canonicalRoot) {
12
+ // Leave room for the setup- prefix and hash inside the plan ID's 120 chars.
13
+ const slug = posix.basename(canonicalRoot.replace(/\\/g, '/')).toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/^-+|-+$/g, '').slice(0, 105) || 'workspace';
14
+ return `${slug}-${hash(canonicalRoot).slice(0, 8)}`;
15
+ }
16
+ export function inventoryId(inventory) {
17
+ return `inv-${hash(JSON.stringify({ projects: inventory.projects.map(p => p.root).sort(),
18
+ tools: inventory.tools.map(t => t.executable).sort(), limits: inventory.limits })).slice(0, 16)}`;
19
+ }
20
+ export function questionId(block, topic, root, provider = 'shared') {
21
+ return `${block}.${topic}.${root ? `ws-${workspaceSlug(root)}.` : ''}${provider}`;
22
+ }
23
+ const precedence = {
24
+ 'explicit-flag': 5, 'existing-setting': 4, interview: 3, discovered: 2, default: 1,
25
+ };
26
+ /** Discovery is advisory even when it wins over a default. It is never an answer. */
27
+ export function mergeSetupAnswers(...layers) {
28
+ const answers = {};
29
+ const recommendations = [];
30
+ for (const layer of layers)
31
+ for (const [id, answer] of Object.entries(layer)) {
32
+ if (answer.source === 'discovered') {
33
+ recommendations.push({ questionId: id, value: answer.value, reason: 'Observed by workspace discovery.' });
34
+ }
35
+ else if (!Object.hasOwn(answers, id) || precedence[answer.source] >= precedence[answers[id].source]) {
36
+ answers[id] = answer;
37
+ }
38
+ }
39
+ return { answers, recommendations };
40
+ }
41
+ export function isAnswered(plan, id) {
42
+ return Object.hasOwn(plan.answers, id);
43
+ }
44
+ export function credentialReference(input, paths = { isAbsolute }) {
45
+ let reference;
46
+ if (input === 'stdin')
47
+ reference = { kind: 'credential-ref', source: 'stdin-at-apply', name: 'stdin' };
48
+ else if (input.startsWith('env:') && /^[A-Za-z_][A-Za-z0-9_]*$/.test(input.slice(4))) {
49
+ reference = { kind: 'credential-ref', source: 'env', name: input.slice(4) };
50
+ }
51
+ else if (input.startsWith('store:') && /^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(input.slice(6))) {
52
+ reference = { kind: 'credential-ref', source: 'store', name: input.slice(6) };
53
+ }
54
+ else if (input.startsWith('file:') && paths.isAbsolute(input.slice(5))) {
55
+ reference = { kind: 'credential-ref', source: 'file', name: input.slice(5) };
56
+ }
57
+ else
58
+ throw new Error('--credential requires <provider>=stdin|file:PATH|store:NAME|env:NAME (file paths must be absolute).');
59
+ assertPlanRedacted(reference);
60
+ return reference;
61
+ }
62
+ export function credentialEnvName(provider) {
63
+ return provider === 'claude' ? 'ANTHROPIC_API_KEY' : provider === 'codex' ? 'OPENAI_API_KEY' : `${provider.toUpperCase().replace(/-/g, '_')}_API_KEY`;
64
+ }
65
+ export function interviewProviders(context) {
66
+ return [...new Set(['claude', ...(context.providers ?? []),
67
+ ...context.inventory.tools.map(t => normalizeProviderDefinitionId(t.executable))
68
+ .filter((p) => p !== null && p !== 'generic')])].sort();
69
+ }
70
+ export function interviewQuestions(context, answers = {}) {
71
+ const { inventory, workspaceRoot } = context;
72
+ const paths = context.paths ?? { join, isAbsolute };
73
+ const workerExecutables = new Set(knownWorkerCliExecutables());
74
+ const questions = [];
75
+ const add = (block, topic, whyItMatters, defaultValue, values, flag, input = 'choice', workspace, provider, optional = false, required = false) => {
76
+ questions.push({ block, topic, questionId: questionId(block, topic, workspace, provider), workspace, provider,
77
+ // The store's sensitive-assignment scan treats "credential from?" as a value.
78
+ // Keep the full prompt in the presentation model and persist a safe purpose sentence.
79
+ explanation: whyItMatters, whyItMatters: input === 'credential' ? "A4 reads the provider's credential." : whyItMatters,
80
+ default: defaultValue, options: values.map(value => ({ label: typeof value === 'string' ? value : JSON.stringify(value), value })),
81
+ flag, input, optional, required });
82
+ };
83
+ add('targets', 'scope', 'Project writes `.aiwg/` here; user writes your global provider config.', 'project', ['project', 'user'], '--scope project|user');
84
+ add('targets', 'workspaces', 'Which observed roots get configured?', [workspaceRoot], inventory.projects.map(p => p.root), '--workspace <root>', 'roots', workspaceRoot);
85
+ const selection = answers[questionId('targets', 'workspaces', workspaceRoot)]?.value ?? [workspaceRoot];
86
+ const selected = Array.isArray(selection) ? selection : [];
87
+ const projects = inventory.projects.filter(p => selected.includes(p.root)).sort((a, b) => a.root.localeCompare(b.root));
88
+ for (const project of projects)
89
+ if (['monorepo', 'ambiguous'].includes(project.layout)) {
90
+ add('targets', 'root', 'Monorepo: configure the root once, or one package?', 'root', ['root', ...project.packageRoots], '--monorepo root|package --package <root>', 'choice', project.root, undefined, false, true);
91
+ }
92
+ add('targets', 'scan-opt-in', 'Also scan these extra directories (bounded)?', [], ['off'], '--scan-roots <dir>', 'roots', undefined, undefined, true);
93
+ for (const project of projects) {
94
+ add('intent', 'use', 'What will this workspace use AIWG for (code, docs, ops)? Drives framework/capability suggestions.', 'code', ['code', 'docs', 'ops', 'mixed'], '--intent code|docs|ops|mixed', 'choice', project.root);
95
+ add('intent', 'team', 'Solo or shared repo? Shared repos keep force_push_policy=never.', 'solo', ['solo', 'shared'], '--force-push-policy <policy>', 'choice', project.root);
96
+ }
97
+ const providers = interviewProviders(context);
98
+ for (const project of projects) {
99
+ for (const provider of providers)
100
+ add('tools', 'provider', 'Which provider toolchains does this project target?', provider === 'claude', [true, false], '--providers <list>', 'boolean', project.root, provider);
101
+ add('tools', 'scripts', 'Add default scripts (deploy/doctor/sync)?', { defaultScripts: true, customScripts: false, scripts: {} }, [true, false], '--default-scripts true|false --script <name>=<command> --custom-scripts true|false', 'scripts', project.root);
102
+ }
103
+ for (const project of projects)
104
+ for (const provider of providers) {
105
+ if (answers[questionId('tools', 'provider', project.root, provider)]?.value !== true)
106
+ continue;
107
+ const target = answers[questionId('targets', 'root', project.root)]?.value;
108
+ const targetRoot = typeof target === 'string' && target !== 'root' ? target : project.root;
109
+ const configPath = (targetRoot === project.root ? project.providerFiles.find(f => f.provider === provider && f.kind === 'config')?.path : undefined)
110
+ ?? (resolveProviderPathValue(getProviderDefinition(provider)?.paths.configFile ?? null, targetRoot, paths) || targetRoot);
111
+ add('locations', 'config-dir', "Where this provider's config file lives (informational; A3 writes nothing).", configPath, [configPath], '--target <dir>', 'path', project.root, provider);
112
+ add('locations', 'credential-source', "Where should A4 read this provider's credential from?", null, [{ kind: 'credential-ref', source: 'stdin-at-apply', name: 'stdin' },
113
+ { kind: 'credential-ref', source: 'file', name: paths.join(project.root, '.credentials', provider) },
114
+ { kind: 'credential-ref', source: 'store', name: provider },
115
+ { kind: 'credential-ref', source: 'env', name: credentialEnvName(provider) }], '--credential <provider>=stdin|file:PATH|store:NAME|env:NAME', 'credential', project.root, provider, true);
116
+ }
117
+ for (const project of projects) {
118
+ const rootChoice = answers[questionId('targets', 'root', project.root)]?.value;
119
+ const targetWorkspace = typeof rootChoice === 'string' && rootChoice !== 'root' ? rootChoice : project.root;
120
+ const detected = inventory.tools.filter(t => t.installed === 'yes' && workerExecutables.has(t.executable));
121
+ const dispatch = answers[questionId('tools', 'worker-dispatch', project.root)]?.value;
122
+ if (evaluateWorkerDispatchOffer(undefined, dispatch === 'decline' ? { worker_dispatch_offer: 'declined' } : undefined, detected.map(t => t.executable)).shouldOffer || dispatch === 'enable') {
123
+ // Keep A3's stable question ID while presenting the offer in optional capabilities.
124
+ add('capabilities', 'worker-dispatch', `Direct workers use their own accounts' quota; shared quotaScope labels share limits. No daemon or scheduler is required. ${detected.map(t => `${t.executable}: ${context.qualifiedWorkers?.[targetWorkspace]?.includes(t.executable) ? 'qualified (recorded evidence; launch rechecks)' : 'installed-not-qualified'}`).join('; ')}.`, false, ['enable', 'decline'], '--worker-dispatch enable|decline', 'choice', project.root, undefined, true);
125
+ questions[questions.length - 1].questionId = questionId('tools', 'worker-dispatch', project.root);
126
+ }
127
+ if (dispatch === 'enable') {
128
+ add('capabilities', 'worker-config', 'Optional worker paths (clis), model roles (groups), and limits (admission/budgets) as existing parallelism JSON; skip preserves current settings. Qualification runs only at apply.', null, [null], '--worker-dispatch enable (configure parallelism with aiwg config set)', 'policy', project.root, undefined, true);
129
+ }
130
+ const jev = answers[questionId('capabilities', 'jev', project.root)]?.value;
131
+ const jevStatus = context.jevStatus?.[targetWorkspace];
132
+ add('capabilities', 'jev', `Jev status: ${jevStatus ? jevStatus.enabled ? 'enabled' : 'off' : 'unknown'}; backend ${jevStatus?.backend.status ?? 'not-probed'}. Decisions are sent to an external service; a Jev account/API access and usage charges apply. Enable decision offload?`, false, ['enable', 'decline'], '--jev enable|decline|defer', 'choice', project.root, undefined, true);
133
+ if (jev === 'enable') {
134
+ add('capabilities', 'credential-source', 'Where should A4 read the Jev API input from? Enter a reference only; setup/defer works without an API input.', null, [{ kind: 'credential-ref', source: 'stdin-at-apply', name: 'stdin' },
135
+ { kind: 'credential-ref', source: 'file', name: paths.join(project.root, '.credentials', 'jev') },
136
+ { kind: 'credential-ref', source: 'store', name: 'jev' }, { kind: 'credential-ref', source: 'env', name: 'JEV_API_KEY' }], '--credential jev=stdin|file:PATH|store:NAME|env:NAME', 'credential', project.root, 'jev', true);
137
+ questions[questions.length - 1].questionId = questionId('locations', 'credential-source', project.root, 'jev');
138
+ add('capabilities', 'jev-options', 'Optional Jev settings as JSON: region, endpoint, verify. Verification sends a billable request at apply.', null, [null], '--region <r> --endpoint <url> --verify', 'policy', project.root, 'jev', true);
139
+ }
140
+ add('capabilities', 'signing', 'Require signed delivery commits?', false, [true, false], '--require-signed-commits --signing-format --signing-key --signing-enforce', 'policy', project.root, undefined, true);
141
+ add('capabilities', 'delivery', 'Delivery mode and branch protections.', { mode: 'pr-required', force_push_policy: 'never' }, [false, { mode: 'pr-required', force_push_policy: 'never' }], '--delivery-mode --default-branch --require-ci-green --auto-close-issues --issue-comment-on-cycle --force-push-policy', 'policy', project.root, undefined, true);
142
+ add('capabilities', 'tracker', 'Issue tracker provider and remotes.', null, ['gitea', 'github', 'local'].map(issue_provider => ({ issue_provider })), '--issue-provider gitea|github|local', 'policy', project.root, undefined, !context.unknownRemote, !!context.unknownRemote);
143
+ }
144
+ return questions;
145
+ }
146
+ export function interviewRecommendations(context) {
147
+ const recommendations = [];
148
+ for (const project of [...context.inventory.projects].sort((a, b) => a.root.localeCompare(b.root))) {
149
+ if (context.trackerRecommendation)
150
+ recommendations.push({ questionId: questionId('capabilities', 'tracker', project.root),
151
+ value: context.trackerRecommendation, reason: 'Detected repository remotes (provider and remote names only).' });
152
+ for (const tool of context.inventory.tools) {
153
+ const provider = normalizeProviderDefinitionId(tool.executable);
154
+ if (provider && provider !== 'generic' && tool.installed === 'yes')
155
+ recommendations.push({
156
+ questionId: questionId('tools', 'provider', project.root, provider), value: true,
157
+ reason: `PATH contains ${tool.executable}; installed=${tool.installed}, configured=${tool.configured}, authenticated=${tool.authenticated}, qualified=${tool.qualified}.`,
158
+ });
159
+ }
160
+ for (const file of project.providerFiles.filter(f => f.kind === 'config'))
161
+ recommendations.push({
162
+ questionId: questionId('locations', 'config-dir', project.root, file.provider), value: file.path,
163
+ reason: `Discovery observed a ${file.provider} config path.`,
164
+ });
165
+ }
166
+ return recommendations.sort((a, b) => a.questionId.localeCompare(b.questionId));
167
+ }
168
+ /** Recompute only from current answers. Declines and unresolved optionals create no capability actions. */
169
+ export function buildInterviewPlan(context, answers, now, previous) {
170
+ const paths = context.paths ?? { join, isAbsolute };
171
+ const questions = interviewQuestions(context, answers);
172
+ const workerExecutables = new Set(knownWorkerCliExecutables());
173
+ const actions = [];
174
+ const scope = answers[questionId('targets', 'scope')]?.value === 'user' ? 'user' : 'project';
175
+ const chosen = answers[questionId('targets', 'workspaces', context.workspaceRoot)]?.value;
176
+ const add = (workspace, provider, kind, dependsOn) => {
177
+ const base = `${provider}-${kind}-${workspaceSlug(workspace)}`;
178
+ let id = base;
179
+ for (let n = 2; actions.some(a => a.id === id); n++)
180
+ id = `${base}-${n}`;
181
+ actions.push({ id, workspace, provider, scope, kind, dependsOn });
182
+ };
183
+ for (const project of [...context.inventory.projects].sort((a, b) => a.root.localeCompare(b.root))) {
184
+ if (!Array.isArray(chosen) || !chosen.includes(project.root))
185
+ continue;
186
+ const rootChoice = answers[questionId('targets', 'root', project.root)];
187
+ if (['monorepo', 'ambiguous'].includes(project.layout) && !Object.hasOwn(answers, questionId('targets', 'root', project.root)))
188
+ continue;
189
+ const workspace = typeof rootChoice?.value === 'string' && rootChoice.value !== 'root' ? rootChoice.value : project.root;
190
+ const layout = { kind: 'project', root: workspace, field: 'layout' };
191
+ const path = (value) => ({ kind: 'path', workspace, path: value });
192
+ // The selected package must itself be in A1's snapshot before A4 can apply it.
193
+ const selectedProviders = questions.filter(q => q.workspace === project.root && q.topic === 'provider' && answers[q.questionId]?.value === true);
194
+ for (const { provider } of selectedProviders) {
195
+ const id = provider;
196
+ const executable = context.inventory.tools.find(tool => normalizeProviderDefinitionId(tool.executable) === id)?.executable ?? id;
197
+ const tool = (field) => ({ kind: 'tool', executable, field });
198
+ const config = answers[questionId('locations', 'config-dir', project.root, id)]?.value;
199
+ if (typeof config === 'string')
200
+ add(workspace, id, 'configure-provider', [tool('installed'), path(config), layout]);
201
+ add(workspace, id, 'verify-provider', [tool('version'), layout]);
202
+ const intent = answers[questionId('intent', 'use', project.root)];
203
+ const dispatch = answers[questionId('tools', 'worker-dispatch', project.root)];
204
+ if ((intent && intent.source !== 'default') || dispatch?.value === 'enable')
205
+ add(workspace, id, 'deploy-framework', [layout, tool('installed')]);
206
+ }
207
+ if (selectedProviders.length > 0) {
208
+ add(workspace, 'shared', 'write-project-config', [path(paths.join(workspace, 'package.json')), layout, path(paths.join(workspace, '.aiwg', 'aiwg.config'))]);
209
+ add(workspace, 'shared', 'register-project', [{ kind: 'project', root: workspace, field: 'root' }, path(paths.join(workspace, '.aiwg'))]);
210
+ }
211
+ const jevChoice = answers[questionId('capabilities', 'jev', project.root)]?.value;
212
+ if (jevChoice === 'enable' || jevChoice === 'remove') {
213
+ add(workspace, 'jev', 'configure-jev', [layout]);
214
+ }
215
+ if (answers[questionId('tools', 'worker-dispatch', project.root)]?.value === 'enable') {
216
+ add(workspace, 'shared', 'configure-worker-dispatch', [layout, ...context.inventory.tools
217
+ .filter(t => t.installed === 'yes' && workerExecutables.has(t.executable))
218
+ .flatMap(t => [{ kind: 'tool', executable: t.executable, field: 'installed' },
219
+ { kind: 'tool', executable: t.executable, field: 'path' }])]);
220
+ }
221
+ if (answers[questionId('tools', 'worker-dispatch', project.root)]?.value === 'decline')
222
+ add(workspace, 'shared', 'record-decline', []);
223
+ }
224
+ const validIds = new Set(actions.map(a => a.id));
225
+ const answerValues = (values) => Object.fromEntries(Object.entries(values).map(([id, answer]) => [id, answer.value]));
226
+ const sameAnswers = previous && isDeepStrictEqual(answerValues(previous.answers), answerValues(answers));
227
+ return validateSetupPlan({ schemaVersion: 1, id: previous?.id ?? `setup-${workspaceSlug(context.workspaceRoot)}`,
228
+ inventoryId: inventoryId(context.inventory), createdAt: previous?.createdAt ?? now, updatedAt: now,
229
+ inventory: context.inventory, answers: Object.fromEntries(Object.entries(answers).sort(([a], [b]) => a.localeCompare(b))), recommendations: interviewRecommendations(context),
230
+ unresolvedChoices: questions.map(({ questionId, options, default: defaultValue, whyItMatters }) => {
231
+ const deferredAt = previous?.unresolvedChoices.find(q => q.questionId === questionId)?.deferredAt;
232
+ return { questionId, options, default: defaultValue, whyItMatters, ...(deferredAt ? { deferredAt } : {}) };
233
+ }), actions, receipts: (sameAnswers ? previous.receipts : []).map(r => ({ ...r,
234
+ actions: Object.fromEntries(Object.entries(r.actions).filter(([id]) => validIds.has(id) &&
235
+ isDeepStrictEqual(previous?.actions.find(action => action.id === id), actions.find(action => action.id === id)))) })),
236
+ });
237
+ }
238
+ //# sourceMappingURL=interview.js.map