@my-life-buddies/cli 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,7 +31,7 @@ buddy-cli init --help
31
31
  buddy-cli guide
32
32
  ```
33
33
 
34
- `--help` 列出命令,`<命令> --help` 说明当前命令的参数、限制和示例。`guide` 输出 Coding Agent 的完整开发指引,包括资料确认、Skill 接续、实现和交付规则;无需工程、登录或联网,不写文件。让 Agent 开始工作前先读取 `guide`,再按 CLI 返回的 `HANDOFF.md` 继续当前工程。`guide --help` 只显示该命令的简短用法。
34
+ `--help` 列出命令,`<命令> --help` 说明当前命令的参数、限制和示例。`guide` 输出 Coding Agent 的完整开发指引,包括资料确认、Skill 接续、实现和交付规则;无需工程或登录,先联网自检并按需自动更新 CLI,再输出新版指引,不修改工程文件。让 Agent 开始工作前先读取 `guide`,再按 CLI 返回的 `HANDOFF.md` 继续当前工程。HANDOFF 只包含当前工程、Skill 路径和接续方式,通用规则以 guide 为准;已生成文件在正常恢复创作时校验、备份并更新,CLI 升级不会直接覆盖工程里的 HANDOFF。`guide --help` 只显示该命令的简短用法。
35
35
 
36
36
  命令执行结果只提示当前状态和下一步;完整规则不再重复出现在顶层帮助和成功输出中。后台同一份入口说明见 [CLI 使用指南](../web/web/docs/cli/index.html)。
37
37
 
@@ -45,7 +45,11 @@ buddy-cli update
45
45
 
46
46
  `--version`(也可写作 `-v`)显示当前运行的 CLI 版本,无需联网。`update --check` 查询公共 npm 的 `latest` 版本,只检查、不安装;`update` 有新版本时安装检查到的准确版本,完成后核验安装结果。版本相同或本机版本更高时不安装、不降级。这三个用法均无需登录搭搭账号或进入搭子工程,从 CLI 0.8.0 开始支持。旧版用户先用上面的 npm 命令升级。
47
47
 
48
- 自动更新支持 npm 全局安装。源码、`npm link`、局部安装和 `npx` 请沿用原来的安装方式更新;使用 nvm 时先切换到安装该 CLI Node.js 环境。更新只针对 CLI,已有工程的 Runtime 依赖和锁文件需要另行修改与验证。
48
+ CLI 0.17.0 起,每条正常命令(含 `guide` 和无参数入口)先查询公共 npm `latest`,当前版本落后时自动更新,并由新版执行原命令。无需手动先运行 `update`,等待当前命令完成即可。更新发生在创建工程、领取凭据、启动 DEV 或上传之前;保留参数、工作目录、终端输入输出和退出状态,一次调用最多自动切换一次。版本相同或更高时继续执行,不降级;已经运行的 DEV 不因新版本发布而中断。
49
+
50
+ 全局安装更新当前 npm 环境中对应的 CLI;源码、`npm link`、项目内安装和 `npx` 自动下载准确版本到 `~/.cache/buddy-cli/updates/releases/`,从缓存运行,不改写原安装或工程依赖。并发更新共用安装锁。手动 `update` 仍只用于 npm 全局安装。查询最多等待 10 秒,安装最多等待 180 秒;失败或取消时停止原业务命令,不回退旧版。帮助、各命令 `--help` 和 `--version` 不联网。自动更新进度写入 stderr,不混入业务命令的 stdout 或 JSON;更新失败时 `--json` 返回 `code=CLI_UPDATE_FAILED` 和 `error`。
51
+
52
+ 更新只针对 CLI,已有工程的 Runtime 依赖、锁文件和业务适配仍需另行修改与验证。旧 CLI 不会自动获得自检能力,需要先用 npm 安装命令升级一次。
49
53
 
50
54
  可追加 `--json` 供 Coding Agent 或脚本读取,例如:
51
55
 
@@ -86,7 +90,7 @@ CLI 会把换取到的会话保存到 macOS Keychain。有效会话会复用,
86
90
  | 命令 | 是否需要 Agent 目录 | 会发生什么 |
87
91
  | --- | --- | --- |
88
92
  | `help`、`--help` | 不需要,也不登录 | 列出命令;参数见 `<命令> --help` |
89
- | `guide` | 不需要,也不登录 | 离线输出 Coding Agent 开发指引,不写文件 |
93
+ | `guide` | 不需要,也不登录 | 自检并按需更新 CLI 后输出当前 Coding Agent 开发指引,不修改工程 |
90
94
  | `--version [--json]` | 不需要,也不登录 | 显示当前运行的 CLI 版本,无需联网 |
91
95
  | `update [--check] [--json]` | 不需要,也不登录 | 检查公共 npm 最新版,或更新 npm 全局 CLI |
92
96
  | `login`、`logout` | 不需要 | 管理账号会话 |
package/dist/bin/buddy.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // apps/cli/src/bin/buddy.ts
4
- import { execFile as execFile2 } from "node:child_process";
5
- import { promisify as promisify2 } from "node:util";
4
+ import { execFile as execFile3 } from "node:child_process";
5
+ import { promisify as promisify3 } from "node:util";
6
6
  import { stdin, stderr, stdout } from "node:process";
7
7
  import { createRequire } from "node:module";
8
- import { dirname } from "node:path";
8
+ import { dirname as dirname2 } from "node:path";
9
9
  import { createInterface } from "node:readline/promises";
10
10
  import {
11
11
  PromptCancelledError,
@@ -438,9 +438,42 @@ function assertSupportedDeveloperPlatform(platform = process.platform) {
438
438
  // apps/cli/src/self-update.ts
439
439
  import { execFile } from "node:child_process";
440
440
  import { lstat, readFile, realpath } from "node:fs/promises";
441
- import { homedir } from "node:os";
442
- import { isAbsolute, join } from "node:path";
441
+ import { homedir as homedir2 } from "node:os";
442
+ import { isAbsolute, join as join2 } from "node:path";
443
443
  import { promisify } from "node:util";
444
+
445
+ // apps/cli/src/update-lock.ts
446
+ import { mkdir, rm, writeFile } from "node:fs/promises";
447
+ import { dirname, join } from "node:path";
448
+ import { homedir } from "node:os";
449
+ import { createHash as createHash2 } from "node:crypto";
450
+ import { setTimeout as delay } from "node:timers/promises";
451
+ var defaultUpdateCache = () => join(homedir(), ".cache", "buddy-cli", "updates");
452
+ var globalUpdateLock = (cache, prefix) => join(cache, "locks", createHash2("sha256").update(prefix).digest("hex"));
453
+ async function withUpdateLock(path, signal, action) {
454
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
455
+ const deadline = Date.now() + 2e5;
456
+ for (; ; ) {
457
+ signal?.throwIfAborted();
458
+ try {
459
+ await mkdir(path, { mode: 448 });
460
+ break;
461
+ } catch (cause) {
462
+ if (cause.code !== "EEXIST") throw cause;
463
+ if (Date.now() >= deadline) throw new Error(`\u7B49\u5F85\u5176\u4ED6 CLI \u66F4\u65B0\u8D85\u65F6\u3002\u8BF7\u786E\u8BA4\u66F4\u65B0\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u518D\u6E05\u7406\u9501\u76EE\u5F55\u5E76\u91CD\u8BD5\uFF1A${path}`);
464
+ await delay(100, void 0, { signal });
465
+ }
466
+ }
467
+ try {
468
+ await writeFile(`${path}/owner.json`, JSON.stringify({ pid: process.pid, startedAt: Date.now() }), { mode: 384 });
469
+ signal?.throwIfAborted();
470
+ return await action();
471
+ } finally {
472
+ await rm(path, { recursive: true, force: true });
473
+ }
474
+ }
475
+
476
+ // apps/cli/src/self-update.ts
444
477
  var PACKAGE = "@my-life-buddies/cli";
445
478
  var REGISTRY = "https://registry.npmjs.org/";
446
479
  var MANUAL_UPDATE = `npm install --global ${PACKAGE}@latest --registry=${REGISTRY}`;
@@ -455,9 +488,12 @@ var HELP = `\u7528\u6CD5:
455
488
  --json \u8F93\u51FA JSON\uFF0C\u4F9B Coding Agent \u6216\u811A\u672C\u8BFB\u53D6
