@aiwg/cli 2026.8.8 → 2026.8.11

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 (70) hide show
  1. package/README.md +23 -8
  2. package/THIRD_PARTY_NOTICES.md +35 -0
  3. package/agentic/code/providers/capability-matrix.yaml +3 -3
  4. package/bin/aiwg.mjs +125 -0
  5. package/dist/src/agents/packaged-agent-inventory.js +37 -1
  6. package/dist/src/artifacts/backends/graphology-backend.js +4 -3
  7. package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
  8. package/dist/src/artifacts/cli.js +2 -2
  9. package/dist/src/artifacts/corpus-tools/cli.js +27 -0
  10. package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
  11. package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
  12. package/dist/src/artifacts/discover-facets.js +2 -2
  13. package/dist/src/artifacts/embedding-index.js +9 -8
  14. package/dist/src/artifacts/fortemi-core-sync.js +23 -8
  15. package/dist/src/artifacts/graph-backend.js +2 -2
  16. package/dist/src/artifacts/query-engine.js +21 -7
  17. package/dist/src/artifacts/repair.js +47 -0
  18. package/dist/src/artifacts/types.js +3 -3
  19. package/dist/src/cli/command-log.js +2 -2
  20. package/dist/src/cli/handlers/artifacts.js +50 -1
  21. package/dist/src/cli/handlers/cost-report.js +71 -0
  22. package/dist/src/cli/handlers/evidence.js +78 -0
  23. package/dist/src/cli/handlers/help.js +9 -0
  24. package/dist/src/cli/handlers/index.js +8 -3
  25. package/dist/src/cli/handlers/local-executor.js +4 -3
  26. package/dist/src/cli/handlers/refresh.js +63 -17
  27. package/dist/src/cli/handlers/regenerate.js +3 -3
  28. package/dist/src/cli/handlers/serve.js +15 -36
  29. package/dist/src/cli/handlers/setup-manifest.js +8 -1
  30. package/dist/src/cli/handlers/use.js +272 -70
  31. package/dist/src/cli/handlers/utilities.js +149 -0
  32. package/dist/src/cli/handlers/workspace.js +10 -0
  33. package/dist/src/cli/help-generator.js +2 -1
  34. package/dist/src/cli/router.js +4 -1
  35. package/dist/src/cli/services/deployment-verification.js +596 -0
  36. package/dist/src/cli/skill-usage.js +2 -2
  37. package/dist/src/cli/workflow-orchestrator.js +1 -1
  38. package/dist/src/cli/workspace-signals.js +2 -2
  39. package/dist/src/config/aiwg-config.js +54 -27
  40. package/dist/src/config/cli.js +3 -3
  41. package/dist/src/config/project-artifacts-health.js +2 -0
  42. package/dist/src/config/project-artifacts-health.mjs +123 -0
  43. package/dist/src/config/project-artifacts-runtime.mjs +16 -0
  44. package/dist/src/config/project-artifacts.js +2 -1
  45. package/dist/src/cost/fleet-report.js +329 -0
  46. package/dist/src/evidence/bundle.js +256 -0
  47. package/dist/src/extensions/commands/definitions.js +77 -25
  48. package/dist/src/extensions/deployment-registration.js +6 -4
  49. package/dist/src/features/catalog.js +26 -0
  50. package/dist/src/features/cli.js +1 -3
  51. package/dist/src/features/runtime.js +17 -1
  52. package/dist/src/issues/cli.js +91 -7
  53. package/dist/src/mcp/server.mjs +1 -1
  54. package/dist/src/ops/registry.js +2 -2
  55. package/dist/src/policy/authorization.js +2 -2
  56. package/dist/src/providers/capability-matrix.yaml +3 -3
  57. package/dist/src/providers/provider-definitions.js +7 -5
  58. package/dist/src/providers/provider-definitions.mjs +1 -1
  59. package/dist/src/serve/pty-bridge.js +2 -8
  60. package/dist/src/serve/screen-reader.js +3 -6
  61. package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
  62. package/dist/src/smiths/context-pipeline/finalization.js +18 -5
  63. package/dist/src/smiths/context-pipeline/generator.js +2 -2
  64. package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
  65. package/package.json +2 -1
  66. package/tools/agents/deploy-agents.mjs +10 -11
  67. package/tools/agents/providers/base.mjs +61 -7
  68. package/tools/agents/providers/openclaw.mjs +5 -2
  69. package/tools/agents/providers/windsurf.mjs +13 -24
  70. package/tools/skills/deploy-skills-codex.mjs +21 -5
