agent-yes 1.44.11 → 1.45.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.
@@ -1,19 +1,19 @@
1
1
  import { a as __esmMin, c as __toESM, i as __commonJSMin, n as require_winston, o as __exportAll, s as __require, t as logger } from "./logger-DH1Rx9HI.js";
2
2
  import { arch, platform } from "process";
3
+ import { execSync } from "child_process";
4
+ import { existsSync } from "fs";
5
+ import path, { dirname, join } from "path";
3
6
  import { constants } from "node:os";
4
- import path from "node:path";
7
+ import path$1 from "node:path";
5
8
  import { homedir } from "os";
6
- import { existsSync } from "fs";
7
- import path$1, { dirname, join } from "path";
8
9
  import { fileURLToPath } from "url";
9
10
  import { appendFileSync, createReadStream, createWriteStream, readFileSync as readFileSync$1, statSync as statSync$1, writeFileSync } from "node:fs";
10
11
  import { fileURLToPath as fileURLToPath$1 } from "node:url";
11
- import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
12
+ import { ChildProcess, execFile, spawn as spawn$1, spawnSync } from "node:child_process";
12
13
  import { StringDecoder } from "node:string_decoder";
13
14
  import { aborted, callbackify, debuglog, inspect, promisify, stripVTControlCharacters } from "node:util";
14
15
  import process$1, { execArgv, execPath, hrtime, platform as platform$1 } from "node:process";
15
16
  import tty from "node:tty";
16
- import { execSync } from "child_process";
17
17
  import { scheduler, setImmediate, setTimeout as setTimeout$1 } from "node:timers/promises";
18
18
  import { EventEmitter, addAbortListener, on, once, setMaxListeners } from "node:events";
19
19
  import { serialize } from "node:v8";
@@ -1232,12 +1232,12 @@ function toPath(urlOrPath) {
1232
1232
  }
1233
1233
  function traversePathUp(startPath) {
1234
1234
  return { *[Symbol.iterator]() {
1235
- let currentPath = path.resolve(toPath(startPath));
1235
+ let currentPath = path$1.resolve(toPath(startPath));
1236
1236
  let previousPath;
1237
1237
  while (previousPath !== currentPath) {
1238
1238
  yield currentPath;
1239
1239
  previousPath = currentPath;
1240
- currentPath = path.resolve(currentPath, "..");
1240
+ currentPath = path$1.resolve(currentPath, "..");
1241
1241
  }
1242
1242
  } };
1243
1243
  }
@@ -1246,21 +1246,21 @@ const TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
1246
1246
  //#endregion
1247
1247
  //#region node_modules/npm-run-path/index.js
1248
1248
  const npmRunPath = ({ cwd = process$1.cwd(), path: pathOption = process$1.env[pathKey()], preferLocal = true, execPath = process$1.execPath, addExecPath = true } = {}) => {
1249
- const cwdPath = path.resolve(toPath(cwd));
1249
+ const cwdPath = path$1.resolve(toPath(cwd));
1250
1250
  const result = [];
1251
- const pathParts = pathOption.split(path.delimiter);
1251
+ const pathParts = pathOption.split(path$1.delimiter);
1252
1252
  if (preferLocal) applyPreferLocal(result, pathParts, cwdPath);
1253
1253
  if (addExecPath) applyExecPath(result, pathParts, execPath, cwdPath);
1254
- return pathOption === "" || pathOption === path.delimiter ? `${result.join(path.delimiter)}${pathOption}` : [...result, pathOption].join(path.delimiter);
1254
+ return pathOption === "" || pathOption === path$1.delimiter ? `${result.join(path$1.delimiter)}${pathOption}` : [...result, pathOption].join(path$1.delimiter);
1255
1255
  };
1256
1256
  const applyPreferLocal = (result, pathParts, cwdPath) => {
1257
1257
  for (const directory of traversePathUp(cwdPath)) {
1258
- const pathPart = path.join(directory, "node_modules/.bin");
1258
+ const pathPart = path$1.join(directory, "node_modules/.bin");
1259
1259
  if (!pathParts.includes(pathPart)) result.push(pathPart);
1260
1260
  }
1261
1261
  };
1262
1262
  const applyExecPath = (result, pathParts, execPath, cwdPath) => {
1263
- const pathPart = path.resolve(cwdPath, toPath(execPath), "..");
1263
+ const pathPart = path$1.resolve(cwdPath, toPath(execPath), "..");
1264
1264
  if (!pathParts.includes(pathPart)) result.push(pathPart);
1265
1265
  };
