@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,492 @@
1
+ import { constants } from 'node:fs';
2
+ import { access, lstat, open, opendir, realpath, stat } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { basename, delimiter, isAbsolute, join, relative, resolve, sep } from 'node:path';
5
+ import { performance } from 'node:perf_hooks';
6
+ import { projectMemoryManifestPath } from '../memory/project-registry.js';
7
+ import { knownWorkerCliExecutables } from '../models/worker-dispatch-offer.js';
8
+ import { loadProviderModelCapabilities } from '../models/provider-policy.js';
9
+ import { listProviderDefinitions } from '../providers/provider-definitions.js';
10
+ export * from './plan-types.js';
11
+ export * from './interview.js';
12
+ export { migrateSetupPlan, readSetupPlan, writeSetupPlan, remainingPlanActions, unansweredPlanChoices, revalidateSetupPlan } from './plan-store.js';
13
+ export const DISCOVERY_EXCLUSIONS = [
14
+ 'node_modules', '.git', '.hg', '.svn', '.cache', '.npm', '.pnpm-store', '.yarn',
15
+ 'dist', 'build', 'out', 'coverage', '.next', '.nuxt', '.turbo', '.parcel-cache',
16
+ 'target', 'vendor', '__pycache__', '.venv', 'venv', '.tox', '.gradle', '.idea',
17
+ // Provider payloads and AIWG artifacts are inspected by known paths, never walked.
18
+ '.aiwg', '.claude', '.codex', '.agents', '.factory', '.opencode',
19
+ ];
20
+ class StopDiscovery extends Error {
21
+ }
22
+ function within(root, path) {
23
+ const rel = relative(root, path);
24
+ return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
25
+ }
26
+ function bound(value, fallback, name) {
27
+ const result = value ?? fallback;
28
+ if (!Number.isSafeInteger(result) || result < 0)
29
+ throw new RangeError(`${name} must be a non-negative safe integer`);
30
+ return result;
31
+ }
32
+ /**
33
+ * Read-only inventory. Workspace and registry roots are inspected shallowly.
34
+ * Only `roots` opts in to walking; no parent discovery, home traversal, network,
35
+ * child processes, credential reads, registry writes, or tool execution occurs.
36
+ * Nested symlinks (even in-root links) are conservatively refused. Explicit root
37
+ * aliases are canonicalized. Deadlines bound waiting for I/O; in-flight kernel
38
+ * metadata operations cannot be cancelled. This is not a sandbox against a
39
+ * concurrently hostile filesystem replacing ancestors during inspection.
40
+ */
41
+ export async function discoverInventory(options = {}) {
42
+ const limits = {
43
+ maxDepth: bound(options.maxDepth, 4, 'maxDepth'),
44
+ maxEntries: bound(options.maxEntries, 20_000, 'maxEntries'),
45
+ timeBudgetMs: bound(options.timeBudgetMs, 2_000, 'timeBudgetMs'),
46
+ maxFileBytes: bound(options.maxFileBytes, 256 * 1024, 'maxFileBytes'),
47
+ };
48
+ const now = options.now ?? (() => performance.now());
49
+ const started = now();
50
+ const result = {
51
+ projects: [], tools: [], diagnostics: [], limits,
52
+ stats: { entriesInspected: 0, elapsedMs: 0, truncated: false },
53
+ };
54
+ const report = (code, path, message) => {
55
+ result.diagnostics.push({ code, path, message });
56
+ if (['depth-limit', 'entry-limit', 'time-limit', 'file-limit'].includes(code))
57
+ result.stats.truncated = true;
58
+ };
59
+ const check = (path) => {
60
+ if (now() - started >= limits.timeBudgetMs) {
61
+ report('time-limit', path, 'Discovery time budget exhausted');
62
+ throw new StopDiscovery();
63
+ }
64
+ };
65
+ // Every filesystem operation shares the same deadline and conservative entry budget.
66
+ const io = async (path, operation) => {
67
+ check(path);
68
+ if (result.stats.entriesInspected >= limits.maxEntries) {
69
+ report('entry-limit', path, 'Discovery entry budget exhausted');
70
+ throw new StopDiscovery();
71
+ }
72
+ result.stats.entriesInspected++;
73
+ let timer;
74
+ try {
75
+ const remaining = Math.max(1, limits.timeBudgetMs - (now() - started));
76
+ const value = await Promise.race([
77
+ operation(),
78
+ new Promise((_, reject) => {
79
+ timer = setTimeout(() => {
80
+ report('time-limit', path, 'Discovery time budget exhausted during I/O');
81
+ reject(new StopDiscovery());
82
+ }, remaining);
83
+ }),
84
+ ]);
85
+ check(path);
86
+ return value;
87
+ }
88
+ finally {
89
+ if (timer)
90
+ clearTimeout(timer);
91
+ }
92
+ };
93
+ const failure = (path, error) => {
94
+ if (error instanceof StopDiscovery)
95
+ throw error;
96
+ const code = error.code;
97
+ if (code !== 'ENOENT' && code !== 'ENOTDIR')
98
+ report('inaccessible', path, `Cannot inspect path (${code ?? 'I/O error'})`);
99
+ };
100
+ // Check each component before access, so a provider directory symlink cannot
101
+ // turn a known relative config path into a read outside the selected root.
102
+ const statCache = new Map();
103
+ const safeStat = async (root, path) => {
104
+ if (!within(root, path)) {
105
+ report('outside-root', path, 'Path is outside the discovery root');
106
+ return null;
107
+ }
108
+ let current = root;
109
+ const components = relative(root, path).split(sep).filter(Boolean);
110
+ try {
111
+ const cachedStat = async (target) => {
112
+ if (statCache.has(target))
113
+ return statCache.get(target);
114
+ try {
115
+ const value = await io(target, () => lstat(target));
116
+ statCache.set(target, value);
117
+ return value;
118
+ }
119
+ catch (error) {
120
+ failure(target, error);
121
+ statCache.set(target, null);
122
+ return null;
123
+ }
124
+ };
125
+ let info = await cachedStat(root);
126
+ if (!info)
127
+ return null;
128
+ for (const component of components) {
129
+ current = join(current, component);
130
+ info = await cachedStat(current);
131
+ if (!info)
132
+ return null;
133
+ if (info.isSymbolicLink()) {
134
+ report('symlink-skipped', current, 'Nested symlinks are not followed');
135
+ return null;
136
+ }
137
+ }
138
+ return info;
139
+ }
140
+ catch (error) {
141
+ failure(current, error);
142
+ return null;
143
+ }
144
+ };
145
+ const readText = async (root, path) => {
146
+ const info = await safeStat(root, path);
147
+ if (!info?.isFile())
148
+ return null;
149
+ if (info.size > limits.maxFileBytes) {
150
+ report('file-limit', path, 'Metadata exceeds maxFileBytes');
151
+ return null;
152
+ }
153
+ try {
154
+ // Closing lives inside the timed operation, so a late open/read still closes.
155
+ const content = await io(path, async () => {
156
+ const file = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
157
+ try {
158
+ const buffer = Buffer.alloc(limits.maxFileBytes + 1);
159
+ const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
160
+ return bytesRead > limits.maxFileBytes ? null : buffer.toString('utf8', 0, bytesRead);
161
+ }
162
+ finally {
163
+ await file.close();
164
+ }
165
+ });
166
+ if (content === null)
167
+ report('file-limit', path, 'Metadata grew beyond maxFileBytes');
168
+ return content;
169
+ }
170
+ catch (error) {
171
+ failure(path, error);
172
+ return null;
173
+ }
174
+ };
175
+ const readJson = async (root, path) => {
176
+ const text = await readText(root, path);
177
+ if (text === null)
178
+ return null;
179
+ try {
180
+ const value = JSON.parse(text);
181
+ if (!value || typeof value !== 'object' || Array.isArray(value))
182
+ throw new Error();
183
+ return value;
184
+ }
185
+ catch {
186
+ report('invalid-metadata', path, 'Expected a JSON object');
187
+ return null;
188
+ }
189
+ };
190
+ const exclusions = new Set([...DISCOVERY_EXCLUSIONS, ...options.excludeNames ?? []]);
191
+ const definitions = listProviderDefinitions().filter(d => d.id !== 'generic');
192
+ for (const definition of definitions) {
193
+ for (const path of Object.values(definition.paths.artifacts)) {
194
+ const directory = path?.split('/')[0];
195
+ if (directory?.startsWith('.') && directory !== '..')
196
+ exclusions.add(directory);
197
+ }
198
+ }
199
+ const projects = new Map();
200
+ const inspected = new Set();
201
+ const inspect = async (root, boundary, source, seed) => {
202
+ const existing = projects.get(root);
203
+ if (existing) {
204
+ if (!existing.sources.includes(source))
205
+ existing.sources.push(source);
206
+ return;
207
+ }
208
+ if (inspected.has(root))
209
+ return;
210
+ inspected.add(root);
211
+ const gitDiagnosticStart = result.diagnostics.length;
212
+ const git = await safeStat(boundary, join(root, '.git'));
213
+ let vcs = git?.isDirectory() ? 'git'
214
+ : git || result.diagnostics.length > gitDiagnosticStart ? 'unknown' : 'none';
215
+ if (git?.isFile()) {
216
+ const marker = await readText(boundary, join(root, '.git'));
217
+ if (marker && /^gitdir:[ \t]*\S[^\r\n]*\r?\n?$/.test(marker))
218
+ vcs = 'worktree';
219
+ else
220
+ report('invalid-metadata', join(root, '.git'), 'Unrecognized Git marker; VCS is unknown');
221
+ }
222
+ const manifestPath = join(root, 'package.json');
223
+ const manifestInfo = await safeStat(boundary, manifestPath);
224
+ const manifest = manifestInfo?.isFile() ? await readJson(boundary, manifestPath) : null;
225
+ const aiwg = await safeStat(boundary, join(root, '.aiwg'));
226
+ const config = await safeStat(boundary, join(root, '.aiwg', 'aiwg.config'));
227
+ const aiwgConfig = config?.isFile() ? await readJson(boundary, join(root, '.aiwg', 'aiwg.config')) : null;
228
+ const context = await safeStat(boundary, join(root, 'AIWG.md'));
229
+ const markers = [];
230
+ for (const marker of ['pnpm-workspace.yaml', 'lerna.json', 'Cargo.toml', 'pyproject.toml', 'go.mod', 'CMakeLists.txt']) {
231
+ if ((await safeStat(boundary, join(root, marker)))?.isFile())
232
+ markers.push(marker);
233
+ }
234
+ if (!seed && !git && !manifestInfo && !aiwg && !markers.length)
235
+ return;
236
+ const rawWorkspaces = manifest?.workspaces;
237
+ const workspaces = Array.isArray(rawWorkspaces) ? rawWorkspaces
238
+ : rawWorkspaces && typeof rawWorkspaces === 'object' ? rawWorkspaces.packages : undefined;
239
+ const patterns = Array.isArray(workspaces) ? workspaces.filter((p) => typeof p === 'string') : [];
240
+ const entry = {
241
+ root, sources: [source], vcs, layout: 'project',
242
+ packageManifest: manifestInfo?.isFile() ? manifestPath : null,
243
+ workspacePatterns: patterns, packageRoots: [],
244
+ aiwg: { directory: !!aiwg?.isDirectory(), configFile: config?.isFile() ? join(root, '.aiwg', 'aiwg.config') : null,
245
+ contextFile: context?.isFile() ? join(root, 'AIWG.md') : null },
246
+ providerFiles: [],
247
+ };
248
+ if (patterns.length || markers.includes('pnpm-workspace.yaml') || markers.includes('lerna.json'))
249
+ entry.layout = 'monorepo';
250
+ if (rawWorkspaces !== undefined && (!Array.isArray(workspaces) || patterns.length !== workspaces.length)) {
251
+ entry.layout = 'ambiguous';
252
+ report('ambiguous', root, 'Malformed workspace declaration; root/package choice requires confirmation');
253
+ }
254
+ projects.set(root, entry);
255
+ if (Array.isArray(aiwgConfig?.providers)) {
256
+ const capabilities = loadProviderModelCapabilities();
257
+ for (const provider of aiwgConfig.providers) {
258
+ if (typeof provider !== 'string')
259
+ continue;
260
+ const capability = capabilities.providers[provider];
261
+ const invocation = (capability?.workerAdapter ?? capability?.workerHealthAdapter)?.invocation;
262
+ if (invocation?.kind !== 'process')
263
+ continue;
264
+ const tool = result.tools.find(t => t.executable === invocation.executable);
265
+ if (tool && !options.toolEvidence?.[tool.executable]?.configured) {
266
+ tool.configured = 'yes';
267
+ tool.evidence.push(`Provider selected in ${entry.aiwg.configFile}`);
268
+ }
269
+ }
270
+ }
271
+ for (const definition of definitions) {
272
+ const paths = new Map();
273
+ for (const path of Object.values(definition.paths.artifacts))
274
+ if (path)
275
+ paths.set(path, 'artifacts');
276
+ if (definition.paths.kernelSkills)
277
+ paths.set(definition.paths.kernelSkills, 'artifacts');
278
+ for (const path of definition.context.startupFiles)
279
+ paths.set(path, 'context');
280
+ if (definition.paths.configFile)
281
+ paths.set(definition.paths.configFile, 'config');
282
+ for (const [path, kind] of paths) {
283
+ // Home/user paths in the registry are never expanded or inspected here.
284
+ if (path.startsWith('~') || isAbsolute(path))
285
+ continue;
286
+ const target = resolve(root, path);
287
+ const info = await safeStat(root, target);
288
+ if (info && (kind === 'artifacts' ? info.isDirectory() : info.isFile())) {
289
+ entry.providerFiles.push({ provider: definition.id, path: target, kind });
290
+ }
291
+ }
292
+ }
293
+ };
294
+ const canonicalRoot = async (path) => {
295
+ try {
296
+ const canonical = await io(path, () => realpath(resolve(path)));
297
+ if (!(await io(canonical, () => lstat(canonical))).isDirectory()) {
298
+ report('inaccessible', path, 'Discovery root is not a directory');
299
+ return null;
300
+ }
301
+ return canonical;
302
+ }
303
+ catch (error) {
304
+ if (error.code === 'ENOENT')
305
+ report('inaccessible', path, 'Discovery root does not exist');
306
+ failure(path, error);
307
+ return null;
308
+ }
309
+ };
310
+ const walkedDepth = new Map();
311
+ const walk = async (root, boundary, depth) => {
312
+ const remaining = limits.maxDepth - depth;
313
+ if ((walkedDepth.get(root) ?? -1) >= remaining)
314
+ return;
315
+ walkedDepth.set(root, remaining);
316
+ await inspect(root, boundary, 'selected', depth === 0);
317
+ if (depth >= limits.maxDepth) {
318
+ report('depth-limit', root, 'No children inspected at maximum depth');
319
+ return;
320
+ }
321
+ try {
322
+ // Open/read incrementally: never materialize an unbounded readdir result.
323
+ let directory;
324
+ let abandoned = false;
325
+ try {
326
+ directory = await io(root, async () => {
327
+ const handle = await opendir(root, { bufferSize: 1 });
328
+ if (abandoned)
329
+ await handle.close();
330
+ else
331
+ directory = handle;
332
+ return handle;
333
+ });
334
+ while (true) {
335
+ const child = await io(root, async () => {
336
+ try {
337
+ return await directory.read();
338
+ }
339
+ finally {
340
+ if (abandoned)
341
+ void directory.close().catch(() => { });
342
+ }
343
+ });
344
+ if (!child)
345
+ break;
346
+ const path = join(root, child.name);
347
+ if (exclusions.has(child.name)) {
348
+ report('excluded', path, 'Excluded directory or metadata name');
349
+ continue;
350
+ }
351
+ if (child.isSymbolicLink()) {
352
+ report('symlink-skipped', path, 'Nested symlinks are not followed');
353
+ continue;
354
+ }
355
+ if (child.isDirectory() && (await safeStat(boundary, path))?.isDirectory())
356
+ await walk(path, boundary, depth + 1);
357
+ }
358
+ }
359
+ finally {
360
+ abandoned = true;
361
+ // Cleanup is not subject to the exhausted budget.
362
+ if (directory)
363
+ void directory.close().catch(() => { });
364
+ }
365
+ }
366
+ catch (error) {
367
+ failure(root, error);
368
+ }
369
+ };
370
+ try {
371
+ // Initialize every tool as unknown, including if an earlier scan hits a limit.
372
+ result.tools = knownWorkerCliExecutables().map(executable => ({
373
+ executable, path: null, installed: 'unknown', configured: 'unknown',
374
+ authenticated: 'unknown', qualified: 'unknown', version: null,
375
+ ...options.toolEvidence?.[executable],
376
+ evidence: [...options.toolEvidence?.[executable]?.evidence ?? []],
377
+ }));
378
+ const workspace = await canonicalRoot(options.workspaceRoot ?? process.cwd());
379
+ if (workspace)
380
+ await inspect(workspace, workspace, 'workspace', true);
381
+ let seeds = options.registrySeeds || [];
382
+ if (options.registrySeeds === undefined) {
383
+ const path = projectMemoryManifestPath();
384
+ // Read only the registry manifest, never its memory roots, indices or home tree.
385
+ const registry = await readJson(resolve(path, '..'), path);
386
+ if (registry) {
387
+ if (registry.version === 1 && Array.isArray(registry.projects)) {
388
+ seeds = registry.projects.flatMap((entry) => {
389
+ if (entry && typeof entry === 'object' && Array.isArray(entry.workspaceRoots)) {
390
+ return entry.workspaceRoots.filter((r) => typeof r === 'string');
391
+ }
392
+ report('invalid-metadata', path, 'Invalid registry project entry');
393
+ return [];
394
+ });
395
+ }
396
+ else
397
+ report('invalid-metadata', path, 'Expected project memory manifest version 1');
398
+ }
399
+ }
400
+ for (const seed of seeds) {
401
+ const root = await canonicalRoot(seed === '~' ? homedir() : seed.startsWith('~/') ? join(homedir(), seed.slice(2)) : seed);
402
+ if (root)
403
+ await inspect(root, root, 'registry', true);
404
+ }
405
+ for (const selected of options.roots ?? []) {
406
+ const root = await canonicalRoot(selected);
407
+ if (!root)
408
+ continue;
409
+ if (exclusions.has(basename(root))) {
410
+ report('excluded', root, 'Selected root matches a mandatory or caller exclusion');
411
+ continue;
412
+ }
413
+ // Even explicitly selecting home is unsafe for an installer scan. Select its project folders.
414
+ if (root === resolve(homedir()) || root === resolve(sep)) {
415
+ report('home-scan-refused', root, 'Select individual project directories instead of a home/filesystem root');
416
+ continue;
417
+ }
418
+ await walk(root, root, 0);
419
+ }
420
+ for (const tool of result.tools) {
421
+ let found = false;
422
+ let uncertain = false;
423
+ for (const dir of (options.pathEnv ?? process.env.PATH ?? '').split(delimiter).filter(Boolean)) {
424
+ // Direct PATH checks only; executable symlinks are legitimate package-manager shims.
425
+ for (const name of process.platform === 'win32' ? [`${tool.executable}.exe`, `${tool.executable}.cmd`, `${tool.executable}.bat`, tool.executable] : [tool.executable]) {
426
+ const target = resolve(dir, name);
427
+ try {
428
+ let info;
429
+ try {
430
+ info = await io(target, () => stat(target));
431
+ }
432
+ catch (error) {
433
+ const code = error.code;
434
+ if (code !== 'ENOENT' && code !== 'ENOTDIR')
435
+ uncertain = true;
436
+ throw error;
437
+ }
438
+ if (!info.isFile())
439
+ continue;
440
+ await io(target, () => access(target, constants.X_OK));
441
+ tool.path = target;
442
+ tool.installed = 'yes';
443
+ tool.evidence.push(`Executable on PATH: ${target}`);
444
+ found = true;
445
+ break;
446
+ }
447
+ catch (error) {
448
+ failure(target, error);
449
+ }
450
+ }
451
+ if (found)
452
+ break;
453
+ }
454
+ if (!found)
455
+ tool.installed = uncertain ? 'unknown' : 'no';
456
+ }
457
+ }
458
+ catch (error) {
459
+ if (!(error instanceof StopDiscovery))
460
+ throw error;
461
+ }
462
+ result.projects = [...projects.values()].sort((a, b) => a.root.localeCompare(b.root));
463
+ try {
464
+ if (!result.diagnostics.some(d => d.code === 'time-limit'))
465
+ for (const project of result.projects) {
466
+ const children = [];
467
+ for (const candidate of result.projects) {
468
+ check(project.root);
469
+ if (candidate.root !== project.root && within(project.root, candidate.root) && candidate.packageManifest)
470
+ children.push(candidate);
471
+ }
472
+ project.packageRoots = children.map(p => p.root);
473
+ if (children.length && project.layout !== 'monorepo') {
474
+ project.layout = 'ambiguous';
475
+ report('ambiguous', project.root, 'Nested packages without an unambiguous workspace declaration; confirm root or package');
476
+ }
477
+ if (project.layout === 'monorepo') {
478
+ report('ambiguous', project.root, 'Workspace root and package roots require caller selection; packageRoots lists observed candidates only');
479
+ for (const child of children)
480
+ if (child.layout === 'project')
481
+ child.layout = 'package';
482
+ }
483
+ }
484
+ }
485
+ catch (error) {
486
+ if (!(error instanceof StopDiscovery))
487
+ throw error;
488
+ }
489
+ result.stats.elapsedMs = Math.max(0, now() - started);
490
+ return result;
491
+ }
492
+ //# sourceMappingURL=index.js.map