agent-yes 1.122.2 → 1.123.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 (56) hide show
  1. package/default.config.yaml +19 -0
  2. package/dist/{SUPPORTED_CLIS-BTu2brih.js → SUPPORTED_CLIS-B4O2cFlt.js} +2 -2
  3. package/dist/SUPPORTED_CLIS-DHkqGoNv.js +8 -0
  4. package/dist/{agent-yes.config-z-IPzH5U.js → agent-yes.config-D6ycMApr.js} +2 -65
  5. package/dist/cli.js +6 -6
  6. package/dist/configShared-C5QaNPnz.js +71 -0
  7. package/dist/{globalPidIndex-gZuTvTBs.js → globalPidIndex-C7r2m6s7.js} +19 -20
  8. package/dist/index.js +4 -4
  9. package/dist/pidStore-C4c2O15q.js +5 -0
  10. package/dist/{pidStore-B5vBu8Px.js → pidStore-CGKIhaJO.js} +5 -4
  11. package/dist/reaper-BLVA780B.js +3 -0
  12. package/dist/{reaper-Dj8R7ltI.js → reaper-BkjPN7mw.js} +24 -2
  13. package/dist/{remotes-CpGcTr7A.js → remotes-BRCDVnR7.js} +1 -1
  14. package/dist/{remotes-D2fqaRU8.js → remotes-D8GvSbhf.js} +1 -1
  15. package/dist/{schedule-DgRrdA_n.js → schedule-DULdIkU9.js} +7 -7
  16. package/dist/{serve-tn7ZetZs.js → serve-r_2v9EKc.js} +202 -58
  17. package/dist/{setup-dZhgpNse.js → setup-DHa6fX8M.js} +3 -3
  18. package/dist/{share-CksllWW-.js → share-YuM6-Q6A.js} +78 -4
  19. package/dist/{subcommands-D9BWZilr.js → subcommands-B13Kto-u.js} +647 -32
  20. package/dist/subcommands-Tv6AwUkD.js +7 -0
  21. package/dist/{tray-DjCIyakK.js → tray-BVnJLThD.js} +1 -1
  22. package/dist/{ts-CIf0uaR7.js → ts-DgukRoEI.js} +10 -7
  23. package/dist/{versionChecker-DjxKi4qe.js → versionChecker-BqOr1YqC.js} +2 -2
  24. package/dist/{workspaceConfig-XP2NEWmV.js → workspaceConfig-BJO4fzEn.js} +1 -1
  25. package/lab/ui/console-logic.js +222 -10
  26. package/lab/ui/icon.svg +5 -0
  27. package/lab/ui/index.html +689 -14
  28. package/lab/ui/landing.html +276 -0
  29. package/lab/ui/manifest.webmanifest +14 -0
  30. package/lab/ui/sw.js +56 -0
  31. package/package.json +5 -1
  32. package/ts/agentTree.spec.ts +92 -0
  33. package/ts/agentTree.ts +149 -0
  34. package/ts/configShared.ts +4 -0
  35. package/ts/globalPidIndex.ts +28 -20
  36. package/ts/idleWaiter.spec.ts +7 -1
  37. package/ts/index.ts +9 -0
  38. package/ts/lsWatch.spec.ts +61 -0
  39. package/ts/lsWatch.ts +94 -0
  40. package/ts/needsInput.spec.ts +55 -0
  41. package/ts/needsInput.ts +68 -0
  42. package/ts/pidStore.ts +3 -0
  43. package/ts/reaper.spec.ts +26 -2
  44. package/ts/reaper.ts +25 -0
  45. package/ts/resultEnvelope.spec.ts +43 -0
  46. package/ts/resultEnvelope.ts +88 -0
  47. package/ts/serve.ts +276 -41
  48. package/ts/share.ts +156 -3
  49. package/ts/subcommands.ts +0 -0
  50. package/ts/todoParse.spec.ts +68 -0
  51. package/ts/todoParse.ts +88 -0
  52. package/ts/utils.spec.ts +4 -1
  53. package/dist/SUPPORTED_CLIS-DcOKE9Nz.js +0 -8
  54. package/dist/pidStore-7y1cTcAE.js +0 -5
  55. package/dist/reaper-HqcUms2d.js +0 -3
  56. package/dist/subcommands-D8sHibKu.js +0 -6
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseTaskCounts } from "./todoParse.ts";
3
+
4
+ const lines = (s: string) => s.split("\n");
5
+
6
+ describe("parseTaskCounts", () => {
7
+ it("counts a standard ⎿-anchored todo block (done as numerator)", () => {
8
+ const out = parseTaskCounts(
9
+ lines(
10
+ [
11
+ "⏺ Update Todos",
12
+ " ⎿ ☒ Wire up the parser",
13
+ " ☒ Add the badge",
14
+ " ◼ Compute in /api/ls",
15
+ " ◻ Render in the console",
16
+ " ◻ Tests",
17
+ ].join("\n"),
18
+ ),
19
+ );
20
+ expect(out).toEqual({ done: 2, total: 5 });
21
+ });
22
+
23
+ it("treats ✔ ☑ ✓ ☒ all as done, ◼ as in-progress, ◻ ☐ as pending", () => {
24
+ const out = parseTaskCounts(
25
+ lines(["⎿ ✔ a", " ☑ b", " ✓ c", " ☒ d", " ◼ e", " ◻ f", " ☐ g"].join("\n")),
26
+ );
27
+ expect(out).toEqual({ done: 4, total: 7 });
28
+ });
29
+
30
+ it("returns null with no ⎿ anchor (avoid false positives from prose glyphs)", () => {
31
+ const out = parseTaskCounts(
32
+ lines(["I finished ✔ the thing", "and ◻ another note", "✓ done-ish"].join("\n")),
33
+ );
34
+ expect(out).toBeNull();
35
+ });
36
+
37
+ it("requires ≥2 marker lines", () => {
38
+ expect(parseTaskCounts(lines(["⎿ ☒ only one"].join("\n")))).toBeNull();
39
+ });
40
+
41
+ it("picks the MOST RECENT block when several are present", () => {
42
+ const out = parseTaskCounts(
43
+ lines(
44
+ ["⎿ ☒ old1", " ◻ old2", " ◻ old3", "...work...", "⎿ ☒ new1", " ☒ new2", " ◻ new3"].join(
45
+ "\n",
46
+ ),
47
+ ),
48
+ );
49
+ expect(out).toEqual({ done: 2, total: 3 });
50
+ });
51
+
52
+ it("accepts the anchor on the line directly above the markers", () => {
53
+ const out = parseTaskCounts(lines([" ⎿", " ☒ a", " ◻ b"].join("\n")));
54
+ expect(out).toEqual({ done: 1, total: 2 });
55
+ });
56
+
57
+ it("stops the block at a non-marker (wrapped/continuation) line", () => {
58
+ // a prose line between two markers splits the run; the qualifying block is the
59
+ // contiguous one with the anchor.
60
+ const out = parseTaskCounts(lines(["⎿ ☒ a", " ☒ b", "some interruption", " ◻ c"].join("\n")));
61
+ expect(out).toEqual({ done: 2, total: 2 });
62
+ });
63
+
64
+ it("returns null for empty / no-todo output", () => {
65
+ expect(parseTaskCounts([])).toBeNull();
66
+ expect(parseTaskCounts(lines("just some logs\nnothing here"))).toBeNull();
67
+ });
68
+ });
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Parse an agent's todo/task list out of its RENDERED TUI screen.
3
+ *
4
+ * Source of truth for EVERY CLI (claude, codex, gemini, …) is the screen the
5
+ * agent draws — never a CLI-specific session file. The durable copy is the
6
+ * per-pid raw log (`<cwd>/.agent-yes/<pid>.raw.log`); rendering it through a
7
+ * headless xterm (see renderRawLog) collapses the reflow/redraw frames into the
8
+ * final coherent text, which is what we scan here.
9
+ *
10
+ * The todo list in these TUIs renders as a tree block anchored by the `⎿`
11
+ * branch glyph, one marker per line:
12
+ *
13
+ * ⎿ ☒ Wire up the parser
14
+ * ☒ Add the badge
15
+ * ◼ Compute in /api/ls ← in progress
16
+ * ◻ Render in the console ← pending
17
+ * ◻ Tests
18
+ *
19
+ * Badge = `${done}/${total}` (done is the numerator → "2/5").
20
+ *
21
+ * This parse is deliberately conservative: we only report a count when a block
22
+ * is confidently detected (the `⎿` anchor + ≥2 consecutive marker lines), so an
23
+ * agent that merely prints a check glyph in prose never produces a phantom badge.
24
+ */
25
+
26
+ // Marker glyphs, by state. Kept as single code points so a line is classified by
27
+ // its first non-indent glyph.
28
+ const DONE = new Set(["✔", "☑", "✓", "☒"]);
29
+ const IN_PROGRESS = new Set(["◼"]);
30
+ const PENDING = new Set(["◻", "☐"]);
31
+ const ANCHOR = "⎿";
32
+
33
+ export interface TaskCounts {
34
+ done: number;
35
+ total: number;
36
+ }
37
+
38
+ type Marker = "done" | "inprogress" | "pending";
39
+
40
+ // Classify a rendered line: strip leading whitespace and an optional leading `⎿`
41
+ // (+ its whitespace), then look at the first glyph. Returns null for non-marker
42
+ // lines (prose, blank lines, wrapped titles).
43
+ function markerOf(line: string): Marker | null {
44
+ let s = line.replace(/^\s+/, "");
45
+ if (s.startsWith(ANCHOR)) s = s.slice(ANCHOR.length).replace(/^\s+/, "");
46
+ const ch = [...s][0];
47
+ if (ch === undefined) return null;
48
+ if (DONE.has(ch)) return "done";
49
+ if (IN_PROGRESS.has(ch)) return "inprogress";
50
+ if (PENDING.has(ch)) return "pending";
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Find the MOST RECENT confidently-detected todo block in the rendered lines and
56
+ * return its {done, total}. Returns null when none qualifies (caller omits the
57
+ * badge entirely — never shows "0/0").
58
+ *
59
+ * A block is a maximal run of consecutive marker lines. It only counts when it
60
+ * is anchored — the `⎿` glyph appears on the run's first line or the line
61
+ * directly above it — and has ≥2 marker lines. The last qualifying block wins,
62
+ * since the agent's current todo state is the one drawn most recently.
63
+ */
64
+ export function parseTaskCounts(lines: string[]): TaskCounts | null {
65
+ let best: TaskCounts | null = null;
66
+ const n = lines.length;
67
+ let i = 0;
68
+ while (i < n) {
69
+ if (markerOf(lines[i]!) === null) {
70
+ i++;
71
+ continue;
72
+ }
73
+ // Start of a marker run at i.
74
+ let hasAnchor = i > 0 && lines[i - 1]!.includes(ANCHOR);
75
+ const counts = { done: 0, inprogress: 0, pending: 0 };
76
+ let j = i;
77
+ for (; j < n; j++) {
78
+ const mk = markerOf(lines[j]!);
79
+ if (mk === null) break;
80
+ if (lines[j]!.includes(ANCHOR)) hasAnchor = true;
81
+ counts[mk]++;
82
+ }
83
+ const total = counts.done + counts.inprogress + counts.pending;
84
+ if (hasAnchor && total >= 2) best = { done: counts.done, total };
85
+ i = j === i ? i + 1 : j;
86
+ }
87
+ return best;
88
+ }
package/ts/utils.spec.ts CHANGED
@@ -19,7 +19,10 @@ describe("utils", () => {
19
19
  const start = Date.now();
20
20
  await sleepms(0);
21
21
  const end = Date.now();
22
- expect(end - start).toBeLessThan(50); // Should be quick
22
+ // Generous bound: catches a real regression (e.g. sleepms ignoring 0 and
23
+ // hanging) without flaking on a GC pause / loaded CI runner. The await
24
+ // itself already proves it resolves; this just guards against a hang.
25
+ expect(end - start).toBeLessThan(1000);
23
26
  });
24
27
  });
