@xynogen/pix-todo 0.1.8 → 0.1.10

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.
package/README.md CHANGED
@@ -6,6 +6,22 @@ Pi tool — durable execution checklist (`todo`).
6
6
 
7
7
  Registers the `todo` tool, which gives the agent a persistent task checklist that survives context compaction and session restore. The checklist is seeded by the model via the `set` action and tracks items through four statuses: `pending` (○), `in_progress` (◐), `done` (●), and `blocked` (⊘). State is persisted via Pi's `appendEntry("todo-state")` so the agent can recover its position after long runs or compaction events. The agent calls `todo(action:"list")` to resume where it left off. Actions: `list`, `set`, `add`, `update`, `clear`.
8
8
 
9
+ ## Auto-collapse
10
+
11
+ The checklist card auto-collapses after a configurable delay (default 10 seconds, previously hardcoded). The delay and the per-tool toggle are read from `~/.pi/agent/pix.json`:
12
+
13
+ ```jsonc
14
+ {
15
+ "collapse": {
16
+ "enabled": true,
17
+ "delayMs": 10000,
18
+ "tools": { "todo": true }
19
+ }
20
+ }
21
+ ```
22
+
23
+ Set `collapse.tools.todo: false` to keep the checklist always expanded. See `@xynogen/pix-data/collapse` for the full API.
24
+
9
25
  ## Install
10
26
 
11
27
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-todo",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Pi tool — durable execution checklist (todo)",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/todo.test.ts CHANGED
@@ -504,6 +504,7 @@ describe("todo actions", () => {
504
504
  // ─── Persistence ────────────────────────────────────────────────────────────
505
505
 
506
506
  describe("persistence", () => {
507
+ type AppendCall = { type: string; data: unknown };
507
508
  test("set persists todos", async () => {
508
509
  const host = makeHost();
509
510
  registerTodo(host.pi);
@@ -515,8 +516,9 @@ describe("persistence", () => {
515
516
  host.appendCalls.length = 0;
516
517
  await run(host.execute, { action: "set", items: "alpha\nbravo" });
517
518
  expect(host.appendCalls.length).toBe(1);
518
- expect(host.appendCalls[0].type).toBe("todo-state");
519
- const data = host.appendCalls[0].data as {
519
+ const ac0 = host.appendCalls[0] as AppendCall;
520
+ expect(ac0.type).toBe("todo-state");
521
+ const data = ac0.data as {
520
522
  todos: Array<{ id: number; text: string; status: string }>;
521
523
  nextTodoId: number;
522
524
  };
@@ -564,7 +566,7 @@ describe("persistence", () => {
564
566
  host.appendCalls.length = 0;
565
567
  await run(host.execute, { action: "clear" });
566
568
  expect(host.appendCalls.length).toBe(1);
567
- const data = host.appendCalls[0].data as {
569
+ const data = (host.appendCalls[0] as AppendCall).data as {
568
570
  todos: Array<unknown>;
569
571
  nextTodoId: number;
570
572
  };
package/src/todo.ts CHANGED
@@ -9,14 +9,66 @@
9
9
  * checklist is seeded by the model via the tool's `set` action.
10
10
  */
11
11
 
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ import { join } from "node:path";
12
14
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
15
  import { Text } from "@earendil-works/pi-tui";
14
16
  import { Type } from "typebox";
15
17
 
16
18
  import { once } from "./once.ts";
17
19
 
18
- /** Seconds before a todo card collapses to a one-line dim summary. */
19
- const COLLAPSE_AFTER_SEC = 10;
20
+ // ── Collapse config from ~/.pi/agent/pix.json ────────────────────────────────
21
+
22
+ interface CollapseConf {
23
+ enabled: boolean;
24
+ delaySec: number;
25
+ tools: Record<string, boolean | undefined>;
26
+ }
27
+
28
+ const DEFAULT_COLLAPSE: CollapseConf = {
29
+ enabled: true,
30
+ delaySec: 10,
31
+ tools: {},
32
+ };
33
+
34
+ function readCollapseConfig(): CollapseConf {
35
+ try {
36
+ const home = process.env.HOME ?? "";
37
+ if (!home) return DEFAULT_COLLAPSE;
38
+ const p = join(home, ".pi/agent", "pix.json");
39
+ if (!existsSync(p)) return DEFAULT_COLLAPSE;
40
+ const raw = JSON.parse(readFileSync(p, "utf-8")) as Record<string, unknown>;
41
+ const c = raw?.collapse as Record<string, unknown> | undefined;
42
+ if (!c || typeof c !== "object") return DEFAULT_COLLAPSE;
43
+ return {
44
+ enabled: typeof c.enabled === "boolean" ? c.enabled : true,
45
+ delaySec:
46
+ typeof c.delaySec === "number" && c.delaySec > 0 ? c.delaySec : 10,
47
+ tools:
48
+ c.tools && typeof c.tools === "object"
49
+ ? (c.tools as Record<string, boolean | undefined>)
50
+ : {},
51
+ };
52
+ } catch {
53
+ return DEFAULT_COLLAPSE;
54
+ }
55
+ }
56
+
57
+ let collapseConf: CollapseConf | null = null;
58
+ function getCollapseConfig(): CollapseConf {
59
+ if (!collapseConf) collapseConf = readCollapseConfig();
60
+ return collapseConf;
61
+ }
62
+
63
+ function shouldCollapseTodo(): boolean {
64
+ const c = getCollapseConfig();
65
+ const perTool = c.tools.todo;
66
+ return typeof perTool === "boolean" ? perTool : c.enabled;
67
+ }
68
+
69
+ function collapseDelayMs(): number {
70
+ return getCollapseConfig().delaySec * 1000;
71
+ }
20
72
 
21
73
  export type TodoStatus = "pending" | "in_progress" | "done" | "blocked";
22
74
 
@@ -161,11 +213,12 @@ export default function registerTodo(pi: ExtensionAPI): void {
161
213
  };
162
214
  if (!state.snapshot) state.snapshot = todos.map((t) => ({ ...t }));
163
215
  // Start the collapse timer once per row; invalidate() triggers rerender.
164
- if (!state.collapsed && !state.timer) {
216
+ // Config-driven: reads from ~/.pi/agent/pix.json collapse section.
217
+ if (shouldCollapseTodo() && !state.collapsed && !state.timer) {
165
218
  state.timer = setTimeout(() => {
166
219
  state.collapsed = true;
167
220
  context.invalidate();
168
- }, COLLAPSE_AFTER_SEC * 1000);
221
+ }, collapseDelayMs());
169
222
  }
170
223
  const render = state.collapsed
171
224
  ? renderTodoSummaryLine