claude-yes 1.32.3 → 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/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
- const timestamp = new Date().toISOString().replace(/\D/g, "").slice(0, 17);
20642
- const randomSuffix = Math.random().toString(36).substring(2, 5);
20643
- fifoPath = `/tmp/agent-yes-${timestamp}${randomSuffix}.stdin`;
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 mkdir3 } from "node:fs/promises";
20863
+ import { mkdir as mkdir4 } from "node:fs/promises";
20759
20864
  import os from "node:os";
20760
- import path9 from "node:path";
20865
+ import path10 from "node:path";
20761
20866
  function getDefaultConfig() {
20762
20867
  return defineCliYesConfig({
20763
20868
  configDir,
20764
- logsDir: configDir && path9.resolve(configDir, "logs"),
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 = path9.resolve(os.homedir(), ".agent-yes");
20862
- const isHomeWritable = await mkdir3(homeConfigDir, { recursive: true }).then(() => true).catch(() => false);
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 = path9.resolve("/tmp/.agent-yes");
20868
- const isWritable = await mkdir3(tmpConfigDir, { recursive: true });
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(path9.resolve(os.homedir(), ".agent-yes/config.ts")).catch(() => ({ default: {} })).then((mod) => mod.default), await import(path9.resolve(process.cwd(), "node_modules/.agent-yes/config.ts")).catch(() => ({ default: {} })).then((mod) => mod.default), await import(path9.resolve(process.cwd(), ".agent-yes/config.ts")).catch(() => ({ default: {} })).then((mod) => mod.default));
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 mkdir4, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
21018
- import path10 from "path";
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
- const datetime = new Date().toISOString().replace(/\D/g, "").slice(0, 17);
21080
- const logPath = config.logsDir && path10.resolve(config.logsDir, `${cli}-yes-${datetime}.log`);
21081
- const rawLogPath = config.logsDir && path10.resolve(config.logsDir, `${cli}-yes-${datetime}.raw.log`);
21082
- const rawLinesLogPath = config.logsDir && path10.resolve(config.logsDir, `${cli}-yes-${datetime}.lines.log`);
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 || path10.parse(currentDir).root;
21220
+ const searchLimit = gitRoot || path11.parse(currentDir).root;
21119
21221
  while (true) {
21120
- const skillPath = path10.resolve(currentDir, "SKILL.md");
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 = path10.dirname(currentDir);
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(() => idleWaiter.ping()).forEach(() => nextStdout.ready()).forkTo(async function rawLogger(f) {
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 mkdir4(path10.dirname(rawLogPath), { recursive: true }).then(() => {
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 mkdir4(path10.dirname(logPath), { recursive: true }).catch(() => null);
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 = path10.resolve(logFile);
21419
- await mkdir4(path10.dirname(logFilePath), { recursive: true }).catch(() => null);
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=8D7068968CDDFD1C64756E2164756E21
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.32.3",
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
- logger.error("Error: No CLI name provided.");
16
- throw new Error(
17
- `missing cli def, available clis: ${Object.keys((await cliYesConfig).clis).join(", ")}`,
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
- const datetime = new Date().toISOString().replace(/\D/g, "").slice(0, 17);
191
- const logPath = config.logsDir && path.resolve(config.logsDir, `${cli}-yes-${datetime}.log`);
192
- const rawLogPath =
193
- config.logsDir && path.resolve(config.logsDir, `${cli}-yes-${datetime}.raw.log`);
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) return pendingExitCode.resolve(exitCode);
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(() => idleWaiter.ping())
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
 
@@ -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
  }