456
489
 
457
490
  \u65E0\u9700\u767B\u5F55\u642D\u642D\u8D26\u53F7\uFF0C\u4E5F\u65E0\u9700\u8FDB\u5165\u642D\u5B50\u5DE5\u7A0B\u3002
458
- update \u66F4\u65B0\u5F53\u524D npm \u5168\u5C40\u5B89\u88C5\u7684 CLI\uFF0C\u4E0D\u4FEE\u6539\u642D\u5B50\u5DE5\u7A0B\u7684 Runtime \u4F9D\u8D56\u3002
491
+ \u6B63\u5E38\u547D\u4EE4\uFF08\u542B guide\uFF09\u4F1A\u5148\u8054\u7F51\u81EA\u68C0\uFF0C\u81EA\u52A8\u66F4\u65B0\u540E\u7531\u65B0\u7248\u7EE7\u7EED\u6267\u884C\uFF1B\u7B49\u5F85\u5B8C\u6210\u5373\u53EF\u3002
492
+ --help\u3001\u5404\u547D\u4EE4 --help \u4E0E --version \u79BB\u7EBF\u53EF\u7528\uFF1B\u67E5\u8BE2\u6216\u66F4\u65B0\u5931\u8D25\u65F6\u505C\u6B62\u4E1A\u52A1\u64CD\u4F5C\u3002
493
+ update \u7528\u4E8E\u5355\u72EC\u66F4\u65B0\u5F53\u524D npm \u5168\u5C40\u5B89\u88C5\u7684 CLI\uFF0C\u4E0D\u4FEE\u6539\u642D\u5B50\u5DE5\u7A0B\u7684 Runtime \u4F9D\u8D56\u3002
459
494
  \u5DF2\u7ECF\u662F\u6700\u65B0\u7248\u6216\u5F53\u524D\u7248\u672C\u66F4\u9AD8\u65F6\u4E0D\u5B89\u88C5\u3001\u4E0D\u964D\u7EA7\u3002
