agent-yes 1.75.2 → 1.76.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.
@@ -1,5 +1,6 @@
1
- import { t as CLIS_CONFIG } from "./ts-Cuys5-PF.js";
1
+ import { t as CLIS_CONFIG } from "./ts-MmVGVMOz.js";
2
2
  import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-CAoC4Jzx.js";
3
4
  import "./pidStore-C1JXxoPi.js";
4
5
  import "./globalPidIndex-Cr-g75QF.js";
5
6
 
@@ -8,4 +9,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
8
9
 
9
10
  //#endregion
10
11
  export { SUPPORTED_CLIS };
11
- //# sourceMappingURL=SUPPORTED_CLIS-C2w9JqbM.js.map
12
+ //# sourceMappingURL=SUPPORTED_CLIS-DAufh-XY.js.map
package/dist/agent-yes.js CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
package/dist/amp-yes.js CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
package/dist/cli.js CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env bun
2
2
  import { n as logger } from "./logger-B9h0djqx.js";
3
- import { n as version, t as name } from "./package-8zpT1iww.js";
3
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-CAoC4Jzx.js";
4
4
  import { argv } from "process";
5
5
  import { execFileSync, spawn } from "child_process";
6
6
  import ms from "ms";
7
7
  import yargs from "yargs";
8
8
  import { hideBin } from "yargs/helpers";
9
- import { existsSync, lstatSync, mkdirSync, readlinkSync, unlinkSync } from "fs";
10
- import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
11
- import { homedir } from "os";
9
+ import { existsSync, mkdirSync, unlinkSync } from "fs";
10
+ import { chmod, copyFile } from "fs/promises";
12
11
  import path from "path";
13
12
 
14
13
  //#region ts/parseCliArgs.ts
@@ -237,176 +236,6 @@ function parseCliArgs(argv, supportedClis) {
237
236
  };
238
237
  }
239
238
 
240
- //#endregion
241
- //#region ts/versionChecker.ts
242
- const CACHE_DIR = path.join(homedir(), ".cache", "agent-yes");
243
- const CACHE_FILE = path.join(CACHE_DIR, "update-check.json");
244
- const TTL_MS = 3600 * 1e3;
245
- async function readUpdateCache() {
246
- try {
247
- const raw = await readFile(CACHE_FILE, "utf8");
248
- return JSON.parse(raw);
249
- } catch {
250
- return null;
251
- }
252
- }
253
- async function writeUpdateCache(data) {
254
- await mkdir(CACHE_DIR, { recursive: true });
255
- await writeFile(CACHE_FILE, JSON.stringify(data));
256
- }
257
- function detectPackageManager() {
258
- if (process.env.BUN_INSTALL || process.execPath?.includes("bun") || process.env.npm_execpath?.includes("bun")) return "bun";
259
- return "npm";
260
- }
261
- /**
262
- * Check for updates, auto-install if newer version is available, and re-exec
263
- * so the current invocation always runs the latest code.
264
- *
265
- * Uses a 1-hour TTL cache to avoid hitting the registry on every run.
266
- * All errors are swallowed — network issues must never break the tool.
267
- * Set AGENT_YES_NO_UPDATE=1 to opt out.
268
- *
269
- * The AGENT_YES_UPDATED env var prevents infinite re-exec loops:
270
- * after updating we re-exec with AGENT_YES_UPDATED=<version> so the
271
- * new process skips the update check.
272
- */
273
- async function checkAndAutoUpdate() {
274
- if (process.env.AGENT_YES_NO_UPDATE) return;
275
- if (process.env.AGENT_YES_UPDATED) return;
276
- if (import.meta.url.startsWith("file://") && !import.meta.url.includes("node_modules")) {
277
- const scriptDir = path.dirname(new URL(import.meta.url).pathname);
278
- const repoRoot = path.resolve(scriptDir, "..");
279
- if (existsSync(path.join(repoRoot, ".git"))) return;
280
- }
281
- try {
282
- let latestVersion;
283
- const cache = await readUpdateCache();
284
- if (cache && Date.now() - cache.checkedAt < TTL_MS) latestVersion = cache.latestVersion;
285
- else {
286
- const fetched = await fetchLatestVersion();
287
- if (!fetched) return;
288
- latestVersion = fetched;
289
- await writeUpdateCache({
290
- checkedAt: Date.now(),
291
- latestVersion
292
- });
293
- }
294
- if (compareVersions(version, latestVersion) < 0) {
295
- if (await runInstall(latestVersion)) reExec(latestVersion);
296
- }
297
- } catch {}
298
- }
299
- async function runInstall(latestVersion) {
300
- const installCmd = detectPackageManager() === "bun" ? `bun add -g agent-yes@${latestVersion}` : `npm install -g agent-yes@${latestVersion}`;
301
- process.stderr.write(`\x1b[33m[agent-yes] Updating ${version} → ${latestVersion}…\x1b[0m\n`);
302
- try {
303
- const { execaCommand } = await import("execa");
304
- await execaCommand(installCmd, { stdio: "inherit" });
305
- process.stderr.write(`\x1b[32m[agent-yes] Updated to ${latestVersion}\x1b[0m\n`);
306
- return true;
307
- } catch {
308
- process.stderr.write(`\x1b[31m[agent-yes] Auto-update failed. Run: ${installCmd}\x1b[0m\n`);
309
- return false;
310
- }
311
- }
312
- /**
313
- * Re-exec the current process so the newly installed version runs.
314
- * Sets AGENT_YES_UPDATED=<version> to prevent an infinite loop.
315
- */
316
- function reExec(version) {
317
- const [bin, ...args] = process.argv;
318
- process.stderr.write(`\x1b[36m[agent-yes] Restarting with v${version}…\x1b[0m\n`);
319
- try {
320
- execFileSync(bin, args, {
321
- stdio: "inherit",
322
- env: {
323
- ...process.env,
324
- AGENT_YES_UPDATED: version
325
- }
326
- });
327
- process.exit(0);
328
- } catch (err) {
329
- process.exit(err.status ?? 1);
330
- }
331
- }
332
- /**
333
- * Fetch the latest version of the package from npm registry
334
- */
335
- async function fetchLatestVersion() {
336
- try {
337
- const response = await fetch(`https://registry.npmjs.org/${name}/latest`, { signal: AbortSignal.timeout(3e3) });
338
- if (!response.ok) return null;
339
- return (await response.json()).version;
340
- } catch {
341
- return null;
342
- }
343
- }
344
- /**
345
- * Compare two semantic versions
346
- * Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
347
- */
348
- function compareVersions(v1, v2) {
349
- const parts1 = v1.split(".").map(Number);
350
- const parts2 = v2.split(".").map(Number);
351
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
352
- const part1 = parts1[i] || 0;
353
- const part2 = parts2[i] || 0;
354
- if (part1 > part2) return 1;
355
- if (part1 < part2) return -1;
356
- }
357
- return 0;
358
- }
359
- /**
360
- * Detect how agent-yes was installed.
361
- * Returns a short label: "git", "bun link", "bun", "npm", "npx", or "unknown"
362
- */
363
- function detectInstallMethod() {
364
- try {
365
- const scriptDir = path.dirname(new URL(import.meta.url).pathname);
366
- if (!scriptDir.includes("node_modules")) {
367
- const repoRoot = path.resolve(scriptDir, "..");
368
- if (existsSync(path.join(repoRoot, ".git"))) return "git";
369
- return "source";
370
- }
371
- const nodeModulesEntry = scriptDir.replace(/\/dist$/, "");
372
- try {
373
- if (lstatSync(nodeModulesEntry).isSymbolicLink()) {
374
- const target = readlinkSync(nodeModulesEntry);
375
- const resolvedTarget = path.resolve(path.dirname(nodeModulesEntry), target);
376
- if (existsSync(path.join(resolvedTarget, ".git"))) return "bun link (git)";
377
- return "bun link";
378
- }
379
- } catch {}
380
- if (scriptDir.includes(".bun/")) return "bun";
381
- if (scriptDir.includes(".npm/")) return "npx";
382
- if (process.env.npm_execpath?.includes("bun")) return "bun";
383
- if (process.env.npm_config_user_agent?.startsWith("bun")) return "bun";
384
- if (process.env.npm_config_user_agent?.startsWith("npm")) return "npm";
385
- return "npm";
386
- } catch {
387
- return "unknown";
388
- }
389
- }
390
- /**
391
- * Format version string with install method
392
- */
393
- function versionString() {
394
- return `agent-yes v${version} (${detectInstallMethod()})`;
395
- }
396
- /**
397
- * Display version information with async latest version check
398
- */
399
- async function displayVersion() {
400
- console.log(versionString());
401
- const latestVersion = await fetchLatestVersion();
402
- if (latestVersion) {
403
- const comparison = compareVersions(version, latestVersion);
404
- if (comparison < 0) console.log(`\x1b[33m${latestVersion} (update available)\x1b[0m`);
405
- else if (comparison > 0) console.log(`${latestVersion} (latest published)`);
406
- else console.log(`${latestVersion} (latest)`);
407
- } else console.log("(unable to check for updates)");
408
- }
409
-
410
239
  //#endregion
