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.
- package/dist/{SUPPORTED_CLIS-4H-pxjDu.js → SUPPORTED_CLIS-hj4ASwQy.js} +106 -60
- package/dist/{agent-yes.config-Dn48YQ7f.js → agent-yes.config-CrlZMQo-.js} +5 -7
- package/dist/{agent-yes.config-DPkGH_ef.js → agent-yes.config-DQLH9OgO.js} +1 -1
- package/dist/cli.js +65 -7
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/ts/cli.ts +72 -1
- package/ts/index.ts +77 -15
- package/ts/parseCliArgs.spec.ts +25 -0
- package/ts/parseCliArgs.ts +19 -3
- package/ts/postbuild.ts +9 -5
|
@@ -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
|
|
8933
|
-
const getCodexSessionsDir = () => process.env.CLI_YES_TEST_HOME ? path
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
9075
|
-
const getLockFile = () => path
|
|
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
|
|
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
|
|
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
|
|
9408
|
-
this.dbPath = path
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
9698
|
-
rawLinesLogPath: path
|
|
9699
|
-
debuggingLogsPath: path
|
|
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
|
|
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
|
|
9733
|
-
await mkdir(path
|
|
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-
|
|
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
|
|
10197
|
+
const searchLimit = gitRoot || path.parse(currentDir).root;
|
|
10207
10198
|
while (true) {
|
|
10208
|
-
const md = await readFile(path
|
|
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
|
|
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
|
-
}).
|
|
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
|
|
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-
|
|
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
|
|
65
|
-
|
|
66
|
-
/^.{0,4} 1\.
|
|
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-
|
|
160
|
+
//# sourceMappingURL=agent-yes.config-CrlZMQo-.js.map
|
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-
|
|
4
|
-
import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
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
|
|
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(
|
|
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-
|
|
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
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;
|
package/ts/parseCliArgs.spec.ts
CHANGED
|
@@ -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
|
});
|
package/ts/parseCliArgs.ts
CHANGED
|
@@ -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
|
|
14
|
+
const scriptBaseName =
|
|
15
15
|
argv[1]
|
|
16
16
|
?.split(/[/\\]/)
|
|
17
17
|
.at(-1)
|
|
18
|
-
?.replace(/(\.[jt]s)?$/, "")
|
|
19
|
-
|
|
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 (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
}
|