agent-yes 1.225.0 → 1.226.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.
Files changed (36) hide show
  1. package/dist/{SUPPORTED_CLIS-ldWdY2jy.js → SUPPORTED_CLIS-Cn3T86CJ.js} +3 -3
  2. package/dist/{SUPPORTED_CLIS-nDMA0vtp.js → SUPPORTED_CLIS-Cnig_ONj.js} +2 -2
  3. package/dist/{agentShare-BLl0-SpO.js → agentShare-Cue9mqvJ.js} +3 -3
  4. package/dist/{callback-CCZfvMHZ.js → callback-DyJC_81s.js} +3 -3
  5. package/dist/{callback-CuWuj9gy.js → callback-Lsz9mZiC.js} +3 -3
  6. package/dist/cli.js +12 -8
  7. package/dist/cwdConflictWarn-CnxkLs7T.js +64 -0
  8. package/dist/{expose-CGpMiQxp.js → expose-CKWXAkzK.js} +1 -1
  9. package/dist/{forkNested-BLa3Mz_t.js → forkNested-DJ2QzBsc.js} +1 -1
  10. package/dist/index.js +2 -2
  11. package/dist/{nodeRuntime-DoBOoeUW.js → nodeRuntime-CR0NwnnO.js} +1 -1
  12. package/dist/{notifyDaemon-DF7_EjVu.js → notifyDaemon-BoE83_av.js} +3 -3
  13. package/dist/{openBrowser-D_qKXZNj.js → openBrowser-D9VVE6cq.js} +1 -1
  14. package/dist/{remotes-D-jPl9Fj.js → remotes-BtXom7Rp.js} +2 -2
  15. package/dist/{remotes-C_YlVvr9.js → remotes-DZ_G2wkM.js} +1 -1
  16. package/dist/{rustBinary-DsKB37nM.js → rustBinary-BHXs5BZD.js} +2 -2
  17. package/dist/{schedule-KFStEXIX.js → schedule-Dd26IiBu.js} +6 -6
  18. package/dist/{serve-BBrh1z4b.js → serve-CC1SbBXS.js} +22 -22
  19. package/dist/{setup-DvP49xV5.js → setup-Cz9t2H2g.js} +3 -3
  20. package/dist/{spawnGate-nUBaOXoG.js → spawnGate-CfcpmQmk.js} +1 -1
  21. package/dist/{spawnGate-Vmd1d9OQ.js → spawnGate-ChYCj6Dm.js} +2 -2
  22. package/dist/{subcommands-T3C9EhOr.js → subcommands-Clrx40Id.js} +22 -14
  23. package/dist/{subcommands-DMoKYWpB.js → subcommands-PPDhW0bB.js} +2 -2
  24. package/dist/{todoCli-dk8WNgcS.js → todoCli-D8bL164c.js} +1 -1
  25. package/dist/{tray-CIm-5aWB.js → tray-BZqJtEmL.js} +1 -1
  26. package/dist/{ts-BPtf-Tpg.js → ts-C27ZwDyZ.js} +2 -2
  27. package/dist/{versionChecker-defO9ozI.js → versionChecker-B5TdNLCF.js} +2 -2
  28. package/dist/{webrtcRemote-CWogO7Qa.js → webrtcRemote-C974zjSt.js} +1 -1
  29. package/dist/{workspaceConfig-dHHBQh0F.js → workspaceConfig-BRl-GwSN.js} +1 -1
  30. package/dist/{ws-DLkxWjt9.js → ws-D9H2UXCC.js} +4 -4
  31. package/package.json +1 -1
  32. package/ts/cli.ts +9 -0
  33. package/ts/cwdConflictWarn.spec.ts +60 -0
  34. package/ts/cwdConflictWarn.ts +97 -0
  35. package/ts/subcommands.spec.ts +12 -0
  36. package/ts/subcommands.ts +27 -1
@@ -1,11 +1,11 @@
1
- import "./ts-BPtf-Tpg.js";
1
+ import "./ts-C27ZwDyZ.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-defO9ozI.js";
3
+ import "./versionChecker-B5TdNLCF.js";
4
4
  import "./JsonlStore-Bm5Nj6v_.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
6
  import "./pidStore-DCqpU_bI.js";
7
7
  import "./messageLog-Bb_Ie3Ck.js";
8
8
  import "./todoAutomation-fxtfT_44.js";
9
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-nDMA0vtp.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Cnig_ONj.js";
10
10
 
11
11
  export { SUPPORTED_CLIS };
@@ -1,8 +1,8 @@
1
- import { t as CLIS_CONFIG } from "./ts-BPtf-Tpg.js";
1
+ import { t as CLIS_CONFIG } from "./ts-C27ZwDyZ.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-nDMA0vtp.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-Cnig_ONj.js.map
@@ -2,10 +2,10 @@ import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./messageLog-Bb_Ie3Ck.js";
4
4
  import "./configShared-nnIwDYY-.js";
5
- import { M as resolveOne } from "./subcommands-T3C9EhOr.js";
5
+ import { M as resolveOne } from "./subcommands-Clrx40Id.js";
6
6
  import "./e2e-D5jaSHyY.js";
7
7
  import "./webrtcLink-DknpJQ0y.js";
8
- import "./remotes-D-jPl9Fj.js";
8
+ import "./remotes-BtXom7Rp.js";
9
9
  import { r as startShare } from "./share-BwEUEHfy.js";
10
10
 
11
11
  //#region ts/agentShare.ts