@@ -0,0 +1,596 @@
1
+ import { access, readFile, readdir } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { loadGraphIndexFile } from '../../artifacts/index-reader.js';
4
+ import { readAiwgConfig } from '../../config/aiwg-config.js';
5
+ import { readUserRegistry } from '../../config/user-registry.js';
6
+ import { getProviderDefinition, normalizeProviderDefinitionId, resolveProviderPathValue, } from '../../providers/provider-definitions.js';
7
+ import { diagnoseWorkspaceContext, providerContextContract, } from '../../smiths/context-pipeline/workspace-context.js';
8
+ import { USER_SCOPE_PATHS } from '../scope-resolver.js';
9
+ const RESTART_NOTICES = {
10
+ claude: {
11
+ action: 'Restart Claude Code so the running session reloads deployed agents and skills.',
12
+ reason: 'Claude Code reads its agent and skill registries when a session starts.',
13
+ },
14
+ codex: {
15
+ action: 'Restart or reopen Codex in this workspace so it reloads deployed agents and skills.',
16
+ reason: 'Codex scans project agent and skill registries when a session starts.',
17
+ },
18
+ copilot: {
19
+ action: 'Reload the VS Code window so Copilot reloads workspace agents and instructions.',
20
+ reason: 'Copilot caches workspace agent definitions until the VS Code window reloads.',
21
+ },
22
+ cursor: {
23
+ action: 'Reload the Cursor workspace so it reloads agents and rules.',
24
+ reason: 'Cursor reads workspace agents and rules when the workspace opens.',
25
+ },
26
+ factory: {
27
+ action: 'Restart the Factory droid runtime so it reloads deployed droids.',
28
+ reason: 'Factory loads its droid registry when the runtime starts.',
29
+ },
30
+ opencode: {
31
+ action: 'Restart the OpenCode session so it reloads deployed agents.',
32
+ reason: 'OpenCode scans its agent directory when the session starts.',
33
+ },
34
+ openclaw: {
35
+ action: 'Restart OpenClaw so it reloads its home-directory registry.',
36
+ reason: 'OpenClaw loads its home-directory registry when the process starts.',
37
+ },
38
+ warp: {
39
+ action: 'Open a fresh Warp tab so it reloads project context.',
40
+ reason: 'Warp reads project context when a tab starts.',
41
+ },
42
+ windsurf: {
43
+ action: 'Reload Devin Desktop so it reparses project context.',
44
+ reason: 'Devin Desktop reads the Windsurf-compatible project context when the workspace opens.',
45
+ },
46
+ };
47
+ const INVENTORY_TYPE_ORDER = [
48
+ 'agent',
49
+ 'skill',
50
+ 'command',
51
+ 'rule',
52
+ 'behavior',
53
+ 'template',
54
+ 'flow',
55
+ 'runbook',
56
+ 'schema',
57
+ ];
58
+ export function normalizeFrameworkDiscoveryInventory(stats) {
59
+ const byType = {};
60
+ for (const type of INVENTORY_TYPE_ORDER)
61
+ byType[type] = Number(stats.byType[type] ?? 0);
62
+ for (const type of Object.keys(stats.byType).sort()) {
63
+ if (!(type in byType))
64
+ byType[type] = Number(stats.byType[type] ?? 0);
65
+ }
66
+ return {
67
+ graph: 'framework',
68
+ totalArtifacts: stats.totalArtifacts,
69
+ byType,
70
+ builtAt: stats.builtAt,
71
+ };
72
+ }
73
+ function frameworkDiscoveryInventory(frameworkRoot) {
74
+ const stats = loadGraphIndexFile(frameworkRoot, 'stats.json', 'framework');
75
+ return stats ? normalizeFrameworkDiscoveryInventory(stats) : null;
76
+ }
77
+ const BUNDLE_INDEX_TOKENS = {
78
+ all: [],
79
+ sdlc: ['sdlc-complete'],
80
+ marketing: ['marketing'],
81
+ 'media-curator': ['media-curator'],
82
+ research: ['research-complete'],
83
+ forensics: ['forensics-complete'],
84
+ dfir: ['forensics-complete'],
85
+ 'security-engineering': ['security-engineering'],
86
+ ops: ['ops-complete'],
87
+ validation: ['validation-complete'],
88
+ 'knowledge-base': ['knowledge-base'],
89
+ };
90
+ function finding(provider, id, severity, message, remediation, evidence) {
91
+ return { id, provider, severity, message, remediation, evidence };
92
+ }
93
+ async function exists(candidate) {
94
+ if (!candidate)
95
+ return false;
96
+ return access(candidate).then(() => true).catch(() => false);
97
+ }
98
+ async function countEntries(candidate) {
99
+ if (!candidate)
100
+ return 0;
101
+ try {
102
+ return (await readdir(candidate, { withFileTypes: true }))
103
+ .filter((entry) => !entry.name.startsWith('.'))
104
+ .length;
105
+ }
106
+ catch {
107
+ return 0;
108
+ }
109
+ }
110
+ function emptyCounts() {
111
+ return { agents: 0, commands: 0, skills: 0, rules: 0, behaviors: 0 };
112
+ }
113
+ function phase(id, state, required, summary, evidence) {
114
+ return { id, state, required, summary, evidence };
115
+ }
116
+ function classifyOutcome(findings, restartRequired) {
117
+ if (findings.some((item) => item.severity === 'blocking'))
118
+ return 'failed';
119
+ if (findings.some((item) => item.severity === 'advisory'))
120
+ return 'degraded';
121
+ return restartRequired ? 'ready-restart-required' : 'ready';
122
+ }
123
+ function indexContainsRequestedBundles(index, requestedBundles) {
124
+ if (requestedBundles.includes('all'))
125
+ return Object.keys(index.entries).length > 0;
126
+ const entryPaths = Object.keys(index.entries);
127
+ return requestedBundles.every((bundle) => {
128
+ const tokens = BUNDLE_INDEX_TOKENS[bundle] ?? [bundle];
129
+ return tokens.some((token) => entryPaths.some((entryPath) => entryPath.includes(token)));
130
+ });
131
+ }
132
+ async function readManagedMarker(candidate) {
133
+ try {
134
+ return (await readFile(candidate, 'utf8')).includes('<!-- aiwg-managed -->');
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ async function collectRegistryFindings(options, provider, actualCounts) {
141
+ const findings = [];
142
+ const projectConfig = await readAiwgConfig(options.projectRoot);
143
+ const useUserRegistry = options.scope === 'user' && provider !== 'openhuman';
144
+ const userRegistry = useUserRegistry ? await readUserRegistry() : null;
145
+ for (const bundle of options.requestedBundles) {
146
+ const projectRecord = projectConfig?.installed[bundle]?.deployedTo[provider];
147
+ const userRecord = userRegistry?.installed[bundle]?.deployedTo[provider];
148
+ const record = useUserRegistry ? userRecord : projectRecord;
149
+ if (!record) {
150
+ findings.push(finding(provider, `registry-missing:${bundle}`, 'blocking', `Installed-state record is missing for '${bundle}' on ${provider} at ${options.scope} scope.`, `Re-run aiwg use ${bundle} --provider ${provider}${options.scope === 'user' ? ' --scope user' : ''}.`));
151
+ continue;
152
+ }
153
+ const comparedTypes = ['agents', 'commands', 'skills', 'rules'];
154
+ for (const type of comparedTypes) {
155
+ const recorded = Number(record[type] ?? 0);
156
+ if (!Number.isSafeInteger(recorded) || recorded < 0) {
157
+ findings.push(finding(provider, `registry-count-invalid:${bundle}:${type}`, 'blocking', `Installed-state count for '${bundle}' ${type} is invalid: ${String(record[type])}.`, `Re-run aiwg use ${bundle} --provider ${provider} to repair the managed deployment.`, { bundle, type, recorded: record[type] }));
158
+ }
159
+ }
160
+ const recordedTotal = comparedTypes.reduce((sum, type) => sum + Number(record[type] ?? 0), 0);
161
+ const actualTotal = Object.values(actualCounts).reduce((sum, count) => sum + count, 0);
162
+ if (recordedTotal > 0 && actualTotal === 0) {
163
+ findings.push(finding(provider, `registry-artifacts-missing:${bundle}`, 'blocking', `Installed state records artifacts for '${bundle}', but no managed artifacts were found for ${provider}.`, `Re-run aiwg use ${bundle} --provider ${provider} to repair the managed deployment.`, { bundle, recordedTotal, actualTotal }));
164
+ }
165
+ if (useUserRegistry && !projectRecord) {
166
+ findings.push(finding(provider, `project-registry-missing:${bundle}`, 'advisory', `Project registry does not describe the additive user-scope deployment for '${bundle}'.`, 'Run aiwg status --probe --json from the originating project to inspect both registries.'));
167
+ }
168
+ }
169
+ return findings;
170
+ }
171
+ export async function verifyProviderDeployment(options) {
172
+ const normalized = normalizeProviderDefinitionId(options.provider) ?? options.provider;
173
+ const definition = getProviderDefinition(normalized);
174
+ const findings = [];
175
+ const counts = emptyCounts();
176
+ const restartNotice = RESTART_NOTICES[normalized] ?? null;
177
+ const restartAction = restartNotice?.action ?? null;
178
+ const restartReason = restartNotice?.reason ?? null;
179
+ const restartRequired = restartAction !== null;
180
+ if (!definition) {
181
+ findings.push(finding(normalized, 'provider-unknown', 'blocking', `No provider definition is available for '${options.provider}'.`, 'Choose a supported provider or repair the project-local provider adapter.'));
182
+ }
183
+ if ((options.deploymentExitCode ?? 0) !== 0) {
184
+ findings.push(finding(normalized, 'deployment-command-failed', 'blocking', options.deploymentMessage || `Deployment exited with code ${options.deploymentExitCode}.`, 'Review the deployment error, correct it, and re-run the same aiwg use command.', { exitCode: options.deploymentExitCode }));
185
+ }
186
+ if (!(await exists(options.projectRoot))) {
187
+ findings.push(finding(normalized, 'project-root-missing', 'blocking', `Resolved project root does not exist: ${options.projectRoot}`, 'Select an existing project root and run aiwg use again.'));
188
+ }
189
+ if (definition) {
190
+ const artifactPaths = options.scope === 'user'
191
+ ? USER_SCOPE_PATHS[normalized] ?? definition.paths.artifacts
192
+ : definition.paths.artifacts;
193
+ for (const type of ['agents', 'commands', 'skills', 'rules', 'behaviors']) {
194
+ const resolved = resolveProviderPathValue(artifactPaths[type], options.projectRoot);
195
+ counts[type] = await countEntries(resolved);
196
+ }
197
+ const resolvedSkillsPath = resolveProviderPathValue(artifactPaths.skills, options.projectRoot);
198
+ const kernelPath = options.scope === 'user'
199
+ ? ''
200
+ : resolveProviderPathValue(definition.paths.kernelSkills, options.projectRoot);
201
+ const kernelCount = await countEntries(kernelPath);
202
+ if (kernelPath && kernelPath !== resolvedSkillsPath)
203
+ counts.skills += kernelCount;
204
+ const artifactTotal = Object.values(counts).reduce((sum, value) => sum + value, 0);
205
+ if (artifactTotal === 0) {
206
+ findings.push(finding(normalized, 'provider-artifacts-missing', 'blocking', `No deployed provider or kernel artifacts were found for ${normalized}.`, `Re-run aiwg use ${options.requestedBundles[0] ?? 'all'} --provider ${normalized}.`, { kernelPath, kernelCount, counts }));
207
+ }
208
+ }
209
+ findings.push(...await collectRegistryFindings(options, normalized, counts));
210
+ const projectConfig = await readAiwgConfig(options.projectRoot);
211
+ const scopedRegistry = options.scope === 'user'
212
+ ? await readUserRegistry()
213
+ : projectConfig;
214
+ const projectLocalBundles = options.requestedBundles.filter((bundle) => scopedRegistry?.installed[bundle]?.source === 'project-local');
215
+ const frameworkBundles = options.requestedBundles.filter((bundle) => !projectLocalBundles.includes(bundle));
216
+ const indexesToVerify = [];
217
+ if (frameworkBundles.length > 0) {
218
+ indexesToVerify.push({
219
+ index: loadGraphIndexFile(options.frameworkRoot, 'metadata.json', 'framework'),
220
+ stats: loadGraphIndexFile(options.frameworkRoot, 'stats.json', 'framework'),
221
+ graph: 'framework',
222
+ bundles: frameworkBundles,
223
+ });
224
+ }
225
+ if (projectLocalBundles.length > 0) {
226
+ const graph = options.scope === 'user' ? 'user' : 'project';
227
+ indexesToVerify.push({
228
+ index: loadGraphIndexFile(options.projectRoot, 'metadata.json', graph),
229
+ graph,
230
+ bundles: projectLocalBundles,
231
+ });
232
+ }
233
+ for (const { index, stats, graph, bundles } of indexesToVerify) {
234
+ if (!index || !index.entries || Object.keys(index.entries).length === 0) {
235
+ findings.push(finding(normalized, `index-unreadable:${graph}`, 'blocking', `The ${graph} capability index is missing, unreadable, or empty.`, `Run aiwg index build --graph ${graph}, then re-run the same aiwg use command.`));
236
+ continue;
237
+ }
238
+ if (graph === 'framework') {
239
+ if (!stats || !stats.byType || !Number.isSafeInteger(stats.totalArtifacts)) {
240
+ findings.push(finding(normalized, 'index-stats-unreadable:framework', 'blocking', 'The framework discovery inventory is missing or unreadable.', 'Run aiwg index build --graph framework, then re-run the same aiwg use command.'));
241
+ }
242
+ else {
243
+ const counts = Object.values(stats.byType);
244
+ const validCounts = counts.every((count) => Number.isSafeInteger(count) && count >= 0);
245
+ const countedTotal = counts.reduce((sum, count) => sum + count, 0);
246
+ if (!validCounts || stats.totalArtifacts < 0 || countedTotal !== stats.totalArtifacts) {
247
+ findings.push(finding(normalized, 'index-stats-invalid:framework', 'blocking', 'The framework discovery inventory contains invalid or inconsistent counts.', 'Run aiwg index build --graph framework, then re-run the same aiwg use command.', { totalArtifacts: stats.totalArtifacts, countedTotal }));
248
+ }
249
+ }
250
+ }
251
+ const entryPaths = Object.keys(index.entries);
252
+ const surfacePresent = graph === 'framework'
253
+ ? indexContainsRequestedBundles(index, bundles)
254
+ : bundles.every((bundle) => entryPaths.some((entryPath) => entryPath.includes(bundle)));
255
+ if (!surfacePresent) {
256
+ findings.push(finding(normalized, `index-surface-missing:${graph}`, 'blocking', `The ${graph} capability index does not contain the requested bundle surface.`, `Rebuild the ${graph} index from the selected AIWG source and re-run aiwg use.`, { requestedBundles: bundles, entryCount: entryPaths.length }));
257
+ }
258
+ if (options.invocationStartedAt) {
259
+ const builtAt = Date.parse(index.builtAt);
260
+ const startedAt = Date.parse(options.invocationStartedAt);
261
+ if (!Number.isFinite(builtAt) || builtAt + 2_000 < startedAt) {
262
+ findings.push(finding(normalized, `index-stale:${graph}`, 'blocking', `The ${graph} capability index was not refreshed during this deployment.`, `Re-run aiwg use after correcting the ${graph} index build failure.`, { graph, builtAt: index.builtAt, invocationStartedAt: options.invocationStartedAt }));
263
+ }
264
+ }
265
+ }
266
+ if (options.contextOptOut) {
267
+ findings.push(finding(normalized, 'context-opt-out', 'advisory', 'Canonical context verification was intentionally skipped by an explicit context opt-out.', 'Run aiwg regenerate when canonical project context is desired.'));
268
+ }
269
+ else {
270
+ const contextContract = providerContextContract(normalized);
271
+ const providerContextUnsupported = contextContract?.loadMode === 'unsupported';
272
+ if (providerContextUnsupported) {
273
+ findings.push(finding(normalized, 'context-provider-unsupported', 'advisory', `${normalized} has no verified project-local automatic context loader; canonical context remains available for audit and explicit use.`, 'Use the provider home-scope adapter and inspect WORKSPACE.md explicitly when project context is needed.'));
274
+ }
275
+ const requiredContext = [
276
+ path.join(options.projectRoot, 'WORKSPACE.md'),
277
+ path.join(options.projectRoot, 'AIWG.md'),
278
+ path.join(options.projectRoot, '.aiwg', 'AIWG.md'),
279
+ ];
280
+ for (const contextPath of requiredContext) {
281
+ if (!(await exists(contextPath))) {
282
+ findings.push(finding(normalized, `context-missing:${path.relative(options.projectRoot, contextPath)}`, providerContextUnsupported ? 'advisory' : 'blocking', `Required canonical context file is missing: ${path.relative(options.projectRoot, contextPath)}`, 'Re-run aiwg regenerate or the same aiwg use command.'));
283
+ }
284
+ }
285
+ const managedContextCount = (await Promise.all(requiredContext.map(readManagedMarker)))
286
+ .filter(Boolean).length;
287
+ if (managedContextCount === 0) {
288
+ findings.push(finding(normalized, 'context-unmanaged', 'advisory', 'Canonical context exists but no managed marker was detected; operator-owned content was preserved.', 'Review the context graph and adopt managed markers only when appropriate.'));
289
+ }
290
+ const diagnostics = await diagnoseWorkspaceContext(options.projectRoot);
291
+ for (const diagnostic of diagnostics) {
292
+ if (diagnostic.severity === 'info')
293
+ continue;
294
+ findings.push(finding(normalized, `context-diagnostic:${diagnostic.code}`, diagnostic.severity === 'error' ? 'blocking' : 'advisory', diagnostic.message, diagnostic.severity === 'error'
295
+ ? 'Run aiwg regenerate after correcting the reported context graph problem.'
296
+ : 'Review the context advisory; operator-owned files are never overwritten implicitly.', diagnostic.path ? { path: diagnostic.path } : undefined));
297
+ }
298
+ }
299
+ const outcome = classifyOutcome(findings, restartRequired);
300
+ const indexFailed = findings.some((item) => item.id.startsWith('index-'));
301
+ const contextFailed = findings.some((item) => item.id.startsWith('context-') && item.severity === 'blocking');
302
+ const deployFailed = findings.some((item) => item.severity === 'blocking'
303
+ && (item.id.startsWith('deployment-') || item.id.startsWith('provider-') || item.id.startsWith('registry-')));
304
+ const phases = [
305
+ phase('resolve', 'passed', true, `Resolved ${options.projectRoot}, ${normalized}, ${options.scope} scope.`),
306
+ phase('deploy', deployFailed ? 'failed' : 'passed', true, deployFailed ? 'Deployment invariants failed.' : 'Provider artifacts and installed state verified.', { counts }),
307
+ phase('index', indexFailed ? 'failed' : 'passed', true, indexFailed ? 'Capability index verification failed.' : 'Capability index is readable, current, and contains the requested surface.'),
308
+ phase('context', options.contextOptOut ? 'skipped' : contextFailed ? 'failed' : 'passed', !options.contextOptOut, options.contextOptOut ? 'Context generation was explicitly suppressed.' : contextFailed ? 'Canonical context verification failed.' : 'Canonical context and provider wiring verified.'),
309
+ phase('verify', outcome === 'failed' ? 'failed' : 'passed', true, `${findings.filter((item) => item.severity === 'blocking').length} blocking and ${findings.filter((item) => item.severity === 'advisory').length} advisory finding(s).`),
310
+ phase('report', 'passed', true, `Final provider outcome: ${outcome}.`),
311
+ ];
312
+ return {
313
+ provider: normalized,
314
+ scope: options.scope,
315
+ outcome,
316
+ restartRequired,
317
+ restartAction,
318
+ restartReason,
319
+ counts,
320
+ phases,
321
+ findings,
322
+ };
323
+ }
324
+ export function buildDryRunUseResult(options) {
325
+ const providers = options.providers.map((provider) => {
326
+ const normalized = normalizeProviderDefinitionId(provider) ?? provider;
327
+ const restartNotice = RESTART_NOTICES[normalized] ?? null;
328
+ const restartAction = restartNotice?.action ?? null;
329
+ const phases = [
330
+ phase('resolve', 'planned', true, `Would resolve ${options.projectRoot}, ${normalized}, ${options.scope} scope.`),
331
+ phase('deploy', 'planned', true, 'Would deploy the requested managed artifact surface.'),
332
+ phase('index', 'planned', true, 'Would refresh and verify the framework capability index.'),
333
+ phase('context', options.contextOptOut ? 'skipped' : 'planned', !options.contextOptOut, options.contextOptOut ? 'Context generation explicitly suppressed.' : 'Would generate and verify canonical context and provider wiring.'),
334
+ phase('verify', 'planned', true, 'Would run scoped deployment verification.'),
335
+ phase('report', 'planned', true, 'Would report a stable final outcome.'),
336
+ ];
337
+ return {
338
+ provider: normalized,
339
+ scope: options.scope,
340
+ outcome: 'planned',
341
+ restartRequired: restartAction !== null,
342
+ restartAction,
343
+ restartReason: restartNotice?.reason ?? null,
344
+ counts: emptyCounts(),
345
+ phases,
346
+ findings: [],
347
+ };
348
+ });
349
+ return {
350
+ schema: 'aiwg.use.result.v1',
351
+ generatedAt: new Date().toISOString(),
352
+ projectRoot: path.resolve(options.projectRoot),
353
+ frameworkRoot: path.resolve(options.frameworkRoot),
354
+ scope: options.scope,
355
+ requestedBundles: options.requestedBundles,
356
+ dryRun: true,
357
+ providers,
358
+ phases: providers[0]?.phases ?? [],
359
+ findings: [],
360
+ outcome: 'planned',
361
+ restartRequired: providers.some((provider) => provider.restartRequired),
362
+ discovery: frameworkDiscoveryInventory(options.frameworkRoot),
363
+ exitClassification: 'preview',
364
+ exitCode: 0,
365
+ };
366
+ }
367
+ export function aggregateUseDeploymentResult(options) {
368
+ const findings = options.providers.flatMap((provider) => provider.findings);
369
+ const hasFailed = options.providers.some((provider) => provider.outcome === 'failed');
370
+ const hasDegraded = options.providers.some((provider) => provider.outcome === 'degraded');
371
+ const restartRequired = options.providers.some((provider) => provider.restartRequired);
372
+ const outcome = hasFailed
373
+ ? 'failed'
374
+ : hasDegraded
375
+ ? 'degraded'
376
+ : restartRequired
377
+ ? 'ready-restart-required'
378
+ : 'ready';
379
+ const phaseIds = ['resolve', 'deploy', 'index', 'context', 'verify', 'report'];
380
+ const phases = phaseIds.map((id) => {
381
+ const matching = options.providers.map((provider) => provider.phases.find((item) => item.id === id)).filter((item) => Boolean(item));
382
+ const state = matching.some((item) => item.state === 'failed')
383
+ ? 'failed'
384
+ : matching.every((item) => item.state === 'skipped')
385
+ ? 'skipped'
386
+ : 'passed';
387
+ return phase(id, state, matching.some((item) => item.required), `${matching.filter((item) => item.state === 'passed').length}/${matching.length} provider result(s) passed.`, { providers: matching.map((item, index) => ({ provider: options.providers[index]?.provider, state: item.state })) });
388
+ });
389
+ return {
390
+ schema: 'aiwg.use.result.v1',
391
+ generatedAt: new Date().toISOString(),
392
+ projectRoot: path.resolve(options.projectRoot),
393
+ frameworkRoot: path.resolve(options.frameworkRoot),
394
+ scope: options.scope,
395
+ requestedBundles: options.requestedBundles,
396
+ dryRun: false,
397
+ providers: options.providers,
398
+ phases,
399
+ findings,
400
+ outcome,
401
+ restartRequired,
402
+ discovery: frameworkDiscoveryInventory(options.frameworkRoot),
403
+ exitClassification: hasFailed ? 'failure' : hasDegraded ? 'degraded' : 'success',
404
+ exitCode: hasFailed ? 1 : 0,
405
+ };
406
+ }
407
+ export async function verifyConfiguredDeployments(projectRoot, filters = {}, frameworkRoot = process.env.AIWG_ROOT || projectRoot) {
408
+ const config = await readAiwgConfig(projectRoot);
409
+ const providers = filters.provider
410
+ ? [filters.provider]
411
+ : config?.providers?.length ? config.providers : [];
412
+ const bundles = filters.bundle ? [filters.bundle] : Object.keys(config?.installed ?? {});
413
+ const results = [];
414
+ for (const provider of providers) {
415
+ const providerBundles = bundles.filter((bundle) => Boolean(config?.installed[bundle]?.deployedTo[provider]));
416
+ if (providerBundles.length === 0)
417
+ continue;
418
+ results.push(await verifyProviderDeployment({
419
+ projectRoot,
420
+ frameworkRoot,
421
+ provider,
422
+ scope: filters.scope ?? 'project',
423
+ requestedBundles: providerBundles,
424
+ }));
425
+ }
426
+ if (results.length === 0) {
427
+ const fallback = providers[0] ?? 'generic';
428
+ results.push({
429
+ provider: fallback,
430
+ scope: filters.scope ?? 'project',
431
+ outcome: 'failed',
432
+ restartRequired: false,
433
+ restartAction: null,
434
+ restartReason: null,
435
+ counts: emptyCounts(),
436
+ phases: [phase('verify', 'failed', true, 'No installed provider deployment could be resolved.')],
437
+ findings: [finding(fallback, 'deployment-not-configured', 'blocking', 'No installed provider deployment could be resolved.', 'Run aiwg use all --provider <provider>.')],
438
+ });
439
+ }
440
+ return aggregateUseDeploymentResult({ projectRoot, frameworkRoot, scope: filters.scope ?? 'project', requestedBundles: bundles, providers: results });
441
+ }
442
+ export async function buildDeploymentStatusProbe(projectRoot, frameworkRoot = process.env.AIWG_ROOT || projectRoot) {
443
+ const result = await verifyConfiguredDeployments(projectRoot, {}, frameworkRoot);
444
+ const notConfigured = result.requestedBundles.length === 0
445
+ && result.findings.length > 0
446
+ && result.findings.every((item) => item.id === 'deployment-not-configured');
447
+ const engaged = result.outcome === 'ready' || result.outcome === 'ready-restart-required' || result.outcome === 'degraded';
448
+ return {
449
+ schema: 'aiwg.status.probe.v1',
450
+ generated_at: result.generatedAt,
451
+ project_root: result.projectRoot,
452
+ engaged,
453
+ status: notConfigured ? 'not-configured' : result.outcome === 'failed' ? 'needs-repair' : result.outcome,
454
+ checks: {
455
+ workspace_exists: await exists(path.join(projectRoot, '.aiwg')),
456
+ framework_count: result.requestedBundles.length,
457
+ provider_deployment_count: notConfigured ? 0 : result.providers.length,
458
+ health: notConfigured ? 'not-configured' : result.outcome === 'failed' ? 'error' : result.outcome === 'degraded' ? 'warning' : 'healthy',
459
+ malformed_config: result.findings.some((item) => item.id.includes('registry')),
460
+ artifact_health: notConfigured ? 'not-configured' : result.outcome,
461
+ external_artifact_reachable: true,
462
+ },
463
+ verification: {
464
+ required: true,
465
+ action: engaged
466
+ ? 'AIWG deployment is verified on disk.'
467
+ : notConfigured
468
+ ? 'Choose a provider and bundle to configure AIWG for this project.'
469
+ : 'Repair the blocking deployment findings, then run this probe again.',
470
+ command: 'aiwg status --probe --json',
471
+ next_command: engaged ? null : notConfigured ? 'aiwg wizard --dry-run' : 'aiwg doctor --deployment',
472
+ },
473
+ provider_deployments: result.providers,
474
+ deployment_verification: result,
475
+ };
476
+ }
477
+ const DEPLOYED_COUNT_LABELS = {
478
+ agents: 'Agents',
479
+ commands: 'Commands',
480
+ skills: 'Skills',
481
+ rules: 'Rules',
482
+ behaviors: 'Behaviors',
483
+ };
484
+ function displayProvider(provider) {
485
+ const definition = getProviderDefinition(provider);
486
+ return definition ? `${definition.displayName} (${provider})` : provider;
487
+ }
488
+ function wrapTokens(tokens, width, indent = ' ') {
489
+ if (tokens.length === 0)
490
+ return [`${indent}None`];
491
+ const max = Math.max(24, width - indent.length);
492
+ const lines = [];
493
+ let line = '';
494
+ for (const token of tokens) {
495
+ const candidate = line ? `${line} · ${token}` : token;
496
+ if (line && candidate.length > max) {
497
+ lines.push(indent + line);
498
+ line = token;
499
+ }
500
+ else {
501
+ line = candidate;
502
+ }
503
+ }
504
+ if (line)
505
+ lines.push(indent + line);
506
+ return lines;
507
+ }
508
+ function wrapParagraph(text, width, indent = ' ') {
509
+ const max = Math.max(24, width - indent.length);
510
+ const words = text.split(/\s+/).filter(Boolean);
511
+ const lines = [];
512
+ let line = '';
513
+ for (const word of words) {
514
+ const candidate = line ? `${line} ${word}` : word;
515
+ if (line && candidate.length > max) {
516
+ lines.push(indent + line);
517
+ line = word;
518
+ }
519
+ else {
520
+ line = candidate;
521
+ }
522
+ }
523
+ if (line)
524
+ lines.push(indent + line);
525
+ return lines;
526
+ }
527
+ function outcomeHeading(result) {
528
+ if (result.outcome === 'ready')
529
+ return 'AIWG ready';
530
+ if (result.outcome === 'ready-restart-required')
531
+ return 'AIWG ready — provider reload required';
532
+ if (result.outcome === 'degraded')
533
+ return 'AIWG ready with advisories';
534
+ return 'AIWG needs repair';
535
+ }
536
+ export function renderUseDeploymentResult(result, options = {}) {
537
+ if (result.dryRun) {
538
+ const providers = result.providers.map((provider) => provider.provider).join(', ');
539
+ return `Deployment preview: ${result.requestedBundles.join(', ')} for ${providers}\nNo files were changed and no verification pass is claimed.`;
540
+ }
541
+ const width = Math.max(60, Math.min(160, Math.floor(options.width ?? 100)));
542
+ const lines = ['', outcomeHeading(result)];
543
+ for (const provider of result.providers) {
544
+ lines.push('', `Deployed to ${displayProvider(provider.provider)}`);
545
+ lines.push(...wrapTokens(Object.entries(DEPLOYED_COUNT_LABELS).map(([type, label]) => `${label} ${provider.counts[type].toLocaleString('en-US')}`), width));
546
+ }
547
+ lines.push('', 'Indexed for discovery');
548
+ if (result.discovery) {
549
+ lines.push(` ${result.discovery.totalArtifacts.toLocaleString('en-US')} artifacts · ${result.discovery.graph} graph`);
550
+ lines.push(...wrapTokens(Object.entries(result.discovery.byType).map(([type, count]) => `${type} ${count.toLocaleString('en-US')}`), width));
551
+ }
552
+ else {
553
+ lines.push(' Unavailable — the framework index could not be read.');
554
+ }
555
+ const visibleFindings = result.findings.filter((candidate) => candidate.severity !== 'info');
556
+ if (visibleFindings.length > 0) {
557
+ lines.push('', result.outcome === 'failed' ? 'Blocking findings' : 'Advisories');
558
+ for (const item of visibleFindings) {
559
+ lines.push(...wrapParagraph(`${item.provider}: ${item.message}`, width));
560
+ if (item.remediation)
561
+ lines.push(...wrapParagraph(`Fix: ${item.remediation}`, width, ' '));
562
+ }
563
+ }
564
+ if (options.verbose) {
565
+ lines.push('', 'Verification details');
566
+ for (const provider of result.providers) {
567
+ for (const item of provider.phases) {
568
+ lines.push(...wrapParagraph(`${provider.provider}/${item.id}: ${item.state} — ${item.summary}`, width));
569
+ }
570
+ if (provider.restartReason) {
571
+ lines.push(...wrapParagraph(`${provider.provider} reload rationale: ${provider.restartReason}`, width));
572
+ }
573
+ }
574
+ if (result.discovery) {
575
+ lines.push(...wrapParagraph(`Framework index built: ${result.discovery.builtAt}`, width));
576
+ }
577
+ }
578
+ const restartActions = result.providers
579
+ .filter((provider) => provider.restartRequired && provider.restartAction)
580
+ .map((provider) => provider.restartAction);
581
+ const next = options.nextSteps?.length
582
+ ? options.nextSteps
583
+ : result.outcome === 'failed'
584
+ ? ['Repair the blocking findings above, then run the same aiwg use command again.']
585
+ : restartActions.length > 0
586
+ ? [...restartActions, 'Ask your AI tool to verify AIWG and recommend one useful next action.']
587
+ : ['Ask your AI tool to verify AIWG and recommend one useful next action.'];
588
+ lines.push('', 'Next');
589
+ for (const step of next)
590
+ lines.push(...wrapParagraph(step, width));
591
+ if (options.version) {
592
+ lines.push('', ...wrapParagraph(`AIWG v${options.version.version} · ${options.version.repository}`, width, ' '));
593
+ }
594
+ return lines.join('\n');
595
+ }
596
+ //# sourceMappingURL=deployment-verification.js.map
@@ -5,7 +5,7 @@ import { createInterface } from 'readline';
5
5
  import path from 'path';
6
6
  import os from 'os';
7
7
  import { readAiwgConfig } from '../config/aiwg-config.js';
8
- import { PROJECT_AIWG_LOCATION_FILE, projectAiwgPath } from '../config/project-artifacts.js';
8
+ import { PROJECT_AIWG_LOCATION_FILE, projectAiwgPath, projectControlPath, } from '../config/project-artifacts.js';
9
9
  const DEFAULT_MAX_BYTES = 1_048_576;
10
10
  const DEFAULT_REPORT_LIMIT = 20;
11
11
  const MAX_TRANSCRIPT_LINE_BYTES = 1_048_576;
@@ -474,7 +474,7 @@ async function findProjectRoot(startDir) {
474
474
  while (current !== path.dirname(current)) {
475
475
  if (existsSync(path.join(current, '.aiwg')) ||
476
476
  existsSync(path.join(current, PROJECT_AIWG_LOCATION_FILE)) ||
477
- existsSync(projectAiwgPath(current, 'aiwg.config'))) {
477
+ existsSync(projectControlPath(current, 'aiwg.config'))) {
478
478
  return current;
479
479
  }
480
480
  current = path.dirname(current);
@@ -11,7 +11,7 @@
11
11
  * @depends @src/writing/prompt-optimizer.ts
12
12
  * @depends @src/cli/config-loader.ts
13
13
  * @depends @src/cli/watch-service.ts
14
- * @cli @docs/CLI_USAGE.md - workflow command
14
+ * @cli @docs/cli/agent-usage.md - workflow command
15
15
  */
16
16
  import { readFile, writeFile, copyFile } from 'fs/promises';
17
17
  import { existsSync } from 'fs';
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
- import { projectAiwgPath } from '../config/project-artifacts.js';
3
+ import { projectAiwgPath, projectControlPath } from '../config/project-artifacts.js';
4
4
  const FRAMEWORKS = [
5
5
  'sdlc',
6
6
  'marketing',
@@ -126,7 +126,7 @@ export async function resolveWorkspaceSignalPlan(projectDir, opts = {}) {
126
126
  addReason(included, 'aiwg-utils', 'always included as core AIWG utilities');
127
127
  let profile = opts.profile;
128
128
  let profileSource = profile ? 'flag' : 'auto';
129
- const config = await readJsonIfPresent(projectAiwgPath(projectDir, 'aiwg.config'));
129
+ const config = await readJsonIfPresent(projectControlPath(projectDir, 'aiwg.config'));
130
130
  const configHints = extractConfigHints(config);
131
131
  if (!profile && configHints.profile) {
132
132
  profile = configHints.profile;