@aiwg/cli 2026.7.19 → 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 +397 -385
- 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
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadResourceTrustRootFile, readVerifiedRegularFile, resolveWebRelease, } from "../../resources/web-release.js";
|
|
3
|
+
import { getProjectDir } from "../../config/aiwg-config.js";
|
|
4
|
+
import { cleanWebResourceCache } from "../../resources/cache-cleanup.js";
|
|
5
|
+
import { writeWebResourceLock } from "../../resources/lockfile.js";
|
|
6
|
+
const MAX_RESOURCE_MANIFEST_BYTES = 4 * 1024 * 1024;
|
|
7
|
+
const DEFAULT_CHANNELS = ["stable", "latest", "canary", "main"];
|
|
8
|
+
function usage() {
|
|
9
|
+
return [
|
|
10
|
+
"Usage: aiwg versions <list|resolve|show|clean-cache> [selector] [--json] [--pretty] [--offline]",
|
|
11
|
+
"",
|
|
12
|
+
"Examples:",
|
|
13
|
+
" aiwg versions list --json",
|
|
14
|
+
" aiwg versions resolve stable --json",
|
|
15
|
+
" aiwg versions resolve stable --write-lock",
|
|
16
|
+
" aiwg versions show 2026.7.18",
|
|
17
|
+
" aiwg versions clean-cache --dry-run",
|
|
18
|
+
].join("\n");
|
|
19
|
+
}
|
|
20
|
+
function flagValue(args, flag) {
|
|
21
|
+
const index = args.indexOf(flag);
|
|
22
|
+
if (index === -1)
|
|
23
|
+
return undefined;
|
|
24
|
+
const value = args[index + 1];
|
|
25
|
+
if (!value || value.startsWith("--"))
|
|
26
|
+
throw new Error(`${flag} requires a value`);
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
function parseArgs(args) {
|
|
30
|
+
const [rawSubcommand = "list", ...rest] = args;
|
|
31
|
+
if (rawSubcommand !== "list" &&
|
|
32
|
+
rawSubcommand !== "resolve" &&
|
|
33
|
+
rawSubcommand !== "show" &&
|
|
34
|
+
rawSubcommand !== "clean-cache") {
|
|
35
|
+
throw new Error(`Unknown versions subcommand: ${rawSubcommand}\n\n${usage()}`);
|
|
36
|
+
}
|
|
37
|
+
const valueFlagIndexes = new Set();
|
|
38
|
+
for (const flag of ["--channels", "--target", "--prefix"]) {
|
|
39
|
+
const index = rest.indexOf(flag);
|
|
40
|
+
if (index !== -1) {
|
|
41
|
+
valueFlagIndexes.add(index);
|
|
42
|
+
valueFlagIndexes.add(index + 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const positionals = rest.filter((arg, index) => !arg.startsWith("--") && !valueFlagIndexes.has(index));
|
|
46
|
+
if ((rawSubcommand === "resolve" || rawSubcommand === "show") && positionals.length !== 1) {
|
|
47
|
+
throw new Error(`aiwg versions ${rawSubcommand} requires exactly one version, range, digest, or channel selector\n\n${usage()}`);
|
|
48
|
+
}
|
|
49
|
+
if (rawSubcommand === "list" && positionals.length > 0) {
|
|
50
|
+
throw new Error(`aiwg versions list does not accept positional selectors\n\n${usage()}`);
|
|
51
|
+
}
|
|
52
|
+
if (rawSubcommand === "clean-cache" && positionals.length > 0) {
|
|
53
|
+
throw new Error(`aiwg versions clean-cache does not accept positional selectors\n\n${usage()}`);
|
|
54
|
+
}
|
|
55
|
+
const writeLock = rest.includes("--write-lock");
|
|
56
|
+
if ((rawSubcommand === "list" || rawSubcommand === "clean-cache") && writeLock) {
|
|
57
|
+
throw new Error(`aiwg versions ${rawSubcommand} cannot write resources.lock.json; use resolve or show with --write-lock`);
|
|
58
|
+
}
|
|
59
|
+
const channelsValue = flagValue(rest, "--channels");
|
|
60
|
+
const channels = channelsValue
|
|
61
|
+
? channelsValue.split(",").map((channel) => channel.trim()).filter(Boolean)
|
|
62
|
+
: [...DEFAULT_CHANNELS];
|
|
63
|
+
if (channels.length === 0)
|
|
64
|
+
throw new Error("--channels must include at least one channel");
|
|
65
|
+
return {
|
|
66
|
+
subcommand: rawSubcommand,
|
|
67
|
+
selector: positionals[0],
|
|
68
|
+
json: rest.includes("--json") || rest.includes("--format=json"),
|
|
69
|
+
pretty: rest.includes("--pretty"),
|
|
70
|
+
offline: rest.includes("--offline"),
|
|
71
|
+
writeLock,
|
|
72
|
+
dryRun: rest.includes("--dry-run"),
|
|
73
|
+
force: rest.includes("--force"),
|
|
74
|
+
channels,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function webReleaseOptionsFromEnvironment() {
|
|
78
|
+
const baseUrl = process.env.AIWG_RESOURCE_BASE_URL;
|
|
79
|
+
const cacheRoot = process.env.AIWG_RESOURCE_CACHE_ROOT;
|
|
80
|
+
const trustRootFile = process.env.AIWG_RESOURCE_TRUST_ROOT_FILE;
|
|
81
|
+
const publicKeyPem = trustRootFile === undefined
|
|
82
|
+
? undefined
|
|
83
|
+
: loadResourceTrustRootFile(path.resolve(trustRootFile));
|
|
84
|
+
return {
|
|
85
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
86
|
+
...(cacheRoot === undefined ? {} : { cacheRoot }),
|
|
87
|
+
...(publicKeyPem === undefined ? {} : { publicKeyPem }),
|
|
88
|
+
...(process.env.AIWG_RESOURCE_ALLOW_INSECURE_LOOPBACK_HTTP === "1"
|
|
89
|
+
? { allowInsecureLoopbackHttp: true }
|
|
90
|
+
: {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function readManifestSummary(release) {
|
|
94
|
+
const bytes = readVerifiedRegularFile(release.releaseManifestPath, {
|
|
95
|
+
label: "verified AIWG resource release manifest",
|
|
96
|
+
maxBytes: MAX_RESOURCE_MANIFEST_BYTES,
|
|
97
|
+
expectedSha256: release.manifestDigest,
|
|
98
|
+
});
|
|
99
|
+
const value = JSON.parse(bytes.toString("utf8"));
|
|
100
|
+
const bundles = Array.isArray(value.bundles) ? value.bundles : [];
|
|
101
|
+
const files = Array.isArray(value.files) ? value.files : [];
|
|
102
|
+
return {
|
|
103
|
+
schemaVersion: value.schemaVersion,
|
|
104
|
+
version: value.version,
|
|
105
|
+
compatibility: value.compatibility,
|
|
106
|
+
source: value.source,
|
|
107
|
+
bundles,
|
|
108
|
+
fileCount: files.length,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function releaseJson(release, manifest, lockfilePath) {
|
|
112
|
+
return {
|
|
113
|
+
selector: release.selector,
|
|
114
|
+
selectorKind: release.selectorKind,
|
|
115
|
+
version: release.version,
|
|
116
|
+
manifestSha256: release.manifestDigest,
|
|
117
|
+
manifestUrl: release.manifestUrl,
|
|
118
|
+
baseUrl: release.baseUrl,
|
|
119
|
+
cacheDir: release.cacheDir,
|
|
120
|
+
channelSequence: release.channelSequence,
|
|
121
|
+
fortemiCore: {
|
|
122
|
+
manifestSha256: release.fortemiManifestSha256,
|
|
123
|
+
manifestSize: release.fortemiManifestSize,
|
|
124
|
+
exportSha256: release.fortemiExportSha256,
|
|
125
|
+
exportSize: release.fortemiExportSize,
|
|
126
|
+
},
|
|
127
|
+
descriptorCount: release.descriptors.size,
|
|
128
|
+
...(lockfilePath === undefined ? {} : { lockfile: lockfilePath }),
|
|
129
|
+
...(manifest === undefined ? {} : { manifest }),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function printJson(value, pretty) {
|
|
133
|
+
console.log(JSON.stringify(value, null, pretty ? 2 : 0));
|
|
134
|
+
}
|
|
135
|
+
function printReleaseText(release, manifest, lockfilePath) {
|
|
136
|
+
console.log(`selector: ${release.selector} (${release.selectorKind})`);
|
|
137
|
+
console.log(`version: ${release.version}`);
|
|
138
|
+
if (release.channelSequence !== undefined)
|
|
139
|
+
console.log(`channel_sequence: ${release.channelSequence}`);
|
|
140
|
+
console.log(`manifest_sha256: ${release.manifestDigest}`);
|
|
141
|
+
console.log(`manifest_url: ${release.manifestUrl}`);
|
|
142
|
+
console.log(`cache_dir: ${release.cacheDir}`);
|
|
143
|
+
console.log(`fortemi_manifest_sha256: ${release.fortemiManifestSha256}`);
|
|
144
|
+
console.log(`fortemi_export_sha256: ${release.fortemiExportSha256}`);
|
|
145
|
+
console.log(`descriptor_count: ${release.descriptors.size}`);
|
|
146
|
+
if (lockfilePath !== undefined)
|
|
147
|
+
console.log(`lockfile: ${lockfilePath}`);
|
|
148
|
+
if (manifest) {
|
|
149
|
+
console.log(`schema_version: ${String(manifest.schemaVersion)}`);
|
|
150
|
+
console.log(`file_count: ${manifest.fileCount}`);
|
|
151
|
+
console.log(`bundle_count: ${manifest.bundles.length}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export const versionsHandler = {
|
|
155
|
+
id: "versions",
|
|
156
|
+
name: "Resource Versions",
|
|
157
|
+
description: "Browse and resolve signed AIWG web resource releases",
|
|
158
|
+
category: "index",
|
|
159
|
+
aliases: [],
|
|
160
|
+
async execute(ctx) {
|
|
161
|
+
if (ctx.args[0] === "help" || ctx.args[0] === "--help" || ctx.args[0] === "-h") {
|
|
162
|
+
console.log(usage());
|
|
163
|
+
return { exitCode: 0 };
|
|
164
|
+
}
|
|
165
|
+
let parsed;
|
|
166
|
+
try {
|
|
167
|
+
parsed = parseArgs(ctx.args);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return { exitCode: 2, message: error instanceof Error ? error.message : String(error) };
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const baseOptions = webReleaseOptionsFromEnvironment();
|
|
174
|
+
if (parsed.subcommand === "clean-cache") {
|
|
175
|
+
const result = cleanWebResourceCache(getProjectDir(ctx, ctx.args), {
|
|
176
|
+
cacheRoot: process.env.AIWG_RESOURCE_CACHE_ROOT,
|
|
177
|
+
dryRun: parsed.dryRun,
|
|
178
|
+
force: parsed.force,
|
|
179
|
+
});
|
|
180
|
+
if (parsed.json) {
|
|
181
|
+
printJson(result, parsed.pretty);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log(`cache_root: ${result.cacheRoot}`);
|
|
185
|
+
console.log(`dry_run: ${result.dryRun}`);
|
|
186
|
+
console.log(`force: ${result.force}`);
|
|
187
|
+
console.log(`locked: ${result.locked.length}`);
|
|
188
|
+
console.log(`preserved: ${result.preserved.length}`);
|
|
189
|
+
console.log(`removed: ${result.removed.length}`);
|
|
190
|
+
console.log(`skipped: ${result.skipped.length}`);
|
|
191
|
+
}
|
|
192
|
+
return { exitCode: 0 };
|
|
193
|
+
}
|
|
194
|
+
if (parsed.subcommand === "list") {
|
|
195
|
+
const resolved = [];
|
|
196
|
+
const unavailable = [];
|
|
197
|
+
for (const channel of parsed.channels) {
|
|
198
|
+
try {
|
|
199
|
+
const release = await resolveWebRelease({
|
|
200
|
+
...baseOptions,
|
|
201
|
+
selector: channel,
|
|
202
|
+
offline: parsed.offline,
|
|
203
|
+
});
|
|
204
|
+
resolved.push(releaseJson(release));
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
unavailable.push({
|
|
208
|
+
channel,
|
|
209
|
+
error: error instanceof Error ? error.message : String(error),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (parsed.json) {
|
|
214
|
+
printJson({ channels: resolved, unavailable }, parsed.pretty);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
for (const release of resolved) {
|
|
218
|
+
console.log(`${release.selector}: ${release.version} ${release.manifestSha256}`);
|
|
219
|
+
}
|
|
220
|
+
if (resolved.length === 0)
|
|
221
|
+
console.log("No configured channels resolved.");
|
|
222
|
+
}
|
|
223
|
+
return { exitCode: 0 };
|
|
224
|
+
}
|
|
225
|
+
const release = await resolveWebRelease({
|
|
226
|
+
...baseOptions,
|
|
227
|
+
selector: parsed.selector,
|
|
228
|
+
offline: parsed.offline,
|
|
229
|
+
});
|
|
230
|
+
const manifest = parsed.subcommand === "show" ? readManifestSummary(release) : undefined;
|
|
231
|
+
const lockfilePath = parsed.writeLock
|
|
232
|
+
? writeWebResourceLock(getProjectDir(ctx, ctx.args), release).path
|
|
233
|
+
: undefined;
|
|
234
|
+
if (parsed.json) {
|
|
235
|
+
printJson(releaseJson(release, manifest, lockfilePath), parsed.pretty);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
printReleaseText(release, manifest, lockfilePath);
|
|
239
|
+
}
|
|
240
|
+
return { exitCode: 0 };
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
return { exitCode: 1, message: error instanceof Error ? error.message : String(error) };
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=resource-versions.js.map
|
|
@@ -1115,9 +1115,44 @@ export const packagePluginHandler = {
|
|
|
1115
1115
|
category: "plugin",
|
|
1116
1116
|
aliases: ["-package-plugin", "--package-plugin"],
|
|
1117
1117
|
async execute(ctx) {
|
|
1118
|
+
if (ctx.args.includes("--help") || ctx.args.includes("-h")) {
|
|
1119
|
+
return {
|
|
1120
|
+
exitCode: 0,
|
|
1121
|
+
message: [
|
|
1122
|
+
"aiwg package-plugin — package a project-local or built-in marketplace wrapper",
|
|
1123
|
+
"",
|
|
1124
|
+
"Usage:",
|
|
1125
|
+
" aiwg package-plugin <name> [--source <path>] [--output <path>] [--provider <name>] [--clean] [--dry-run]",
|
|
1126
|
+
" aiwg package-plugin --plugin <name> [options] # compatibility form",
|
|
1127
|
+
"",
|
|
1128
|
+
"Options:",
|
|
1129
|
+
" --source <path> explicit project-local wrapper source (must stay inside the project)",
|
|
1130
|
+
" --output <path> standalone archive output (default: dist/plugins)",
|
|
1131
|
+
" --provider <name> claude, codex, or all for standalone wrappers; built-ins retain all formats",
|
|
1132
|
+
" --clean clean generated plugin output before packaging",
|
|
1133
|
+
" --dry-run, -n preview without writing",
|
|
1134
|
+
" --help, -h show this help",
|
|
1135
|
+
"",
|
|
1136
|
+
"Project-local wrappers are discovered under .aiwg/plugins and packaged as deterministic archives.",
|
|
1137
|
+
].join("\n"),
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
const hasExplicitPlugin = ctx.args.includes("--plugin") || ctx.args.includes("-p");
|
|
1141
|
+
const positional = ctx.args[0] && !ctx.args[0].startsWith("-")
|
|
1142
|
+
? ctx.args[0]
|
|
1143
|
+
: undefined;
|
|
1144
|
+
if (!hasExplicitPlugin && !positional) {
|
|
1145
|
+
return {
|
|
1146
|
+
exitCode: 1,
|
|
1147
|
+
message: "Error: plugin name is required.\n\nRun `aiwg package-plugin --help` for usage.",
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
const normalizedArgs = hasExplicitPlugin
|
|
1151
|
+
? ctx.args
|
|
1152
|
+
: ["--plugin", positional, ...ctx.args.slice(1)];
|
|
1118
1153
|
const frameworkRoot = await getFrameworkRoot();
|
|
1119
1154
|
const runner = createScriptRunner(frameworkRoot);
|
|
1120
|
-
return runner.run("tools/plugin/package-plugins.mjs",
|
|
1155
|
+
return runner.run("tools/plugin/package-plugins.mjs", normalizedArgs, {
|
|
1121
1156
|
cwd: ctx.cwd,
|
|
1122
1157
|
});
|
|
1123
1158
|
},
|
|
@@ -1134,6 +1169,23 @@ export const packageAllPluginsHandler = {
|
|
|
1134
1169
|
category: "plugin",
|
|
1135
1170
|
aliases: ["-package-all-plugins", "--package-all-plugins"],
|
|
1136
1171
|
async execute(ctx) {
|
|
1172
|
+
if (ctx.args.includes("--help") || ctx.args.includes("-h")) {
|
|
1173
|
+
return {
|
|
1174
|
+
exitCode: 0,
|
|
1175
|
+
message: [
|
|
1176
|
+
"aiwg package-all-plugins — package every built-in marketplace wrapper",
|
|
1177
|
+
"",
|
|
1178
|
+
"Usage:",
|
|
1179
|
+
" aiwg package-all-plugins [--provider <name>] [--clean] [--dry-run]",
|
|
1180
|
+
"",
|
|
1181
|
+
"Options:",
|
|
1182
|
+
" --provider <name> claude, codex, cursor, factory, openclaw, or all",
|
|
1183
|
+
" --clean clean generated plugin output before packaging",
|
|
1184
|
+
" --dry-run, -n preview without writing",
|
|
1185
|
+
" --help, -h show this help",
|
|
1186
|
+
].join("\n"),
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1137
1189
|
const frameworkRoot = await getFrameworkRoot();
|
|
1138
1190
|
const runner = createScriptRunner(frameworkRoot);
|
|
1139
1191
|
return runner.run("tools/plugin/package-plugins.mjs", ["--all", ...ctx.args], {
|
|
@@ -1199,7 +1251,7 @@ export const corpusHandler = {
|
|
|
1199
1251
|
* with the project's general-purpose artifact graph indices.
|
|
1200
1252
|
*
|
|
1201
1253
|
* aiwg discover "<phrase>" [--limit N] [--type skill,agent,...] [--json]
|
|
1202
|
-
* [--resource-source local|web|auto] [--aiwg-version <
|
|
1254
|
+
* [--resource-source local|web|auto] [--aiwg-version <version|range|digest|channel>] [--offline]
|
|
1203
1255
|
*/
|
|
1204
1256
|
export const discoverHandler = {
|
|
1205
1257
|
id: "discover",
|
|
@@ -1254,7 +1306,7 @@ export const featuresHandler = {
|
|
|
1254
1306
|
* so consumers don't need to navigate AIWG's storage paths themselves.
|
|
1255
1307
|
*
|
|
1256
1308
|
* aiwg show <name> [--type skill,agent,...] [--json] [--first]
|
|
1257
|
-
* [--resource-source local|web|auto] [--aiwg-version <
|
|
1309
|
+
* [--resource-source local|web|auto] [--aiwg-version <version|range|digest|channel>] [--offline]
|
|
1258
1310
|
*/
|
|
1259
1311
|
export const showHandler = {
|
|
1260
1312
|
id: "show",
|
|
@@ -775,9 +775,18 @@ async function countBundleSourceArtifacts(bundlePath) {
|
|
|
775
775
|
*/
|
|
776
776
|
async function deployOneProjectLocalBundle(opts) {
|
|
777
777
|
const { bundle, ctx, frameworkRoot, provider, target, dryRun, verbose, quiet, modelArgs } = opts;
|
|
778
|
+
const counts = await countBundleSourceArtifacts(bundle.artifactPath);
|
|
779
|
+
const artifactTotal = counts.agents + counts.commands + counts.skills + counts.rules;
|
|
780
|
+
if (verbose || dryRun) {
|
|
781
|
+
ui.dim(` Artifacts: agents=${counts.agents} commands=${counts.commands} skills=${counts.skills} rules=${counts.rules}`);
|
|
782
|
+
}
|
|
783
|
+
if (artifactTotal === 0) {
|
|
784
|
+
ui.warn(`Project-local ${bundle.type} '${bundle.id}' has no deployable agents, commands, skills, or rules at ${bundle.artifactPath}`);
|
|
785
|
+
return { exitCode: 1, counts };
|
|
786
|
+
}
|
|
778
787
|
const runner = createScriptRunner(frameworkRoot);
|
|
779
788
|
const args = [
|
|
780
|
-
'--source', bundle.
|
|
789
|
+
'--source', bundle.artifactPath,
|
|
781
790
|
'--deploy-commands', '--deploy-skills', '--deploy-rules',
|
|
782
791
|
'--provider', provider,
|
|
783
792
|
'--target', target,
|
|
@@ -814,7 +823,6 @@ async function deployOneProjectLocalBundle(opts) {
|
|
|
814
823
|
});
|
|
815
824
|
// Approximate counts from the bundle's source dirs (deploy-agents.mjs is
|
|
816
825
|
// idempotent and copies file-for-file from these dirs)
|
|
817
|
-
const counts = await countBundleSourceArtifacts(bundle.bundlePath);
|
|
818
826
|
void ctx;
|
|
819
827
|
return { exitCode: result.exitCode, counts };
|
|
820
828
|
}
|
|
@@ -903,6 +911,10 @@ async function deployProjectLocalBundles(opts) {
|
|
|
903
911
|
if (verbose || dryRun) {
|
|
904
912
|
const action = dryRun ? '[dry-run] Would deploy' : 'Deploying';
|
|
905
913
|
console.log(`${action} project-local ${bundle.type} '${bundle.id}' from ${bundle.localPath} → ${provider}`);
|
|
914
|
+
if (bundle.artifactPath !== bundle.bundlePath) {
|
|
915
|
+
const payloadDisplay = path.relative(projectDir, bundle.artifactPath) || '.';
|
|
916
|
+
ui.dim(` Resolved plugin payload: ${payloadDisplay}`);
|
|
917
|
+
}
|
|
906
918
|
}
|
|
907
919
|
const result = await deployOneProjectLocalBundle({
|
|
908
920
|
bundle, ctx, frameworkRoot, provider, target, dryRun, verbose, quiet, modelArgs,
|
|
@@ -941,7 +953,7 @@ async function deployProjectLocalBundles(opts) {
|
|
|
941
953
|
const mHash = await hashManifest(manifestAbsPath);
|
|
942
954
|
// #1037 — record per-artifact source hashes so `aiwg remove` can
|
|
943
955
|
// detect pristine vs mutated vs replaced deployed files.
|
|
944
|
-
const artifactHashes = await hashBundleArtifacts(bundle.
|
|
956
|
+
const artifactHashes = await hashBundleArtifacts(bundle.artifactPath);
|
|
945
957
|
const updated = updateInstalled(config, bundle.id, provider, result.counts, {
|
|
946
958
|
version: bundle.manifest.version,
|
|
947
959
|
source: 'project-local',
|
|
@@ -14,7 +14,7 @@ import fsp from 'fs/promises';
|
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { createScriptRunner } from './script-runner.js';
|
|
16
16
|
import { getFrameworkRoot } from '../../channel/manager.mjs';
|
|
17
|
-
import {
|
|
17
|
+
import { updateInstallation } from '../../update/service.mjs';
|
|
18
18
|
import { useHandler as useFrameworkHandler } from './use.js';
|
|
19
19
|
import { projectAiwgPath } from '../../config/project-artifacts.js';
|
|
20
20
|
import { checkCollisions, } from '../../smiths/skillsmith/collision-detector.js';
|
|
@@ -536,6 +536,21 @@ export const doctorHandler = {
|
|
|
536
536
|
catch {
|
|
537
537
|
// Project-local section is non-fatal for doctor
|
|
538
538
|
}
|
|
539
|
+
// Web-backed resource lock/cache diagnostics (#1850). Report lock source
|
|
540
|
+
// mode, cold cache, and digest drift without requiring web mode to be in use.
|
|
541
|
+
try {
|
|
542
|
+
const { buildWebResourceDoctorSection } = await import('../../resources/doctor.js');
|
|
543
|
+
const section = buildWebResourceDoctorSection(ctx.cwd || process.cwd(), {
|
|
544
|
+
cacheRoot: process.env.AIWG_RESOURCE_CACHE_ROOT,
|
|
545
|
+
});
|
|
546
|
+
if (section.output)
|
|
547
|
+
console.log(section.output);
|
|
548
|
+
if (section.hasFailures)
|
|
549
|
+
return { exitCode: 1, message: '' };
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
console.log(`\n── Web resource cache ──\n ⚠ unable to audit: ${error instanceof Error ? error.message : String(error)}`);
|
|
553
|
+
}
|
|
539
554
|
// Canonical workspace-context graph diagnostics (#1811). Legacy projects
|
|
540
555
|
// remain valid; drift, loops, conflicts, and possible credentials fail.
|
|
541
556
|
try {
|
|
@@ -578,7 +593,7 @@ export const updateHandler = {
|
|
|
578
593
|
name: 'Update',
|
|
579
594
|
description: 'Update AIWG and re-deploy installed frameworks',
|
|
580
595
|
category: 'maintenance',
|
|
581
|
-
aliases: ['-update', '--update'],
|
|
596
|
+
aliases: ['-update', '--update', 'upgrade'],
|
|
582
597
|
async execute(ctx) {
|
|
583
598
|
const args = ctx.args;
|
|
584
599
|
const deployAll = args.includes('--all');
|
|
@@ -592,8 +607,12 @@ export const updateHandler = {
|
|
|
592
607
|
// Step 1: Check for package updates (unless --skip-check)
|
|
593
608
|
if (!skipCheck) {
|
|
594
609
|
try {
|
|
595
|
-
console.log('
|
|
596
|
-
await
|
|
610
|
+
console.log('Updating AIWG installation...\n');
|
|
611
|
+
const update = await updateInstallation({
|
|
612
|
+
dryRun,
|
|
613
|
+
offline: args.includes('--offline'),
|
|
614
|
+
});
|
|
615
|
+
console.log(`${update.message}\n`);
|
|
597
616
|
}
|
|
598
617
|
catch (error) {
|
|
599
618
|
console.error(`Warning: Update check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -626,30 +645,12 @@ export const updateHandler = {
|
|
|
626
645
|
console.log(' aiwg use all');
|
|
627
646
|
return { exitCode: 0 };
|
|
628
647
|
}
|
|
629
|
-
//
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const useName = REGISTRY_ID_TO_USE_NAME[fw.id];
|
|
634
|
-
if (useName) {
|
|
635
|
-
installedFrameworks.push(useName);
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
638
|
-
unmapped.push(fw.id);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (installedFrameworks.length === 0) {
|
|
642
|
-
console.log('No recognized frameworks in registry');
|
|
643
|
-
if (unmapped.length > 0) {
|
|
644
|
-
console.log(`Unrecognized entries: ${unmapped.join(', ')}`);
|
|
645
|
-
}
|
|
646
|
-
return { exitCode: 0 };
|
|
647
|
-
}
|
|
648
|
+
// Canonical framework IDs need their historical public aliases. Add-ons,
|
|
649
|
+
// extensions, and project-local bundles are already accepted by `aiwg use`,
|
|
650
|
+
// so preserve their registry IDs instead of silently skipping them.
|
|
651
|
+
const installedFrameworks = registry.frameworks.map(item => REGISTRY_ID_TO_USE_NAME[item.id] ?? item.id);
|
|
648
652
|
// Report what will be updated
|
|
649
653
|
console.log(`Installed frameworks: ${installedFrameworks.join(', ')}`);
|
|
650
|
-
if (unmapped.length > 0) {
|
|
651
|
-
console.log(`Skipping unrecognized: ${unmapped.join(', ')}`);
|
|
652
|
-
}
|
|
653
654
|
console.log('');
|
|
654
655
|
if (dryRun) {
|
|
655
656
|
console.log('Dry run: Would re-deploy the following frameworks:');
|
package/dist/src/config/cli.js
CHANGED
|
@@ -21,6 +21,7 @@ import { fileURLToPath } from 'url';
|
|
|
21
21
|
import path from 'path';
|
|
22
22
|
import { UserConfig } from './user-config.js';
|
|
23
23
|
import { AiwgError, EXIT_CODES } from '../cli/errors.js';
|
|
24
|
+
import { projectAiwgPath, resolveProjectAiwgDir } from './project-artifacts.js';
|
|
24
25
|
const _scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
25
26
|
/**
|
|
26
27
|
* Main CLI entry point for `aiwg config <subcommand> [args]`
|
|
@@ -182,7 +183,7 @@ async function projectConfigGet(key, args) {
|
|
|
182
183
|
if (!cfg) {
|
|
183
184
|
throw new AiwgError({
|
|
184
185
|
code: 'ERR_NO_PROJECT_CONFIG',
|
|
185
|
-
message: 'No
|
|
186
|
+
message: 'No project AIWG config found at the resolved artifact root.',
|
|
186
187
|
hint: 'Run `aiwg init`, then ask your AIWG agent to set up repo/tracker/delivery policy.',
|
|
187
188
|
exitCode: EXIT_CODES.CONFIG,
|
|
188
189
|
});
|
|
@@ -362,7 +363,7 @@ async function handleProjectValidate(args) {
|
|
|
362
363
|
if (!cfg) {
|
|
363
364
|
throw new AiwgError({
|
|
364
365
|
code: 'ERR_NO_PROJECT_CONFIG',
|
|
365
|
-
message: 'No
|
|
366
|
+
message: 'No project AIWG config found at the resolved artifact root.',
|
|
366
367
|
hint: 'Run `aiwg init`, then configure project policy.',
|
|
367
368
|
exitCode: EXIT_CODES.CONFIG,
|
|
368
369
|
});
|
|
@@ -403,7 +404,8 @@ async function handleProjectValidate(args) {
|
|
|
403
404
|
? validateIssueLabels(cfg.issues)
|
|
404
405
|
: resolveIssueLabels(undefined, 'local').diagnostics;
|
|
405
406
|
const diagnostics = [...indexErrors, ...externalLinkErrors, ...labelDiagnostics];
|
|
406
|
-
console.log(`Project config: ${projectDir
|
|
407
|
+
console.log(`Project config: ${projectAiwgPath(projectDir, 'aiwg.config')}`);
|
|
408
|
+
console.log(`Artifact root: ${resolveProjectAiwgDir(projectDir)}\n`);
|
|
407
409
|
if (diagnostics.length === 0) {
|
|
408
410
|
console.log('✓ Project config valid');
|
|
409
411
|
return;
|
|
@@ -464,7 +466,7 @@ async function projectConfigReset(key, args) {
|
|
|
464
466
|
if (!cfg) {
|
|
465
467
|
throw new AiwgError({
|
|
466
468
|
code: 'ERR_NO_PROJECT_CONFIG',
|
|
467
|
-
message: 'No
|
|
469
|
+
message: 'No project AIWG config found at the resolved artifact root.',
|
|
468
470
|
hint: 'Run `aiwg init`, then ask your AIWG agent to establish project policy.',
|
|
469
471
|
exitCode: EXIT_CODES.CONFIG,
|
|
470
472
|
});
|
|
@@ -553,7 +555,7 @@ For project-level config: aiwg config show --project [--json]
|
|
|
553
555
|
if (!cfg) {
|
|
554
556
|
throw new AiwgError({
|
|
555
557
|
code: 'ERR_NO_PROJECT_CONFIG',
|
|
556
|
-
message: 'No
|
|
558
|
+
message: 'No project AIWG config found at the resolved artifact root.',
|
|
557
559
|
hint: 'Run `aiwg init`, then ask your AIWG agent to set up repo/tracker/delivery policy.',
|
|
558
560
|
exitCode: EXIT_CODES.CONFIG,
|
|
559
561
|
});
|
|
@@ -593,7 +595,8 @@ For project-level config: aiwg config show --project [--json]
|
|
|
593
595
|
return;
|
|
594
596
|
}
|
|
595
597
|
// Human-readable view
|
|
596
|
-
console.log(`Project config: ${projectDir
|
|
598
|
+
console.log(`Project config: ${projectAiwgPath(projectDir, 'aiwg.config')}`);
|
|
599
|
+
console.log(`Artifact root: ${resolveProjectAiwgDir(projectDir)}\n`);
|
|
597
600
|
console.log(`Schema version: ${cfg.version}`);
|
|
598
601
|
console.log(`Providers: ${cfg.providers.join(', ') || '(none)'}`);
|
|
599
602
|
console.log('');
|
|
@@ -663,13 +666,14 @@ function printUsage() {
|
|
|
663
666
|
|
|
664
667
|
Subcommands:
|
|
665
668
|
get <key> Read a user config value
|
|
666
|
-
get --project <key> Read a project config value (.aiwg/aiwg.config
|
|
669
|
+
get --project <key> Read a project config value (default .aiwg/aiwg.config;
|
|
670
|
+
AIWG_ARTIFACTS_PATH may override the artifact root)
|
|
667
671
|
set <key> <value> Write a user config value
|
|
668
672
|
set --project <key> <value> Write a project config value (validates enums)
|
|
669
673
|
list Show all user config
|
|
670
|
-
show --project Show resolved project config
|
|
674
|
+
show --project Show resolved project config and artifact root
|
|
671
675
|
validate Validate user config files
|
|
672
|
-
validate --project Validate
|
|
676
|
+
validate --project Validate resolved project config taxonomy/index semantics
|
|
673
677
|
[--provider gitea|github|local] [--available-label NAME ...]
|
|
674
678
|
reset [<key>] Reset key or all config to defaults
|
|
675
679
|
path Print config directory path
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime implementation of project AIWG artifact directory resolution.
|
|
3
|
+
*
|
|
4
|
+
* Kept as ESM JavaScript so direct `.mjs` tools and compiled TypeScript
|
|
5
|
+
* consumers share exactly one resolver implementation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
import { existsSync, readFileSync } from 'fs';
|
|
10
|
+
import { isAbsolute, join, resolve } from 'path';
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_PROJECT_AIWG_DIR = '.aiwg';
|
|
13
|
+
export const AIWG_ARTIFACTS_PATH_ENV = 'AIWG_ARTIFACTS_PATH';
|
|
14
|
+
export const PROJECT_AIWG_LOCATION_FILE = '.aiwg-location';
|
|
15
|
+
|
|
16
|
+
const ARTIFACT_PATH_ENV_ALIASES = [
|
|
17
|
+
AIWG_ARTIFACTS_PATH_ENV,
|
|
18
|
+
'AIWG_PROJECT_ARTIFACTS_PATH',
|
|
19
|
+
'AIWG_PROJECT_AIWG_DIR',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export function expandProjectArtifactPath(pathValue, projectDir) {
|
|
23
|
+
const trimmed = pathValue.trim();
|
|
24
|
+
if (trimmed === '~') return homedir();
|
|
25
|
+
if (trimmed.startsWith('~/')) return resolve(homedir(), trimmed.slice(2));
|
|
26
|
+
if (isAbsolute(trimmed)) return trimmed;
|
|
27
|
+
return resolve(projectDir, trimmed);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseProjectArtifactLocation(contents) {
|
|
31
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
32
|
+
let line = rawLine.trim();
|
|
33
|
+
if (line.length === 0 || line.startsWith('#')) continue;
|
|
34
|
+
if (line.startsWith('export ')) line = line.slice('export '.length).trim();
|
|
35
|
+
const assignment = line.match(/^AIWG_ARTIFACTS_PATH\s*=\s*(.+)$/);
|
|
36
|
+
if (assignment) line = assignment[1].trim();
|
|
37
|
+
if (
|
|
38
|
+
(line.startsWith('"') && line.endsWith('"')) ||
|
|
39
|
+
(line.startsWith("'") && line.endsWith("'"))
|
|
40
|
+
) {
|
|
41
|
+
line = line.slice(1, -1);
|
|
42
|
+
}
|
|
43
|
+
return line.length > 0 ? line : null;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function readProjectArtifactLocation(projectDir) {
|
|
49
|
+
const pointerPath = resolve(projectDir, PROJECT_AIWG_LOCATION_FILE);
|
|
50
|
+
if (!existsSync(pointerPath)) return null;
|
|
51
|
+
return parseProjectArtifactLocation(readFileSync(pointerPath, 'utf-8'));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function resolveProjectAiwgDir(projectDir, env = process.env) {
|
|
55
|
+
for (const key of ARTIFACT_PATH_ENV_ALIASES) {
|
|
56
|
+
const value = env[key];
|
|
57
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
58
|
+
return expandProjectArtifactPath(value, projectDir);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const configuredLocation = readProjectArtifactLocation(projectDir);
|
|
62
|
+
if (configuredLocation) return expandProjectArtifactPath(configuredLocation, projectDir);
|
|
63
|
+
return resolve(projectDir, DEFAULT_PROJECT_AIWG_DIR);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function projectAiwgPath(projectDir, ...segments) {
|
|
67
|
+
return join(resolveProjectAiwgDir(projectDir), ...segments);
|
|
68
|
+
}
|