agent-yes 1.141.0 → 1.142.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.
@@ -0,0 +1,8 @@
1
+ import "./ts-DkAVuuIK.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-Dl2LrFFo.js";
4
+ import "./pidStore-fqXqTKkh.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C03sKqhu.js";
7
+
8
+ export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-CMMwNq--.js";
1
+ import { t as CLIS_CONFIG } from "./ts-DkAVuuIK.js";
2
2
 
3
3
  //#region ts/SUPPORTED_CLIS.ts
4
4
  const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
5
5
 
6
6
  //#endregion
7
7
  export { SUPPORTED_CLIS as t };
8
- //# sourceMappingURL=SUPPORTED_CLIS-CnBoRIDN.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-C03sKqhu.js.map
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-zdFbz1ST.js";
3
3
  import { n as logger } from "./logger-CDIsZ-Pp.js";
4
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DMpCuaZe.js";
4
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-Dl2LrFFo.js";
5
5
  import { argv } from "process";
6
6
  import { execFileSync, spawn } from "child_process";
7
7
  import ms from "ms";
@@ -480,7 +480,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
480
480
  const rawArg = process.argv[2];
481
481
  const managerCommands = !invokedCliName(process.argv);
482
482
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
483
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-3BaA-AmB.js");
483
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DyhY1zTt.js");
484
484
  if (isHelpFlag && process.argv.length === 3) {
485
485
  cmdHelp(managerCommands);
486
486
  process.exit(0);
@@ -513,7 +513,7 @@ if (config.useRust) {
513
513
  }
514
514
  }
