@kevlns/v-cli 0.1.1-beta.1 → 0.2.0-beta.2
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/AGENTS.md +75 -0
- package/README.md +114 -16
- package/dist/cli.mjs +1243 -39
- package/package.json +13 -5
- package/schemas/v-cli-plugin.schema.json +144 -0
package/dist/cli.mjs
CHANGED
|
@@ -75,31 +75,1071 @@ function defaultHomeDir() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// src/core/loader.ts
|
|
78
|
-
import
|
|
79
|
-
import
|
|
78
|
+
import fs5 from "fs";
|
|
79
|
+
import path7 from "path";
|
|
80
80
|
import { pathToFileURL } from "url";
|
|
81
81
|
|
|
82
|
+
// src/core/official.ts
|
|
83
|
+
import { createRequire } from "module";
|
|
84
|
+
import { fileURLToPath } from "url";
|
|
85
|
+
import path3 from "path";
|
|
86
|
+
import fs2 from "fs";
|
|
87
|
+
|
|
88
|
+
// src/core/manifest.ts
|
|
89
|
+
var RESERVED_MANIFEST_COMMAND_NAMES = ["doctor", "plugin", "ts", "agent", "help"];
|
|
90
|
+
var KNOWN_PLATFORMS = ["darwin", "linux", "win32"];
|
|
91
|
+
var OUTPUT_FORMATS = ["json", "stdout", "text"];
|
|
92
|
+
var COMMAND_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
93
|
+
var PACKAGE_NAME_RE = /^@[a-zA-Z0-9][a-zA-Z0-9._~-]*\/[a-zA-Z0-9][a-zA-Z0-9._~-]*$/;
|
|
94
|
+
function isPlainObject(v) {
|
|
95
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
96
|
+
}
|
|
97
|
+
function isStringArray(v) {
|
|
98
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
99
|
+
}
|
|
100
|
+
function validateManifest(input) {
|
|
101
|
+
if (!isPlainObject(input)) {
|
|
102
|
+
return { ok: false, errors: ["\u6E05\u5355\u5FC5\u987B\u662F JSON \u5BF9\u8C61"] };
|
|
103
|
+
}
|
|
104
|
+
const errors = [];
|
|
105
|
+
const m = input;
|
|
106
|
+
if (typeof m.schemaVersion !== "number" || !Number.isInteger(m.schemaVersion)) {
|
|
107
|
+
errors.push("schemaVersion \u5FC5\u987B\u662F\u6574\u6570");
|
|
108
|
+
} else if (m.schemaVersion === 1) {
|
|
109
|
+
} else if (m.schemaVersion >= 2) {
|
|
110
|
+
errors.push(
|
|
111
|
+
`schemaVersion ${m.schemaVersion} \u4E0D\u53D7\u652F\u6301\uFF1Av-cli 0.2 \u4EC5\u652F\u6301\u5951\u7EA6\u7248\u672C 1 \u7684\u6E05\u5355\uFF0C\u8BF7\u5347\u7EA7\u63D2\u4EF6\u6216 v-cli`
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
errors.push("schemaVersion \u5FC5\u987B\u4E3A 1");
|
|
115
|
+
}
|
|
116
|
+
const requireString = (key) => {
|
|
117
|
+
const v = m[key];
|
|
118
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
119
|
+
errors.push(`${key} \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
return v;
|
|
123
|
+
};
|
|
124
|
+
const packageName = requireString("package");
|
|
125
|
+
if (typeof packageName === "string" && !PACKAGE_NAME_RE.test(packageName)) {
|
|
126
|
+
errors.push("package \u5FC5\u987B\u662F @scope/name \u5F62\u5F0F\u7684 npm \u5305\u540D");
|
|
127
|
+
}
|
|
128
|
+
const command = requireString("command");
|
|
129
|
+
if (typeof command === "string") {
|
|
130
|
+
if (!COMMAND_NAME_RE.test(command)) {
|
|
131
|
+
errors.push(`command "${command}" \u4E0D\u5408\u6CD5\uFF1A\u53EA\u80FD\u662F\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\u7684 [a-z0-9-] \u5B57\u7B26\u4E32`);
|
|
132
|
+
}
|
|
133
|
+
if (RESERVED_MANIFEST_COMMAND_NAMES.includes(command)) {
|
|
134
|
+
errors.push(
|
|
135
|
+
`command "${command}" \u662F v-cli \u4FDD\u7559\u547D\u4EE4\u540D\uFF08${RESERVED_MANIFEST_COMMAND_NAMES.join("/")}\uFF09\uFF0C\u63D2\u4EF6\u4E0D\u80FD\u5360\u7528`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
requireString("bin");
|
|
140
|
+
requireString("description");
|
|
141
|
+
if (!Array.isArray(m.platforms)) {
|
|
142
|
+
errors.push("platforms \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4");
|
|
143
|
+
} else if (m.platforms.length === 0) {
|
|
144
|
+
errors.push("platforms \u81F3\u5C11\u8981\u58F0\u660E\u4E00\u4E2A\u5E73\u53F0");
|
|
145
|
+
} else if (!isStringArray(m.platforms)) {
|
|
146
|
+
errors.push("platforms \u7684\u5143\u7D20\u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
147
|
+
} else {
|
|
148
|
+
const unknown = m.platforms.filter((p) => !KNOWN_PLATFORMS.includes(p));
|
|
149
|
+
if (unknown.length > 0) {
|
|
150
|
+
errors.push(`platforms \u542B\u672A\u77E5\u5E73\u53F0: ${unknown.join(", ")}\uFF08\u4EC5\u652F\u6301 ${KNOWN_PLATFORMS.join("/")}\uFF09`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!isPlainObject(m.runtime)) {
|
|
154
|
+
errors.push("runtime \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
155
|
+
}
|
|
156
|
+
if (!Array.isArray(m.environment)) {
|
|
157
|
+
errors.push("environment \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
158
|
+
} else {
|
|
159
|
+
m.environment.forEach((entry, i) => {
|
|
160
|
+
if (!isPlainObject(entry)) {
|
|
161
|
+
errors.push(`environment[${i}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
162
|
+
} else {
|
|
163
|
+
if (typeof entry.name !== "string" || entry.name.length === 0) {
|
|
164
|
+
errors.push(`environment[${i}].name \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
165
|
+
}
|
|
166
|
+
if (typeof entry.description !== "string" || entry.description.length === 0) {
|
|
167
|
+
errors.push(`environment[${i}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const agent2 = m.agent;
|
|
173
|
+
if (!isPlainObject(agent2)) {
|
|
174
|
+
errors.push("agent \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
175
|
+
} else {
|
|
176
|
+
if (typeof agent2.whenToUse !== "string") {
|
|
177
|
+
errors.push("agent.whenToUse \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
178
|
+
}
|
|
179
|
+
if (!Array.isArray(agent2.globalOptions)) {
|
|
180
|
+
errors.push("agent.globalOptions \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
181
|
+
} else {
|
|
182
|
+
agent2.globalOptions.forEach((o, i) => {
|
|
183
|
+
if (!isPlainObject(o)) {
|
|
184
|
+
errors.push(`agent.globalOptions[${i}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
185
|
+
} else {
|
|
186
|
+
if (typeof o.flags !== "string" || o.flags.length === 0) {
|
|
187
|
+
errors.push(`agent.globalOptions[${i}].flags \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
188
|
+
}
|
|
189
|
+
if (typeof o.description !== "string" || o.description.length === 0) {
|
|
190
|
+
errors.push(`agent.globalOptions[${i}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (!Array.isArray(agent2.commands)) {
|
|
196
|
+
errors.push("agent.commands \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
197
|
+
} else {
|
|
198
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
199
|
+
agent2.commands.forEach((c, i) => {
|
|
200
|
+
const at = `agent.commands[${i}]`;
|
|
201
|
+
if (!isPlainObject(c)) {
|
|
202
|
+
errors.push(`${at} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!Array.isArray(c.path) || !isStringArray(c.path) || c.path.length === 0) {
|
|
206
|
+
errors.push(`${at}.path \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4`);
|
|
207
|
+
} else {
|
|
208
|
+
const badSegment = c.path.find((seg) => !COMMAND_NAME_RE.test(seg));
|
|
209
|
+
if (badSegment !== void 0) {
|
|
210
|
+
errors.push(`${at}.path \u542B\u975E\u6CD5\u6BB5 "${badSegment}"\uFF08\u6BCF\u6BB5\u987B\u5339\u914D ^[a-z][a-z0-9-]*$\uFF09`);
|
|
211
|
+
}
|
|
212
|
+
const key = c.path.join(" ");
|
|
213
|
+
if (seenPaths.has(key)) {
|
|
214
|
+
errors.push(`${at}.path ${key} \u4E0E\u524D\u9762\u7684\u547D\u4EE4\u91CD\u590D`);
|
|
215
|
+
}
|
|
216
|
+
seenPaths.add(key);
|
|
217
|
+
}
|
|
218
|
+
if (typeof c.usage !== "string" || c.usage.length === 0) {
|
|
219
|
+
errors.push(`${at}.usage \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
220
|
+
}
|
|
221
|
+
if (typeof c.description !== "string" || c.description.length === 0) {
|
|
222
|
+
errors.push(`${at}.description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
223
|
+
}
|
|
224
|
+
if (!Array.isArray(c.arguments)) {
|
|
225
|
+
errors.push(`${at}.arguments \u5FC5\u987B\u662F\u6570\u7EC4`);
|
|
226
|
+
} else {
|
|
227
|
+
c.arguments.forEach((a, j) => {
|
|
228
|
+
if (!isPlainObject(a)) {
|
|
229
|
+
errors.push(`${at}.arguments[${j}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
230
|
+
} else {
|
|
231
|
+
if (typeof a.name !== "string" || a.name.length === 0) {
|
|
232
|
+
errors.push(`${at}.arguments[${j}].name \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
233
|
+
}
|
|
234
|
+
if (typeof a.required !== "boolean") {
|
|
235
|
+
errors.push(`${at}.arguments[${j}].required \u5FC5\u987B\u662F\u5E03\u5C14\u503C`);
|
|
236
|
+
}
|
|
237
|
+
if (typeof a.description !== "string" || a.description.length === 0) {
|
|
238
|
+
errors.push(`${at}.arguments[${j}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (!Array.isArray(c.options)) {
|
|
244
|
+
errors.push(`${at}.options \u5FC5\u987B\u662F\u6570\u7EC4`);
|
|
245
|
+
} else {
|
|
246
|
+
c.options.forEach((o, j) => {
|
|
247
|
+
if (!isPlainObject(o)) {
|
|
248
|
+
errors.push(`${at}.options[${j}] \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
249
|
+
} else {
|
|
250
|
+
if (typeof o.flags !== "string" || o.flags.length === 0) {
|
|
251
|
+
errors.push(`${at}.options[${j}].flags \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
252
|
+
}
|
|
253
|
+
if (typeof o.description !== "string" || o.description.length === 0) {
|
|
254
|
+
errors.push(`${at}.options[${j}].description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (!isPlainObject(c.output)) {
|
|
260
|
+
errors.push(`${at}.output \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
261
|
+
} else {
|
|
262
|
+
if (typeof c.output.format !== "string" || c.output.format.length === 0 || !OUTPUT_FORMATS.includes(c.output.format)) {
|
|
263
|
+
errors.push(
|
|
264
|
+
`${at}.output.format \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u4E14\u5C5E\u4E8E [${OUTPUT_FORMATS.join(", ")}]`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (typeof c.output.description !== "string" || c.output.description.length === 0) {
|
|
268
|
+
errors.push(`${at}.output.description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (!isPlainObject(c.exitCodes)) {
|
|
272
|
+
errors.push(`${at}.exitCodes \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
273
|
+
} else {
|
|
274
|
+
const badExit = Object.values(c.exitCodes).find((v) => typeof v !== "string");
|
|
275
|
+
if (badExit !== void 0) {
|
|
276
|
+
errors.push(`${at}.exitCodes \u7684\u503C\u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!Array.isArray(c.safety) || !isStringArray(c.safety)) {
|
|
280
|
+
errors.push(`${at}.safety \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (errors.length > 0) {
|
|
286
|
+
return { ok: false, errors };
|
|
287
|
+
}
|
|
288
|
+
return { ok: true, manifest: input };
|
|
289
|
+
}
|
|
290
|
+
function validatePluginIdentity(manifest, pkgJson) {
|
|
291
|
+
const errors = [];
|
|
292
|
+
if (manifest.package !== pkgJson.name) {
|
|
293
|
+
errors.push(
|
|
294
|
+
`\u6E05\u5355 package "${manifest.package}" \u4E0E package.json name "${String(pkgJson.name)}" \u4E0D\u4E00\u81F4`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const binKeys = isPlainObject(pkgJson.bin) ? Object.keys(pkgJson.bin) : [];
|
|
298
|
+
if (!binKeys.includes(manifest.bin)) {
|
|
299
|
+
errors.push(`\u6E05\u5355 bin "${manifest.bin}" \u4E0D\u5728 package.json bin (${binKeys.join(", ") || "\u7A7A"}) \u4E2D`);
|
|
300
|
+
}
|
|
301
|
+
return errors;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/core/official.ts
|
|
305
|
+
var OFFICIAL_PLUGINS = [
|
|
306
|
+
{
|
|
307
|
+
package: "@kevlns/xlmerge",
|
|
308
|
+
command: "xlmerge",
|
|
309
|
+
description: "Git \u4E2D .xlsx/.xlsm \u51B2\u7A81\u53EF\u89C6\u5316\u89E3\u51B3\u65B9\u6848\uFF08\u4E09\u5411 diff\u3001\u672C\u5730 UI\u3001\u5199\u56DE\u4E0E\u63D0\u4EA4\uFF09"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
package: "@kevlns/u-cli-mod",
|
|
313
|
+
command: "unity",
|
|
314
|
+
description: "Unity \u5DE5\u7A0B\u7CBE\u786E\u7248\u672C\u8DEF\u7531 + \u5B98\u65B9 CLI \u4E0B\u8F7D + com.unity.pipeline \u9002\u914D\u5305\uFF08Windows-first\uFF09"
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
var OFFICIAL_COMMAND_NAMES = new Set(
|
|
318
|
+
OFFICIAL_PLUGINS.map((p) => p.command)
|
|
319
|
+
);
|
|
320
|
+
function defaultResolvePackage(pkg, opts = {}) {
|
|
321
|
+
const baseFile = opts.base ?? fileURLToPath(import.meta.url);
|
|
322
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
323
|
+
const resolveFrom = opts.resolveFrom ?? process.env.V_CLI_PLUGIN_RESOLVE_FROM;
|
|
324
|
+
const candidates = [];
|
|
325
|
+
if (resolveFrom) {
|
|
326
|
+
candidates.push(path3.join(path3.resolve(cwd, resolveFrom), "node_modules", pkg, "package.json"));
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
candidates.push(createRequire(baseFile).resolve(`${pkg}/package.json`));
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
const reqCwd = createRequire(path3.join(cwd, "__v_cli_resolve__.cjs"));
|
|
334
|
+
candidates.push(reqCwd.resolve(`${pkg}/package.json`, { paths: [cwd] }));
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
for (const candidate of candidates) {
|
|
338
|
+
try {
|
|
339
|
+
if (fs2.existsSync(candidate)) return candidate;
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return void 0;
|
|
344
|
+
}
|
|
345
|
+
function defaultExists(file) {
|
|
346
|
+
try {
|
|
347
|
+
return fs2.existsSync(file);
|
|
348
|
+
} catch {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function defaultRead(file) {
|
|
353
|
+
return fs2.readFileSync(file, "utf-8");
|
|
354
|
+
}
|
|
355
|
+
function formatErrors(errors) {
|
|
356
|
+
return errors.join("\uFF1B");
|
|
357
|
+
}
|
|
358
|
+
function isPlainObject2(v) {
|
|
359
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
360
|
+
}
|
|
361
|
+
function discoverOfficialPlugin(spec, io = {}) {
|
|
362
|
+
const platform = io.platform ?? process.platform;
|
|
363
|
+
const exists = io.existsFile ?? defaultExists;
|
|
364
|
+
const read = io.readFile ?? defaultRead;
|
|
365
|
+
const resolvePackage = io.resolvePackage ?? defaultResolvePackage;
|
|
366
|
+
const base = {
|
|
367
|
+
source: "official",
|
|
368
|
+
package: spec.package,
|
|
369
|
+
name: spec.command,
|
|
370
|
+
description: spec.description,
|
|
371
|
+
status: "missing",
|
|
372
|
+
platform
|
|
373
|
+
};
|
|
374
|
+
let pkgJsonPath;
|
|
375
|
+
try {
|
|
376
|
+
pkgJsonPath = resolvePackage(spec.package);
|
|
377
|
+
} catch {
|
|
378
|
+
pkgJsonPath = void 0;
|
|
379
|
+
}
|
|
380
|
+
if (!pkgJsonPath || !exists(pkgJsonPath)) {
|
|
381
|
+
return {
|
|
382
|
+
...base,
|
|
383
|
+
error: `\u672A\u627E\u5230\u5B98\u65B9\u63D2\u4EF6\u5305 ${spec.package}\uFF08\u672A\u5B89\u88C5\u6216\u4E0D\u5728\u53EF\u89E3\u6790\u8DEF\u5F84\uFF1B\u8BF7\u5B89\u88C5\u540E\u91CD\u8BD5\uFF09`
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
let pkgJson;
|
|
387
|
+
try {
|
|
388
|
+
pkgJson = JSON.parse(read(pkgJsonPath));
|
|
389
|
+
} catch (err) {
|
|
390
|
+
return {
|
|
391
|
+
...base,
|
|
392
|
+
status: "invalid",
|
|
393
|
+
error: `package.json \u4E0D\u662F\u5408\u6CD5 JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
const vCliMeta = pkgJson.vCli && isPlainObject2(pkgJson.vCli) ? pkgJson.vCli : {};
|
|
397
|
+
const manifestRel = typeof vCliMeta.manifest === "string" ? vCliMeta.manifest : "v-cli.plugin.json";
|
|
398
|
+
const pkgRoot = path3.dirname(pkgJsonPath);
|
|
399
|
+
const manifestPath = path3.join(pkgRoot, manifestRel);
|
|
400
|
+
if (!exists(manifestPath)) {
|
|
401
|
+
return {
|
|
402
|
+
...base,
|
|
403
|
+
error: `\u5305\u5185\u7F3A\u5C11\u63D2\u4EF6\u6E05\u5355 ${manifestRel}\uFF08package.json \u7684 vCli.manifest \u6307\u5411\u5B83\uFF09`
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
let manifestRaw;
|
|
407
|
+
try {
|
|
408
|
+
manifestRaw = read(manifestPath);
|
|
409
|
+
} catch (err) {
|
|
410
|
+
return {
|
|
411
|
+
...base,
|
|
412
|
+
error: `\u8BFB\u53D6\u6E05\u5355\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
let parsed;
|
|
416
|
+
try {
|
|
417
|
+
parsed = JSON.parse(manifestRaw);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
return {
|
|
420
|
+
...base,
|
|
421
|
+
status: "invalid",
|
|
422
|
+
error: `\u6E05\u5355 ${manifestRel} \u4E0D\u662F\u5408\u6CD5 JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const validated = validateManifest(parsed);
|
|
426
|
+
if (!validated.ok) {
|
|
427
|
+
return { ...base, status: "invalid", error: `\u6E05\u5355\u6821\u9A8C\u5931\u8D25: ${formatErrors(validated.errors)}` };
|
|
428
|
+
}
|
|
429
|
+
const manifest = validated.manifest;
|
|
430
|
+
const identityErrors = validatePluginIdentity(manifest, pkgJson);
|
|
431
|
+
if (identityErrors.length > 0) {
|
|
432
|
+
return {
|
|
433
|
+
...base,
|
|
434
|
+
status: "invalid",
|
|
435
|
+
error: `\u8EAB\u4EFD\u6821\u9A8C\u5931\u8D25: ${formatErrors(identityErrors)}`
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (manifest.command !== spec.command) {
|
|
439
|
+
return {
|
|
440
|
+
...base,
|
|
441
|
+
status: "invalid",
|
|
442
|
+
error: `\u6E05\u5355 command "${manifest.command}" \u4E0E\u5B98\u65B9\u767D\u540D\u5355\u547D\u4EE4 "${spec.command}" \u4E0D\u4E00\u81F4`
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
const binMap = pkgJson.bin;
|
|
446
|
+
const binRel = isPlainObject2(binMap) ? binMap[manifest.bin] : void 0;
|
|
447
|
+
if (typeof binRel !== "string" || binRel.length === 0) {
|
|
448
|
+
return {
|
|
449
|
+
...base,
|
|
450
|
+
status: "invalid",
|
|
451
|
+
error: `bin "${manifest.bin}" \u7684\u542F\u52A8\u76EE\u6807\u4E0D\u662F\u5B57\u7B26\u4E32\u8DEF\u5F84`
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
const binPath = path3.resolve(pkgRoot, binRel);
|
|
455
|
+
const rootPrefix = pkgRoot.endsWith(path3.sep) ? pkgRoot : pkgRoot + path3.sep;
|
|
456
|
+
if (!binPath.startsWith(rootPrefix)) {
|
|
457
|
+
return {
|
|
458
|
+
...base,
|
|
459
|
+
status: "invalid",
|
|
460
|
+
error: `bin "${manifest.bin}" \u7684\u76EE\u6807 ${binRel} \u9003\u9038\u4E86\u5305\u76EE\u5F55`
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
const requiredPlatforms = manifest.platforms;
|
|
464
|
+
if (!requiredPlatforms.includes(platform)) {
|
|
465
|
+
return {
|
|
466
|
+
...base,
|
|
467
|
+
status: "platform-mismatch",
|
|
468
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : void 0,
|
|
469
|
+
requiredPlatforms,
|
|
470
|
+
error: `\u8BE5\u63D2\u4EF6\u4EC5\u652F\u6301\u5E73\u53F0 [${requiredPlatforms.join(", ")}]\uFF0C\u5F53\u524D\u5E73\u53F0\u4E3A ${platform}`
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
if (!exists(binPath)) {
|
|
474
|
+
return {
|
|
475
|
+
...base,
|
|
476
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : void 0,
|
|
477
|
+
requiredPlatforms,
|
|
478
|
+
error: `bin \u6587\u4EF6\u4E0D\u5B58\u5728: ${binRel}\uFF08manifest.bin=${manifest.bin}\uFF09`
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
...base,
|
|
483
|
+
status: "available",
|
|
484
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : void 0,
|
|
485
|
+
requiredPlatforms,
|
|
486
|
+
bin: binPath,
|
|
487
|
+
whenToUse: manifest.agent.whenToUse,
|
|
488
|
+
globalOptions: manifest.agent.globalOptions,
|
|
489
|
+
commands: manifest.agent.commands.map((c) => ({
|
|
490
|
+
path: c.path,
|
|
491
|
+
usage: c.usage,
|
|
492
|
+
description: c.description,
|
|
493
|
+
arguments: c.arguments,
|
|
494
|
+
options: c.options,
|
|
495
|
+
output: c.output,
|
|
496
|
+
exitCodes: c.exitCodes,
|
|
497
|
+
safety: c.safety
|
|
498
|
+
})),
|
|
499
|
+
runtime: manifest.runtime,
|
|
500
|
+
environment: manifest.environment
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function discoverAllOfficialPlugins(io = {}) {
|
|
504
|
+
return OFFICIAL_PLUGINS.map((spec) => discoverOfficialPlugin(spec, io));
|
|
505
|
+
}
|
|
506
|
+
function isOfficialCommand(token) {
|
|
507
|
+
return OFFICIAL_COMMAND_NAMES.has(token);
|
|
508
|
+
}
|
|
509
|
+
|
|
82
510
|
// src/core/command.ts
|
|
83
|
-
|
|
84
|
-
|
|
511
|
+
var RESERVED_COMMAND_NAMES = ["doctor", "plugin", "ts", "agent", "help"];
|
|
512
|
+
var NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
513
|
+
function isStringArray2(v) {
|
|
514
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
515
|
+
}
|
|
516
|
+
function isPlainObject3(v) {
|
|
517
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
518
|
+
}
|
|
519
|
+
function validateAgentMeta(agent2, errors) {
|
|
520
|
+
if (agent2 === void 0 || agent2 === null) return;
|
|
521
|
+
if (!isPlainObject3(agent2)) {
|
|
522
|
+
errors.push("agent \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (agent2.whenToUse !== void 0 && typeof agent2.whenToUse !== "string") {
|
|
526
|
+
errors.push("agent.whenToUse \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
|
|
527
|
+
}
|
|
528
|
+
if (agent2.globalOptions !== void 0) {
|
|
529
|
+
if (!Array.isArray(agent2.globalOptions)) {
|
|
530
|
+
errors.push("agent.globalOptions \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
531
|
+
} else {
|
|
532
|
+
agent2.globalOptions.forEach((o, i) => {
|
|
533
|
+
if (!isPlainObject3(o) || typeof o.flags !== "string" || typeof o.description !== "string") {
|
|
534
|
+
errors.push(`agent.globalOptions[${i}] \u5FC5\u987B\u662F {flags: string, description: string}`);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (agent2.commands !== void 0) {
|
|
540
|
+
if (!Array.isArray(agent2.commands)) {
|
|
541
|
+
errors.push("agent.commands \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
542
|
+
} else {
|
|
543
|
+
agent2.commands.forEach((c, i) => {
|
|
544
|
+
const at = `agent.commands[${i}]`;
|
|
545
|
+
if (!isPlainObject3(c) || typeof c.description !== "string") {
|
|
546
|
+
errors.push(`${at} \u5FC5\u987B\u662F\u542B\u5B57\u7B26\u4E32 description \u7684\u5BF9\u8C61`);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (!Array.isArray(c.path) || !isStringArray2(c.path) || c.path.length === 0) {
|
|
550
|
+
errors.push(`${at}.path \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u6BCF\u6BB5\u5339\u914D ^[a-z][a-z0-9-]*$\uFF09`);
|
|
551
|
+
} else if (c.path.some((seg) => !NAME_RE.test(seg))) {
|
|
552
|
+
errors.push(`${at}.path \u542B\u975E\u6CD5\u6BB5\uFF08\u6BCF\u6BB5\u987B\u5339\u914D ^[a-z][a-z0-9-]*$\uFF09`);
|
|
553
|
+
}
|
|
554
|
+
if (c.usage !== void 0 && typeof c.usage !== "string") {
|
|
555
|
+
errors.push(`${at}.usage \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
556
|
+
}
|
|
557
|
+
if (c.arguments !== void 0) {
|
|
558
|
+
const bad = !Array.isArray(c.arguments) || c.arguments.some(
|
|
559
|
+
(a) => !isPlainObject3(a) || typeof a.name !== "string" || typeof a.required !== "boolean" || typeof a.description !== "string"
|
|
560
|
+
);
|
|
561
|
+
if (bad) errors.push(`${at}.arguments \u5FC5\u987B\u662F { name: string; required: boolean; description: string }[]`);
|
|
562
|
+
}
|
|
563
|
+
if (c.options !== void 0) {
|
|
564
|
+
const bad = !Array.isArray(c.options) || c.options.some((o) => !isPlainObject3(o) || typeof o.flags !== "string" || typeof o.description !== "string");
|
|
565
|
+
if (bad) errors.push(`${at}.options \u5FC5\u987B\u662F { flags: string; description: string }[]`);
|
|
566
|
+
}
|
|
567
|
+
if (c.output !== void 0) {
|
|
568
|
+
const bad = !isPlainObject3(c.output) || typeof c.output.format !== "string" || typeof c.output.description !== "string";
|
|
569
|
+
if (bad) errors.push(`${at}.output \u5FC5\u987B\u662F { format: string; description: string }`);
|
|
570
|
+
}
|
|
571
|
+
if (c.exitCodes !== void 0) {
|
|
572
|
+
const bad = !isPlainObject3(c.exitCodes) || Object.entries(c.exitCodes).some(([code, desc]) => typeof code !== "string" || typeof desc !== "string");
|
|
573
|
+
if (bad) errors.push(`${at}.exitCodes \u5FC5\u987B\u662F { [\u9000\u51FA\u7801: string]: string }`);
|
|
574
|
+
}
|
|
575
|
+
if (c.safety !== void 0 && !isStringArray2(c.safety)) {
|
|
576
|
+
errors.push(`${at}.safety \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4`);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function validateLocalCommand(candidate, options = {}) {
|
|
583
|
+
const errors = [];
|
|
584
|
+
if (!isPlainObject3(candidate)) {
|
|
585
|
+
return { ok: false, errors: ["\u63D2\u4EF6\u5BFC\u51FA\u5FC5\u987B\u662F\u5BF9\u8C61"] };
|
|
586
|
+
}
|
|
85
587
|
const cmd = candidate;
|
|
86
|
-
|
|
588
|
+
if (cmd.apiVersion !== 1) {
|
|
589
|
+
const got = cmd.apiVersion === void 0 ? "\u7F3A\u5931" : JSON.stringify(cmd.apiVersion);
|
|
590
|
+
errors.push(
|
|
591
|
+
`apiVersion \u5FC5\u987B\u4E3A 1\uFF08\u5F53\u524D: ${got}\uFF09\u3002v-cli 0.2 \u8D77\u91C7\u7528 apiVersion 1 \u63D2\u4EF6\u5951\u7EA6\uFF0C\u65E7\u7248\u63D2\u4EF6\u8BF7\u8865\u4E0A apiVersion: 1 \u540E\u91CD\u65B0\u52A0\u8F7D`
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
if (typeof cmd.name !== "string" || cmd.name.length === 0) {
|
|
595
|
+
errors.push("name \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
596
|
+
} else {
|
|
597
|
+
if (!NAME_RE.test(cmd.name)) {
|
|
598
|
+
errors.push(`\u547D\u4EE4\u540D "${cmd.name}" \u4E0D\u5408\u6CD5\uFF1A\u53EA\u80FD\u662F\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\u7684 [a-z0-9-] \u5B57\u7B26\u4E32`);
|
|
599
|
+
}
|
|
600
|
+
const reserved = [...RESERVED_COMMAND_NAMES, ...OFFICIAL_COMMAND_NAMES];
|
|
601
|
+
if (reserved.includes(cmd.name)) {
|
|
602
|
+
const kind = RESERVED_COMMAND_NAMES.includes(cmd.name) ? "v-cli \u5185\u7F6E\u4FDD\u7559\u547D\u4EE4" : "\u5B98\u65B9\u63D2\u4EF6\u547D\u4EE4";
|
|
603
|
+
errors.push(`\u547D\u4EE4\u540D "${cmd.name}" \u662F${kind}\uFF0C\u672C\u5730\u63D2\u4EF6\u4E0D\u80FD\u5360\u7528`);
|
|
604
|
+
}
|
|
605
|
+
if ((options.existingNames ?? []).includes(cmd.name)) {
|
|
606
|
+
errors.push(`\u91CD\u590D\u7684\u547D\u4EE4\u540D "${cmd.name}"\uFF08\u5DF2\u7531\u5176\u4ED6\u672C\u5730\u63D2\u4EF6\u5B9A\u4E49\uFF09`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (typeof cmd.description !== "string" || cmd.description.length === 0) {
|
|
610
|
+
errors.push("description \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
611
|
+
}
|
|
612
|
+
if (typeof cmd.register !== "function") {
|
|
613
|
+
errors.push("register \u5FC5\u987B\u662F\u51FD\u6570");
|
|
614
|
+
}
|
|
615
|
+
validateAgentMeta(cmd.agent, errors);
|
|
616
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
617
|
+
return { ok: true, command: candidate, errors: [] };
|
|
87
618
|
}
|
|
88
619
|
|
|
89
|
-
// src/commands/
|
|
90
|
-
import
|
|
620
|
+
// src/commands/agent.ts
|
|
621
|
+
import path5 from "path";
|
|
622
|
+
|
|
623
|
+
// src/core/agent-docs.ts
|
|
624
|
+
import fs3 from "fs";
|
|
625
|
+
import path4 from "path";
|
|
626
|
+
import { createHash, randomBytes } from "crypto";
|
|
627
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
91
628
|
|
|
92
629
|
// src/version.ts
|
|
93
|
-
var VERSION = "0.
|
|
630
|
+
var VERSION = "0.2.0-beta.2";
|
|
631
|
+
|
|
632
|
+
// src/core/agent-docs.ts
|
|
633
|
+
var BUNDLED_DOCS_FILE = "AGENTS.md";
|
|
634
|
+
var BUNDLED_DOCS_PACKAGE = "@kevlns/v-cli";
|
|
635
|
+
function bundledCandidatePaths(moduleFile) {
|
|
636
|
+
const candidates = [];
|
|
637
|
+
const push = (p) => {
|
|
638
|
+
if (!candidates.includes(p)) candidates.push(p);
|
|
639
|
+
};
|
|
640
|
+
if (path4.basename(path4.dirname(moduleFile)) === "dist") {
|
|
641
|
+
push(path4.join(path4.dirname(path4.dirname(moduleFile)), BUNDLED_DOCS_FILE));
|
|
642
|
+
}
|
|
643
|
+
let dir = path4.dirname(moduleFile);
|
|
644
|
+
for (; ; ) {
|
|
645
|
+
if (pkgName(path4.join(dir, "package.json")) === BUNDLED_DOCS_PACKAGE) {
|
|
646
|
+
push(path4.join(dir, BUNDLED_DOCS_FILE));
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
const nestedPkg = path4.join(dir, "node_modules", "@kevlns", "v-cli", "package.json");
|
|
650
|
+
if (pkgName(nestedPkg) === BUNDLED_DOCS_PACKAGE) {
|
|
651
|
+
push(path4.join(dir, "node_modules", "@kevlns", "v-cli", BUNDLED_DOCS_FILE));
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
const parent = path4.dirname(dir);
|
|
655
|
+
if (parent === dir) break;
|
|
656
|
+
dir = parent;
|
|
657
|
+
}
|
|
658
|
+
return candidates;
|
|
659
|
+
}
|
|
660
|
+
function pkgName(pkgJsonPath) {
|
|
661
|
+
try {
|
|
662
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
|
|
663
|
+
return typeof pkg.name === "string" ? pkg.name : void 0;
|
|
664
|
+
} catch {
|
|
665
|
+
return void 0;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function resolveBundledAgentsMd(opts = {}) {
|
|
669
|
+
const moduleFile = opts.base ?? fileURLToPath2(import.meta.url);
|
|
670
|
+
for (const candidate of bundledCandidatePaths(moduleFile)) {
|
|
671
|
+
try {
|
|
672
|
+
if (fs3.lstatSync(candidate).isFile()) return candidate;
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return void 0;
|
|
677
|
+
}
|
|
678
|
+
function readBundledAgentsMd(opts = {}) {
|
|
679
|
+
const file = resolveBundledAgentsMd(opts);
|
|
680
|
+
if (!file) {
|
|
681
|
+
const tried = bundledCandidatePaths(opts.base ?? fileURLToPath2(import.meta.url));
|
|
682
|
+
const lastTried = tried.length > 0 ? tried[tried.length - 1] : "\uFF08\u65E0\u5019\u9009\u4F4D\u7F6E\uFF09";
|
|
683
|
+
throw new Error(
|
|
684
|
+
`\u65E0\u6CD5\u5B9A\u4F4D ${BUNDLED_DOCS_PACKAGE} \u5185\u7F6E ${BUNDLED_DOCS_FILE}\uFF1A\u5DF2\u68C0\u67E5 ${tried.length} \u4E2A\u5019\u9009\u4F4D\u7F6E\u5747\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u8BFB\uFF08\u6700\u540E\u68C0\u67E5: ${lastTried}\uFF09\u3002\u8BF7\u786E\u8BA4\u5B89\u88C5\u7684\u5305\u5185\u5305\u542B AGENTS.md\uFF08\u91CD\u65B0\u5B89\u88C5 @kevlns/v-cli \u540E\u91CD\u8BD5\uFF09\u3002`
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
let content;
|
|
688
|
+
try {
|
|
689
|
+
content = fs3.readFileSync(file, "utf-8");
|
|
690
|
+
} catch (err) {
|
|
691
|
+
throw new Error(
|
|
692
|
+
`\u65E0\u6CD5\u8BFB\u53D6 ${BUNDLED_DOCS_PACKAGE} \u5185\u7F6E ${BUNDLED_DOCS_FILE}\uFF08${file}\uFF09: ${err instanceof Error ? err.message : String(err)}`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
file,
|
|
697
|
+
content,
|
|
698
|
+
sha256: sha256Hex(content),
|
|
699
|
+
bytes: Buffer.byteLength(content, "utf-8")
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function sha256Hex(content) {
|
|
703
|
+
return createHash("sha256").update(content, "utf-8").digest("hex");
|
|
704
|
+
}
|
|
705
|
+
function performAgentInit(opts) {
|
|
706
|
+
const { directory, docs, force = false, dryRun = false, explicitDirectory = false } = opts;
|
|
707
|
+
const target = path4.join(directory, BUNDLED_DOCS_FILE);
|
|
708
|
+
const base = {
|
|
709
|
+
ok: false,
|
|
710
|
+
dryRun,
|
|
711
|
+
action: "refused",
|
|
712
|
+
directory,
|
|
713
|
+
target,
|
|
714
|
+
package: BUNDLED_DOCS_PACKAGE,
|
|
715
|
+
version: VERSION,
|
|
716
|
+
sha256: docs.sha256,
|
|
717
|
+
bytes: docs.bytes
|
|
718
|
+
};
|
|
719
|
+
const refuse = (reason) => ({ ...base, ok: false, action: "refused", reason });
|
|
720
|
+
let dirStat;
|
|
721
|
+
try {
|
|
722
|
+
dirStat = fs3.statSync(directory);
|
|
723
|
+
} catch {
|
|
724
|
+
return refuse(`\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u8BBF\u95EE: ${directory}`);
|
|
725
|
+
}
|
|
726
|
+
if (!dirStat.isDirectory()) {
|
|
727
|
+
return refuse(`\u76EE\u6807\u4E0D\u662F\u76EE\u5F55: ${directory}`);
|
|
728
|
+
}
|
|
729
|
+
if (explicitDirectory) {
|
|
730
|
+
let dirLstat;
|
|
731
|
+
try {
|
|
732
|
+
dirLstat = fs3.lstatSync(directory);
|
|
733
|
+
} catch {
|
|
734
|
+
}
|
|
735
|
+
if (dirLstat && dirLstat.isSymbolicLink()) {
|
|
736
|
+
return refuse(
|
|
737
|
+
`\u62D2\u7EDD\u5199\u5165\uFF1A\u76EE\u6807\u76EE\u5F55 ${directory} \u662F\u7B26\u53F7\u94FE\u63A5/\u8054\u63A5\u76EE\u5F55\uFF08fail-closed\uFF0C\u4E0D\u8DDF\u968F\uFF09\u3002\u8BF7\u4F20\u5165\u771F\u5B9E\u76EE\u5F55\u8DEF\u5F84\uFF0C\u6216\u7701\u7565\u53C2\u6570\u8BA9 v-cli \u4F7F\u7528\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
let targetStat;
|
|
742
|
+
try {
|
|
743
|
+
targetStat = fs3.lstatSync(target);
|
|
744
|
+
} catch {
|
|
745
|
+
targetStat = void 0;
|
|
746
|
+
}
|
|
747
|
+
if (targetStat) {
|
|
748
|
+
if (targetStat.isSymbolicLink()) {
|
|
749
|
+
return refuse(
|
|
750
|
+
`\u62D2\u7EDD\u5199\u5165 ${target}\uFF1A\u76EE\u6807\u662F\u7B26\u53F7\u94FE\u63A5\uFF08fail-closed\uFF0C\u4E0D\u8DDF\u968F\u3001\u4E0D\u8986\u76D6\u94FE\u63A5\u76EE\u6807\uFF09\u3002\u8BF7\u5148\u79FB\u9664\u94FE\u63A5\u6216\u9009\u62E9\u5176\u4ED6\u76EE\u5F55`
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
if (targetStat.isDirectory()) {
|
|
754
|
+
return refuse(`\u62D2\u7EDD\u5199\u5165 ${target}\uFF1A\u76EE\u6807\u662F\u4E00\u4E2A\u76EE\u5F55\uFF08\u65E0\u6CD5\u4EE5\u6587\u4EF6\u8986\u76D6\uFF09`);
|
|
755
|
+
}
|
|
756
|
+
if (!force) {
|
|
757
|
+
return refuse(
|
|
758
|
+
`${target} \u5DF2\u5B58\u5728\uFF0C\u9ED8\u8BA4\u62D2\u7EDD\u8986\u76D6\uFF08\u4E0D\u505A\u4EFB\u4F55\u6539\u52A8\uFF09\u3002\u5982\u9700\u8986\u76D6\u8BF7\u4F7F\u7528 --force\uFF1B\u5982\u9700\u9884\u89C8\u8BF7\u4F7F\u7528 --dry-run\uFF1B\u5982\u9700\u5148\u67E5\u770B\u5185\u5BB9\u8BF7\u8FD0\u884C v-cli agent docs`
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const existed = targetStat !== void 0;
|
|
763
|
+
const action = existed ? dryRun ? "overwrite" : "overwritten" : dryRun ? "write" : "written";
|
|
764
|
+
if (dryRun) {
|
|
765
|
+
return { ...base, ok: true, action };
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
atomicWrite(target, docs.content);
|
|
769
|
+
} catch (err) {
|
|
770
|
+
return refuse(`\u5199\u5165\u5931\u8D25 ${target}: ${err instanceof Error ? err.message : String(err)}`);
|
|
771
|
+
}
|
|
772
|
+
return { ...base, ok: true, action };
|
|
773
|
+
}
|
|
774
|
+
function isEexist(err) {
|
|
775
|
+
return typeof err === "object" && err !== null && err.code === "EEXIST";
|
|
776
|
+
}
|
|
777
|
+
var MAX_TMP_RETRIES = 5;
|
|
778
|
+
function atomicWrite(target, content) {
|
|
779
|
+
let lastError;
|
|
780
|
+
for (let attempt = 0; attempt < MAX_TMP_RETRIES; attempt++) {
|
|
781
|
+
const tmp = path4.join(
|
|
782
|
+
path4.dirname(target),
|
|
783
|
+
`.${BUNDLED_DOCS_FILE}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`
|
|
784
|
+
);
|
|
785
|
+
let created = false;
|
|
786
|
+
try {
|
|
787
|
+
fs3.writeFileSync(tmp, content, { encoding: "utf-8", flag: "wx", mode: 420 });
|
|
788
|
+
created = true;
|
|
789
|
+
fs3.renameSync(tmp, target);
|
|
790
|
+
return;
|
|
791
|
+
} catch (err) {
|
|
792
|
+
lastError = err;
|
|
793
|
+
if (created) {
|
|
794
|
+
try {
|
|
795
|
+
fs3.rmSync(tmp, { force: true });
|
|
796
|
+
} catch {
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (isEexist(err)) continue;
|
|
800
|
+
throw err;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
const detail = lastError instanceof Error ? lastError.message : String(lastError ?? "\u672A\u77E5\u9519\u8BEF");
|
|
804
|
+
throw new Error(`\u539F\u5B50\u5199\u5165\u5931\u8D25\uFF08\u4E34\u65F6\u6587\u4EF6\u547D\u540D\u78B0\u649E\u91CD\u8BD5 ${MAX_TMP_RETRIES} \u6B21\u672A\u679C\uFF09: ${detail}`);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/commands/agent.ts
|
|
808
|
+
function buildAgentIndex(builtin, local, official) {
|
|
809
|
+
const rows = [];
|
|
810
|
+
for (const cmd of builtin) {
|
|
811
|
+
rows.push({
|
|
812
|
+
name: cmd.name,
|
|
813
|
+
type: "builtin",
|
|
814
|
+
description: cmd.description,
|
|
815
|
+
metadataStatus: cmd.agent ? "full" : "minimal",
|
|
816
|
+
whenToUse: cmd.agent?.whenToUse,
|
|
817
|
+
globalOptions: cmd.agent?.globalOptions,
|
|
818
|
+
commands: cmd.agent?.commands
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
for (const item of local) {
|
|
822
|
+
const cmd = item.command;
|
|
823
|
+
if (!cmd) {
|
|
824
|
+
rows.push({
|
|
825
|
+
name: item.file ? item.file.split(/[\\/]/).pop()?.replace(/\.mjs$/, "") ?? "(\u52A0\u8F7D\u5931\u8D25)" : "(\u52A0\u8F7D\u5931\u8D25)",
|
|
826
|
+
type: "local",
|
|
827
|
+
description: item.error ?? "\u52A0\u8F7D\u5931\u8D25",
|
|
828
|
+
metadataStatus: "minimal",
|
|
829
|
+
error: item.error
|
|
830
|
+
});
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
rows.push({
|
|
834
|
+
name: cmd.name,
|
|
835
|
+
type: "local",
|
|
836
|
+
description: cmd.description,
|
|
837
|
+
metadataStatus: cmd.agent ? "full" : "minimal",
|
|
838
|
+
whenToUse: cmd.agent?.whenToUse,
|
|
839
|
+
globalOptions: cmd.agent?.globalOptions,
|
|
840
|
+
commands: cmd.agent?.commands
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
for (const info of official) {
|
|
844
|
+
rows.push({
|
|
845
|
+
name: info.name,
|
|
846
|
+
type: "official",
|
|
847
|
+
description: info.description,
|
|
848
|
+
metadataStatus: info.status === "available" ? "full" : "minimal",
|
|
849
|
+
package: info.package,
|
|
850
|
+
version: info.version,
|
|
851
|
+
status: info.status,
|
|
852
|
+
platform: info.platform,
|
|
853
|
+
requiredPlatforms: info.requiredPlatforms,
|
|
854
|
+
error: info.error,
|
|
855
|
+
whenToUse: info.whenToUse,
|
|
856
|
+
globalOptions: info.globalOptions,
|
|
857
|
+
// 完整清单元数据全量透传(arguments/options/output/exitCodes/safety)
|
|
858
|
+
commands: info.commands,
|
|
859
|
+
runtime: info.runtime,
|
|
860
|
+
environment: info.environment
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
return rows;
|
|
864
|
+
}
|
|
865
|
+
function rowToText(row) {
|
|
866
|
+
const status = row.status ? ` [${row.status}]` : "";
|
|
867
|
+
const meta = row.metadataStatus === "full" ? "\u5B8C\u6574\u5143\u6570\u636E" : "\u6700\u5C0F\u5143\u6570\u636E";
|
|
868
|
+
return `[${row.type}] ${row.name}${status}: ${row.description} (${meta})`;
|
|
869
|
+
}
|
|
870
|
+
function describeCommandLine(c) {
|
|
871
|
+
const pathText = Array.isArray(c.path) ? c.path.join(" ") : "";
|
|
872
|
+
const argText = Array.isArray(c.arguments) && c.arguments.length > 0 ? ` \u53C2\u6570: ${c.arguments.map((a) => `${a.required ? `<${a.name}>` : `[${a.name}]`}`).join(", ")}` : "";
|
|
873
|
+
const optText = Array.isArray(c.options) && c.options.length > 0 ? ` \u9009\u9879: ${c.options.map((o) => o.flags).join(", ")}` : "";
|
|
874
|
+
const outputText = c.output && typeof c.output === "object" && typeof c.output.format === "string" ? ` \u8F93\u51FA: ${c.output.format}${c.output.description ? `\uFF08${c.output.description}\uFF09` : ""}` : "";
|
|
875
|
+
const exitText = c.exitCodes && typeof c.exitCodes === "object" && Object.keys(c.exitCodes).length > 0 ? ` \u9000\u51FA\u7801: ${Object.entries(c.exitCodes).map(([code, desc]) => `${code}=${desc}`).join(", ")}` : "";
|
|
876
|
+
const safeText = Array.isArray(c.safety) && c.safety.length > 0 ? ` \u5B89\u5168: ${c.safety.join("\uFF1B")}` : "";
|
|
877
|
+
return ` ${pathText} \u2014 ${c.description}${c.usage ? ` (\u7528\u6CD5: ${c.usage})` : ""}${argText}${optText}${outputText}${exitText}${safeText}`;
|
|
878
|
+
}
|
|
879
|
+
var AGENT_HELP_TEXT = [
|
|
880
|
+
"agent \u5B50\u547D\u4EE4\u4E00\u89C8\uFF1A",
|
|
881
|
+
" index \u5217\u51FA\u5168\u90E8\u547D\u4EE4\uFF08builtin/local/official\uFF09\u4E0E agent \u5143\u6570\u636E\uFF1B--json \u8F93\u51FA\u7A33\u5B9A JSON \u6570\u7EC4",
|
|
882
|
+
" describe \u67E5\u770B\u5355\u4E2A\u547D\u4EE4\u7684\u5B8C\u6574\u8BB0\u5F55\uFF08\u7528\u6CD5/\u53C2\u6570/\u9009\u9879/\u8F93\u51FA/\u9000\u51FA\u7801/\u5B89\u5168\u6807\u7B7E\uFF09\uFF1B--json \u8F93\u51FA\u5355\u6761\u8BB0\u5F55",
|
|
883
|
+
" docs \u8F93\u51FA\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587\uFF1B--json \u8F93\u51FA { package, version, sha256, content }",
|
|
884
|
+
" init \u628A\u5185\u7F6E AGENTS.md \u5199\u5165\u5DE5\u4F5C\u533A\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B58\u5728\u9ED8\u8BA4\u62D2\u7EDD\uFF0C--force \u8986\u76D6\uFF0C--dry-run \u9884\u89C8",
|
|
885
|
+
"",
|
|
886
|
+
"JSON\uFF1A\u6240\u6709\u5B50\u547D\u4EE4\u652F\u6301 --json\uFF08\u6216\u524D\u7F6E\u5168\u5C40 --json\uFF0C\u5982 v-cli --json agent index\uFF09",
|
|
887
|
+
"",
|
|
888
|
+
"AI Agent \u5FEB\u901F\u5F00\u59CB\uFF08\u63A8\u8350\u987A\u5E8F\uFF09\uFF1A",
|
|
889
|
+
" 1. v-cli agent docs",
|
|
890
|
+
" 2. v-cli agent index --json",
|
|
891
|
+
" 3. v-cli agent describe <name> --json",
|
|
892
|
+
" 4. v-cli agent init . \uFF08\u53EF\u9009\uFF1A\u5199\u5165\u5DE5\u4F5C\u533A AGENTS.md\uFF0CAI Agent \u81EA\u52A8\u8BFB\u53D6\uFF09",
|
|
893
|
+
"",
|
|
894
|
+
"\u793A\u4F8B\uFF1A",
|
|
895
|
+
" v-cli agent index --json # \u5168\u91CF\u547D\u4EE4\u7D22\u5F15",
|
|
896
|
+
" v-cli agent describe ts --json # \u5355\u547D\u4EE4\u8BE6\u60C5",
|
|
897
|
+
" v-cli agent docs # \u5185\u7F6E\u5F15\u5BFC\u6587\u6863\u539F\u6587",
|
|
898
|
+
" v-cli agent init . --dry-run # \u9884\u89C8\u521D\u59CB\u5316\u76EE\u6807"
|
|
899
|
+
].join("\n");
|
|
900
|
+
var DOCS_HELP_TEXT = [
|
|
901
|
+
"\u8F93\u51FA\u5F53\u524D\u5B89\u88C5\u7684 @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587\uFF08AI Agent \u5F15\u5BFC\u6587\u6863\uFF09\uFF0C\u9010\u5B57\u8282\u539F\u6837\u8F93\u51FA\u3001\u4E0D\u8FFD\u52A0\u6362\u884C\u3002",
|
|
902
|
+
"",
|
|
903
|
+
"JSON \u884C\u4E3A\uFF08--json \u6216\u524D\u7F6E\u5168\u5C40 --json\uFF09\uFF1A",
|
|
904
|
+
" \u6210\u529F\uFF1Astdout \u8F93\u51FA\u7A33\u5B9A\u5BF9\u8C61 { package, version, sha256, content }",
|
|
905
|
+
" package \u5305\u540D @kevlns/v-cli",
|
|
906
|
+
" version \u5F53\u524D\u5305\u7248\u672C",
|
|
907
|
+
" sha256 content \u7684 SHA-256\uFF08hex\uFF09\uFF0C\u53EF\u6821\u9A8C\u5F15\u5BFC\u6587\u6863\u672A\u88AB\u7BE1\u6539",
|
|
908
|
+
" content AGENTS.md \u539F\u6587",
|
|
909
|
+
" \u5931\u8D25\uFF1Astdout \u8F93\u51FA\u7A00\u758F\u5BF9\u8C61 { ok: false, error }\uFF0Cstderr \u518D\u8F93\u51FA\u540C\u4E00 error\uFF0C\u9000\u51FA\u7801 1",
|
|
910
|
+
"",
|
|
911
|
+
"\u9000\u51FA\u7801\uFF1A0 \u6210\u529F\uFF1B1 \u5185\u7F6E AGENTS.md \u7F3A\u5931/\u4E0D\u53EF\u8BFB",
|
|
912
|
+
"",
|
|
913
|
+
"\u793A\u4F8B\uFF1A",
|
|
914
|
+
" v-cli agent docs",
|
|
915
|
+
" v-cli agent docs --json"
|
|
916
|
+
].join("\n");
|
|
917
|
+
var INIT_HELP_TEXT = [
|
|
918
|
+
"\u628A\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6837\u5199\u5165 <\u76EE\u5F55>/AGENTS.md\uFF0CAI Agent \u4F1A\u81EA\u52A8\u8BFB\u53D6\u5DE5\u4F5C\u533A\u6587\u6863\u3002",
|
|
919
|
+
"",
|
|
920
|
+
"\u53C2\u6570\uFF1A",
|
|
921
|
+
" [directory] \u76EE\u6807\u76EE\u5F55\uFF0C\u9ED8\u8BA4\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1B\u5FC5\u987B\u5DF2\u5B58\u5728\u4E14\u4E3A\u76EE\u5F55",
|
|
922
|
+
"",
|
|
923
|
+
"\u9ED8\u8BA4\u884C\u4E3A\uFF08\u65E0 --force\uFF09\uFF1A\u76EE\u6807\u5DF2\u5B58\u5728 AGENTS.md \u65F6\u62D2\u7EDD\u5E76\u9000\u51FA 1\uFF0C\u7EDD\u4E0D\u6539\u52A8\u73B0\u6709\u6587\u4EF6\u3002",
|
|
924
|
+
" --force \u8986\u76D6\u5DF2\u5B58\u5728\u7684 AGENTS.md\uFF08\u539F\u5B50\u5199\u5165\uFF1A\u540C\u76EE\u5F55\u4E34\u65F6\u6587\u4EF6 + rename\uFF09",
|
|
925
|
+
" --dry-run \u53EA\u62A5\u544A\u76EE\u6807\u4E0E\u5C06\u6267\u884C\u7684\u52A8\u4F5C\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6587\u4EF6",
|
|
926
|
+
" --json \u6210\u529F/\u5E72\u8DD1\u8F93\u51FA\u7A33\u5B9A JSON { ok, dryRun, action, directory, target, package, version, sha256, bytes }\uFF1B",
|
|
927
|
+
' \u62D2\u7EDD\uFF08\u5982\u5DF2\u5B58\u5728\u672A\u52A0 --force\uFF09\u8F93\u51FA { ok: false, action: "refused", reason, \u2026 } \u4E14\u9000\u51FA 1\uFF1B',
|
|
928
|
+
" \u5185\u7F6E\u6587\u6863\u7F3A\u5931\u65F6\u8F93\u51FA\u7A00\u758F { ok: false, error } \u4E14\u9000\u51FA 1",
|
|
929
|
+
" \u7B26\u53F7\u94FE\u63A5\u89C4\u5219\uFF1A\u76EE\u6807 AGENTS.md \u662F\u7B26\u53F7\u94FE\u63A5\u4E00\u5F8B\u62D2\u7EDD\uFF08fail-closed\uFF1A\u4E0D\u8DDF\u968F\u3001\u4E0D\u8986\u76D6\u94FE\u63A5\u76EE\u6807\uFF09\uFF1B",
|
|
930
|
+
" \u663E\u5F0F\u4F20\u5165\u7684\u76EE\u5F55\u672C\u8EAB\u662F\u7B26\u53F7\u94FE\u63A5/\u8054\u63A5\u76EE\u5F55\uFF08junction\uFF09\u4E5F\u62D2\u7EDD\uFF1B\u7701\u7565\u76EE\u5F55\u53C2\u6570\uFF08\u9ED8\u8BA4\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF09\u4E0D\u53D7\u6B64\u9650\u5236",
|
|
931
|
+
"",
|
|
932
|
+
"\u9000\u51FA\u7801\uFF1A0 \u6210\u529F\u6216\u5E72\u8DD1\uFF1B1 \u76EE\u5F55\u65E0\u6548 / \u5DF2\u5B58\u5728\u672A\u52A0 --force / \u7B26\u53F7\u94FE\u63A5\u6216\u8054\u63A5\u76EE\u6807 / \u5185\u7F6E\u6587\u6863\u7F3A\u5931 / \u5199\u5165\u5931\u8D25",
|
|
933
|
+
"",
|
|
934
|
+
"\u793A\u4F8B\uFF1A",
|
|
935
|
+
" v-cli agent init",
|
|
936
|
+
" v-cli agent init ./myproj",
|
|
937
|
+
" v-cli agent init --dry-run",
|
|
938
|
+
" v-cli agent init --force",
|
|
939
|
+
" v-cli agent init --json"
|
|
940
|
+
].join("\n");
|
|
941
|
+
var agent = {
|
|
942
|
+
name: "agent",
|
|
943
|
+
description: "agent\uFF1Aindex \u547D\u4EE4\u7D22\u5F15\uFF0Cdescribe \u8BE6\u60C5\uFF0Cdocs \u5185\u7F6E AGENTS.md\uFF0Cinit \u5DE5\u4F5C\u533A\u5F15\u5BFC",
|
|
944
|
+
apiVersion: 1,
|
|
945
|
+
agent: {
|
|
946
|
+
whenToUse: "\u5F53\u7528\u6237\u9700\u8981 AGENTS \u5F15\u5BFC\u6587\u6863\u3001\u547D\u4EE4\u53D1\u73B0\u6216\u4E3A AI Agent \u521D\u59CB\u5316\u5DE5\u4F5C\u533A\u65F6\uFF1A\u5148 `docs` \u8BFB\u5185\u7F6E AGENTS.md\uFF0C\u518D `index --json` \u679A\u4E3E\u5168\u90E8\u547D\u4EE4\u4E0E\u5143\u6570\u636E\uFF0C\u7528 `describe <name> --json` \u770B\u5355\u547D\u4EE4\u8BE6\u60C5\uFF0C\u6700\u540E\u53EF `init` \u628A AGENTS.md \u5199\u5165\u5DE5\u4F5C\u533A",
|
|
947
|
+
globalOptions: [
|
|
948
|
+
{ flags: "-h, --help", description: "\u663E\u793A\u547D\u4EE4\u5E2E\u52A9" }
|
|
949
|
+
],
|
|
950
|
+
commands: [
|
|
951
|
+
{
|
|
952
|
+
path: ["index"],
|
|
953
|
+
usage: "v-cli agent index [--json]",
|
|
954
|
+
description: "\u5217\u51FA\u5168\u90E8\u547D\u4EE4\uFF08builtin/local/official\uFF09\u4E0E agent \u5143\u6570\u636E",
|
|
955
|
+
arguments: [],
|
|
956
|
+
options: [{ flags: "--json", description: "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON" }],
|
|
957
|
+
output: {
|
|
958
|
+
format: "stdout",
|
|
959
|
+
description: "\u6B63\u6587\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u7A33\u5B9A JSON \u6570\u7EC4\uFF08name/type/description/metadataStatus/commands/\u2026\uFF09"
|
|
960
|
+
},
|
|
961
|
+
exitCodes: { "0": "\u6210\u529F" },
|
|
962
|
+
safety: ["read-only", "no-network"]
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
path: ["describe"],
|
|
966
|
+
usage: "v-cli agent describe <name> [--json]",
|
|
967
|
+
description: "\u67E5\u770B\u5355\u4E2A\u547D\u4EE4\u7684\u5B8C\u6574\u8BB0\u5F55",
|
|
968
|
+
arguments: [{ name: "name", required: true, description: "\u547D\u4EE4\u540D\uFF08builtin/local/official\uFF09" }],
|
|
969
|
+
options: [{ flags: "--json", description: "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON" }],
|
|
970
|
+
output: { format: "stdout", description: "\u6B63\u6587\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u5355\u6761\u7A33\u5B9A JSON \u5BF9\u8C61" },
|
|
971
|
+
exitCodes: { "0": "\u6210\u529F", "1": "\u672A\u627E\u5230\u547D\u4EE4\uFF08stderr \u8BF4\u660E\uFF09" },
|
|
972
|
+
safety: ["read-only"]
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
path: ["docs"],
|
|
976
|
+
usage: "v-cli agent docs [--json]",
|
|
977
|
+
description: "\u8F93\u51FA\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587",
|
|
978
|
+
arguments: [],
|
|
979
|
+
options: [{ flags: "--json", description: "\u8F93\u51FA { package, version, sha256, content }" }],
|
|
980
|
+
output: {
|
|
981
|
+
format: "stdout",
|
|
982
|
+
description: "AGENTS.md \u539F\u6587\u9010\u5B57\u8282\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u542B sha256/content \u7684\u7A33\u5B9A JSON"
|
|
983
|
+
},
|
|
984
|
+
exitCodes: { "0": "\u6210\u529F", "1": "\u5185\u7F6E AGENTS.md \u7F3A\u5931/\u4E0D\u53EF\u8BFB" },
|
|
985
|
+
safety: ["read-only", "no-network"]
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
path: ["init"],
|
|
989
|
+
usage: "v-cli agent init [directory] [--force] [--dry-run] [--json]",
|
|
990
|
+
description: "\u628A\u5185\u7F6E AGENTS.md \u5199\u5165 <\u76EE\u5F55>/AGENTS.md\uFF1B\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF1B\u5DF2\u5B58\u5728\u9ED8\u8BA4\u62D2\u7EDD",
|
|
991
|
+
arguments: [
|
|
992
|
+
{ name: "directory", required: false, description: "\u76EE\u6807\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1B\u987B\u5DF2\u5B58\u5728\u4E14\u4E3A\u76EE\u5F55\uFF09" }
|
|
993
|
+
],
|
|
994
|
+
options: [
|
|
995
|
+
{ flags: "--force", description: "\u8986\u76D6\u5DF2\u5B58\u5728 AGENTS.md\uFF08\u539F\u5B50\u5199\u5165\uFF09" },
|
|
996
|
+
{ flags: "--dry-run", description: "\u53EA\u62A5\u544A\u76EE\u6807\u4E0E\u52A8\u4F5C\uFF0C\u4E0D\u5199\u5165" },
|
|
997
|
+
{ flags: "--json", description: "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB\u7ED3\u679C" }
|
|
998
|
+
],
|
|
999
|
+
output: {
|
|
1000
|
+
format: "stdout",
|
|
1001
|
+
description: "\u7ED3\u679C\u6587\u672C\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u7A33\u5B9A JSON\uFF08ok/dryRun/action/directory/target/package/version/sha256/bytes\uFF09"
|
|
1002
|
+
},
|
|
1003
|
+
exitCodes: {
|
|
1004
|
+
"0": "\u6210\u529F\u6216\u5E72\u8DD1",
|
|
1005
|
+
"1": "\u76EE\u5F55\u65E0\u6548 / \u5DF2\u5B58\u5728\u672A\u8986\u76D6 / \u7B26\u53F7\u94FE\u63A5\u6216\u8054\u63A5\u76EE\u6807 / \u6587\u6863\u7F3A\u5931 / \u5199\u5165\u5931\u8D25"
|
|
1006
|
+
},
|
|
1007
|
+
safety: [
|
|
1008
|
+
"writes-target-atomic",
|
|
1009
|
+
"refuses-existing",
|
|
1010
|
+
"fail-closed-symlink",
|
|
1011
|
+
"dry-run-supported",
|
|
1012
|
+
"no-commit"
|
|
1013
|
+
]
|
|
1014
|
+
}
|
|
1015
|
+
]
|
|
1016
|
+
},
|
|
1017
|
+
register(program, ctx) {
|
|
1018
|
+
program.command("index").description("\u5217\u51FA\u5168\u90E8\u547D\u4EE4\uFF08builtin/local/official\uFF09\u4E0E agent \u5143\u6570\u636E").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
|
|
1019
|
+
const json = ctx.json || opts.json;
|
|
1020
|
+
const rows = buildAgentIndex(
|
|
1021
|
+
builtinCommands,
|
|
1022
|
+
await loadLocalPlugins(ctx),
|
|
1023
|
+
discoverAllOfficialPlugins()
|
|
1024
|
+
);
|
|
1025
|
+
ctx.log.result(json ? rows : rows.map(rowToText).join("\n"));
|
|
1026
|
+
});
|
|
1027
|
+
program.command("describe").description("\u67E5\u770B\u5355\u4E2A\u547D\u4EE4\u7684\u5B8C\u6574\u8BB0\u5F55\uFF08builtin/local/official\uFF09").argument("<name>", "\u547D\u4EE4\u540D").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (name, opts) => {
|
|
1028
|
+
const json = ctx.json || opts.json;
|
|
1029
|
+
const row = buildAgentIndex(
|
|
1030
|
+
builtinCommands,
|
|
1031
|
+
await loadLocalPlugins(ctx),
|
|
1032
|
+
discoverAllOfficialPlugins()
|
|
1033
|
+
).find((r) => r.name === name);
|
|
1034
|
+
if (!row) {
|
|
1035
|
+
ctx.log.error(`\u672A\u627E\u5230\u547D\u4EE4: ${name}`);
|
|
1036
|
+
process.exitCode = 1;
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
if (json) {
|
|
1040
|
+
ctx.log.result(row);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
const lines = [
|
|
1044
|
+
`\u547D\u4EE4: ${row.name} (${row.type})`,
|
|
1045
|
+
`\u63CF\u8FF0: ${row.description}`,
|
|
1046
|
+
`\u5143\u6570\u636E: ${row.metadataStatus}`
|
|
1047
|
+
];
|
|
1048
|
+
if (row.status) lines.push(`\u72B6\u6001: ${row.status}${row.error ? `\uFF08${row.error}\uFF09` : ""}`);
|
|
1049
|
+
if (row.package) lines.push(`\u5305: ${row.package}${row.version ? `@${row.version}` : ""}`);
|
|
1050
|
+
if (row.requiredPlatforms) lines.push(`\u652F\u6301\u5E73\u53F0: ${row.requiredPlatforms.join(", ")}`);
|
|
1051
|
+
if (row.whenToUse) lines.push(`\u4F55\u65F6\u4F7F\u7528: ${row.whenToUse}`);
|
|
1052
|
+
if (row.globalOptions && row.globalOptions.length > 0) {
|
|
1053
|
+
lines.push("\u5168\u5C40\u9009\u9879:");
|
|
1054
|
+
for (const o of row.globalOptions) lines.push(` ${o.flags} \u2014 ${o.description}`);
|
|
1055
|
+
}
|
|
1056
|
+
if (row.commands && row.commands.length > 0) {
|
|
1057
|
+
lines.push("\u5B50\u547D\u4EE4:");
|
|
1058
|
+
for (const c of row.commands) lines.push(describeCommandLine(c));
|
|
1059
|
+
}
|
|
1060
|
+
ctx.log.result(lines.join("\n"));
|
|
1061
|
+
});
|
|
1062
|
+
program.command("docs").description("\u8F93\u51FA\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587\uFF08AI Agent \u5F15\u5BFC\u6587\u6863\uFF09").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON\uFF08\u542B sha256/content\uFF09").addHelpText("after", DOCS_HELP_TEXT).action((opts) => {
|
|
1063
|
+
const json = ctx.json || opts.json;
|
|
1064
|
+
let docs;
|
|
1065
|
+
try {
|
|
1066
|
+
docs = readBundledAgentsMd();
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1069
|
+
if (json) ctx.log.result({ ok: false, error: message });
|
|
1070
|
+
ctx.log.error(message);
|
|
1071
|
+
process.exitCode = 1;
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
if (json) {
|
|
1075
|
+
ctx.log.result({
|
|
1076
|
+
package: BUNDLED_DOCS_PACKAGE,
|
|
1077
|
+
version: VERSION,
|
|
1078
|
+
sha256: docs.sha256,
|
|
1079
|
+
content: docs.content
|
|
1080
|
+
});
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
process.stdout.write(docs.content);
|
|
1084
|
+
});
|
|
1085
|
+
program.command("init").description("\u521D\u59CB\u5316 <\u76EE\u5F55>/AGENTS.md\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B58\u5728\u9ED8\u8BA4\u62D2\u7EDD\uFF0C--force \u8986\u76D6\uFF0C--dry-run \u9884\u89C8").argument("[directory]", "\u76EE\u6807\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1B\u5FC5\u987B\u5DF2\u5B58\u5728\u4E14\u4E3A\u76EE\u5F55\uFF09").option("--force", "\u8986\u76D6\u5DF2\u5B58\u5728\u7684 AGENTS.md\uFF08\u539F\u5B50\u5199\u5165\uFF09").option("--dry-run", "\u53EA\u62A5\u544A\u76EE\u6807\u4E0E\u52A8\u4F5C\uFF0C\u4E0D\u5199\u5165\u4EFB\u4F55\u6587\u4EF6").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB\u7ED3\u679C").addHelpText("after", INIT_HELP_TEXT).action(
|
|
1086
|
+
(directory, opts) => {
|
|
1087
|
+
const json = ctx.json || opts.json;
|
|
1088
|
+
let docs;
|
|
1089
|
+
try {
|
|
1090
|
+
docs = readBundledAgentsMd();
|
|
1091
|
+
} catch (err) {
|
|
1092
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1093
|
+
if (json) ctx.log.result({ ok: false, error: message });
|
|
1094
|
+
ctx.log.error(message);
|
|
1095
|
+
process.exitCode = 1;
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
const result = performAgentInit({
|
|
1099
|
+
directory: path5.resolve(directory ?? process.cwd()),
|
|
1100
|
+
docs,
|
|
1101
|
+
force: opts.force,
|
|
1102
|
+
dryRun: opts.dryRun,
|
|
1103
|
+
// 显式传入目录才做目录符号链接/联接 fail-closed;默认 cwd 不受限
|
|
1104
|
+
explicitDirectory: directory !== void 0
|
|
1105
|
+
});
|
|
1106
|
+
if (json) {
|
|
1107
|
+
ctx.log.result(result);
|
|
1108
|
+
if (!result.ok && result.reason) ctx.log.error(result.reason);
|
|
1109
|
+
if (!result.ok) process.exitCode = 1;
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (!result.ok) {
|
|
1113
|
+
if (result.reason) ctx.log.error(result.reason);
|
|
1114
|
+
process.exitCode = 1;
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
if (result.dryRun) {
|
|
1118
|
+
ctx.log.result(
|
|
1119
|
+
`[dry-run] \u5C06${result.action === "overwrite" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF09`
|
|
1120
|
+
);
|
|
1121
|
+
} else {
|
|
1122
|
+
ctx.log.result(
|
|
1123
|
+
`\u5DF2${result.action === "overwritten" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF0CSHA-256 ${result.sha256.slice(0, 12)}\u2026\uFF09`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
);
|
|
1128
|
+
program.addHelpText("after", AGENT_HELP_TEXT);
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
94
1131
|
|
|
95
1132
|
// src/commands/doctor.ts
|
|
1133
|
+
import fs4 from "fs";
|
|
96
1134
|
var doctor = {
|
|
97
1135
|
name: "doctor",
|
|
98
1136
|
description: "\u4F53\u68C0\uFF1Anode/v-cli \u7248\u672C\u3001homeDir\u3001config\u3001\u63D2\u4EF6\u72B6\u6001",
|
|
1137
|
+
apiVersion: 1,
|
|
99
1138
|
register(program, ctx) {
|
|
100
|
-
program.action(async () => {
|
|
1139
|
+
program.option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
|
|
1140
|
+
const json = ctx.json || opts.json;
|
|
101
1141
|
const homeDir = ctx.homeDir;
|
|
102
|
-
const exists =
|
|
1142
|
+
const exists = fs4.existsSync(homeDir);
|
|
103
1143
|
let configWritable = false;
|
|
104
1144
|
try {
|
|
105
1145
|
ctx.config.set((cfg) => cfg);
|
|
@@ -113,6 +1153,14 @@ var doctor = {
|
|
|
113
1153
|
file: p.file,
|
|
114
1154
|
error: p.error ?? null
|
|
115
1155
|
}));
|
|
1156
|
+
const officialPlugins = discoverAllOfficialPlugins().map((o) => ({
|
|
1157
|
+
package: o.package,
|
|
1158
|
+
name: o.name,
|
|
1159
|
+
version: o.version,
|
|
1160
|
+
status: o.status,
|
|
1161
|
+
platform: o.platform,
|
|
1162
|
+
error: o.error
|
|
1163
|
+
}));
|
|
116
1164
|
const report = {
|
|
117
1165
|
version: VERSION,
|
|
118
1166
|
node: process.version,
|
|
@@ -120,9 +1168,10 @@ var doctor = {
|
|
|
120
1168
|
homeDirExists: exists,
|
|
121
1169
|
configWritable,
|
|
122
1170
|
localPluginCount: plugins.length,
|
|
123
|
-
localPlugins: list
|
|
1171
|
+
localPlugins: list,
|
|
1172
|
+
officialPlugins
|
|
124
1173
|
};
|
|
125
|
-
if (
|
|
1174
|
+
if (json) {
|
|
126
1175
|
ctx.log.result(report);
|
|
127
1176
|
} else {
|
|
128
1177
|
ctx.log.result(`vcli ${VERSION} (node ${process.version})`);
|
|
@@ -132,18 +1181,26 @@ var doctor = {
|
|
|
132
1181
|
for (const p of plugins) {
|
|
133
1182
|
ctx.log.result(` - ${p.file}${p.error ? ` [\u5931\u8D25: ${p.error}]` : ""}`);
|
|
134
1183
|
}
|
|
1184
|
+
ctx.log.result(`\u5B98\u65B9\u63D2\u4EF6: ${officialPlugins.length} \u4E2A`);
|
|
1185
|
+
for (const o of officialPlugins) {
|
|
1186
|
+
ctx.log.result(
|
|
1187
|
+
` - ${o.name} (${o.package}${o.version ? `@${o.version}` : ""}) [${o.status}]${o.error ? ` ${o.error}` : ""}`
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
135
1190
|
}
|
|
136
1191
|
});
|
|
137
1192
|
}
|
|
138
1193
|
};
|
|
139
1194
|
|
|
140
1195
|
// src/commands/plugin.ts
|
|
141
|
-
import
|
|
1196
|
+
import path6 from "path";
|
|
142
1197
|
var plugin = {
|
|
143
1198
|
name: "plugin",
|
|
144
1199
|
description: "\u63D2\u4EF6\u7BA1\u7406\uFF1Alist \u5217\u51FA\u547D\u4EE4\uFF0Cpath \u663E\u793A\u672C\u5730\u63D2\u4EF6\u76EE\u5F55",
|
|
1200
|
+
apiVersion: 1,
|
|
145
1201
|
register(program, ctx) {
|
|
146
|
-
program.command("list").description("\u5217\u51FA\u5185\u7F6E\u547D\u4EE4\
|
|
1202
|
+
program.command("list").description("\u5217\u51FA\u5185\u7F6E\u547D\u4EE4\u3001\u672C\u5730\u63D2\u4EF6\u4E0E\u5B98\u65B9\u63D2\u4EF6\u72B6\u6001").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
|
|
1203
|
+
const json = ctx.json || opts.json;
|
|
147
1204
|
const all = await loadAllCommands(ctx);
|
|
148
1205
|
const rows = all.map((item) => ({
|
|
149
1206
|
source: item.source,
|
|
@@ -151,17 +1208,32 @@ var plugin = {
|
|
|
151
1208
|
description: item.command?.description ?? item.error ?? "",
|
|
152
1209
|
file: item.file ?? ""
|
|
153
1210
|
}));
|
|
154
|
-
|
|
155
|
-
|
|
1211
|
+
const official = discoverAllOfficialPlugins().map((o) => ({
|
|
1212
|
+
source: "official",
|
|
1213
|
+
name: o.name,
|
|
1214
|
+
description: o.description,
|
|
1215
|
+
package: o.package,
|
|
1216
|
+
version: o.version,
|
|
1217
|
+
status: o.status,
|
|
1218
|
+
platform: o.platform,
|
|
1219
|
+
error: o.error
|
|
1220
|
+
}));
|
|
1221
|
+
if (json) {
|
|
1222
|
+
ctx.log.result([...rows, ...official]);
|
|
156
1223
|
} else {
|
|
157
1224
|
for (const row of rows) {
|
|
158
1225
|
ctx.log.result(`[${row.source}] ${row.name}: ${row.description}`);
|
|
159
1226
|
}
|
|
1227
|
+
for (const row of official) {
|
|
1228
|
+
ctx.log.result(
|
|
1229
|
+
`[${row.source}] ${row.name}: ${row.description} [${row.status}]${row.error ? ` ${row.error}` : ""}`
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
160
1232
|
}
|
|
161
1233
|
});
|
|
162
|
-
program.command("path").description("\u6253\u5370\u672C\u5730\u63D2\u4EF6\u76EE\u5F55\uFF08\u653E\u5165 .mjs \u6587\u4EF6\u5373\u751F\u6548\uFF09").action(() => {
|
|
163
|
-
const dir =
|
|
164
|
-
if (ctx.json) {
|
|
1234
|
+
program.command("path").description("\u6253\u5370\u672C\u5730\u63D2\u4EF6\u76EE\u5F55\uFF08\u653E\u5165 .mjs \u6587\u4EF6\u5373\u751F\u6548\uFF09").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action((opts) => {
|
|
1235
|
+
const dir = path6.join(ctx.homeDir, "commands");
|
|
1236
|
+
if (ctx.json || opts.json) {
|
|
165
1237
|
ctx.log.result({ dir });
|
|
166
1238
|
} else {
|
|
167
1239
|
ctx.log.result(dir);
|
|
@@ -192,8 +1264,10 @@ function parseInput(input) {
|
|
|
192
1264
|
var ts = {
|
|
193
1265
|
name: "ts",
|
|
194
1266
|
description: "\u65F6\u95F4\u6233\u5DE5\u5177\uFF1A\u65F6\u95F4\u6233 <-> \u53EF\u8BFB\u65F6\u95F4\u4E92\u8F6C",
|
|
1267
|
+
apiVersion: 1,
|
|
195
1268
|
register(program, ctx) {
|
|
196
|
-
program.argument("[value]", "\u6570\u5B57\u65F6\u95F4\u6233\uFF08\u79D2\u6216\u6BEB\u79D2\uFF0C\u81EA\u52A8\u8BC6\u522B\uFF09\u6216\u65E5\u671F\u5B57\u7B26\u4E32").action((value) => {
|
|
1269
|
+
program.argument("[value]", "\u6570\u5B57\u65F6\u95F4\u6233\uFF08\u79D2\u6216\u6BEB\u79D2\uFF0C\u81EA\u52A8\u8BC6\u522B\uFF09\u6216\u65E5\u671F\u5B57\u7B26\u4E32").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action((value, opts) => {
|
|
1270
|
+
const json = ctx.json || opts.json;
|
|
197
1271
|
if (!value || value === "now") {
|
|
198
1272
|
const now = Date.now();
|
|
199
1273
|
const report2 = {
|
|
@@ -202,7 +1276,7 @@ var ts = {
|
|
|
202
1276
|
iso: new Date(now).toISOString(),
|
|
203
1277
|
local: new Date(now).toString()
|
|
204
1278
|
};
|
|
205
|
-
ctx.log.result(
|
|
1279
|
+
ctx.log.result(json ? report2 : report2.iso);
|
|
206
1280
|
return;
|
|
207
1281
|
}
|
|
208
1282
|
const result = parseInput(value);
|
|
@@ -213,36 +1287,38 @@ var ts = {
|
|
|
213
1287
|
iso: new Date(result.milliseconds).toISOString(),
|
|
214
1288
|
local: formatTime(result.milliseconds)
|
|
215
1289
|
};
|
|
216
|
-
ctx.log.result(
|
|
1290
|
+
ctx.log.result(json ? report : report.local);
|
|
217
1291
|
});
|
|
218
1292
|
}
|
|
219
1293
|
};
|
|
220
1294
|
|
|
221
1295
|
// src/commands/index.ts
|
|
222
|
-
var builtinCommands = [doctor, plugin, ts];
|
|
1296
|
+
var builtinCommands = [doctor, plugin, ts, agent];
|
|
223
1297
|
|
|
224
1298
|
// src/core/loader.ts
|
|
225
1299
|
async function loadBuiltinCommands() {
|
|
226
1300
|
return builtinCommands.map((command) => ({ command, source: "builtin" }));
|
|
227
1301
|
}
|
|
228
1302
|
async function loadLocalPlugins(ctx) {
|
|
229
|
-
const dir =
|
|
1303
|
+
const dir = path7.join(ctx.homeDir, "commands");
|
|
230
1304
|
const loaded = [];
|
|
231
|
-
if (!
|
|
232
|
-
const files =
|
|
1305
|
+
if (!fs5.existsSync(dir)) return loaded;
|
|
1306
|
+
const files = fs5.readdirSync(dir).filter((f) => f.endsWith(".mjs")).sort();
|
|
233
1307
|
for (const file of files) {
|
|
234
|
-
const abs =
|
|
1308
|
+
const abs = path7.join(dir, file);
|
|
235
1309
|
try {
|
|
236
1310
|
const mod = await import(
|
|
237
1311
|
/* @vite-ignore */
|
|
238
1312
|
pathToFileURL(abs).href
|
|
239
1313
|
);
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
1314
|
+
const candidate = mod.default ?? mod;
|
|
1315
|
+
const acceptedNames = loaded.map((l) => l.command?.name).filter((n) => typeof n === "string");
|
|
1316
|
+
const validation = validateLocalCommand(candidate, { existingNames: acceptedNames });
|
|
1317
|
+
if (!validation.ok || !validation.command) {
|
|
1318
|
+
loaded.push({ source: "local", file: abs, error: validation.errors.join("\uFF1B") });
|
|
243
1319
|
continue;
|
|
244
1320
|
}
|
|
245
|
-
loaded.push({ command:
|
|
1321
|
+
loaded.push({ command: validation.command, source: "local", file: abs });
|
|
246
1322
|
} catch (err) {
|
|
247
1323
|
loaded.push({
|
|
248
1324
|
source: "local",
|
|
@@ -257,14 +1333,132 @@ async function loadAllCommands(ctx) {
|
|
|
257
1333
|
return [...await loadBuiltinCommands(), ...await loadLocalPlugins(ctx)];
|
|
258
1334
|
}
|
|
259
1335
|
|
|
1336
|
+
// src/core/runner.ts
|
|
1337
|
+
import { spawn } from "child_process";
|
|
1338
|
+
var PLUGIN_ENV_KEYS = {
|
|
1339
|
+
V_CLI_HOST_VERSION: VERSION,
|
|
1340
|
+
V_CLI_PLUGIN_API: "1",
|
|
1341
|
+
V_CLI_INVOKED_BY: "v-cli"
|
|
1342
|
+
};
|
|
1343
|
+
var SIGNAL_NUMBERS = {
|
|
1344
|
+
SIGHUP: 1,
|
|
1345
|
+
SIGINT: 2,
|
|
1346
|
+
SIGQUIT: 3,
|
|
1347
|
+
SIGILL: 4,
|
|
1348
|
+
SIGTRAP: 5,
|
|
1349
|
+
SIGABRT: 6,
|
|
1350
|
+
SIGBUS: 7,
|
|
1351
|
+
SIGFPE: 8,
|
|
1352
|
+
SIGKILL: 9,
|
|
1353
|
+
SIGUSR1: 10,
|
|
1354
|
+
SIGSEGV: 11,
|
|
1355
|
+
SIGUSR2: 12,
|
|
1356
|
+
SIGPIPE: 13,
|
|
1357
|
+
SIGALRM: 14,
|
|
1358
|
+
SIGTERM: 15,
|
|
1359
|
+
SIGSTKFLT: 16,
|
|
1360
|
+
SIGCHLD: 17,
|
|
1361
|
+
SIGCONT: 18,
|
|
1362
|
+
SIGSTOP: 19,
|
|
1363
|
+
SIGTSTP: 20,
|
|
1364
|
+
SIGTTIN: 21,
|
|
1365
|
+
SIGTTOU: 22,
|
|
1366
|
+
SIGURG: 23,
|
|
1367
|
+
SIGXCPU: 24,
|
|
1368
|
+
SIGXFSZ: 25,
|
|
1369
|
+
SIGVTALRM: 26,
|
|
1370
|
+
SIGPROF: 27,
|
|
1371
|
+
SIGWINCH: 28,
|
|
1372
|
+
SIGIO: 29,
|
|
1373
|
+
SIGPWR: 30,
|
|
1374
|
+
SIGSYS: 31
|
|
1375
|
+
};
|
|
1376
|
+
function runPluginBin({ bin, argv, env = {}, cwd }) {
|
|
1377
|
+
return new Promise((resolve, reject) => {
|
|
1378
|
+
const child = spawn(process.execPath, [bin, ...argv], {
|
|
1379
|
+
cwd,
|
|
1380
|
+
env: { ...process.env, ...PLUGIN_ENV_KEYS, ...env },
|
|
1381
|
+
stdio: "inherit",
|
|
1382
|
+
shell: false
|
|
1383
|
+
});
|
|
1384
|
+
const forward = (signal) => {
|
|
1385
|
+
try {
|
|
1386
|
+
child.kill(signal);
|
|
1387
|
+
} catch {
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
const onSigint = () => forward("SIGINT");
|
|
1391
|
+
const onSigterm = () => forward("SIGTERM");
|
|
1392
|
+
process.on("SIGINT", onSigint);
|
|
1393
|
+
process.on("SIGTERM", onSigterm);
|
|
1394
|
+
const cleanup = () => {
|
|
1395
|
+
process.removeListener("SIGINT", onSigint);
|
|
1396
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
1397
|
+
};
|
|
1398
|
+
child.on("error", (err) => {
|
|
1399
|
+
cleanup();
|
|
1400
|
+
reject(err);
|
|
1401
|
+
});
|
|
1402
|
+
child.on("close", (code, signal) => {
|
|
1403
|
+
cleanup();
|
|
1404
|
+
resolve({ code, signal });
|
|
1405
|
+
});
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
function mapExitCode(result) {
|
|
1409
|
+
if (result.code !== null) return result.code;
|
|
1410
|
+
if (result.signal !== null) {
|
|
1411
|
+
if (process.platform !== "win32") {
|
|
1412
|
+
const signum = SIGNAL_NUMBERS[result.signal];
|
|
1413
|
+
if (signum !== void 0) return 128 + signum;
|
|
1414
|
+
}
|
|
1415
|
+
return 1;
|
|
1416
|
+
}
|
|
1417
|
+
return 1;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
260
1420
|
// src/cli.ts
|
|
1421
|
+
function scanLeadingJson(rawArgs) {
|
|
1422
|
+
let i = 0;
|
|
1423
|
+
while (i < rawArgs.length && rawArgs[i] === "--json") i++;
|
|
1424
|
+
return { json: i > 0, args: rawArgs.slice(i), commandIndex: i };
|
|
1425
|
+
}
|
|
1426
|
+
async function interceptOfficialCommand(rawArgs, commandIndex) {
|
|
1427
|
+
const token = rawArgs[commandIndex];
|
|
1428
|
+
if (!isOfficialCommand(token)) return void 0;
|
|
1429
|
+
const spec = OFFICIAL_PLUGINS.find((p) => p.command === token);
|
|
1430
|
+
if (!spec) return void 0;
|
|
1431
|
+
const info = discoverOfficialPlugin(spec);
|
|
1432
|
+
if (info.status !== "available" || !info.bin) {
|
|
1433
|
+
process.stderr.write(
|
|
1434
|
+
`[v-cli] \u9519\u8BEF: \u5B98\u65B9\u63D2\u4EF6 ${spec.package}\uFF08\u547D\u4EE4 ${spec.command}\uFF09\u4E0D\u53EF\u7528\uFF1A${info.error ?? info.status}
|
|
1435
|
+
`
|
|
1436
|
+
);
|
|
1437
|
+
return 1;
|
|
1438
|
+
}
|
|
1439
|
+
const result = await runPluginBin({ bin: info.bin, argv: rawArgs.slice(commandIndex + 1) });
|
|
1440
|
+
return mapExitCode(result);
|
|
1441
|
+
}
|
|
261
1442
|
async function main() {
|
|
262
1443
|
const rawArgs = process.argv.slice(2);
|
|
263
|
-
const
|
|
264
|
-
const
|
|
265
|
-
const
|
|
1444
|
+
const span = scanLeadingJson(rawArgs);
|
|
1445
|
+
const ctx = createContext(defaultHomeDir(), span.json);
|
|
1446
|
+
const intercepted = await interceptOfficialCommand(rawArgs, span.commandIndex);
|
|
1447
|
+
if (intercepted !== void 0) return intercepted;
|
|
266
1448
|
const program = new Command();
|
|
267
|
-
program.name("v-cli").description("kevlns \u7684\u4E2A\u4EBA\u5DE5\u5177\u7BB1").version(VERSION).
|
|
1449
|
+
program.name("v-cli").description("kevlns \u7684\u4E2A\u4EBA\u5DE5\u5177\u7BB1").version(VERSION).showHelpAfterError();
|
|
1450
|
+
program.addHelpText(
|
|
1451
|
+
"after",
|
|
1452
|
+
[
|
|
1453
|
+
"AI Agent \u5FEB\u901F\u5F00\u59CB\uFF08\u63A8\u8350\u987A\u5E8F\uFF09\uFF1A",
|
|
1454
|
+
" 1. v-cli agent docs",
|
|
1455
|
+
" 2. v-cli agent index --json",
|
|
1456
|
+
" 3. v-cli agent describe <name> --json",
|
|
1457
|
+
" 4. v-cli agent init . \uFF08\u53EF\u9009\uFF1A\u5199\u5165\u5DE5\u4F5C\u533A AGENTS.md\uFF0CAI Agent \u81EA\u52A8\u8BFB\u53D6\uFF09",
|
|
1458
|
+
"",
|
|
1459
|
+
"\u8BE6\u60C5\u89C1 `v-cli agent --help`\uFF1B\u5355\u547D\u4EE4\u8BF4\u660E\u4E66\uFF1A`v-cli agent docs --help` / `v-cli agent init --help`\u3002"
|
|
1460
|
+
].join("\n")
|
|
1461
|
+
);
|
|
268
1462
|
const loaded = await loadAllCommands(ctx);
|
|
269
1463
|
for (const item of loaded) {
|
|
270
1464
|
if (!item.command) {
|
|
@@ -273,12 +1467,22 @@ async function main() {
|
|
|
273
1467
|
}
|
|
274
1468
|
const cmd = item.command;
|
|
275
1469
|
if (cmd.hidden) continue;
|
|
276
|
-
const sub =
|
|
277
|
-
|
|
1470
|
+
const sub = new Command(cmd.name).description(cmd.description);
|
|
1471
|
+
try {
|
|
1472
|
+
cmd.register(sub, ctx);
|
|
1473
|
+
program.addCommand(sub);
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
ctx.log.warn(
|
|
1476
|
+
`${item.file ?? cmd.name} register \u5931\u8D25: ${err instanceof Error ? err.message : String(err)}\uFF1B\u5DF2\u8DF3\u8FC7`
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
for (const spec of OFFICIAL_PLUGINS) {
|
|
1481
|
+
program.command(spec.command).description(`${spec.description}\uFF08\u5B98\u65B9\u63D2\u4EF6\uFF1A\u5B89\u88C5\u540E\u53EF\u7528\uFF09`);
|
|
278
1482
|
}
|
|
279
1483
|
try {
|
|
280
|
-
await program.parseAsync(args, { from: "user" });
|
|
281
|
-
return 0;
|
|
1484
|
+
await program.parseAsync(span.args, { from: "user" });
|
|
1485
|
+
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
282
1486
|
} catch (err) {
|
|
283
1487
|
if (err instanceof CommanderError) {
|
|
284
1488
|
return err.exitCode ?? 1;
|