claude-yes 1.166.0 → 1.167.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.
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-CXxZiZKE.js";
1
+ import { t as CLIS_CONFIG } from "./ts-s3wYccKf.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-Xem8nliL.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-DFBATC85.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-s3wYccKf.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-BvR6tV3u.js";
4
+ import "./pidStore-BfoBWUjc.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DFBATC85.js";
7
+
8
+ export { SUPPORTED_CLIS };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-uqM2YYA7.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-Dv5DhRs3.js";
4
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-BvR6tV3u.js";
5
5
  import { argv } from "process";
6
6
  import { execFileSync, spawn } from "child_process";
7
7
  import ms from "ms";
@@ -481,9 +481,9 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
481
481
  const rawArg = process.argv[2];
482
482
  const managerCommands = !invokedCliName(process.argv);
483
483
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
484
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BnsjTNpy.js");
484
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-B-WoBVhk.js");
485
485
  if (isHelpFlag && process.argv.length === 3) {
486
- cmdHelp(managerCommands);
486
+ await cmdHelp(managerCommands);
487
487
  process.exit(0);
488
488
  }
489
489
  if (isSubcommand(rawArg, managerCommands)) {
@@ -523,7 +523,7 @@ if (config.useRust) {
523
523
  }
524
524
  }
525
525
  if (rustBinary) {
526
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-ClQgRKQu.js");
526
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-DLFqRE8W.js");
527
527
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
528
528
  if (config.verbose) {
529
529
  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-CXxZiZKE.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-s3wYccKf.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-Dv5DhRs3.js";
3
+ import "./versionChecker-BvR6tV3u.js";
4
4
  import "./pidStore-BfoBWUjc.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts-CXxZiZKE.js";
1
+ import "./ts-s3wYccKf.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-Dv5DhRs3.js";
3
+ import "./versionChecker-BvR6tV3u.js";
4
4
  import "./pidStore-BfoBWUjc.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Xem8nliL.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DFBATC85.js";
7
7
  import { l as resolveSpawnCwd } from "./workspaceConfig-D3OH7and.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-CwAX3y8s.js.map
144
+ //# sourceMappingURL=schedule-C2RMA3qm.js.map
@@ -1,16 +1,16 @@
1
- import "./ts-CXxZiZKE.js";
1
+ import "./ts-s3wYccKf.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-Dv5DhRs3.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-BvR6tV3u.js";
4
4
  import "./pidStore-BfoBWUjc.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-CEyhl0WH.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Xem8nliL.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-DFBATC85.js";
9
9
  import "./e2e-Bfw7qL9O.js";
10
10
  import "./webrtcLink-CBZkZ-LT.js";
11
11
  import "./remotes-T6nf0t3K.js";
12
12
  import { c as isProvisionAllowed, i as getSpawnHook, l as resolveSpawnCwd, r as getProvisionRoot, s as hasSpawnHook } from "./workspaceConfig-D3OH7and.js";
13
- import { A as writeToIpc, D as snapshotStatus, S as resolveOne, b as renderRawLog, c as extractNeedsInput, h as listRecords, i as controlCodeFromName, l as extractTaskCounts, o as deriveLiveStatus, v as readNotes, y as readPtysize } from "./subcommands-BY0mIDo9.js";
13
+ import { A as writeToIpc, D as snapshotStatus, S as resolveOne, b as renderRawLog, c as extractNeedsInput, h as listRecords, i as controlCodeFromName, l as extractTaskCounts, o as deriveLiveStatus, v as readNotes, y as readPtysize } from "./subcommands-DWeo7ZLc.js";
14
14
  import { r as spawnRejectionReason } from "./spawnGate-B_VDMXYL.js";
15
15
  import yargs from "yargs";
16
16
  import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
@@ -1445,4 +1445,4 @@ Options:
1445
1445
 
1446
1446
  //#endregion
1447
1447
  export { cmdServe };
1448
- //# sourceMappingURL=serve-DeJBMba0.js.map
1448
+ //# sourceMappingURL=serve-mLqsRwfR.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-DeJBMba0.js");
35
+ const { cmdServe } = await import("./serve-mLqsRwfR.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-hWG3ZBG3.js.map
45
+ //# sourceMappingURL=setup-BtqKZx3q.js.map
@@ -4,6 +4,6 @@ import "./configShared-CEyhl0WH.js";
4
4
  import "./e2e-Bfw7qL9O.js";
5
5
  import "./webrtcLink-CBZkZ-LT.js";
6
6
  import "./remotes-T6nf0t3K.js";
7
- import { A as writeToIpc, C as resolveReadWindow, D as snapshotStatus, E as runSubcommand, O as stopTipForCli, S as resolveOne, T as restartHintLines, _ as menuSelectKeys, a as cursorAbs, b as renderRawLog, c as extractNeedsInput, d as isAgentStuck, f as isExitRequest, g as matchKeyword, h as listRecords, i as controlCodeFromName, k as writeKeysPaced, l as extractTaskCounts, m as isSubcommand, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isPidAlive, r as cmdHelp, s as extractMenu, t as GRACEFUL_EXIT_COMMANDS, u as finalizedLines, v as readNotes, w as resolveResumeArgs, x as renderRawLogLines, y as readPtysize } from "./subcommands-BY0mIDo9.js";
7
+ import { A as writeToIpc, C as resolveReadWindow, D as snapshotStatus, E as runSubcommand, O as stopTipForCli, S as resolveOne, T as restartHintLines, _ as menuSelectKeys, a as cursorAbs, b as renderRawLog, c as extractNeedsInput, d as isAgentStuck, f as isExitRequest, g as matchKeyword, h as listRecords, i as controlCodeFromName, k as writeKeysPaced, l as extractTaskCounts, m as isSubcommand, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isPidAlive, r as cmdHelp, s as extractMenu, t as GRACEFUL_EXIT_COMMANDS, u as finalizedLines, v as readNotes, w as resolveResumeArgs, x as renderRawLogLines, y as readPtysize } from "./subcommands-DWeo7ZLc.js";
8
8
 
9
9
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -597,15 +597,15 @@ async function runSubcommand(argv) {
597
597
  case "restart": return await cmdRestart(rest);
598
598
  case "note": return await cmdNote(rest);
599
599
  case "serve": {
600
- const { cmdServe } = await import("./serve-DeJBMba0.js");
600
+ const { cmdServe } = await import("./serve-mLqsRwfR.js");
601
601
  return cmdServe(rest);
602
602
  }
603
603
  case "setup": {
604
- const { cmdSetup } = await import("./setup-hWG3ZBG3.js");
604
+ const { cmdSetup } = await import("./setup-BtqKZx3q.js");
605
605
  return cmdSetup(rest);
606
606
  }
607
607
  case "schedule": {
608
- const { cmdSchedule } = await import("./schedule-CwAX3y8s.js");
608
+ const { cmdSchedule } = await import("./schedule-C2RMA3qm.js");
609
609
  return cmdSchedule(rest);
610
610
  }
611
611
  case "remote": {
@@ -624,9 +624,29 @@ async function runSubcommand(argv) {
624
624
  return 1;
625
625
  }
626
626
  }
627
- function cmdHelp(managerCommands = true) {
627
+ /**
628
+ * The banner shown by `ay help` / `ay -h` when this process is itself running
629
+ * inside an agent (`AGENT_YES_PID` set — see resolveSender). Answers the three
630
+ * things a nested agent actually needs: who am I, who spawned me, and how do I
631
+ * drive sub-agents of my own — so it doesn't have to rediscover the fan-out
632
+ * primitives (spawn / ay ls forest / ay ls --watch) from scratch every session.
633
+ */
634
+ async function buildAgentContextSection(self) {
635
+ const hasParentPid = typeof self.parent_pid === "number" && self.parent_pid > 0;
636
+ const parent = hasParentPid ? (await listRecords(void 0, {
637
+ all: true,
638
+ active: false,
639
+ json: false,
640
+ latest: false,
641
+ cwdScope: null
642
+ })).find((r) => r.wrapper_pid === self.parent_pid) : void 0;
643
+ return `You are running inside an agent:\n ${`You are agent pid ${self.pid} (${self.cli}) in ${shortenPath(self.cwd)}.`}\n ${!hasParentPid ? `Top-level agent — no parent (started from a human shell or scheduler).` : parent ? `Spawned by agent pid ${parent.pid} (${parent.cli}) in ${shortenPath(parent.cwd)}.` : `Nested under a parent (wrapper pid ${self.parent_pid}) whose record isn't in the local registry.`}\n\nAs an agent, you can:\n Spawn a sub-agent:\n ay <cli> -- "<prompt>" auto-links as your child\n ay claude --model sonnet --advisor opus -- "<prompt>" routine task\n ay claude --model opus --advisor fable -- "<prompt>" complex task\n (pick --model by task complexity so easy tasks don't cost like hard ones;\n --advisor is a claude-cli flag — only takes effect for claude/cy)\n List agents (your children nest under your own pid in the tree):\n ay ls --cwd ${shortenPath(self.cwd)}\n Watch agent state changes, scoped to your workspace:\n ay ls --watch --cwd ${shortenPath(self.cwd)}\n (NDJSON stream of state changes across every matched agent — one watcher\n for the whole fan-out instead of N \`ay status --watch\`es)\n Read one sub-agent's output:\n ay tail -f <pid> follow live output (no single command tails\n many agents' content at once yet — loop\n \`ay ls --json\` pids into per-pid \`ay tail\`)\n\n`;
644
+ }
645
+ async function cmdHelp(managerCommands = true) {
628
646
  const setupLine = managerCommands ? ` ay setup guided setup: pick a workspace, share to agent-yes.com\n` : ``;
629
- 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 key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\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 restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\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");
647
+ const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
648
+ const agentSection = self ? await buildAgentContextSection(self) : "";
649
+ process.stdout.write(agentSection + "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 key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n ay select <keyword> <N> pick option N of a needs_input selection menu\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 restart <keyword> [--fresh] stop (if live) + relaunch resuming the session; --fresh replays the prompt\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");
630
650
  return 0;
631
651
  }
632
652
  function matchKeyword(record, keyword) {
@@ -3005,4 +3025,4 @@ async function cmdResultSet(rest) {
3005
3025
 
3006
3026
  //#endregion
3007
3027
  export { writeToIpc as A, resolveReadWindow as C, snapshotStatus as D, runSubcommand as E, stopTipForCli as O, resolveOne as S, restartHintLines as T, menuSelectKeys as _, cursorAbs as a, renderRawLog as b, extractNeedsInput as c, isAgentStuck as d, isExitRequest as f, matchKeyword as g, listRecords as h, controlCodeFromName as i, writeKeysPaced as k, extractTaskCounts as l, isSubcommand as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, isPidAlive as p, cmdHelp as r, extractMenu as s, GRACEFUL_EXIT_COMMANDS as t, finalizedLines as u, readNotes as v, resolveResumeArgs as w, renderRawLogLines as x, readPtysize as y };
3008
- //# sourceMappingURL=subcommands-BY0mIDo9.js.map
3028
+ //# sourceMappingURL=subcommands-DWeo7ZLc.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-Dv5DhRs3.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-BvR6tV3u.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-BfoBWUjc.js";
@@ -1803,4 +1803,4 @@ function sleep(ms) {
1803
1803
 
1804
1804
  //#endregion
1805
1805
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1806
- //# sourceMappingURL=ts-CXxZiZKE.js.map
1806
+ //# sourceMappingURL=ts-s3wYccKf.js.map
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
 
8
8
  //#region package.json
9
9
  var name = "claude-yes";
10
- var version = "1.166.0";
10
+ var version = "1.167.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-Dv5DhRs3.js.map
218
+ //# sourceMappingURL=versionChecker-BvR6tV3u.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.166.0",
3
+ "version": "1.167.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",
package/ts/cli.ts CHANGED
@@ -30,7 +30,7 @@ import { buildRustArgs } from "./buildRustArgs.ts";
30
30
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
31
31
  const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands.ts");
32
32
  if (isHelpFlag && process.argv.length === 3) {
33
- cmdHelp(managerCommands);
33
+ await cmdHelp(managerCommands);
34
34
  process.exit(0);
35
35
  }
36
36
  if (isSubcommand(rawArg, managerCommands)) {
@@ -113,25 +113,134 @@ describe("subcommands.isSubcommand", () => {
113
113
  });
114
114
 
115
115
  describe("subcommands.cmdHelp", () => {
116
- it("hides the manager-only `setup` line for cli-bound aliases", async () => {
116
+ const capture = async (managerCommands?: boolean) => {
117
117
  const { cmdHelp } = await loadModule();
118
- const capture = (managerCommands?: boolean) => {
119
- let out = "";
120
- const spy = vi.spyOn(process.stdout, "write").mockImplementation((s: unknown) => {
121
- out += String(s);
122
- return true;
123
- });
124
- try {
125
- cmdHelp(managerCommands);
126
- } finally {
127
- spy.mockRestore();
128
- }
129
- return out;
130
- };
131
- expect(capture(true)).toContain("ay setup"); // manager
132
- expect(capture()).toContain("ay setup"); // default = manager
133
- expect(capture(false)).not.toContain("ay setup"); // cli-bound alias (cy)
134
- expect(capture(false)).toContain("ay ls"); // universal commands still shown
118
+ let out = "";
119
+ const spy = vi.spyOn(process.stdout, "write").mockImplementation((s: unknown) => {
120
+ out += String(s);
121
+ return true;
122
+ });
123
+ try {
124
+ await cmdHelp(managerCommands);
125
+ } finally {
126
+ spy.mockRestore();
127
+ }
128
+ return out;
129
+ };
130
+
131
+ it("hides the manager-only `setup` line for cli-bound aliases", async () => {
132
+ expect(await capture(true)).toContain("ay setup"); // manager
133
+ expect(await capture()).toContain("ay setup"); // default = manager
134
+ expect(await capture(false)).not.toContain("ay setup"); // cli-bound alias (cy)
135
+ expect(await capture(false)).toContain("ay ls"); // universal commands still shown
136
+ });
137
+
138
+ it("stays plain for a human shell (no AGENT_YES_PID)", async () => {
139
+ const out = await capture();
140
+ expect(out).not.toContain("You are running inside an agent");
141
+ });
142
+
143
+ it("prints self + parent identity and sub-agent guidance when nested in an agent", async () => {
144
+ const { appendGlobalPid } = await import("./globalPidIndex.ts");
145
+ const parentWrapperPid = 555001;
146
+ const selfWrapperPid = 555002;
147
+ await appendGlobalPid({
148
+ pid: 900001,
149
+ cli: "codex",
150
+ prompt: "orchestrate the migration",
151
+ cwd: "/work/parent",
152
+ log_file: null,
153
+ fifo_file: null,
154
+ status: "active",
155
+ exit_code: null,
156
+ exit_reason: null,
157
+ started_at: Date.now(),
158
+ wrapper_pid: parentWrapperPid,
159
+ });
160
+ await appendGlobalPid({
161
+ pid: process.pid,
162
+ cli: "claude",
163
+ prompt: "fix the failing test",
164
+ cwd: "/work/parent/child",
165
+ log_file: null,
166
+ fifo_file: null,
167
+ status: "active",
168
+ exit_code: null,
169
+ exit_reason: null,
170
+ started_at: Date.now(),
171
+ wrapper_pid: selfWrapperPid,
172
+ parent_pid: parentWrapperPid,
173
+ });
174
+ const saved = process.env.AGENT_YES_PID;
175
+ process.env.AGENT_YES_PID = String(selfWrapperPid);
176
+ try {
177
+ const out = await capture();
178
+ expect(out).toContain("You are running inside an agent");
179
+ expect(out).toContain(`You are agent pid ${process.pid} (claude)`);
180
+ expect(out).toContain(`Spawned by agent pid 900001 (codex)`);
181
+ expect(out).toContain("Spawn a sub-agent");
182
+ expect(out).toContain(`ay ls --cwd /work/parent/child`);
183
+ expect(out).toContain(`ay ls --watch --cwd /work/parent/child`);
184
+ } finally {
185
+ if (saved === undefined) delete process.env.AGENT_YES_PID;
186
+ else process.env.AGENT_YES_PID = saved;
187
+ }
188
+ });
189
+
190
+ it("reports a nested-but-unresolved parent distinctly from top-level", async () => {
191
+ const { appendGlobalPid } = await import("./globalPidIndex.ts");
192
+ const selfWrapperPid = 555004;
193
+ await appendGlobalPid({
194
+ pid: process.pid,
195
+ cli: "claude",
196
+ prompt: null,
197
+ cwd: process.cwd(),
198
+ log_file: null,
199
+ fifo_file: null,
200
+ status: "active",
201
+ exit_code: null,
202
+ exit_reason: null,
203
+ started_at: Date.now(),
204
+ wrapper_pid: selfWrapperPid,
205
+ parent_pid: 999999999, // no record ever registered for this wrapper pid
206
+ });
207
+ const saved = process.env.AGENT_YES_PID;
208
+ process.env.AGENT_YES_PID = String(selfWrapperPid);
209
+ try {
210
+ const out = await capture();
211
+ expect(out).not.toContain("Top-level agent");
212
+ expect(out).toContain("Nested under a parent (wrapper pid 999999999)");
213
+ } finally {
214
+ if (saved === undefined) delete process.env.AGENT_YES_PID;
215
+ else process.env.AGENT_YES_PID = saved;
216
+ }
217
+ });
218
+
219
+ it("reports top-level (no parent) when parent_pid is absent", async () => {
220
+ const { appendGlobalPid } = await import("./globalPidIndex.ts");
221
+ const selfWrapperPid = 555003;
222
+ await appendGlobalPid({
223
+ pid: process.pid,
224
+ cli: "claude",
225
+ prompt: null,
226
+ cwd: process.cwd(),
227
+ log_file: null,
228
+ fifo_file: null,
229
+ status: "active",
230
+ exit_code: null,
231
+ exit_reason: null,
232
+ started_at: Date.now(),
233
+ wrapper_pid: selfWrapperPid,
234
+ });
235
+ const saved = process.env.AGENT_YES_PID;
236
+ process.env.AGENT_YES_PID = String(selfWrapperPid);
237
+ try {
238
+ const out = await capture();
239
+ expect(out).toContain("Top-level agent");
240
+ } finally {
241
+ if (saved === undefined) delete process.env.AGENT_YES_PID;
242
+ else process.env.AGENT_YES_PID = saved;
243
+ }
135
244
  });
136
245
  });
137
246
 
package/ts/subcommands.ts CHANGED
@@ -387,14 +387,77 @@ export async function runSubcommand(argv: string[]): Promise<number | null> {
387
387
  // ay help
388
388
  // ---------------------------------------------------------------------------
389
389
 
390
- export function cmdHelp(managerCommands = true): number {
390
+ /**
391
+ * The banner shown by `ay help` / `ay -h` when this process is itself running
392
+ * inside an agent (`AGENT_YES_PID` set — see resolveSender). Answers the three
393
+ * things a nested agent actually needs: who am I, who spawned me, and how do I
394
+ * drive sub-agents of my own — so it doesn't have to rediscover the fan-out
395
+ * primitives (spawn / ay ls forest / ay ls --watch) from scratch every session.
396
+ */
397
+ async function buildAgentContextSection(self: GlobalPidRecord): Promise<string> {
398
+ const hasParentPid = typeof self.parent_pid === "number" && self.parent_pid > 0;
399
+ const parent = hasParentPid
400
+ ? (
401
+ await listRecords(undefined, {
402
+ all: true,
403
+ active: false,
404
+ json: false,
405
+ latest: false,
406
+ cwdScope: null,
407
+ })
408
+ ).find((r) => r.wrapper_pid === self.parent_pid)
409
+ : undefined;
410
+
411
+ const whoAmI = `You are agent pid ${self.pid} (${self.cli}) in ${shortenPath(self.cwd)}.`;
412
+ // Three distinct states: no parent at all (top-level); a parent_pid whose
413
+ // record we can resolve; or a parent_pid we can't resolve (its record aged
414
+ // out / lives on a remote) — that last case is still nested, just unknown,
415
+ // so it must not collapse into the "top-level" line.
416
+ const parentLine = !hasParentPid
417
+ ? `Top-level agent — no parent (started from a human shell or scheduler).`
418
+ : parent
419
+ ? `Spawned by agent pid ${parent.pid} (${parent.cli}) in ${shortenPath(parent.cwd)}.`
420
+ : `Nested under a parent (wrapper pid ${self.parent_pid}) whose record isn't in the local registry.`;
421
+
422
+ return (
423
+ `You are running inside an agent:\n` +
424
+ ` ${whoAmI}\n` +
425
+ ` ${parentLine}\n` +
426
+ `\n` +
427
+ `As an agent, you can:\n` +
428
+ ` Spawn a sub-agent:\n` +
429
+ ` ay <cli> -- "<prompt>" auto-links as your child\n` +
430
+ ` ay claude --model sonnet --advisor opus -- "<prompt>" routine task\n` +
431
+ ` ay claude --model opus --advisor fable -- "<prompt>" complex task\n` +
432
+ ` (pick --model by task complexity so easy tasks don't cost like hard ones;\n` +
433
+ ` --advisor is a claude-cli flag — only takes effect for claude/cy)\n` +
434
+ ` List agents (your children nest under your own pid in the tree):\n` +
435
+ ` ay ls --cwd ${shortenPath(self.cwd)}\n` +
436
+ ` Watch agent state changes, scoped to your workspace:\n` +
437
+ ` ay ls --watch --cwd ${shortenPath(self.cwd)}\n` +
438
+ ` (NDJSON stream of state changes across every matched agent — one watcher\n` +
439
+ ` for the whole fan-out instead of N \`ay status --watch\`es)\n` +
440
+ ` Read one sub-agent's output:\n` +
441
+ ` ay tail -f <pid> follow live output (no single command tails\n` +
442
+ ` many agents' content at once yet — loop\n` +
443
+ ` \`ay ls --json\` pids into per-pid \`ay tail\`)\n` +
444
+ `\n`
445
+ );
446
+ }
447
+
448
+ export async function cmdHelp(managerCommands = true): Promise<number> {
391
449
  // `setup` is manager-only — hide it when invoked through a cli-bound alias
392
450
  // (cy/claude-yes/…), where `cy setup` runs the agent instead of managing the host.
393
451
  const setupLine = managerCommands
394
452
  ? ` ay setup guided setup: pick a workspace, share to agent-yes.com\n`
395
453
  : ``;
454
+ // Only agents carry AGENT_YES_PID — a human shell never sets it — so this
455
+ // section is skipped entirely (no async work at all) for interactive use.
456
+ const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
457
+ const agentSection = self ? await buildAgentContextSection(self) : "";
396
458
  process.stdout.write(
397
- `ay - agent-yes CLI\n` +
459
+ agentSection +
460
+ `ay - agent-yes CLI\n` +
398
461
  `\n` +
399
462
  `Management:\n` +
400
463
  ` ay ls [keyword] list running agents\n` +
@@ -1,8 +0,0 @@
1
- import "./ts-CXxZiZKE.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-Dv5DhRs3.js";
4
- import "./pidStore-BfoBWUjc.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Xem8nliL.js";
7
-
8
- export { SUPPORTED_CLIS };