claude-yes 1.72.3 → 1.73.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.
@@ -8,9 +8,9 @@ import { appendFile, mkdir, readFile, readdir, rename, unlink, writeFile } from
8
8
  import path, { dirname, join } from "path";
9
9
  import DIE from "phpdie";
10
10
  import sflow from "sflow";
11
- import { TerminalRenderStream } from "terminal-render";
12
- import { homedir } from "os";
11
+ import xterm from "@xterm/headless";
13
12
  import winston from "winston";
13
+ import { homedir } from "os";
14
14
  import { closeSync, constants, createReadStream, existsSync, fsyncSync, mkdirSync, openSync } from "fs";
15
15
  import { createServer } from "net";
16
16
  import { lock } from "proper-lockfile";
@@ -37,6 +37,109 @@ var __exportAll = (all, no_symbols) => {
37
37
  return target;
38
38
  };
39
39
 
40
+ //#endregion
41
+ //#region ts/xterm-proxy.ts
42
+ const { Terminal } = xterm;
43
+ /**
44
+ * XtermProxy wraps @xterm/headless to act as a full xterm terminal emulator
45
+ * between a PTY process and downstream consumers.
46
+ *
47
+ * It automatically responds to ALL terminal queries (DSR, DA, OSC, etc.)
48
+ * by piping xterm's onData responses back to the PTY — so the spawned
49
+ * process never blocks waiting for a terminal reply, even in non-TTY
50
+ * environments or when the real terminal is backgrounded.
51
+ */
52
+ var XtermProxy = class {
53
+ term;
54
+ writeToPty;
55
+ readableController = null;
56
+ /** Downstream readable — passthrough of PTY output for sflow pipeline */
57
+ readable;
58
+ constructor(opts) {
59
+ const cols = opts.cols ?? 80;
60
+ const rows = opts.rows ?? 24;
61
+ this.writeToPty = opts.writeToPty;
62
+ this.term = new Terminal({
63
+ cols,
64
+ rows,
65
+ allowProposedApi: true,
66
+ scrollback: 1e4
67
+ });
68
+ this.term.onData((data) => {
69
+ logger.debug("xterm-proxy|onData response", data);
70
+ this.writeToPty(data);
71
+ });
72
+ this.readable = new ReadableStream({ start: (controller) => {
73
+ this.readableController = controller;
74
+ } });
75
+ }
76
+ /**
77
+ * Feed PTY output into the xterm emulator.
78
+ * - xterm processes escape sequences and updates internal state
79
+ * - Terminal queries (ESC[6n, ESC[c, etc.) trigger onData → writeToPty
80
+ * - Raw data is pushed to readable for downstream consumption
81
+ */
82
+ write(data) {
83
+ this.term.write(data, () => {
84
+ try {
85
+ this.readableController?.enqueue(data);
86
+ } catch {}
87
+ });
88
+ }
89
+ /** Get cursor position from xterm's buffer state */
90
+ getCursorPosition() {
91
+ const buf = this.term.buffer.active;
92
+ return {
93
+ row: buf.cursorY,
94
+ col: buf.cursorX
95
+ };
96
+ }
97
+ /**
98
+ * Get the last N lines of rendered terminal content (plain text, no ANSI).
99
+ * Equivalent to terminal-render's tail(n).
100
+ */
101
+ tail(n) {
102
+ const buf = this.term.buffer.active;
103
+ const totalLines = buf.length;
104
+ const startLine = Math.max(0, totalLines - n);
105
+ const lines = [];
106
+ for (let i = startLine; i < totalLines; i++) {
107
+ const line = buf.getLine(i);
108
+ lines.push(line ? line.translateToString(true) : "");
109
+ }
110
+ while (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
111
+ return lines.join("\n");
112
+ }
113
+ /**
114
+ * Render the full terminal buffer as plain text.
115
+ * Equivalent to terminal-render's render().
116
+ */
117
+ render() {
118
+ const buf = this.term.buffer.active;
119
+ const lines = [];
120
+ for (let i = 0; i < buf.length; i++) {
121
+ const line = buf.getLine(i);
122
+ lines.push(line ? line.translateToString(true) : "");
123
+ }
124
+ while (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
125
+ return lines.join("\n");
126
+ }
127
+ /** Resize the virtual terminal */
128
+ resize(cols, rows) {
129
+ this.term.resize(cols, rows);
130
+ }
131
+ /** Clean up resources */
132
+ dispose() {
133
+ if (this.readableController) {
134
+ try {
135
+ this.readableController.close();
136
+ } catch {}
137
+ this.readableController = null;
138
+ }
139
+ this.term.dispose();
140
+ }
141
+ };
142
+
40
143
  //#endregion
41
144
  //#region ts/resume/codexSessionManager.ts
42
145
  const getSessionsFile = () => process.env.CLI_YES_TEST_HOME ? path.join(process.env.CLI_YES_TEST_HOME, ".config", "agent-yes", "codex-sessions.json") : path.join(homedir(), ".config", "agent-yes", "codex-sessions.json");
@@ -815,7 +918,7 @@ function tryCatch(catchFn, fn) {
815
918
  //#endregion
816
919
  //#region package.json
817
920
  var name = "agent-yes";
818
- var version = "1.72.3";
921
+ var version = "1.72.4";
819
922
 
820
923
  //#endregion
821
924
  //#region ts/pty-fix.ts
@@ -1299,9 +1402,11 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1299
1402
  if (verbose) logger.debug(`[stdin] isTTY: ${process.stdin.isTTY}, setRawMode available: ${!!process.stdin.setRawMode}`);
1300
1403
  process.stdin.setRawMode?.(true);
1301
1404
  if (verbose) logger.debug(`[stdin] Raw mode set, isRaw: ${process.stdin.isRaw}`);
1302
- const terminalStream = new TerminalRenderStream({ mode: "raw" });
1303
- const terminalRender = terminalStream.getRenderer();
1304
- const outputWriter = terminalStream.writable.getWriter();
1405
+ let shellWrite = () => {};
1406
+ const xtermProxy = new XtermProxy({
1407
+ ...getTerminalDimensions(),
1408
+ writeToPty: (data) => shellWrite(data)
1409
+ });
1305
1410
  logger.debug(`Using ${ptyPackage} for pseudo terminal management.`);
1306
1411
  if (!!process.env.CLAUDE_PPID) logger.info(`[${cli}-yes] Running as sub-agent (CLAUDE_PPID=${process.env.CLAUDE_PPID})`);
1307
1412
  const cliConf = CLIS_CONFIG[cli] || {};
@@ -1393,9 +1498,10 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1393
1498
  install,
1394
1499
  ptyOptions
1395
1500
  });
1501
+ shellWrite = (data) => shell.write(data);
1396
1502
  function onData(data) {
1397
1503
  const currentPid = shell.pid;
1398
- outputWriter.write(data);
1504
+ xtermProxy.write(data);
1399
1505
  globalAgentRegistry.appendStdout(currentPid, data);
1400
1506
  }
1401
1507
  shell.onData(onData);
@@ -1479,6 +1585,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1479
1585
  env: ptyEnv
1480
1586
  };
