@aiwg/cli 2026.10.5 → 2026.10.6

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 (212) 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 +3 -1
  6. package/dist/src/artifacts/browser-export.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +8 -1
  8. package/dist/src/artifacts/cli.js +57 -6
  9. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  10. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  11. package/dist/src/artifacts/graph-query.js +18 -8
  12. package/dist/src/artifacts/index-adapters.js +339 -0
  13. package/dist/src/artifacts/index-builder.js +143 -14
  14. package/dist/src/artifacts/index-files.js +15 -4
  15. package/dist/src/artifacts/index-reader.js +44 -0
  16. package/dist/src/artifacts/index-refresh.js +114 -55
  17. package/dist/src/artifacts/index-status.js +20 -5
  18. package/dist/src/artifacts/memory-maintenance.js +503 -0
  19. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  20. package/dist/src/artifacts/query-engine.js +263 -108
  21. package/dist/src/artifacts/relations-builder.js +2 -2
  22. package/dist/src/artifacts/source-graph.js +2 -2
  23. package/dist/src/artifacts/types.js +9 -1
  24. package/dist/src/cli/handlers/decision.js +19 -1
  25. package/dist/src/cli/handlers/guided-setup.js +470 -0
  26. package/dist/src/cli/handlers/help.js +2 -2
  27. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  28. package/dist/src/cli/handlers/index.js +4 -2
  29. package/dist/src/cli/handlers/init.js +41 -6
  30. package/dist/src/cli/handlers/local-executor.js +5 -3
  31. package/dist/src/cli/handlers/mc.js +509 -53
  32. package/dist/src/cli/handlers/models.js +32 -2
  33. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  34. package/dist/src/cli/handlers/rules.js +1 -1
  35. package/dist/src/cli/handlers/script-runner.js +73 -2
  36. package/dist/src/cli/handlers/serve.js +5 -3
  37. package/dist/src/cli/handlers/sessions.js +1 -0
  38. package/dist/src/cli/handlers/setup.js +428 -1
  39. package/dist/src/cli/handlers/skill-lint.js +1 -1
  40. package/dist/src/cli/handlers/subcommands.js +118 -8
  41. package/dist/src/cli/handlers/use.js +279 -93
  42. package/dist/src/cli/handlers/workspace.js +3 -1
  43. package/dist/src/cli/prompt-utils.js +8 -0
  44. package/dist/src/cli/router.js +16 -1
  45. package/dist/src/config/aiwg-config.js +42 -0
  46. package/dist/src/config/cli.js +7 -3
  47. package/dist/src/config/decision-config.js +14 -1
  48. package/dist/src/config/native-sandbox-warnings.js +25 -0
  49. package/dist/src/contributors/validation.js +6 -5
  50. package/dist/src/decision/admission.js +7 -3
  51. package/dist/src/decision/benchmark.js +469 -0
  52. package/dist/src/decision/ledger.js +22 -3
  53. package/dist/src/decision/pricing.js +147 -0
  54. package/dist/src/decision/qualification/gate-evidence.js +18 -15
  55. package/dist/src/decision/qualification/load-staged.js +539 -0
  56. package/dist/src/decision/qualification/load.js +109 -33
  57. package/dist/src/decision/qualification/runner.js +5 -2
  58. package/dist/src/decision/review/service.js +6 -4
  59. package/dist/src/decision/review/validate.js +23 -2
  60. package/dist/src/decision/savings.js +8 -3
  61. package/dist/src/discovery/index.js +492 -0
  62. package/dist/src/discovery/interview.js +233 -0
  63. package/dist/src/discovery/plan-apply.js +571 -0
  64. package/dist/src/discovery/plan-store.js +171 -0
  65. package/dist/src/discovery/plan-types.js +2 -0
  66. package/dist/src/discovery/plan-validation.js +139 -0
  67. package/dist/src/discovery/types.js +2 -0
  68. package/dist/src/extensions/commands/definitions.js +49 -9
  69. package/dist/src/extensions/manifest.js +71 -44
  70. package/dist/src/extensions/project-local-discovery.js +1 -1
  71. package/dist/src/extensions/project-quickref.js +18 -17
  72. package/dist/src/extensions/validation.js +32 -29
  73. package/dist/src/housekeeper/daemon-config.js +148 -0
  74. package/dist/src/housekeeper/ipc.js +84 -0
  75. package/dist/src/housekeeper/lifecycle.js +148 -0
  76. package/dist/src/housekeeper/platform.js +35 -0
  77. package/dist/src/housekeeper/status.js +70 -0
  78. package/dist/src/housekeeper/units.js +114 -0
  79. package/dist/src/jobs/flow.js +22 -21
  80. package/dist/src/lint/rule-activation.js +20 -5
  81. package/dist/src/lint/runner.js +1 -1
  82. package/dist/src/mcp/cli.mjs +5 -6
  83. package/dist/src/mcp/config-file.mjs +6 -2
  84. package/dist/src/mcp/config-layers.mjs +23 -7
  85. package/dist/src/mcp/profiles.js +18 -31
  86. package/dist/src/mcp/profiles.mjs +18 -32
  87. package/dist/src/mcp/registry.js +7 -27
  88. package/dist/src/mcp/registry.mjs +7 -28
  89. package/dist/src/mcp/server.mjs +34 -1
  90. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  91. package/dist/src/mcp/tools/command-run.mjs +3 -2
  92. package/dist/src/mcp/tools/decision.mjs +49 -48
  93. package/dist/src/mcp/tools/discovery.mjs +46 -21
  94. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  95. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  96. package/dist/src/mcp/tools/worker-invoke.mjs +17 -4
  97. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  98. package/dist/src/memory/audit.js +719 -0
  99. package/dist/src/memory/canonical-context.js +392 -13
  100. package/dist/src/memory/canonical-maintenance.js +95 -0
  101. package/dist/src/memory/cli-result.js +22 -0
  102. package/dist/src/memory/cli.js +176 -0
  103. package/dist/src/memory/context-pack.js +57 -6
  104. package/dist/src/memory/cost.js +512 -0
  105. package/dist/src/memory/firewall-classification.d.mts +7 -0
  106. package/dist/src/memory/firewall-classification.mjs +32 -0
  107. package/dist/src/memory/index.js +8 -0
  108. package/dist/src/memory/line-maintenance.js +68 -0
  109. package/dist/src/memory/maintenance-cli.js +286 -0
  110. package/dist/src/memory/maintenance-contract.js +136 -0
  111. package/dist/src/memory/maintenance-index.js +18 -0
  112. package/dist/src/memory/maintenance-owner.js +2 -0
  113. package/dist/src/memory/maintenance-record.js +138 -0
  114. package/dist/src/memory/maintenance.js +254 -0
  115. package/dist/src/memory/provider-memory-adapter.js +154 -0
  116. package/dist/src/memory/recommend-cli.js +83 -0
  117. package/dist/src/memory/recommend.js +269 -0
  118. package/dist/src/memory/scheduled.js +81 -0
  119. package/dist/src/memory/taxonomy.js +44 -0
  120. package/dist/src/mission-protocol/codecs.js +19 -9
  121. package/dist/src/models/config-loader.js +11 -1
  122. package/dist/src/models/model-capabilities.v1.json +74 -50
  123. package/dist/src/models/provider-policy.js +125 -62
  124. package/dist/src/models/resolver.js +45 -8
  125. package/dist/src/models/worker-installation.d.mts +7 -0
  126. package/dist/src/models/worker-installation.mjs +169 -37
  127. package/dist/src/models/wrapper-route.js +1 -1
  128. package/dist/src/output-modes/registry.js +8 -7
  129. package/dist/src/plugin/metadata-validator.js +1 -1
  130. package/dist/src/providers/deploy-version.d.mts +1 -0
  131. package/dist/src/providers/deploy-version.mjs +17 -0
  132. package/dist/src/providers/provider-definitions.js +19 -16
  133. package/dist/src/providers/skill-ownership.d.mts +11 -2
  134. package/dist/src/providers/skill-ownership.mjs +35 -17
  135. package/dist/src/rules/activation.d.mts +11 -2
  136. package/dist/src/rules/activation.mjs +82 -6
  137. package/dist/src/rules/inventory.js +1 -1
  138. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  139. package/dist/src/schema/zod-validation.d.mts +6 -0
  140. package/dist/src/schema/zod-validation.mjs +50 -0
  141. package/dist/src/serve/executor-registry.js +297 -20
  142. package/dist/src/serve/executor-token.d.mts +1 -0
  143. package/dist/src/serve/executor-token.mjs +8 -0
  144. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  145. package/dist/src/serve/worker-invocation.js +114 -15
  146. package/dist/src/sessions/adapters/claude.js +19 -18
  147. package/dist/src/sessions/adapters/codex.js +10 -9
  148. package/dist/src/sessions/adapters/copilot.js +14 -13
  149. package/dist/src/sessions/adapters/cursor.js +19 -18
  150. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  151. package/dist/src/sessions/adapters/factory.js +11 -10
  152. package/dist/src/sessions/adapters/generic.js +32 -23
  153. package/dist/src/sessions/adapters/grokbot.js +35 -35
  154. package/dist/src/sessions/adapters/hermes.js +20 -19
  155. package/dist/src/sessions/adapters/muse.js +50 -49
  156. package/dist/src/sessions/adapters/omp.js +5 -4
  157. package/dist/src/sessions/adapters/openclaw.js +30 -29
  158. package/dist/src/sessions/adapters/opencode.js +26 -26
  159. package/dist/src/sessions/adapters/openhuman.js +24 -23
  160. package/dist/src/sessions/adapters/pi.js +5 -4
  161. package/dist/src/sessions/adapters/windsurf.js +8 -8
  162. package/dist/src/sessions/analytics.js +10 -9
  163. package/dist/src/sessions/candidates.js +7 -6
  164. package/dist/src/sessions/contracts.js +63 -53
  165. package/dist/src/sessions/output-registration.js +11 -10
  166. package/dist/src/sessions/workspace-discovery.js +3 -4
  167. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  168. package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
  169. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  170. package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
  171. package/dist/src/update/checker.d.mts +4 -0
  172. package/dist/src/update/checker.mjs +14 -0
  173. package/dist/src/writing/exemplar-selection.js +6 -6
  174. package/dist/src/writing/voice-evaluation.js +24 -23
  175. package/dist/src/writing/writer-migration.js +14 -13
  176. package/dist/src/writing/writer-profile.js +19 -18
  177. package/dist/src/writing/writing-brief.js +18 -17
  178. package/dist/src/writing/writing-receipt.js +47 -46
  179. package/package.json +5 -2
  180. package/schemas/decision/DecisionReview.schema.json +78 -1
  181. package/schemas/mission-v1.schema.json +3 -0
  182. package/tools/agents/deploy-agents.mjs +67 -39
  183. package/tools/agents/providers/antigravity.mjs +6 -1
  184. package/tools/agents/providers/base.mjs +19 -13
  185. package/tools/agents/providers/claude.mjs +1 -1
  186. package/tools/agents/providers/codex.mjs +50 -56
  187. package/tools/agents/providers/copilot.mjs +8 -2
  188. package/tools/agents/providers/cursor.mjs +1 -1
  189. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  190. package/tools/agents/providers/factory.mjs +7 -1
  191. package/tools/agents/providers/grok-build.mjs +18 -2
  192. package/tools/agents/providers/grokbot.mjs +1 -1
  193. package/tools/agents/providers/hermes.mjs +7 -13
  194. package/tools/agents/providers/muse-hooks.mjs +21 -14
  195. package/tools/agents/providers/muse.mjs +7 -2
  196. package/tools/agents/providers/omp.mjs +28 -4
  197. package/tools/agents/providers/opencode.mjs +14 -5
  198. package/tools/agents/providers/openhuman.mjs +1 -1
  199. package/tools/agents/providers/pi.mjs +1 -1
  200. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  201. package/tools/agents/providers/rule-delivery.mjs +263 -71
  202. package/tools/agents/providers/windsurf.mjs +7 -0
  203. package/tools/cli/workspace-status.mjs +5 -0
  204. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  205. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  206. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  207. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  208. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  209. package/tools/ralph-external/process-monitor.mjs +7 -4
  210. package/tools/ralph-external/session-launcher.mjs +280 -32
  211. package/tools/skills/deploy-skills-codex.mjs +11 -7
  212. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -0,0 +1,470 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { isAbsolute, resolve } from 'node:path';
