claude-yes 1.32.2 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +337 -180
- package/dist/index.js +164 -35
- package/package.json +4 -2
- package/ts/cli.ts +22 -5
- package/ts/index.ts +45 -20
- package/ts/parseCliArgs.ts +11 -0
- package/ts/pidStore.ts +135 -0
package/dist/index.js
CHANGED
|
@@ -20631,16 +20631,20 @@ import { fromReadable } from "from-node-stream";
|
|
|
20631
20631
|
import { createReadStream as createReadStream2, mkdirSync } from "fs";
|
|
20632
20632
|
import { unlink } from "fs/promises";
|
|
20633
20633
|
import { dirname } from "path";
|
|
20634
|
-
function createFifoStream(cli) {
|
|
20634
|
+
function createFifoStream(cli, customPath) {
|
|
20635
20635
|
if (process.platform !== "linux") {
|
|
20636
20636
|
return null;
|
|
20637
20637
|
}
|
|
20638
20638
|
let fifoPath = null;
|
|
20639
20639
|
let fifoStream = null;
|
|
20640
20640
|
try {
|
|
20641
|
-
|
|
20642
|
-
|
|
20643
|
-
|
|
20641
|
+
if (customPath) {
|
|
20642
|
+
fifoPath = customPath;
|
|
20643
|
+
} else {
|
|
20644
|
+
const timestamp = new Date().toISOString().replace(/\D/g, "").slice(0, 17);
|
|
20645
|
+
const randomSuffix = Math.random().toString(36).substring(2, 5);
|
|
20646
|
+
fifoPath = `/tmp/agent-yes-${timestamp}${randomSuffix}.stdin`;
|
|
20647
|
+
}
|
|
20644
20648
|
mkdirSync(dirname(fifoPath), { recursive: true });
|
|
20645
20649
|
const mkfifoResult = execaCommandSync(`mkfifo ${fifoPath}`, {
|
|
20646
20650
|
reject: false
|
|
@@ -20725,6 +20729,107 @@ var init_fifo = __esm(() => {
|
|
|
20725
20729
|
init_logger();
|
|
20726
20730
|
});
|
|
20727
20731
|
|
|
20732
|
+
// ts/pidStore.ts
|
|
20733
|
+
import Datastore from "@seald-io/nedb";
|
|
20734
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
20735
|
+
import path9 from "path";
|
|
20736
|
+
|
|
20737
|
+
class PidStore {
|
|
20738
|
+
db;
|
|
20739
|
+
baseDir;
|
|
20740
|
+
constructor(workingDir) {
|
|
20741
|
+
this.baseDir = path9.resolve(workingDir, ".agent-yes");
|
|
20742
|
+
}
|
|
20743
|
+
async init() {
|
|
20744
|
+
await mkdir3(path9.join(this.baseDir, "logs"), { recursive: true });
|
|
20745
|
+
await mkdir3(path9.join(this.baseDir, "fifo"), { recursive: true });
|
|
20746
|
+
this.db = new Datastore({
|
|
20747
|
+
filename: path9.join(this.baseDir, "pid.jsonl"),
|
|
20748
|
+
autoload: true
|
|
20749
|
+
});
|
|
20750
|
+
await this.db.loadDatabaseAsync();
|
|
20751
|
+
await this.cleanStaleRecords();
|
|
20752
|
+
}
|
|
20753
|
+
async registerProcess({
|
|
20754
|
+
pid,
|
|
20755
|
+
cli,
|
|
20756
|
+
args,
|
|
20757
|
+
prompt
|
|
20758
|
+
}) {
|
|
20759
|
+
const now = Date.now();
|
|
20760
|
+
const record = {
|
|
20761
|
+
pid,
|
|
20762
|
+
cli,
|
|
20763
|
+
args,
|
|
20764
|
+
prompt,
|
|
20765
|
+
logFile: this.getLogPath(pid),
|
|
20766
|
+
fifoFile: this.getFifoPath(pid),
|
|
20767
|
+
status: "active",
|
|
20768
|
+
exitReason: "",
|
|
20769
|
+
startedAt: now,
|
|
20770
|
+
updatedAt: now
|
|
20771
|
+
};
|
|
20772
|
+
await this.db.insertAsync(record);
|
|
20773
|
+
logger.debug(`[pidStore] Registered process ${pid}`);
|
|
20774
|
+
return record;
|
|
20775
|
+
}
|
|
20776
|
+
async updateStatus(pid, status, extra) {
|
|
20777
|
+
const update2 = {
|
|
20778
|
+
status,
|
|
20779
|
+
updatedAt: Date.now(),
|
|
20780
|
+
...extra
|
|
20781
|
+
};
|
|
20782
|
+
await this.db.updateAsync({ pid }, { $set: update2 }, {});
|
|
20783
|
+
logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
|
|
20784
|
+
}
|
|
20785
|
+
getLogPath(pid) {
|
|
20786
|
+
return path9.resolve(this.baseDir, "logs", `${pid}.log`);
|
|
20787
|
+
}
|
|
20788
|
+
getFifoPath(pid) {
|
|
20789
|
+
return path9.resolve(this.baseDir, "fifo", `${pid}.stdin`);
|
|
20790
|
+
}
|
|
20791
|
+
async cleanStaleRecords() {
|
|
20792
|
+
const activeRecords = await this.db.findAsync({
|
|
20793
|
+
status: { $ne: "exited" }
|
|
20794
|
+
});
|
|
20795
|
+
for (const record of activeRecords) {
|
|
20796
|
+
if (!this.isProcessAlive(record.pid)) {
|
|
20797
|
+
await this.db.updateAsync({ pid: record.pid }, {
|
|
20798
|
+
$set: {
|
|
20799
|
+
status: "exited",
|
|
20800
|
+
exitReason: "stale-cleanup",
|
|
20801
|
+
updatedAt: Date.now()
|
|
20802
|
+
}
|
|
20803
|
+
}, {});
|
|
20804
|
+
logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
|
|
20805
|
+
}
|
|
20806
|
+
}
|
|
20807
|
+
}
|
|
20808
|
+
async close() {
|
|
20809
|
+
await this.db.compactDatafileAsync();
|
|
20810
|
+
logger.debug("[pidStore] Database compacted and closed");
|
|
20811
|
+
}
|
|
20812
|
+
isProcessAlive(pid) {
|
|
20813
|
+
try {
|
|
20814
|
+
process.kill(pid, 0);
|
|
20815
|
+
return true;
|
|
20816
|
+
} catch {
|
|
20817
|
+
return false;
|
|
20818
|
+
}
|
|
20819
|
+
}
|
|
20820
|
+
static async findActiveFifo(workingDir) {
|
|
20821
|
+
const store = new PidStore(workingDir);
|
|
20822
|
+
await store.init();
|
|
20823
|
+
const records = await store.db.findAsync({ status: { $ne: "exited" } });
|
|
20824
|
+
await store.close();
|
|
20825
|
+
const sorted = records.sort((a2, b) => b.startedAt - a2.startedAt);
|
|
20826
|
+
return sorted[0]?.fifoFile ?? null;
|
|
20827
|
+
}
|
|
20828
|
+
}
|
|
20829
|
+
var init_pidStore = __esm(() => {
|
|
20830
|
+
init_logger();
|
|
20831
|
+
});
|
|
20832
|
+
|
|
20728
20833
|
// ts/defineConfig.ts
|
|
20729
20834
|
async function defineCliYesConfig(cfg) {
|
|
20730
20835
|
if (typeof cfg === "function")
|
|
@@ -20755,13 +20860,13 @@ var exports_agent_yes_config = {};
|
|
|
20755
20860
|
__export(exports_agent_yes_config, {
|
|
20756
20861
|
default: () => agent_yes_config_default
|
|
20757
20862
|
});
|
|
20758
|
-
import { mkdir as
|
|
20863
|
+
import { mkdir as mkdir4 } from "node:fs/promises";
|
|
20759
20864
|
import os from "node:os";
|
|
20760
|
-
import
|
|
20865
|
+
import path10 from "node:path";
|
|
20761
20866
|
function getDefaultConfig() {
|
|
20762
20867
|
return defineCliYesConfig({
|
|
20763
20868
|
configDir,
|
|
20764
|
-
logsDir: configDir &&
|
|
20869
|
+
logsDir: configDir && path10.resolve(configDir, "logs"),
|
|
20765
20870
|
clis: {
|
|
20766
20871
|
qwen: {
|
|
20767
20872
|
install: "npm install -g @qwen-code/qwen-code@latest",
|
|
@@ -20858,21 +20963,21 @@ var init_agent_yes_config = __esm(async () => {
|
|
|
20858
20963
|
init_logger();
|
|
20859
20964
|
logger.debug("loading cli-yes.config.ts from " + import.meta.url);
|
|
20860
20965
|
configDir = await (async () => {
|
|
20861
|
-
const homeConfigDir =
|
|
20862
|
-
const isHomeWritable = await
|
|
20966
|
+
const homeConfigDir = path10.resolve(os.homedir(), ".agent-yes");
|
|
20967
|
+
const isHomeWritable = await mkdir4(homeConfigDir, { recursive: true }).then(() => true).catch(() => false);
|
|
20863
20968
|
if (isHomeWritable) {
|
|
20864
20969
|
logger.debug("[config] Using home directory:", homeConfigDir);
|
|
20865
20970
|
return homeConfigDir;
|
|
20866
20971
|
}
|
|
20867
|
-
const tmpConfigDir =
|
|
20868
|
-
const isWritable = await
|
|
20972
|
+
const tmpConfigDir = path10.resolve("/tmp/.agent-yes");
|
|
20973
|
+
const isWritable = await mkdir4(tmpConfigDir, { recursive: true });
|
|
20869
20974
|
if (isWritable) {
|
|
20870
20975
|
logger.debug("[config] Using workspace directory:", tmpConfigDir);
|
|
20871
20976
|
return tmpConfigDir;
|
|
20872
20977
|
}
|
|
20873
20978
|
return;
|
|
20874
20979
|
})();
|
|
20875
|
-
agent_yes_config_default = deepMixin(await getDefaultConfig(), await import(
|
|
20980
|
+
agent_yes_config_default = deepMixin(await getDefaultConfig(), await import(path10.resolve(os.homedir(), ".agent-yes/config.ts")).catch(() => ({ default: {} })).then((mod) => mod.default), await import(path10.resolve(process.cwd(), "node_modules/.agent-yes/config.ts")).catch(() => ({ default: {} })).then((mod) => mod.default), await import(path10.resolve(process.cwd(), ".agent-yes/config.ts")).catch(() => ({ default: {} })).then((mod) => mod.default));
|
|
20876
20981
|
});
|
|
20877
20982
|
|
|
20878
20983
|
// ts/pty-fix.ts
|
|
@@ -21011,11 +21116,12 @@ init_codexSessionManager();
|
|
|
21011
21116
|
init_runningLock();
|
|
21012
21117
|
init_logger();
|
|
21013
21118
|
init_fifo();
|
|
21119
|
+
init_pidStore();
|
|
21014
21120
|
await init_pty();
|
|
21015
21121
|
var import_winston2 = __toESM(require_winston(), 1);
|
|
21016
21122
|
import { fromReadable as fromReadable2, fromWritable } from "from-node-stream";
|
|
21017
|
-
import { mkdir as
|
|
21018
|
-
import
|
|
21123
|
+
import { mkdir as mkdir5, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
21124
|
+
import path11 from "path";
|
|
21019
21125
|
var config = await init_agent_yes_config().then(() => exports_agent_yes_config).then((mod) => mod.default || mod);
|
|
21020
21126
|
var CLIS_CONFIG = config.clis;
|
|
21021
21127
|
async function agentYes({
|
|
@@ -21061,6 +21167,8 @@ async function agentYes({
|
|
|
21061
21167
|
process.exit(code);
|
|
21062
21168
|
});
|
|
21063
21169
|
}
|
|
21170
|
+
const pidStore = new PidStore(workingDir);
|
|
21171
|
+
await pidStore.init();
|
|
21064
21172
|
process.stdin.setRawMode?.(true);
|
|
21065
21173
|
let isFatal = false;
|
|
21066
21174
|
let shouldRestartWithoutContinue = false;
|
|
@@ -21076,16 +21184,10 @@ async function agentYes({
|
|
|
21076
21184
|
const shellOutputStream = new TransformStream;
|
|
21077
21185
|
const outputWriter = shellOutputStream.writable.getWriter();
|
|
21078
21186
|
logger.debug(`Using ${ptyPackage} for pseudo terminal management.`);
|
|
21079
|
-
|
|
21080
|
-
|
|
21081
|
-
|
|
21082
|
-
|
|
21083
|
-
const debuggingLogsPath = config.logsDir && path10.resolve(config.logsDir, `${cli}-yes-${datetime}.debug.log`);
|
|
21084
|
-
if (debuggingLogsPath)
|
|
21085
|
-
logger.add(new import_winston2.default.transports.File({
|
|
21086
|
-
filename: debuggingLogsPath,
|
|
21087
|
-
level: "debug"
|
|
21088
|
-
}));
|
|
21187
|
+
let logPath = false;
|
|
21188
|
+
let rawLogPath = false;
|
|
21189
|
+
let rawLinesLogPath = false;
|
|
21190
|
+
let debuggingLogsPath = false;
|
|
21089
21191
|
const isSubAgent = !!process.env.CLAUDE_PPID;
|
|
21090
21192
|
if (isSubAgent)
|
|
21091
21193
|
logger.info(`[${cli}-yes] Running as sub-agent (CLAUDE_PPID=${process.env.CLAUDE_PPID})`);
|
|
@@ -21115,9 +21217,9 @@ async function agentYes({
|
|
|
21115
21217
|
} catch {}
|
|
21116
21218
|
const skillHeaders = [];
|
|
21117
21219
|
let currentDir = workingDir2;
|
|
21118
|
-
const searchLimit = gitRoot ||
|
|
21220
|
+
const searchLimit = gitRoot || path11.parse(currentDir).root;
|
|
21119
21221
|
while (true) {
|
|
21120
|
-
const skillPath =
|
|
21222
|
+
const skillPath = path11.resolve(currentDir, "SKILL.md");
|
|
21121
21223
|
const md = await readFile3(skillPath, "utf8").catch(() => null);
|
|
21122
21224
|
if (md) {
|
|
21123
21225
|
const headerMatch = md.match(/^[\s\S]*?(?=\n##\s)/);
|
|
@@ -21130,7 +21232,7 @@ async function agentYes({
|
|
|
21130
21232
|
}
|
|
21131
21233
|
if (currentDir === searchLimit)
|
|
21132
21234
|
break;
|
|
21133
|
-
const parentDir =
|
|
21235
|
+
const parentDir = path11.dirname(currentDir);
|
|
21134
21236
|
if (parentDir === currentDir)
|
|
21135
21237
|
break;
|
|
21136
21238
|
currentDir = parentDir;
|
|
@@ -21230,6 +21332,16 @@ ${prompt}` : prefix;
|
|
|
21230
21332
|
return false;
|
|
21231
21333
|
}
|
|
21232
21334
|
}, spawn2)();
|
|
21335
|
+
await pidStore.registerProcess({ pid: shell.pid, cli, args: cliArgs, prompt });
|
|
21336
|
+
logPath = pidStore.getLogPath(shell.pid);
|
|
21337
|
+
rawLogPath = path11.resolve(path11.dirname(logPath), `${shell.pid}.raw.log`);
|
|
21338
|
+
rawLinesLogPath = path11.resolve(path11.dirname(logPath), `${shell.pid}.lines.log`);
|
|
21339
|
+
debuggingLogsPath = path11.resolve(path11.dirname(logPath), `${shell.pid}.debug.log`);
|
|
21340
|
+
if (debuggingLogsPath)
|
|
21341
|
+
logger.add(new import_winston2.default.transports.File({
|
|
21342
|
+
filename: debuggingLogsPath,
|
|
21343
|
+
level: "debug"
|
|
21344
|
+
}));
|
|
21233
21345
|
const pendingExitCode = Promise.withResolvers();
|
|
21234
21346
|
async function onData(data) {
|
|
21235
21347
|
await outputWriter.write(data);
|
|
@@ -21239,6 +21351,7 @@ ${prompt}` : prefix;
|
|
|
21239
21351
|
stdinReady.unready();
|
|
21240
21352
|
const agentCrashed = exitCode2 !== 0;
|
|
21241
21353
|
if (shouldRestartWithoutContinue) {
|
|
21354
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason: "restarted", exitCode: exitCode2 ?? undefined });
|
|
21242
21355
|
shouldRestartWithoutContinue = false;
|
|
21243
21356
|
isFatal = false;
|
|
21244
21357
|
const cliCommand = cliConf?.binary || cli;
|
|
@@ -21248,6 +21361,7 @@ ${prompt}` : prefix;
|
|
|
21248
21361
|
];
|
|
21249
21362
|
logger.info(`Restarting ${cli} ${JSON.stringify([bin, ...args])}`);
|
|
21250
21363
|
shell = pty_default.spawn(bin, args, getPtyOptions());
|
|
21364
|
+
await pidStore.registerProcess({ pid: shell.pid, cli, args, prompt });
|
|
21251
21365
|
shell.onData(onData);
|
|
21252
21366
|
shell.onExit(onExit);
|
|
21253
21367
|
return;
|
|
@@ -21257,8 +21371,11 @@ ${prompt}` : prefix;
|
|
|
21257
21371
|
logger.warn(`robust is only supported for ${Object.entries(CLIS_CONFIG).filter(([_, v]) => v.restoreArgs).map(([k]) => k).join(", ")} currently, not ${cli}`);
|
|
21258
21372
|
return;
|
|
21259
21373
|
}
|
|
21260
|
-
if (isFatal)
|
|
21374
|
+
if (isFatal) {
|
|
21375
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason: "fatal", exitCode: exitCode2 ?? undefined });
|
|
21261
21376
|
return pendingExitCode.resolve(exitCode2);
|
|
21377
|
+
}
|
|
21378
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason: "restarted", exitCode: exitCode2 ?? undefined });
|
|
21262
21379
|
logger.info(`${cli} crashed, restarting...`);
|
|
21263
21380
|
let restoreArgs = conf.restoreArgs;
|
|
21264
21381
|
if (cli === "codex") {
|
|
@@ -21271,10 +21388,13 @@ ${prompt}` : prefix;
|
|
|
21271
21388
|
}
|
|
21272
21389
|
}
|
|
21273
21390
|
shell = pty_default.spawn(cli, restoreArgs, getPtyOptions());
|
|
21391
|
+
await pidStore.registerProcess({ pid: shell.pid, cli, args: restoreArgs, prompt });
|
|
21274
21392
|
shell.onData(onData);
|
|
21275
21393
|
shell.onExit(onExit);
|
|
21276
21394
|
return;
|
|
21277
21395
|
}
|
|
21396
|
+
const exitReason = agentCrashed ? "crash" : "normal";
|
|
21397
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason, exitCode: exitCode2 ?? undefined });
|
|
21278
21398
|
return pendingExitCode.resolve(exitCode2);
|
|
21279
21399
|
});
|
|
21280
21400
|
process.stdout.on("resize", () => {
|
|
@@ -21286,6 +21406,7 @@ ${prompt}` : prefix;
|
|
|
21286
21406
|
const idleWaiter = new IdleWaiter;
|
|
21287
21407
|
if (exitOnIdle)
|
|
21288
21408
|
idleWaiter.wait(exitOnIdle).then(async () => {
|
|
21409
|
+
await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
|
|
21289
21410
|
if (isStillWorkingQ()) {
|
|
21290
21411
|
logger.warn("[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet");
|
|
21291
21412
|
return;
|
|
@@ -21311,10 +21432,14 @@ ${prompt}` : prefix;
|
|
|
21311
21432
|
}).by((s) => {
|
|
21312
21433
|
if (!useFifo)
|
|
21313
21434
|
return s;
|
|
21314
|
-
const fifoResult = createFifoStream(cli);
|
|
21435
|
+
const fifoResult = createFifoStream(cli, pidStore.getFifoPath(shell.pid));
|
|
21315
21436
|
if (!fifoResult)
|
|
21316
21437
|
return s;
|
|
21317
21438
|
pendingExitCode.promise.finally(() => fifoResult.cleanup());
|
|
21439
|
+
process.stderr.write(`
|
|
21440
|
+
Append prompts: ${cli}-yes --append-prompt '...'
|
|
21441
|
+
|
|
21442
|
+
`);
|
|
21318
21443
|
return s.merge(fifoResult.stream);
|
|
21319
21444
|
}).onStart(async function promptOnStart() {
|
|
21320
21445
|
logger.debug("Sending prompt message: " + JSON.stringify(prompt));
|
|
@@ -21328,10 +21453,13 @@ ${prompt}` : prefix;
|
|
|
21328
21453
|
}
|
|
21329
21454
|
}),
|
|
21330
21455
|
readable: shellOutputStream.readable
|
|
21331
|
-
}).forEach(() =>
|
|
21456
|
+
}).forEach(() => {
|
|
21457
|
+
idleWaiter.ping();
|
|
21458
|
+
pidStore.updateStatus(shell.pid, "active").catch(() => null);
|
|
21459
|
+
}).forEach(() => nextStdout.ready()).forkTo(async function rawLogger(f) {
|
|
21332
21460
|
if (!rawLogPath)
|
|
21333
21461
|
return f.run();
|
|
21334
|
-
return await
|
|
21462
|
+
return await mkdir5(path11.dirname(rawLogPath), { recursive: true }).then(() => {
|
|
21335
21463
|
logger.debug(`[${cli}-yes] raw logs streaming to ${rawLogPath}`);
|
|
21336
21464
|
return f.forEach(async (chars) => {
|
|
21337
21465
|
await writeFile3(rawLogPath, chars, { flag: "a" }).catch(() => null);
|
|
@@ -21405,18 +21533,19 @@ ${prompt}` : prefix;
|
|
|
21405
21533
|
flush: (ctrl) => ctrl.terminate()
|
|
21406
21534
|
})).to(fromWritable(process.stdout));
|
|
21407
21535
|
if (logPath) {
|
|
21408
|
-
await
|
|
21536
|
+
await mkdir5(path11.dirname(logPath), { recursive: true }).catch(() => null);
|
|
21409
21537
|
await writeFile3(logPath, terminalRender.render()).catch(() => null);
|
|
21410
21538
|
logger.info(`[${cli}-yes] Full logs saved to ${logPath}`);
|
|
21411
21539
|
}
|
|
21412
21540
|
const exitCode = await pendingExitCode.promise;
|
|
21413
21541
|
logger.info(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
|
|
21542
|
+
await pidStore.close();
|
|
21414
21543
|
await outputWriter.close();
|
|
21415
21544
|
if (logFile) {
|
|
21416
21545
|
if (verbose)
|
|
21417
21546
|
logger.info(`[${cli}-yes] Writing rendered logs to ${logFile}`);
|
|
21418
|
-
const logFilePath =
|
|
21419
|
-
await
|
|
21547
|
+
const logFilePath = path11.resolve(logFile);
|
|
21548
|
+
await mkdir5(path11.dirname(logFilePath), { recursive: true }).catch(() => null);
|
|
21420
21549
|
await writeFile3(logFilePath, terminalRender.render());
|
|
21421
21550
|
}
|
|
21422
21551
|
return { exitCode, logs: terminalRender.render() };
|
|
@@ -21484,5 +21613,5 @@ export {
|
|
|
21484
21613
|
CLIS_CONFIG
|
|
21485
21614
|
};
|
|
21486
21615
|
|
|
21487
|
-
//# debugId=
|
|
21616
|
+
//# debugId=E6394A1D786EEA6E64756E2164756E21
|
|
21488
21617
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-yes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.33.0",
|
|
4
4
|
"description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"url": "git+https://github.com/snomiao/agent-yes.git"
|
|
30
30
|
},
|
|
31
31
|
"bin": {
|
|
32
|
+
"ay": "./dist/agent-yes.js",
|
|
32
33
|
"agent-yes": "./dist/agent-yes.js",
|
|
33
34
|
"amp-yes": "./dist/amp-yes.js",
|
|
34
35
|
"auggie-yes": "./dist/auggie-yes.js",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"registry": "https://registry.npmjs.org/"
|
|
62
63
|
},
|
|
63
64
|
"scripts": {
|
|
64
|
-
"build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
65
|
+
"build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@seald-io/nedb --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
|
|
65
66
|
"postbuild": "bun ./ts/postbuild.ts",
|
|
66
67
|
"demo": "bun run build && bun link && claude-yes -- demo",
|
|
67
68
|
"dev": "bun ts/index.ts",
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
"test": "bun test --coverage"
|
|
74
75
|
},
|
|
75
76
|
"dependencies": {
|
|
77
|
+
"@seald-io/nedb": "^4.0.4",
|
|
76
78
|
"@snomiao/bun-pty": "^0.3.4",
|
|
77
79
|
"bun-pty": "^0.4.8",
|
|
78
80
|
"from-node-stream": "^0.1.2"
|
package/ts/cli.ts
CHANGED
|
@@ -3,19 +3,36 @@ import { argv } from "process";
|
|
|
3
3
|
import cliYesConfig from "../agent-yes.config.ts";
|
|
4
4
|
import { parseCliArgs } from "./parseCliArgs.ts";
|
|
5
5
|
import { logger } from "./logger.ts";
|
|
6
|
+
import { PidStore } from "./pidStore.ts";
|
|
6
7
|
|
|
7
8
|
// Import the CLI module
|
|
8
9
|
|
|
9
10
|
// Parse CLI arguments
|
|
10
11
|
const config = parseCliArgs(process.argv);
|
|
11
12
|
|
|
13
|
+
// Handle --append-prompt: write to active FIFO and exit
|
|
14
|
+
if (config.appendPrompt) {
|
|
15
|
+
const fifoPath = await PidStore.findActiveFifo(process.cwd());
|
|
16
|
+
if (!fifoPath) {
|
|
17
|
+
console.error("No active agent with FIFO found in current directory.");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const { writeFileSync, openSync, closeSync } = await import("fs");
|
|
21
|
+
const fd = openSync(fifoPath, "w");
|
|
22
|
+
writeFileSync(fd, config.appendPrompt + "\r");
|
|
23
|
+
closeSync(fd);
|
|
24
|
+
console.log(`Sent prompt to ${fifoPath}`);
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
// Validate CLI name
|
|
13
29
|
if (!config.cli) {
|
|
14
|
-
logger.error(process.argv);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
30
|
+
// logger.error(process.argv);
|
|
31
|
+
config.cli = "claude"; // default to claude, for smooth UX
|
|
32
|
+
logger.warn("Warning: No CLI name provided. Using default 'claude'.");
|
|
33
|
+
// throw new Error(
|
|
34
|
+
// `missing cli def, available clis: ${Object.keys((await cliYesConfig).clis).join(", ")}`,
|
|
35
|
+
// );
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
// console.log(`Using CLI: ${config.cli}`);
|
package/ts/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { removeControlCharacters } from "./removeControlCharacters.ts";
|
|
|
18
18
|
import { acquireLock, releaseLock, shouldUseLock } from "./runningLock.ts";
|
|
19
19
|
import { logger } from "./logger.ts";
|
|
20
20
|
import { createFifoStream } from "./beta/fifo.ts";
|
|
21
|
+
import { PidStore } from "./pidStore.ts";
|
|
21
22
|
import { SUPPORTED_CLIS } from "./SUPPORTED_CLIS.ts";
|
|
22
23
|
import winston from "winston";
|
|
23
24
|
import { mapObject, pipe } from "rambda";
|
|
@@ -167,6 +168,10 @@ export default async function agentYes({
|
|
|
167
168
|
});
|
|
168
169
|
}
|
|
169
170
|
|
|
171
|
+
// Initialize process registry
|
|
172
|
+
const pidStore = new PidStore(workingDir);
|
|
173
|
+
await pidStore.init();
|
|
174
|
+
|
|
170
175
|
process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage
|
|
171
176
|
|
|
172
177
|
let isFatal = false; // when true, do not restart on crash, and exit agent
|
|
@@ -187,23 +192,10 @@ export default async function agentYes({
|
|
|
187
192
|
|
|
188
193
|
logger.debug(`Using ${ptyPackage} for pseudo terminal management.`);
|
|
189
194
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const rawLinesLogPath =
|
|
195
|
-
config.logsDir && path.resolve(config.logsDir, `${cli}-yes-${datetime}.lines.log`);
|
|
196
|
-
const debuggingLogsPath =
|
|
197
|
-
config.logsDir && path.resolve(config.logsDir, `${cli}-yes-${datetime}.debug.log`);
|
|
198
|
-
|
|
199
|
-
// add
|
|
200
|
-
if (debuggingLogsPath)
|
|
201
|
-
logger.add(
|
|
202
|
-
new winston.transports.File({
|
|
203
|
-
filename: debuggingLogsPath,
|
|
204
|
-
level: "debug",
|
|
205
|
-
}),
|
|
206
|
-
);
|
|
195
|
+
let logPath: string | false = false;
|
|
196
|
+
let rawLogPath: string | false = false;
|
|
197
|
+
let rawLinesLogPath: string | false = false;
|
|
198
|
+
let debuggingLogsPath: string | false = false;
|
|
207
199
|
|
|
208
200
|
// Detect if running as sub-agent
|
|
209
201
|
const isSubAgent = !!process.env.CLAUDE_PPID;
|
|
@@ -392,6 +384,22 @@ export default async function agentYes({
|
|
|
392
384
|
},
|
|
393
385
|
spawn,
|
|
394
386
|
)();
|
|
387
|
+
|
|
388
|
+
// Register process in pidStore and compute log paths
|
|
389
|
+
await pidStore.registerProcess({ pid: shell.pid, cli, args: cliArgs, prompt });
|
|
390
|
+
logPath = pidStore.getLogPath(shell.pid);
|
|
391
|
+
rawLogPath = path.resolve(path.dirname(logPath), `${shell.pid}.raw.log`);
|
|
392
|
+
rawLinesLogPath = path.resolve(path.dirname(logPath), `${shell.pid}.lines.log`);
|
|
393
|
+
debuggingLogsPath = path.resolve(path.dirname(logPath), `${shell.pid}.debug.log`);
|
|
394
|
+
|
|
395
|
+
if (debuggingLogsPath)
|
|
396
|
+
logger.add(
|
|
397
|
+
new winston.transports.File({
|
|
398
|
+
filename: debuggingLogsPath,
|
|
399
|
+
level: "debug",
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
|
|
395
403
|
const pendingExitCode = Promise.withResolvers<number | null>();
|
|
396
404
|
|
|
397
405
|
async function onData(data: string) {
|
|
@@ -407,6 +415,7 @@ export default async function agentYes({
|
|
|
407
415
|
// Handle restart without continue args (e.g., "No conversation found to continue")
|
|
408
416
|
// logger.debug(``, { shouldRestartWithoutContinue, robust })
|
|
409
417
|
if (shouldRestartWithoutContinue) {
|
|
418
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason: "restarted", exitCode: exitCode ?? undefined });
|
|
410
419
|
shouldRestartWithoutContinue = false; // reset flag
|
|
411
420
|
isFatal = false; // reset fatal flag to allow restart
|
|
412
421
|
|
|
@@ -419,6 +428,7 @@ export default async function agentYes({
|
|
|
419
428
|
logger.info(`Restarting ${cli} ${JSON.stringify([bin, ...args])}`);
|
|
420
429
|
|
|
421
430
|
shell = pty.spawn(bin!, args, getPtyOptions());
|
|
431
|
+
await pidStore.registerProcess({ pid: shell.pid, cli, args, prompt });
|
|
422
432
|
shell.onData(onData);
|
|
423
433
|
shell.onExit(onExit);
|
|
424
434
|
return;
|
|
@@ -434,8 +444,12 @@ export default async function agentYes({
|
|
|
434
444
|
);
|
|
435
445
|
return;
|
|
436
446
|
}
|
|
437
|
-
if (isFatal)
|
|
447
|
+
if (isFatal) {
|
|
448
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason: "fatal", exitCode: exitCode ?? undefined });
|
|
449
|
+
return pendingExitCode.resolve(exitCode);
|
|
450
|
+
}
|
|
438
451
|
|
|
452
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason: "restarted", exitCode: exitCode ?? undefined });
|
|
439
453
|
logger.info(`${cli} crashed, restarting...`);
|
|
440
454
|
|
|
441
455
|
// For codex, try to use stored session ID for this directory
|
|
@@ -452,10 +466,13 @@ export default async function agentYes({
|
|
|
452
466
|
}
|
|
453
467
|
|
|
454
468
|
shell = pty.spawn(cli, restoreArgs, getPtyOptions());
|
|
469
|
+
await pidStore.registerProcess({ pid: shell.pid, cli, args: restoreArgs, prompt });
|
|
455
470
|
shell.onData(onData);
|
|
456
471
|
shell.onExit(onExit);
|
|
457
472
|
return;
|
|
458
473
|
}
|
|
474
|
+
const exitReason = agentCrashed ? "crash" : "normal";
|
|
475
|
+
await pidStore.updateStatus(shell.pid, "exited", { exitReason, exitCode: exitCode ?? undefined });
|
|
459
476
|
return pendingExitCode.resolve(exitCode);
|
|
460
477
|
});
|
|
461
478
|
|
|
@@ -475,6 +492,7 @@ export default async function agentYes({
|
|
|
475
492
|
const idleWaiter = new IdleWaiter();
|
|
476
493
|
if (exitOnIdle)
|
|
477
494
|
idleWaiter.wait(exitOnIdle).then(async () => {
|
|
495
|
+
await pidStore.updateStatus(shell.pid, "idle").catch(() => null);
|
|
478
496
|
if (isStillWorkingQ()) {
|
|
479
497
|
logger.warn("[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet");
|
|
480
498
|
return;
|
|
@@ -513,9 +531,10 @@ export default async function agentYes({
|
|
|
513
531
|
// read from FIFO if available, e.g. /tmp/agent-yes-*.stdin, which can be used to send additional input from other processes
|
|
514
532
|
.by((s) => {
|
|
515
533
|
if (!useFifo) return s;
|
|
516
|
-
const fifoResult = createFifoStream(cli);
|
|
534
|
+
const fifoResult = createFifoStream(cli, pidStore.getFifoPath(shell.pid));
|
|
517
535
|
if (!fifoResult) return s;
|
|
518
536
|
pendingExitCode.promise.finally(() => fifoResult.cleanup());
|
|
537
|
+
process.stderr.write(`\n Append prompts: ${cli}-yes --append-prompt '...'\n\n`);
|
|
519
538
|
return s.merge(fifoResult.stream);
|
|
520
539
|
})
|
|
521
540
|
|
|
@@ -539,7 +558,10 @@ export default async function agentYes({
|
|
|
539
558
|
readable: shellOutputStream.readable,
|
|
540
559
|
})
|
|
541
560
|
|
|
542
|
-
.forEach(() =>
|
|
561
|
+
.forEach(() => {
|
|
562
|
+
idleWaiter.ping();
|
|
563
|
+
pidStore.updateStatus(shell.pid, "active").catch(() => null);
|
|
564
|
+
})
|
|
543
565
|
.forEach(() => nextStdout.ready())
|
|
544
566
|
|
|
545
567
|
.forkTo(async function rawLogger(f) {
|
|
@@ -695,6 +717,9 @@ export default async function agentYes({
|
|
|
695
717
|
const exitCode = await pendingExitCode.promise;
|
|
696
718
|
logger.info(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
|
|
697
719
|
|
|
720
|
+
// Final pidStore cleanup
|
|
721
|
+
await pidStore.close();
|
|
722
|
+
|
|
698
723
|
// Update task status.writable release lock
|
|
699
724
|
await outputWriter.close();
|
|
700
725
|
|
package/ts/parseCliArgs.ts
CHANGED
|
@@ -88,6 +88,15 @@ export function parseCliArgs(argv: string[]) {
|
|
|
88
88
|
default: false,
|
|
89
89
|
alias: "c",
|
|
90
90
|
})
|
|
91
|
+
.option("append-prompt", {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Send a prompt to the active agent's FIFO stdin in current directory",
|
|
94
|
+
})
|
|
95
|
+
.option("fifo", {
|
|
96
|
+
type: "boolean",
|
|
97
|
+
description: "Enable FIFO input stream for additional stdin input (Linux only)",
|
|
98
|
+
default: false,
|
|
99
|
+
})
|
|
91
100
|
.positional("cli", {
|
|
92
101
|
describe: "The AI CLI to run, e.g., claude, codex, copilot, cursor, gemini",
|
|
93
102
|
type: "string",
|
|
@@ -180,5 +189,7 @@ export function parseCliArgs(argv: string[]) {
|
|
|
180
189
|
verbose: parsedArgv.verbose,
|
|
181
190
|
resume: parsedArgv.continue, // Note: intentional use resume here to avoid preserved keyword (continue)
|
|
182
191
|
useSkills: parsedArgv.useSkills,
|
|
192
|
+
appendPrompt: parsedArgv.appendPrompt,
|
|
193
|
+
useFifo: parsedArgv.fifo,
|
|
183
194
|
};
|
|
184
195
|
}
|