460
- \u6E90\u7801\u3001npm link\u3001\u5C40\u90E8\u5B89\u88C5\u6216 npx \u8FD0\u884C\u65F6\uFF0C\u8BF7\u4F7F\u7528\u539F\u6765\u7684\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002
495
+ \u6E90\u7801\u3001npm link\u3001\u5C40\u90E8\u5B89\u88C5\u6216 npx \u7684\u6B63\u5E38\u547D\u4EE4\u4F7F\u7528\u7F13\u5B58\u7684\u65B0\u7248\u6267\u884C\uFF0C\u4E0D\u8986\u76D6\u6E90\u7801\u6216\u5DE5\u7A0B\u4F9D\u8D56\u3002
496
+ \u4E0A\u8FF0\u5B89\u88C5\u65B9\u5F0F\u4E0D\u80FD\u7528 update \u5355\u72EC\u4FEE\u6539\u539F\u5B89\u88C5\uFF0C\u8BF7\u6CBF\u7528\u539F\u6765\u7684\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002
461
497
  \u65E7\u7248 CLI \u5C1A\u65E0 update \u547D\u4EE4\u65F6\uFF0C\u8FD0\u884C\uFF1A
462
498
  ${MANUAL_UPDATE}`;
463
499
  function parseVersion(version) {
@@ -510,11 +546,11 @@ async function latestVersion(options) {
510
546
  }
511
547
  async function installUpdate(version, options) {
512
548
  const runNpm = options.runNpm ?? ((args, settings2) => execute("npm", args, { ...settings2, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 }));
513
- const settings = { cwd: homedir(), signal: options.signal, timeout: 1e4 };
549
+ const settings = { cwd: homedir2(), signal: options.signal, timeout: 1e4 };
514
550
  const { stdout: stdout2 } = await runNpm(["prefix", "--global"], settings);
515
551
  const prefix = stdout2.trim();
516
552
  if (!isAbsolute(prefix) || /[\r\n]/u.test(prefix)) throw new Error("\u65E0\u6CD5\u786E\u5B9A npm \u5168\u5C40\u5B89\u88C5\u76EE\u5F55\u3002");
517
- const installedRoot = join(prefix, "lib", "node_modules", PACKAGE);
553
+ const installedRoot = join2(prefix, "lib", "node_modules", PACKAGE);
518
554
  let sameInstallation = false;
519
555
  try {
520
556
  sameInstallation = !(await lstat(installedRoot)).isSymbolicLink() && await realpath(installedRoot) === await realpath(options.packageRoot);
@@ -523,28 +559,33 @@ async function installUpdate(version, options) {
523
559
  if (!sameInstallation) {
524
560
  throw new Error("\u5F53\u524D CLI \u4E0D\u662F\u8FD9\u4E2A npm \u73AF\u5883\u7684\u5168\u5C40\u5B89\u88C5\uFF0C\u4E0D\u80FD\u76F4\u63A5\u66F4\u65B0\u3002\u8BF7\u7528\u539F\u6765\u7684\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\uFF1B\u82E5\u4F7F\u7528 nvm\uFF0C\u8BF7\u5148\u5207\u6362\u5230\u5B89\u88C5\u8BE5 CLI \u7684 Node.js \u73AF\u5883\u3002");
525
561
  }
526
- try {
527
- await runNpm([
528
- "install",
529
- "--global",
530
- `${PACKAGE}@${version}`,
531
- "--prefix",
532
- prefix,
533
- `--registry=${REGISTRY}`,
534
- `--@my-life-buddies:registry=${REGISTRY}`,
535
- "--engine-strict",
536
- "--no-audit",
537
- "--no-fund"
538
- ], { ...settings, timeout: 18e4 });
539
- } catch (cause) {
540
- if (options.signal?.aborted) throw new Error("\u66F4\u65B0\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u8FD0\u884C buddy-cli --version \u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u3002");
541
- const code = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "unknown";
542
- throw new Error(`npm \u5B89\u88C5\u672A\u5B8C\u6210\uFF08${code}\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u548C\u5168\u5C40\u76EE\u5F55\u6743\u9650\uFF0C\u7136\u540E\u91CD\u8BD5\uFF1A${MANUAL_UPDATE}`);
543
- }
544
- const installed = JSON.parse(await readFile(join(installedRoot, "package.json"), "utf8"));
545
- if (installed.name !== PACKAGE || installed.version !== version) {
546
- throw new Error(`npm \u5DF2\u7ED3\u675F\uFF0C\u4F46\u672A\u786E\u8BA4\u5B89\u88C5\u5230 ${version}\u3002\u8BF7\u8FD0\u884C buddy-cli --version \u68C0\u67E5\uFF0C\u5FC5\u8981\u65F6\u91CD\u8BD5\uFF1A${MANUAL_UPDATE}`);
547
- }
562
+ await withUpdateLock(globalUpdateLock(options.cacheDirectory ?? defaultUpdateCache(), prefix), options.signal, async () => {
563
+ const current = JSON.parse(await readFile(join2(installedRoot, "package.json"), "utf8"));
564
+ if (current.name === PACKAGE && current.version === version) return;
565
+ if (current.name === PACKAGE && compareVersions(current.version, version) > 0) throw new Error("\u53E6\u4E00\u4E2A\u8FDB\u7A0B\u5DF2\u5B89\u88C5\u66F4\u65B0\u7684 CLI\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C\u547D\u4EE4\u3002");
566
+ try {
567
+ await runNpm([
568
+ "install",
569
+ "--global",
570
+ `${PACKAGE}@${version}`,
571
+ "--prefix",
572
+ prefix,
573
+ `--registry=${REGISTRY}`,
574
+ `--@my-life-buddies:registry=${REGISTRY}`,
575
+ "--engine-strict",
576
+ "--no-audit",
577
+ "--no-fund"
578
+ ], { ...settings, timeout: 18e4 });
579
+ } catch (cause) {
580
+ if (options.signal?.aborted) throw new Error("\u66F4\u65B0\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u8FD0\u884C buddy-cli --version \u68C0\u67E5\u5B89\u88C5\u72B6\u6001\u3002");
581
+ const code = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "unknown";
582
+ throw new Error(`npm \u5B89\u88C5\u672A\u5B8C\u6210\uFF08${code}\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u548C\u5168\u5C40\u76EE\u5F55\u6743\u9650\uFF0C\u7136\u540E\u91CD\u8BD5\uFF1A${MANUAL_UPDATE}`);
583
+ }
584
+ const installed = JSON.parse(await readFile(join2(installedRoot, "package.json"), "utf8"));
585
+ if (installed.name !== PACKAGE || installed.version !== version) {
586
+ throw new Error(`npm \u5DF2\u7ED3\u675F\uFF0C\u4F46\u672A\u786E\u8BA4\u5B89\u88C5\u5230 ${version}\u3002\u8BF7\u8FD0\u884C buddy-cli --version \u68C0\u67E5\uFF0C\u5FC5\u8981\u65F6\u91CD\u8BD5\uFF1A${MANUAL_UPDATE}`);
587
+ }
588
+ });
548
589
  }
549
590
  async function runSelfUpdateCommand(args, io, options) {
550
591
  const versionOnly = args[0] === "--version" || args[0] === "-v";
@@ -599,6 +640,143 @@ async function runSelfUpdateCommand(args, io, options) {
599
640
  }
600
641
  }
601
642
 
643
+ // apps/cli/src/auto-update.ts
644
+ import { execFile as execFile2, spawn } from "node:child_process";
645
+ import { lstat as lstat2, mkdir as mkdir2, mkdtemp, readFile as readFile2, realpath as realpath2, rename, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
646
+ import { constants, homedir as homedir3 } from "node:os";
647
+ import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
648
+ import { promisify as promisify2 } from "node:util";
649
+ var PACKAGE2 = "@my-life-buddies/cli";
650
+ var REGISTRY2 = "https://registry.npmjs.org/";
651
+ var execute2 = promisify2(execFile2);
652
+ var HANDOFF_ENV = "BUDDY_CLI_UPDATE_HANDOFF";
653
+ async function installedVersion(root) {
654
+ try {
655
+ const pkg = JSON.parse(await readFile2(join3(root, "package.json"), "utf8"));
656
+ return pkg.name === PACKAGE2 && typeof pkg.version === "string" ? pkg.version : void 0;
657
+ } catch {
658
+ return void 0;
659
+ }
660
+ }
661
+ async function checkedEntry(root, version) {
662
+ if (await installedVersion(root) !== version) throw new Error(`CLI \u5B89\u88C5\u7ED3\u679C\u4E0E\u672C\u6B21\u8981\u6C42\u7684 ${version} \u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u8BD5\u3002`);
663
+ const entry = join3(root, "dist", "bin", "buddy.js");
664
+ if (!(await lstat2(entry)).isFile()) throw new Error("\u65B0\u7248 CLI \u7F3A\u5C11\u6709\u6548\u7684\u6267\u884C\u5165\u53E3\u3002");
665
+ return entry;
666
+ }
667
+ async function prepareLatest(version, options) {
668
+ const cache = options.cacheDirectory ?? defaultUpdateCache();
669
+ const runNpm = options.runNpm ?? ((args, settings2) => execute2("npm", args, { ...settings2, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 }));
670
+ const settings = { cwd: homedir3(), signal: options.signal, timeout: 1e4 };
671
+ const { stdout: stdout2 } = await runNpm(["prefix", "--global"], settings);
672
+ const prefix = stdout2.trim();
673
+ if (!isAbsolute2(prefix) || /[\r\n]/u.test(prefix)) throw new Error("\u65E0\u6CD5\u786E\u5B9A npm \u5168\u5C40\u5B89\u88C5\u76EE\u5F55\u3002");
674
+ const globalRoot = join3(prefix, "lib", "node_modules", PACKAGE2);
675
+ let isGlobal = false;
676
+ try {
677
+ isGlobal = !(await lstat2(globalRoot)).isSymbolicLink() && await realpath2(globalRoot) === await realpath2(options.packageRoot);
678
+ } catch {
679
+ }
680
+ if (isGlobal) {
681
+ await installUpdate(version, options);
682
+ return checkedEntry(globalRoot, version);
683
+ }
684
+ const destination = join3(cache, "releases", version);
685
+ return withUpdateLock(join3(cache, "locks", `release-${version}`), options.signal, async () => {
686
+ const root = join3(destination, "node_modules", PACKAGE2);
687
+ try {
688
+ return await checkedEntry(root, version);
689
+ } catch {
690
+ }
691
+ await mkdir2(join3(cache, "releases"), { recursive: true, mode: 448 });
692
+ const temporary = await mkdtemp(join3(cache, "releases", ".install-"));
693
+ try {
694
+ await writeFile2(join3(temporary, "package.json"), JSON.stringify({ private: true }));
695
+ try {
696
+ await runNpm(
697
+ [
698
+ "install",
699
+ "--prefix",
700
+ temporary,
701
+ "--save-exact",
702
+ `${PACKAGE2}@${version}`,
703
+ `--registry=${REGISTRY2}`,
704
+ `--@my-life-buddies:registry=${REGISTRY2}`,
705
+ "--engine-strict",
706
+ "--no-audit",
707
+ "--no-fund"
708
+ ],
709
+ { cwd: temporary, signal: options.signal, timeout: 18e4 }
710
+ );
711
+ } catch (cause) {
712
+ const code = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "unknown";
713
+ throw new Error(`\u65B0\u7248 CLI \u7F13\u5B58\u5B89\u88C5\u5931\u8D25\uFF08${code}\uFF09\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u3001Node.js \u7248\u672C\u548C\u76EE\u5F55\u6743\u9650\u540E\u91CD\u8BD5\u3002`);
714
+ }
715
+ await checkedEntry(join3(temporary, "node_modules", PACKAGE2), version);
716
+ options.signal?.throwIfAborted();
717
+ await rm2(destination, { recursive: true, force: true });
718
+ await rename(temporary, destination);
719
+ return checkedEntry(root, version);
720
+ } finally {
721
+ await rm2(temporary, { recursive: true, force: true });
722
+ }
723
+ });
724
+ }
725
+ async function runUpdatedCli(entry, args, options) {
726
+ options.signal?.throwIfAborted();
727
+ return new Promise((resolve2, reject) => {
728
+ const child = spawn(process.execPath, [entry, ...args], { cwd: options.cwd, env: options.env, stdio: "inherit" });
729
+ let killTimer;
730
+ const abort = () => {
731
+ const signal = options.signal?.reason?.message === "SIGINT" ? "SIGINT" : "SIGTERM";
732
+ child.kill(signal);
733
+ killTimer ??= setTimeout(() => child.kill("SIGKILL"), 5e3);
734
+ killTimer.unref();
735
+ };
736
+ const cleanup = () => {
737
+ options.signal?.removeEventListener("abort", abort);
738
+ clearTimeout(killTimer);
739
+ };
740
+ child.once("error", (error) => {
741
+ cleanup();
742
+ reject(error);
743
+ });
744
+ child.once("exit", (code, signal) => {
745
+ cleanup();
746
+ resolve2({ exitCode: code ?? 128 + (signal ? constants.signals[signal] : 1) });
747
+ });
748
+ options.signal?.addEventListener("abort", abort, { once: true });
749
+ if (options.signal?.aborted) abort();
750
+ });
751
+ }
752
+ async function runAutoUpdate(args, io, options) {
753
+ if (args[0] === "help" || args[0] === "update" || args[0] === "--version" || args[0] === "-v" || args.includes("--help") || args.includes("-h")) return void 0;
754
+ try {
755
+ const env = { ...options.env ?? process.env };
756
+ const handoff = env[HANDOFF_ENV];
757
+ if (handoff !== void 0) {
758
+ delete env[HANDOFF_ENV];
759
+ if (!options.env) delete process.env[HANDOFF_ENV];
760
+ const expected = JSON.parse(handoff);
761
+ const entry2 = await realpath2(join3(options.packageRoot, "dist", "bin", "buddy.js"));
762
+ if (expected.parent !== process.ppid || expected.version !== options.version || expected.entry !== entry2 || JSON.stringify(expected.args) !== JSON.stringify(args)) throw new Error("CLI \u81EA\u52A8\u66F4\u65B0\u63A5\u7EED\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u3002");
763
+ return void 0;
764
+ }
765
+ const latest = await latestVersion(options);
766
+ if (compareVersions(latest, options.version) <= 0) return void 0;
767
+ io.writeError(`CLI \u81EA\u52A8\u66F4\u65B0\uFF1A${options.version} \u2192 ${latest}\u3002\u5B8C\u6210\u540E\u7EE7\u7EED\u5F53\u524D\u547D\u4EE4\uFF0C\u8BF7\u52FF\u91CD\u590D\u6267\u884C\u3002`);
768
+ const entry = await realpath2(await prepareLatest(latest, options));
769
+ options.signal?.throwIfAborted();
770
+ env[HANDOFF_ENV] = JSON.stringify({ parent: process.pid, version: latest, entry, args });
771
+ return await (options.runCli ?? runUpdatedCli)(entry, args, { cwd: options.cwd ?? process.cwd(), env, signal: options.signal });
772
+ } catch (cause) {
773
+ const error = options.signal?.aborted ? "CLI \u81EA\u52A8\u66F4\u65B0\u5DF2\u53D6\u6D88\uFF0C\u539F\u547D\u4EE4\u672A\u6267\u884C\u3002" : `CLI \u81EA\u68C0\u6216\u66F4\u65B0\u5931\u8D25\uFF0C\u539F\u547D\u4EE4\u672A\u6267\u884C\uFF1A${cause instanceof Error ? cause.message : String(cause)}`;
774
+ if (args.includes("--json")) io.write(JSON.stringify({ code: "CLI_UPDATE_FAILED", error }));
775
+ else io.writeError(error);
776
+ return { exitCode: 1 };
777
+ }
778
+ }
779
+
602
780
  // apps/cli/src/bin/buddy.ts
603
781
  import { latestRuntimeTemplate, scaffoldPlatformAgent } from "./core.js";
604
782
  var TerminalIO = class {
@@ -616,7 +794,7 @@ var TerminalIO = class {
616
794
  }
617
795
  async openMetaPreview(path) {
618
796
  try {
619
- await promisify2(execFile2)("open", [path]);
797
+ await promisify3(execFile3)("open", [path]);
620
798
  } catch {
621
799
  this.write(`\u8BF7\u6253\u5F00\u672C\u5730\u5934\u50CF\u9884\u89C8\u540E\u518D\u786E\u8BA4\uFF1A${path}`);
622
800
  }
@@ -683,11 +861,12 @@ try {
683
861
  const args = process.argv.slice(2);
684
862
  const require2 = createRequire(import.meta.url);
685
863
  const manifestPath = require2.resolve("@my-life-buddies/cli/package.json");
686
- const maintenance = await runSelfUpdateCommand(args, terminal, {
864
+ const updateOptions = {
687
865
  version: require2(manifestPath).version,
688
- packageRoot: dirname(manifestPath),
866
+ packageRoot: dirname2(manifestPath),
689
867
  signal: lifecycle.signal
690
- });
868
+ };
869
+ const maintenance = await runSelfUpdateCommand(args, terminal, updateOptions) ?? await runAutoUpdate(args, terminal, updateOptions);
691
870
  const command = args[0];
692
871
  let platformHost;
693
872
  const host = () => platformHost ??= new BuddyPlatformHost({ signal: lifecycle.signal });