agent-yes 1.67.0 → 1.68.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.
@@ -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.67.0";
1062
+ var version = "1.68.1";
1063
1063
 
1064
1064
  //#endregion
1065
1065
  //#region ts/pty-fix.ts
@@ -2139,4 +2139,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
2139
2139
 
2140
2140
  //#endregion
2141
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 };
2142
- //# sourceMappingURL=SUPPORTED_CLIS-CmSMCHW2.js.map
2142
+ //# sourceMappingURL=SUPPORTED_CLIS-Dxapc1qa.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-CmSMCHW2.js";
2
+ import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Dxapc1qa.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
@@ -202,6 +204,62 @@ function parseCliArgs(argv) {
202
204
 
203
205
  //#endregion
204
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
+ }
205
263
  /**
206
264
  * Fetch the latest version of the package from npm registry
207
265
  */
@@ -415,37 +473,42 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
415
473
 
416
474
  //#endregion
417
475
  //#region ts/cli.ts
476
+ const updateCheckPromise = checkAndAutoUpdate();
418
477
  const config = parseCliArgs(process.argv);
419
478
  if (config.useRust) {
420
479
  let rustBinary;
421
480
  try {
422
481
  rustBinary = await getRustBinary({ verbose: config.verbose });
423
482
  } catch (err) {
424
- console.error(err instanceof Error ? err.message : String(err));
425
- process.exit(1);
483
+ if (config.verbose) {
484
+ console.error(`[rust] ${err instanceof Error ? err.message : String(err)}`);
485
+ console.error("[rust] Falling back to TypeScript implementation.");
486
+ }
426
487
  }
427
- const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
428
- if (config.verbose) {
429
- console.log(`[rust] Using binary: ${rustBinary}`);
430
- console.log(`[rust] Args: ${rustArgs.join(" ")}`);
488
+ if (rustBinary) {
489
+ const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
490
+ if (config.verbose) {
491
+ console.log(`[rust] Using binary: ${rustBinary}`);
492
+ console.log(`[rust] Args: ${rustArgs.join(" ")}`);
493
+ }
494
+ const child = spawn(rustBinary, rustArgs, {
495
+ stdio: "inherit",
496
+ env: process.env,
497
+ cwd: process.cwd()
498
+ });
499
+ child.on("error", (err) => {
500
+ if (err.code === "ENOENT") console.error(`Rust binary '${rustBinary}' not found. Try: npx agent-yes --rust --verbose`);
501
+ else console.error(`Failed to spawn Rust binary: ${err.message}`);
502
+ process.exit(1);
503
+ });
504
+ child.on("exit", (code, signal) => {
505
+ if (signal) process.exit(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
506
+ process.exit(code ?? 1);
507
+ });
508
+ process.on("SIGINT", () => child.kill("SIGINT"));
509
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
510
+ await new Promise(() => {});
431
511
  }
432
- const child = spawn(rustBinary, rustArgs, {
433
- stdio: "inherit",
434
- env: process.env,
435
- cwd: process.cwd()
436
- });
437
- child.on("error", (err) => {
438
- if (err.code === "ENOENT") console.error(`Rust binary '${rustBinary}' not found. Try: npx agent-yes --rust --verbose`);
439
- else console.error(`Failed to spawn Rust binary: ${err.message}`);
440
- process.exit(1);
441
- });
442
- child.on("exit", (code, signal) => {
443
- if (signal) process.exit(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
444
- process.exit(code ?? 1);
445
- });
446
- process.on("SIGINT", () => child.kill("SIGINT"));
447
- process.on("SIGTERM", () => child.kill("SIGTERM"));
448
- await new Promise(() => {});
449
512
  }
450
513
  if (config.showVersion) {
451
514
  await displayVersion();
@@ -504,6 +567,7 @@ const { exitCode } = await cliYes({
504
567
  ...config,
505
568
  autoYes: config.autoYes
506
569
  });
570
+ await updateCheckPromise;
507
571
  console.log("exiting process");
508
572
  process.exit(exitCode ?? 1);
509
573
 
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-CmSMCHW2.js";
1
+ import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-Dxapc1qa.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.67.0",
3
+ "version": "1.68.1",
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,62 +5,65 @@ 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
 
15
- // Handle --rust: spawn the Rust binary instead
18
+ // Handle --rust: spawn the Rust binary instead, fall back to TypeScript if unavailable
16
19
  if (config.useRust) {
17
- let rustBinary: string;
20
+ let rustBinary: string | undefined;
18
21
 
19
22
  try {
20
- // Get or download the Rust binary for the current platform
21
23
  rustBinary = await getRustBinary({ verbose: config.verbose });
22
24
  } catch (err) {
23
- console.error(err instanceof Error ? err.message : String(err));
24
- process.exit(1);
25
- }
26
-
27
- // Build args for Rust binary
28
- const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
29
-
30
- if (config.verbose) {
31
- console.log(`[rust] Using binary: ${rustBinary}`);
32
- console.log(`[rust] Args: ${rustArgs.join(" ")}`);
25
+ // Rust binary unavailable (not yet released for this version, or network issue) — fall back to TypeScript
26
+ if (config.verbose) {
27
+ console.error(`[rust] ${err instanceof Error ? err.message : String(err)}`);
28
+ console.error("[rust] Falling back to TypeScript implementation.");
29
+ }
33
30
  }
34
31
 
35
- // Spawn the Rust process with stdio inheritance
36
- const child = spawn(rustBinary, rustArgs, {
37
- stdio: "inherit",
38
- env: process.env,
39
- cwd: process.cwd(),
40
- });
41
-
42
- child.on("error", (err) => {
43
- if ((err as NodeJS.ErrnoException).code === "ENOENT") {
44
- console.error(`Rust binary '${rustBinary}' not found. Try: npx agent-yes --rust --verbose`);
45
- } else {
46
- console.error(`Failed to spawn Rust binary: ${err.message}`);
47
- }
48
- process.exit(1);
49
- });
32
+ if (rustBinary) {
33
+ const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
50
34
 
51
- child.on("exit", (code, signal) => {
52
- if (signal) {
53
- process.exit(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
35
+ if (config.verbose) {
36
+ console.log(`[rust] Using binary: ${rustBinary}`);
37
+ console.log(`[rust] Args: ${rustArgs.join(" ")}`);
54
38
  }
55
- process.exit(code ?? 1);
56
- });
57
-
58
- // Forward signals to child
59
- process.on("SIGINT", () => child.kill("SIGINT"));
60
- process.on("SIGTERM", () => child.kill("SIGTERM"));
61
39
 
62
- // Keep the process alive while child is running
63
- await new Promise(() => {}); // Never resolves, exits via child.on("exit")
40
+ const child = spawn(rustBinary, rustArgs, {
41
+ stdio: "inherit",
42
+ env: process.env,
43
+ cwd: process.cwd(),
44
+ });
45
+
46
+ child.on("error", (err) => {
47
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
48
+ console.error(`Rust binary '${rustBinary}' not found. Try: npx agent-yes --rust --verbose`);
49
+ } else {
50
+ console.error(`Failed to spawn Rust binary: ${err.message}`);
51
+ }
52
+ process.exit(1);
53
+ });
54
+
55
+ child.on("exit", (code, signal) => {
56
+ if (signal) {
57
+ process.exit(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
58
+ }
59
+ process.exit(code ?? 1);
60
+ });
61
+
62
+ process.on("SIGINT", () => child.kill("SIGINT"));
63
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
64
+
65
+ await new Promise(() => {}); // Never resolves, exits via child.on("exit")
66
+ }
64
67
  }
65
68
 
66
69
  // Handle --version: display version and exit
@@ -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);
@@ -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
  */