25
28
 
@@ -1,8 +0,0 @@
1
- import "./ts-CIf0uaR7.js";
2
- import "./logger-B9h0djqx.js";
3
- import "./versionChecker-DjxKi4qe.js";
4
- import "./pidStore-B5vBu8Px.js";
5
- import "./globalPidIndex-gZuTvTBs.js";
6
- import { t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-BTu2brih.js";
7
-
8
- export { SUPPORTED_CLIS };
@@ -1,5 +0,0 @@
1
- import "./logger-B9h0djqx.js";
2
- import { t as PidStore } from "./pidStore-B5vBu8Px.js";
3
- import "./globalPidIndex-gZuTvTBs.js";
4
-
5
- export { PidStore };
@@ -1,3 +0,0 @@
1
- import { n as sweep, t as register } from "./reaper-Dj8R7ltI.js";
2
-
3
- export { sweep };
@@ -1,6 +0,0 @@
1
- import "./logger-B9h0djqx.js";
2
- import "./globalPidIndex-gZuTvTBs.js";
3
- import "./remotes-D2fqaRU8.js";
4
- import { a as finalizedLines, c as listRecords, d as renderRawLog, f as resolveOne, g as writeToIpc, h as stopTipForCli, i as cursorAbs, l as matchKeyword, m as snapshotStatus, n as cmdHelp, o as isPidAlive, p as runSubcommand, r as controlCodeFromName, s as isSubcommand, t as GRACEFUL_EXIT_COMMANDS, u as readNotes } from "./subcommands-D9BWZilr.js";
5
-
6
- export { cmdHelp, isSubcommand, runSubcommand };