@@ -229,4 +229,4 @@ function transformEvent(rawEvent, agentId, forwarded) {
229
229
 
230
230
  //#endregion
231
231
  export { createScopedShare, listShares, revokeAllShares, revokeShare };
232
- //# sourceMappingURL=agentShare-BLl0-SpO.js.map
232
+ //# sourceMappingURL=agentShare-Cue9mqvJ.js.map
@@ -1,5 +1,5 @@
1
1
  import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
2
- import { M as resolveOne } from "./subcommands-T3C9EhOr.js";
2
+ import { M as resolveOne } from "./subcommands-Clrx40Id.js";
3
3
  import { a as mintCapability, o as parseExpires } from "./callbackCore-CgnOT1Cp.js";
4
4
  import { randomBytes } from "node:crypto";
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -72,7 +72,7 @@ async function resolveBase(flag) {
72
72
  if (flag) return flag;
73
73
  if (process.env.AGENT_YES_CALLBACK_BASE) return process.env.AGENT_YES_CALLBACK_BASE;
74
74
  try {
75
- const { resolveLocalServeUrl } = await import("./serve-BBrh1z4b.js");
75
+ const { resolveLocalServeUrl } = await import("./serve-CC1SbBXS.js");
76
76
  const url = await resolveLocalServeUrl();
77
77
  if (url) return url;
78
78
  } catch {}
@@ -187,4 +187,4 @@ async function cmdCallback(rest) {
187
187
 
188
188
  //#endregion
189
189
  export { loadOrCreateCallbackSecret as i, isCallbackRevoked as n, loadCallbackSecretReadOnly as r, cmdCallback as t };
190
- //# sourceMappingURL=callback-CCZfvMHZ.js.map
190
+ //# sourceMappingURL=callback-DyJC_81s.js.map
@@ -2,11 +2,11 @@ import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./messageLog-Bb_Ie3Ck.js";
4
4
  import "./configShared-nnIwDYY-.js";
5
- import "./subcommands-T3C9EhOr.js";
5
+ import "./subcommands-Clrx40Id.js";
6
6
  import "./e2e-D5jaSHyY.js";
7
7
  import "./webrtcLink-DknpJQ0y.js";
8
- import "./remotes-D-jPl9Fj.js";
8
+ import "./remotes-BtXom7Rp.js";
9
9
  import "./callbackCore-CgnOT1Cp.js";
10
- import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-CCZfvMHZ.js";
10
+ import { i as loadOrCreateCallbackSecret, n as isCallbackRevoked, r as loadCallbackSecretReadOnly, t as cmdCallback } from "./callback-DyJC_81s.js";
11
11
 
12
12
  export { cmdCallback };
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
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, t as checkAndAutoUpdate } from "./versionChecker-defO9ozI.js";
5
- import { n as getRustBinary } from "./rustBinary-DsKB37nM.js";
4
+ import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-B5TdNLCF.js";
5
+ import { n as getRustBinary } from "./rustBinary-BHXs5BZD.js";
6
6
  import { argv } from "process";
7
7
  import { spawn } from "child_process";
8
8
  import ms from "ms";
@@ -285,7 +285,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
285
285
  const rawArg = process.argv[2];
286
286
  const managerCommands = !invokedCliName(process.argv);
287
287
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
288
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DMoKYWpB.js");
288
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-PPDhW0bB.js");
289
289
  if (isHelpFlag && process.argv.length === 3) {
290
290
  await cmdHelp(managerCommands);
291
291
  process.exit(0);
@@ -299,16 +299,16 @@ await checkAndAutoUpdate();
299
299
  logger.info(versionString());
300
300
  const config = parseCliArgs(process.argv);
301
301
  if (config.tray) {
302
- const { startTray } = await import("./tray-CIm-5aWB.js");
302
+ const { startTray } = await import("./tray-BZqJtEmL.js");
303
303
  await startTray();
304
304
  await new Promise(() => {});
305
305
  }
306
306
  {
307
- const { ensureTray } = await import("./tray-CIm-5aWB.js");
307
+ const { ensureTray } = await import("./tray-BZqJtEmL.js");
308
308
  ensureTray();
309
309
  }
310
310
  if (!config.showVersion && !config.appendPrompt && !config.tray) {
311
- const { waitForSpawnCapacity } = await import("./spawnGate-nUBaOXoG.js");
311
+ const { waitForSpawnCapacity } = await import("./spawnGate-CfcpmQmk.js");
312
312
  await waitForSpawnCapacity({
313
313
  onWait: (reason, waitedMs) => {
314
314
  if (config.verbose || waitedMs === 0) console.error(`[agent-yes] spawn gate: ${reason} — waiting…`);
@@ -316,6 +316,10 @@ if (!config.showVersion && !config.appendPrompt && !config.tray) {
316
316
  onProceedAnyway: (reason, waitedMs) => console.error(`[agent-yes] spawn gate: ${reason} — waited ${Math.round(waitedMs / 1e3)}s, proceeding anyway`)
317
317
  });
318
318
  }
319
+ if (!config.showVersion && !config.appendPrompt && !config.tray) {
320
+ const { warnIfCwdOccupied } = await import("./cwdConflictWarn-CnxkLs7T.js");
321
+ await warnIfCwdOccupied(process.argv);
322
+ }
319
323
  if (config.useRust) {
320
324
  let rustBinary;
321
325
  try {
@@ -327,14 +331,14 @@ if (config.useRust) {
327
331
  }
328
332
  }
329
333
  if (rustBinary) {
330
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-ldWdY2jy.js");
334
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-Cn3T86CJ.js");
331
335
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
332
336
  if (config.verbose) {
333
337
  console.log(`[rust] Using binary: ${rustBinary}`);
334
338
  console.log(`[rust] Args: ${rustArgs.join(" ")}`);
335
339
  }
336
340
  const attach = config.attach || process.env.AGENT_YES_ATTACH === "1";
337
- const { shouldForkNested, buildSpawnTutorial, waitForFifo } = await import("./forkNested-BLa3Mz_t.js");
341
+ const { shouldForkNested, buildSpawnTutorial, waitForFifo } = await import("./forkNested-DJ2QzBsc.js");
338
342
  if (shouldForkNested({
339
343
  isTTY: Boolean(process.stdout.isTTY),
340
344
  ayPid: process.env.AGENT_YES_PID,
@@ -0,0 +1,64 @@
1
+ import "./logger-CDIsZ-Pp.js";
2
+ import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
3
+ import path from "path";
4
+
5
+ //#region ts/cwdConflictWarn.ts
6
+ /**
7
+ * Pre-launch warning: another live agent is already working in THIS exact
8
+ * directory. Two agents sharing one working tree clobber each other's edits,
9
+ * builds, and dev-server ports — so we warn (stderr, non-fatal) and hand the
10
+ * user a copy-pasteable git-worktree isolation command.
11
+ *
12
+ * Lives in cli.ts's launch path (before the --rust dispatch) so it covers BOTH
13
+ * the Rust and TypeScript runtimes with one check. The launching agent isn't
14
+ * registered in the pid index yet, so every same-cwd match here is a genuine
15
+ * pre-existing peer, never itself.
16
+ */
17
+ /**
18
+ * POSIX single-quote a shell token so a copy-pasted recovery command survives
19
+ * spaces, quotes, `$`, `;`, `&`, etc. Safe-charset tokens pass through unquoted
20
+ * so the common case (`ay claude -- fix`) stays readable. Empty string → `''`.
21
+ */
22
+ function shQuote(s) {
23
+ if (s === "") return "''";
24
+ if (/^[A-Za-z0-9,._+@%/:=-]+$/.test(s)) return s;
25
+ return `'${s.replace(/'/g, `'\\''`)}'`;
26
+ }
27
+ /**
28
+ * Pure renderer — given the peers already in `cwd`, the cwd, and the original
29
+ * `ay …` command, produce the warning text (or null when there's no conflict).
30
+ * Separated from IO so the message/branching is unit-tested without a live index.
31
+ */
32
+ function formatCwdConflictWarning(occupants, cwd, origCmd) {
33
+ if (occupants.length === 0) return null;
34
+ const wt = `${path.basename(cwd) || "agent"}-work`;
35
+ const list = occupants.slice(0, 3).map((r) => ` ${r.pid} ${r.cli}`).join("\n");
36
+ const more = occupants.length > 3 ? `\n …and ${occupants.length - 3} more` : "";
37
+ const cmd = origCmd.trim() || "ay <cli> …";
38
+ const branch = shQuote(wt);
39
+ const dir = shQuote(`../${wt}`);
40
+ return `\n⚠ ${occupants.length} agent${occupants.length > 1 ? "s are" : " is"} already running in this directory (${cwd}):\n${list}${more}\n Parallel agents in one working tree clobber each other's files, builds, and ports.\n To isolate this run in its own git worktree, cancel (Ctrl-C) and instead:\n git worktree add -b ${branch} ${dir} && cd ${dir} && ${cmd}\n (set AGENT_YES_NO_CWD_WARN=1 to silence)\n\n`;
41
+ }
42
+ /**
43
+ * IO wrapper: discover live same-cwd peers and print the warning to stderr.
44
+ * Best-effort and swallow-all — a discovery hiccup must NEVER block a launch.
45
+ * No-op when AGENT_YES_NO_CWD_WARN=1.
46
+ */
47
+ async function warnIfCwdOccupied(rawArgv) {
48
+ if (process.env.AGENT_YES_NO_CWD_WARN === "1") return;
49
+ try {
50
+ const here = path.resolve(process.cwd());
51
+ const msg = formatCwdConflictWarning((await readGlobalPids({ liveOnly: true })).filter((r) => path.resolve(r.cwd) === here).map((r) => ({
52
+ pid: r.pid,
53
+ cli: r.cli
54
+ })), here, ["ay", ...rawArgv.slice(2)].map(shQuote).join(" "));
55
+ if (msg) {
56
+ process.stderr.write(msg);
57
+ if (process.stdout.isTTY) await new Promise((r) => setTimeout(r, 2500));
58
+ }
59
+ } catch {}
60
+ }
61
+
62
+ //#endregion
63
+ export { warnIfCwdOccupied };
64
+ //# sourceMappingURL=cwdConflictWarn-CnxkLs7T.js.map
@@ -546,4 +546,4 @@ async function cmdExpose(args) {
546
546
 
547
547
  //#endregion
548
548
  export { cmdExpose, ensureExposure, listExposures, stopExposure };
549
- //# sourceMappingURL=expose-CGpMiQxp.js.map
549
+ //# sourceMappingURL=expose-CKWXAkzK.js.map
@@ -56,4 +56,4 @@ async function waitForFifo(pid, timeoutMs = 2e3, aborted) {
56
56
 
57
57
  //#endregion
58
58
  export { buildSpawnTutorial, shouldForkNested, waitForFifo };
59
- //# sourceMappingURL=forkNested-BLa3Mz_t.js.map
59
+ //# sourceMappingURL=forkNested-DJ2QzBsc.js.map
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { i as AgentContext, n as agentYes, o as removeControlCharacters, r as config, t as CLIS_CONFIG } from "./ts-BPtf-Tpg.js";
1
+ import { i as AgentContext, n as agentYes, o as removeControlCharacters, r as config, t as CLIS_CONFIG } from "./ts-C27ZwDyZ.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-defO9ozI.js";
3
+ import "./versionChecker-B5TdNLCF.js";
4
4
  import "./JsonlStore-Bm5Nj6v_.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
6
  import "./pidStore-DCqpU_bI.js";
@@ -53,4 +53,4 @@ async function ensureNodeRuntime(which = Bun.which) {
53
53
 
54
54
  //#endregion
55
55
  export { liveEnv as n, ensureNodeRuntime as t };
56
- //# sourceMappingURL=nodeRuntime-DoBOoeUW.js.map
56
+ //# sourceMappingURL=nodeRuntime-CR0NwnnO.js.map
@@ -2,10 +2,10 @@ import { n as logger } from "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./messageLog-Bb_Ie3Ck.js";
4
4
  import "./configShared-nnIwDYY-.js";
5
- import { $ as daemonLockOwnerPath, G as gcInboxes, J as listInboxParents, K as hostId, Q as daemonLockDir, S as listRecords, W as appendEvent, X as readInbox, Y as liveWatchers, Z as shouldStealLock, _ as isPidAlive, c as deriveLiveState, et as notifyDir, k as renderLogTailLines } from "./subcommands-T3C9EhOr.js";
5
+ import { $ as daemonLockOwnerPath, G as gcInboxes, J as listInboxParents, K as hostId, Q as daemonLockDir, S as listRecords, W as appendEvent, X as readInbox, Y as liveWatchers, Z as shouldStealLock, _ as isPidAlive, c as deriveLiveState, et as notifyDir, k as renderLogTailLines } from "./subcommands-Clrx40Id.js";
6
6
  import "./e2e-D5jaSHyY.js";
7
7
  import "./webrtcLink-DknpJQ0y.js";
8
- import "./remotes-D-jPl9Fj.js";
8
+ import "./remotes-BtXom7Rp.js";
9
9
  import { mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { execFile } from "node:child_process";
@@ -589,4 +589,4 @@ async function ensureDaemon() {
589
589
 
590
590
  //#endregion
591
591
  export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
592
- //# sourceMappingURL=notifyDaemon-DF7_EjVu.js.map
592
+ //# sourceMappingURL=notifyDaemon-BoE83_av.js.map
@@ -68,4 +68,4 @@ async function offerOpenInBrowser(url, prompt = "Open in browser now? [Y/n] ") {
68
68
 
69
69
  //#endregion
70
70
  export { offerOpenInBrowser };
71
- //# sourceMappingURL=openBrowser-D_qKXZNj.js.map
71
+ //# sourceMappingURL=openBrowser-D9VVE6cq.js.map
@@ -86,7 +86,7 @@ async function resolveRemoteSpec(spec) {
86
86
  * The bridge lives for the rest of the process (torn down on `process.exit`).
87
87
  */
88
88
  async function resolveWebrtc(link, keyword) {
89
- const { startWebrtcBridge } = await import("./webrtcRemote-CWogO7Qa.js");
89
+ const { startWebrtcBridge } = await import("./webrtcRemote-C974zjSt.js");
90
90
  const bridge = await startWebrtcBridge(link);
91
91
  return {
92
92
  url: bridge.baseUrl,
@@ -173,4 +173,4 @@ async function cmdRemote(rest) {
173
173
 
174
174
  //#endregion
175
175
  export { resolveRemoteSpec as a, readRemotes as i, deleteRemoteAlias as n, writeRemoteAlias as o, parseDirectRemoteSpec as r, cmdRemote as t };
176
- //# sourceMappingURL=remotes-D-jPl9Fj.js.map
176
+ //# sourceMappingURL=remotes-BtXom7Rp.js.map
@@ -1,5 +1,5 @@
1
1
  import "./e2e-D5jaSHyY.js";
2
2
  import "./webrtcLink-DknpJQ0y.js";
3
- import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-D-jPl9Fj.js";
3
+ import { a as resolveRemoteSpec, i as readRemotes, n as deleteRemoteAlias, o as writeRemoteAlias, r as parseDirectRemoteSpec, t as cmdRemote } from "./remotes-BtXom7Rp.js";
4
4
 
5
5
  export { cmdRemote };
@@ -1,4 +1,4 @@
1
- import { r as getInstalledPackage } from "./versionChecker-defO9ozI.js";
1
+ import { r as getInstalledPackage } from "./versionChecker-B5TdNLCF.js";
2
2
  import { execFileSync } from "child_process";
3
3
  import { existsSync, mkdirSync, unlinkSync } from "fs";
4
4
  import { chmod, copyFile } from "fs/promises";
@@ -225,4 +225,4 @@ async function getRustBinary(options = {}) {
225
225
 
226
226
  //#endregion
227
227
  export { getRustBinary as n, findSpawnHiddenLauncher as t };
228
- //# sourceMappingURL=rustBinary-DsKB37nM.js.map
228
+ //# sourceMappingURL=rustBinary-BHXs5BZD.js.map
@@ -1,14 +1,14 @@
1
- import "./ts-BPtf-Tpg.js";
1
+ import "./ts-C27ZwDyZ.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-defO9ozI.js";
3
+ import "./versionChecker-B5TdNLCF.js";
4
4
  import "./JsonlStore-Bm5Nj6v_.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
6
  import "./pidStore-DCqpU_bI.js";
7
7
  import "./messageLog-Bb_Ie3Ck.js";
8
8
  import "./todoAutomation-fxtfT_44.js";
9
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-nDMA0vtp.js";
10
- import { d as resolveSpawnCwd } from "./workspaceConfig-dHHBQh0F.js";
11
- import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-DoBOoeUW.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Cnig_ONj.js";
10
+ import { d as resolveSpawnCwd } from "./workspaceConfig-BRl-GwSN.js";
11
+ import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-CR0NwnnO.js";
12
12
  import { createHash } from "node:crypto";
13
13
 
14
14
  //#region ts/oxmgrService.ts
@@ -153,4 +153,4 @@ async function cmdSchedule(rest) {
153
153
 
154
154
  //#endregion
155
155
  export { cmdSchedule };
156
- //# sourceMappingURL=schedule-KFStEXIX.js.map
156
+ //# sourceMappingURL=schedule-Dd26IiBu.js.map
@@ -1,7 +1,7 @@
1
- import { a as CLAUDE_SESSION_PIN_ENV } from "./ts-BPtf-Tpg.js";
1
+ import { a as CLAUDE_SESSION_PIN_ENV } from "./ts-C27ZwDyZ.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-defO9ozI.js";
4
- import { t as findSpawnHiddenLauncher } from "./rustBinary-DsKB37nM.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-B5TdNLCF.js";
4
+ import { t as findSpawnHiddenLauncher } from "./rustBinary-BHXs5BZD.js";
5
5
  import { t as agentYesHome$1 } from "./agentYesHome-CtHb5b71.js";
6
6
  import "./JsonlStore-Bm5Nj6v_.js";
7
7
  import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
@@ -10,16 +10,16 @@ import { r as recordInbox } from "./messageLog-Bb_Ie3Ck.js";
10
10
  import { t as pgidForWrapper } from "./reaper-BNKvujYy.js";
11
11
  import "./todoAutomation-fxtfT_44.js";
12
12
  import "./configShared-nnIwDYY-.js";
13
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-nDMA0vtp.js";
14
- import { A as renderRawLog, D as recentMessageEdges, E as readPtysize, L as snapshotStatus, M as resolveOne, O as recentReadEdges, S as listRecords, T as readNotes, U as writeToIpc, b as isUserTyping, f as extractNeedsInput, j as renderRawLogLines, k as renderLogTailLines, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, q as isTransientLockMkdirError, tt as TYPING_BADGE, u as extractBadges } from "./subcommands-T3C9EhOr.js";
13
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Cnig_ONj.js";
14
+ import { A as renderRawLog, D as recentMessageEdges, E as readPtysize, L as snapshotStatus, M as resolveOne, O as recentReadEdges, S as listRecords, T as readNotes, U as writeToIpc, b as isUserTyping, f as extractNeedsInput, j as renderRawLogLines, k as renderLogTailLines, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, q as isTransientLockMkdirError, tt as TYPING_BADGE, u as extractBadges } from "./subcommands-Clrx40Id.js";
15
15
  import "./e2e-D5jaSHyY.js";
16
16
  import "./webrtcLink-DknpJQ0y.js";
17
- import "./remotes-D-jPl9Fj.js";
17
+ import "./remotes-BtXom7Rp.js";
18
18
  import { i as frameVisitorMessage, s as verifyCapability, t as MAX_CALLBACK_MSG_BYTES } from "./callbackCore-CgnOT1Cp.js";
19
- import { n as isCallbackRevoked, r as loadCallbackSecretReadOnly } from "./callback-CCZfvMHZ.js";
20
- import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-dHHBQh0F.js";
21
- import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-DoBOoeUW.js";
22
- import { r as spawnRejectionReason } from "./spawnGate-Vmd1d9OQ.js";
19
+ import { n as isCallbackRevoked, r as loadCallbackSecretReadOnly } from "./callback-DyJC_81s.js";
20
+ import { a as getSpawnHook, c as hasProvisionHook, d as resolveSpawnCwd, i as getProvisionRoot, l as hasSpawnHook, r as getProvisionHook, u as isProvisionAllowed } from "./workspaceConfig-BRl-GwSN.js";
21
+ import { n as liveEnv, t as ensureNodeRuntime } from "./nodeRuntime-CR0NwnnO.js";
22
+ import { r as spawnRejectionReason } from "./spawnGate-ChYCj6Dm.js";
23
23
  import yargs from "yargs";
24
24
  import { appendFile, mkdir, open, readFile, rename, rm, stat, unlink, writeFile } from "fs/promises";
25
25
  import { arch, cpus, freemem, homedir, hostname, loadavg, platform, totalmem, uptime, userInfo } from "os";
@@ -952,7 +952,7 @@ async function cmdServeDaemon(sub, args) {
952
952
  if (!webrtcDaemon) return;
953
953
  if (shareLink) {
954
954
  process.stdout.write(`\nshared over WebRTC — open this link (the token is eaten from the URL on open):\n ${shareLink}\n` + (shareLinkMinted ? ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n` : ``));
955
- const { offerOpenInBrowser } = await import("./openBrowser-D_qKXZNj.js");
955
+ const { offerOpenInBrowser } = await import("./openBrowser-D9VVE6cq.js");
956
956
  await offerOpenInBrowser(shareLink);
957
957
  } else process.stdout.write("\nthe WebRTC share link carries a secret, so the daemon does NOT log it —\nread it from ~/.agent-yes/.share-link (mode 0600). The room persists in\n~/.agent-yes/.share-room, so the link survives restarts.\n");
958
958
  };
@@ -1992,7 +1992,7 @@ Options:
1992
1992
  }
1993
1993
  });
1994
1994
  if (req.method === "GET" && p === "/api/ws") {
1995
- const ws = await import("./ws-DLkxWjt9.js");
1995
+ const ws = await import("./ws-D9H2UXCC.js");
1996
1996
  try {
1997
1997
  await ws.loadProvision();
1998
1998
  } catch (e) {
@@ -2012,7 +2012,7 @@ Options:
2012
2012
  if (req.method === "GET" && p === "/api/ws/status") {
2013
2013
  const dirRaw = url.searchParams.get("path");
2014
2014
  if (!dirRaw) return new Response("missing ?path=<workspace dir>", { status: 400 });
2015
- const ws = await import("./ws-DLkxWjt9.js");
2015
+ const ws = await import("./ws-D9H2UXCC.js");
2016
2016
  let prov;
2017
2017
  try {
2018
2018
  prov = await ws.loadProvision();
@@ -2601,7 +2601,7 @@ Options:
2601
2601
  const perm = body.perm ?? "r";
2602
2602
  if (perm !== "r" && perm !== "rw") return new Response(`invalid perm ${perm} (want r or rw)`, { status: 400 });
2603
2603
  try {
2604
- const { createScopedShare } = await import("./agentShare-BLl0-SpO.js");
2604
+ const { createScopedShare } = await import("./agentShare-Cue9mqvJ.js");
2605
2605
  const share = await createScopedShare({
2606
2606
  agent: body.agent,
2607
2607
  perm,
@@ -2616,12 +2616,12 @@ Options:
2616
2616
  }
2617
2617
  }
2618
2618
  if (req.method === "GET" && p === "/api/shares") {
2619
- const { listShares } = await import("./agentShare-BLl0-SpO.js");
2619
+ const { listShares } = await import("./agentShare-Cue9mqvJ.js");
2620
2620
  return Response.json(listShares());
2621
2621
  }
2622
2622
  const revokeM = /^\/api\/share\/([^/]+)$/.exec(p);
2623
2623
  if (req.method === "DELETE" && revokeM) {
2624
- const { revokeShare } = await import("./agentShare-BLl0-SpO.js");
2624
+ const { revokeShare } = await import("./agentShare-Cue9mqvJ.js");
2625
2625
  const ok = revokeShare(decodeURIComponent(revokeM[1]));
2626
2626
  return new Response(ok ? "revoked" : "no such share", { status: ok ? 200 : 404 });
2627
2627
  }
@@ -2635,7 +2635,7 @@ Options:
2635
2635
  const port = Number(body.port);
2636
2636
  if (!Number.isInteger(port) || port < 1 || port > 65535) return new Response("valid port required", { status: 400 });
2637
2637
  try {
2638
- const { ensureExposure } = await import("./expose-CGpMiQxp.js");
2638
+ const { ensureExposure } = await import("./expose-CKWXAkzK.js");
2639
2639
  const h = await ensureExposure(port, body.relay);
2640
2640
  return Response.json({
2641
2641
  id: h.id,
@@ -2649,12 +2649,12 @@ Options:
2649
2649
  }
2650
2650
  }
2651
2651
  if (req.method === "GET" && p === "/api/exposes") {
2652
- const { listExposures } = await import("./expose-CGpMiQxp.js");
2652
+ const { listExposures } = await import("./expose-CKWXAkzK.js");
2653
2653
  return Response.json(listExposures());
2654
2654
  }
2655
2655
  const unexposeM = /^\/api\/expose\/(\d+)$/.exec(p);
2656
2656
  if (req.method === "DELETE" && unexposeM) {
2657
- const { stopExposure } = await import("./expose-CGpMiQxp.js");
2657
+ const { stopExposure } = await import("./expose-CKWXAkzK.js");
2658
2658
  const ok = stopExposure(Number(unexposeM[1]));
2659
2659
  return new Response(ok ? "revoked" : "no such exposure", { status: ok ? 200 : 404 });
2660
2660
  }
@@ -2881,7 +2881,7 @@ Options:
2881
2881
  const persistNote = explicitUrl ? "\n" : ` (persistent room — same link across restarts; delete ~/.agent-yes/.share-room to rotate)\n\n`;
2882
2882
  process.stdout.write(`${wantHttp ? "\n" : ""}${lead}:\n ${link}\n` + persistNote);
2883
2883
  if (!rotated) {
2884
- const { offerOpenInBrowser } = await import("./openBrowser-D_qKXZNj.js");
2884
+ const { offerOpenInBrowser } = await import("./openBrowser-D9VVE6cq.js");
2885
2885
  await offerOpenInBrowser(link);
2886
2886
  }
2887
2887
  } else {
@@ -2923,7 +2923,7 @@ Options:
2923
2923
  const shutdown = (resolve) => {
2924
2924
  if (heartbeat) clearInterval(heartbeat);
2925
2925
  closeShare?.();
2926
- import("./agentShare-BLl0-SpO.js").then((m) => m.revokeAllShares()).catch(() => {});
2926
+ import("./agentShare-Cue9mqvJ.js").then((m) => m.revokeAllShares()).catch(() => {});
2927
2927
  server?.stop();
2928
2928
  Promise.resolve(releaseHostLock?.()).catch(() => {}).then(resolve);
2929
2929
  };
@@ -2936,4 +2936,4 @@ Options:
2936
2936
 
2937
2937
  //#endregion
2938
2938
  export { cmdServe, resolveLocalServeUrl };
2939
- //# sourceMappingURL=serve-BBrh1z4b.js.map
2939
+ //# sourceMappingURL=serve-CC1SbBXS.js.map
@@ -1,4 +1,4 @@
1
- import { f as setWorkspaceRoot, s as getWorkspaceRoot } from "./workspaceConfig-dHHBQh0F.js";
1
+ import { f as setWorkspaceRoot, s as getWorkspaceRoot } from "./workspaceConfig-BRl-GwSN.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import { stdin, stdout } from "node:process";
4
4
  import { createInterface } from "node:readline/promises";
@@ -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-BBrh1z4b.js");
35
+ const { cmdServe } = await import("./serve-CC1SbBXS.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-DvP49xV5.js.map
45
+ //# sourceMappingURL=setup-Cz9t2H2g.js.map
@@ -1,5 +1,5 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
- import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-Vmd1d9OQ.js";
3
+ import { i as waitForSpawnCapacity, n as spawnGateEnabled, r as spawnRejectionReason, t as memAvailableMb } from "./spawnGate-ChYCj6Dm.js";
4
4
 
5
5
  export { waitForSpawnCapacity };
@@ -1,5 +1,5 @@
1
1
  import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
2
- import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-dHHBQh0F.js";
2
+ import { n as getMinFreeMb, o as getSpawnWaitMs, t as getMaxAgents } from "./workspaceConfig-BRl-GwSN.js";
3
3
  import { readFile } from "fs/promises";
4
4
 
5
5
  //#region ts/spawnGate.ts
@@ -104,4 +104,4 @@ async function waitForSpawnCapacity(opts) {
104
104
 
105
105
  //#endregion
106
106
  export { waitForSpawnCapacity as i, spawnGateEnabled as n, spawnRejectionReason as r, memAvailableMb as t };
107
- //# sourceMappingURL=spawnGate-Vmd1d9OQ.js.map
107
+ //# sourceMappingURL=spawnGate-ChYCj6Dm.js.map
@@ -5,7 +5,7 @@ import { a as updateGlobalPidStatus, i as readGlobalPids } from "./globalPidInde
5
5
  import { a as recordOutbox, i as recordMessage, n as readMailbox, t as partyMatches } from "./messageLog-Bb_Ie3Ck.js";
6
6
  import { t as loadSharedCliDefaults } from "./configShared-nnIwDYY-.js";
7
7
  import { n as isWebrtcSpec } from "./webrtcLink-DknpJQ0y.js";
8
- import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-D-jPl9Fj.js";
8
+ import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-BtXom7Rp.js";
9
9
  import ms from "ms";
10
10
  import yargs from "yargs";
11
11
  import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
@@ -1239,35 +1239,35 @@ async function runSubcommand(argv) {
1239
1239
  case "restart": return await cmdRestart(rest);
1240
1240
  case "note": return await cmdNote(rest);
1241
1241
  case "todo": {
1242
- const { runTodoSubcommand } = await import("./todoCli-dk8WNgcS.js");
1242
+ const { runTodoSubcommand } = await import("./todoCli-D8bL164c.js");
1243
1243
  return runTodoSubcommand(rest);
1244
1244
  }
1245
1245
  case "serve": {
1246
- const { cmdServe } = await import("./serve-BBrh1z4b.js");
1246
+ const { cmdServe } = await import("./serve-CC1SbBXS.js");
1247
1247
  return cmdServe(rest);
1248
1248
  }
1249
1249
  case "setup": {
1250
- const { cmdSetup } = await import("./setup-DvP49xV5.js");
1250
+ const { cmdSetup } = await import("./setup-Cz9t2H2g.js");
1251
1251
  return cmdSetup(rest);
1252
1252
  }
1253
1253
  case "ws": {
1254
- const { cmdWs } = await import("./ws-DLkxWjt9.js");
1254
+ const { cmdWs } = await import("./ws-D9H2UXCC.js");
1255
1255
  return cmdWs(rest);
1256
1256
  }
1257
1257
  case "schedule": {
1258
- const { cmdSchedule } = await import("./schedule-KFStEXIX.js");
1258
+ const { cmdSchedule } = await import("./schedule-Dd26IiBu.js");
1259
1259
  return cmdSchedule(rest);
1260
1260
  }
1261
1261
  case "remote": {
1262
- const { cmdRemote } = await import("./remotes-C_YlVvr9.js");
1262
+ const { cmdRemote } = await import("./remotes-DZ_G2wkM.js");
1263
1263
  return cmdRemote(rest);
1264
1264
  }
1265
1265
  case "expose": {
1266
- const { cmdExpose } = await import("./expose-CGpMiQxp.js");
1266
+ const { cmdExpose } = await import("./expose-CKWXAkzK.js");
1267
1267
  return cmdExpose(rest);
1268
1268
  }
1269
1269
  case "callback": {
1270
- const { cmdCallback } = await import("./callback-CuWuj9gy.js");
1270
+ const { cmdCallback } = await import("./callback-Lsz9mZiC.js");
1271
1271
  return cmdCallback(rest);
1272
1272
  }
1273
1273
  case "reap":
@@ -1305,7 +1305,7 @@ async function cmdHelp(managerCommands = true) {
1305
1305
  const wsLines = managerCommands ? " ay ws ls [--status] list <owner>/<repo>/tree/<branch> workspaces\n ay ws new <owner>/<repo>[@branch] clone/refresh a workspace (ay ws help for more)\n" : ``;
1306
1306
  const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
1307
1307
  const agentSection = self ? await buildAgentContextSection(self) : "";
1308
- 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 msgs [keyword] [--in|--out] inter-agent message log (sent + received)\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" + wsLines + "\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 expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay callback --expires 7d mint an embeddable message-me widget for one agent\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");
1308
+ 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 (keyword '.' = agent in this cwd)\n ay msgs [keyword] [--in|--out] inter-agent message log (sent + received)\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" + wsLines + "\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 expose <port> share localhost:<port> at https://<id>.agent-yes.com (private link)\n ay callback --expires 7d mint an embeddable message-me widget for one agent\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");
1309
1309
  return 0;
1310
1310
  }
1311
1311
  function matchKeyword(record, keyword) {
@@ -1315,6 +1315,7 @@ function matchKeyword(record, keyword) {
1315
1315
  if (record.pid === Number(keyword)) return true;
1316
1316
  return !!(record.agent_id && record.agent_id.toLowerCase().startsWith(kw));
1317
1317
  }
1318
+ if (kw === "." || kw === "./") return path.resolve(record.cwd) === path.resolve(process.cwd());
1318
1319
  if (record.cwd.toLowerCase().includes(kw)) return true;
1319
1320
  if (record.cli.toLowerCase() === kw) return true;
1320
1321
  if (record.prompt && record.prompt.toLowerCase().includes(kw)) return true;
@@ -1684,7 +1685,7 @@ async function fetchRemoteRecordsRaw(url, token, opts) {
1684
1685
  let base = url;
1685
1686
  let bearer = token;
1686
1687
  if (isWebrtcSpec(url)) {
1687
- const { startWebrtcBridge } = await import("./webrtcRemote-CWogO7Qa.js");
1688
+ const { startWebrtcBridge } = await import("./webrtcRemote-C974zjSt.js");
1688
1689
  bridge = await startWebrtcBridge(url);
1689
1690
  base = bridge.baseUrl;
1690
1691
  bearer = bridge.token;
@@ -3107,6 +3108,13 @@ async function cmdSend(rest) {
3107
3108
  wrapped: Boolean(nonce)
3108
3109
  });
3109
3110
  if (!confirmed) process.stderr.write(`\nwarning: couldn't confirm the CLI acted on it after ${SEND_SUBMIT_MAX_RETRIES + 1} attempt(s) — it may still be sitting unsubmitted in the prompt. Last screen:\n` + lastScreen.slice(-8).map((l) => ` ${l}`).join("\n") + "\n");
3111
+ if (record.log_file) {
3112
+ const tail = await renderLogTailLines(record.log_file, 10) ?? [];
3113
+ let end = tail.length;
3114
+ while (end > 0 && tail[end - 1].trim() === "") end--;
3115
+ const trimmed = tail.slice(0, end);
3116
+ if (trimmed.length) process.stderr.write(`\n── pid ${record.pid} · last ${trimmed.length} line${trimmed.length === 1 ? "" : "s"} ──\n` + trimmed.map((l) => ` ${l}`).join("\n") + `\n`);
3117
+ }
3110
3118
  const replyHint = sender.agent ? ` ay send ${replyTarget} "..." # reply to sender\n` : "";
3111
3119
  process.stderr.write(`\n` + replyHint + ` ay tail ${record.pid} # watch output\n ay ls # list all agents\n`);
3112
3120
  if (codeName === "ctrl-c" || codeName === "ctrlc") {
@@ -4279,7 +4287,7 @@ async function cmdNotify(rest) {
4279
4287
  }
4280
4288
  const ensure = async () => {
4281
4289
  if (!argv["ensure-daemon"]) return;
4282
- const { ensureDaemon } = await import("./notifyDaemon-DF7_EjVu.js");
4290
+ const { ensureDaemon } = await import("./notifyDaemon-BoE83_av.js");
4283
4291
  await ensureDaemon().catch(() => null);
4284
4292
  };
4285
4293
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4353,7 +4361,7 @@ async function cmdNotifyCursor(args) {
4353
4361
  }
4354
4362
  async function cmdNotifyd(rest) {
4355
4363
  const sub = rest[0] ?? "status";
4356
- const daemon = await import("./notifyDaemon-DF7_EjVu.js");
4364
+ const daemon = await import("./notifyDaemon-BoE83_av.js");
4357
4365
  switch (sub) {
4358
4366
  case "run": return daemon.runDaemon();
4359
4367
  case "once": return daemon.runDaemon({ once: true });
@@ -4380,4 +4388,4 @@ async function cmdNotifyd(rest) {
4380
4388
 
4381
4389
  //#endregion
4382
4390
  export { daemonLockOwnerPath as $, renderRawLog as A, submitAndConfirm as B, matchKeyword as C, recentMessageEdges as D, readPtysize as E, restartHintLines as F, gcInboxes as G, writeKeysPaced as H, runSubcommand as I, listInboxParents as J, hostId as K, snapshotStatus as L, resolveOne as M, resolveReadWindow as N, recentReadEdges as O, resolveResumeArgs as P, daemonLockDir as Q, stdinActivityPath as R, listRecords as S, readNotes as T, writeToIpc as U, waitForLogQuiet as V, appendEvent as W, readInbox as X, liveWatchers as Y, shouldStealLock as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, notifyDir as et, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, renderRawLogLines as j, renderLogTailLines as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, isTransientLockMkdirError as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, TYPING_BADGE as tt, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, stopTipForCli as z };
4383
- //# sourceMappingURL=subcommands-T3C9EhOr.js.map
4391
+ //# sourceMappingURL=subcommands-Clrx40Id.js.map
@@ -2,9 +2,9 @@ import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./messageLog-Bb_Ie3Ck.js";
4
4
  import "./configShared-nnIwDYY-.js";
5
- import { A as renderRawLog, B as submitAndConfirm, C as matchKeyword, D as recentMessageEdges, E as readPtysize, F as restartHintLines, H as writeKeysPaced, I as runSubcommand, L as snapshotStatus, M as resolveOne, N as resolveReadWindow, O as recentReadEdges, P as resolveResumeArgs, R as stdinActivityPath, S as listRecords, T as readNotes, U as writeToIpc, V as waitForLogQuiet, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as renderRawLogLines, k as renderLogTailLines, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as stopTipForCli } from "./subcommands-T3C9EhOr.js";
5
+ import { A as renderRawLog, B as submitAndConfirm, C as matchKeyword, D as recentMessageEdges, E as readPtysize, F as restartHintLines, H as writeKeysPaced, I as runSubcommand, L as snapshotStatus, M as resolveOne, N as resolveReadWindow, O as recentReadEdges, P as resolveResumeArgs, R as stdinActivityPath, S as listRecords, T as readNotes, U as writeToIpc, V as waitForLogQuiet, _ as isPidAlive, a as cmdHelp, b as isUserTyping, c as deriveLiveState, d as extractMenu, f as extractNeedsInput, g as isExitRequest, h as isAgentStuck, i as backoffWhileTyping, j as renderRawLogLines, k as renderLogTailLines, l as deriveLiveStatus, m as finalizedLines, n as READ_PAGE_DEFAULT, o as controlCodeFromName, p as extractTaskCounts, r as TYPING_WINDOW_MS, s as cursorAbs, t as GRACEFUL_EXIT_COMMANDS, u as extractBadges, v as isSlashCommand, w as menuSelectKeys, x as lastStdinAt, y as isSubcommand, z as stopTipForCli } from "./subcommands-Clrx40Id.js";
6
6
  import "./e2e-D5jaSHyY.js";
7
7
  import "./webrtcLink-DknpJQ0y.js";
8
- import "./remotes-D-jPl9Fj.js";
8
+ import "./remotes-BtXom7Rp.js";
9
9
 
10
10
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -333,4 +333,4 @@ async function runTodoSubcommand(rest0) {
333
333
 
334
334
  //#endregion
335
335
  export { runTodoSubcommand };
336
- //# sourceMappingURL=todoCli-dk8WNgcS.js.map
336
+ //# sourceMappingURL=todoCli-D8bL164c.js.map
@@ -187,4 +187,4 @@ async function startTray() {
187
187
 
188
188
  //#endregion
189
189
  export { ensureTray, startTray };
190
- //# sourceMappingURL=tray-CIm-5aWB.js.map
190
+ //# sourceMappingURL=tray-BZqJtEmL.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-defO9ozI.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-B5TdNLCF.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-CNMl13dC.js";
5
5
  import { i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
@@ -1919,4 +1919,4 @@ function sleep(ms) {
1919
1919
 
1920
1920
  //#endregion
1921
1921
  export { CLAUDE_SESSION_PIN_ENV as a, AgentContext as i, agentYes as n, removeControlCharacters as o, config as r, CLIS_CONFIG as t };
1922
- //# sourceMappingURL=ts-BPtf-Tpg.js.map
1922
+ //# sourceMappingURL=ts-C27ZwDyZ.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.225.0";
10
+ var version = "1.226.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-defO9ozI.js.map
218
+ //# sourceMappingURL=versionChecker-B5TdNLCF.js.map
@@ -256,4 +256,4 @@ async function startWebrtcBridge(link) {
256
256
 
257
257
  //#endregion
258
258
  export { startWebrtcBridge };
259
- //# sourceMappingURL=webrtcRemote-CWogO7Qa.js.map
259
+ //# sourceMappingURL=webrtcRemote-C974zjSt.js.map
@@ -193,4 +193,4 @@ function resolveSpawnCwd(input) {
193
193
 
194
194
  //#endregion
195
195
  export { getSpawnHook as a, hasProvisionHook as c, resolveSpawnCwd as d, setWorkspaceRoot as f, getProvisionRoot as i, hasSpawnHook as l, getMinFreeMb as n, getSpawnWaitMs as o, getProvisionHook as r, getWorkspaceRoot as s, getMaxAgents as t, isProvisionAllowed as u };
196
- //# sourceMappingURL=workspaceConfig-dHHBQh0F.js.map
196
+ //# sourceMappingURL=workspaceConfig-BRl-GwSN.js.map
@@ -2,11 +2,11 @@ import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./messageLog-Bb_Ie3Ck.js";
4
4
  import "./configShared-nnIwDYY-.js";
5
- import { S as listRecords } from "./subcommands-T3C9EhOr.js";
5
+ import { S as listRecords } from "./subcommands-Clrx40Id.js";
6
6
  import "./e2e-D5jaSHyY.js";
7
7
  import "./webrtcLink-DknpJQ0y.js";
8
- import "./remotes-D-jPl9Fj.js";
9
- import { i as getProvisionRoot } from "./workspaceConfig-dHHBQh0F.js";
8
+ import "./remotes-BtXom7Rp.js";
9
+ import { i as getProvisionRoot } from "./workspaceConfig-BRl-GwSN.js";
10
10
  import { existsSync } from "fs";
11
11
  import { lstat, opendir } from "fs/promises";
12
12
  import path from "path";
@@ -418,4 +418,4 @@ async function cmdWs(args) {
418
418
 
419
419
  //#endregion
420
420
  export { WS_JSON_SCHEMA, cmdWs, collectWorkspaces, isPathInside, loadProvision, workspaceStatus };
421
- //# sourceMappingURL=ws-DLkxWjt9.js.map
421
+ //# sourceMappingURL=ws-D9H2UXCC.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.225.0",
3
+ "version": "1.226.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
@@ -83,6 +83,15 @@ if (!config.showVersion && !config.appendPrompt && !config.tray) {
83
83
  });
84
84
  }
85
85
 
86
+ // Warn if another live agent already occupies this cwd — two agents in one
87
+ // working tree clobber each other's files/builds/ports. Sits alongside the
88
+ // spawn gate (real-launch only) and BEFORE the --rust dispatch so it covers both
89
+ // runtimes. Best-effort, non-blocking; silence with AGENT_YES_NO_CWD_WARN=1.
90
+ if (!config.showVersion && !config.appendPrompt && !config.tray) {
91
+ const { warnIfCwdOccupied } = await import("./cwdConflictWarn.ts");
92
+ await warnIfCwdOccupied(process.argv);
93
+ }
94
+
86
95
  // Handle --rust: spawn the Rust binary instead, fall back to TypeScript if unavailable
87
96
  if (config.useRust) {
88
97
  let rustBinary: string | undefined;
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { formatCwdConflictWarning, shQuote } from "./cwdConflictWarn.ts";
3
+
4
+ describe("cwdConflictWarn.formatCwdConflictWarning", () => {
5
+ const cwd = "/code/proj/tree/main";
6
+
7
+ it("returns null when no peers occupy the cwd", () => {
8
+ expect(formatCwdConflictWarning([], cwd, "ay claude")).toBeNull();
9
+ });
10
+
11
+ it("warns and suggests a git-worktree command for a single peer", () => {
12
+ const msg = formatCwdConflictWarning([{ pid: 111, cli: "claude" }], cwd, "ay claude -- fix");
13
+ expect(msg).toContain("1 agent is already running in this directory (/code/proj/tree/main)");
14
+ expect(msg).toContain("111 claude");
15
+ // uses `git worktree add` (not `git clone .`) — worktree name = dir + branch
16
+ expect(msg).toContain("git worktree add -b main-work ../main-work && cd ../main-work");
17
+ expect(msg).not.toContain("git clone");
18
+ // original command is appended so the suggestion is copy-pasteable
19
+ expect(msg).toContain("&& ay claude -- fix");
20
+ expect(msg).toContain("AGENT_YES_NO_CWD_WARN=1");
21
+ });
22
+
23
+ it("pluralizes and lists at most 3 peers with an overflow count", () => {
24
+ const peers = [1, 2, 3, 4, 5].map((n) => ({ pid: n, cli: "codex" }));
25
+ const msg = formatCwdConflictWarning(peers, cwd, "ay codex")!;
26
+ expect(msg).toContain("5 agents are already running");
27
+ expect(msg).toContain("1 codex");
28
+ expect(msg).toContain("3 codex");
29
+ expect(msg).not.toContain("4 codex"); // capped at 3 listed
30
+ expect(msg).toContain("…and 2 more");
31
+ });
32
+
33
+ it("falls back to a placeholder when cwd has no basename or command is empty", () => {
34
+ const msg = formatCwdConflictWarning([{ pid: 9, cli: "gemini" }], "/", " ")!;
35
+ expect(msg).toContain("../agent-work"); // basename('/') is empty → 'agent'
36
+ expect(msg).toContain("&& ay <cli> …"); // empty origCmd → placeholder
37
+ });
38
+ });
39
+
40
+ describe("cwdConflictWarn.shQuote", () => {
41
+ it("passes safe tokens through unquoted", () => {
42
+ expect(shQuote("ay")).toBe("ay");
43
+ expect(shQuote("claude")).toBe("claude");
44
+ expect(shQuote("--")).toBe("--");
45
+ expect(shQuote("../main-work")).toBe("../main-work");
46
+ expect(shQuote("main-work_2.3")).toBe("main-work_2.3");
47
+ });
48
+
49
+ it("single-quotes tokens with spaces or shell metacharacters", () => {
50
+ expect(shQuote("fix the bug")).toBe("'fix the bug'");
51
+ expect(shQuote("a;rm -rf b")).toBe("'a;rm -rf b'");
52
+ expect(shQuote("$HOME")).toBe("'$HOME'");
53
+ expect(shQuote("")).toBe("''");
54
+ });
55
+
56
+ it("escapes embedded single quotes safely", () => {
57
+ // POSIX close-quote / escaped-quote / reopen-quote dance
58
+ expect(shQuote("it's")).toBe(`'it'\\''s'`);
59
+ });
60
+ });
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Pre-launch warning: another live agent is already working in THIS exact
3
+ * directory. Two agents sharing one working tree clobber each other's edits,
4
+ * builds, and dev-server ports — so we warn (stderr, non-fatal) and hand the
5
+ * user a copy-pasteable git-worktree isolation command.
6
+ *
7
+ * Lives in cli.ts's launch path (before the --rust dispatch) so it covers BOTH
8
+ * the Rust and TypeScript runtimes with one check. The launching agent isn't
9
+ * registered in the pid index yet, so every same-cwd match here is a genuine
10
+ * pre-existing peer, never itself.
11
+ */
12
+ import path from "path";
13
+ import { readGlobalPids, type GlobalPidRecord } from "./globalPidIndex.ts";
14
+
15
+ /** A pre-existing peer for the warning: just what we render. */
16
+ export interface CwdOccupant {
17
+ pid: number;
18
+ cli: string;
19
+ }
20
+
21
+ /**
22
+ * POSIX single-quote a shell token so a copy-pasted recovery command survives
23
+ * spaces, quotes, `$`, `;`, `&`, etc. Safe-charset tokens pass through unquoted
24
+ * so the common case (`ay claude -- fix`) stays readable. Empty string → `''`.
25
+ */
26
+ export function shQuote(s: string): string {
27
+ if (s === "") return "''";
28
+ if (/^[A-Za-z0-9,._+@%/:=-]+$/.test(s)) return s;
29
+ return `'${s.replace(/'/g, `'\\''`)}'`;
30
+ }
31
+
32
+ /**
33
+ * Pure renderer — given the peers already in `cwd`, the cwd, and the original
34
+ * `ay …` command, produce the warning text (or null when there's no conflict).
35
+ * Separated from IO so the message/branching is unit-tested without a live index.
36
+ */
37
+ export function formatCwdConflictWarning(
38
+ occupants: CwdOccupant[],
39
+ cwd: string,
40
+ origCmd: string,
41
+ ): string | null {
42
+ if (occupants.length === 0) return null;
43
+ // Worktree name doubles as the new dir AND the new branch. `git worktree add`
44
+ // (not `git clone .`) is the correct primitive: it resolves the repo root even
45
+ // from a subdir, shares the object store, and creates the branch in one step.
46
+ const wt = `${path.basename(cwd) || "agent"}-work`;
47
+ const list = occupants
48
+ .slice(0, 3)
49
+ .map((r) => ` ${r.pid} ${r.cli}`)
50
+ .join("\n");
51
+ const more = occupants.length > 3 ? `\n …and ${occupants.length - 3} more` : "";
52
+ const cmd = origCmd.trim() || "ay <cli> …";
53
+ const branch = shQuote(wt);
54
+ const dir = shQuote(`../${wt}`);
55
+ return (
56
+ `\n⚠ ${occupants.length} agent${occupants.length > 1 ? "s are" : " is"} already running in this directory (${cwd}):\n` +
57
+ `${list}${more}\n` +
58
+ ` Parallel agents in one working tree clobber each other's files, builds, and ports.\n` +
59
+ ` To isolate this run in its own git worktree, cancel (Ctrl-C) and instead:\n` +
60
+ ` git worktree add -b ${branch} ${dir} && cd ${dir} && ${cmd}\n` +
61
+ ` (set AGENT_YES_NO_CWD_WARN=1 to silence)\n\n`
62
+ );
63
+ }
64
+
65
+ /**
66
+ * IO wrapper: discover live same-cwd peers and print the warning to stderr.
67
+ * Best-effort and swallow-all — a discovery hiccup must NEVER block a launch.
68
+ * No-op when AGENT_YES_NO_CWD_WARN=1.
69
+ */
70
+ export async function warnIfCwdOccupied(rawArgv: string[]): Promise<void> {
71
+ if (process.env.AGENT_YES_NO_CWD_WARN === "1") return;
72
+ try {
73
+ const here = path.resolve(process.cwd());
74
+ const occupants: CwdOccupant[] = (await readGlobalPids({ liveOnly: true }))
75
+ .filter((r: GlobalPidRecord) => path.resolve(r.cwd) === here)
76
+ .map((r) => ({ pid: r.pid, cli: r.cli }));
77
+ // Shell-quote each argv token so the suggested command reproduces the
78
+ // original launch even with spaces/quotes in a prompt (e.g. a multi-word
79
+ // `-- "fix the bug"`). We can't recover the user's exact original quoting
80
+ // from argv, but per-token quoting is copy-paste-correct.
81
+ const origCmd = ["ay", ...rawArgv.slice(2)].map(shQuote).join(" ");
82
+ const msg = formatCwdConflictWarning(occupants, here, origCmd);
83
+ if (msg) {
84
+ process.stderr.write(msg);
85
+ // Interactive CLIs (claude/codex/gemini) switch to the alternate screen
86
+ // buffer on startup, which clears the terminal — the warning would flash
87
+ // and vanish before a human could read it. Hold briefly (only on a real
88
+ // conflict, only when attached to a TTY) so they get a beat to Ctrl-C and
89
+ // switch to a worktree. Non-TTY / detached launches skip the pause.
90
+ if (process.stdout.isTTY) {
91
+ await new Promise((r) => setTimeout(r, 2500));
92
+ }
93
+ }
94
+ } catch {
95
+ // Non-fatal — never block launching the agent over a warning.
96
+ }
97
+ }
@@ -336,6 +336,18 @@ describe("subcommands.matchKeyword", () => {
336
336
  expect(matchKeyword(r, "parser")).toBe(false);
337
337
  });
338
338
 
339
+ it("treats '.' / './' as the current working directory (exact cwd match)", async () => {
340
+ const { matchKeyword } = await loadModule();
341
+ const here = { ...baseRecord, cwd: process.cwd() };
342
+ const elsewhere = { ...baseRecord, cwd: "/some/other/place" };
343
+ // sibling agent in this exact cwd resolves
344
+ expect(matchKeyword(here, ".")).toBe(true);
345
+ expect(matchKeyword(here, "./")).toBe(true);
346
+ // an agent in a different cwd (even a subdir/parent) does not
347
+ expect(matchKeyword(elsewhere, ".")).toBe(false);
348
+ expect(matchKeyword({ ...baseRecord, cwd: process.cwd() + "/sub" }, ".")).toBe(false);
349
+ });
350
+
339
351
  it("matches by agent_id prefix", async () => {
340
352
  const { matchKeyword } = await loadModule();
341
353
  const r = { ...baseRecord, agent_id: "a1b2c3d4e5f6" };
package/ts/subcommands.ts CHANGED
@@ -612,7 +612,7 @@ export async function cmdHelp(managerCommands = true): Promise<number> {
612
612
  ` --before-line L [--limit N]\n` +
613
613
  ` ay cat <keyword> full log\n` +
614
614
  ` ay head <keyword> first N lines\n` +
615
- ` ay send <keyword> <msg> send a message\n` +
615
+ ` ay send <keyword> <msg> send a message (keyword '.' = agent in this cwd)\n` +
616
616
  ` ay msgs [keyword] [--in|--out] inter-agent message log (sent + received)\n` +
617
617
  ` ay key <keyword> <key...> send raw keystrokes (down/up/enter/esc/…) — drives menus\n` +
618
618
  ` ay select <keyword> <N> pick option N of a needs_input selection menu\n` +
@@ -676,6 +676,14 @@ export function matchKeyword(record: GlobalPidRecord, keyword: string): boolean
676
676
  if (record.pid === Number(keyword)) return true;
677
677
  return !!(record.agent_id && record.agent_id.toLowerCase().startsWith(kw));
678
678
  }
679
+ // 1b. `.` / `./` — shell convention for "the current directory": target the
680
+ // agent whose cwd IS process.cwd() (exact match). Lets `ay send .` reach the
681
+ // sibling agent running in this same dir without typing its pid or a path
682
+ // fragment. Repurposed from the (near-useless) substring behavior — a lone `.`
683
+ // was in almost every path, so it never selected anything meaningful.
684
+ if (kw === "." || kw === "./") {
685
+ return path.resolve(record.cwd) === path.resolve(process.cwd());
686
+ }
679
687
  // 2. cwd contains keyword
680
688
  if (record.cwd.toLowerCase().includes(kw)) return true;
681
689
  // 3. cli exact (lowercase)
@@ -3192,6 +3200,24 @@ async function cmdSend(rest: string[]): Promise<number> {
3192
3200
  );
3193
3201
  }
3194
3202
 
3203
+ // Echo the tail of the target's screen so the sender sees, inline, whether the
3204
+ // message landed and how the agent reacted — no separate `ay tail` round-trip.
3205
+ // Rendered fresh from the log after the settle/confirm wait above, so it
3206
+ // reflects the post-submit state. Best-effort: a missing/empty log just skips.
3207
+ if (record.log_file) {
3208
+ const tail = (await renderLogTailLines(record.log_file, 10)) ?? [];
3209
+ let end = tail.length;
3210
+ while (end > 0 && tail[end - 1]!.trim() === "") end--; // drop trailing blanks
3211
+ const trimmed = tail.slice(0, end);
3212
+ if (trimmed.length) {
3213
+ process.stderr.write(
3214
+ `\n── pid ${record.pid} · last ${trimmed.length} line${trimmed.length === 1 ? "" : "s"} ──\n` +
3215
+ trimmed.map((l) => ` ${l}`).join("\n") +
3216
+ `\n`,
3217
+ );
3218
+ }
3219
+ }
3220
+
3195
3221
  const replyHint = sender.agent
3196
3222
  ? ` ay send ${replyTarget} "..." # reply to sender\n`
3197
3223
  : "";