@aiwg/cli 2026.7.18 → 2026.7.20

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 (41) hide show
  1. package/README.md +1037 -13
  2. package/dist/src/artifacts/cli.js +59 -5
  3. package/dist/src/artifacts/discover-facets.js +15 -0
  4. package/dist/src/artifacts/discovery-eval.js +290 -0
  5. package/dist/src/artifacts/fortemi-core-query-adapter.js +1 -1
  6. package/dist/src/artifacts/fortemi-shard-export.js +1 -1
  7. package/dist/src/artifacts/query-engine.js +10 -6
  8. package/dist/src/cli/handlers/help.js +2 -1
  9. package/dist/src/cli/handlers/index.js +3 -1
  10. package/dist/src/cli/handlers/resource-versions.js +247 -0
  11. package/dist/src/cli/handlers/subcommands.js +55 -3
  12. package/dist/src/cli/handlers/use.js +15 -3
  13. package/dist/src/cli/handlers/utilities.js +27 -26
  14. package/dist/src/config/cli.js +13 -9
  15. package/dist/src/config/project-artifacts-runtime.mjs +68 -0
  16. package/dist/src/config/project-artifacts.js +1 -68
  17. package/dist/src/extensions/commands/definitions.js +36 -2
  18. package/dist/src/extensions/project-local-discovery.js +86 -2
  19. package/dist/src/extensions/project-local-remove.js +52 -56
  20. package/dist/src/extensions/shadow-resolver.js +3 -1
  21. package/dist/src/plugins/standalone-packager.js +143 -0
  22. package/dist/src/resources/cache-cleanup.js +67 -0
  23. package/dist/src/resources/doctor.js +107 -0
  24. package/dist/src/resources/lockfile.js +125 -0
  25. package/dist/src/resources/resolver.js +133 -0
  26. package/dist/src/resources/web-release.d.ts +8 -0
  27. package/dist/src/resources/web-release.js +159 -1
  28. package/dist/src/smiths/context-pipeline/aiwg-md.js +5 -1
  29. package/dist/src/smiths/context-pipeline/claude-hook.js +21 -1
  30. package/dist/src/smiths/context-pipeline/finalization.js +5 -3
  31. package/dist/src/smiths/context-pipeline/generator.js +4 -1
  32. package/dist/src/smiths/context-pipeline/parallelism-section.js +34 -1
  33. package/dist/src/smiths/context-pipeline/workspace-context.js +15 -3
  34. package/dist/src/smiths/mcpsmith/example.js +3 -1
  35. package/dist/src/smiths/mcpsmith/generator.js +3 -1
  36. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +2 -1
  37. package/dist/src/storage/cli.js +3 -2
  38. package/dist/src/storage/subsystem-cli.js +7 -2
  39. package/dist/src/update/notifier.mjs +1 -1
  40. package/dist/src/update/service.mjs +123 -0
  41. package/package.json +3 -2
