claude-yes 1.162.0 → 1.164.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--UKFJGU-.js";
1
+ import { t as CLIS_CONFIG } from "./ts-BzyVSzo1.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-BNLYYzBZ.js.map
8
+ //# sourceMappingURL=SUPPORTED_CLIS-C_J_okdw.js.map
@@ -0,0 +1,8 @@
1
+ import "./ts-BzyVSzo1.js";
2
+ import "./logger-CDIsZ-Pp.js";
3
+ import "./versionChecker-CCHuozmQ.js";
4
+ import "./pidStore-BfoBWUjc.js";
5
+ import "./globalPidIndex-DlmmJlO8.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C_J_okdw.js";
7
+
8
+ export { SUPPORTED_CLIS };
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import { t as invokedCliName } from "./invokedCli-uqM2YYA7.js";
3
3
  import { n as logger } from "./logger-CDIsZ-Pp.js";
4
- import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-DJObC8O_.js";
4
+ import { i as versionString, n as displayVersion, r as getInstalledPackage, t as checkAndAutoUpdate } from "./versionChecker-CCHuozmQ.js";
5
5
  import { argv } from "process";
6
6
  import { execFileSync, spawn } from "child_process";
7
7
  import ms from "ms";
@@ -481,7 +481,7 @@ function buildRustArgs(argv, cliFromScript, supportedClis) {
481
481
  const rawArg = process.argv[2];
482
482
  const managerCommands = !invokedCliName(process.argv);
483
483
  const isHelpFlag = rawArg === "-h" || rawArg === "--help";
484
- const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-DeW-4FGU.js");
484
+ const { isSubcommand, runSubcommand, cmdHelp } = await import("./subcommands-C-aCmtre.js");
485
485
  if (isHelpFlag && process.argv.length === 3) {
486
486
  cmdHelp(managerCommands);
487
487
  process.exit(0);
@@ -514,7 +514,7 @@ if (config.useRust) {
514
514
  }
515
515
  }
516
516
  if (rustBinary) {
517
- const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-C9TQK7ME.js");
517
+ const { SUPPORTED_CLIS } = await import("./SUPPORTED_CLIS-CmJam_wA.js");
518
518
  const rustArgs = buildRustArgs(process.argv, config.cli, SUPPORTED_CLIS);
519
519
  if (config.verbose) {
520
520
  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--UKFJGU-.js";
1
+ import { a as removeControlCharacters, i as AgentContext, n as agentYes, r as config, t as CLIS_CONFIG } from "./ts-BzyVSzo1.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DJObC8O_.js";
3
+ import "./versionChecker-CCHuozmQ.js";
4
4
  import "./pidStore-BfoBWUjc.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
6
 
@@ -1,9 +1,9 @@
1
- import "./ts--UKFJGU-.js";
1
+ import "./ts-BzyVSzo1.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DJObC8O_.js";
3
+ import "./versionChecker-CCHuozmQ.js";
4
4
  import "./pidStore-BfoBWUjc.js";
5
5
  import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BNLYYzBZ.js";
6
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C_J_okdw.js";
7
7
  import { o as resolveSpawnCwd } from "./workspaceConfig-B0Q9-q2B.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-DzFEVEoq.js.map
144
+ //# sourceMappingURL=schedule-B-Ncgklt.js.map
@@ -1,16 +1,16 @@
1
- import "./ts--UKFJGU-.js";
1
+ import "./ts-BzyVSzo1.js";
2
2
  import "./logger-CDIsZ-Pp.js";
3
- import { r as getInstalledPackage } from "./versionChecker-DJObC8O_.js";
3
+ import { r as getInstalledPackage } from "./versionChecker-CCHuozmQ.js";
4
4
  import "./pidStore-BfoBWUjc.js";
5
5
  import { a as updateGlobalPidStatus } from "./globalPidIndex-DlmmJlO8.js";
6
6
  import { t as pgidForWrapper } from "./reaper-C-eWAxIj.js";
7
7
  import "./configShared-CEyhl0WH.js";
8
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BNLYYzBZ.js";
8
+ import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-C_J_okdw.js";
9
9
  import "./e2e-Bfw7qL9O.js";
10
10
  import "./webrtcLink-CBZkZ-LT.js";
11
11
  import "./remotes-DBCvpp3B.js";
12
12
  import { a as isProvisionAllowed, i as hasSpawnHook, n as getSpawnHook, o as resolveSpawnCwd, t as getProvisionRoot } from "./workspaceConfig-B0Q9-q2B.js";
13
- import { D as writeToIpc, T as snapshotStatus, _ as readPtysize, b as resolveOne, c as extractTaskCounts, g as readNotes, i as controlCodeFromName, m as listRecords, o as deriveLiveStatus, v as renderRawLog } from "./subcommands-CKMEWv6U.js";
13
+ import { D as writeToIpc, T as snapshotStatus, _ as readPtysize, b as resolveOne, c as extractTaskCounts, g as readNotes, i as controlCodeFromName, m as listRecords, o as deriveLiveStatus, s as extractNeedsInput, v as renderRawLog } from "./subcommands-BbKngO7F.js";
14
14
  import yargs from "yargs";
15
15
  import { mkdir, open, readFile, stat, unlink, writeFile } from "fs/promises";
16
16
  import { homedir, hostname, userInfo } from "os";
@@ -625,6 +625,24 @@ Options:
625
625
  return null;
626
626
  }
627
627
  };
628
+ const niCache = /* @__PURE__ */ new Map();
629
+ const logNeedsInput = async (logFile, cli) => {
630
+ if (!logFile) return null;
631
+ try {
632
+ const { size, mtimeMs } = await stat(logFile);
633
+ const hit = niCache.get(logFile);
634
+ if (hit && hit.size === size && hit.mtimeMs === mtimeMs) return hit.question;
635
+ const question = (await extractNeedsInput(logFile, cli))?.question ?? null;
636
+ niCache.set(logFile, {
637
+ size,
638
+ mtimeMs,
639
+ question
640
+ });
641
+ return question;
642
+ } catch {
643
+ return null;
644
+ }
645
+ };
628
646
  const GIT_DEBOUNCE_MS = 800;
629
647
  const GIT_SAFETY_MS = 6e4;
630
648
  const runGit = async (args, cwd) => {
@@ -643,17 +661,43 @@ Options:
643
661
  }
644
662
  };