3
+ import { decisionCapabilities } from '../../decision/driver.js';
4
+ import { ConfigLoader } from '../../models/config-loader.js';
5
+ import { inspectRecordedWorkerQualifications } from '../../models/worker-installation.mjs';
6
+ import { readAiwgConfig, validateParallelismConfig } from '../../config/aiwg-config.js';
7
+ import { projectAiwgPath } from '../../config/project-artifacts.js';
8
+ import { discoverInventory } from '../../discovery/index.js';
9
+ import { buildInterviewPlan, credentialEnvName, credentialReference, INSTALL_METHOD_EXPLANATION, interviewProviders, interviewQuestions, mergeSetupAnswers, questionId, workspaceSlug } from '../../discovery/interview.js';
10
+ import { readSetupPlan, unansweredPlanChoices, writeSetupPlan } from '../../discovery/plan-store.js';
11
+ import { assertPlanRedacted } from '../../discovery/plan-validation.js';
12
+ import { normalizeProviderDefinitionId } from '../../providers/provider-definitions.js';
13
+ import { AiwgError, EXIT_CODES } from '../errors.js';
14
+ import { collectFlagValues, MissingFlagValueError, requireFlagValue } from '../flag-parsing.js';
15
+ import { askString, createPromptInterface } from '../prompt-utils.js';
16
+ const NEW_FLAGS = ['--scope', '--workspace', '--monorepo', '--package', '--scan-roots', '--intent', '--credential', '--jev', '--region', '--endpoint'];
17
+ export function parseGuidedSetupFlags(args, command) {
18
+ const usable = (name, raw) => {
19
+ if (!raw.trim())
20
+ throw new MissingFlagValueError(name);
21
+ return raw;
22
+ };
23
+ const value = (name) => {
24
+ const raw = requireFlagValue(args, { rejectEmpty: true }, name);
25
+ return raw === undefined ? undefined : usable(name, raw);
26
+ };
27
+ const values = (name) => collectFlagValues(args, { rejectEmpty: true }, name).map(raw => usable(name, raw));
28
+ const enumeration = (name, options) => {
29
+ const raw = value(name);
30
+ if (raw === undefined)
31
+ return undefined;
32
+ if (!options.includes(raw))
33
+ throw new Error(`${name} must be one of: ${options.join(', ')}`);
34
+ return raw;
35
+ };
36
+ const jev = enumeration('--jev', ['enable', 'decline', 'defer']);
37
+ const region = value('--region');
38
+ const endpoint = value('--endpoint');
39
+ const switches = args.slice(0, args.indexOf('--') < 0 ? args.length : args.indexOf('--'));
40
+ const verify = switches.includes('--verify');
41
+ const remove = switches.includes('--remove');
42
+ const tokenStdin = switches.includes('--token-stdin');
43
+ if ((jev === 'decline' || jev === 'defer' || remove) && (tokenStdin || region || endpoint || verify))
44
+ throw new Error('Jev decline/defer/remove cannot be combined with Jev enable options.');
45
+ if (remove && jev === 'enable')
46
+ throw new Error('--remove cannot be combined with --jev enable.');
47
+ const jevOptions = region || endpoint || verify || remove ? { ...(region ? { region } : {}), ...(endpoint ? { endpoint } : {}), ...(verify ? { verify } : {}), ...(remove ? { remove } : {}) } : undefined;
48
+ const scope = enumeration('--scope', ['project', 'user']);
49
+ const intent = enumeration('--intent', ['code', 'docs', 'ops', 'mixed']);
50
+ const monorepo = enumeration('--monorepo', ['root', 'package']);
51
+ const packageRoot = value('--package');
52
+ const workspaces = values('--workspace');
53
+ const scanRoots = values('--scan-roots');
54
+ const credentials = {};
55
+ for (const raw of values('--credential')) {
56
+ const separator = raw.indexOf('=');
57
+ const provider = raw.slice(0, separator) === 'jev' ? 'jev' : normalizeProviderDefinitionId(raw.slice(0, separator));
58
+ if (separator < 1 || !provider || provider === 'generic')
59
+ throw new Error('--credential requires a known <provider>=env:NAME|file:PATH|stdin.');
60
+ credentials[provider] = credentialReference(raw.slice(separator + 1));
61
+ }
62
+ if (tokenStdin) {
63
+ if (credentials.jev && credentials.jev.source !== 'stdin-at-apply')
64
+ throw new Error('--token-stdin conflicts with --credential jev.');
65
+ credentials.jev = credentialReference('stdin');
66
+ }
67
+ if (jevOptions)
68
+ validateJevOptions(jevOptions);
69
+ if (monorepo === 'package' && !packageRoot)
70
+ throw new Error('--monorepo package requires --package <root>.');
71
+ if (packageRoot && monorepo !== 'package')
72
+ throw new Error('--package requires --monorepo package.');
73
+ if (command === 'init' && (workspaces.length || monorepo || packageRoot || scanRoots.length)) {
74
+ throw new Error('--workspace, --monorepo, --package and --scan-roots are available on setup.');
75
+ }
76
+ // The strict helpers honor the bare -- terminator, including malformed later occurrences.
77
+ const enabled = NEW_FLAGS.some(name => value(name) !== undefined) || tokenStdin || verify || remove;
78
+ return { jev: jev ?? (remove ? 'decline' : undefined) ?? (tokenStdin || region || endpoint || verify ? 'enable' : undefined), jevOptions, scope, intent, monorepo, packageRoot, workspaces: workspaces.length ? workspaces : undefined,
79
+ scanRoots: scanRoots.length ? scanRoots : undefined, credentials, enabled };
80
+ }
81
+ function existingValues(config) {
82
+ if (!config)
83
+ return {};
84
+ const { require_signed_commits, signing, committer: _committer, ...delivery } = config.delivery ?? {};
85
+ const remotes = config.remotes;
86
+ // Read only settings owned by these questions, never transport/auth configuration.
87
+ const tracker = remotes ? Object.fromEntries(Object.entries({ primary: remotes.primary,
88
+ issue_tracker: remotes.issue_tracker, issue_provider: remotes.issue_provider, ci: remotes.ci,
89
+ customer_issue_tracker: remotes.customer_issue_tracker, customer_issue_provider: remotes.customer_issue_provider,
90
+ tracker_actor_login: remotes.tracker_actor?.login, tracker_actor_via: remotes.tracker_actor?.via,
91
+ customer_tracker_actor_login: remotes.customer_tracker_actor?.login, customer_tracker_actor_via: remotes.customer_tracker_actor?.via,
92
+ }).filter((entry) => entry[1] !== undefined)) : undefined;
93
+ return { providers: config.providers,
94
+ scripts: { defaultScripts: false, customScripts: true, scripts: config.scripts },
95
+ ...(config.parallelism?.worker_dispatch === true ? { workerDispatch: 'enable' }
96
+ : config.setup?.worker_dispatch_offer === 'declined' ? { workerDispatch: 'decline' } : {}),
97
+ ...(require_signed_commits !== undefined || signing ? { signing: require_signed_commits === false ? false
98
+ : Object.fromEntries(Object.entries({ required: require_signed_commits ?? false,
99
+ format: signing?.format, key: signing?.key, enforce: signing?.enforce }).filter(([, value]) => value !== undefined)) } : {}),
100
+ ...(Object.keys(delivery).length ? { delivery: delivery } : {}),
101
+ ...(tracker ? { tracker } : {}), };
102
+ }
103
+ function answersFromValues(context, values, flags, source, now, roots) {
104
+ const answers = {};
105
+ const put = (id, value) => { if (value !== undefined)
106
+ answers[id] = { value, source, answeredAt: now }; };
107
+ if (flags) {
108
+ put(questionId('targets', 'scope'), flags.scope);
109
+ put(questionId('targets', 'workspaces', context.workspaceRoot), flags.workspaces);
110
+ put(questionId('targets', 'scan-opt-in'), flags.scanRoots);
111
+ }
112
+ for (const project of context.inventory.projects) {
113
+ const root = project.root;
114
+ if (roots && !roots.includes(root))
115
+ continue;
116
+ if (flags?.monorepo && ['monorepo', 'ambiguous'].includes(project.layout)) {
117
+ const choice = flags.monorepo === 'root' ? 'root' : flags.packageRoot;
118
+ if (choice !== 'root' && !project.packageRoots.includes(choice))
119
+ throw new AiwgError({ code: 'ERR_INVALID_VALUE',
120
+ exitCode: EXIT_CODES.USAGE, message: '--package must name an observed package root.' });
121
+ put(questionId('targets', 'root', root), choice);
122
+ }
123
+ put(questionId('intent', 'use', root), flags?.intent);
124
+ put(questionId('intent', 'team', root), values.team);
125
+ if (values.providers) {
126
+ // Explicit selection also records false for observed providers not selected.
127
+ const candidates = interviewProviders(context);
128
+ for (const provider of new Set([...candidates, ...values.providers]))
129
+ put(questionId('tools', 'provider', root, provider), values.providers.includes(provider));
130
+ }
131
+ put(questionId('tools', 'worker-dispatch', root), values.workerDispatch);
132
+ put(questionId('tools', 'scripts', root), values.scripts);
133
+ if (flags?.jev !== 'defer')
134
+ put(questionId('capabilities', 'jev', root), flags?.jevOptions?.remove === true ? 'remove' : flags?.jev);
135
+ put(questionId('capabilities', 'jev-options', root, 'jev'), flags?.jevOptions);
136
+ put(questionId('capabilities', 'signing', root), values.signing);
137
+ put(questionId('capabilities', 'delivery', root), values.delivery);
138
+ put(questionId('capabilities', 'tracker', root), values.tracker);
139
+ for (const [provider, reference] of Object.entries(flags?.credentials ?? {}))
140
+ put(questionId('locations', 'credential-source', root, provider), reference);
141
+ }
142
+ return answers;
143
+ }
144
+ function missingChoice(question) {
145
+ return new AiwgError({ code: 'ERR_MISSING_SETUP_CHOICE', exitCode: EXIT_CODES.USAGE,
146
+ message: `Missing required choice '${question.questionId}': re-run with ${question.flag}` });
147
+ }
148
+ /** Jev uses the existing decision setup option names, with no credential bytes. */
149
+ export function validateJevOptions(value) {
150
+ if (!value || typeof value !== 'object' || Array.isArray(value))
151
+ throw new Error('Jev settings must be an object.');
152
+ const options = value;
153
+ if (Object.keys(options).some(key => !['region', 'endpoint', 'verify', 'remove'].includes(key)) ||
154
+ (options.region !== undefined && (typeof options.region !== 'string' || !options.region.trim())) ||
155
+ (options.verify !== undefined && typeof options.verify !== 'boolean') ||
156
+ (options.remove !== undefined && typeof options.remove !== 'boolean'))
157
+ throw new Error('Invalid Jev setup settings.');
158
+ if (options.endpoint !== undefined) {
159
+ const url = new URL(String(options.endpoint));
160
+ if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash)
161
+ throw new Error('Jev endpoint must be HTTPS without auth, query, or fragment.');
162
+ }
163
+ }
164
+ /** Parse only the displayed value format. Invalid input never becomes an answer. */
165
+ export function parseInterviewAnswer(question, raw) {
166
+ if (question.input === 'credential')
167
+ return credentialReference(raw);
168
+ if (raw === '')
169
+ return question.default;
170
+ if (question.input === 'boolean') {
171
+ if (['true', 'yes', 'y'].includes(raw))
172
+ return true;
173
+ if (['false', 'no', 'n'].includes(raw))
174
+ return false;
175
+ }
176
+ else if (question.input === 'roots') {
177
+ if (raw === 'off')
178
+ return [];
179
+ const roots = raw.split(',').map(root => root.trim()).filter(Boolean);
180
+ if (question.topic === 'workspaces' && !roots.every(root => question.options.some(o => o.value === root)))
181
+ throw new Error('Select only observed workspace roots.');
182
+ return roots;
183
+ }
184
+ else if (question.input === 'path') {
185
+ if (isAbsolute(raw))
186
+ return raw;
187
+ }
188
+ else if (question.input === 'scripts') {
189
+ if (['true', 'yes', 'y', 'false', 'no', 'n'].includes(raw))
190
+ return { defaultScripts: ['true', 'yes', 'y'].includes(raw), customScripts: false, scripts: {} };
191
+ if (raw.startsWith('{')) {
192
+ const value = JSON.parse(raw);
193
+ if (typeof value.defaultScripts === 'boolean' && typeof value.customScripts === 'boolean' &&
194
+ value.scripts && typeof value.scripts === 'object' && !Array.isArray(value.scripts) &&
195
+ Object.values(value.scripts).every(command => typeof command === 'string'))
196
+ return value;
197
+ }
198
+ }
199
+ else if (question.input === 'policy') {
200
+ if (['false', 'no', 'n', 'decline'].includes(raw))
201
+ return false;
202
+ if (question.topic === 'signing' && ['true', 'yes', 'y'].includes(raw))
203
+ return true;
204
+ if (question.topic === 'tracker' && ['gitea', 'github', 'local'].includes(raw))
205
+ return { issue_provider: raw };
206
+ // Structured policies share exactly the same JSON shape as flag answers.
207
+ if (raw.startsWith('{')) {
208
+ let value;
209
+ try {
210
+ value = JSON.parse(raw);
211
+ }
212
+ catch {
213
+ throw new Error('Invalid structured settings; enter a JSON object.');
214
+ }
215
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
216
+ if (question.topic === 'jev-options') {
217
+ validateJevOptions(value);
218
+ if (Object.hasOwn(value, 'remove'))
219
+ throw new Error('Use the explicit --remove flag to remove Jev setup.');
220
+ }
221
+ if (question.topic === 'worker-config') {
222
+ if (Object.keys(value).some(key => !['clis', 'groups', 'admission', 'budgets'].includes(key)) || validateParallelismConfig(value).length)
223
+ throw new Error('Invalid worker parallelism settings.');
224
+ }
225
+ return value;
226
+ }
227
+ }
228
+ }
229
+ else if (question.options.some(o => o.value === raw))
230
+ return raw;
231
+ throw new Error(`Invalid answer for '${question.questionId}'; use one of the displayed options.`);
232
+ }
233
+ /** The caller owns interactivity. No stdio/environment checks or credential resolution here. */
234
+ export async function conductInterview(options) {
235
+ const now = options.now ?? (() => new Date().toISOString());
236
+ let plan = buildInterviewPlan(options.context, options.answers, now(), options.previous);
237
+ const visited = new Set(options.deferJev ? plan.unresolvedChoices.filter(q => q.questionId.startsWith('capabilities.jev.')).map(q => q.questionId) : []);
238
+ const history = [];
239
+ let active;
240
+ let block;
241
+ let dirty = Object.keys(options.answers).length > 0;
242
+ const save = async () => {
243
+ if (!dirty)
244
+ return;
245
+ await options.save(plan);
246
+ dirty = false;
247
+ };
248
+ for (;;) {
249
+ if (options.signal?.aborted)
250
+ return { plan, cancelled: true };
251
+ const questions = interviewQuestions(options.context, plan.answers);
252
+ const unanswered = new Set(unansweredPlanChoices(plan).map(q => q.questionId));
253
+ const deferred = new Set(plan.unresolvedChoices.filter(q => q.deferredAt && unanswered.has(q.questionId)).map(q => q.questionId));
254
+ const queue = [...questions].sort((a, b) => Number(deferred.has(b.questionId)) - Number(deferred.has(a.questionId)));
255
+ const question = active ? questions.find(q => q.questionId === active)
256
+ : queue.find(q => unanswered.has(q.questionId) && !visited.has(q.questionId));
257
+ active = undefined;
258
+ if (!question)
259
+ break;
260
+ if (block && block !== question.block)
261
+ await save();
262
+ block = question.block;
263
+ if (!options.interactive) {
264
+ if (question.required)
265
+ throw missingChoice(question);
266
+ if (!question.optional) {
267
+ plan.answers[question.questionId] = { value: question.default, source: 'default', answeredAt: now() };
268
+ dirty = true;
269
+ }
270
+ }
271
+ else {
272
+ const raw = (await options.ask(question, plan.answers[question.questionId])).trim();
273
+ if (raw === 'cancel')
274
+ return { plan, cancelled: true };
275
+ if (raw === 'back') {
276
+ active = history.pop();
277
+ continue;
278
+ }
279
+ if (raw.startsWith('edit ')) {
280
+ const id = raw.slice(5).trim();
281
+ if (!Object.hasOwn(plan.answers, id) || !questions.some(q => q.questionId === id))
282
+ throw new Error('Edit requires an answered question ID.');
283
+ if (plan.answers[id].source === 'explicit-flag')
284
+ throw new Error('An explicit flag takes precedence; change the flag to edit this answer.');
285
+ active = id;
286
+ continue;
287
+ }
288
+ if (raw === 'skip' || raw === 'defer' || (raw === '' && question.optional)) {
289
+ // Revisiting an answered question and skipping keeps its previous answer.
290
+ if (raw === 'defer' && !Object.hasOwn(plan.answers, question.questionId)) {
291
+ plan.unresolvedChoices.find(q => q.questionId === question.questionId).deferredAt = now();
292
+ dirty = true;
293
+ }
294
+ }
295
+ else {
296
+ let value = parseInterviewAnswer(question, raw);
297
+ if (question.topic === 'scan-opt-in' && Array.isArray(value))
298
+ value = value.map(root => resolve(options.context.workspaceRoot, String(root)));
299
+ const answer = { value, source: 'interview', answeredAt: now() };
300
+ assertPlanRedacted({ answers: { [question.questionId]: answer } });
301
+ plan.answers[question.questionId] = answer;
302
+ if (question.topic === 'scan-opt-in' && Array.isArray(value) && value.length && options.scan) {
303
+ options.context.inventory = await options.scan(value);
304
+ if (options.answersForInventory)
305
+ plan.answers = mergeSetupAnswers(plan.answers, await options.answersForInventory()).answers;
306
+ }
307
+ dirty = true;
308
+ }
309
+ history.push(question.questionId);
310
+ }
311
+ visited.add(question.questionId);
312
+ plan = buildInterviewPlan(options.context, plan.answers, now(), plan);
313
+ }
314
+ // Flags are provisionally available to every observed root while the user can
315
+ // still edit the workspace selection. Persist only the selected roots' answers
316
+ // once the interview completes, just as prompting only visits those roots.
317
+ const selectionId = questionId('targets', 'workspaces', options.context.workspaceRoot);
318
+ const selected = plan.answers[selectionId]?.value;
319
+ if (Array.isArray(selected)) {
320
+ const unselected = options.context.inventory.projects.filter(project => !selected.includes(project.root))
321
+ .map(project => `.ws-${workspaceSlug(project.root)}.`);
322
+ const answers = Object.fromEntries(Object.entries(plan.answers).filter(([id]) => id === selectionId || !unselected.some(slug => id.includes(slug))));
323
+ if (Object.keys(answers).length !== Object.keys(plan.answers).length) {
324
+ plan = buildInterviewPlan(options.context, answers, now(), plan);
325
+ dirty = true;
326
+ }
327
+ }
328
+ await save();
329
+ const required = interviewQuestions(options.context, plan.answers).find(q => q.required && !Object.hasOwn(plan.answers, q.questionId));
330
+ if (required)
331
+ throw missingChoice(required);
332
+ return { plan, cancelled: false };
333
+ }
334
+ export async function runGuidedSetup(ctx, flags, supplied, interactive, projectDir, unknownRemote = false, trackerRecommendation) {
335
+ const absolute = (root) => resolve(projectDir, root);
336
+ if (flags.workspaces)
337
+ flags.workspaces = flags.workspaces.map(absolute);
338
+ if (flags.scanRoots)
339
+ flags.scanRoots = flags.scanRoots.map(absolute);
340
+ if (flags.packageRoot)
341
+ flags.packageRoot = absolute(flags.packageRoot);
342
+ const inventory = await discoverInventory({ workspaceRoot: projectDir, roots: flags.scanRoots });
343
+ const workspaceRoot = inventory.projects.find(p => p.sources.includes('workspace'))?.root;
344
+ if (!workspaceRoot)
345
+ throw new Error('Discovery could not observe the workspace root.');
346
+ if (flags.workspaces?.some(root => !inventory.projects.some(p => p.root === root)))
347
+ throw new AiwgError({ code: 'ERR_INVALID_VALUE',
348
+ exitCode: EXIT_CODES.USAGE, message: '--workspace must name an observed root; opt in with --scan-roots if needed.' });
349
+ const force = ctx.args.includes('--force');
350
+ const settings = await Promise.all(inventory.projects.map(async (project) => ({ root: project.root,
351
+ values: existingValues(force ? null : await readAiwgConfig(project.root)) })));
352
+ const context = { inventory, workspaceRoot, unknownRemote, trackerRecommendation,
353
+ providers: [...new Set([...settings.flatMap(s => s.values.providers ?? []), ...(supplied.providers ?? []), ...Object.keys(flags.credentials).filter(p => p !== 'jev')])] };
354
+ context.jevStatus = {};
355
+ context.qualifiedWorkers = {};
356
+ // Observe existing metadata for A1's roots only. No driver import, auth read, or CLI probe.
357
+ for (const root of new Set(inventory.projects.flatMap(project => [project.root, ...project.packageRoots]))) {
358
+ try {
359
+ context.jevStatus[root] = decisionCapabilities({ cwd: root, env: process.env });
360
+ }
361
+ catch { /* An unavailable optional status must not block core setup. */ }
362
+ try {
363
+ const config = await readAiwgConfig(root);
364
+ if (!config?.parallelism?.worker_dispatch)
365
+ continue;
366
+ const models = await new ConfigLoader(root).load(config.parallelism);
367
+ if (!models.defaults.workerSelection)
368
+ continue;
369
+ const recorded = await inspectRecordedWorkerQualifications({ cwd: root, parallelism: config.parallelism,
370
+ policy: models.defaults.workerSelection });
371
+ context.qualifiedWorkers[root] = recorded.map(row => row.provider);
372
+ }
373
+ catch {
374
+ context.qualifiedWorkers[root] = [];
375
+ }
376
+ }
377
+ const id = `setup-${workspaceSlug(workspaceRoot)}`;
378
+ let previous;
379
+ // Use A2's loader for every persisted snapshot; an unreadable or invalid plan is never replaced silently.
380
+ try {
381
+ await stat(projectAiwgPath(projectDir, 'setup', 'plans', `${id}.json`));
382
+ previous = await readSetupPlan(projectDir, id);
383
+ }
384
+ catch (error) {
385
+ if (error.code !== 'ENOENT')
386
+ throw error;
387
+ }
388
+ const timestamp = new Date().toISOString();
389
+ const retained = force ? {} : previous?.answers ?? {};
390
+ const storedSettings = Object.assign({}, ...settings.map(setting => answersFromValues(context, setting.values, undefined, 'existing-setting', timestamp, [setting.root])));
391
+ // An explicitly edited setting remains edited on resume. New settings still use normal precedence.
392
+ for (const [id, answer] of Object.entries(retained))
393
+ if (answer.source === 'interview')
394
+ delete storedSettings[id];
395
+ const baseAnswers = mergeSetupAnswers(retained, storedSettings).answers;
396
+ const explicitAnswers = answersFromValues(context, supplied, flags, 'explicit-flag', timestamp);
397
+ for (const [id, answer] of Object.entries(explicitAnswers)) {
398
+ const priorValue = baseAnswers[id]?.value;
399
+ if (id.startsWith('capabilities.') && priorValue && typeof priorValue === 'object' && !Array.isArray(priorValue) &&
400
+ answer.value && typeof answer.value === 'object' && !Array.isArray(answer.value))
401
+ answer.value = { ...priorValue, ...answer.value };
402
+ }
403
+ const answers = mergeSetupAnswers(baseAnswers, explicitAnswers).answers;
404
+ let incompleteTracker;
405
+ if (unknownRemote) {
406
+ const trackerId = questionId('capabilities', 'tracker', workspaceRoot);
407
+ const tracker = answers[trackerId]?.value;
408
+ if (!tracker || typeof tracker !== 'object' || Array.isArray(tracker) || !Object.hasOwn(tracker, 'issue_provider')) {
409
+ if (!interactive)
410
+ throw missingChoice(interviewQuestions(context, answers).find(q => q.topic === 'tracker'));
411
+ // A supplied remote name does not answer the unknown provider selection.
412
+ if (tracker && typeof tracker === 'object' && !Array.isArray(tracker))
413
+ incompleteTracker = tracker;
414
+ delete answers[trackerId];
415
+ assertPlanRedacted(incompleteTracker);
416
+ }
417
+ }
418
+ if (flags.jev === 'defer') {
419
+ for (const project of inventory.projects)
420
+ delete answers[questionId('capabilities', 'jev', project.root)];
421
+ previous = buildInterviewPlan(context, answers, timestamp, previous);
422
+ for (const choice of previous.unresolvedChoices)
423
+ if (choice.questionId.startsWith('capabilities.jev.'))
424
+ choice.deferredAt = timestamp;
425
+ }
426
+ let destination;
427
+ const dryRun = ctx.dryRun || ctx.args.includes('--dry-run');
428
+ const rl = interactive ? createPromptInterface() : undefined;
429
+ try {
430
+ if (interactive)
431
+ console.log(INSTALL_METHOD_EXPLANATION);
432
+ const result = await conductInterview({ context, answers, previous, interactive, deferJev: flags.jev === 'defer', signal: ctx.signal,
433
+ scan: roots => discoverInventory({ workspaceRoot: projectDir, roots }),
434
+ answersForInventory: async () => {
435
+ const added = await Promise.all(context.inventory.projects.filter(project => !settings.some(setting => setting.root === project.root))
436
+ .map(async (project) => ({ root: project.root, values: existingValues(force ? null : await readAiwgConfig(project.root)) })));
437
+ settings.push(...added);
438
+ context.providers = [...new Set([...(context.providers ?? []), ...added.flatMap(setting => setting.values.providers ?? [])])];
439
+ return mergeSetupAnswers(...added.map(setting => answersFromValues(context, setting.values, undefined, 'existing-setting', new Date().toISOString(), [setting.root])), answersFromValues(context, supplied, flags, 'explicit-flag', new Date().toISOString())).answers;
440
+ },
441
+ ask: async (question, current) => {
442
+ console.log(`${question.questionId}: ${question.explanation}`);
443
+ if (question.topic === 'provider') {
444
+ const tool = context.inventory.tools.find(t => t.executable === question.provider);
445
+ console.log(`${question.provider}: installed=${tool?.installed ?? 'unknown'}, configured=${tool?.configured ?? 'unknown'}, authenticated=${tool?.authenticated ?? 'unknown'}, qualified=${tool?.qualified ?? 'unknown'}`);
446
+ }
447
+ if (question.input === 'credential')
448
+ console.log(`${question.provider}: A4 provider credential; env:NAME (name suggestion ${credentialEnvName(question.provider)}), file:/absolute/path, stdin [skip] [defer]`);
449
+ else
450
+ console.log(`Options: ${question.options.map(o => o.label).join(', ')}; default: ${JSON.stringify(question.default)}`);
451
+ if (current)
452
+ console.log(`Current: ${JSON.stringify(current.value)}`);
453
+ const response = await askString(rl, '[back | edit <question-id> | skip | defer | cancel] > ', 'cancel', ctx.signal);
454
+ if (question.topic === 'tracker' && incompleteTracker && ['gitea', 'github', 'local'].includes(response)) {
455
+ return JSON.stringify({ ...incompleteTracker, issue_provider: response });
456
+ }
457
+ return response;
458
+ },
459
+ save: async (plan) => { if (!dryRun)
460
+ destination = await writeSetupPlan(projectDir, plan); }, });
461
+ if (ctx.args.includes('--json'))
462
+ return { exitCode: 0, rawOutput: true, message: JSON.stringify(result.plan, null, 2) };
463
+ return { exitCode: 0, message: result.cancelled ? 'Guided setup cancelled; last saved plan retained.'
464
+ : dryRun ? 'Guided setup plan preview complete.' : destination ? `Saved setup plan: ${destination}` : 'Setup plan is unchanged.' };
465
+ }
466
+ finally {
467
+ rl?.close();
468
+ }
469
+ }
470
+ //# sourceMappingURL=guided-setup.js.map
@@ -89,7 +89,7 @@ function displayHelp() {
89
89
  ]);