@@ -62,6 +62,7 @@ export async function buildContextFinalizationBlock(projectPath) {
62
62
  const installedNames = installed.map(([name]) => name);
63
63
  const providerDeployments = new Set();
64
64
  const normalizedAiwgMdPath = displayProjectPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
65
+ const normalizedAiwgMdLabel = `\`${normalizedAiwgMdPath}\``;
65
66
  for (const [, entry] of installed) {
66
67
  for (const provider of Object.keys(entry.deployedTo ?? {})) {
67
68
  providerDeployments.add(provider);
@@ -94,9 +95,9 @@ export async function buildContextFinalizationBlock(projectPath) {
94
95
  '',
95
96
  '### Source Model',
96
97
  '',
97
- '- `.aiwg/AIWG.md` is the normalized project-local context entry point.',
98
+ `- ${normalizedAiwgMdLabel} is the normalized project-local context entry point.`,
98
99
  '- Root `AIWG.md` is the generated cross-provider companion loaded through `AGENTS.md` and provider twins.',
99
- '- `AGENTS.md`, `WARP.md`, `.hermes.md`, and `.github/copilot-instructions.md` are provider-facing bridges, not replacements for `.aiwg/AIWG.md`.',
100
+ `- \`AGENTS.md\`, \`WARP.md\`, \`.hermes.md\`, and \`.github/copilot-instructions.md\` are provider-facing bridges, not replacements for ${normalizedAiwgMdLabel}.`,
100
101
  FINALIZATION_END,
101
102
  '',
102
103
  ];
@@ -113,6 +114,7 @@ export function replaceOrAppendFinalizationBlock(content, block) {
113
114
  export async function buildNormalizedAiwgMd(projectPath, existing = '') {
114
115
  const block = await buildContextFinalizationBlock(projectPath);
115
116
  const externalLinksSection = await buildExternalLinksSection(projectPath);
117
+ const normalizedAiwgMdPath = displayProjectPath(projectPath, projectAiwgPath(projectPath, 'AIWG.md'));
116
118
  const base = existing.trim().length > 0
117
119
  ? existing
118
120
  : [
@@ -120,7 +122,7 @@ export async function buildNormalizedAiwgMd(projectPath, existing = '') {
120
122
  AIWG_SIGNATURE_COMMENT,
121
123
  '<!-- Normalized project-local AIWG context. Operator notes may live outside AIWG-managed blocks. -->',
122
124
  '',
123
- 'This file is the stable `.aiwg/AIWG.md` entry point for AIWG skills, rules, and generated provider context.',
125
+ `This file is the stable \`${normalizedAiwgMdPath}\` entry point for AIWG skills, rules, and generated provider context.`,
124
126
  '',
125
127
  ].join('\n');
126
128
  const signed = base.includes(AIWG_SIGNATURE_COMMENT)
@@ -21,6 +21,7 @@ import { shouldEmitAgentsMd, shouldEmitAiwgMd, shouldEmitClaudeMdHook } from './
21
21
  import { ensureClaudeMdHook } from './claude-hook.js';
22
22
  import { ensureManagedHook } from './managed-hook.js';
23
23
  import { buildProviderBootstrapBlock, ensureWorkspaceContext, registerProviderContext, } from './workspace-context.js';
24
+ import { projectAiwgPath } from '../../config/project-artifacts.js';
24
25
  const SECTION_TITLES = {
25
26
  agents: 'Agents',
26
27
  rules: 'Rules',
@@ -136,7 +137,9 @@ export async function buildAgentsMd(opts) {
136
137
  parts.push('');
137
138
  parts.push('See [AIWG.md](./AIWG.md) for the full AIWG framework context');
138
139
  parts.push('(active frameworks, addons, agents, behaviors, rules).');
139
- parts.push('Tracker and delivery source of truth: [.aiwg/aiwg.config](./.aiwg/aiwg.config).');
140
+ const configRelative = path.relative(opts.projectPath, projectAiwgPath(opts.projectPath, 'aiwg.config')).replace(/\\/g, '/');
141
+ const configLink = configRelative.startsWith('.') ? configRelative : `./${configRelative}`;
142
+ parts.push(`Tracker and delivery source of truth: [${configRelative}](${configLink}).`);
140
143
  parts.push('');
141
144
  parts.push('Deployed artifacts live under your provider\'s native directory');
142
145
  parts.push('(for example `.codex/agents/`, `.warp/agents/`, `.github/agents/`).');
@@ -16,6 +16,27 @@
16
16
  import { readAiwgConfig, resolveParallelism, PROVIDER_PARALLELISM_DEFAULTS } from '../../config/aiwg-config.js';
17
17
  export const PARALLELISM_BLOCK_START = '<!-- AIWG-PARALLELISM-CAP:START -->';
18
18
  export const PARALLELISM_BLOCK_END = '<!-- AIWG-PARALLELISM-CAP:END -->';
19
+ function delegationSupport(provider) {
20
+ switch (provider) {
21
+ case 'claude':
22
+ case 'codex':
23
+ case 'copilot':
24
+ case 'cursor':
25
+ case 'factory':
26
+ case 'opencode':
27
+ case 'openclaw':
28
+ return `**Provider behavior (${provider})**: native custom subagents can select the deployed model-worker wrapper. Verify the resolved model when provider or account policy may substitute it.`;
29
+ case 'openhuman':
30
+ return '**Provider behavior (openhuman)**: wrapper roles compile to OpenHuman agent definitions; exact pins validate while semantic hints may be routed dynamically.';
31
+ case 'warp':
32
+ case 'hermes':
33
+ return `**Provider behavior (${provider})**: delegation is available, but model selection is global/run-scoped. Use the wrapper rubric for task selection while reporting that heterogeneous per-worker pinning is not enforced.`;
34
+ case 'windsurf':
35
+ return '**Provider behavior (windsurf)**: portable subagent model selection is unsupported. Apply the decomposition rubric in the primary agent and do not claim wrapper pinning or parallel delegation.';
36
+ default:
37
+ return '**Provider behavior (unknown)**: delegation and per-worker model selection are unverified. Apply the rubric only where the active provider exposes those capabilities, and state any fallback explicitly.';
38
+ }
39
+ }
19
40
  /**
20
41
  * Build the parallelism-cap markdown section for injection into context files.
21
42
  * Returns `''` when injection should be skipped:
@@ -61,7 +82,19 @@ export async function buildParallelismSection(projectPath) {
61
82
  lines.push(`*Rationale*: ${resolved.rationale}`);
62
83
  }
63
84
  lines.push('');
64
- lines.push('When spawning parallel subagents, take the MIN of: this cap, `AIWG_CONTEXT_WINDOW` budget, the RLM 7-agent hard cap (RLM dispatches only), and the natural task decomposition. Bump via `aiwg config set --project parallelism.max_parallel_subagents N`.');
85
+ lines.push('### Model-selected delegation rubric');
86
+ lines.push('');
87
+ lines.push('For each non-trivial task, assess whether it contains independent, bounded subtasks that can run concurrently. When delegation is supported, prefer the deployed model-pinned wrappers by task characteristics and consequence:');
88
+ lines.push('');
89
+ lines.push('- `aiwg-model-efficiency-worker`: discovery, inventory, focused edits, and other bounded low-cost work.');
90
+ lines.push('- `aiwg-model-coding-worker`: implementation, tests, debugging, and routine technical delivery.');
91
+ lines.push('- `aiwg-model-reasoning-worker`: architecture, synthesis, difficult analysis, and high-consequence review.');
92
+ lines.push('');
93
+ lines.push('Do not delegate trivial work, tightly coupled changes, serial dependencies, or tasks likely to collide in shared state; also keep work local when coordination costs exceed the expected benefit. Parallelize only independent work, and take the MIN of provider limits, `max_parallel_subagents`, `AIWG_CONTEXT_WINDOW` budget, framework-specific caps (including the RLM 7-agent hard cap for RLM dispatches), and natural task decomposition. Bump the project cap via `aiwg config set --project parallelism.max_parallel_subagents N`.');
94
+ lines.push('');
95
+ lines.push('The primary agent retains orchestration, final integration, conflict resolution, validation, and user-facing accountability.');
96
+ lines.push('');
97
+ lines.push(delegationSupport(primary));
65
98
  lines.push('');
66
99
  lines.push(PARALLELISM_BLOCK_END);
67
100
  lines.push('');
@@ -384,7 +384,15 @@ export async function ensureWorkspaceContext(projectPath, options = {}) {
384
384
  }
385
385
  const block = buildWorkspaceManagedBlock(projectPath, options.providerFiles);
386
386
  if (existing.includes(WORKSPACE_MANAGED_START) || existing.includes(WORKSPACE_MANAGED_END)) {
387
- const updated = replaceBlock(existing, WORKSPACE_MANAGED_START, WORKSPACE_MANAGED_END, block);
387
+ let updated = replaceBlock(existing, WORKSPACE_MANAGED_START, WORKSPACE_MANAGED_END, block);
388
+ // A managed project-extraction block explicitly points operators back to
389
+ // README/package metadata as its source. Refresh that block during normal
390
+ // regeneration so the documented edit-source-then-regenerate contract is
391
+ // true (#1866). Workspaces without an extraction remain opt-in.
392
+ if (updated.includes(PROJECT_EXTRACTION_START) || updated.includes(PROJECT_EXTRACTION_END)) {
393
+ const extracted = await extractExistingProjectContext(projectPath);
394
+ updated = replaceBlock(updated, PROJECT_EXTRACTION_START, PROJECT_EXTRACTION_END, extracted.content);
395
+ }
388
396
  if (updated === existing)
389
397
  return { path: workspacePath, action: 'unchanged', warnings };
390
398
  await atomicWrite(workspacePath, updated);
@@ -512,6 +520,7 @@ function sensitiveEvidence(content) {
512
520
  async function nestedInstructionFiles(projectPath) {
513
521
  const found = [];
514
522
  const ignored = new Set(['.git', 'node_modules', 'dist', 'build', 'coverage', '.aiwg']);
523
+ const artifactRoot = path.resolve(resolveProjectAiwgDir(projectPath));
515
524
  async function walk(directory) {
516
525
  let entries;
517
526
  try {
@@ -522,8 +531,11 @@ async function nestedInstructionFiles(projectPath) {
522
531
  }
523
532
  for (const entry of entries) {
524
533
  if (entry.isDirectory()) {
525
- if (!ignored.has(entry.name) && !entry.name.startsWith('.context-migration-'))
526
- await walk(path.join(directory, entry.name));
534
+ const child = path.join(directory, entry.name);
535
+ if (!ignored.has(entry.name)
536
+ && !entry.name.startsWith('.context-migration-')
537
+ && path.resolve(child) !== artifactRoot)
538
+ await walk(child);
527
539
  }
528
540
  else if (['AGENTS.md', 'CLAUDE.md', 'WARP.md'].includes(entry.name)) {
529
541
  const relative = path.relative(projectPath, path.join(directory, entry.name)).replace(/\\/g, '/');
@@ -12,6 +12,7 @@ import { analyzeCLI } from './analyzers/cli-analyzer.js';
12
12
  import { generateServer, updateRegistry } from './generator.js';
13
13
  import path from 'path';
14
14
  import { fileURLToPath } from 'url';
15
+ import { projectAiwgPath } from '../../config/project-artifacts.js';
15
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
17
  async function main() {
17
18
  console.log('MCPsmith Example: Generating Git MCP Server\n');
@@ -28,7 +29,8 @@ async function main() {
28
29
  console.log();
29
30
  // Step 2: Generate the MCP server
30
31
  console.log('Step 2: Generating MCP server...');
31
- const outputDir = path.join(__dirname, '..', '..', '..', '.aiwg', 'smiths', 'mcpsmith', 'servers');
32
+ const projectRoot = path.resolve(__dirname, '..', '..', '..');
33
+ const outputDir = projectAiwgPath(projectRoot, 'smiths', 'mcpsmith', 'servers');
32
34
  const generatedServer = await generateServer({
33
35
  serverId: 'git',
34
36
  serverName: 'Git CLI MCP Server',
@@ -10,6 +10,7 @@ import fs from 'fs/promises';
10
10
  import path from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
  import crypto from 'crypto';
13
+ import { projectAiwgPath } from '../../config/project-artifacts.js';
13
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15
  /**
15
16
  * Generate MCP server from analyzer result
@@ -174,7 +175,8 @@ function createManifest(serverId, serverName, analyzerResult, config) {
174
175
  */
175
176
  async function generateServerCode(serverId, serverName, analyzerResult) {
176
177
  // Load template
177
- const templatePath = path.join(path.dirname(__dirname), '..', '..', '.aiwg', 'smiths', 'mcpsmith', 'templates', 'server-template.mjs');
178
+ const projectRoot = path.resolve(path.dirname(__dirname), '..', '..');
179
+ const templatePath = projectAiwgPath(projectRoot, 'smiths', 'mcpsmith', 'templates', 'server-template.mjs');
178
180
  let template = await fs.readFile(templatePath, 'utf-8');
179
181
  // Replace template variables
180
182
  const now = new Date().toISOString();
@@ -13,6 +13,7 @@ import { existsSync, constants } from 'fs';
13
13
  import { resolve, join, dirname } from 'path';
14
14
  import { platform, arch, homedir, tmpdir, cpus, totalmem, freemem } from 'os';
15
15
  import { fileURLToPath } from 'url';
16
+ import { projectAiwgPath } from '../../config/project-artifacts-runtime.mjs';
16
17
 
17
18
  const __filename = fileURLToPath(import.meta.url);
18
19
  const __dirname = dirname(__filename);
@@ -22,7 +23,7 @@ const __dirname = dirname(__filename);
22
23
  */
23
24
  export class RuntimeDiscovery {
24
25
  constructor(basePath = null) {
25
- this.basePath = basePath || resolve(process.cwd(), '.aiwg/smiths/toolsmith');
26
+ this.basePath = basePath || projectAiwgPath(process.cwd(), 'smiths', 'toolsmith');
26
27
  this.knownTools = this.#loadKnownTools();
27
28
  }
28
29
 
@@ -18,6 +18,7 @@ import { existsSync } from 'fs';
18
18
  import { mkdir, readFile, writeFile, appendFile } from 'fs/promises';
19
19
  import { dirname, join, resolve as resolvePath } from 'path';
20
20
  import { BACKEND_TYPES, FilesystemAdapter, ObsidianAdapter, LogseqAdapter, FortemiAdapter, SUBSYSTEM_KEYS, getLoadedConfig, initStorage, resolveStorage, resolveSubsystemRoot, storageConfigPath, } from './index.js';
21
+ import { projectAiwgPath, resolveProjectAiwgDir } from '../config/project-artifacts.js';
21
22
  export async function main(args) {
22
23
  const subcommand = args[0];
23
24
  const subArgs = args.slice(1);
@@ -47,7 +48,7 @@ async function handleShow(projectRoot) {
47
48
  const config = await getLoadedConfig(projectRoot);
48
49
  const cfgPath = storageConfigPath(projectRoot);
49
50
  if (!config) {
50
- console.log(`No storage.config — every subsystem uses the default fs backend under .aiwg/.\n`);
51
+ console.log(`No storage.config — every subsystem uses the default fs backend under ${resolveProjectAiwgDir(projectRoot)}.\n`);
51
52
  console.log(`Expected location (when configured): ${cfgPath}\n`);
52
53
  }
53
54
  else {
@@ -178,7 +179,7 @@ async function handleMigrate(projectRoot, args) {
178
179
  return;
179
180
  }
180
181
  // Resume support: read the per-migration log; skip already-done paths
181
- const migrationLogPath = resolvePath(projectRoot, '.aiwg', '.storage-cache', 'migrations', `${opts.subsystem}-${specSlug(opts.from)}-to-${specSlug(opts.to)}.jsonl`);
182
+ const migrationLogPath = projectAiwgPath(projectRoot, '.storage-cache', 'migrations', `${opts.subsystem}-${specSlug(opts.from)}-to-${specSlug(opts.to)}.jsonl`);
182
183
  const completed = await readCompletedSet(migrationLogPath);
183
184
  let copied = 0;
184
185
  let skipped = 0;
@@ -20,6 +20,7 @@
20
20
  */
21
21
  import { getLoadedConfig, resolveStorage } from './index.js';
22
22
  import { resolveSubsystemRootRuntime } from './config.js';
23
+ import { projectAiwgPath, resolveProjectAiwgDir } from '../config/project-artifacts.js';
23
24
  export async function runSubsystemCli(subsystem, args, opts = {}) {
24
25
  const subcommand = args[0];
25
26
  const subArgs = args.slice(1);
@@ -177,6 +178,9 @@ async function readStdin() {
177
178
  return Buffer.concat(chunks).toString('utf-8');
178
179
  }
179
180
  function printUsage(subsystem, display) {
181
+ const projectRoot = process.cwd();
182
+ const subsystemPath = projectAiwgPath(projectRoot, subsystem);
183
+ const configPath = projectAiwgPath(projectRoot, 'storage.config');
180
184
  console.log(`Usage: aiwg ${display} <subcommand>
181
185
 
182
186
  Subcommands:
@@ -195,7 +199,8 @@ Examples:
195
199
  echo "# page" | aiwg ${display} put some/page.md
196
200
  echo '{"event":"x"}' | aiwg ${display} append-log some/log.jsonl
197
201
 
198
- The ${display} subsystem persists at .aiwg/${subsystem}/ on the default fs backend.
199
- Configure .aiwg/storage.config to redirect (#934).`);
202
+ The ${display} subsystem persists at ${subsystemPath} on the default fs backend
203
+ (artifact root: ${resolveProjectAiwgDir(projectRoot)}).
204
+ Configure ${configPath} to redirect (#934).`);
200
205
  }
201
206
  //# sourceMappingURL=subsystem-cli.js.map
@@ -218,7 +218,7 @@ export function maybePrintNotice() {
218
218
  const cache = readCache();
219
219
  if (!cache?.hasUpdate || !cache.current || !cache.latest) return;
220
220
  // One-line, non-intrusive. Users who want to update run `aiwg update`.
221
- const msg = `aiwg: update available ${cache.current} → ${cache.latest} (run: npm install -g aiwg)`;
221
+ const msg = `aiwg: update available ${cache.current} → ${cache.latest} (run: aiwg update)`;
222
222
  process.stderr.write(`\n${msg}\n`);
223
223
  }
224
224
 
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Install-aware AIWG update service.
3
+ *
4
+ * This is the single package-update primitive shared by `aiwg update` and
5
+ * `aiwg refresh`. It deliberately keeps provider/framework redeployment in
6
+ * the calling handlers.
7
+ */
8
+
9
+ import { execFileSync } from 'node:child_process';
10
+ import { existsSync, readFileSync } from 'node:fs';
11
+ import path from 'node:path';
12
+ import { getPackageRoot, loadConfig } from '../channel/manager.mjs';
13
+
14
+ const VALID_MODES = new Set(['npm', 'web', 'source']);
15
+
16
+ function readPackageName(packageRoot) {
17
+ try {
18
+ return JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8')).name;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ export async function detectInstallMode(options = {}) {
25
+ const packageRoot = options.packageRoot ?? getPackageRoot();
26
+ const override = options.env?.AIWG_INSTALL_MODE ?? process.env.AIWG_INSTALL_MODE;
27
+ if (override) {
28
+ if (!VALID_MODES.has(override)) {
29
+ throw new Error(`AIWG_INSTALL_MODE must be one of: ${[...VALID_MODES].join(', ')}`);
30
+ }
31
+ return { mode: override, packageRoot, packageName: readPackageName(packageRoot) };
32
+ }
33
+
34
+ const config = options.config ?? await loadConfig();
35
+ const packageName = readPackageName(packageRoot);
36
+ if (packageName === '@aiwg/cli') return { mode: 'web', packageRoot, packageName };
37
+ if (config.devMode || config.channel === 'edge' || existsSync(path.join(packageRoot, '.git'))) {
38
+ return { mode: 'source', packageRoot, packageName };
39
+ }
40
+ return { mode: 'npm', packageRoot, packageName };
41
+ }
42
+
43
+ function npmTag(channel) {
44
+ if (channel === 'next') return 'next';
45
+ if (channel === 'nightly') return 'nightly';
46
+ return 'latest';
47
+ }
48
+
49
+ /**
50
+ * Update the active distribution.
51
+ *
52
+ * `web` means the lightweight CLI's signed resource distribution. Its
53
+ * resources resolve the selected signed channel on demand; it must not replace
54
+ * itself with the full global npm package. `source` is intentionally manual.
55
+ */
56
+ export async function updateInstallation(options = {}) {
57
+ const config = options.config ?? await loadConfig();
58
+ const detected = await detectInstallMode({ ...options, config });
59
+ const channel = options.channel ?? config.channel ?? 'stable';
60
+ const dryRun = options.dryRun === true;
61
+ const offline = options.offline === true;
62
+
63
+ if (detected.mode === 'web') {
64
+ if (offline) {
65
+ return {
66
+ ...detected,
67
+ channel,
68
+ status: 'unsupported-offline',
69
+ changed: false,
70
+ message: 'Web-backed AIWG cannot refresh signed resources while offline; verified cached resources remain available.',
71
+ };
72
+ }
73
+ if (dryRun) {
74
+ return {
75
+ ...detected,
76
+ channel,
77
+ status: 'dry-run',
78
+ changed: false,
79
+ message: `Would refresh the verified signed '${channel}' web resource release; no global package install would be attempted.`,
80
+ };
81
+ }
82
+ const refreshWebResources = options.refreshWebResources ?? (async (selector) => {
83
+ const { resolveWebRelease } = await import('../resources/web-release.js');
84
+ return resolveWebRelease({ selector });
85
+ });
86
+ const release = await refreshWebResources(channel);
87
+ return {
88
+ ...detected,
89
+ channel,
90
+ version: release.version,
91
+ status: 'updated',
92
+ changed: true,
93
+ message: `Refreshed the verified signed '${channel}' web resource release${release.version ? ` (${release.version})` : ''}; no global package install was attempted.`,
94
+ };
95
+ }
96
+
97
+ if (detected.mode === 'source') {
98
+ return {
99
+ ...detected,
100
+ channel,
101
+ status: 'manual',
102
+ changed: false,
103
+ message: `Source checkout detected at ${detected.packageRoot}. Update it with 'git pull --ff-only', then run 'npm ci && npm run build'; AIWG will not overwrite it with npm.`,
104
+ };
105
+ }
106
+
107
+ const tag = npmTag(channel);
108
+ const command = ['install', '--global', `aiwg@${tag}`];
109
+ if (!dryRun) {
110
+ const execute = options.execute ?? ((file, args) => execFileSync(file, args, { stdio: 'inherit' }));
111
+ execute('npm', command);
112
+ }
113
+ return {
114
+ ...detected,
115
+ channel,
116
+ status: dryRun ? 'dry-run' : 'updated',
117
+ changed: !dryRun,
118
+ command: `npm ${command.join(' ')}`,
119
+ message: dryRun
120
+ ? `Would update the full AIWG npm distribution on the '${tag}' channel.`
121
+ : `Updated the full AIWG npm distribution on the '${tag}' channel.`,
122
+ };
123
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiwg/cli",
3
- "version": "2026.7.18",
3
+ "version": "2026.7.20",
4
4
  "description": "Lightweight AIWG CLI for signed, versioned web-backed resources.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -56,7 +56,7 @@
56
56
  "node": ">=20.0.0"
57
57
  },
58
58
  "dependencies": {
59
- "@fortemi/core": "2026.7.11",
59
+ "@fortemi/core": "2026.7.13",
60
60
  "@modelcontextprotocol/sdk": "^1.24.0",
61
61
  "chalk": "^4.1.2",
62
62
  "chokidar": "^3.6.0",
@@ -66,6 +66,7 @@
66
66
  "js-yaml": "^4.1.0",
67
67
  "listr2": "^8.2.5",
68
68
  "ora": "^5.4.1",
69
+ "semver": "^7.8.5",
69
70
  "yaml": "^2.8.1",
70
71
  "zod": "^3.25.0"
71
72
  },