645
663
  const parseGitStatus = (out) => {
646
- const lines = out.split("\n");
647
- const h = /^## (.+)$/.exec(lines[0] ?? "")?.[1] ?? "";
648
- const unborn = /^No commits yet on (.+)$/.exec(h);
649
- const branch = unborn ? unborn[1] : /^(.+?)(?:\.\.\.|\s|$)/.exec(h)?.[1] || null;
650
- const ahead = Number(/\bahead (\d+)/.exec(h)?.[1] ?? 0);
651
- const behind = Number(/\bbehind (\d+)/.exec(h)?.[1] ?? 0);
652
- const changed = lines.slice(1).filter((l) => l.trim().length > 0).length;
664
+ let branch = null;
665
+ let ahead = 0;
666
+ let behind = 0;
667
+ let changed = 0;
668
+ let pins = 0;
669
+ let subDirty = 0;
670
+ for (const line of out.split("\n")) {
671
+ if (line.length === 0) continue;
672
+ if (line[0] === "#") {
673
+ const head = /^# branch\.head (.+)$/.exec(line);
674
+ if (head) {
675
+ branch = head[1] === "(detached)" ? null : head[1];
676
+ continue;
677
+ }
678
+ const ab = /^# branch\.ab \+(\d+) -(\d+)/.exec(line);
679
+ if (ab) {
680
+ ahead = Number(ab[1]);
681
+ behind = Number(ab[2]);
682
+ }
683
+ continue;
684
+ }
685
+ const type = line[0];
686
+ if (type === "?") changed++;
687
+ else if (type === "u") changed++;
688
+ else if (type === "1" || type === "2") {
689
+ const sub = line.split(" ")[2] ?? "N...";
690
+ if (sub[0] === "S") if (sub[1] === "C") pins++;
691
+ else subDirty++;
692
+ else changed++;
693
+ }
694
+ }
653
695
  return {
654
696
  branch,
655
697
  dirty: changed > 0,
656
698
  changed,
699
+ pins,
700
+ subDirty,
657
701
  ahead,
658
702
  behind
659
703
  };
@@ -676,7 +720,7 @@ Options:
676
720
  try {
677
721
  const out = await runGit([
678
722
  "status",
679
- "--porcelain",
723
+ "--porcelain=v2",
680
724
  "--branch"
681
725
  ], root);
682
726
  if (out != null) rw.val = parseGitStatus(out);
@@ -717,9 +761,11 @@ Options:
717
761
  };
718
762
  const withMeta = async (r) => {
719
763
  const status = await deriveLiveStatus(r);
764
+ const question = status !== "exited" && !r.unresponsive ? await logNeedsInput(r.log_file, r.cli) : null;
720
765
  return {
721
766
  ...r,
722
- status: status !== "exited" && r.unresponsive ? "stuck" : status,
767
+ status: status === "exited" ? status : r.unresponsive ? "stuck" : question ? "needs_input" : status,
768
+ question,
723
769
  title: await logTitle(r.log_file),
724
770
  git: status === "exited" ? null : await gitStatus(r.cwd),
725
771
  tasks: status === "exited" ? null : await logTasks(r.log_file)
@@ -1396,4 +1442,4 @@ Options:
1396
1442
 
1397
1443
  //#endregion
1398
1444
  export { cmdServe };
1399
- //# sourceMappingURL=serve-BFnjq7JW.js.map
1445
+ //# sourceMappingURL=serve-yVh9Y6ot.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-BFnjq7JW.js");
35
+ const { cmdServe } = await import("./serve-yVh9Y6ot.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-BmUQ_Zbt.js.map
45
+ //# sourceMappingURL=setup-BmYrvUsz.js.map
@@ -556,15 +556,15 @@ async function runSubcommand(argv) {
556
556
  case "restart": return await cmdRestart(rest);
557
557
  case "note": return await cmdNote(rest);
558
558
  case "serve": {
559
- const { cmdServe } = await import("./serve-BFnjq7JW.js");
559
+ const { cmdServe } = await import("./serve-yVh9Y6ot.js");
560
560
  return cmdServe(rest);
561
561
  }
562
562
  case "setup": {
563
- const { cmdSetup } = await import("./setup-BmUQ_Zbt.js");
563
+ const { cmdSetup } = await import("./setup-BmYrvUsz.js");
564
564
  return cmdSetup(rest);
565
565
  }
566
566
  case "schedule": {
567
- const { cmdSchedule } = await import("./schedule-DzFEVEoq.js");
567
+ const { cmdSchedule } = await import("./schedule-B-Ncgklt.js");
568
568
  return cmdSchedule(rest);
569
569
  }
570
570
  case "remote": {
@@ -2795,4 +2795,4 @@ async function cmdResultSet(rest) {
2795
2795
 
2796
2796
  //#endregion
2797
2797
  export { restartHintLines as C, writeToIpc as D, stopTipForCli as E, resolveResumeArgs as S, snapshotStatus as T, readPtysize as _, cursorAbs as a, resolveOne as b, extractTaskCounts as c, isExitRequest as d, isPidAlive as f, readNotes as g, matchKeyword as h, controlCodeFromName as i, finalizedLines as l, listRecords as m, READ_PAGE_DEFAULT as n, deriveLiveStatus as o, isSubcommand as p, cmdHelp as r, extractNeedsInput as s, GRACEFUL_EXIT_COMMANDS as t, isAgentStuck as u, renderRawLog as v, runSubcommand as w, resolveReadWindow as x, renderRawLogLines as y };
2798
- //# sourceMappingURL=subcommands-CKMEWv6U.js.map
2798
+ //# sourceMappingURL=subcommands-BbKngO7F.js.map
@@ -4,6 +4,6 @@ import "./configShared-CEyhl0WH.js";
4
4
  import "./e2e-Bfw7qL9O.js";
5
5
  import "./webrtcLink-CBZkZ-LT.js";
6
6
  import "./remotes-DBCvpp3B.js";
7
- import { C as restartHintLines, D as writeToIpc, E as stopTipForCli, S as resolveResumeArgs, T as snapshotStatus, _ as readPtysize, a as cursorAbs, b as resolveOne, c as extractTaskCounts, d as isExitRequest, f as isPidAlive, g as readNotes, h as matchKeyword, i as controlCodeFromName, l as finalizedLines, m as listRecords, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isSubcommand, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as renderRawLog, w as runSubcommand, x as resolveReadWindow, y as renderRawLogLines } from "./subcommands-CKMEWv6U.js";
7
+ import { C as restartHintLines, D as writeToIpc, E as stopTipForCli, S as resolveResumeArgs, T as snapshotStatus, _ as readPtysize, a as cursorAbs, b as resolveOne, c as extractTaskCounts, d as isExitRequest, f as isPidAlive, g as readNotes, h as matchKeyword, i as controlCodeFromName, l as finalizedLines, m as listRecords, n as READ_PAGE_DEFAULT, o as deriveLiveStatus, p as isSubcommand, r as cmdHelp, s as extractNeedsInput, t as GRACEFUL_EXIT_COMMANDS, u as isAgentStuck, v as renderRawLog, w as runSubcommand, x as resolveReadWindow, y as renderRawLogLines } from "./subcommands-BbKngO7F.js";
8
8
 
9
9
  export { cmdHelp, isSubcommand, runSubcommand };
@@ -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-DJObC8O_.js";
2
+ import { r as getInstalledPackage } from "./versionChecker-CCHuozmQ.js";
3
3
  import { t as agentYesHome } from "./agentYesHome-_eJa3DaX.js";
4
4
  import { i as shouldUseLock, r as releaseLock, t as acquireLock } from "./runningLock-V4qvXgAw.js";
5
5
  import { t as PidStore } from "./pidStore-BfoBWUjc.js";
@@ -1803,4 +1803,4 @@ function sleep(ms) {
1803
1803
 
1804
1804
  //#endregion
1805
1805
  export { removeControlCharacters as a, AgentContext as i, agentYes as n, config as r, CLIS_CONFIG as t };
1806
- //# sourceMappingURL=ts--UKFJGU-.js.map
1806
+ //# sourceMappingURL=ts-BzyVSzo1.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.162.0";
10
+ var version = "1.164.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-DJObC8O_.js.map
218
+ //# sourceMappingURL=versionChecker-CCHuozmQ.js.map
@@ -153,14 +153,19 @@ export function tagsFor(e) {
153
153
  }
154
154
 
155
155
  // Compact git indicator from the record's `git` snapshot (server-side
156
- // `git status --porcelain --branch`): "±3" changed files, "↑1" ahead, "↓2"
157
- // behind. Returns "" when there's no git info or the tree is clean and in sync,
158
- // so a tidy repo adds no noise. Branch itself is shown via the path identity.
156
+ // `git status --porcelain=v2 --branch`): "±3" real changed files, "2"
157
+ // submodule pin-bumps, "⊙1" submodule internal dirt, "↑1" ahead, "↓2" behind.
158
+ // Pins/sub-dirt are split out of "±" so submodule drift (constant in a repo with
159
+ // many submodules) never buries the real file edits. Returns "" when there's no
160
+ // git info or the tree is clean and in sync, so a tidy repo adds no noise. Branch
161
+ // itself is shown via the path identity.
159
162
  export function gitLabel(e) {
160
163
  const g = e.git;
161
164
  if (!g) return "";
162
165
  const parts = [];
163
166
  if (g.changed > 0) parts.push("±" + g.changed);
167
+ if (g.pins > 0) parts.push("⑂" + g.pins);
168
+ if (g.subDirty > 0) parts.push("⊙" + g.subDirty);
164
169
  if (g.ahead > 0) parts.push("↑" + g.ahead);
165
170
  if (g.behind > 0) parts.push("↓" + g.behind);
166
171
  return parts.join(" ");
@@ -521,10 +526,21 @@ export function fitTransform(gridW, gridH, paneW, paneH) {
521
526
 
522
527
  // Browser-tab title: "<glyph> <selected agent title> - agent-yes", or the bare
523
528
  // console title when nothing is selected (blank/whitespace name). The leading
524
- // glyph mirrors the agent's status dot — active, idle, exited — so the tab
525
- // shows liveness at a glance; an unknown status adds no glyph.
529
+ // glyph mirrors the agent's status dot — needs_input ("your turn"), stuck,
530
+ // ● active, ○ idle, ✗ exited — so the tab shows liveness at a glance even in a
531
+ // background tab; an unknown status adds no glyph.
526
532
  export function statusGlyph(status) {
527
- return status === "active" ? "●" : status === "idle" ? "○" : status === "exited" ? "✗" : "";
533
+ return status === "needs_input"
534
+ ? "⌨"
535
+ : status === "stuck"
536
+ ? "⚠"
537
+ : status === "active"
538
+ ? "●"
539
+ : status === "idle"
540
+ ? "○"
541
+ : status === "exited"
542
+ ? "✗"
543
+ : "";
528
544
  }
529
545
  export function docTitle(name, status) {
530
546
  const n = name && String(name).trim();
package/lab/ui/index.html CHANGED
@@ -891,6 +891,11 @@
891
891
  color: var(--green);
892
892
  border-color: #2a3a2a;
893
893
  }
894
+ /* The pending question when an agent needs_input — blue accent to match the
895
+ needs_input dot, reading as "your turn / here's what it's asking". */
896
+ .detail.ask {
897
+ color: var(--accent);
898
+ }
894
899
  .detail {
895
900
  color: var(--muted);
896
901
  font-size: 12.5px;
@@ -3187,13 +3192,19 @@
3187
3192
  );
3188
3193
  }
3189
3194
 
3190
- // A small git chip (±changed ↑ahead ↓behind) for a row, amber when the tree
3191
- // is dirty. Empty for clean/in-sync repos and non-git cwds no noise.
3195
+ // A small git chip (±changed ⑂pins ⊙sub-dirty ↑ahead ↓behind) for a row,
3196
+ // amber when the tree has real file changes. Submodule pin-drift shows as a
3197
+ // muted "⑂" that never turns the chip amber, so it can't bury real edits.
3198
+ // Empty for clean/in-sync repos and non-git cwds — no noise.
3192
3199
  function gitChipHtml(e) {
3193
3200
  const label = gitLabel(e);
3194
3201
  if (!label) return "";
3195
3202
  const g = e.git || {};
3196
- const tip = `git: ${g.changed || 0} changed · ${g.ahead || 0} ahead · ${g.behind || 0} behind`;
3203
+ const tip =
3204
+ `git: ${g.changed || 0} changed file${(g.changed || 0) === 1 ? "" : "s"}` +
3205
+ (g.pins ? ` · ${g.pins} submodule pin-bump${g.pins === 1 ? "" : "s"}` : "") +
3206
+ (g.subDirty ? ` · ${g.subDirty} submodule dirty` : "") +
3207
+ ` · ${g.ahead || 0} ahead · ${g.behind || 0} behind`;
3197
3208
  return `<span class="git${g.dirty ? " dirty" : ""}" title="${esc(tip)}">${esc(label)}</span>`;
3198
3209
  }
3199
3210
 
@@ -3285,7 +3296,13 @@
3285
3296
  ${gitChipHtml(e)}
3286
3297
  <span class="age">${age(e)}</span></div>
3287
3298
  ${e.title ? `<div class="rowtitle" title="${esc(e.title)}">${esc(e.title)}</div>` : ""}
3288
- ${e.prompt ? `<div class="detail" title="${esc(e.prompt)}">${esc(e.prompt)}</div>` : ""}
3299
+ ${
3300
+ e.status === "needs_input" && e.question
3301
+ ? `<div class="detail ask" title="${esc(e.question)}">⌨ ${esc(e.question)}</div>`
3302
+ : e.prompt
3303
+ ? `<div class="detail" title="${esc(e.prompt)}">${esc(e.prompt)}</div>`
3304
+ : ""
3305
+ }
3289
3306
  <div class="rowtags">${tags}</div>
3290
3307
  </div>`;
3291
3308
  })
@@ -3918,7 +3935,7 @@
3918
3935
  return `<div class="omni-row omni-spawn${sc}" data-i="${i}">
3919
3936
  <span class="dot active"></span>
3920
3937
  <div class="omni-main">
3921
- <div class="omni-title">⑂ Fork <b>${esc(r.srcBranch)}</b> &amp; run <span style="opacity:.55">(carries uncommitted work)</span></div>
3938
+ <div class="omni-title">⑂ Fork <b>${esc(r.srcBranch)}</b> &amp; run <span style="opacity:.55">(clean committed work only)</span></div>
3922
3939
  <div class="omni-sub">${esc(r.fromCwd)} &nbsp;→&nbsp; new branch <b>${esc(r.branch)}</b> &nbsp;·&nbsp; ⏎ to edit &amp; launch</div>
3923
3940
  </div></div>`;
3924
3941
  }
@@ -4050,14 +4067,12 @@
4050
4067
  return;
4051
4068
  }
4052
4069
  if (row && row.kind === "fork") {
4053
- // Editable at launch (auto-slug is the default); carries the source's WIP.
4070
+ // Editable at launch (auto-slug is the default). The fork is clean —
4071
+ // committed work only, the source's uncommitted changes stay put.
4054
4072
  // Fork from the source PINNED on the row (row.fromCwd), not a freshly
4055
4073
  // re-resolved anchor — the anchor can drift as the list refreshes, but the
4056
4074
  // branch we fork from must be the one the row was built for.
4057
- const branch = prompt(
4058
- "Fork to a new branch (carries your uncommitted work):",
4059
- row.branch,
4060
- );
4075
+ const branch = prompt("Fork to a new branch (clean — committed work only):", row.branch);
4061
4076
  if (!branch || !branch.trim()) return; // cancelled — leave the omnibox open
4062
4077
  closeOmni(false);
4063
4078
  await spawnAndSelect(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.162.0",
3
+ "version": "1.164.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
@@ -9,6 +9,7 @@ import yargs from "yargs";
9
9
  import {
10
10
  controlCodeFromName,
11
11
  deriveLiveStatus,
12
+ extractNeedsInput,
12
13
  extractTaskCounts,
13
14
  listRecords,
14
15
  readNotes,
@@ -172,12 +173,15 @@ function loginShellEnv(): Record<string, string> | null {
172
173
  // Delimiters fence off the env dump from any banner/prompt noise the rc files
173
174
  // print to stdout; `env -0` is NUL-separated so values with newlines survive.
174
175
  const delim = "_AY_SHELL_ENV_DELIM_";
175
- const res = Bun.spawnSync([shell, "-ilc", `printf %s "${delim}"; env -0; printf %s "${delim}"`], {
176
- stdin: "ignore",
177
- stderr: "ignore",
178
- timeout: 5_000,
179
- env: process.env as Record<string, string>,
180
- });
176
+ const res = Bun.spawnSync(
177
+ [shell, "-ilc", `printf %s "${delim}"; env -0; printf %s "${delim}"`],
178
+ {
179
+ stdin: "ignore",
180
+ stderr: "ignore",
181
+ timeout: 5_000,
182
+ env: process.env as Record<string, string>,
183
+ },
184
+ );
181
185
  const out = res.stdout?.toString() ?? "";
182
186
  const start = out.indexOf(delim);
183
187
  const end = out.lastIndexOf(delim);
@@ -930,8 +934,37 @@ export async function cmdServe(rest: string[]): Promise<number> {
930
934
  }
931
935
  };
932
936
 
937
+ // Per-agent "waiting on you" detection: the agent is parked on an interactive
938
+ // menu it did NOT auto-resolve (config `needsInput` patterns). Same source and
939
+ // classifier as `ay ls` / `ay status`, so the console's dot matches the CLI's
940
+ // needs_input. Cached per (size, mtime) exactly like logTitle/logTasks — each
941
+ // miss renders a log window through xterm, so the 1s list tick stays cheap on
942
+ // an idle fleet. Returns the pending question text (surfaced in the UI), or
943
+ // null when the agent isn't blocked.
944
+ const niCache = new Map<string, { size: number; mtimeMs: number; question: string | null }>();
945
+ const logNeedsInput = async (
946
+ logFile: string | null | undefined,
947
+ cli: string,
948
+ ): Promise<string | null> => {
949
+ if (!logFile) return null;
950
+ try {
951
+ const { size, mtimeMs } = await stat(logFile);
952
+ const hit = niCache.get(logFile);
953
+ if (hit && hit.size === size && hit.mtimeMs === mtimeMs) return hit.question;
954
+ const ni = await extractNeedsInput(logFile, cli);
955
+ const question = ni?.question ?? null;
956
+ niCache.set(logFile, { size, mtimeMs, question });
957
+ return question;
958
+ } catch {
959
+ return null;
960
+ }
961
+ };
962
+
933
963
  // Per-repo git snapshot for the list (branch + dirty/changed + ahead/behind, from
934
- // one `git status --porcelain --branch`). WATCHER-INVALIDATED, not polled: a read
964
+ // one `git status --porcelain=v2 --branch`). v2 (not v1) so the submodule field
965
+ // lets us split real file changes from submodule pin-drift, which would otherwise
966
+ // inflate `changed` — in a superproject with many submodules the constant gitlink
967
+ // drift buries the real edits. WATCHER-INVALIDATED, not polled: a read
935
968
  // returns the cached snapshot instantly and NEVER spawns `git status` on the
936
969
  // request path. A per-repo-root fs watcher recomputes (debounced) only when the
937
970
  // repo actually changes, so an idle fleet costs ~0 git processes. The old design
@@ -942,7 +975,9 @@ export async function cmdServe(rest: string[]): Promise<number> {
942
975
  interface GitInfo {
943
976
  branch: string | null;
944
977
  dirty: boolean;
945
- changed: number;
978
+ changed: number; // real file changes (excludes submodule pin-bumps & internal dirt)
979
+ pins: number; // submodule gitlinks pointing at new commit(s) — pin-bump/drift
980
+ subDirty: number; // submodule has internal changes but its recorded pin is unchanged
946
981
  ahead: number;
947
982
  behind: number;
948
983
  }
@@ -964,16 +999,55 @@ export async function cmdServe(rest: string[]): Promise<number> {
964
999
  }
965
1000
  };
966
1001
  const parseGitStatus = (out: string): GitInfo => {
967
- const lines = out.split("\n");
968
- // Branch header, e.g. "## main...origin/main [ahead 1, behind 2]", "## main"
969
- // (no upstream), "## HEAD (no branch)", or "## No commits yet on x".
970
- const h = /^## (.+)$/.exec(lines[0] ?? "")?.[1] ?? "";
971
- const unborn = /^No commits yet on (.+)$/.exec(h);
972
- const branch = unborn ? unborn[1]! : /^(.+?)(?:\.\.\.|\s|$)/.exec(h)?.[1] || null;
973
- const ahead = Number(/\bahead (\d+)/.exec(h)?.[1] ?? 0);
974
- const behind = Number(/\bbehind (\d+)/.exec(h)?.[1] ?? 0);
975
- const changed = lines.slice(1).filter((l) => l.trim().length > 0).length;
976
- return { branch, dirty: changed > 0, changed, ahead, behind };
1002
+ // porcelain=v2 --branch: "# branch.*" headers then one line per changed entry.
1003
+ let branch: string | null = null;
1004
+ let ahead = 0;
1005
+ let behind = 0;
1006
+ let changed = 0; // real file changes
1007
+ let pins = 0; // submodule pin-bumps (gitlink new commit)
1008
+ let subDirty = 0; // submodule internal dirt, recorded pin unchanged
1009
+ for (const line of out.split("\n")) {
1010
+ if (line.length === 0) continue;
1011
+ if (line[0] === "#") {
1012
+ // "# branch.head <name|(detached)>" and "# branch.ab +A -B" (ab absent
1013
+ // when there's no upstream). branch.head carries the name even on an
1014
+ // unborn branch, so no special-casing needed.
1015
+ const head = /^# branch\.head (.+)$/.exec(line);
1016
+ if (head) {
1017
+ branch = head[1] === "(detached)" ? null : head[1]!;
1018
+ continue;
1019
+ }
1020
+ const ab = /^# branch\.ab \+(\d+) -(\d+)/.exec(line);
1021
+ if (ab) {
1022
+ ahead = Number(ab[1]);
1023
+ behind = Number(ab[2]);
1024
+ }
1025
+ continue;
1026
+ }
1027
+ // Changed-entry lines. Untracked ("?") and unmerged ("u") are always real
1028
+ // work; ignored ("!") never appears (we don't pass --ignored). For ordinary
1029
+ // ("1") / renamed ("2") entries the 3rd space-delimited token is the
1030
+ // submodule field <sub>: "N..." for a normal path, or "S<c><m><u>" for a
1031
+ // submodule where c=C means its gitlink moved to new commit(s) (pin-bump),
1032
+ // and m/u flag internal dirty/untracked with the recorded pin unchanged.
1033
+ // Paths (which may contain spaces) come after token 2, so counting by token
1034
+ // index is robust without -z.
1035
+ const type = line[0];
1036
+ if (type === "?") {
1037
+ changed++;
1038
+ } else if (type === "u") {
1039
+ changed++;
1040
+ } else if (type === "1" || type === "2") {
1041
+ const sub = line.split(" ")[2] ?? "N...";
1042
+ if (sub[0] === "S") {
1043
+ if (sub[1] === "C") pins++;
1044
+ else subDirty++;
1045
+ } else {
1046
+ changed++;
1047
+ }
1048
+ }
1049
+ }
1050
+ return { branch, dirty: changed > 0, changed, pins, subDirty, ahead, behind };
977
1051
  };
978
1052
  // cwd -> repo root ("" = resolved, not a repo). Resolved once per cwd via a cheap
979
1053
  // `git rev-parse --show-toplevel` (no tree scan) and cached, so many agents in the
@@ -999,7 +1073,7 @@ export async function cmdServe(rest: string[]): Promise<number> {
999
1073
  if (rw.busy) return void recompute(root, rw); // re-arm if one is in flight
1000
1074
  rw.busy = true;
1001
1075
  try {
1002
- const out = await runGit(["status", "--porcelain", "--branch"], root);
1076
+ const out = await runGit(["status", "--porcelain=v2", "--branch"], root);
1003
1077
  if (out != null) rw.val = parseGitStatus(out);
1004
1078
  } finally {
1005
1079
  rw.busy = false;
@@ -1051,12 +1125,23 @@ export async function cmdServe(rest: string[]): Promise<number> {
1051
1125
  // the LIVE status here — same liveness+log-mtime basis as `ay ls` — so the
1052
1126
  // console's dot (and the browser tab glyph) flips to idle in step with `ay ls`.
1053
1127
  const status = await deriveLiveStatus(r);
1128
+ // "Waiting on you": alive, quiet, but parked on an unanswered menu. Checked
1129
+ // only for live agents, and skipped when unresponsive (the Rust wedge signal
1130
+ // wins) — mirroring deriveLiveState's precedence in `ay ls` so the console's
1131
+ // dot and the CLI agree.
1132
+ const question =
1133
+ status !== "exited" && !r.unresponsive ? await logNeedsInput(r.log_file, r.cli) : null;
1054
1134
  return {
1055
1135
  ...r,
1056
- // The Rust supervisor's unresponsive flag is an authoritative wedge signal —
1057
- // surface it as `stuck` so the console's dot matches `ay ls`. (A dead agent
1058
- // is never unresponsiveRust clears the flag on exit.)
1059
- status: status !== "exited" && r.unresponsive ? "stuck" : status,
1136
+ // Precedence: exited stays exited; the Rust supervisor's unresponsive flag is
1137
+ // an authoritative wedge signal (`stuck`); then a blocked menu (`needs_input`);
1138
+ // else the base live status so the console's dot matches `ay ls`. (A dead
1139
+ // agent is never unresponsive Rust clears the flag on exit.)
1140
+ status:
1141
+ status === "exited" ? status : r.unresponsive ? "stuck" : question ? "needs_input" : status,
1142
+ // The pending menu/question text when needs_input, for the console to show
1143
+ // WHAT the agent is waiting on. Null otherwise.
1144
+ question,
1060
1145
  title: await logTitle(r.log_file),
1061
1146
  git: status === "exited" ? null : await gitStatus(r.cwd),
1062
1147
  // Task progress from the rendered todo block (null when none detected → no
@@ -1642,9 +1727,11 @@ export async function cmdServe(rest: string[]): Promise<number> {
1642
1727
  return new Response("fork requires a non-empty fromCwd and branch", { status: 400 });
1643
1728
  const from = typeof body.from === "string" ? body.from.trim() : "";
1644
1729
  if (fork) {
1645
- // Fork the anchor agent's branch (carrying its WIP) into a new sibling
1646
- // worktree via codehost/provision (git worktree off HEAD, no clone), then
1647
- // spawn the agent there.
1730
+ // Fork the anchor agent's branch into a new sibling worktree via
1731
+ // codehost/provision (git worktree off HEAD, no clone), then spawn the
1732
+ // agent there. The fork is clean — committed work only; the source's
1733
+ // uncommitted changes stay put (codehost forkWorktree defaults wip:false,
1734
+ // and we don't opt in).
1648
1735
  let prov: {
1649
1736
  forkWorktree: (o: { fromCwd: string; branch: string; wsRoot?: string }) => Promise<{
1650
1737
  ok: boolean;
@@ -1800,7 +1887,12 @@ export async function cmdServe(rest: string[]): Promise<number> {
1800
1887
  const child = Bun.spawn([shell, "-c", script, "ay-spawn", ...agentArgv], {
1801
1888
  cwd,
1802
1889
  detached: true,
1803
- env: { ...agentEnv, AGENT_YES_CWD: cwd, AGENT_YES_CLI: cli, AGENT_YES_AGENT_ID: agentId },
1890
+ env: {
1891
+ ...agentEnv,
1892
+ AGENT_YES_CWD: cwd,
1893
+ AGENT_YES_CLI: cli,
1894
+ AGENT_YES_AGENT_ID: agentId,
1895
+ },
1804
1896
  stdin: "ignore",
1805
1897
  stdout: "ignore",
1806
1898
  stderr: Bun.file(errPath),
@@ -1,8 +0,0 @@
1
- import "./ts--UKFJGU-.js";
2
- import "./logger-CDIsZ-Pp.js";
3
- import "./versionChecker-DJObC8O_.js";
4
- import "./pidStore-BfoBWUjc.js";
5
- import "./globalPidIndex-DlmmJlO8.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BNLYYzBZ.js";
7
-
8
- export { SUPPORTED_CLIS };