90
90
  helpGroup('DISCOVERY', [
91
91
  ['discover "<phrase>"', 'Find skills/agents/commands/rules by capability'],
92
- ['show <type> <name>', 'Stream the body of an indexed artifact'],
92
+ ['show <type> <name>', 'Show a metadata card; --body fetches instructions'],
93
93
  ['versions <list|resolve|show>', 'Browse and resolve signed AIWG web resource releases'],
94
94
  ['auth <login|status|logout>', 'Authenticate for paid AIWG web resources'],
95
95
  ['corpus <subcommand>', 'Research corpus tools (snapshot, radar, profiles, curator)'],
@@ -167,7 +167,7 @@ function displayHelp() {
167
167
  console.log(` aiwg use cockpit ${ui.dimText('Install opt-in Cockpit package')}`);
168
168
  console.log(` aiwg cockpit ${ui.dimText('Launch Cockpit after install')}`);
169
169
  console.log(` aiwg discover "deploy" ${ui.dimText('Find skills by capability')}`);
170
- console.log(` aiwg show skill intake-wizard ${ui.dimText('Stream a skill body')}`);
170
+ console.log(` aiwg show skill intake-wizard ${ui.dimText('Show a skill metadata card')}`);
171
171
  console.log(` aiwg doctor ${ui.dimText('Check installation health')}`);
172
172
  console.log(` aiwg refresh ${ui.dimText('Pull latest + redeploy frameworks')}`);
173
173
  maybePrintCommunityFooter();