411
240
  //#region ts/rustBinary.ts
412
241
  /**
@@ -552,7 +381,7 @@ function getRustBinaryVersion(binaryPath) {
552
381
  "ignore"
553
382
  ]
554
383
  }).match(/(\d+\.\d+\.\d+)/);
555
- return match ? match[1] : null;
384
+ return match ? match[1] ?? null : null;
556
385
  } catch {
557
386
  return null;
558
387
  }
@@ -564,20 +393,31 @@ function getRustBinaryVersion(binaryPath) {
564
393
  function autoRebuildIfOutdated(binaryPath, verbose) {
565
394
  if (!binaryPath.includes("/target/release") && !binaryPath.includes("/target/debug")) return true;
566
395
  const binaryVersion = getRustBinaryVersion(binaryPath);
567
- if (verbose) console.log(`[rust] Binary version: ${binaryVersion}, package version: ${version}`);
568
- if (binaryVersion === version) return true;
396
+ const pkgVersion = getInstalledPackage().version;
397
+ if (verbose) console.log(`[rust] Binary version: ${binaryVersion}, package version: ${pkgVersion}`);
398
+ if (binaryVersion === pkgVersion) return true;
569
399
  const rsDir = binaryPath.replace(/\/target\/(release|debug)\/agent-yes.*$/, "");
570
400
  if (!existsSync(path.join(rsDir, "Cargo.toml"))) {
571
401
  if (verbose) console.log(`[rust] Cannot find Cargo.toml at ${rsDir}, skipping rebuild`);
572
402
  return true;
573
403
  }
574
- process.stderr.write(`\x1b[33m[rust] Binary outdated (${binaryVersion ?? "unknown"} → ${version}), rebuilding…\x1b[0m\n`);
404
+ process.stderr.write(`\x1b[33m[rust] Binary outdated (${binaryVersion ?? "unknown"} → ${pkgVersion}), rebuilding…\x1b[0m\n`);
575
405
  try {
576
406
  execFileSync("cargo", ["build", ...binaryPath.includes("/target/release") ? ["--release"] : []], {
577
407
  cwd: rsDir,
578
408
  stdio: "inherit",
579
409
  timeout: 3e5
580
410
  });
411
+ try {
412
+ execFileSync("cargo", [
413
+ "install",
414
+ "--path",
415
+ rsDir
416
+ ], {
417
+ stdio: "inherit",
418
+ timeout: 6e4
419
+ });
420
+ } catch {}
581
421
  process.stderr.write(`\x1b[32m[rust] Rebuild complete\x1b[0m\n`);
582
422
  return true;
583
423
  } catch {
@@ -645,12 +485,12 @@ await checkAndAutoUpdate();
645
485
  logger.info(versionString());
646
486
  const config = parseCliArgs(process.argv);
647
487
  if (config.tray) {
648
- const { startTray } = await import("./tray-D5deJPjk.js");
488
+ const { startTray } = await import("./tray-CH_G7aXM.js");
649
489
  await startTray();
650
490
  await new Promise(() => {});
651
491
  }
652
492
  {
653
- const { ensureTray } = await import("./tray-D5deJPjk.js");
493
+ const { ensureTray } = await import("./tray-CH_G7aXM.js");
654
494
  ensureTray();
655
495
  }
656
496
  if (config.useRust) {
@@ -664,7 +504,7 @@ if (config.useRust) {
664
504
  }
665
505
  }
666
506
  if (rustBinary) {
667
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C2w9JqbM.js");
507
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DAufh-XY.js");
668
508
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
669
509
  if (config.verbose) {
670
510
  console.log(`[rust] Using binary: ${rustBinary}`);
package/dist/codex-yes.js CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
package/dist/cy.js CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
package/dist/grok-yes.js CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-Cuys5-PF.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-MmVGVMOz.js";
2
2
  import "./logger-B9h0djqx.js";
3
+ import "./versionChecker-CAoC4Jzx.js";
3
4
  import "./pidStore-C1JXxoPi.js";
4
5
  import "./globalPidIndex-Cr-g75QF.js";
5
6
 
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
package/dist/qwen-yes.js CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- await import('./cli.js')
2
+ import { existsSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
6
+ if (existsSync(join(root, ".git"))) {
7
+ await import("../ts/cli.ts");
8
+ } else {
9
+ await import("./cli.js");
10
+ }
@@ -28,6 +28,21 @@ function isProcessRunning(pid) {
28
28
  }
29
29
  }
30
30
  /**
31
+ * Build an env that scrubs inherited GIT_* repo-locating vars so the spawned
32
+ * `git` resolves the repo from `cwd` only. Without this, running inside a git
33
+ * hook (where GIT_DIR / GIT_INDEX_FILE / GIT_WORK_TREE / GIT_COMMON_DIR are
34
+ * exported) makes `git rev-parse --show-toplevel` use the hook's repo context
35
+ * instead of the requested directory.
36
+ */
37
+ function gitCleanEnv() {
38
+ const env = { ...process.env };
39
+ delete env.GIT_DIR;
40
+ delete env.GIT_WORK_TREE;
41
+ delete env.GIT_INDEX_FILE;
42
+ delete env.GIT_COMMON_DIR;
43
+ return env;
44
+ }
45
+ /**
31
46
  * Get git repository root for a directory
32
47
  */