1266
1266
  const npmRunPathEnv = ({ env = process$1.env, ...options } = {}) => {
@@ -2295,7 +2295,7 @@ const mapNode = ({ options }) => {
2295
2295
  const handleNodeOption = (file, commandArguments, { node: shouldHandleNode = false, nodePath = execPath, nodeOptions = execArgv.filter((nodeOption) => !nodeOption.startsWith("--inspect")), cwd, execPath: formerNodePath, ...options }) => {
2296
2296
  if (formerNodePath !== void 0) throw new TypeError("The \"execPath\" option has been removed. Please use the \"nodePath\" option instead.");
2297
2297
  const normalizedNodePath = safeNormalizeFileUrl(nodePath, "The \"nodePath\" option");
2298
- const resolvedNodePath = path.resolve(cwd, normalizedNodePath);
2298
+ const resolvedNodePath = path$1.resolve(cwd, normalizedNodePath);
2299
2299
  const newOptions = {
2300
2300
  ...options,
2301
2301
  nodePath: resolvedNodePath,
@@ -2307,7 +2307,7 @@ const handleNodeOption = (file, commandArguments, { node: shouldHandleNode = fal
2307
2307
  commandArguments,
2308
2308
  newOptions
2309
2309
  ];
2310
- if (path.basename(file, ".exe") === "node") throw new TypeError("When the \"node\" option is true, the first argument does not need to be \"node\".");
2310
+ if (path$1.basename(file, ".exe") === "node") throw new TypeError("When the \"node\" option is true, the first argument does not need to be \"node\".");
2311
2311
  return [
2312
2312
  resolvedNodePath,
2313
2313
  [
@@ -2394,7 +2394,7 @@ const serializeEncoding = (encoding) => typeof encoding === "string" ? `"${encod
2394
2394
  //#region node_modules/execa/lib/arguments/cwd.js
2395
2395
  const normalizeCwd = (cwd = getDefaultCwd()) => {
2396
2396
  const cwdString = safeNormalizeFileUrl(cwd, "The \"cwd\" option");
2397
- return path.resolve(cwdString);
2397
+ return path$1.resolve(cwdString);
2398
2398
  };
2399
2399
  const getDefaultCwd = () => {
2400
2400
  try {
@@ -2434,7 +2434,7 @@ const normalizeOptions = (filePath, rawArguments, rawOptions) => {
2434
2434
  options.killSignal = normalizeKillSignal(options.killSignal);
2435
2435
  options.forceKillAfterDelay = normalizeForceKillAfterDelay(options.forceKillAfterDelay);
2436
2436
  options.lines = options.lines.map((lines, fdNumber) => lines && !BINARY_ENCODINGS.has(options.encoding) && options.buffer[fdNumber]);
2437
- if (process$1.platform === "win32" && path.basename(file, ".exe") === "cmd") commandArguments.unshift("/q");
2437
+ if (process$1.platform === "win32" && path$1.basename(file, ".exe") === "cmd") commandArguments.unshift("/q");
2438
2438
  return {
2439
2439
  file,
2440
2440
  commandArguments,
@@ -6127,7 +6127,7 @@ const handleAsyncOptions = ({ timeout, signal, ...options }) => {
6127
6127
  const spawnSubprocessAsync = ({ file, commandArguments, options, startTime, verboseInfo, command, escapedCommand, fileDescriptors }) => {
6128
6128
  let subprocess;
6129
6129
  try {
6130
- subprocess = spawn(...concatenateShell(file, commandArguments, options));
6130
+ subprocess = spawn$1(...concatenateShell(file, commandArguments, options));
6131
6131
  } catch (error) {
6132
6132
  return handleEarlyError({
6133
6133
  error,
@@ -8929,8 +8929,8 @@ var TerminalTextRender = class {
8929
8929
 
8930
8930
  //#endregion
8931
8931
  //#region ts/resume/codexSessionManager.ts
8932
- const getSessionsFile = () => process.env.CLI_YES_TEST_HOME ? path$1.join(process.env.CLI_YES_TEST_HOME, ".config", "agent-yes", "codex-sessions.json") : path$1.join(homedir(), ".config", "agent-yes", "codex-sessions.json");
8933
- const getCodexSessionsDir = () => process.env.CLI_YES_TEST_HOME ? path$1.join(process.env.CLI_YES_TEST_HOME, ".codex", "sessions") : path$1.join(homedir(), ".codex", "sessions");
8932
+ 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");
8933
+ const getCodexSessionsDir = () => process.env.CLI_YES_TEST_HOME ? path.join(process.env.CLI_YES_TEST_HOME, ".codex", "sessions") : path.join(homedir(), ".codex", "sessions");
8934
8934
  /**
8935
8935
  * Load the session map from the config file
8936
8936
  */
@@ -8948,7 +8948,7 @@ async function loadSessionMap() {
8948
8948
  async function saveSessionMap(sessionMap) {
8949
8949
  try {
8950
8950
  const sessionsFile = getSessionsFile();
8951
- await mkdir(path$1.dirname(sessionsFile), { recursive: true });
8951
+ await mkdir(path.dirname(sessionsFile), { recursive: true });
8952
8952
  await writeFile$1(sessionsFile, JSON.stringify(sessionMap, null, 2));
8953
8953
  } catch (error) {
8954
8954
  console.warn("Failed to save codex session map:", error);
@@ -8999,19 +8999,19 @@ async function getAllCodexSessions() {
8999
8999
  try {
9000
9000
  const years = await readdir(codexSessionsDir);
9001
9001
  for (const year of years) {
9002
- const yearPath = path$1.join(codexSessionsDir, year);
9002
+ const yearPath = path.join(codexSessionsDir, year);
9003
9003
  try {
9004
9004
  const months = await readdir(yearPath);
9005
9005
  for (const month of months) {
9006
- const monthPath = path$1.join(yearPath, month);
9006
+ const monthPath = path.join(yearPath, month);
9007
9007
  try {
9008
9008
  const days = await readdir(monthPath);
9009
9009
  for (const day of days) {
9010
- const dayPath = path$1.join(monthPath, day);
9010
+ const dayPath = path.join(monthPath, day);
9011
9011
  try {
9012
9012
  const files = await readdir(dayPath);
9013
9013
  for (const file of files) if (file.endsWith(".jsonl")) {
9014
- const session = await parseCodexSessionFile(path$1.join(dayPath, file));
9014
+ const session = await parseCodexSessionFile(path.join(dayPath, file));
9015
9015
  if (session) sessions.push(session);
9016
9016
  }
9017
9017
  } catch {}
@@ -9071,8 +9071,8 @@ function removeControlCharacters(str) {
9071
9071
 
9072
9072
  //#endregion
9073
9073
  //#region ts/runningLock.ts
9074
- const getLockDir = () => path$1.join(process.env.CLAUDE_YES_HOME || homedir(), ".claude-yes");
9075
- const getLockFile = () => path$1.join(getLockDir(), "running.lock.json");
9074
+ const getLockDir = () => path.join(process.env.CLAUDE_YES_HOME || homedir(), ".claude-yes");
9075
+ const getLockFile = () => path.join(getLockDir(), "running.lock.json");
9076
9076
  const MAX_RETRIES = 5;
9077
9077
  const RETRY_DELAYS = [
9078
9078
  50,
@@ -9126,7 +9126,7 @@ function isGitRepo(cwd) {
9126
9126
  */
9127
9127
  function resolveRealPath(p) {
9128
9128
  try {
9129
- return path$1.resolve(p);
9129
+ return path.resolve(p);
9130
9130
  } catch {
9131
9131
  return p;
9132
9132
  }
@@ -9321,7 +9321,7 @@ var SqliteAdapter = class {
9321
9321
  isInitialized = false;
9322
9322
  async init(dbPath) {
9323
9323
  try {
9324
- await mkdir(path$1.dirname(dbPath), { recursive: true });
9324
+ await mkdir(path.dirname(dbPath), { recursive: true });
9325
9325
  if (typeof globalThis.Bun !== "undefined") {
9326
9326
  const { Database } = await import("bun:sqlite");
9327
9327
  this.db = new Database(dbPath);
@@ -9404,8 +9404,8 @@ var PidStore = class PidStore {
9404
9404
  storeDir;
9405
9405
  dbPath;
9406
9406
  constructor(workingDir) {
9407
- this.storeDir = path$1.resolve(workingDir, ".agent-yes");
9408
- this.dbPath = path$1.join(this.storeDir, "pid.sqlite");
9407
+ this.storeDir = path.resolve(workingDir, ".agent-yes");
9408
+ this.dbPath = path.join(this.storeDir, "pid.sqlite");
9409
9409
  }
9410
9410
  async init() {
9411
9411
  try {
@@ -9451,7 +9451,7 @@ var PidStore = class PidStore {
9451
9451
  async registerProcess({ pid, cli, args, prompt, cwd }) {
9452
9452
  const now = Date.now();
9453
9453
  const argsJson = JSON.stringify(args);
9454
- const logFile = path$1.resolve(this.getLogDir(), `${pid}.log`);
9454
+ const logFile = path.resolve(this.getLogDir(), `${pid}.log`);
9455
9455
  const fifoFile = this.getFifoPath(pid);
9456
9456
  try {
9457
9457
  this.db.run(`
@@ -9518,11 +9518,11 @@ var PidStore = class PidStore {
9518
9518
  return this.db.query("SELECT * FROM pid_records");
9519
9519
  }
9520
9520
  getLogDir() {
9521
- return path$1.resolve(this.storeDir, "logs");
9521
+ return path.resolve(this.storeDir, "logs");
9522
9522
  }
9523
9523
  getFifoPath(pid) {
9524
9524
  if (process.platform === "win32") return `\\\\.\\pipe\\agent-yes-${pid}`;
9525
- else return path$1.resolve(this.storeDir, "fifo", `${pid}.stdin`);
9525
+ else return path.resolve(this.storeDir, "fifo", `${pid}.stdin`);
9526
9526
  }
9527
9527
  async cleanStaleRecords() {
9528
9528
  const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
@@ -9550,7 +9550,7 @@ var PidStore = class PidStore {
9550
9550
  }
9551
9551
  }
9552
9552
  async ensureGitignore() {
9553
- const gitignorePath = path$1.join(this.storeDir, ".gitignore");
9553
+ const gitignorePath = path.join(this.storeDir, ".gitignore");
9554
9554
  const gitignoreContent = `# Auto-generated .gitignore for agent-yes
9555
9555
  # Ignore all log files and runtime data
9556
9556
  logs/
@@ -9694,9 +9694,9 @@ async function initializeLogPaths(pidStore, pid) {
9694
9694
  await mkdir(logDir, { recursive: true });
9695
9695
  return {
9696
9696
  logPath: logDir,
9697
- rawLogPath: path$1.resolve(path$1.dirname(logDir), `${pid}.raw.log`),
9698
- rawLinesLogPath: path$1.resolve(path$1.dirname(logDir), `${pid}.lines.log`),
9699
- debuggingLogsPath: path$1.resolve(path$1.dirname(logDir), `${pid}.debug.log`)
9697
+ rawLogPath: path.resolve(path.dirname(logDir), `${pid}.raw.log`),
9698
+ rawLinesLogPath: path.resolve(path.dirname(logDir), `${pid}.lines.log`),
9699
+ debuggingLogsPath: path.resolve(path.dirname(logDir), `${pid}.debug.log`)
9700
9700
  };
9701
9701
  }
9702
9702
  /**
@@ -9716,7 +9716,7 @@ function setupDebugLogging(debuggingLogsPath) {
9716
9716
  */
9717
9717
  async function saveLogFile(logPath, content) {
9718
9718
  if (!logPath) return;
9719
- await mkdir(path$1.dirname(logPath), { recursive: true }).catch(() => null);
9719
+ await mkdir(path.dirname(logPath), { recursive: true }).catch(() => null);
9720
9720
  await writeFile$1(logPath, content).catch(() => null);
9721
9721
  logger.info(`Full logs saved to ${logPath}`);
9722
9722
  }
@@ -9729,8 +9729,8 @@ async function saveLogFile(logPath, content) {
9729
9729
  async function saveDeprecatedLogFile(logFile, content, verbose) {
9730
9730
  if (!logFile) return;
9731
9731
  if (verbose) logger.info(`Writing rendered logs to ${logFile}`);
9732
- const logFilePath = path$1.resolve(logFile);
9733
- await mkdir(path$1.dirname(logFilePath), { recursive: true }).catch(() => null);
9732
+ const logFilePath = path.resolve(logFile);
9733
+ await mkdir(path.dirname(logFilePath), { recursive: true }).catch(() => null);
9734
9734
  await writeFile$1(logFilePath, content);
9735
9735
  }
9736
9736
 
@@ -10001,6 +10001,7 @@ var AgentContext = class {
10001
10001
  idleWaiter = new IdleWaiter();
10002
10002
  isFatal = false;
10003
10003
  shouldRestartWithoutContinue = false;
10004
+ autoYesEnabled = true;
10004
10005
  constructor(params) {
10005
10006
  this.shell = params.shell;
10006
10007
  this.pidStore = params.pidStore;
@@ -10009,6 +10010,7 @@ var AgentContext = class {
10009
10010
  this.cliConf = params.cliConf;
10010
10011
  this.verbose = params.verbose;
10011
10012
  this.robust = params.robust;
10013
+ this.autoYesEnabled = params.autoYes ?? true;
10012
10014
  }
10013
10015
  /**
10014
10016
  * Get message context for sendMessage/sendEnter helpers
@@ -10116,7 +10118,7 @@ const globalAgentRegistry = new AgentRegistry();
10116
10118
 
10117
10119
  //#endregion
10118
10120
  //#region ts/index.ts
10119
- const config = await import("./agent-yes.config-DPkGH_ef.js").then((mod) => mod.default || mod);
10121
+ const config = await import("./agent-yes.config-DQLH9OgO.js").then((mod) => mod.default || mod);
10120
10122
  const CLIS_CONFIG = config.clis;
10121
10123
  /**
10122
10124
  * Main function to run agent-cli with automatic yes/no responses
@@ -10146,7 +10148,7 @@ const CLIS_CONFIG = config.clis;
10146
10148
  * });
10147
10149
  * ```
10148
10150
  */
10149
- 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 }) {
10151
+ 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 }) {
10150
10152
  if (!cli) throw new Error(`cli is required`);
10151
10153
  const conf = CLIS_CONFIG[cli] || phpdie_default(`Unsupported cli tool: ${cli}, current process.argv: ${process.argv.join(" ")}`);
10152
10154
  const workingDir = cwd ?? process.cwd();
@@ -10169,18 +10171,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10169
10171
  }
10170
10172
  const pidStore = new PidStore(workingDir);
10171
10173
  await pidStore.init();
10172
- const stdinReady = new ReadyManager();
10173
- const stdinFirstReady = new ReadyManager();
10174
10174
  let userSentCtrlC = false;
10175
- if (conf.ready && conf.ready.length === 0) {
10176
- stdinReady.ready();
10177
- stdinFirstReady.ready();
10178
- }
10179
- sleep(1e4).then(() => {
10180
- if (!stdinReady.isReady) stdinReady.ready();
10181
- if (!stdinFirstReady.isReady) stdinFirstReady.ready();
10182
- });
10183
- new ReadyManager();
10184
10175
  if (verbose) logger.debug(`[stdin] isTTY: ${process.stdin.isTTY}, setRawMode available: ${!!process.stdin.setRawMode}`);
10185
10176
  process.stdin.setRawMode?.(true);
10186
10177
  if (verbose) logger.debug(`[stdin] Raw mode set, isRaw: ${process.stdin.isRaw}`);
@@ -10203,9 +10194,9 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10203
10194
  } catch {}
10204
10195
  const skillHeaders = [];
10205
10196
  let currentDir = workingDir;
10206
- const searchLimit = gitRoot || path$1.parse(currentDir).root;
10197
+ const searchLimit = gitRoot || path.parse(currentDir).root;
10207
10198
  while (true) {
10208
- const md = await readFile(path$1.resolve(currentDir, "SKILL.md"), "utf8").catch(() => null);
10199
+ const md = await readFile(path.resolve(currentDir, "SKILL.md"), "utf8").catch(() => null);
10209
10200
  if (md) {
10210
10201
  const headerMatch = md.match(/^[\s\S]*?(?=\n##\s)/);
10211
10202
  const headerRaw = (headerMatch ? headerMatch[0] : md).trim();
@@ -10215,7 +10206,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10215
10206
  }
10216
10207
  }
10217
10208
  if (currentDir === searchLimit) break;
10218
- const parentDir = path$1.dirname(currentDir);
10209
+ const parentDir = path.dirname(currentDir);
10219
10210
  if (parentDir === currentDir) break;
10220
10211
  currentDir = parentDir;
10221
10212
  }
@@ -10297,7 +10288,8 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10297
10288
  cli,
10298
10289
  cliConf,
10299
10290
  verbose,
10300
- robust
10291
+ robust,
10292
+ autoYes
10301
10293
  });
10302
10294
  try {
10303
10295
  globalAgentRegistry.register(shell.pid, {
@@ -10312,6 +10304,11 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10312
10304
  } catch (error) {
10313
10305
  logger.warn(`[agentRegistry] Failed to register agent ${shell.pid}:`, error);
10314
10306
  }
10307
+ if (!ctx.autoYesEnabled) process.stderr.write("\x1B[33m[auto-yes: OFF]\x1B[0m Press Ctrl+Y to toggle\n");
10308
+ if (cliConf.ready && cliConf.ready.length === 0 || !ctx.autoYesEnabled) {
10309
+ ctx.stdinReady.ready();
10310
+ ctx.stdinFirstReady.ready();
10311
+ }
10315
10312
  sleep(1e4).then(() => {
10316
10313
  if (!ctx.stdinReady.isReady) ctx.stdinReady.ready();
10317
10314
  if (!ctx.stdinFirstReady.isReady) ctx.stdinFirstReady.ready();
@@ -10375,6 +10372,10 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10375
10372
  }
10376
10373
  shell.onData(onData);
10377
10374
  shell.onExit(onExit);
10375
+ if (cliConf.ready && cliConf.ready.length === 0 || !ctx.autoYesEnabled) {
10376
+ ctx.stdinReady.ready();
10377
+ ctx.stdinFirstReady.ready();
10378
+ }
10378
10379
  return;
10379
10380
  }
10380
10381
  if (agentCrashed && robust && conf?.restoreArgs) {
@@ -10444,6 +10445,10 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10444
10445
  }
10445
10446
  shell.onData(onData);
10446
10447
  shell.onExit(onExit);
10448
+ if (cliConf.ready && cliConf.ready.length === 0 || !ctx.autoYesEnabled) {
10449
+ ctx.stdinReady.ready();
10450
+ ctx.stdinFirstReady.ready();
10451
+ }
10447
10452
  return;
10448
10453
  }
10449
10454
  const exitReason = agentCrashed ? "crash" : "normal";
@@ -10577,7 +10582,48 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10577
10582
  }, 2e3);
10578
10583
  }
10579
10584
  return str;
10580
- }).onStart(async function promptOnStart() {
10585
+ }).map((() => {
10586
+ let line = "";
10587
+ const toggleAutoYes = () => {
10588
+ ctx.autoYesEnabled = !ctx.autoYesEnabled;
10589
+ if (!ctx.autoYesEnabled) {
10590
+ ctx.stdinReady.ready();
10591
+ ctx.stdinFirstReady.ready();
10592
+ }
10593
+ const status = ctx.autoYesEnabled ? "\x1B[32m[auto-yes: ON]\x1B[0m" : "\x1B[33m[auto-yes: OFF]\x1B[0m";
10594
+ process.stderr.write(`\r${status} (Ctrl+Y to toggle)\n`);
10595
+ };
10596
+ return (data) => {
10597
+ let out = "";
10598
+ for (const ch of data) {
10599
+ if (ch === "") {
10600
+ toggleAutoYes();
10601
+ continue;
10602
+ }
10603
+ if (ch === "\r" || ch === "\n") {
10604
+ if (line.length <= 20) {
10605
+ if (line.replace(/[\x00-\x1f]|\x1b\[[0-9;]*[A-Za-z]|\[[A-Z]/g, "").trim() === "/auto") {
10606
+ out += "";
10607
+ toggleAutoYes();
10608
+ line = "";
10609
+ continue;
10610
+ }
10611
+ }
10612
+ line = "";
10613
+ out += ch;
10614
+ continue;
10615
+ }
10616
+ if (ch === "" || ch === "\b") {
10617
+ if (line.length > 0) line = line.slice(0, -1);
10618
+ out += ch;
10619
+ continue;
10620
+ }
10621
+ if (ch >= " " && ch <= "~" && line.length < 50) line += ch;
10622
+ out += ch;
10623
+ }
10624
+ return out;
10625
+ };
10626
+ })()).onStart(async function promptOnStart() {
10581
10627
  logger.debug("Sending prompt message: " + JSON.stringify(prompt));
10582
10628
  if (prompt) await sendMessage(ctx.messageContext, prompt);
10583
10629
  }).by({
@@ -10593,7 +10639,7 @@ async function agentYes({ cli, cliArgs = [], prompt, robust = true, cwd, env, ex
10593
10639
  }).forkTo(async function rawLogger(f) {
10594
10640
  const rawLogPath = ctx.logPaths.rawLogPath;
10595
10641
  if (!rawLogPath) return f.run();
10596
- return await mkdir(path$1.dirname(rawLogPath), { recursive: true }).then(() => {
10642
+ return await mkdir(path.dirname(rawLogPath), { recursive: true }).then(() => {
10597
10643
  logger.debug(`[${cli}-yes] raw logs streaming to ${rawLogPath}`);
10598
10644
  return f.forEach(async (chars) => {
10599
10645
  await writeFile$1(rawLogPath, chars, { flag: "a" }).catch(() => null);
@@ -10689,4 +10735,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
10689
10735
 
10690
10736
  //#endregion
10691
10737
  export { AgentContext as a, config as i, CLIS_CONFIG as n, PidStore as o, agentYes as r, removeControlCharacters as s, SUPPORTED_CLIS as t };
10692
- //# sourceMappingURL=SUPPORTED_CLIS-4H-pxjDu.js.map
10738
+ //# sourceMappingURL=SUPPORTED_CLIS-hj4ASwQy.js.map
@@ -60,12 +60,10 @@ function getDefaultConfig() {
60
60
  typingRespond: { "1\n": [/│ Do you want to use this API key\?/] },
61
61
  enter: [
62
62
  / > 1. Yes, I trust this folder/m,
63
- /❯ 1\. Dark mode ?✔/m,
64
- /❯ 1\. Yes, continue/m,
65
- /❯ 1\. Yes/m,
66
- /^.{0,4} 1\. Dark mode ?✔/m,
67
- /^.{0,4} 1\. Yes, continue/m,
68
- /^.{0,4} 1\. Yes/m,
63
+ /❯ ?1\. ?Dark mode ?✔/m,
64
+ /❯ ?1\. ?Yes/m,
65
+ /^.{0,4} ?1\. ?Dark mode ?✔/m,
66
+ /^.{0,4} ?1\. ?Yes/m,
69
67
  /Press Enter to continue…/m
70
68
  ],
71
69
  fatal: [/⎿ Claude usage limit reached\./, /^error: unknown option/],
@@ -159,4 +157,4 @@ function getDefaultConfig() {
159
157
 
160
158
  //#endregion
161
159
  export { agent_yes_config_default as t };
162
- //# sourceMappingURL=agent-yes.config-Dn48YQ7f.js.map
160
+ //# sourceMappingURL=agent-yes.config-CrlZMQo-.js.map
@@ -1,4 +1,4 @@
1
1
  import "./logger-DH1Rx9HI.js";
2
- import { t as agent_yes_config_default } from "./agent-yes.config-Dn48YQ7f.js";
2
+ import { t as agent_yes_config_default } from "./agent-yes.config-CrlZMQo-.js";
3
3
 
4
4
  export { agent_yes_config_default as default };
package/dist/cli.js CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env bun
2
2
  import { c as __toESM, i as __commonJSMin, r as require_ms, t as logger } from "./logger-DH1Rx9HI.js";
3
- import "./agent-yes.config-Dn48YQ7f.js";
4
- import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-4H-pxjDu.js";
3
+ import "./agent-yes.config-CrlZMQo-.js";
4
+ import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-hj4ASwQy.js";
5
5
  import { createRequire } from "node:module";
6
6
  import { argv } from "process";
7
+ import { spawn } from "child_process";
8
+ import { existsSync, readFileSync, readdirSync, statSync, writeFile } from "fs";
9
+ import path, { basename, dirname, extname, join, normalize, relative, resolve } from "path";
7
10
  import { format, inspect } from "util";
8
- import { readFileSync, readdirSync, statSync, writeFile } from "fs";
9
- import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
10
11
  import { notStrictEqual, strictEqual } from "assert";
11
12
  import { fileURLToPath } from "url";
12
13
  import { readFileSync as readFileSync$1, readdirSync as readdirSync$1 } from "node:fs";
@@ -4512,7 +4513,7 @@ var version = "1.31.41";
4512
4513
  * This is a test helper that mirrors the parsing logic in cli.ts
4513
4514
  */
4514
4515
  function parseCliArgs(argv) {
4515
- const cliName = argv[1]?.split(/[/\\]/).at(-1)?.replace(/(\.[jt]s)?$/, "").replace(/^(cli|agent)(-yes$)?/, "").replace(/^ay$/, "").replace(/-yes$/, "") || void 0;
4516
+ const cliName = (argv[1]?.split(/[/\\]/).at(-1)?.replace(/(\.[jt]s)?$/, "") || "").replace(/^(cli|agent)(-yes)?$/, "").replace(/^ay$/, "").replace(/-yes$/, "") || void 0;
4516
4517
  const parsedArgv = Yargs(hideBin(argv)).usage("Usage: $0 [cli] [agent-yes args] [agent-cli args] [--] [prompts...]").example("$0 claude --timeout=30s -- solve all todos in my codebase, commit one by one", "Run Claude with a 30 seconds idle timeout (will type /exit when timeout), everything after `--` will be treated as the prompt").example("$0 claude --stdpush", "Run Claude with external stdin input enabled via --append-prompt").option("robust", {
4517
4518
  type: "boolean",
4518
4519
  default: true,
@@ -4569,6 +4570,15 @@ function parseCliArgs(argv) {
4569
4570
  description: "Enable external input stream to push additional data to stdin",
4570
4571
  default: false,
4571
4572
  alias: ["ipc", "fifo"]
4573
+ }).option("auto", {
4574
+ type: "string",
4575
+ description: "Control auto-yes mode: 'yes' to auto-approve prompts (default), 'no' to start in manual mode. Press Ctrl+Y during the session to toggle at any time.",
4576
+ choices: ["yes", "no"],
4577
+ default: "yes"
4578
+ }).option("rust", {
4579
+ type: "boolean",
4580
+ description: "Use the Rust implementation instead of TypeScript",
4581
+ default: false
4572
4582
  }).positional("cli", {
4573
4583
  describe: "The AI CLI to run, e.g., claude, codex, copilot, cursor, gemini",
4574
4584
  type: "string",
@@ -4636,7 +4646,9 @@ function parseCliArgs(argv) {
4636
4646
  useSkills: parsedArgv.useSkills,
4637
4647
  appendPrompt: parsedArgv.appendPrompt,
4638
4648
  useStdinAppend: Boolean(parsedArgv.stdpush || parsedArgv.ipc || parsedArgv.fifo),
4639
- showVersion: parsedArgv.version
4649
+ showVersion: parsedArgv.version,
4650
+ autoYes: parsedArgv.auto !== "no",
4651
+ useRust: parsedArgv.rust
4640
4652
  };
4641
4653
  }
4642
4654
 
@@ -4686,6 +4698,49 @@ async function displayVersion() {
4686
4698
  //#endregion
4687
4699
  //#region ts/cli.ts
4688
4700
  const config = parseCliArgs(process.argv);
4701
+ if (config.useRust) {
4702
+ const rustBinaryName = config.cli ? `${config.cli}-yes` : "agent-yes";
4703
+ const rustBinaryPaths = [
4704
+ path.resolve(import.meta.dir, "../rs/target/release/agent-yes"),
4705
+ path.resolve(import.meta.dir, "../rs/target/debug/agent-yes"),
4706
+ rustBinaryName,
4707
+ "agent-yes"
4708
+ ];
4709
+ let rustBinary;
4710
+ for (const p of rustBinaryPaths) if (p.includes("/") && existsSync(p)) {
4711
+ rustBinary = p;
4712
+ break;
4713
+ } else if (!p.includes("/")) {
4714
+ rustBinary = p;
4715
+ break;
4716
+ }
4717
+ if (!rustBinary) {
4718
+ console.error("Rust binary not found. Please build with: cd rs && cargo build --release");
4719
+ process.exit(1);
4720
+ }
4721
+ const rustArgs = process.argv.slice(2).filter((arg) => arg !== "--rust" && !arg.startsWith("--rust="));
4722
+ if (config.verbose) {
4723
+ console.log(`[rust] Using binary: ${rustBinary}`);
4724
+ console.log(`[rust] Args: ${rustArgs.join(" ")}`);
4725
+ }
4726
+ const child = spawn(rustBinary, rustArgs, {
4727
+ stdio: "inherit",
4728
+ env: process.env,
4729
+ cwd: process.cwd()
4730
+ });
4731
+ child.on("error", (err) => {
4732
+ if (err.code === "ENOENT") console.error(`Rust binary '${rustBinary}' not found in PATH. Please build with: cd rs && cargo build --release`);
4733
+ else console.error(`Failed to spawn Rust binary: ${err.message}`);
4734
+ process.exit(1);
4735
+ });
4736
+ child.on("exit", (code, signal) => {
4737
+ if (signal) process.exit(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
4738
+ process.exit(code ?? 1);
4739
+ });
4740
+ process.on("SIGINT", () => child.kill("SIGINT"));
4741
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
4742
+ await new Promise(() => {});
4743
+ }
4689
4744
  if (config.showVersion) {
4690
4745
  await displayVersion();
4691
4746
  process.exit(0);
@@ -4739,7 +4794,10 @@ if (config.verbose) {
4739
4794
  console.log(argv);
4740
4795
  }
4741
4796
  const { default: cliYes } = await import("./index.js");
4742
- const { exitCode } = await cliYes(config);
4797
+ const { exitCode } = await cliYes({
4798
+ ...config,
4799
+ autoYes: config.autoYes
4800
+ });
4743
4801
  console.log("exiting process");
4744
4802
  process.exit(exitCode ?? 1);
4745
4803
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./logger-DH1Rx9HI.js";
2
- import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-4H-pxjDu.js";
2
+ import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-hj4ASwQy.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.44.11",
3
+ "version": "1.45.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
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  import { argv } from "process";
3
+ import { spawn } from "child_process";
4
+ import { existsSync } from "fs";
5
+ import path from "path";
3
6
  import cliYesConfig from "../agent-yes.config.ts";
4
7
  import { parseCliArgs } from "./parseCliArgs.ts";
5
8
  import { logger } from "./logger.ts";
@@ -9,6 +12,74 @@ import { displayVersion } from "./versionChecker.ts";
9
12
  // Parse CLI arguments
10
13
  const config = parseCliArgs(process.argv);
11
14
 
15
+ // Handle --rust: spawn the Rust binary instead
16
+ if (config.useRust) {
17
+ const rustBinaryName = config.cli ? `${config.cli}-yes` : "agent-yes";
18
+ const rustBinaryPaths = [
19
+ // Check relative to this script (in the repo)
20
+ path.resolve(import.meta.dir, "../rs/target/release/agent-yes"),
21
+ path.resolve(import.meta.dir, "../rs/target/debug/agent-yes"),
22
+ // Check in PATH
23
+ rustBinaryName,
24
+ "agent-yes",
25
+ ];
26
+
27
+ let rustBinary: string | undefined;
28
+ for (const p of rustBinaryPaths) {
29
+ if (p.includes("/") && existsSync(p)) {
30
+ rustBinary = p;
31
+ break;
32
+ } else if (!p.includes("/")) {
33
+ // For PATH lookup, just use it directly
34
+ rustBinary = p;
35
+ break;
36
+ }
37
+ }
38
+
39
+ if (!rustBinary) {
40
+ console.error("Rust binary not found. Please build with: cd rs && cargo build --release");
41
+ process.exit(1);
42
+ }
43
+
44
+ // Build args for Rust binary (filter out --rust flag)
45
+ const rustArgs = process.argv.slice(2).filter((arg) => arg !== "--rust" && !arg.startsWith("--rust="));
46
+
47
+ if (config.verbose) {
48
+ console.log(`[rust] Using binary: ${rustBinary}`);
49
+ console.log(`[rust] Args: ${rustArgs.join(" ")}`);
50
+ }
51
+
52
+ // Spawn the Rust process with stdio inheritance
53
+ const child = spawn(rustBinary, rustArgs, {
54
+ stdio: "inherit",
55
+ env: process.env,
56
+ cwd: process.cwd(),
57
+ });
58
+
59
+ child.on("error", (err) => {
60
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
61
+ console.error(`Rust binary '${rustBinary}' not found in PATH. Please build with: cd rs && cargo build --release`);
62
+ } else {
63
+ console.error(`Failed to spawn Rust binary: ${err.message}`);
64
+ }
65
+ process.exit(1);
66
+ });
67
+
68
+ child.on("exit", (code, signal) => {
69
+ if (signal) {
70
+ process.exit(128 + (signal === "SIGINT" ? 2 : signal === "SIGTERM" ? 15 : 1));
71
+ }
72
+ process.exit(code ?? 1);
73
+ });
74
+
75
+ // Forward signals to child
76
+ process.on("SIGINT", () => child.kill("SIGINT"));
77
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
78
+
79
+ // Keep the process alive while child is running
80
+ await new Promise(() => {}); // Never resolves, exits via child.on("exit")
81
+ }
82
+
12
83
  // Handle --version: display version and exit
13
84
  if (config.showVersion) {
14
85
  await displayVersion();
@@ -79,6 +150,6 @@ if (config.verbose) {
79
150
  }
80
151
 
81
152
  const { default: cliYes } = await import("./index.ts");
82
- const { exitCode } = await cliYes(config);
153
+ const { exitCode } = await cliYes({ ...config, autoYes: config.autoYes });
83
154
  console.log("exiting process");
84
155
  process.exit(exitCode ?? 1);
package/ts/index.ts CHANGED
@@ -119,6 +119,7 @@ export default async function agentYes({
119
119
  resume = false,
120
120
  useSkills = false,
121
121
  useStdinAppend = false,
122
+ autoYes = true,
122
123
  }: {
123
124
  cli: SUPPORTED_CLIS;
124
125
  cliArgs?: string[];
@@ -135,6 +136,7 @@ export default async function agentYes({
135
136
  resume?: boolean; // if true, resume previous session in current cwd if any
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
139
+ autoYes?: boolean; // if true, auto-yes is enabled (default), toggle with Ctrl+Y during session
138
140
  }) {
139
141
  if (!cli) throw new Error(`cli is required`);
140
142
  const conf =
@@ -172,24 +174,9 @@ export default async function agentYes({
172
174
  const pidStore = new PidStore(workingDir);
173
175
  await pidStore.init();
174
176
 
175
- const stdinReady = new ReadyManager();
176
- const stdinFirstReady = new ReadyManager(); // if user send ctrl+c before
177
-
178
177
  // Track when user sends Ctrl+C to avoid treating intentional exit as crash
179
178
  let userSentCtrlC = false;
180
179
 
181
- // If ready check is disabled (empty array), mark stdin ready immediately
182
- if (conf.ready && conf.ready.length === 0) {
183
- stdinReady.ready();
184
- stdinFirstReady.ready();
185
- }
186
-
187
- // force ready after 10s to avoid stuck forever if the ready-word mismatched
188
- sleep(10e3).then(() => {
189
- if (!stdinReady.isReady) stdinReady.ready();
190
- if (!stdinFirstReady.isReady) stdinFirstReady.ready();
191
- });
192
- const nextStdout = new ReadyManager();
193
180
  if (verbose) logger.debug(`[stdin] isTTY: ${process.stdin.isTTY}, setRawMode available: ${!!process.stdin.setRawMode}`);
194
181
  process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage
195
182
  if (verbose) logger.debug(`[stdin] Raw mode set, isRaw: ${(process.stdin as any).isRaw}`);
@@ -357,6 +344,7 @@ export default async function agentYes({
357
344
  cliConf,
358
345
  verbose,
359
346
  robust,
347
+ autoYes,
360
348
  });
361
349
 
362
350
  // Register agent in global registry (non-blocking)
@@ -374,6 +362,18 @@ export default async function agentYes({
374
362
  logger.warn(`[agentRegistry] Failed to register agent ${shell.pid}:`, error);
375
363
  }
376
364
 
365
+ // Show startup mode if not default (i.e., when starting in manual mode)
366
+ if (!ctx.autoYesEnabled) {
367
+ process.stderr.write("\x1b[33m[auto-yes: OFF]\x1b[0m Press Ctrl+Y to toggle\n");
368
+ }
369
+
370
+ // If ready check is disabled (empty array) or manual mode, mark stdin ready immediately
371
+ // Manual mode needs immediate stdin so user can respond to trust prompts
372
+ if ((cliConf.ready && cliConf.ready.length === 0) || !ctx.autoYesEnabled) {
373
+ ctx.stdinReady.ready();
374
+ ctx.stdinFirstReady.ready();
375
+ }
376
+
377
377
  // force ready after 10s to avoid stuck forever if the ready-word mismatched
378
378
  sleep(10e3).then(() => {
379
379
  if (!ctx.stdinReady.isReady) ctx.stdinReady.ready();
@@ -455,6 +455,11 @@ export default async function agentYes({
455
455
  }
456
456
  shell.onData(onData);
457
457
  shell.onExit(onExit);
458
+ // Re-mark stdin ready for manual mode after restart
459
+ if ((cliConf.ready && cliConf.ready.length === 0) || !ctx.autoYesEnabled) {
460
+ ctx.stdinReady.ready();
461
+ ctx.stdinFirstReady.ready();
462
+ }
458
463
  return;
459
464
  }
460
465
 
@@ -536,6 +541,11 @@ export default async function agentYes({
536
541
  }
537
542
  shell.onData(onData);
538
543
  shell.onExit(onExit);
544
+ // Re-mark stdin ready for manual mode after restart
545
+ if ((cliConf.ready && cliConf.ready.length === 0) || !ctx.autoYesEnabled) {
546
+ ctx.stdinReady.ready();
547
+ ctx.stdinFirstReady.ready();
548
+ }
539
549
  return;
540
550
  }
541
551
  const exitReason = agentCrashed ? "crash" : "normal";
@@ -743,6 +753,58 @@ export default async function agentYes({
743
753
  return str;
744
754
  })
745
755
 
756
+ // Detect Ctrl+Y or /auto command to toggle auto-yes mode
757
+ .map((() => {
758
+ let line = "";
759
+ const toggleAutoYes = () => {
760
+ ctx.autoYesEnabled = !ctx.autoYesEnabled;
761
+ // When switching to manual mode, mark stdin ready so user keystrokes are not blocked
762
+ if (!ctx.autoYesEnabled) {
763
+ ctx.stdinReady.ready();
764
+ ctx.stdinFirstReady.ready();
765
+ }
766
+ const status = ctx.autoYesEnabled ? "\x1b[32m[auto-yes: ON]\x1b[0m" : "\x1b[33m[auto-yes: OFF]\x1b[0m";
767
+ process.stderr.write(`\r${status} (Ctrl+Y to toggle)\n`);
768
+ };
769
+ return (data: string) => {
770
+ let out = "";
771
+ for (const ch of data) {
772
+ // Ctrl+Y (\x19) toggles auto-yes immediately
773
+ if (ch === "\x19") {
774
+ toggleAutoYes();
775
+ // Do not forward Ctrl+Y to the PTY
776
+ continue;
777
+ }
778
+ // Handle Enter
779
+ if (ch === "\r" || ch === "\n") {
780
+ // Only check for /auto if line is short enough
781
+ if (line.length <= 20) {
782
+ const cleanLine = line.replace(/[\x00-\x1f]|\x1b\[[0-9;]*[A-Za-z]|\[[A-Z]/g, '').trim();
783
+ if (cleanLine === "/auto") {
784
+ out += "\x15"; // Ctrl+U to clear the /auto text from shell input
785
+ toggleAutoYes();
786
+ line = "";
787
+ continue;
788
+ }
789
+ }
790
+ line = "";
791
+ out += ch;
792
+ continue;
793
+ }
794
+ // Handle backspace
795
+ if (ch === "\x7f" || ch === "\b") {
796
+ if (line.length > 0) line = line.slice(0, -1);
797
+ out += ch;
798
+ continue;
799
+ }
800
+ // Track only printable ASCII for line, with size limit
801
+ if (ch >= " " && ch <= "~" && line.length < 50) line += ch;
802
+ out += ch;
803
+ }
804
+ return out;
805
+ };
806
+ })())
807
+
746
808
  // TODO(sno): Read from IPC stream if available (FIFO on Linux, Named Pipes on Windows)
747
809
  // .by(async (s) => {
748
810
  // if (!useStdinAppend) return s;
@@ -292,4 +292,29 @@ describe("CLI argument parsing", () => {
292
292
 
293
293
  warnSpy.mockRestore();
294
294
  });
295
+
296
+ it("should have autoYes enabled by default", () => {
297
+ const result = parseCliArgs(["node", "/path/to/cli", "claude"]);
298
+
299
+ expect(result.autoYes).toBe(true);
300
+ });
301
+
302
+ it("should parse --auto=no to disable autoYes", () => {
303
+ const result = parseCliArgs(["node", "/path/to/cli", "--auto=no", "claude"]);
304
+
305
+ expect(result.autoYes).toBe(false);
306
+ });
307
+
308
+ it("should parse --auto=yes to keep autoYes enabled", () => {
309
+ const result = parseCliArgs(["node", "/path/to/cli", "--auto=yes", "claude"]);
310
+
311
+ expect(result.autoYes).toBe(true);
312
+ });
313
+
314
+ it("should have autoYes enabled for -yes suffix", () => {
315
+ const result = parseCliArgs(["node", "/usr/local/bin/claude-yes", "--prompt", "test"]);
316
+
317
+ expect(result.cli).toBe("claude");
318
+ expect(result.autoYes).toBe(true);
319
+ });
295
320
  });
@@ -11,12 +11,15 @@ import pkg from "../package.json" with { type: "json" };
11
11
  */
12
12
  export function parseCliArgs(argv: string[]) {
13
13
  // Detect cli name from script name (same logic as cli.ts:10-14)
14
- const cliName =
14
+ const scriptBaseName =
15
15
  argv[1]
16
16
  ?.split(/[/\\]/)
17
17
  .at(-1)
18
- ?.replace(/(\.[jt]s)?$/, "")
19
- .replace(/^(cli|agent)(-yes$)?/, "")
18
+ ?.replace(/(\.[jt]s)?$/, "") || "";
19
+
20
+ const cliName =
21
+ scriptBaseName
22
+ .replace(/^(cli|agent)(-yes)?$/, "")
20
23
  .replace(/^ay$/, "") // treat standalone "ay" same as "agent-yes"
21
24
  .replace(/-yes$/, "") || undefined;
22
25
 
@@ -106,6 +109,17 @@ export function parseCliArgs(argv: string[]) {
106
109
  default: false,
107
110
  alias: ["ipc", "fifo"], // backward compatibility
108
111
  })
112
+ .option("auto", {
113
+ type: "string",
114
+ description: "Control auto-yes mode: 'yes' to auto-approve prompts (default), 'no' to start in manual mode. Press Ctrl+Y during the session to toggle at any time.",
115
+ choices: ["yes", "no"] as const,
116
+ default: "yes",
117
+ })
118
+ .option("rust", {
119
+ type: "boolean",
120
+ description: "Use the Rust implementation instead of TypeScript",
121
+ default: false,
122
+ })
109
123
  .positional("cli", {
110
124
  describe: "The AI CLI to run, e.g., claude, codex, copilot, cursor, gemini",
111
125
  type: "string",
@@ -215,5 +229,7 @@ export function parseCliArgs(argv: string[]) {
215
229
  appendPrompt: parsedArgv.appendPrompt,
216
230
  useStdinAppend: Boolean(parsedArgv.stdpush || parsedArgv.ipc || parsedArgv.fifo), // Support --stdpush, --ipc, and --fifo (backward compatibility)
217
231
  showVersion: parsedArgv.version,
232
+ autoYes: parsedArgv.auto !== "no", // auto-yes enabled by default, disabled with --auto=no
233
+ useRust: parsedArgv.rust,
218
234
  };
219
235
  }
package/ts/postbuild.ts CHANGED
@@ -8,10 +8,13 @@ import { CLIS_CONFIG } from "./index.ts";
8
8
  import sflow from "sflow";
9
9
  import pkg from "../package.json";
10
10
 
11
- // Create copies for each CLI variant (all use the same wrapper logic)
12
- await sflow([...Object.keys(CLIS_CONFIG), "agent"])
13
- .map(async (cli) => {
14
- const cliName = `${cli}-yes`;
11
+ // Create copies for each CLI variant (-yes versions only; use --auto=no flag to disable auto-yes)
12
+ const cliNames = [...Object.keys(CLIS_CONFIG), "agent"];
13
+ const suffixes = ["-yes"];
14
+
15
+ await sflow(cliNames.flatMap((cli) => suffixes.map((suffix) => ({ cli, suffix }))))
16
+ .map(async ({ cli, suffix }) => {
17
+ const cliName = `${cli}${suffix}`;
15
18
 
16
19
  const wrapperPath = `./dist/${cliName}.js`;
17
20
  await writeFile(
@@ -23,7 +26,8 @@ await import('./cli.js')
23
26
  );
24
27
  await chmod(wrapperPath, 0o755);
25
28
 
26
- if (!(pkg.bin as Record<string, string>)?.[cliName]) {
29
+ // Only register -yes variants in package.json bin
30
+ if (suffix === "-yes" && !(pkg.bin as Record<string, string>)?.[cliName]) {
27
31
  await Bun.$`npm pkg set ${"bin." + cliName}=${wrapperPath}`;
28
32
  console.log(`${wrapperPath} created`);
29
33
  }