@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.
- package/README.md +1037 -13
- package/dist/src/artifacts/cli.js +59 -5
- package/dist/src/artifacts/discover-facets.js +15 -0
- package/dist/src/artifacts/discovery-eval.js +290 -0
- package/dist/src/artifacts/fortemi-core-query-adapter.js +1 -1
- package/dist/src/artifacts/fortemi-shard-export.js +1 -1
- package/dist/src/artifacts/query-engine.js +10 -6
- package/dist/src/cli/handlers/help.js +2 -1
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/resource-versions.js +247 -0
- package/dist/src/cli/handlers/subcommands.js +55 -3
- package/dist/src/cli/handlers/use.js +15 -3
- package/dist/src/cli/handlers/utilities.js +27 -26
- package/dist/src/config/cli.js +13 -9
- package/dist/src/config/project-artifacts-runtime.mjs +68 -0
- package/dist/src/config/project-artifacts.js +1 -68
- package/dist/src/extensions/commands/definitions.js +36 -2
- package/dist/src/extensions/project-local-discovery.js +86 -2
- package/dist/src/extensions/project-local-remove.js +52 -56
- package/dist/src/extensions/shadow-resolver.js +3 -1
- package/dist/src/plugins/standalone-packager.js +143 -0
- package/dist/src/resources/cache-cleanup.js +67 -0
- package/dist/src/resources/doctor.js +107 -0
- package/dist/src/resources/lockfile.js +125 -0
- package/dist/src/resources/resolver.js +133 -0
- package/dist/src/resources/web-release.d.ts +8 -0
- package/dist/src/resources/web-release.js +159 -1
- package/dist/src/smiths/context-pipeline/aiwg-md.js +5 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +21 -1
- package/dist/src/smiths/context-pipeline/finalization.js +5 -3
- package/dist/src/smiths/context-pipeline/generator.js +4 -1
- package/dist/src/smiths/context-pipeline/parallelism-section.js +34 -1
- package/dist/src/smiths/context-pipeline/workspace-context.js +15 -3
- package/dist/src/smiths/mcpsmith/example.js +3 -1
- package/dist/src/smiths/mcpsmith/generator.js +3 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +2 -1
- package/dist/src/storage/cli.js +3 -2
- package/dist/src/storage/subsystem-cli.js +7 -2
- package/dist/src/update/notifier.mjs +1 -1
- package/dist/src/update/service.mjs +123 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
526
|
-
|
|
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
|
|
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
|
|
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 ||
|
|
26
|
+
this.basePath = basePath || projectAiwgPath(process.cwd(), 'smiths', 'toolsmith');
|
|
26
27
|
this.knownTools = this.#loadKnownTools();
|
|
27
28
|
}
|
|
28
29
|
|
package/dist/src/storage/cli.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
|
199
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
},
|