claude-yes 1.170.1 → 1.171.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-kJRKPdFi.js";
1
+ import { t as CLIS_CONFIG } from "./ts-CJuOKFYC.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-Bz-oXKG8.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-CLwNa69l.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-CJuOKFYC.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-OhTh_a0W.js";
4
+ import "./pidStore-BIvsBQ8X.js";
5
+ import "./globalPidIndex-CoNr7tS8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CLwNa69l.js";
7
+
8
+ export { SUPPORTED_CLIS };
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-eVbxzEL3.js";
5
- import { n as getRustBinary } from "./rustBinary-CYS7148m.js";
4
+ import { i as versionString, n as displayVersion, t as checkAndAutoUpdate } from "./versionChecker-OhTh_a0W.js";
5
+ import { n as getRustBinary } from "./rustBinary-4qI6-ALa.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-DOnqoHyP.js");
288
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-BG8zmd95.js");
289
289
  if (isHelpFlag && process.argv.length === 3) {
290
290
  await cmdHelp(managerCommands);
291
291
  process.exit(0);
@@ -327,7 +327,7 @@ if (config.useRust) {
327
327
  }
328
328
  }
329
329
  if (rustBinary) {
330
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-Dby-8X2S.js");
330
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-vi4ofK0T.js");
331
331
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
332
332
  if (config.verbose) {
333
333
  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-kJRKPdFi.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-CJuOKFYC.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-eVbxzEL3.js";
3
+ import "./versionChecker-OhTh_a0W.js";
4
4
  import "./pidStore-BIvsBQ8X.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
6
 
@@ -1,7 +1,7 @@
1
1
  import { n as logger } from "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./configShared-0MnIQ652.js";
4
- import { D as renderLogTailLines, G as hostId, J as readInbox, K as listInboxParents, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-CbcBfInM.js";
4
+ import { D as renderLogTailLines, G as hostId, J as readInbox, K as listInboxParents, Q as notifyDir, S as listRecords, U as appendEvent, W as gcInboxes, X as daemonLockDir, Y as shouldStealLock, Z as daemonLockOwnerPath, _ as isPidAlive, c as deriveLiveState, q as liveWatchers } from "./subcommands-CGFVw3RU.js";
5
5
  import "./e2e-jb0Hp43q.js";
6
6
  import "./webrtcLink-B7REGtK2.js";
7
7
  import "./remotes-CC-4GuJb.js";
@@ -588,4 +588,4 @@ async function ensureDaemon() {
588
588
 
589
589
  //#endregion
590
590
  export { daemonStatus, ensureDaemon, requestDaemonStop, runDaemon };
591
- //# sourceMappingURL=notifyDaemon-BB08sE7n.js.map
591
+ //# sourceMappingURL=notifyDaemon-DKoSrGSO.js.map
@@ -1,4 +1,4 @@
1
- import { r as getInstalledPackage } from "./versionChecker-eVbxzEL3.js";
1
+ import { r as getInstalledPackage } from "./versionChecker-OhTh_a0W.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-CYS7148m.js.map
228
+ //# sourceMappingURL=rustBinary-4qI6-ALa.js.map
@@ -1,9 +1,9 @@
1
- import "./ts-kJRKPdFi.js";
1
+ import "./ts-CJuOKFYC.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-eVbxzEL3.js";
3
+ import "./versionChecker-OhTh_a0W.js";
4
4
  import "./pidStore-BIvsBQ8X.js";
5
5
  import "./globalPidIndex-CoNr7tS8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bz-oXKG8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CLwNa69l.js";
7
7
  import { d as resolveSpawnCwd } from "./workspaceConfig-BgqK-31W.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-D4KvBh5w.js.map
144
+ //# sourceMappingURL=schedule-DclnMR6W.js.map
@@ -1,13 +1,13 @@
1
- import "./ts-kJRKPdFi.js";
1
+ import "./ts-CJuOKFYC.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-eVbxzEL3.js";
4
- import { t as findSpawnHiddenLauncher } from "./rustBinary-CYS7148m.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-OhTh_a0W.js";
4
+ import { t as findSpawnHiddenLauncher } from "./rustBinary-4qI6-ALa.js";
5
5
  import "./pidStore-BIvsBQ8X.js";
6
6
  import { a as updateGlobalPidStatus } from "./globalPidIndex-CoNr7tS8.js";
7
7
  import { t as pgidForWrapper } from "./reaper-CWF2_ATd.js";
8
8
  import "./configShared-0MnIQ652.js";
9
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bz-oXKG8.js";
10
- import { A as resolveOne, E as readPtysize, F as snapshotStatus, H as TYPING_BADGE, O as renderRawLog, S as listRecords, T as readNotes, V as writeToIpc, b as isUserTyping, f as extractNeedsInput, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-CbcBfInM.js";
9
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CLwNa69l.js";
10
+ import { A as resolveOne, E as readPtysize, F as snapshotStatus, H as TYPING_BADGE, O as renderRawLog, S as listRecords, T as readNotes, V as writeToIpc, b as isUserTyping, f as extractNeedsInput, l as deriveLiveStatus, o as controlCodeFromName, p as extractTaskCounts, u as extractBadges } from "./subcommands-CGFVw3RU.js";
11
11
  import "./e2e-jb0Hp43q.js";
12
12
  import "./webrtcLink-B7REGtK2.js";
13
13
  import "./remotes-CC-4GuJb.js";
@@ -849,9 +849,11 @@ Options:
849
849
  const status = await deriveLiveStatus(r);
850
850
  const question = status !== "exited" && !r.unresponsive ? await logNeedsInput(r.log_file, r.cli) : null;
851
851
  const lastActiveAt = r.log_file ? await stat(r.log_file).then((s) => s.mtimeMs).catch(() => r.started_at) : r.started_at;
852
+ const lastStdinAt = status !== "exited" && r.fifo_file ? await stat(r.fifo_file).then((s) => s.mtimeMs).catch(() => null) : null;
852
853
  return {
853
854
  ...r,
854
855
  last_active_at: lastActiveAt,
856
+ last_stdin_at: lastStdinAt,
855
857
  status: status === "exited" ? status : r.unresponsive ? "stuck" : question ? "needs_input" : status,
856
858
  question,
857
859
  title: await logTitle(r.log_file),
@@ -1558,4 +1560,4 @@ Options:
1558
1560
 
1559
1561
  //#endregion
1560
1562
  export { cmdServe };
1561
- //# sourceMappingURL=serve-BN0mUK9m.js.map
1563
+ //# sourceMappingURL=serve-B6suh4Mj.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-BN0mUK9m.js");
35
+ const { cmdServe } = await import("./serve-B6suh4Mj.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-CUrjwXNE.js.map
45
+ //# sourceMappingURL=setup-Bt9v70jO.js.map
@@ -1,7 +1,7 @@
1
1
  import "./logger-CDIsZ-Pp.js";
2
2
  import "./globalPidIndex-CoNr7tS8.js";
3
3
  import "./configShared-0MnIQ652.js";
4
- import { A as resolveOne, B as writeKeysPaced, C as matchKeyword, D as renderLogTailLines, E as readPtysize, F as snapshotStatus, I as stdinActivityPath, L as stopTipForCli, M as resolveResumeArgs, N as restartHintLines, O as renderRawLog, P as runSubcommand, R as submitAndConfirm, S as listRecords, T as readNotes, V as writeToIpc, _ 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 resolveReadWindow, k as renderRawLogLines, 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 waitForLogQuiet } from "./subcommands-CbcBfInM.js";
4
+ import { A as resolveOne, B as writeKeysPaced, C as matchKeyword, D as renderLogTailLines, E as readPtysize, F as snapshotStatus, I as stdinActivityPath, L as stopTipForCli, M as resolveResumeArgs, N as restartHintLines, O as renderRawLog, P as runSubcommand, R as submitAndConfirm, S as listRecords, T as readNotes, V as writeToIpc, _ 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 resolveReadWindow, k as renderRawLogLines, 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 waitForLogQuiet } from "./subcommands-CGFVw3RU.js";
5
5
  import "./e2e-jb0Hp43q.js";
6
6
  import "./webrtcLink-B7REGtK2.js";
7
7
  import "./remotes-CC-4GuJb.js";
@@ -1126,15 +1126,15 @@ async function runSubcommand(argv) {
1126
1126
  case "restart": return await cmdRestart(rest);
1127
1127
  case "note": return await cmdNote(rest);
1128
1128
  case "serve": {
1129
- const { cmdServe } = await import("./serve-BN0mUK9m.js");
1129
+ const { cmdServe } = await import("./serve-B6suh4Mj.js");
1130
1130
  return cmdServe(rest);
1131
1131
  }
1132
1132
  case "setup": {
1133
- const { cmdSetup } = await import("./setup-CUrjwXNE.js");
1133
+ const { cmdSetup } = await import("./setup-Bt9v70jO.js");
1134
1134
  return cmdSetup(rest);
1135
1135
  }
1136
1136
  case "schedule": {
1137
- const { cmdSchedule } = await import("./schedule-D4KvBh5w.js");
1137
+ const { cmdSchedule } = await import("./schedule-DclnMR6W.js");
1138
1138
  return cmdSchedule(rest);
1139
1139
  }
1140
1140
  case "remote": {
@@ -3963,7 +3963,7 @@ async function cmdNotify(rest) {
3963
3963
  }
3964
3964
  const ensure = async () => {
3965
3965
  if (!argv["ensure-daemon"]) return;
3966
- const { ensureDaemon } = await import("./notifyDaemon-BB08sE7n.js");
3966
+ const { ensureDaemon } = await import("./notifyDaemon-DKoSrGSO.js");
3967
3967
  await ensureDaemon().catch(() => null);
3968
3968
  };
3969
3969
  await heartbeatWatcher(parent, selfStartedAt);
@@ -4037,7 +4037,7 @@ async function cmdNotifyCursor(args) {
4037
4037
  }
4038
4038
  async function cmdNotifyd(rest) {
4039
4039
  const sub = rest[0] ?? "status";
4040
- const daemon = await import("./notifyDaemon-BB08sE7n.js");
4040
+ const daemon = await import("./notifyDaemon-DKoSrGSO.js");
4041
4041
  switch (sub) {
4042
4042
  case "run": return daemon.runDaemon();
4043
4043
  case "once": return daemon.runDaemon({ once: true });
@@ -4064,4 +4064,4 @@ async function cmdNotifyd(rest) {
4064
4064
 
4065
4065
  //#endregion
4066
4066
  export { resolveOne as A, writeKeysPaced as B, matchKeyword as C, renderLogTailLines as D, readPtysize as E, snapshotStatus as F, hostId as G, TYPING_BADGE as H, stdinActivityPath as I, readInbox as J, listInboxParents as K, stopTipForCli as L, resolveResumeArgs as M, restartHintLines as N, renderRawLog as O, runSubcommand as P, notifyDir as Q, submitAndConfirm as R, listRecords as S, readNotes as T, appendEvent as U, writeToIpc as V, gcInboxes as W, daemonLockDir as X, shouldStealLock as Y, daemonLockOwnerPath as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, resolveReadWindow as j, renderRawLogLines as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, controlCodeFromName as o, extractTaskCounts as p, liveWatchers as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, waitForLogQuiet as z };
4067
- //# sourceMappingURL=subcommands-CbcBfInM.js.map
4067
+ //# sourceMappingURL=subcommands-CGFVw3RU.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-eVbxzEL3.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-OhTh_a0W.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 { t as PidStore } from "./pidStore-BIvsBQ8X.js";
@@ -1824,4 +1824,4 @@ function sleep(ms) {
1824
1824
 
1825
1825
  //#endregion
1826
1826
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1827
- //# sourceMappingURL=ts-kJRKPdFi.js.map
1827
+ //# sourceMappingURL=ts-CJuOKFYC.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.170.1";
10
+ var version = "1.171.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-eVbxzEL3.js.map
218
+ //# sourceMappingURL=versionChecker-OhTh_a0W.js.map
@@ -549,6 +549,52 @@ export function nextIndex(len, i, dir) {
549
549
  // coordinate math lives here so it's unit-testable without a browser.
550
550
  // ---------------------------------------------------------------------------
551
551
 
552
+ // ---- collaborative presence: peer focus + stdin flash ----------------------
553
+ // The left panel colour-codes who's on which agent: your own selection is blue
554
+ // (the .sel bar), every OTHER human peer's focus is yellow (.peerfocus), and a
555
+ // row pulses when its stdin was just written — independent of focus, since an
556
+ // `ay send` feeds an agent no one is looking at. These pure helpers own the
557
+ // bookkeeping so index.html only measures the DOM.
558
+
559
+ // Detect agents whose stdin just advanced. `seen` maps _key -> the last
560
+ // last_stdin_at we observed; given the current entries, return the _keys whose
561
+ // last_stdin_at is NEWER than what we'd seen (someone typed / `ay send` pushed),
562
+ // and fold the new values into `seen`. A key seen for the FIRST time never
563
+ // flashes (else every agent would pulse on initial load) — only a later bump
564
+ // does. Keys no longer present are pruned so `seen` can't grow without bound.
565
+ export function advanceStdinFlashes(entries, seen) {
566
+ const fresh = [];
567
+ const alive = new Set();
568
+ for (const e of entries) {
569
+ const key = e._key;
570
+ if (!key) continue;
571
+ alive.add(key);
572
+ const at = e.last_stdin_at;
573
+ if (typeof at !== "number") continue;
574
+ const prev = seen.get(key);
575
+ if (prev !== undefined && at > prev) fresh.push(key);
576
+ if (prev === undefined || at > prev) seen.set(key, at);
577
+ }
578
+ for (const key of [...seen.keys()]) if (!alive.has(key)) seen.delete(key);
579
+ return fresh;
580
+ }
581
+
582
+ // Summarize fleet presence for the peers badge + per-row chips. `records` is one
583
+ // entry per OTHER viewer currently watching an agent: { key, viewer } (key = the
584
+ // composite _key of the agent they're on). Returns { total, byKey }: `total`
585
+ // distinct viewers (the "N peers" badge), `byKey` a Map of _key -> how many
586
+ // peers watch that agent (the yellow per-row chip + .peerfocus trigger).
587
+ export function focusSummary(records) {
588
+ const viewers = new Set();
589
+ const byKey = new Map();
590
+ for (const r of records) {
591
+ if (!r || !r.key || !r.viewer) continue;
592
+ viewers.add(r.viewer);
593
+ byKey.set(r.key, (byKey.get(r.key) || 0) + 1);
594
+ }
595
+ return { total: viewers.size, byKey };
596
+ }
597
+
552
598
  // Stable per-viewer hue (0..359) for colour-coding peers' selections.
553
599
  export function hashHue(s) {
554
600
  let h = 0;
package/lab/ui/index.html CHANGED
@@ -37,6 +37,9 @@
37
37
  --purple: #bc8cff;
38
38
  --pink: #f778ba;
39
39
  --red: #f85149;
40
+ /* Peer-focus yellow: a human collaborator (not you) is on this agent.
41
+ Distinct from your own blue (--accent) and the idle-dot amber. */
42
+ --peer: #e3b341;
40
43
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
41
44
  /* Safe-area insets — non-zero only in a standalone PWA on a notched /
42
45
  gesture-bar device (else 0, so these are harmless on desktop). Consumed
@@ -70,6 +73,7 @@
70
73
  --purple: #8250df;
71
74
  --pink: #bf3989;
72
75
  --red: #cf222e;
76
+ --peer: #bf8700;
73
77
  }
74
78
  }
75
79
  * {
@@ -420,6 +424,21 @@
420
424
  font-size: 11.5px;
421
425
  margin-top: 8px;
422
426
  }
427
+ .metaleft {
428
+ display: inline-flex;
429
+ align-items: center;
430
+ gap: 8px;
431
+ }
432
+ /* Peers badge beside the agent counter — how many other humans are in the
433
+ fleet right now. Yellow ("here") when a peer shares the agent you have
434
+ open, muted otherwise. Hidden entirely when you're alone. */
435
+ .peers {
436
+ color: var(--muted);
437
+ cursor: default;
438
+ }
439
+ .peers.here {
440
+ color: var(--peer);
441
+ }
423
442
  .connbadge {
424
443
  cursor: pointer;
425
444
  user-select: none;
@@ -818,6 +837,39 @@
818
837
  background: var(--panel);
819
838
  box-shadow: inset 3px 0 0 var(--accent);
820
839
  }
840
+ /* A human peer (not you) is watching this agent: yellow, mirrored on the
841
+ RIGHT edge so it can coexist with your own blue selection bar on the
842
+ left — a row you BOTH have open shows blue-left + yellow-right. */
843
+ .row.peerfocus {
844
+ box-shadow: inset -3px 0 0 var(--peer);
845
+ background: color-mix(in srgb, var(--peer) 7%, transparent);
846
+ }
847
+ .row.sel.peerfocus {
848
+ background: var(--panel);
849
+ box-shadow: inset 3px 0 0 var(--accent), inset -3px 0 0 var(--peer);
850
+ }
851
+ /* stdin just landed here (someone typed, or `ay send` pushed a command) —
852
+ pulse the row regardless of who (if anyone) is focused on it. Runs while
853
+ the .stdinflash class is present; index.html removes it after the window. */
854
+ .row.stdinflash {
855
+ animation: stdinpulse 0.6s ease-in-out infinite;
856
+ }
857
+ @keyframes stdinpulse {
858
+ 0%,
859
+ 100% {
860
+ background-color: transparent;
861
+ }
862
+ 50% {
863
+ background-color: color-mix(in srgb, var(--peer) 24%, transparent);
864
+ }
865
+ }
866
+ /* Per-row chip: how many peers are watching THIS agent (yellow, mono). */
867
+ .peerchip {
868
+ font-family: var(--mono);
869
+ font-size: 11px;
870
+ color: var(--peer);
871
+ flex: none;
872
+ }
821
873
  .r1 {
822
874
  display: flex;
823
875
  align-items: center;
@@ -1392,7 +1444,10 @@
1392
1444
  />
1393
1445
  </div>
1394
1446
  <div class="meta">
1395
- <span id="count"></span>
1447
+ <span class="metaleft">
1448
+ <span id="count"></span>
1449
+ <span id="peers" class="peers" hidden></span>
1450
+ </span>
1396
1451
  <span class="metaright">
1397
1452
  <button id="foldbtn" class="viewbtn" title="fold subagent trees">⊞ subs</button>
1398
1453
  <button id="sortbtn" class="viewbtn" title="cycle sort order">⇅ state</button>
@@ -1584,6 +1639,8 @@
1584
1639
  SORT_MODES,
1585
1640
  taskLabel,
1586
1641
  badgesFor,
1642
+ advanceStdinFlashes,
1643
+ focusSummary,
1587
1644
  hashHue,
1588
1645
  selFromBottom,
1589
1646
  parseSel,
@@ -1610,6 +1667,17 @@
1610
1667
 
1611
1668
  let entries = [];
1612
1669
  let sel = null; // selected agent's composite key (room#pid)
1670
+ // Collaborative presence, fleet-wide: focusByKey maps an agent's _key ->
1671
+ // how many OTHER human peers are watching it (yellow bars + per-row chip);
1672
+ // peerTotal is the distinct-peer count for the header badge. stdinSeen +
1673
+ // flashUntil drive the stdin pulse: stdinSeen remembers each agent's last
1674
+ // last_stdin_at so we can spot a bump, flashUntil holds the deadline the
1675
+ // row keeps pulsing to. See advanceStdinFlashes/focusSummary (console-logic).
1676
+ let focusByKey = new Map();
1677
+ let peerTotal = 0;
1678
+ const stdinSeen = new Map();
1679
+ const flashUntil = new Map();
1680
+ const STDIN_FLASH_MS = 1200;
1613
1681
  let es = null; // live-tail subscription closer
1614
1682
  let term = null; // xterm.js Terminal rendering the raw PTY stream
1615
1683
  let fit = null;
@@ -2713,10 +2781,65 @@
2713
2781
  }
2714
2782
  renderPeerSelections();
2715
2783
  }
2784
+ // Fleet-wide presence: unlike pollPresence (only the SELECTED agent's host,
2785
+ // for the terminal overlay), this reads EVERY live source's blackboard so
2786
+ // the left panel can colour any agent a peer is watching. Each viewer
2787
+ // self-reports the pid it's on; we map that back to the source's composite
2788
+ // _key. Best-effort — a source that 404s (older host) contributes nothing.
2789
+ async function pollFleetPresence() {
2790
+ const recs = [];
2791
+ await Promise.all(
2792
+ [...sources.values()].map(async (s) => {
2793
+ if (!s.tx || !s.live) return;
2794
+ let all;
2795
+ try {
2796
+ all = await s.tx.fetchJSON("/api/presence");
2797
+ } catch {
2798
+ return;
2799
+ }
2800
+ if (!Array.isArray(all)) return;
2801
+ const keyOfPid = new Map((s.agents || []).map((e) => [String(e.pid), e._key]));
2802
+ for (const v of all) {
2803
+ if (!v || v.viewer === myViewerId) continue;
2804
+ const key = keyOfPid.get(String(v.agent));
2805
+ // Prefix the viewer id with its source so the same id on two hosts
2806
+ // still counts as two distinct people in the fleet total.
2807
+ if (key) recs.push({ key, viewer: s.id + ":" + v.viewer });
2808
+ }
2809
+ }),
2810
+ );
2811
+ const sum = focusSummary(recs);
2812
+ focusByKey = sum.byKey;
2813
+ peerTotal = sum.total;
2814
+ paintPeersBadge();
2815
+ renderList();
2816
+ }
2817
+ // "N peers" badge beside the agent counter. Yellow when a peer shares the
2818
+ // agent you have open ("same agent"); its tooltip breaks the count down by
2819
+ // agent so you can see who's where. Hidden when you're the only one here.
2820
+ function paintPeersBadge() {
2821
+ const el = $("peers");
2822
+ if (!el) return;
2823
+ if (!peerTotal) {
2824
+ el.hidden = true;
2825
+ el.removeAttribute("title");
2826
+ return;
2827
+ }
2828
+ el.hidden = false;
2829
+ el.textContent = `👤 ${peerTotal} peer${peerTotal === 1 ? "" : "s"}`;
2830
+ el.classList.toggle("here", !!(sel && focusByKey.get(sel)));
2831
+ const lines = [...focusByKey.entries()].map(([k, n]) => {
2832
+ const e = entries.find((x) => x._key === k);
2833
+ const name = e ? e.title || cliLabel(e) || ident(e) || `pid ${e.pid}` : k;
2834
+ return ` ${n} on ${name}`;
2835
+ });
2836
+ el.title = "peers watching agents in this fleet:\n" + lines.join("\n");
2837
+ }
2716
2838
  // 3s heartbeat (< the host's 12s TTL): refresh our presence + read others',
2717
2839
  // and follow the canonical grid size (resize+scale, never reflow) so all
2718
2840
  // viewers stay on one shared canvas and selections line up.
2719
2841
  setInterval(() => {
2842
+ pollFleetPresence(); // fleet-wide peer focus (independent of what's selected)
2720
2843
  if (!sel) return;
2721
2844
  sendPresence();
2722
2845
  pollPresence();
@@ -2727,6 +2850,20 @@
2727
2850
  .then(followAgentSize)
2728
2851
  .catch(() => {});
2729
2852
  }, 3000);
2853
+ // Fast pulse loop: drive the stdin flash on the STABLE list DOM (toggle a
2854
+ // class, no re-render) so a pulse begins/ends promptly regardless of the
2855
+ // list's own render cadence. Cheap — a no-op until something is flashing.
2856
+ setInterval(() => {
2857
+ if (!flashUntil.size) return;
2858
+ const now = Date.now();
2859
+ for (const [k, until] of [...flashUntil.entries()]) if (until <= now) flashUntil.delete(k);
2860
+ const listEl = $("list");
2861
+ if (!listEl) return;
2862
+ for (const el of listEl.querySelectorAll(".row[data-key]")) {
2863
+ const on = (flashUntil.get(el.getAttribute("data-key")) || 0) > now;
2864
+ el.classList.toggle("stdinflash", on);
2865
+ }
2866
+ }, 200);
2730
2867
 
2731
2868
  // Peer-selection overlay. The coordinate math (parseSel / selSegments /
2732
2869
  // hashHue) lives in console-logic.js (unit-tested); here we just measure the
@@ -3311,6 +3448,22 @@
3311
3448
  return `<div class="ghead g-${r.kind}">${r.branch ? `<span class="tbranch">${esc(r.branch)}</span>` : ""}<span class="gicon">${icon}</span><span class="glabel">${esc(r.label || "")}</span>${ctag}<span class="gkind">${r.kind}</span></div>`;
3312
3449
  }
3313
3450
 
3451
+ // Extra row classes for collaborative presence: a yellow bar when a human
3452
+ // peer is watching this agent, plus the stdin pulse while its input just
3453
+ // advanced. Empty in the solo case, so a single-user list is unchanged.
3454
+ function rowFlags(e) {
3455
+ const peers = focusByKey.get(e._key) || 0;
3456
+ const flash = (flashUntil.get(e._key) || 0) > Date.now();
3457
+ return (peers ? " peerfocus" : "") + (flash ? " stdinflash" : "");
3458
+ }
3459
+ // A yellow chip showing how many peers are watching this agent ("" if none).
3460
+ function peerChipHtml(e) {
3461
+ const n = focusByKey.get(e._key) || 0;
3462
+ return n
3463
+ ? `<span class="peerchip" title="${n} peer${n === 1 ? "" : "s"} watching this agent">👤${n}</span>`
3464
+ : "";
3465
+ }
3466
+
3314
3467
  function renderList() {
3315
3468
  const toks = $("q").value.trim().split(/\s+/).filter(Boolean);
3316
3469
  // Build the full signalling-server > rooms > peers > agents > subagents
@@ -3325,6 +3478,7 @@
3325
3478
  ? fullRows.filter((r) => !(r.kind === "agent" && r.parentEntry))
3326
3479
  : fullRows;
3327
3480
  $("count").textContent = `${agentRows.length} / ${entries.length} agents`;
3481
+ paintPeersBadge(); // keep the peers badge in step with sel / the entry list
3328
3482
  $("foldbtn").textContent = foldSubs ? "⊞ subs" : "⊟ subs";
3329
3483
  $("foldbtn").classList.toggle("on", !foldSubs);
3330
3484
  $("viewbtn").classList.toggle("on", compactList);
@@ -3347,7 +3501,7 @@
3347
3501
  const t = e.title || e.prompt || "";
3348
3502
  const id = compactIdent(e, ctx, 3, r.parentEntry);
3349
3503
  const cli = cliLabel(e);
3350
- return `<div class="row crow ${e._key === sel ? "sel" : ""}" data-key="${esc(e._key)}">
3504
+ return `<div class="row crow ${e._key === sel ? "sel" : ""}${rowFlags(e)}" data-key="${esc(e._key)}">
3351
3505
  ${r.branch ? `<span class="tbranch">${esc(r.branch)}</span>` : ""}
3352
3506
  <span class="dot ${esc(e.status)}"></span>
3353
3507
  ${hasIdent(id) ? `<span class="cident" title="${esc(fullIdent(e))}">${esc(id)}</span>` : ""}
@@ -3357,6 +3511,7 @@
3357
3511
  ${taskChipHtml(e)}
3358
3512
  ${badgeChipsHtml(e)}
3359
3513
  ${gitChipHtml(e)}
3514
+ ${peerChipHtml(e)}
3360
3515
  <span class="age">${age(e)}</span></div>`;
3361
3516
  })
3362
3517
  .join("") || `<div class="empty">no match</div>`;
@@ -3376,7 +3531,7 @@
3376
3531
  `<span class="rtag" data-k="${k}"><span style="opacity:.55">${k}:</span>${esc(v)}</span>`,
3377
3532
  )
3378
3533
  .join("");
3379
- return `<div class="row ${e._key === sel ? "sel" : ""}" data-key="${esc(e._key)}">
3534
+ return `<div class="row ${e._key === sel ? "sel" : ""}${rowFlags(e)}" data-key="${esc(e._key)}">
3380
3535
  <div class="r1">${r.branch ? `<span class="tbranch">${esc(r.branch)}</span>` : ""}<span class="dot ${esc(e.status)}"></span>
3381
3536
  <span class="name">${esc(cliLabel(e) || ident(e) || "agent")}</span>
3382
3537
  <span class="badge">pid ${e.pid}</span>
@@ -3384,6 +3539,7 @@
3384
3539
  ${taskChipHtml(e)}
3385
3540
  ${badgeChipsHtml(e)}
3386
3541
  ${gitChipHtml(e)}
3542
+ ${peerChipHtml(e)}
3387
3543
  <span class="age">${age(e)}</span></div>
3388
3544
  ${e.title ? `<div class="rowtitle" title="${esc(e.title)}">${esc(e.title)}</div>` : ""}
3389
3545
  ${
@@ -3445,6 +3601,13 @@
3445
3601
  function mergeRender() {
3446
3602
  const srcs = [...sources.values()];
3447
3603
  entries = srcs.flatMap((s) => s.agents || []);
3604
+ // Spot agents whose stdin just advanced (last_stdin_at bumped since we
3605
+ // last saw them — someone typed, or `ay send` pushed input) and arm the
3606
+ // pulse. Runs before renderList so the flashing rows render pulsing; the
3607
+ // fast loop above stops them when the window lapses. Streamed deltas carry
3608
+ // the new mtime, so a silent `ay send` still trips the flash within ~1s.
3609
+ const until = Date.now() + STDIN_FLASH_MS;
3610
+ for (const k of advanceStdinFlashes(entries, stdinSeen)) flashUntil.set(k, until);
3448
3611
  // Badge: total agents + how many rooms are live. Red only when nothing
3449
3612
  // at all is reachable (no source answered).
3450
3613
  const roomSrcs = srcs.filter((s) => s.id !== LOCAL);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.170.1",
3
+ "version": "1.171.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/serve.ts CHANGED
@@ -1255,9 +1255,23 @@ export async function cmdServe(rest: string[]): Promise<number> {
1255
1255
  .then((s) => s.mtimeMs)
1256
1256
  .catch(() => r.started_at)
1257
1257
  : r.started_at;
1258
+ // Last-stdin time: the mtime of the agent's stdin FIFO, which the kernel
1259
+ // bumps on EVERY write to it — the console composer, a remote `ay send`, or
1260
+ // a peer's `ay send` all funnel through writeToIpc(fifo_file, …). So this is
1261
+ // a source-agnostic "someone just fed this agent input" signal, distinct from
1262
+ // last_active_at (stdout). The console flashes a row when it advances, so a
1263
+ // collaborator sees keystrokes land even when they weren't the one typing.
1264
+ // Null when there's no live input pipe (exited, or no fifo yet).
1265
+ const lastStdinAt =
1266
+ status !== "exited" && r.fifo_file
1267
+ ? await stat(r.fifo_file)
1268
+ .then((s) => s.mtimeMs)
1269
+ .catch(() => null)
1270
+ : null;
1258
1271
  return {
1259
1272
  ...r,
1260
1273
  last_active_at: lastActiveAt,
1274
+ last_stdin_at: lastStdinAt,
1261
1275
  // Precedence: exited stays exited; the Rust supervisor's unresponsive flag is
1262
1276
  // an authoritative wedge signal (`stuck`); then a blocked menu (`needs_input`);
1263
1277
  // else the base live status — so the console's dot matches `ay ls`. (A dead
@@ -1,8 +0,0 @@
1
- import "./ts-kJRKPdFi.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-eVbxzEL3.js";
4
- import "./pidStore-BIvsBQ8X.js";
5
- import "./globalPidIndex-CoNr7tS8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-Bz-oXKG8.js";
7
-
8
- export { SUPPORTED_CLIS };