@nick848/fet 1.0.10 → 1.1.1
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_en.md
CHANGED
|
@@ -42,6 +42,7 @@ Check the installation:
|
|
|
42
42
|
```sh
|
|
43
43
|
fet --version
|
|
44
44
|
fet --help
|
|
45
|
+
fet update
|
|
45
46
|
```
|
|
46
47
|
|
|
47
48
|
## Quick Start
|
|
@@ -100,6 +101,7 @@ fet init --lang en
|
|
|
100
101
|
| Command | Usage | Description |
|
|
101
102
|
|---------|-------|-------------|
|
|
102
103
|
| `fet init` | `fet init [--yes] [--lang <language>]` | Initialize FET and OpenSpec; generate context, state, Cursor integration, and Codex workflow guides. |
|
|
104
|
+
| `fet update` | `fet update` | Check whether FET is the latest published version and automatically upgrade when a newer version is available. |
|
|
103
105
|
| `fet update-context` | `fet update-context [--yes]` | Rescan the project and update FET-managed regions in `AGENTS.md` and `openspec/config.yaml`. |
|
|
104
106
|
| `fet fill-context` | `fet fill-context [--yes]` | Refresh IDE handoff commands that ask AI to replace `AGENTS.md` placeholders. |
|
|
105
107
|
| `fet doctor` | `fet doctor [--fix-lock]` | Diagnose OpenSpec, FET state, context files, tool integration, and lock files. |
|
|
@@ -15,6 +15,7 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
15
15
|
ErrorCode2["LockHeld"] = "LOCK_HELD";
|
|
16
16
|
ErrorCode2["UserCancelled"] = "USER_CANCELLED";
|
|
17
17
|
ErrorCode2["UnsafeScriptApprovalRequired"] = "UNSAFE_SCRIPT_APPROVAL_REQUIRED";
|
|
18
|
+
ErrorCode2["UpdateFailed"] = "UPDATE_FAILED";
|
|
18
19
|
ErrorCode2["ToolAdapterConflict"] = "TOOL_ADAPTER_CONFLICT";
|
|
19
20
|
ErrorCode2["ConfigInvalid"] = "CONFIG_INVALID";
|
|
20
21
|
ErrorCode2["FileSystemError"] = "FILE_SYSTEM_ERROR";
|
|
@@ -31,6 +32,7 @@ function exitCodeForError(code) {
|
|
|
31
32
|
return 3;
|
|
32
33
|
case "OPENSPEC_COMMAND_FAILED" /* OpenSpecCommandFailed */:
|
|
33
34
|
case "GRAPH_COMMAND_FAILED" /* GraphCommandFailed */:
|
|
35
|
+
case "UPDATE_FAILED" /* UpdateFailed */:
|
|
34
36
|
return 4;
|
|
35
37
|
case "OPENSPEC_STRUCTURE_UNKNOWN" /* OpenSpecStructureUnknown */:
|
|
36
38
|
case "STATE_SCHEMA_UNSUPPORTED" /* StateSchemaUnsupported */:
|
|
@@ -105,4 +107,4 @@ export {
|
|
|
105
107
|
FetError,
|
|
106
108
|
toFetError
|
|
107
109
|
};
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
110
|
+
//# sourceMappingURL=chunk-J5WB4KAL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/codes.ts","../src/errors/fet-error.ts"],"sourcesContent":["export enum ErrorCode {\n Unknown = \"UNKNOWN\",\n InvalidArguments = \"INVALID_ARGUMENTS\",\n OpenSpecNotFound = \"OPENSPEC_NOT_FOUND\",\n OpenSpecUnsupportedVersion = \"OPENSPEC_UNSUPPORTED_VERSION\",\n OpenSpecCommandFailed = \"OPENSPEC_COMMAND_FAILED\",\n OpenSpecStructureUnknown = \"OPENSPEC_STRUCTURE_UNKNOWN\",\n GraphProviderNotFound = \"GRAPH_PROVIDER_NOT_FOUND\",\n GraphCommandFailed = \"GRAPH_COMMAND_FAILED\",\n StateSchemaUnsupported = \"STATE_SCHEMA_UNSUPPORTED\",\n StateCorrupted = \"STATE_CORRUPTED\",\n LockHeld = \"LOCK_HELD\",\n UserCancelled = \"USER_CANCELLED\",\n UnsafeScriptApprovalRequired = \"UNSAFE_SCRIPT_APPROVAL_REQUIRED\",\n UpdateFailed = \"UPDATE_FAILED\",\n ToolAdapterConflict = \"TOOL_ADAPTER_CONFLICT\",\n ConfigInvalid = \"CONFIG_INVALID\",\n FileSystemError = \"FILE_SYSTEM_ERROR\"\n}\n\nexport function exitCodeForError(code: ErrorCode): number {\n switch (code) {\n case ErrorCode.InvalidArguments:\n case ErrorCode.ConfigInvalid:\n return 2;\n case ErrorCode.OpenSpecNotFound:\n case ErrorCode.OpenSpecUnsupportedVersion:\n case ErrorCode.GraphProviderNotFound:\n return 3;\n case ErrorCode.OpenSpecCommandFailed:\n case ErrorCode.GraphCommandFailed:\n case ErrorCode.UpdateFailed:\n return 4;\n case ErrorCode.OpenSpecStructureUnknown:\n case ErrorCode.StateSchemaUnsupported:\n case ErrorCode.StateCorrupted:\n case ErrorCode.ToolAdapterConflict:\n case ErrorCode.FileSystemError:\n return 5;\n case ErrorCode.LockHeld:\n return 6;\n case ErrorCode.UserCancelled:\n return 7;\n case ErrorCode.UnsafeScriptApprovalRequired:\n return 8;\n case ErrorCode.Unknown:\n default:\n return 1;\n }\n}\n","import { ErrorCode, exitCodeForError } from \"./codes.js\";\n\nexport interface FetErrorOptions {\n code: ErrorCode;\n message: string;\n details?: unknown;\n recoverable?: boolean;\n suggestedCommand?: string;\n cause?: unknown;\n}\n\nexport class FetError extends Error {\n readonly code: ErrorCode;\n readonly exitCode: number;\n readonly details?: unknown;\n readonly recoverable: boolean;\n readonly suggestedCommand?: string;\n override readonly cause?: unknown;\n\n constructor(options: FetErrorOptions) {\n super(options.message);\n this.name = \"FetError\";\n this.code = options.code;\n this.exitCode = exitCodeForError(options.code);\n this.details = options.details;\n this.recoverable = options.recoverable ?? true;\n this.suggestedCommand = options.suggestedCommand;\n this.cause = options.cause;\n }\n\n toJSON() {\n return {\n code: this.code,\n exitCode: this.exitCode,\n message: this.message,\n details: this.details,\n recoverable: this.recoverable,\n suggestedCommand: this.suggestedCommand\n };\n }\n}\n\nexport function toFetError(error: unknown): FetError {\n if (error instanceof FetError) {\n return error;\n }\n\n if (error instanceof Error) {\n return new FetError({\n code: ErrorCode.Unknown,\n message: error.message,\n recoverable: false,\n cause: error\n });\n }\n\n return new FetError({\n code: ErrorCode.Unknown,\n message: \"Unknown error\",\n details: error,\n recoverable: false\n });\n}\n"],"mappings":";;;AAAO,IAAK,YAAL,kBAAKA,eAAL;AACL,EAAAA,WAAA,aAAU;AACV,EAAAA,WAAA,sBAAmB;AACnB,EAAAA,WAAA,sBAAmB;AACnB,EAAAA,WAAA,gCAA6B;AAC7B,EAAAA,WAAA,2BAAwB;AACxB,EAAAA,WAAA,8BAA2B;AAC3B,EAAAA,WAAA,2BAAwB;AACxB,EAAAA,WAAA,wBAAqB;AACrB,EAAAA,WAAA,4BAAyB;AACzB,EAAAA,WAAA,oBAAiB;AACjB,EAAAA,WAAA,cAAW;AACX,EAAAA,WAAA,mBAAgB;AAChB,EAAAA,WAAA,kCAA+B;AAC/B,EAAAA,WAAA,kBAAe;AACf,EAAAA,WAAA,yBAAsB;AACtB,EAAAA,WAAA,mBAAgB;AAChB,EAAAA,WAAA,qBAAkB;AAjBR,SAAAA;AAAA,GAAA;AAoBL,SAAS,iBAAiB,MAAyB;AACxD,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;;;ACtCO,IAAM,WAAN,cAAuB,MAAM;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACS;AAAA,EAElB,YAAY,SAA0B;AACpC,UAAM,QAAQ,OAAO;AACrB,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,WAAW,iBAAiB,QAAQ,IAAI;AAC7C,SAAK,UAAU,QAAQ;AACvB,SAAK,cAAc,QAAQ,eAAe;AAC1C,SAAK,mBAAmB,QAAQ;AAChC,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA,EAEA,SAAS;AACP,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,aAAa,KAAK;AAAA,MAClB,kBAAkB,KAAK;AAAA,IACzB;AAAA,EACF;AACF;AAEO,SAAS,WAAW,OAA0B;AACnD,MAAI,iBAAiB,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,OAAO;AAC1B,WAAO,IAAI,SAAS;AAAA,MAClB;AAAA,MACA,SAAS,MAAM;AAAA,MACf,aAAa;AAAA,MACb,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,SAAS;AAAA,IAClB;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT,aAAa;AAAA,EACf,CAAC;AACH;","names":["ErrorCode"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
FetError,
|
|
4
4
|
toFetError
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-J5WB4KAL.js";
|
|
6
6
|
|
|
7
7
|
// src/cli/index.ts
|
|
8
|
-
import { createInterface } from "readline/promises";
|
|
8
|
+
import { createInterface as createInterface2 } from "readline/promises";
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
|
|
11
11
|
// src/commands/doctor.ts
|
|
@@ -1575,6 +1575,7 @@ ${block}
|
|
|
1575
1575
|
|
|
1576
1576
|
// src/commands/update-context.ts
|
|
1577
1577
|
import { readFile as readFile7 } from "fs/promises";
|
|
1578
|
+
import { createInterface } from "readline/promises";
|
|
1578
1579
|
import { join as join11 } from "path";
|
|
1579
1580
|
|
|
1580
1581
|
// src/config/yaml.ts
|
|
@@ -1627,7 +1628,7 @@ async function updateContextFiles(ctx) {
|
|
|
1627
1628
|
});
|
|
1628
1629
|
}
|
|
1629
1630
|
if (existingAgents && !hasManagedAutoRegion(existingAgents)) {
|
|
1630
|
-
if (!ctx.yes) {
|
|
1631
|
+
if (!ctx.yes && !await confirmInitCanReplaceUnmanagedAgents(ctx)) {
|
|
1631
1632
|
throw new FetError({
|
|
1632
1633
|
code: "TOOL_ADAPTER_CONFLICT" /* ToolAdapterConflict */,
|
|
1633
1634
|
message: ctx.language === "en" ? "AGENTS.md already exists and does not contain a FET-managed region." : "AGENTS.md \u5DF2\u5B58\u5728\uFF0C\u4E14\u4E0D\u5305\u542B FET \u6258\u7BA1\u533A\u57DF\u3002",
|
|
@@ -1664,6 +1665,27 @@ async function updateContextFiles(ctx) {
|
|
|
1664
1665
|
await ctx.stateStore.writeGlobal(state);
|
|
1665
1666
|
return { warnings };
|
|
1666
1667
|
}
|
|
1668
|
+
async function confirmInitCanReplaceUnmanagedAgents(ctx) {
|
|
1669
|
+
if (ctx.command !== "init" || ctx.json || !process.stdin.isTTY || !process.stderr.isTTY) {
|
|
1670
|
+
return false;
|
|
1671
|
+
}
|
|
1672
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
1673
|
+
try {
|
|
1674
|
+
const question = ctx.language === "en" ? "AGENTS.md already exists and is not managed by FET. Continue now with the same effect as fet init --yes? [y/N] " : "AGENTS.md \u5DF2\u5B58\u5728\u4E14\u4E0D\u7531 FET \u6258\u7BA1\u3002\u662F\u5426\u7EE7\u7EED\u6267\u884C\uFF0C\u6548\u679C\u7B49\u540C\u4E8E fet init --yes\uFF1F[y/N] ";
|
|
1675
|
+
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
1676
|
+
if (answer === "y" || answer === "yes") {
|
|
1677
|
+
return true;
|
|
1678
|
+
}
|
|
1679
|
+
} finally {
|
|
1680
|
+
rl.close();
|
|
1681
|
+
}
|
|
1682
|
+
throw new FetError({
|
|
1683
|
+
code: "USER_CANCELLED" /* UserCancelled */,
|
|
1684
|
+
message: ctx.language === "en" ? "fet init cancelled so you can handle AGENTS.md manually." : "\u5DF2\u53D6\u6D88 fet init\uFF0C\u4F60\u53EF\u4EE5\u5148\u624B\u52A8\u5904\u7406 AGENTS.md\u3002",
|
|
1685
|
+
details: { path: "AGENTS.md" },
|
|
1686
|
+
suggestedCommand: ctx.language === "en" ? "Review AGENTS.md, then rerun fet init or fet init --yes." : "\u68C0\u67E5 AGENTS.md \u540E\uFF0C\u624B\u52A8\u91CD\u65B0\u8FD0\u884C fet init \u6216 fet init --yes\u3002"
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1667
1689
|
async function readOptional2(path) {
|
|
1668
1690
|
try {
|
|
1669
1691
|
return await readFile7(path, "utf8");
|
|
@@ -2254,6 +2276,195 @@ async function assertVerified(ctx) {
|
|
|
2254
2276
|
}
|
|
2255
2277
|
}
|
|
2256
2278
|
|
|
2279
|
+
// src/commands/update.ts
|
|
2280
|
+
import { spawn } from "child_process";
|
|
2281
|
+
var DEFAULT_PACKAGE_NAME = "@nick848/fet";
|
|
2282
|
+
async function updateCommand(ctx) {
|
|
2283
|
+
const packageName = process.env.FET_UPDATE_PACKAGE_NAME ?? DEFAULT_PACKAGE_NAME;
|
|
2284
|
+
const npmExecutable = process.env.FET_UPDATE_NPM_EXECUTABLE ?? defaultNpmExecutable();
|
|
2285
|
+
const latestVersion = await resolveLatestVersion(packageName, npmExecutable);
|
|
2286
|
+
const currentVersion = ctx.fetVersion;
|
|
2287
|
+
if (compareVersions(currentVersion, latestVersion) >= 0) {
|
|
2288
|
+
ctx.output.result({
|
|
2289
|
+
ok: true,
|
|
2290
|
+
command: "update",
|
|
2291
|
+
summary: ctx.language === "en" ? `FET is already up to date (${currentVersion}).` : `FET \u5DF2\u662F\u6700\u65B0\u7248 (${currentVersion})\u3002`,
|
|
2292
|
+
data: {
|
|
2293
|
+
packageName,
|
|
2294
|
+
currentVersion,
|
|
2295
|
+
latestVersion,
|
|
2296
|
+
updated: false
|
|
2297
|
+
}
|
|
2298
|
+
});
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
if (!ctx.json) {
|
|
2302
|
+
ctx.output.info(
|
|
2303
|
+
ctx.language === "en" ? `Updating FET from ${currentVersion} to ${latestVersion}...` : `\u6B63\u5728\u5C06 FET \u4ECE ${currentVersion} \u5347\u7EA7\u5230 ${latestVersion}...`
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
const installArgs = ["install", "-g", `${packageName}@latest`];
|
|
2307
|
+
const result = await runNpm(npmExecutable, installArgs, {
|
|
2308
|
+
cwd: ctx.cwd,
|
|
2309
|
+
stdio: ctx.json ? "pipe" : "inherit"
|
|
2310
|
+
});
|
|
2311
|
+
if (result.exitCode !== 0) {
|
|
2312
|
+
throw new FetError({
|
|
2313
|
+
code: "UPDATE_FAILED" /* UpdateFailed */,
|
|
2314
|
+
message: ctx.language === "en" ? "FET update failed." : "FET \u5347\u7EA7\u5931\u8D25\u3002",
|
|
2315
|
+
details: result,
|
|
2316
|
+
suggestedCommand: `${npmExecutable} ${installArgs.join(" ")}`
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
ctx.output.result({
|
|
2320
|
+
ok: true,
|
|
2321
|
+
command: "update",
|
|
2322
|
+
summary: ctx.language === "en" ? `FET updated from ${currentVersion} to ${latestVersion}.` : `FET \u5DF2\u4ECE ${currentVersion} \u5347\u7EA7\u5230 ${latestVersion}\u3002`,
|
|
2323
|
+
data: {
|
|
2324
|
+
packageName,
|
|
2325
|
+
currentVersion,
|
|
2326
|
+
latestVersion,
|
|
2327
|
+
updated: true,
|
|
2328
|
+
installCommand: `${npmExecutable} ${installArgs.join(" ")}`
|
|
2329
|
+
}
|
|
2330
|
+
});
|
|
2331
|
+
}
|
|
2332
|
+
async function resolveLatestVersion(packageName, npmExecutable) {
|
|
2333
|
+
const override = process.env.FET_UPDATE_LATEST_VERSION?.trim();
|
|
2334
|
+
if (override) {
|
|
2335
|
+
return override;
|
|
2336
|
+
}
|
|
2337
|
+
const result = await runNpm(npmExecutable, ["view", packageName, "version", "--json"], {
|
|
2338
|
+
cwd: process.cwd(),
|
|
2339
|
+
stdio: "pipe"
|
|
2340
|
+
});
|
|
2341
|
+
if (result.exitCode !== 0) {
|
|
2342
|
+
throw new FetError({
|
|
2343
|
+
code: "UPDATE_FAILED" /* UpdateFailed */,
|
|
2344
|
+
message: "Unable to check the latest FET version from npm.",
|
|
2345
|
+
details: result,
|
|
2346
|
+
suggestedCommand: `${npmExecutable} view ${packageName} version`
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
const version = parseNpmVersion(result.stdout ?? "");
|
|
2350
|
+
if (!version) {
|
|
2351
|
+
throw new FetError({
|
|
2352
|
+
code: "UPDATE_FAILED" /* UpdateFailed */,
|
|
2353
|
+
message: "npm returned an invalid FET version.",
|
|
2354
|
+
details: { stdout: result.stdout }
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
return version;
|
|
2358
|
+
}
|
|
2359
|
+
function parseNpmVersion(stdout) {
|
|
2360
|
+
const trimmed = stdout.trim();
|
|
2361
|
+
if (!trimmed) {
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
try {
|
|
2365
|
+
const parsed = JSON.parse(trimmed);
|
|
2366
|
+
return typeof parsed === "string" && parsed.length > 0 ? parsed : null;
|
|
2367
|
+
} catch {
|
|
2368
|
+
return trimmed;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
function runNpm(command, args, options) {
|
|
2372
|
+
return new Promise((resolve2, reject) => {
|
|
2373
|
+
const stdout = [];
|
|
2374
|
+
const stderr = [];
|
|
2375
|
+
const child = spawn(command, args, {
|
|
2376
|
+
cwd: options.cwd,
|
|
2377
|
+
stdio: options.stdio,
|
|
2378
|
+
shell: process.platform === "win32"
|
|
2379
|
+
});
|
|
2380
|
+
child.stdout?.on("data", (chunk) => stdout.push(chunk));
|
|
2381
|
+
child.stderr?.on("data", (chunk) => stderr.push(chunk));
|
|
2382
|
+
child.on("error", (error) => {
|
|
2383
|
+
reject(
|
|
2384
|
+
new FetError({
|
|
2385
|
+
code: "UPDATE_FAILED" /* UpdateFailed */,
|
|
2386
|
+
message: "Unable to run npm for FET update.",
|
|
2387
|
+
details: { command, args },
|
|
2388
|
+
cause: error,
|
|
2389
|
+
suggestedCommand: `${command} ${args.join(" ")}`
|
|
2390
|
+
})
|
|
2391
|
+
);
|
|
2392
|
+
});
|
|
2393
|
+
child.on("close", (exitCode, signal) => {
|
|
2394
|
+
resolve2({
|
|
2395
|
+
command,
|
|
2396
|
+
args,
|
|
2397
|
+
exitCode: exitCode ?? 1,
|
|
2398
|
+
signal,
|
|
2399
|
+
stdout: stdout.length ? Buffer.concat(stdout).toString("utf8") : void 0,
|
|
2400
|
+
stderr: stderr.length ? Buffer.concat(stderr).toString("utf8") : void 0
|
|
2401
|
+
});
|
|
2402
|
+
});
|
|
2403
|
+
});
|
|
2404
|
+
}
|
|
2405
|
+
function defaultNpmExecutable() {
|
|
2406
|
+
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
2407
|
+
}
|
|
2408
|
+
function compareVersions(left, right) {
|
|
2409
|
+
const leftVersion = parseVersion(left);
|
|
2410
|
+
const rightVersion = parseVersion(right);
|
|
2411
|
+
for (let index = 0; index < 3; index += 1) {
|
|
2412
|
+
const diff = leftVersion.main[index] - rightVersion.main[index];
|
|
2413
|
+
if (diff !== 0) {
|
|
2414
|
+
return Math.sign(diff);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
if (!leftVersion.prerelease.length && rightVersion.prerelease.length) {
|
|
2418
|
+
return 1;
|
|
2419
|
+
}
|
|
2420
|
+
if (leftVersion.prerelease.length && !rightVersion.prerelease.length) {
|
|
2421
|
+
return -1;
|
|
2422
|
+
}
|
|
2423
|
+
const length = Math.max(leftVersion.prerelease.length, rightVersion.prerelease.length);
|
|
2424
|
+
for (let index = 0; index < length; index += 1) {
|
|
2425
|
+
const leftPart = leftVersion.prerelease[index];
|
|
2426
|
+
const rightPart = rightVersion.prerelease[index];
|
|
2427
|
+
if (leftPart === void 0) {
|
|
2428
|
+
return -1;
|
|
2429
|
+
}
|
|
2430
|
+
if (rightPart === void 0) {
|
|
2431
|
+
return 1;
|
|
2432
|
+
}
|
|
2433
|
+
const diff = comparePrereleasePart(leftPart, rightPart);
|
|
2434
|
+
if (diff !== 0) {
|
|
2435
|
+
return diff;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
return 0;
|
|
2439
|
+
}
|
|
2440
|
+
function parseVersion(version) {
|
|
2441
|
+
const [withoutBuild] = version.trim().replace(/^v/i, "").split("+");
|
|
2442
|
+
const [mainValue = "", prereleaseValue = ""] = withoutBuild.split("-");
|
|
2443
|
+
const mainParts = mainValue.split(".").map((part) => Number.parseInt(part, 10));
|
|
2444
|
+
return {
|
|
2445
|
+
main: [
|
|
2446
|
+
Number.isFinite(mainParts[0]) ? mainParts[0] : 0,
|
|
2447
|
+
Number.isFinite(mainParts[1]) ? mainParts[1] : 0,
|
|
2448
|
+
Number.isFinite(mainParts[2]) ? mainParts[2] : 0
|
|
2449
|
+
],
|
|
2450
|
+
prerelease: prereleaseValue ? prereleaseValue.split(".") : []
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
function comparePrereleasePart(left, right) {
|
|
2454
|
+
const leftNumber = /^\d+$/.test(left) ? Number.parseInt(left, 10) : null;
|
|
2455
|
+
const rightNumber = /^\d+$/.test(right) ? Number.parseInt(right, 10) : null;
|
|
2456
|
+
if (leftNumber !== null && rightNumber !== null) {
|
|
2457
|
+
return Math.sign(leftNumber - rightNumber);
|
|
2458
|
+
}
|
|
2459
|
+
if (leftNumber !== null) {
|
|
2460
|
+
return -1;
|
|
2461
|
+
}
|
|
2462
|
+
if (rightNumber !== null) {
|
|
2463
|
+
return 1;
|
|
2464
|
+
}
|
|
2465
|
+
return left.localeCompare(right);
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2257
2468
|
// src/commands/verify.ts
|
|
2258
2469
|
import { createHash } from "crypto";
|
|
2259
2470
|
import { mkdir as mkdir7, readFile as readFile12, stat as stat5 } from "fs/promises";
|
|
@@ -3921,14 +4132,14 @@ function exec(command, args) {
|
|
|
3921
4132
|
}
|
|
3922
4133
|
|
|
3923
4134
|
// src/openspec/runner.ts
|
|
3924
|
-
import { spawn } from "child_process";
|
|
4135
|
+
import { spawn as spawn2 } from "child_process";
|
|
3925
4136
|
async function runOpenSpec(executablePath, command, args, options) {
|
|
3926
4137
|
const spawnCommand = executablePath === "npx openspec" ? "npx" : executablePath;
|
|
3927
4138
|
const spawnArgs = executablePath === "npx openspec" ? ["openspec", command, ...args] : [command, ...args];
|
|
3928
4139
|
return new Promise((resolve2, reject) => {
|
|
3929
4140
|
const stdout = [];
|
|
3930
4141
|
const stderr = [];
|
|
3931
|
-
const child =
|
|
4142
|
+
const child = spawn2(spawnCommand, spawnArgs, {
|
|
3932
4143
|
cwd: options.cwd,
|
|
3933
4144
|
stdio: options.stdio ?? "inherit",
|
|
3934
4145
|
shell: process.platform === "win32"
|
|
@@ -4350,6 +4561,7 @@ async function createCommandContext(command, options) {
|
|
|
4350
4561
|
var program = new Command();
|
|
4351
4562
|
program.name("fet").description("\u56F4\u7ED5 OpenSpec \u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u4F5C\u6D41\u7F16\u6392\u5DE5\u5177\u3002").enablePositionalOptions().version(FET_VERSION).option("--cwd <path>", "\u6307\u5B9A\u9879\u76EE\u6839\u76EE\u5F55").option("--change <id>", "\u6307\u5B9A OpenSpec change").option("--lang <language>", "\u6307\u5B9A FET \u4EA4\u4E92\u4FE1\u606F\u548C\u751F\u6210\u4EA7\u7269\u8BED\u8A00\uFF0C\u9ED8\u8BA4 zh-CN").option("--yes", "\u5BF9\u4F4E\u98CE\u9669\u786E\u8BA4\u4F7F\u7528\u9ED8\u8BA4\u540C\u610F").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").option("--verbose", "\u8F93\u51FA\u8BCA\u65AD\u7EC6\u8282").option("--no-color", "\u7981\u7528\u7EC8\u7AEF\u989C\u8272");
|
|
4352
4563
|
addGlobalOptions(program.command("init")).description("\u521D\u59CB\u5316 FET + OpenSpec").action(wrap("init", initCommand));
|
|
4564
|
+
addGlobalOptions(program.command("update")).description("\u68C0\u67E5 FET \u662F\u5426\u4E3A\u6700\u65B0\u7248\uFF0C\u5E76\u5728\u9700\u8981\u65F6\u81EA\u52A8\u5347\u7EA7").action(wrap("update", updateCommand));
|
|
4353
4565
|
addGlobalOptions(program.command("update-context")).description("\u66F4\u65B0\u9879\u76EE\u4E0A\u4E0B\u6587").action(wrap("update-context", updateContextCommand));
|
|
4354
4566
|
addGlobalOptions(program.command("fill-context")).description("\u5237\u65B0 IDE \u586B\u5145 AGENTS.md \u5360\u4F4D\u7B26\u7684\u63D0\u793A\u6587\u4EF6").action(wrap("fill-context", fillContextCommand));
|
|
4355
4567
|
var graph = addGlobalOptions(program.command("graph").description("\u7BA1\u7406\u53EF\u9009\u7684 GitNexus \u4EE3\u7801\u56FE\u652F\u6301"));
|
|
@@ -4409,7 +4621,7 @@ async function handleModelPolicyRecommendation(ctx) {
|
|
|
4409
4621
|
if (policyMode !== "confirm" || ctx.yes || ctx.json || !process.stdin.isTTY || !process.stderr.isTTY) {
|
|
4410
4622
|
return;
|
|
4411
4623
|
}
|
|
4412
|
-
const rl =
|
|
4624
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
4413
4625
|
try {
|
|
4414
4626
|
const question = ctx.language === "en" ? "Continue anyway? [y/N] " : "\u4ECD\u7136\u7EE7\u7EED\uFF1F[y/N] ";
|
|
4415
4627
|
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
@@ -4432,7 +4644,7 @@ function renderModelPolicyActionHint(policyMode, language) {
|
|
|
4432
4644
|
return language === "en" ? "This is advisory because FET_MODEL_POLICY=warn; the command will continue." : "\u5F53\u524D\u8BBE\u7F6E FET_MODEL_POLICY=warn\uFF0C\u8BE5\u63D0\u9192\u4EC5\u4F5C\u4E3A\u5EFA\u8BAE\uFF0C\u547D\u4EE4\u4F1A\u7EE7\u7EED\u6267\u884C\u3002";
|
|
4433
4645
|
}
|
|
4434
4646
|
async function warnIfContextPlaceholdersRemain(ctx) {
|
|
4435
|
-
if (["init", "update-context", "fill-context", "doctor"].includes(ctx.command)) {
|
|
4647
|
+
if (["init", "update", "update-context", "fill-context", "doctor"].includes(ctx.command)) {
|
|
4436
4648
|
return;
|
|
4437
4649
|
}
|
|
4438
4650
|
const count2 = await countAgentsLlmPlaceholders(ctx.projectRoot);
|