33
48
  function getGitRoot(cwd) {
@@ -39,7 +54,8 @@ function getGitRoot(cwd) {
39
54
  "pipe",
40
55
  "pipe",
41
56
  "ignore"
42
- ]
57
+ ],
58
+ env: gitCleanEnv()
43
59
  }).trim();
44
60
  } catch {
45
61
  return null;
@@ -260,4 +276,4 @@ function shouldUseLock(_cwd) {
260
276
 
261
277
  //#endregion
262
278
  export { shouldUseLock as i, getRunningAgentCount as n, releaseLock as r, acquireLock as t };
263
- //# sourceMappingURL=runningLock-DQWJSptq.js.map
279
+ //# sourceMappingURL=runningLock-C22d9SRJ.js.map
@@ -1,4 +1,4 @@
1
- import { n as getRunningAgentCount } from "./runningLock-DQWJSptq.js";
1
+ import { n as getRunningAgentCount } from "./runningLock-C22d9SRJ.js";
2
2
  import { existsSync } from "fs";
3
3
  import { mkdir, readFile, unlink, writeFile } from "fs/promises";
4
4
  import { homedir } from "os";
@@ -175,4 +175,4 @@ async function startTray() {
175
175
 
176
176
  //#endregion
177
177
  export { ensureTray, startTray };
178
- //# sourceMappingURL=tray-D5deJPjk.js.map
178
+ //# sourceMappingURL=tray-CH_G7aXM.js.map
@@ -1,6 +1,6 @@
1
1
  import { n as logger, t as addTransport } from "./logger-B9h0djqx.js";
2
- import { n as version } from "./package-8zpT1iww.js";
3
- import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-DQWJSptq.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-CAoC4Jzx.js";
3
+ import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-C22d9SRJ.js";
4
4
  import { t as PidStore } from "./pidStore-C1JXxoPi.js";
5
5
  import { arch, platform } from "process";
6
6
  import { execSync } from "child_process";
@@ -8,6 +8,7 @@ import { closeSync, constants, createReadStream, existsSync, mkdirSync, openSync
8
8
  import { mkdir, readFile, readdir, unlink, writeFile } from "fs/promises";
9
9
  import { homedir } from "os";
10
10
  import path, { dirname, join } from "path";
11
+ import { fileURLToPath } from "url";
11
12
  import { execaCommandSync, parseCommandString } from "execa";
12
13
  import { fromReadable, fromWritable } from "from-node-stream";
13
14
  import DIE from "phpdie";
@@ -15,7 +16,6 @@ import sflow from "sflow";
15
16
  import xterm from "@xterm/headless";
16
17
  import { createServer } from "net";
17
18
  import { execSync as execSync$1 } from "node:child_process";
18
- import { fileURLToPath } from "url";
19
19
  import os from "node:os";
20
20
  import { readFile as readFile$1 } from "node:fs/promises";
21
21
  import path$1 from "node:path";
@@ -775,7 +775,7 @@ function spawnAgent(options) {
775
775
  let [bin, ...args] = [...parseCommandString(cliConf?.binary || cli), ...cliArgs];
776
776
  logger.debug(`Spawning ${bin} with args: ${JSON.stringify(args)}`);
777
777
  const spawned = pty.spawn(bin, args, ptyOptions);
778
- logger.info(`[${cli}-yes] Spawned ${bin} with PID ${spawned.pid} (agent-yes v${version})`);
778
+ logger.info(`[${cli}-yes] Spawned ${bin} with PID ${spawned.pid} (agent-yes v${getInstalledPackage().version})`);
779
779
  return spawned;
780
780
  };
781
781
  return tryCatch((error, attempts, spawn, ...args) => {
@@ -1679,4 +1679,4 @@ function sleep(ms) {
1679
1679
 
1680
1680
  //#endregion
1681
1681
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1682
- //# sourceMappingURL=ts-Cuys5-PF.js.map
1682
+ //# sourceMappingURL=ts-MmVGVMOz.js.map
@@ -0,0 +1,224 @@
1
+ import { execFileSync } from "child_process";
2
+ import { existsSync, lstatSync, readFileSync, readlinkSync } from "fs";
3
+ import { mkdir, readFile, writeFile } from "fs/promises";
4
+ import { homedir } from "os";
5
+ import path from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ //#region package.json
9
+ var name = "agent-yes";
10
+ var version = "1.76.0";
11
+
12
+ //#endregion
13
+ //#region ts/versionChecker.ts
14
+ const CACHE_DIR = path.join(homedir(), ".cache", "agent-yes");
15
+ const CACHE_FILE = path.join(CACHE_DIR, "update-check.json");
16
+ const TTL_MS = 3600 * 1e3;
17
+ const CANONICAL_PKG_NAME = "agent-yes";
18
+ let cachedInstalledPkg = null;
19
+ /**
20
+ * Read the live `package.json` from disk for the running module.
21
+ *
22
+ * The bundled `package.json` import is inlined at build time; if `dist/` is
23
+ * published without a fresh build (issue #39), the inlined `version` lies
24
+ * and the auto-update loop fires forever. Reading the on-disk manifest each
25
+ * run keeps the version honest even when the bundle is stale.
26
+ */
27
+ function getInstalledPackage() {
28
+ if (cachedInstalledPkg) return cachedInstalledPkg;
29
+ let dir = null;
30
+ try {
31
+ dir = path.dirname(fileURLToPath(import.meta.url));
32
+ } catch {}
33
+ if (dir) for (let i = 0; i < 6; i++) {
34
+ const candidate = path.join(dir, "package.json");
35
+ try {
36
+ if (existsSync(candidate)) {
37
+ const json = JSON.parse(readFileSync(candidate, "utf8"));
38
+ if (json.name === name && typeof json.version === "string") {
39
+ cachedInstalledPkg = {
40
+ name: json.name,
41
+ version: json.version
42
+ };
43
+ return cachedInstalledPkg;
44
+ }
45
+ }
46
+ } catch {}
47
+ const parent = path.dirname(dir);
48
+ if (parent === dir) break;
49
+ dir = parent;
50
+ }
51
+ cachedInstalledPkg = {
52
+ name,
53
+ version
54
+ };
55
+ return cachedInstalledPkg;
56
+ }
57
+ async function readUpdateCache() {
58
+ try {
59
+ const raw = await readFile(CACHE_FILE, "utf8");
60
+ return JSON.parse(raw);
61
+ } catch {
62
+ return null;
63
+ }
64
+ }
65
+ async function writeUpdateCache(data) {
66
+ await mkdir(CACHE_DIR, { recursive: true });
67
+ await writeFile(CACHE_FILE, JSON.stringify(data));
68
+ }
69
+ function detectPackageManager() {
70
+ if (process.env.BUN_INSTALL || process.execPath?.includes("bun") || process.env.npm_execpath?.includes("bun")) return "bun";
71
+ return "npm";
72
+ }
73
+ /**
74
+ * Check for updates, auto-install if newer version is available, and re-exec
75
+ * so the current invocation always runs the latest code.
76
+ *
77
+ * Uses a 1-hour TTL cache to avoid hitting the registry on every run.
78
+ * All errors are swallowed — network issues must never break the tool.
79
+ * Set AGENT_YES_NO_UPDATE=1 to opt out.
80
+ *
81
+ * The AGENT_YES_UPDATED env var prevents infinite re-exec loops:
82
+ * after updating we re-exec with AGENT_YES_UPDATED=<version> so the
83
+ * new process skips the update check.
84
+ */
85
+ async function checkAndAutoUpdate() {
86
+ if (process.env.AGENT_YES_NO_UPDATE) return;
87
+ if (process.env.AGENT_YES_UPDATED) return;
88
+ if (import.meta.url.startsWith("file://") && !import.meta.url.includes("node_modules")) {
89
+ const scriptDir = path.dirname(new URL(import.meta.url).pathname);
90
+ const repoRoot = path.resolve(scriptDir, "..");
91
+ if (existsSync(path.join(repoRoot, ".git"))) return;
92
+ }
93
+ try {
94
+ let latestVersion;
95
+ const cache = await readUpdateCache();
96
+ if (cache && Date.now() - cache.checkedAt < TTL_MS) latestVersion = cache.latestVersion;
97
+ else {
98
+ const fetched = await fetchLatestVersion();
99
+ if (!fetched) return;
100
+ latestVersion = fetched;
101
+ await writeUpdateCache({
102
+ checkedAt: Date.now(),
103
+ latestVersion
104
+ });
105
+ }
106
+ if (compareVersions(getInstalledPackage().version, latestVersion) < 0) {
107
+ if (await runInstall(latestVersion)) reExec(latestVersion);
108
+ }
109
+ } catch {}
110
+ }
111
+ async function runInstall(latestVersion) {
112
+ const installCmd = detectPackageManager() === "bun" ? `bun add -g ${CANONICAL_PKG_NAME}@${latestVersion}` : `npm install -g ${CANONICAL_PKG_NAME}@${latestVersion}`;
113
+ process.stderr.write(`\x1b[33m[agent-yes] Updating ${getInstalledPackage().version} → ${latestVersion}…\x1b[0m\n`);
114
+ try {
115
+ const { execaCommand } = await import("execa");
116
+ await execaCommand(installCmd, { stdio: "inherit" });
117
+ process.stderr.write(`\x1b[32m[agent-yes] Updated to ${latestVersion}\x1b[0m\n`);
118
+ return true;
119
+ } catch {
120
+ process.stderr.write(`\x1b[31m[agent-yes] Auto-update failed. Run: ${installCmd}\x1b[0m\n`);
121
+ return false;
122
+ }
123
+ }
124
+ /**
125
+ * Re-exec the current process so the newly installed version runs.
126
+ * Sets AGENT_YES_UPDATED=<version> to prevent an infinite loop.
127
+ */
128
+ function reExec(version) {
129
+ const [bin, ...args] = process.argv;
130
+ process.stderr.write(`\x1b[36m[agent-yes] Restarting with v${version}…\x1b[0m\n`);
131
+ try {
132
+ execFileSync(bin, args, {
133
+ stdio: "inherit",
134
+ env: {
135
+ ...process.env,
136
+ AGENT_YES_UPDATED: version
137
+ }
138
+ });
139
+ process.exit(0);
140
+ } catch (err) {
141
+ process.exit(err.status ?? 1);
142
+ }
143
+ }
144
+ /**
145
+ * Fetch the latest version of the package from npm registry
146
+ */
147
+ async function fetchLatestVersion() {
148
+ try {
149
+ const response = await fetch(`https://registry.npmjs.org/${CANONICAL_PKG_NAME}/latest`, { signal: AbortSignal.timeout(3e3) });
150
+ if (!response.ok) return null;
151
+ return (await response.json()).version;
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Compare two semantic versions
158
+ * Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
159
+ */
160
+ function compareVersions(v1, v2) {
161
+ const parts1 = v1.split(".").map(Number);
162
+ const parts2 = v2.split(".").map(Number);
163
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
164
+ const part1 = parts1[i] || 0;
165
+ const part2 = parts2[i] || 0;
166
+ if (part1 > part2) return 1;
167
+ if (part1 < part2) return -1;
168
+ }
169
+ return 0;
170
+ }
171
+ /**
172
+ * Detect how agent-yes was installed.
173
+ * Returns a short label: "git", "bun link", "bun", "npm", "npx", or "unknown"
174
+ */
175
+ function detectInstallMethod() {
176
+ try {
177
+ const scriptDir = path.dirname(new URL(import.meta.url).pathname);
178
+ if (!scriptDir.includes("node_modules")) {
179
+ const repoRoot = path.resolve(scriptDir, "..");
180
+ if (existsSync(path.join(repoRoot, ".git"))) return "git";
181
+ return "source";
182
+ }
183
+ const nodeModulesEntry = scriptDir.replace(/\/dist$/, "");
184
+ try {
185
+ if (lstatSync(nodeModulesEntry).isSymbolicLink()) {
186
+ const target = readlinkSync(nodeModulesEntry);
187
+ const resolvedTarget = path.resolve(path.dirname(nodeModulesEntry), target);
188
+ if (existsSync(path.join(resolvedTarget, ".git"))) return "bun link (git)";
189
+ return "bun link";
190
+ }
191
+ } catch {}
192
+ if (scriptDir.includes(".bun/")) return "bun";
193
+ if (scriptDir.includes(".npm/")) return "npx";
194
+ if (process.env.npm_execpath?.includes("bun")) return "bun";
195
+ if (process.env.npm_config_user_agent?.startsWith("bun")) return "bun";
196
+ if (process.env.npm_config_user_agent?.startsWith("npm")) return "npm";
197
+ return "npm";
198
+ } catch {
199
+ return "unknown";
200
+ }
201
+ }
202
+ /**
203
+ * Format version string with install method
204
+ */
205
+ function versionString() {
206
+ return `agent-yes v${getInstalledPackage().version} (${detectInstallMethod()})`;
207
+ }
208
+ /**
209
+ * Display version information with async latest version check
210
+ */
211
+ async function displayVersion() {
212
+ console.log(versionString());
213
+ const latestVersion = await fetchLatestVersion();
214
+ if (latestVersion) {
215
+ const comparison = compareVersions(getInstalledPackage().version, latestVersion);
216
+ if (comparison < 0) console.log(`\x1b[33m${latestVersion} (update available)\x1b[0m`);
217
+ else if (comparison > 0) console.log(`${latestVersion} (latest published)`);
218
+ else console.log(`${latestVersion} (latest)`);
219
+ } else console.log("(unable to check for updates)");
220
+ }
221
+
222
+ //#endregion
223
+ export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
224
+ //# sourceMappingURL=versionChecker-CAoC4Jzx.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.75.2",
3
+ "version": "1.76.0",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
@@ -77,7 +77,7 @@
77
77
  "fmt": "oxlint --fix --fix-suggestions && oxfmt",
78
78
  "verify-deps": "node ./scripts/verify-deps.js",
79
79
  "_prepack": "bun run build",
80
- "prepublishOnly": "npm run verify-deps",
80
+ "prepublishOnly": "bun run build && npm run verify-deps",
81
81
  "prepare": "husky",
82
82
  "release": "standard-version && npm publish",
83
83
  "release:beta": "standard-version && npm publish --tag beta",
package/ts/postbuild.ts CHANGED
@@ -15,18 +15,26 @@ const suffixes = ["-yes"];
15
15
  // Short aliases: maps alias name → target CLI name (alias resolves in parseCliArgs.ts)
16
16
  const shortAliases: Record<string, string> = { cy: "claude" };
17
17
 
18
+ // When .git exists (git clone), run TypeScript directly via bun — no build needed.
19
+ // When absent (npm install), fall back to compiled dist/cli.js.
20
+ const wrapperContent = `\
21
+ #!/usr/bin/env bun
22
+ import { existsSync } from "node:fs";
23
+ import { join, dirname } from "node:path";
24
+ import { fileURLToPath } from "node:url";
25
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
26
+ if (existsSync(join(root, ".git"))) {
27
+ await import("../ts/cli.ts");
28
+ } else {
29
+ await import("./cli.js");
30
+ }`;
31
+
18
32
  await sflow(cliNames.flatMap((cli) => suffixes.map((suffix) => ({ cli, suffix }))))
19
33
  .map(async ({ cli, suffix }) => {
20
34
  const cliName = `${cli}${suffix}`;
21
35
 
22
36
  const wrapperPath = `./dist/${cliName}.js`;
23
- await writeFile(
24
- wrapperPath,
25
- `
26
- #!/usr/bin/env bun
27
- await import('./cli.js')
28
- `.trim(),
29
- );
37
+ await writeFile(wrapperPath, wrapperContent);
30
38
  await chmod(wrapperPath, 0o755);
31
39
 
32
40
  // Only register -yes variants in package.json bin
@@ -41,13 +49,7 @@ await import('./cli.js')
41
49
  // Generate short alias wrapper files
42
50
  for (const [alias] of Object.entries(shortAliases)) {
43
51
  const wrapperPath = `./dist/${alias}.js`;
44
- await writeFile(
45
- wrapperPath,
46
- `
47
- #!/usr/bin/env bun
48
- await import('./cli.js')
49
- `.trim(),
50
- );
52
+ await writeFile(wrapperPath, wrapperContent);
51
53
  await chmod(wrapperPath, 0o755);
52
54
  if (!(pkg.bin as Record<string, string>)?.[alias]) {
53
55
  await Bun.$`npm pkg set ${"bin." + alias}=${wrapperPath}`;
package/ts/runningLock.ts CHANGED
@@ -43,6 +43,22 @@ function isProcessRunning(pid: number): boolean {
43
43
  }
44
44
  }
45
45
 
46
+ /**
47
+ * Build an env that scrubs inherited GIT_* repo-locating vars so the spawned
48
+ * `git` resolves the repo from `cwd` only. Without this, running inside a git
49
+ * hook (where GIT_DIR / GIT_INDEX_FILE / GIT_WORK_TREE / GIT_COMMON_DIR are
50
+ * exported) makes `git rev-parse --show-toplevel` use the hook's repo context
51
+ * instead of the requested directory.
52
+ */
53
+ function gitCleanEnv(): NodeJS.ProcessEnv {
54
+ const env: NodeJS.ProcessEnv = { ...process.env };
55
+ delete env.GIT_DIR;
56
+ delete env.GIT_WORK_TREE;
57
+ delete env.GIT_INDEX_FILE;
58
+ delete env.GIT_COMMON_DIR;
59
+ return env;
60
+ }
61
+
46
62
  /**
47
63
  * Get git repository root for a directory
48
64
  */
@@ -52,6 +68,7 @@ function getGitRoot(cwd: string): string | null {
52
68
  cwd,
53
69
  encoding: "utf8",
54
70
  stdio: ["pipe", "pipe", "ignore"],
71
+ env: gitCleanEnv(),
55
72
  });
56
73
  return result.trim();
57
74
  } catch {
package/ts/rustBinary.ts CHANGED
@@ -6,7 +6,7 @@ import { execFileSync } from "child_process";
6
6
  import { existsSync, mkdirSync, unlinkSync } from "fs";
7
7
  import { chmod, copyFile } from "fs/promises";
8
8
  import path from "path";
9
- import pkg from "../package.json" with { type: "json" };
9
+ import { getInstalledPackage } from "./versionChecker.ts";
10
10
 
11
11
  // Platform/arch to binary name mapping
12
12
  const PLATFORM_MAP: Record<string, string> = {
@@ -206,7 +206,7 @@ function getRustBinaryVersion(binaryPath: string): string | null {
206
206
  });
207
207
  // Output is like "agent-yes 1.72.3" or "agent-yes v1.72.3"
208
208
  const match = output.match(/(\d+\.\d+\.\d+)/);
209
- return match ? match[1] : null;
209
+ return match ? (match[1] ?? null) : null;
210
210
  } catch {
211
211
  return null;
212
212
  }
@@ -223,11 +223,12 @@ function autoRebuildIfOutdated(binaryPath: string, verbose: boolean): boolean {
223
223
  }
224
224
 
225
225
  const binaryVersion = getRustBinaryVersion(binaryPath);
226
+ const pkgVersion = getInstalledPackage().version;
226
227
  if (verbose) {
227
- console.log(`[rust] Binary version: ${binaryVersion}, package version: ${pkg.version}`);
228
+ console.log(`[rust] Binary version: ${binaryVersion}, package version: ${pkgVersion}`);
228
229
  }
229
230
 
230
- if (binaryVersion === pkg.version) {
231
+ if (binaryVersion === pkgVersion) {
231
232
  return true; // up to date
232
233
  }
233
234
 
@@ -239,7 +240,7 @@ function autoRebuildIfOutdated(binaryPath: string, verbose: boolean): boolean {
239
240
  }
240
241
 
241
242
  process.stderr.write(
242
- `\x1b[33m[rust] Binary outdated (${binaryVersion ?? "unknown"} → ${pkg.version}), rebuilding…\x1b[0m\n`,
243
+ `\x1b[33m[rust] Binary outdated (${binaryVersion ?? "unknown"} → ${pkgVersion}), rebuilding…\x1b[0m\n`,
243
244
  );
244
245
 
245
246
  try {
@@ -250,6 +251,15 @@ function autoRebuildIfOutdated(binaryPath: string, verbose: boolean): boolean {
250
251
  stdio: "inherit",
251
252
  timeout: 300_000, // 5 min max
252
253
  });
254
+ // Also update ~/.cargo/bin so the system-wide binary stays current
255
+ try {
256
+ execFileSync("cargo", ["install", "--path", rsDir], {
257
+ stdio: "inherit",
258
+ timeout: 60_000,
259
+ });
260
+ } catch {
261
+ // non-fatal: the target/ binary is already updated
262
+ }
253
263
  process.stderr.write(`\x1b[32m[rust] Rebuild complete\x1b[0m\n`);
254
264
  return true;
255
265
  } catch {
@@ -1,12 +1,14 @@
1
1
  import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
2
  import {
3
+ _setInstalledPackageForTesting,
3
4
  checkAndAutoUpdate,
4
5
  compareVersions,
5
6
  fetchLatestVersion,
6
7
  displayVersion,
7
8
  detectInstallMethod,
9
+ getInstalledPackage,
8
10
  versionString,
9
- } from "./versionChecker";
11
+ } from "./versionChecker.ts";
10
12
 
11
13
  vi.mock("execa", () => ({ execaCommand: vi.fn().mockResolvedValue({}) }));
12
14
  vi.mock("fs/promises", () => ({
@@ -18,8 +20,12 @@ vi.mock("fs", async (importOriginal) => {
18
20
  const actual = await importOriginal<typeof import("fs")>();
19
21
  return {
20
22
  ...actual,
21
- // Return false for .git checks so the dev-checkout guard doesn't skip auto-update in tests
23
+ // Return false for .git / package.json lookups so neither the dev-checkout
24
+ // guard nor getInstalledPackage's disk read fires during the auto-update tests.
22
25
  existsSync: vi.fn(() => false),
26
+ readFileSync: vi.fn(() => {
27
+ throw new Error("readFileSync not stubbed");
28
+ }),
23
29
  lstatSync: actual.lstatSync,
24
30
  readlinkSync: actual.readlinkSync,
25
31
  };
@@ -99,6 +105,7 @@ describe("versionChecker", () => {
99
105
  describe("checkAndAutoUpdate", () => {
100
106
  beforeEach(() => {
101
107
  vi.clearAllMocks();
108
+ _setInstalledPackageForTesting(null);
102
109
  vi.stubGlobal("fetch", vi.fn());
103
110
  vi.spyOn(process.stderr, "write").mockImplementation(() => true);
104
111
  // Use a mock for process.exit to prevent actual exit in tests
@@ -300,4 +307,77 @@ describe("versionChecker", () => {
300
307
  expect(str).toMatch(/agent-yes v\d+\.\d+\.\d+ \(.+\)/);
301
308
  });
302
309
  });
310
+
311
+ // Regression test for https://github.com/snomiao/agent-yes/issues/39:
312
+ // a stale bundled version string must not pin the auto-update comparison
313
+ // when a fresh package.json is on disk next to the running module.
314
+ describe("getInstalledPackage (issue #39)", () => {
315
+ beforeEach(() => {
316
+ vi.clearAllMocks();
317
+ _setInstalledPackageForTesting(null);
318
+ });
319
+
320
+ afterEach(() => {
321
+ vi.restoreAllMocks();
322
+ _setInstalledPackageForTesting(null);
323
+ });
324
+
325
+ it("prefers the on-disk package.json over the bundled (potentially stale) import", async () => {
326
+ const fs = await import("fs");
327
+ vi.mocked(fs.existsSync).mockReturnValueOnce(true);
328
+ vi.mocked(fs.readFileSync).mockReturnValueOnce(
329
+ JSON.stringify({ name: "agent-yes", version: "999.0.0" }) as any,
330
+ );
331
+
332
+ const resolved = getInstalledPackage();
333
+ expect(resolved.version).toBe("999.0.0");
334
+ expect(resolved.name).toBe("agent-yes");
335
+ });
336
+
337
+ it("continues walking parents when a candidate package.json is unreadable", async () => {
338
+ // Per-candidate try/catch: an unreadable/unparsable manifest at one
339
+ // level must not abort the upward walk and silently fall back to the
340
+ // bundled (stale) manifest. The walk must keep going until it finds
341
+ // a matching package.json or exhausts parents.
342
+ const fs = await import("fs");
343
+ let call = 0;
344
+ vi.mocked(fs.existsSync).mockImplementation(() => true);
345
+ vi.mocked(fs.readFileSync).mockImplementation(() => {
346
+ call += 1;
347
+ if (call === 1) throw new Error("EACCES");
348
+ if (call === 2) return "{not json" as any;
349
+ return JSON.stringify({ name: "agent-yes", version: "999.0.0" }) as any;
350
+ });
351
+
352
+ const resolved = getInstalledPackage();
353
+ expect(resolved.version).toBe("999.0.0");
354
+ expect(call).toBeGreaterThanOrEqual(3);
355
+ });
356
+
357
+ it("does not trigger an auto-update when on-disk version already matches the registry", async () => {
358
+ // Simulate the post-fix scenario: the bundled `pkg.version` (frozen at
359
+ // build time) is older than the registry, but the runtime resolver
360
+ // surfaces the correct on-disk version and the comparison short-circuits.
361
+ // Pre-fix behavior was install + reExec on every invocation → infinite loop.
362
+ _setInstalledPackageForTesting({ name: "agent-yes", version: "999.0.0" });
363
+
364
+ const { readFile } = await import("fs/promises");
365
+ const { execaCommand } = await import("execa");
366
+ vi.mocked(readFile).mockRejectedValueOnce(new Error("no cache"));
367
+ vi.stubGlobal(
368
+ "fetch",
369
+ vi.fn().mockResolvedValue({
370
+ ok: true,
371
+ json: async () => ({ version: "999.0.0" }),
372
+ } as Response),
373
+ );
374
+ vi.spyOn(process.stderr, "write").mockImplementation(() => true);
375
+ vi.spyOn(process, "exit").mockImplementation(() => undefined as never);
376
+ delete process.env.AGENT_YES_NO_UPDATE;
377
+ delete process.env.AGENT_YES_UPDATED;
378
+
379
+ await checkAndAutoUpdate();
380
+ expect(execaCommand).not.toHaveBeenCalled();
381
+ });
382
+ });
303
383
  });
@@ -1,14 +1,80 @@
1
1
  import { execFileSync } from "child_process";
2
- import { existsSync, lstatSync, readlinkSync } from "fs";
2
+ import { existsSync, lstatSync, readFileSync, readlinkSync } from "fs";
3
3
  import { mkdir, readFile, writeFile } from "fs/promises";
4
4
  import { homedir } from "os";
5
5
  import path from "path";
6
- import pkg from "../package.json" with { type: "json" };
6
+ import { fileURLToPath } from "url";
7
+ import bundledPkg from "../package.json" with { type: "json" };
7
8
 
8
9
  const CACHE_DIR = path.join(homedir(), ".cache", "agent-yes");
9
10
  const CACHE_FILE = path.join(CACHE_DIR, "update-check.json");
10
11
  const TTL_MS = 60 * 60 * 1000; // 1 hour
11
12
 
13
+ // The release pipeline publishes both `agent-yes` and `claude-yes` from the
14
+ // same source by flipping `package.json#name` and re-running `npm publish`
15
+ // (which now triggers `bun run build`, rebuilding dist with whichever name is
16
+ // set). The auto-updater's registry lookup, install command, and shared
17
+ // cache file must all stay pinned to the canonical package — otherwise a
18
+ // `claude-yes` install would query `claude-yes/latest` while `runInstall`
19
+ // still hard-codes `agent-yes`.
20
+ const CANONICAL_PKG_NAME = "agent-yes";
21
+
22
+ let cachedInstalledPkg: { name: string; version: string } | null = null;
23
+
24
+ /**
25
+ * Read the live `package.json` from disk for the running module.
26
+ *
27
+ * The bundled `package.json` import is inlined at build time; if `dist/` is
28
+ * published without a fresh build (issue #39), the inlined `version` lies
29
+ * and the auto-update loop fires forever. Reading the on-disk manifest each
30
+ * run keeps the version honest even when the bundle is stale.
31
+ */
32
+ export function getInstalledPackage(): { name: string; version: string } {
33
+ if (cachedInstalledPkg) return cachedInstalledPkg;
34
+ let dir: string | null = null;
35
+ try {
36
+ dir = path.dirname(fileURLToPath(import.meta.url));
37
+ } catch {
38
+ // import.meta.url malformed; fall through to bundled
39
+ }
40
+ if (dir) {
41
+ for (let i = 0; i < 6; i++) {
42
+ const candidate = path.join(dir, "package.json");
43
+ // A per-candidate try/catch: a transient read error, partial write, or
44
+ // BOM on any single package.json must NOT abort the upward walk —
45
+ // otherwise we'd silently fall back to the stale bundled manifest that
46
+ // issue #39 was about. Keep walking until we either find a matching
47
+ // manifest or exhaust parents.
48
+ try {
49
+ if (existsSync(candidate)) {
50
+ const json = JSON.parse(readFileSync(candidate, "utf8")) as {
51
+ name?: string;
52
+ version?: string;
53
+ };
54
+ if (json.name === bundledPkg.name && typeof json.version === "string") {
55
+ cachedInstalledPkg = { name: json.name, version: json.version };
56
+ return cachedInstalledPkg;
57
+ }
58
+ }
59
+ } catch {
60
+ // unreadable / unparsable — continue walking
61
+ }
62
+ const parent = path.dirname(dir);
63
+ if (parent === dir) break;
64
+ dir = parent;
65
+ }
66
+ }
67
+ cachedInstalledPkg = { name: bundledPkg.name, version: bundledPkg.version };
68
+ return cachedInstalledPkg;
69
+ }
70
+
71
+ /** Test-only: clear or seed the memoized lookup. */
72
+ export function _setInstalledPackageForTesting(
73
+ value: { name: string; version: string } | null,
74
+ ): void {
75
+ cachedInstalledPkg = value;
76
+ }
77
+
12
78
  type UpdateCache = { checkedAt: number; latestVersion: string };
13
79
 
14
80
  async function readUpdateCache(): Promise<UpdateCache | null> {
@@ -75,7 +141,7 @@ export async function checkAndAutoUpdate(): Promise<void> {
75
141
  await writeUpdateCache({ checkedAt: Date.now(), latestVersion });
76
142
  }
77
143
 
78
- if (compareVersions(pkg.version, latestVersion) < 0) {
144
+ if (compareVersions(getInstalledPackage().version, latestVersion) < 0) {
79
145
  const installed = await runInstall(latestVersion);
80
146
  if (installed) {
81
147
  reExec(latestVersion);
@@ -90,10 +156,12 @@ async function runInstall(latestVersion: string): Promise<boolean> {
90
156
  const pm = detectPackageManager();
91
157
  const installCmd =
92
158
  pm === "bun"
93
- ? `bun add -g agent-yes@${latestVersion}`
94
- : `npm install -g agent-yes@${latestVersion}`;
159
+ ? `bun add -g ${CANONICAL_PKG_NAME}@${latestVersion}`
160
+ : `npm install -g ${CANONICAL_PKG_NAME}@${latestVersion}`;
95
161
 
96
- process.stderr.write(`\x1b[33m[agent-yes] Updating ${pkg.version} → ${latestVersion}…\x1b[0m\n`);
162
+ process.stderr.write(
163
+ `\x1b[33m[agent-yes] Updating ${getInstalledPackage().version} → ${latestVersion}…\x1b[0m\n`,
164
+ );
97
165
  try {
98
166
  const { execaCommand } = await import("execa");
99
167
  await execaCommand(installCmd, { stdio: "inherit" });
@@ -128,7 +196,7 @@ function reExec(version: string): never {
128
196
  */
129
197
  export async function fetchLatestVersion(): Promise<string | null> {
130
198
  try {
131
- const response = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, {
199
+ const response = await fetch(`https://registry.npmjs.org/${CANONICAL_PKG_NAME}/latest`, {
132
200
  signal: AbortSignal.timeout(3000), // 3 second timeout
133
201
  });
134
202
 
@@ -215,7 +283,7 @@ export function detectInstallMethod(): string {
215
283
  * Format version string with install method
216
284
  */
217
285
  export function versionString(): string {
218
- return `agent-yes v${pkg.version} (${detectInstallMethod()})`;
286
+ return `agent-yes v${getInstalledPackage().version} (${detectInstallMethod()})`;
219
287
  }
220
288
 
221
289
  /**
@@ -227,7 +295,7 @@ export async function displayVersion(): Promise<void> {
227
295
  const latestVersion = await fetchLatestVersion();
228
296
 
229
297
  if (latestVersion) {
230
- const comparison = compareVersions(pkg.version, latestVersion);
298
+ const comparison = compareVersions(getInstalledPackage().version, latestVersion);
231
299
 
232
300
  if (comparison < 0) {
233
301
  console.log(`\x1b[33m${latestVersion} (update available)\x1b[0m`);
@@ -1,7 +0,0 @@
1
- //#region package.json
2
- var name = "agent-yes";
3
- var version = "1.75.1";
4
-
5
- //#endregion
6
- export { version as n, name as t };
7
- //# sourceMappingURL=package-8zpT1iww.js.map