@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-V4ZRBF5L.js.map
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-V4ZRBF5L.js";
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 = spawn(spawnCommand, spawnArgs, {
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 = createInterface({ input: process.stdin, output: process.stderr });
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);