agent-yes 1.66.0 → 1.68.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.
@@ -1059,7 +1059,7 @@ function tryCatch(catchFn, fn) {
1059
1059
  //#endregion
1060
1060
  //#region package.json
1061
1061
  var name = "agent-yes";
1062
- var version = "1.66.0";
1062
+ var version = "1.68.0";
1063
1063
 
1064
1064
  //#endregion
1065
1065
  //#region ts/pty-fix.ts
@@ -1516,7 +1516,7 @@ const CLIS_CONFIG = config.clis;
1516
1516
  * });
1517
1517
  * ```
1518
1518
  */
1519
- async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, exitOnIdle, logFile, removeControlCharactersFromStdout = false, verbose = false, queue = false, install = false, resume = false, useSkills = false, useStdinAppend = false, autoYes = true }) {
1519
+ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, exitOnIdle, logFile, removeControlCharactersFromStdout = false, verbose = false, queue = false, install = false, resume = false, useSkills = false, useStdinAppend = false, autoYes = true, idleAction }) {
1520
1520
  if (!cli) throw new Error(`cli is required`);
1521
1521
  const conf = CLIS_CONFIG[cli] || DIE(`Unsupported cli tool: ${cli}, current process.argv: ${process.argv.join(" ")}`);
1522
1522
  const workingDir = cwd ?? process.cwd();
@@ -1908,16 +1908,26 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1908
1908
  }, 800);
1909
1909
  const cleanupHeartbeat = () => clearInterval(heartbeatInterval);
1910
1910
  shell.onExit(cleanupHeartbeat);
1911
- if (exitOnIdle) ctx.idleWaiter.wait(exitOnIdle).then(async () => {
1912
- await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
1913
- if (isStillWorkingQ()) {
1914
- logger.warn(`[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet`);
1915
- return;
1911
+ if (exitOnIdle) (async () => {
1912
+ while (true) {
1913
+ await ctx.idleWaiter.wait(exitOnIdle);
1914
+ await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
1915
+ if (isStillWorkingQ()) {
1916
+ logger.warn(`[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet`);
1917
+ continue;
1918
+ }
1919
+ if (idleAction) {
1920
+ logger.info(`[${cli}-yes] ${cli} is idle, performing idle action: ${idleAction}`);
1921
+ notifyWebhook("IDLE", `action=${idleAction}`, workingDir).catch(() => null);
1922
+ await sendMessage(ctx.messageContext, idleAction);
1923
+ continue;
1924
+ }
1925
+ logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
1926
+ notifyWebhook("IDLE", "", workingDir).catch(() => null);
1927
+ await exitAgent();
1928
+ break;
1916
1929
  }
1917
- logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
1918
- notifyWebhook("IDLE", "", workingDir).catch(() => null);
1919
- await exitAgent();
1920
- });
1930
+ })();
1921
1931
  const stdinStream = new ReadableStream({
1922
1932
  start(controller) {
1923
1933
  process.stdin.resume();
@@ -2129,4 +2139,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
2129
2139
 
2130
2140
  //#endregion
2131
2141
  export { AgentContext as a, PidStore as c, config as i, removeControlCharacters as l, CLIS_CONFIG as n, name as o, agentYes as r, version as s, SUPPORTED_CLIS as t };
2132
- //# sourceMappingURL=SUPPORTED_CLIS-DbTReaSd.js.map
2142
+ //# sourceMappingURL=SUPPORTED_CLIS-Cl2oCgKo.js.map
package/dist/cli.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env bun
2
- import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DbTReaSd.js";
2
+ import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Cl2oCgKo.js";
3
3
  import { t as logger } from "./logger-CX77vJDA.js";
4
4
  import { argv } from "process";
5
5
  import { 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 { chmod, copyFile } from "fs/promises";
9
+ import { execaCommand } from "execa";
10
+ import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
10
11
  import path from "path";
12
+ import { homedir } from "os";
11
13
  import { existsSync, mkdirSync, unlinkSync } from "fs";
12
14
 
13
15
  //#region ts/parseCliArgs.ts
@@ -56,7 +58,8 @@ function parseCliArgs(argv) {
56
58
  alias: "i"
57
59
  }).option("idle-action", {
58
60
  type: "string",
59
- description: "Idle action to perform when idle time is reached, e.g., \"/exit\" or \"check TODO.md\""
61
+ description: "Idle action to perform when idle time is reached, e.g., \"/exit\" or \"check TODO.md\"",
62
+ alias: "ia"
60
63
  }).option("queue", {
61
64
  type: "boolean",
62
65
  description: "Queue Agent Commands when spawning multiple agents in the same directory/repo, can be disabled with --no-queue",
@@ -189,6 +192,7 @@ function parseCliArgs(argv) {
189
192
  useStdinAppend: Boolean(parsedArgv.stdpush || parsedArgv.ipc || parsedArgv.fifo),
190
193
  showVersion: parsedArgv.version,
191
194
  autoYes: parsedArgv.auto !== "no",
195
+ idleAction: parsedArgv.idleAction,
192
196
  useRust: parsedArgv.rust,
193
197
  swarm: parsedArgv.swarm ?? (parsedArgv.experimentalSwarm ? parsedArgv.swarmTopic : void 0),
194
198
  experimentalSwarm: parsedArgv.experimentalSwarm,
@@ -200,6 +204,62 @@ function parseCliArgs(argv) {
200
204
 
201
205
  //#endregion
202
206
  //#region ts/versionChecker.ts
207
+ const CACHE_DIR = path.join(homedir(), ".cache", "agent-yes");
208
+ const CACHE_FILE = path.join(CACHE_DIR, "update-check.json");
209
+ const TTL_MS = 3600 * 1e3;
210
+ async function readUpdateCache() {
211
+ try {
212
+ const raw = await readFile(CACHE_FILE, "utf8");
213
+ return JSON.parse(raw);
214
+ } catch {
215
+ return null;
216
+ }
217
+ }
218
+ async function writeUpdateCache(data) {
219
+ await mkdir(CACHE_DIR, { recursive: true });
220
+ await writeFile(CACHE_FILE, JSON.stringify(data));
221
+ }
222
+ function detectPackageManager() {
223
+ if (process.env.BUN_INSTALL || process.env.npm_execpath?.includes("bun")) return "bun";
224
+ return "npm";
225
+ }
226
+ /**
227
+ * Check for updates and auto-install if a newer version is available.
228
+ * Uses a 1-hour TTL cache to avoid hitting the registry on every run.
229
+ * All errors are swallowed — network issues must never break the tool.
230
+ * Set AGENT_YES_NO_UPDATE=1 to opt out.
231
+ */
232
+ async function checkAndAutoUpdate() {
233
+ if (process.env.AGENT_YES_NO_UPDATE) return;
234
+ try {
235
+ const cache = await readUpdateCache();
236
+ if (cache && Date.now() - cache.checkedAt < TTL_MS) {
237
+ if (compareVersions(version, cache.latestVersion) < 0) await runInstall(cache.latestVersion);
238
+ return;
239
+ }
240
+ const latestVersion = await fetchLatestVersion();
241
+ if (!latestVersion) return;
242
+ await writeUpdateCache({
243
+ checkedAt: Date.now(),
244
+ latestVersion
245
+ });
246
+ if (compareVersions(version, latestVersion) < 0) await runInstall(latestVersion);
247
+ } catch {}
248
+ }
249
+ async function runInstall(latestVersion) {
250
+ const installArgs = detectPackageManager() === "bun" ? `bun add -g agent-yes@${latestVersion}` : `npm install -g agent-yes@${latestVersion}`;
251
+ process.stderr.write(`\x1b[33m[agent-yes] Updating ${version} → ${latestVersion}…\x1b[0m\n`);
252
+ try {
253
+ await execaCommand(installArgs, { stdio: "inherit" });
254
+ await writeUpdateCache({
255
+ checkedAt: 0,
256
+ latestVersion
257
+ });
258
+ process.stderr.write(`\x1b[32m[agent-yes] Updated to ${latestVersion}\x1b[0m\n`);
259
+ } catch {
260
+ process.stderr.write(`\x1b[31m[agent-yes] Auto-update failed. Run: ${installArgs}\x1b[0m\n`);
261
+ }
262
+ }
203
263
  /**
204
264
  * Fetch the latest version of the package from npm registry
205
265
  */
@@ -413,6 +473,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
413
473
 
414
474
  //#endregion
415
475
  //#region ts/cli.ts
476
+ const updateCheckPromise = checkAndAutoUpdate();
416
477
  const config = parseCliArgs(process.argv);
417
478
  if (config.useRust) {
418
479
  let rustBinary;
@@ -502,6 +563,7 @@ const { exitCode } = await cliYes({
502
563
  ...config,
503
564
  autoYes: config.autoYes
504
565
  });
566
+ await updateCheckPromise;
505
567
  console.log("exiting process");
506
568
  process.exit(exitCode ?? 1);
507
569
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-DbTReaSd.js";
1
+ import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-Cl2oCgKo.js";
2
2
  import "./logger-CX77vJDA.js";
3
3
 
4
4
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.66.0",
3
+ "version": "1.68.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",
package/ts/cli.ts CHANGED
@@ -5,10 +5,13 @@ import { parseCliArgs } from "./parseCliArgs.ts";
5
5
  import { SUPPORTED_CLIS } from "./SUPPORTED_CLIS.ts";
6
6
  import { logger } from "./logger.ts";
7
7
  import { PidStore } from "./pidStore.ts";
8
- import { displayVersion } from "./versionChecker.ts";
8
+ import { checkAndAutoUpdate, displayVersion } from "./versionChecker.ts";
9
9
  import { getRustBinary } from "./rustBinary.ts";
10
10
  import { buildRustArgs } from "./buildRustArgs.ts";
11
11
 
12
+ // Start update check in background immediately (runs in parallel with the agent session)
13
+ const updateCheckPromise = checkAndAutoUpdate();
14
+
12
15
  // Parse CLI arguments
13
16
  const config = parseCliArgs(process.argv);
14
17
 
@@ -134,5 +137,9 @@ if (config.verbose) {
134
137
 
135
138
  const { default: cliYes } = await import("./index.ts");
136
139
  const { exitCode } = await cliYes({ ...config, autoYes: config.autoYes });
140
+
141
+ // Apply update if one was found during the session
142
+ await updateCheckPromise;
143
+
137
144
  console.log("exiting process");
138
145
  process.exit(exitCode ?? 1);
package/ts/index.ts CHANGED
@@ -119,6 +119,7 @@ export default async function agentYes({
119
119
  useSkills = false,
120
120
  useStdinAppend = false,
121
121
  autoYes = true,
122
+ idleAction,
122
123
  }: {
123
124
  cli: SUPPORTED_CLIS;
124
125
  cliArgs?: string[];
@@ -136,6 +137,7 @@ export default async function agentYes({
136
137
  useSkills?: boolean; // if true, prepend SKILL.md header to the prompt for non-Claude agents
137
138
  useStdinAppend?: boolean; // if true, enable FIFO input stream on Linux, for additional stdin input
138
139
  autoYes?: boolean; // if true, auto-yes is enabled (default), toggle with Ctrl+Y during session
140
+ idleAction?: string; // if set, type this message when idle instead of exiting
139
141
  }) {
140
142
  if (!cli) throw new Error(`cli is required`);
141
143
  const conf =
@@ -689,17 +691,26 @@ export default async function agentYes({
689
691
  shell.onExit(cleanupHeartbeat);
690
692
 
691
693
  if (exitOnIdle)
692
- ctx.idleWaiter.wait(exitOnIdle).then(async () => {
693
- await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
694
- if (isStillWorkingQ()) {
695
- logger.warn(`[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet`);
696
- return;
694
+ (async () => {
695
+ while (true) {
696
+ await ctx.idleWaiter.wait(exitOnIdle);
697
+ await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
698
+ if (isStillWorkingQ()) {
699
+ logger.warn(`[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet`);
700
+ continue;
701
+ }
702
+ if (idleAction) {
703
+ logger.info(`[${cli}-yes] ${cli} is idle, performing idle action: ${idleAction}`);
704
+ notifyWebhook("IDLE", `action=${idleAction}`, workingDir).catch(() => null);
705
+ await sendMessage(ctx.messageContext, idleAction);
706
+ continue;
707
+ }
708
+ logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
709
+ notifyWebhook("IDLE", "", workingDir).catch(() => null);
710
+ await exitAgent();
711
+ break;
697
712
  }
698
-
699
- logger.info(`[${cli}-yes] ${cli} is idle, exiting...`);
700
- notifyWebhook("IDLE", "", workingDir).catch(() => null);
701
- await exitAgent();
702
- });
713
+ })();
703
714
 
704
715
  // Message streaming
705
716
 
@@ -83,6 +83,7 @@ export function parseCliArgs(argv: string[]) {
83
83
  type: "string",
84
84
  description:
85
85
  'Idle action to perform when idle time is reached, e.g., "/exit" or "check TODO.md"',
86
+ alias: "ia",
86
87
  })
87
88
  .option("queue", {
88
89
  type: "boolean",
@@ -273,6 +274,7 @@ export function parseCliArgs(argv: string[]) {
273
274
  useStdinAppend: Boolean(parsedArgv.stdpush || parsedArgv.ipc || parsedArgv.fifo), // Support --stdpush, --ipc, and --fifo (backward compatibility)
274
275
  showVersion: parsedArgv.version,
275
276
  autoYes: parsedArgv.auto !== "no", // auto-yes enabled by default, disabled with --auto=no
277
+ idleAction: parsedArgv.idleAction as string | undefined,
276
278
  useRust: parsedArgv.rust,
277
279
  // New unified --swarm flag (takes precedence over deprecated flags)
278
280
  swarm: parsedArgv.swarm ?? (parsedArgv.experimentalSwarm ? parsedArgv.swarmTopic : undefined),
@@ -1,5 +1,17 @@
1
1
  import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
- import { compareVersions, fetchLatestVersion, displayVersion } from "./versionChecker";
2
+ import {
3
+ checkAndAutoUpdate,
4
+ compareVersions,
5
+ fetchLatestVersion,
6
+ displayVersion,
7
+ } from "./versionChecker";
8
+
9
+ vi.mock("execa", () => ({ execaCommand: vi.fn().mockResolvedValue({}) }));
10
+ vi.mock("fs/promises", () => ({
11
+ mkdir: vi.fn().mockResolvedValue(undefined),
12
+ readFile: vi.fn(),
13
+ writeFile: vi.fn().mockResolvedValue(undefined),
14
+ }));
3
15
 
4
16
  describe("versionChecker", () => {
5
17
  describe("compareVersions", () => {
@@ -64,6 +76,107 @@ describe("versionChecker", () => {
64
76
  });
65
77
  });
66
78
 
79
+ describe("checkAndAutoUpdate", () => {
80
+ beforeEach(() => {
81
+ vi.clearAllMocks();
82
+ vi.stubGlobal("fetch", vi.fn());
83
+ vi.spyOn(process.stderr, "write").mockImplementation(() => true);
84
+ delete process.env.AGENT_YES_NO_UPDATE;
85
+ delete process.env.BUN_INSTALL;
86
+ });
87
+
88
+ afterEach(() => {
89
+ vi.restoreAllMocks();
90
+ });
91
+
92
+ it("should skip when AGENT_YES_NO_UPDATE is set", async () => {
93
+ process.env.AGENT_YES_NO_UPDATE = "1";
94
+ await checkAndAutoUpdate();
95
+ expect(fetch).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it("should use cached result within TTL and not install when up-to-date", async () => {
99
+ const { readFile } = await import("fs/promises");
100
+ vi.mocked(readFile).mockResolvedValueOnce(
101
+ JSON.stringify({ checkedAt: Date.now(), latestVersion: "0.0.1" }) as any,
102
+ );
103
+ await checkAndAutoUpdate();
104
+ expect(fetch).not.toHaveBeenCalled();
105
+ expect(process.stderr.write).not.toHaveBeenCalled();
106
+ });
107
+
108
+ it("should install from cache when cached version is newer and within TTL", async () => {
109
+ const { readFile } = await import("fs/promises");
110
+ const { execaCommand } = await import("execa");
111
+ vi.mocked(readFile).mockResolvedValueOnce(
112
+ JSON.stringify({ checkedAt: Date.now(), latestVersion: "999.0.0" }) as any,
113
+ );
114
+ await checkAndAutoUpdate();
115
+ expect(execaCommand).toHaveBeenCalled();
116
+ });
117
+
118
+ it("should fetch and write cache when stale, install if behind", async () => {
119
+ const { readFile, writeFile } = await import("fs/promises");
120
+ const { execaCommand } = await import("execa");
121
+ vi.mocked(readFile).mockRejectedValueOnce(new Error("no cache"));
122
+ vi.mocked(fetch).mockResolvedValue({
123
+ ok: true,
124
+ json: async () => ({ version: "999.0.0" }),
125
+ } as Response);
126
+ await checkAndAutoUpdate();
127
+ expect(writeFile).toHaveBeenCalled();
128
+ expect(execaCommand).toHaveBeenCalled();
129
+ });
130
+
131
+ it("should fetch and write cache but not install if up-to-date", async () => {
132
+ const { readFile, writeFile } = await import("fs/promises");
133
+ const { execaCommand } = await import("execa");
134
+ vi.mocked(readFile).mockRejectedValueOnce(new Error("no cache"));
135
+ vi.mocked(fetch).mockResolvedValue({
136
+ ok: true,
137
+ json: async () => ({ version: "0.0.1" }),
138
+ } as Response);
139
+ await checkAndAutoUpdate();
140
+ expect(writeFile).toHaveBeenCalled();
141
+ expect(execaCommand).not.toHaveBeenCalled();
142
+ });
143
+
144
+ it("should silently handle fetch failure", async () => {
145
+ const { readFile } = await import("fs/promises");
146
+ vi.mocked(readFile).mockRejectedValueOnce(new Error("no cache"));
147
+ vi.mocked(fetch).mockRejectedValue(new Error("network error"));
148
+ await expect(checkAndAutoUpdate()).resolves.toBeUndefined();
149
+ });
150
+
151
+ it("should use bun when BUN_INSTALL is set", async () => {
152
+ process.env.BUN_INSTALL = "/home/user/.bun";
153
+ const { readFile } = await import("fs/promises");
154
+ const { execaCommand } = await import("execa");
155
+ vi.mocked(readFile).mockRejectedValueOnce(new Error("no cache"));
156
+ vi.mocked(fetch).mockResolvedValue({
157
+ ok: true,
158
+ json: async () => ({ version: "999.0.0" }),
159
+ } as Response);
160
+ await checkAndAutoUpdate();
161
+ expect(vi.mocked(execaCommand).mock.calls[0]?.[0]).toContain("bun");
162
+ });
163
+
164
+ it("should print error and not throw when install fails", async () => {
165
+ const { readFile } = await import("fs/promises");
166
+ const { execaCommand } = await import("execa");
167
+ vi.mocked(readFile).mockRejectedValueOnce(new Error("no cache"));
168
+ vi.mocked(fetch).mockResolvedValue({
169
+ ok: true,
170
+ json: async () => ({ version: "999.0.0" }),
171
+ } as Response);
172
+ vi.mocked(execaCommand).mockRejectedValueOnce(new Error("install failed"));
173
+ await expect(checkAndAutoUpdate()).resolves.toBeUndefined();
174
+ expect(process.stderr.write).toHaveBeenCalledWith(
175
+ expect.stringContaining("Auto-update failed"),
176
+ );
177
+ });
178
+ });
179
+
67
180
  describe("displayVersion", () => {
68
181
  beforeEach(() => {
69
182
  vi.stubGlobal("fetch", vi.fn());
@@ -1,5 +1,86 @@
1
+ import { execaCommand } from "execa";
2
+ import { mkdir, readFile, writeFile } from "fs/promises";
3
+ import { homedir } from "os";
4
+ import path from "path";
1
5
  import pkg from "../package.json" with { type: "json" };
2
6
 
7
+ const CACHE_DIR = path.join(homedir(), ".cache", "agent-yes");
8
+ const CACHE_FILE = path.join(CACHE_DIR, "update-check.json");
9
+ const TTL_MS = 60 * 60 * 1000; // 1 hour
10
+
11
+ type UpdateCache = { checkedAt: number; latestVersion: string };
12
+
13
+ async function readUpdateCache(): Promise<UpdateCache | null> {
14
+ try {
15
+ const raw = await readFile(CACHE_FILE, "utf8");
16
+ return JSON.parse(raw) as UpdateCache;
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ async function writeUpdateCache(data: UpdateCache): Promise<void> {
23
+ await mkdir(CACHE_DIR, { recursive: true });
24
+ await writeFile(CACHE_FILE, JSON.stringify(data));
25
+ }
26
+
27
+ function detectPackageManager(): string {
28
+ if (process.env.BUN_INSTALL || process.env.npm_execpath?.includes("bun")) return "bun";
29
+ return "npm";
30
+ }
31
+
32
+ /**
33
+ * Check for updates and auto-install if a newer version is available.
34
+ * Uses a 1-hour TTL cache to avoid hitting the registry on every run.
35
+ * All errors are swallowed — network issues must never break the tool.
36
+ * Set AGENT_YES_NO_UPDATE=1 to opt out.
37
+ */
38
+ export async function checkAndAutoUpdate(): Promise<void> {
39
+ if (process.env.AGENT_YES_NO_UPDATE) return;
40
+
41
+ try {
42
+ // Check cache TTL
43
+ const cache = await readUpdateCache();
44
+ if (cache && Date.now() - cache.checkedAt < TTL_MS) {
45
+ // Use cached result
46
+ if (compareVersions(pkg.version, cache.latestVersion) < 0) {
47
+ await runInstall(cache.latestVersion);
48
+ }
49
+ return;
50
+ }
51
+
52
+ // Fetch latest from registry
53
+ const latestVersion = await fetchLatestVersion();
54
+ if (!latestVersion) return;
55
+
56
+ await writeUpdateCache({ checkedAt: Date.now(), latestVersion });
57
+
58
+ if (compareVersions(pkg.version, latestVersion) < 0) {
59
+ await runInstall(latestVersion);
60
+ }
61
+ } catch {
62
+ // Silently ignore all errors
63
+ }
64
+ }
65
+
66
+ async function runInstall(latestVersion: string): Promise<void> {
67
+ const pm = detectPackageManager();
68
+ const installArgs =
69
+ pm === "bun"
70
+ ? `bun add -g agent-yes@${latestVersion}`
71
+ : `npm install -g agent-yes@${latestVersion}`;
72
+
73
+ process.stderr.write(`\x1b[33m[agent-yes] Updating ${pkg.version} → ${latestVersion}…\x1b[0m\n`);
74
+ try {
75
+ await execaCommand(installArgs, { stdio: "inherit" });
76
+ // Clear cache so next run re-checks
77
+ await writeUpdateCache({ checkedAt: 0, latestVersion });
78
+ process.stderr.write(`\x1b[32m[agent-yes] Updated to ${latestVersion}\x1b[0m\n`);
79
+ } catch {
80
+ process.stderr.write(`\x1b[31m[agent-yes] Auto-update failed. Run: ${installArgs}\x1b[0m\n`);
81
+ }
82
+ }
83
+
3
84
  /**
4
85
  * Fetch the latest version of the package from npm registry
5
86
  */