1481
1587
  shell = pty.spawn(bin, args, restartPtyOptions);
1588
+ shellWrite = (data) => shell.write(data);
1482
1589
  try {
1483
1590
  await pidStore.registerProcess({
1484
1591
  pid: shell.pid,
@@ -1561,6 +1668,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1561
1668
  env: ptyEnv
1562
1669
  };
1563
1670
  shell = pty.spawn(cli, restoreArgs, restorePtyOptions);
1671
+ shellWrite = (data) => shell.write(data);
1564
1672
  try {
1565
1673
  await pidStore.registerProcess({
1566
1674
  pid: shell.pid,
@@ -1609,15 +1717,16 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1609
1717
  process.stdout.on("resize", () => {
1610
1718
  const { cols, rows } = getTerminalDimensions();
1611
1719
  shell.resize(cols, rows);
1720
+ xtermProxy.resize(cols, rows);
1612
1721
  });
1613
1722
  const isStillWorkingQ = () => {
1614
- const rendered = terminalRender.tail(24).replace(/\s+/g, " ");
1723
+ const rendered = xtermProxy.tail(24).replace(/\s+/g, " ");
1615
1724
  return conf.working?.some((rgx) => rgx.test(rendered));
1616
1725
  };
1617
1726
  let lastHeartbeatRendered = "";
1618
1727
  const heartbeatInterval = setInterval(async () => {
1619
1728
  try {
1620
- const rendered = removeControlCharacters(terminalRender.tail(12));
1729
+ const rendered = removeControlCharacters(xtermProxy.tail(12));
1621
1730
  if (rendered === lastHeartbeatRendered) return;
1622
1731
  lastHeartbeatRendered = rendered;
1623
1732
  const lines = rendered.split("\n").filter((line) => line.trim());
@@ -1792,7 +1901,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1792
1901
  await ctx.stdinReady.wait();
1793
1902
  shell.write(data);
1794
1903
  } }),
1795
- readable: terminalStream.readable
1904
+ readable: xtermProxy.readable
1796
1905
  }).forEach(() => {
1797
1906
  ctx.idleWaiter.ping();
1798
1907
  pidStore.updateStatus(shell.pid, "active").catch(() => null);
@@ -1808,19 +1917,10 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1808
1917
  }).catch(() => f.run());
1809
1918
  }).by(function consoleResponder(e) {
1810
1919
  let lastRendered = "";
1811
- return e.forEach((chunk) => {
1812
- if (chunk.includes("\x1B[c") || chunk.includes("\x1B[0c")) {
1813
- shell.write("\x1B[?1;2c");
1814
- if (verbose) logger.debug("device|respond DA: VT100 with Advanced Video Option");
1815
- return;
1816
- }
1817
- if (process.stdin.isTTY) return;
1818
- if (!chunk.includes("\x1B[6n")) return;
1819
- const { col, row } = terminalRender.getCursorPosition();
1820
- shell.write(`\u001b[${row};${col}R`);
1821
- logger.debug(`cursor|respond position: row=${String(row)}, col=${String(col)}`);
1822
- }).forEach(async (line, lineIndex) => {
1823
- if (terminalRender.tail(24) === lastRendered) return;
1920
+ return e.forEach(async (line, lineIndex) => {
1921
+ const rendered = xtermProxy.tail(24);
1922
+ if (rendered === lastRendered) return;
1923
+ lastRendered = rendered;
1824
1924
  logger.debug(`stdout|${line}`);
1825
1925
  if (conf.ready?.some((rx) => line.match(rx))) {
1826
1926
  logger.debug(`ready |${line}`);
@@ -1854,15 +1954,16 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
1854
1954
  }
1855
1955
  });
1856
1956
  }).by((s) => removeControlCharactersFromStdout ? s.map((e) => removeControlCharacters(e)) : s).by(createTerminatorStream(pendingExitCode.promise)).to(fromWritable(process.stdout));
1857
- await saveLogFile(ctx.logPaths.logPath, terminalRender.render());
1957
+ await saveLogFile(ctx.logPaths.logPath, xtermProxy.render());
1858
1958
  const exitCode = await pendingExitCode.promise;
1859
1959
  logger.info(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
1860
1960
  await pidStore.close();
1861
- await outputWriter.close();
1862
- await saveDeprecatedLogFile(logFile, terminalRender.render(), verbose);
1961
+ const finalRender = xtermProxy.render();
1962
+ xtermProxy.dispose();
1963
+ await saveDeprecatedLogFile(logFile, finalRender, verbose);
1863
1964
  return {
1864
1965
  exitCode,
1865
- logs: terminalRender.render()
1966
+ logs: finalRender
1866
1967
  };
1867
1968
  async function exitAgent() {
1868
1969
  ctx.robust = false;
@@ -1895,4 +1996,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
1895
1996
 
1896
1997
  //#endregion
1897
1998
  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 };
1898
- //# sourceMappingURL=SUPPORTED_CLIS-C-KnmE0Y.js.map
1999
+ //# sourceMappingURL=SUPPORTED_CLIS-DgHs-Q6i.js.map
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bun
2
- import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C-KnmE0Y.js";
2
+ import { c as PidStore, o as name, s as version, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DgHs-Q6i.js";
3
3
  import { t as logger } from "./logger-CX77vJDA.js";
4
4
  import { argv } from "process";
5
- import { spawn } from "child_process";
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";
@@ -10,7 +10,7 @@ import { execaCommand } from "execa";
10
10
  import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
11
11
  import path from "path";
12
12
  import { homedir } from "os";
13
- import { existsSync, mkdirSync, unlinkSync } from "fs";
13
+ import { existsSync, lstatSync, mkdirSync, readlinkSync, unlinkSync } from "fs";
14
14
 
15
15
  //#region ts/parseCliArgs.ts
16
16
  /**
@@ -156,26 +156,53 @@ function parseCliArgs(argv) {
156
156
  if (key === "continue") yargsConsumed.add("-c");
157
157
  }
158
158
  });
159
+ const positionalPromptWords = [];
159
160
  const cliArgsForSpawn = (() => {
160
- if (parsedArgv._[0] && !cliName) return rawArgs.slice((cliArgIndex ?? 0) + 1, dashIndex ?? void 0);
161
- else if (cliName) {
161
+ if (parsedArgv._[0] && !cliName) {
162
+ const allAfterCli = rawArgs.slice((cliArgIndex ?? 0) + 1, dashIndex ?? void 0);
163
+ const result = [];
164
+ for (let i = 0; i < allAfterCli.length; i++) {
165
+ const arg = allAfterCli[i];
166
+ if (arg.startsWith("-")) {
167
+ result.push(arg);
168
+ if (!arg.includes("=") && i + 1 < allAfterCli.length) {
169
+ const nextArg = allAfterCli[i + 1];
170
+ if (nextArg && !nextArg.startsWith("-")) {
171
+ result.push(nextArg);
172
+ i++;
173
+ }
174
+ }
175
+ } else positionalPromptWords.push(arg);
176
+ }
177
+ return result;
178
+ } else if (cliName) {
162
179
  const result = [];
163
180
  const argsToCheck = rawArgs.slice(0, dashIndex ?? void 0);
164
181
  for (let i = 0; i < argsToCheck.length; i++) {
165
182
  const arg = argsToCheck[i];
166
183
  if (!arg) continue;
167
184
  const [flag] = arg.split("=");
168
- if (flag && yargsConsumed.has(flag)) {
185
+ if (flag && yargsConsumed.has(flag) || flag?.startsWith("--no-") && yargsConsumed.has(`--${flag.slice(5)}`)) {
169
186
  if (!arg.includes("=") && i + 1 < argsToCheck.length) {
170
187
  const nextArg = argsToCheck[i + 1];
171
188
  if (nextArg && !nextArg.startsWith("-")) i++;
172
189
  }
173
- } else result.push(arg);
190
+ } else if (arg.startsWith("-")) {
191
+ result.push(arg);
192
+ if (!arg.includes("=") && i + 1 < argsToCheck.length) {
193
+ const nextArg = argsToCheck[i + 1];
194
+ if (nextArg && !nextArg.startsWith("-")) {
195
+ result.push(nextArg);
196
+ i++;
197
+ }
198
+ }
199
+ } else positionalPromptWords.push(arg);
174
200
  }
175
201
  return result;
176
202
  }
177
203
  return [];
178
204
  })();
205
+ const positionalPrompt = positionalPromptWords.join(" ") || void 0;
179
206
  const dashPrompt = dashIndex === void 0 ? void 0 : rawArgs.slice(dashIndex + 1).join(" ");
180
207
  if (parsedArgv.exitOnIdle !== void 0) console.warn("\x1B[33m⚠ Warning: --exit-on-idle and -e are deprecated. Please use --timeout instead.\x1B[0m");
181
208
  return {
@@ -183,7 +210,11 @@ function parseCliArgs(argv) {
183
210
  env: process.env,
184
211
  cli: cliName || parsedArgv.cli || (dashIndex !== 0 ? parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "") : void 0),
185
212
  cliArgs: [...cliArgsForSpawn, ...parsedArgv.yes ? ["--dangerously-skip-permissions"] : []],
186
- prompt: [parsedArgv.prompt, dashPrompt].filter(Boolean).join(" ") || void 0,
213
+ prompt: [
214
+ parsedArgv.prompt,
215
+ positionalPrompt,
216
+ dashPrompt
217
+ ].filter(Boolean).join(" ") || void 0,
187
218
  install: parsedArgv.install,
188
219
  exitOnIdle: Number((parsedArgv.timeout || parsedArgv.idle || parsedArgv.exitOnIdle)?.replace(/.*/, (e) => String(ms(e))) || 0),
189
220
  queue: parsedArgv.queue,
@@ -225,44 +256,77 @@ async function writeUpdateCache(data) {
225
256
  await writeFile(CACHE_FILE, JSON.stringify(data));
226
257
  }
227
258
  function detectPackageManager() {
228
- if (process.env.BUN_INSTALL || process.env.npm_execpath?.includes("bun")) return "bun";
259
+ if (process.env.BUN_INSTALL || process.execPath?.includes("bun") || process.env.npm_execpath?.includes("bun")) return "bun";
229
260
  return "npm";
230
261
  }
231
262
  /**
232
- * Check for updates and auto-install if a newer version is available.
263
+ * Check for updates, auto-install if newer version is available, and re-exec
264
+ * so the current invocation always runs the latest code.
265
+ *
233
266
  * Uses a 1-hour TTL cache to avoid hitting the registry on every run.
234
267
  * All errors are swallowed — network issues must never break the tool.
235
268
  * Set AGENT_YES_NO_UPDATE=1 to opt out.
269
+ *
270
+ * The AGENT_YES_UPDATED env var prevents infinite re-exec loops:
271
+ * after updating we re-exec with AGENT_YES_UPDATED=<version> so the
272
+ * new process skips the update check.
236
273
  */
237
274
  async function checkAndAutoUpdate() {
238
275
  if (process.env.AGENT_YES_NO_UPDATE) return;
276
+ if (process.env.AGENT_YES_UPDATED) return;
277
+ if (import.meta.url.startsWith("file://") && !import.meta.url.includes("node_modules")) {
278
+ const scriptDir = path.dirname(new URL(import.meta.url).pathname);
279
+ const repoRoot = path.resolve(scriptDir, "..");
280
+ if (existsSync(path.join(repoRoot, ".git"))) return;
281
+ }
239
282
  try {
283
+ let latestVersion;
240
284
  const cache = await readUpdateCache();
241
- if (cache && Date.now() - cache.checkedAt < TTL_MS) {
242
- if (compareVersions(version, cache.latestVersion) < 0) await runInstall(cache.latestVersion);
243
- return;
285
+ if (cache && Date.now() - cache.checkedAt < TTL_MS) latestVersion = cache.latestVersion;
286
+ else {
287
+ const fetched = await fetchLatestVersion();
288
+ if (!fetched) return;
289
+ latestVersion = fetched;
290
+ await writeUpdateCache({
291
+ checkedAt: Date.now(),
292
+ latestVersion
293
+ });
294
+ }
295
+ if (compareVersions(version, latestVersion) < 0) {
296
+ if (await runInstall(latestVersion)) reExec(latestVersion);
244
297
  }
245
- const latestVersion = await fetchLatestVersion();
246
- if (!latestVersion) return;
247
- await writeUpdateCache({
248
- checkedAt: Date.now(),
249
- latestVersion
250
- });
251
- if (compareVersions(version, latestVersion) < 0) await runInstall(latestVersion);
252
298
  } catch {}
253
299
  }
254
300
  async function runInstall(latestVersion) {
255
- const installArgs = detectPackageManager() === "bun" ? `bun add -g agent-yes@${latestVersion}` : `npm install -g agent-yes@${latestVersion}`;
301
+ const installCmd = detectPackageManager() === "bun" ? `bun add -g agent-yes@${latestVersion}` : `npm install -g agent-yes@${latestVersion}`;
256
302
  process.stderr.write(`\x1b[33m[agent-yes] Updating ${version} → ${latestVersion}…\x1b[0m\n`);
257
303
  try {
258
- await execaCommand(installArgs, { stdio: "inherit" });
259
- await writeUpdateCache({
260
- checkedAt: 0,
261
- latestVersion
262
- });
304
+ await execaCommand(installCmd, { stdio: "inherit" });
263
305
  process.stderr.write(`\x1b[32m[agent-yes] Updated to ${latestVersion}\x1b[0m\n`);
306
+ return true;
264
307
  } catch {
265
- process.stderr.write(`\x1b[31m[agent-yes] Auto-update failed. Run: ${installArgs}\x1b[0m\n`);
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);
266
330
  }
267
331
  }
268
332
  /**
@@ -293,10 +357,47 @@ function compareVersions(v1, v2) {
293
357
  return 0;
294
358
  }
295
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
+ /**
296
397
  * Display version information with async latest version check
297
398
  */
298
399
  async function displayVersion() {
299
- console.log(version);
400
+ console.log(versionString());
300
401
  const latestVersion = await fetchLatestVersion();
301
402
  if (latestVersion) {
302
403
  const comparison = compareVersions(version, latestVersion);
@@ -438,6 +539,53 @@ async function downloadBinary(verbose = false) {
438
539
  return binaryPath;
439
540
  }
440
541
  /**
542
+ * Get the version of a Rust binary by running it with --version
543
+ */
544
+ function getRustBinaryVersion(binaryPath) {
545
+ try {
546
+ const match = execFileSync(binaryPath, ["--version"], {
547
+ timeout: 5e3,
548
+ encoding: "utf8",
549
+ stdio: [
550
+ "ignore",
551
+ "pipe",
552
+ "ignore"
553
+ ]
554
+ }).match(/(\d+\.\d+\.\d+)/);
555
+ return match ? match[1] : null;
556
+ } catch {
557
+ return null;
558
+ }
559
+ }
560
+ /**
561
+ * Check if a binary path is inside a git repo (dev build), and rebuild if outdated.
562
+ * Returns the same path if up-to-date or rebuilt, undefined if rebuild failed.
563
+ */
564
+ function autoRebuildIfOutdated(binaryPath, verbose) {
565
+ if (!binaryPath.includes("/target/release") && !binaryPath.includes("/target/debug")) return true;
566
+ const binaryVersion = getRustBinaryVersion(binaryPath);
567
+ if (verbose) console.log(`[rust] Binary version: ${binaryVersion}, package version: ${version}`);
568
+ if (binaryVersion === version) return true;
569
+ const rsDir = binaryPath.replace(/\/target\/(release|debug)\/agent-yes.*$/, "");
570
+ if (!existsSync(path.join(rsDir, "Cargo.toml"))) {
571
+ if (verbose) console.log(`[rust] Cannot find Cargo.toml at ${rsDir}, skipping rebuild`);
572
+ return true;
573
+ }
574
+ process.stderr.write(`\x1b[33m[rust] Binary outdated (${binaryVersion ?? "unknown"} → ${version}), rebuilding…\x1b[0m\n`);
575
+ try {
576
+ execFileSync("cargo", ["build", ...binaryPath.includes("/target/release") ? ["--release"] : []], {
577
+ cwd: rsDir,
578
+ stdio: "inherit",
579
+ timeout: 3e5
580
+ });
581
+ process.stderr.write(`\x1b[32m[rust] Rebuild complete\x1b[0m\n`);
582
+ return true;
583
+ } catch {
584
+ process.stderr.write(`\x1b[31m[rust] Auto-rebuild failed, using outdated binary\x1b[0m\n`);
585
+ return true;
586
+ }
587
+ }
588
+ /**
441
589
  * Get or download the Rust binary
442
590
  */
443
591
  async function getRustBinary(options = {}) {
@@ -446,6 +594,7 @@ async function getRustBinary(options = {}) {
446
594
  const existing = findRustBinary(verbose);
447
595
  if (existing) {
448
596
  if (verbose) console.log(`[rust] Using existing binary: ${existing}`);
597
+ autoRebuildIfOutdated(existing, verbose);
449
598
  return existing;
450
599
  }
451
600
  }
@@ -478,15 +627,16 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
478
627
 
479
628
  //#endregion
480
629
  //#region ts/cli.ts
481
- const updateCheckPromise = checkAndAutoUpdate();
630
+ await checkAndAutoUpdate();
631
+ logger.info(versionString());
482
632
  const config = parseCliArgs(process.argv);
483
633
  if (config.tray) {
484
- const { startTray } = await import("./tray-BQkynk6r.js");
634
+ const { startTray } = await import("./tray-CPpdxTV-.js");
485
635
  await startTray();
486
636
  await new Promise(() => {});
487
637
  }
488
638
  {
489
- const { ensureTray } = await import("./tray-BQkynk6r.js");
639
+ const { ensureTray } = await import("./tray-CPpdxTV-.js");
490
640
  ensureTray();
491
641
  }
492
642
  if (config.useRust) {
@@ -581,7 +731,6 @@ const { exitCode } = await cliYes({
581
731
  ...config,
582
732
  autoYes: config.autoYes
583
733
  });
584
- await updateCheckPromise;
585
734
  console.log("exiting process");
586
735
  process.exit(exitCode ?? 1);
587
736
 
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-C-KnmE0Y.js";
1
+ import { a as AgentContext, i as config, l as removeControlCharacters, n as CLIS_CONFIG, r as agentYes } from "./SUPPORTED_CLIS-DgHs-Q6i.js";
2
2
  import "./logger-CX77vJDA.js";
3
3
 
4
4
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
@@ -6,7 +6,6 @@ import { existsSync } from "fs";
6
6
 
7
7
  //#region ts/tray.ts
8
8
  const POLL_INTERVAL = 2e3;
9
- const IDLE_EXIT_POLLS = 15;
10
9
  const getTrayDir = () => path.join(process.env.CLAUDE_YES_HOME || homedir(), ".claude-yes");
11
10
  const getTrayPidFile = () => path.join(getTrayDir(), "tray.pid");
12
11
  const ICON_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAjklEQVQ4T2NkoBAwUqifgWoGMDIyNjAyMv5nYGBYQMgVjMgCQM0LGBkZHYDYAY8BDUBxByB2wGcAyAUOQOwAxPYMDAyOeCzAbwBIMyMjowNQsz0ely8ACjng8wJeA0CaGRgY7IHYAZ8hQHEHfF7AawBYMwODPZABRHsBpwEgzUDN9kDsgM8lQHEHfC4gJhwAAM3hMBGq3cNNAAAAAElFTkSuQmCC";
@@ -153,17 +152,9 @@ async function startTray() {
153
152
  if (action.item.title === "Quit Tray") cleanup();
154
153
  });
155
154
  let lastCount = count;
156
- let idlePolls = count === 0 ? 1 : 0;
157
155
  intervalId = setInterval(async () => {
158
156
  try {
159
157
  const { count: newCount, tasks: newTasks } = await getRunningAgentCount();
160
- if (newCount === 0) {
161
- idlePolls++;
162
- if (idlePolls >= IDLE_EXIT_POLLS) {
163
- cleanup();
164
- return;
165
- }
166
- } else idlePolls = 0;
167
158
  if (newCount !== lastCount) {
168
159
  lastCount = newCount;
169
160
  systray.sendAction({
@@ -184,4 +175,4 @@ async function startTray() {
184
175
 
185
176
  //#endregion
186
177
  export { ensureTray, startTray };
187
- //# sourceMappingURL=tray-BQkynk6r.js.map
178
+ //# sourceMappingURL=tray-CPpdxTV-.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.72.3",
3
+ "version": "1.73.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",
@@ -81,10 +81,12 @@
81
81
  "release": "standard-version && npm publish",
82
82
  "release:beta": "standard-version && npm publish --tag beta",
83
83
  "test": "vitest run",
84
- "test:coverage": "vitest run --coverage"
84
+ "test:coverage": "vitest run --coverage",
85
+ "test:ui": "vitest run --config tests/ui-test/vitest.config.ts"
85
86
  },
86
87
  "dependencies": {
87
88
  "@snomiao/bun-pty": "^0.3.4",
89
+ "@xterm/headless": "^6.0.0",
88
90
  "bun-pty": "^0.4.8",
89
91
  "execa": "^9.6.1",
90
92
  "from-node-stream": "^0.2.0",
@@ -92,12 +94,12 @@
92
94
  "phpdie": "^1.7.0",
93
95
  "proper-lockfile": "^4.1.2",
94
96
  "sflow": "^1.27.0",
95
- "terminal-render": "^1.5.1",
96
97
  "winston": "^3.19.0",
97
98
  "yaml": "^2.8.2",
98
99
  "yargs": "^18.0.0"
99
100
  },
100
101
  "devDependencies": {
102
+ "@google/generative-ai": "^0.24.1",
101
103
  "@semantic-release/exec": "^7.1.0",
102
104
  "@semantic-release/git": "^10.0.1",
103
105
  "@types/bun": "^1.3.6",
@@ -105,6 +107,7 @@
105
107
  "@types/ms": "^2.1.0",
106
108
  "@types/node": "^25.0.10",
107
109
  "@types/proper-lockfile": "^4.1.4",
110
+ "@types/ws": "^8.18.1",
108
111
  "@types/yargs": "^17.0.35",
109
112
  "@typescript/native-preview": "^7.0.0-dev.20260124.1",
110
113
  "@vitest/coverage-v8": "4.1.0",
@@ -114,11 +117,13 @@
114
117
  "oxfmt": "^0.26.0",
115
118
  "oxlint": "^1.41.0",
116
119
  "patch-package": "^8.0.1",
120
+ "playwright": "^1.58.2",
117
121
  "rambda": "^11.0.1",
118
122
  "semantic-release": "^25.0.2",
119
123
  "standard-version": "^9.5.0",
120
124
  "tsdown": "^0.20.3",
121
125
  "vitest": "4.1.0",
126
+ "ws": "^8.20.0",
122
127
  "zod": "^3.23.0"
123
128
  },
124
129
  "peerDependencies": {