515
515
  if (rustBinary) {
516
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-bW-3DoHX.js");
516
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-BKX1tigt.js");
517
517
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
518
518
  if (config.verbose) {
519
519
  console.log(`[rust] Using binary: ${rustBinary}`);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-CMMwNq--.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-DkAVuuIK.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DMpCuaZe.js";
3
+ import "./versionChecker-Dl2LrFFo.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts-CMMwNq--.js";
1
+ import "./ts-DkAVuuIK.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DMpCuaZe.js";
3
+ import "./versionChecker-Dl2LrFFo.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnBoRIDN.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C03sKqhu.js";
7
7
  import { n as resolveSpawnCwd } from "./workspaceConfig-BCOqRBEW.js";
8
8
  import { createHash } from "node:crypto";
9
9
 
@@ -141,4 +141,4 @@ async function cmdSchedule(rest) {
141
141
 
142
142
  //#endregion
143
143
  export { cmdSchedule };
144
- //# sourceMappingURL=schedule-PVnlPldE.js.map
144
+ //# sourceMappingURL=schedule-BB_gXj7p.js.map
@@ -1,13 +1,13 @@
1
- import "./ts-CMMwNq--.js";
1
+ import "./ts-DkAVuuIK.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-DMpCuaZe.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-Dl2LrFFo.js";
4
4
  import "./pidStore-fqXqTKkh.js";
5
5
  import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
6
6
  import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
7
7
  import "./configShared-C1C04bbq.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnBoRIDN.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C03sKqhu.js";
9
9
  import "./remotes-PKKjfTI1.js";
10
- import { C as writeToIpc, c as extractTaskCounts, g as renderRawLog, h as readNotes, i as controlCodeFromName, o as deriveLiveStatus, p as listRecords, v as resolveOne, x as snapshotStatus } from "./subcommands-dbx7EoK-.js";
10
+ import { S as snapshotStatus, _ as renderRawLog, c as extractTaskCounts, g as readNotes, i as controlCodeFromName, m as listRecords, o as deriveLiveStatus, w as writeToIpc, y as resolveOne } from "./subcommands-OKH2CsBu.js";
11
11
  import yargs from "yargs";
12
12
  import { mkdir, open, readFile, stat, writeFile } from "fs/promises";
13
13
  import { homedir, hostname, userInfo } from "os";
@@ -1175,4 +1175,4 @@ Options:
1175
1175
 
1176
1176
  //#endregion
1177
1177
  export { cmdServe };
1178
- //# sourceMappingURL=serve-D8UEMOas.js.map
1178
+ //# sourceMappingURL=serve-B8tHXvH2.js.map
@@ -32,7 +32,7 @@ async function cmdSetup(rest) {
32
32
  if (!existsSync(abs)) process.stderr.write(` note: that directory doesn't exist yet — create it, or agents spawned there will fail\n`);
33
33
  if (noShare) return 0;
34
34
  process.stdout.write(`\nsharing this machine to agent-yes.com…\n`);
35
- const { cmdServe } = await import("./serve-D8UEMOas.js");
35
+ const { cmdServe } = await import("./serve-B8tHXvH2.js");
36
36
  return cmdServe([
37
37
  "install",
38
38
  "--share",
@@ -42,4 +42,4 @@ async function cmdSetup(rest) {
42
42
 
43
43
  //#endregion
44
44
  export { cmdSetup };
45
- //# sourceMappingURL=setup-BPAdHGDG.js.map
45
+ //# sourceMappingURL=setup-DKF0uPs6.js.map
@@ -0,0 +1,7 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import "./globalPidIndex-DlmmJlO8.js";
3
+ import "./configShared-C1C04bbq.js";
4
+ import "./remotes-PKKjfTI1.js";
5
+ import { C as stopTipForCli, S as snapshotStatus, _ as renderRawLog, a as cursorAbs, b as resolveReadWindow, c as extractTaskCounts, d as isExitRequest, f as isPidAlive, g as readNotes, h as matchKeyword, i as controlCodeFromName, l as finalizedLines, m as listRecords, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isSubcommand, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as renderRawLogLines, w as writeToIpc, x as runSubcommand, y as resolveOne } from "./subcommands-OKH2CsBu.js";
6
+
7
+ export { cmdHelp, isSubcommand, runSubcommand };
@@ -501,6 +501,7 @@ const SUBCOMMANDS = new Set([
501
501
  "send",
502
502
  "attach",
503
503
  "stop",
504
+ "exit",
504
505
  "restart",
505
506
  "note",
506
507
  "serve",
@@ -548,18 +549,19 @@ async function runSubcommand(argv) {
548
549
  case "send": return await cmdSend(rest);
549
550
  case "attach": return await cmdAttach(rest);
550
551
  case "stop": return await cmdStop(rest);
552
+ case "exit": return await cmdExit(rest);
551
553
  case "restart": return await cmdRestart(rest);
552
554
  case "note": return await cmdNote(rest);
553
555
  case "serve": {
554
- const { cmdServe } = await import("./serve-D8UEMOas.js");
556
+ const { cmdServe } = await import("./serve-B8tHXvH2.js");
555
557
  return cmdServe(rest);
556
558
  }
557
559
  case "setup": {
558
- const { cmdSetup } = await import("./setup-BPAdHGDG.js");
560
+ const { cmdSetup } = await import("./setup-DKF0uPs6.js");
559
561
  return cmdSetup(rest);
560
562
  }
561
563
  case "schedule": {
562
- const { cmdSchedule } = await import("./schedule-PVnlPldE.js");
564
+ const { cmdSchedule } = await import("./schedule-BB_gXj7p.js");
563
565
  return cmdSchedule(rest);
564
566
  }
565
567
  case "remote": {
@@ -580,7 +582,7 @@ async function runSubcommand(argv) {
580
582
  }
581
583
  function cmdHelp(managerCommands = true) {
582
584
  const setupLine = managerCommands ? ` ay setup guided setup: pick a workspace, share to agent-yes.com\n` : ``;
583
- process.stdout.write("ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay status <keyword> agent status snapshot\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n\nRemote:\n" + setupLine + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
585
+ process.stdout.write("ay - agent-yes CLI\n\nManagement:\n ay ls [keyword] list running agents\n ay tail [-f] [-n N] <keyword> last N lines (96), -f to follow\n ay read <keyword> [page opts] paginate: --last/--head N, --range A:B,\n --before-line L [--limit N]\n ay cat <keyword> full log\n ay head <keyword> first N lines\n ay send <keyword> <msg> send a message\n ay attach <keyword> interactive attach (detach: Ctrl-\\)\n ay stop <keyword> graceful shutdown (/exit for claude/codex)\n ay exit <keyword> [reason] graceful shutdown, recording who/why (= 'ay send <kw> exit')\n ay status <keyword> agent status snapshot\n ay result <keyword> [--wait] pull an agent's structured result envelope\n ay result set '<json>' (inside an agent) deposit your result envelope\n ay reap kill process groups leaked by dead agents\n\nRemote:\n" + setupLine + " ay schedule <when> <cli> -- <msg> run an agent on a schedule (HH:MM or cron)\n ay serve [--port N] start HTTP API server (prints token)\n ay serve status show serve daemon/server status\n ay remote add <alias> http://<token>@<host>:<port>\n ay remote ls / rm <alias> manage saved remotes\n ay ls <token>@<host>:<port> connect inline (no alias needed)\n ay send <token>@<host>:<port>:<kw> <msg>\n\nRun an agent:\n ay [claude|codex|gemini|...] [options] -- [prompt]\n ay claude -- \"fix the bug in auth.ts\"\n ay claude --help full agent-runner options\n\nLabs (examples at https://github.com/snomiao/agent-yes/tree/main/lab):\n local-role-play/ designer + builder on one machine\n http-remote/ ay serve remote access demo\n p2p-pairing/ libp2p P2P (needs: cargo build --features swarm)\n");
584
586
  return 0;
585
587
  }
586
588
  function matchKeyword(record, keyword) {
@@ -1824,6 +1826,12 @@ async function cmdSend(rest) {
1824
1826
  if (sender.agent) throw new Error(`${what}.\n Confirm it's the right agent first: ay tail ${record.pid}\n then resend, or pass --force to override.`);
1825
1827
  process.stderr.write(`warning: ${what} — make sure this is the agent you meant (ay tail ${record.pid}).\n`);
1826
1828
  }
1829
+ if (isExitRequest(body)) {
1830
+ const reason = sender.agent ? `requested by ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)}` : `requested via 'ay send ${keyword} exit'`;
1831
+ const { strategy } = await gracefulExitAgent(record, reason);
1832
+ process.stdout.write(`pid ${record.pid} (${record.cli}): exit requested — sent ${strategy} (${reason})\n`);
1833
+ return 0;
1834
+ }
1827
1835
  const fullBody = (sender.agent ? `[from ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)} — reply: ay send ${sender.agent.pid} "..."]\n` : "") + body;
1828
1836
  if (fullBody && trailing) {
1829
1837
  await writeToIpc(fifoPath, fullBody);
@@ -1986,6 +1994,74 @@ async function cmdStop(rest) {
1986
1994
  process.stderr.write(`\n ay status ${record.pid} # confirm it exited\n ay ls --all # see exit codes\n`);
1987
1995
  return 0;
1988
1996
  }
1997
+ /** A `send` body that is exactly the exit word (not a sentence that merely
1998
+ * contains it). Bare "exit" and the literal "/exit" both qualify. */
1999
+ function isExitRequest(body) {
2000
+ const t = body.trim().toLowerCase();
2001
+ return t === "exit" || t === "/exit";
2002
+ }
2003
+ /**
2004
+ * Gracefully terminate a live agent and record WHY in its note (the audit trail
2005
+ * shown by `ay ls`). Sends the CLI's graceful-exit command (e.g. claude's
2006
+ * `/exit`) or a double-Ctrl+C fallback. `reason` is agent-yes metadata — claude's
2007
+ * `/exit` takes no argument, so the reason is the note, not appended to `/exit`.
2008
+ * Shared by `ay exit` and by `ay send <kw> exit`'s routing.
2009
+ */
2010
+ async function gracefulExitAgent(record, reason) {
2011
+ if (!record.fifo_file) throw new Error(`pid ${record.pid}: no fifo_file — cannot send shutdown command`);
2012
+ await writeNote(record.pid, `↩ exit — ${reason}`).catch(() => {});
2013
+ const fifoPath = record.fifo_file;
2014
+ const graceful = GRACEFUL_EXIT_COMMANDS[record.cli];
2015
+ if (graceful) {
2016
+ await writeToIpc(fifoPath, graceful);
2017
+ await new Promise((r) => setTimeout(r, 200));
2018
+ await writeToIpc(fifoPath, "\r");
2019
+ return { strategy: `'${graceful}' + Enter` };
2020
+ }
2021
+ await writeToIpc(fifoPath, "");
2022
+ await new Promise((r) => setTimeout(r, 200));
2023
+ await writeToIpc(fifoPath, "");
2024
+ return { strategy: `double Ctrl+C (no known /exit for cli "${record.cli}")` };
2025
+ }
2026
+ async function cmdExit(rest) {
2027
+ const argv = await yargs(rest).usage("Usage: ay exit <keyword> [reason]").option("all", {
2028
+ type: "boolean",
2029
+ default: false,
2030
+ description: "Include exited agents"
2031
+ }).option("latest", {
2032
+ type: "boolean",
2033
+ default: false,
2034
+ description: "Use most recent match"
2035
+ }).option("cwd", {
2036
+ type: "string",
2037
+ description: "Restrict to agents under this dir"
2038
+ }).help(false).version(false).exitProcess(false).parseAsync();
2039
+ const opts = {
2040
+ all: argv.all,
2041
+ active: false,
2042
+ json: false,
2043
+ latest: argv.latest,
2044
+ cwdScope: typeof argv.cwd === "string" ? path.resolve(argv.cwd) : null
2045
+ };
2046
+ const keyword = argv._[0] !== void 0 ? String(argv._[0]) : void 0;
2047
+ if (!keyword) throw new Error("usage: ay exit <keyword> [reason]");
2048
+ const reasonArg = argv._.slice(1).map(String).join(" ").trim();
2049
+ const record = await resolveOne(keyword, opts);
2050
+ if (!isPidAlive(record.pid)) {
2051
+ await updateGlobalPidStatus(record.pid, {
2052
+ status: "exited",
2053
+ exit_reason: "already-stopped"
2054
+ }).catch(() => {});
2055
+ process.stdout.write(`pid ${record.pid} (${record.cli}) already stopped — marked exited\n`);
2056
+ return 0;
2057
+ }
2058
+ const sender = await senderContext();
2059
+ const reason = reasonArg || (sender.agent ? `requested by ${sender.agent.cli} #${sender.agent.pid} @ ${shortenPath(sender.agent.cwd)}` : "manual");
2060
+ const { strategy } = await gracefulExitAgent(record, reason);
2061
+ process.stdout.write(`exiting pid ${record.pid} (${record.cli}) via ${strategy} — ${reason}\n`);
2062
+ process.stderr.write(`\n ay status ${record.pid} # confirm it exited\n`);
2063
+ return 0;
2064
+ }
1989
2065
  async function cmdAttach(rest) {
1990
2066
  const argv = await yargs(rest).usage("Usage: ay attach <keyword> [--escape ctrl-\\]").option("escape", {
1991
2067
  type: "string",
@@ -2504,5 +2580,5 @@ async function cmdResultSet(rest) {
2504
2580
  }
2505
2581
 
2506
2582
  //#endregion
2507
- export { writeToIpc as C, stopTipForCli as S, renderRawLogLines as _, cursorAbs as a, runSubcommand as b, extractTaskCounts as c, isPidAlive as d, isSubcommand as f, renderRawLog as g, readNotes as h, controlCodeFromName as i, finalizedLines as l, matchKeyword as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, listRecords as p, cmdHelp as r, extractNeedsInput as s, GRACEFUL_EXIT_COMMANDS as t, isAgentStuck as u, resolveOne as v, snapshotStatus as x, resolveReadWindow as y };
2508
- //# sourceMappingURL=subcommands-dbx7EoK-.js.map
2583
+ export { stopTipForCli as C, snapshotStatus as S, renderRawLog as _, cursorAbs as a, resolveReadWindow as b, extractTaskCounts as c, isExitRequest as d, isPidAlive as f, readNotes as g, matchKeyword as h, controlCodeFromName as i, finalizedLines as l, listRecords as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, isSubcommand as p, cmdHelp as r, extractNeedsInput as s, GRACEFUL_EXIT_COMMANDS as t, isAgentStuck as u, renderRawLogLines as v, writeToIpc as w, runSubcommand as x, resolveOne as y };
2584
+ //# sourceMappingURL=subcommands-OKH2CsBu.js.map
@@ -1,5 +1,5 @@
1
1
  import { n as logger, t as addTransport } from "./logger-CDIsZ-Pp.js";
2
- import { r as getInstalledPackage } from "./versionChecker-DMpCuaZe.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-Dl2LrFFo.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-_eJa3DaX.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-V4qvXgAw.js";
5
5
  import { t as PidStore } from "./pidStore-fqXqTKkh.js";
@@ -1788,4 +1788,4 @@ function sleep(ms) {
1788
1788
 
1789
1789
  //#endregion
1790
1790
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1791
- //# sourceMappingURL=ts-CMMwNq--.js.map
1791
+ //# sourceMappingURL=ts-DkAVuuIK.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "agent-yes";
10
- var version = "1.141.0";
10
+ var version = "1.142.0";
11
11
 
12
12
  //#endregion
13
13
  //#region ts/versionChecker.ts
@@ -215,4 +215,4 @@ async function displayVersion() {
215
215
 
216
216
  //#endregion
217
217
  export { versionString as i, displayVersion as n, getInstalledPackage as r, checkAndAutoUpdate as t };
218
- //# sourceMappingURL=versionChecker-DMpCuaZe.js.map
218
+ //# sourceMappingURL=versionChecker-Dl2LrFFo.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.141.0",
3
+ "version": "1.142.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",
@@ -717,6 +717,82 @@ describe("subcommands.cmdSend writes bytes to FIFO", () => {
717
717
  const { controlCodeFromName } = await loadModule();
718
718
  expect(controlCodeFromName("none")).toBe("");
719
719
  });
720
+
721
+ it.skipIf(!itUnix)(
722
+ "routes a bare 'exit' to the graceful /exit, not the literal word",
723
+ async () => {
724
+ const { runSubcommand } = await loadModule();
725
+ const { appendGlobalPid } = await import("./globalPidIndex.ts");
726
+ const { spawnSync } = await import("child_process");
727
+ const tmp = await mkdtemp(path.join(tmpdir(), "ay-fifo-"));
728
+ try {
729
+ const fifo = path.join(tmp, "exit.fifo");
730
+ if (spawnSync("mkfifo", [fifo]).status !== 0) return;
731
+ const fs = await import("fs");
732
+ const rdwrFd = fs.openSync(fifo, fs.constants.O_RDWR);
733
+ await appendGlobalPid({
734
+ pid: process.pid,
735
+ cli: "claude",
736
+ prompt: null,
737
+ cwd: process.cwd(),
738
+ log_file: null,
739
+ fifo_file: fifo,
740
+ status: "active",
741
+ exit_code: null,
742
+ exit_reason: null,
743
+ started_at: Date.now(),
744
+ });
745
+ const stdout: string[] = [];
746
+ const orig = process.stdout.write.bind(process.stdout);
747
+ (process.stdout as any).write = (s: any) => (stdout.push(String(s)), true);
748
+ const savedAyPid = process.env.AGENT_YES_PID;
749
+ delete process.env.AGENT_YES_PID;
750
+ try {
751
+ const code = await runSubcommand([
752
+ "bun",
753
+ "cli.js",
754
+ "send",
755
+ String(process.pid),
756
+ "exit",
757
+ "--force",
758
+ ]);
759
+ expect(code).toBe(0);
760
+ expect(stdout.join("")).toMatch(/exit requested/);
761
+ } finally {
762
+ process.stdout.write = orig;
763
+ if (savedAyPid !== undefined) process.env.AGENT_YES_PID = savedAyPid;
764
+ }
765
+ const buf = Buffer.alloc(4096);
766
+ const n = fs.readSync(rdwrFd, buf, 0, buf.length, null);
767
+ // The real `/exit` command + Enter — NOT the literal "exit\r" that claude ignores.
768
+ expect(buf.subarray(0, n).toString()).toBe("/exit\r");
769
+ fs.closeSync(rdwrFd);
770
+ } finally {
771
+ await rm(tmp, { recursive: true, force: true }).catch(() => null);
772
+ }
773
+ },
774
+ );
775
+ });
776
+
777
+ describe("subcommands.isExitRequest", () => {
778
+ it("matches the bare exit word and the literal /exit (any case, trimmed)", async () => {
779
+ const { isExitRequest } = await loadModule();
780
+ for (const s of ["exit", "/exit", " exit ", "EXIT", "/Exit", "\nexit\n"]) {
781
+ expect(isExitRequest(s)).toBe(true);
782
+ }
783
+ });
784
+ it("does NOT match a sentence that merely contains 'exit'", async () => {
785
+ const { isExitRequest } = await loadModule();
786
+ for (const s of [
787
+ "please exit now",
788
+ "exit the loop after step 3",
789
+ "do not exit",
790
+ "exiting",
791
+ "",
792
+ ]) {
793
+ expect(isExitRequest(s)).toBe(false);
794
+ }
795
+ });
720
796
  });
721
797
 
722
798
  describe("subcommands.writeToIpc reliable delivery", () => {
package/ts/subcommands.ts CHANGED
Binary file
@@ -1,8 +0,0 @@
1
- import "./ts-CMMwNq--.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DMpCuaZe.js";
4
- import "./pidStore-fqXqTKkh.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CnBoRIDN.js";
7
-
8
- export { SUPPORTED_CLIS };
@@ -1,7 +0,0 @@
1
- import "./logger-CDIsZ-Pp.js";
2
- import "./globalPidIndex-DlmmJlO8.js";
3
- import "./configShared-C1C04bbq.js";
4
- import "./remotes-PKKjfTI1.js";
5
- import { C as writeToIpc, S as stopTipForCli, _ as renderRawLogLines, a as cursorAbs, b as runSubcommand, c as extractTaskCounts, d as isPidAlive, f as isSubcommand, g as renderRawLog, h as readNotes, i as controlCodeFromName, l as finalizedLines, m as matchKeyword, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as listRecords, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as resolveOne, x as snapshotStatus, y as resolveReadWindow } from "./subcommands-dbx7EoK-.js";
6
-
7
- export { cmdHelp, isSubcommand, runSubcommand };