@xynogen/pix-core 0.1.5 → 0.1.6
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/package.json +1 -1
- package/src/tool/todo/todo.test.ts +45 -1
- package/src/tool/todo/todo.ts +37 -0
package/package.json
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import registerTodo from "./todo.ts";
|
|
2
|
+
import registerTodo, { renderTodoLines, type TodoItem } from "./todo.ts";
|
|
3
|
+
|
|
4
|
+
// Stub theme tags each fragment with its color/bold so assertions can verify
|
|
5
|
+
// which status got which tint, without depending on real ANSI codes.
|
|
6
|
+
const tagTheme = {
|
|
7
|
+
fg: (color: string, text: string) => `[${color}]${text}[/]`,
|
|
8
|
+
bold: (text: string) => `<b>${text}</b>`,
|
|
9
|
+
};
|
|
3
10
|
|
|
4
11
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
5
12
|
|
|
@@ -600,3 +607,40 @@ describe("restore", () => {
|
|
|
600
607
|
expect(text(result)).toBe("(no todos)");
|
|
601
608
|
});
|
|
602
609
|
});
|
|
610
|
+
|
|
611
|
+
describe("renderTodoLines (colored TUI render)", () => {
|
|
612
|
+
const items: TodoItem[] = [
|
|
613
|
+
{ id: 1, text: "alpha", status: "done" },
|
|
614
|
+
{ id: 2, text: "bravo", status: "in_progress" },
|
|
615
|
+
{ id: 3, text: "charlie", status: "pending" },
|
|
616
|
+
{ id: 4, text: "delta", status: "blocked" },
|
|
617
|
+
];
|
|
618
|
+
|
|
619
|
+
test("empty list renders muted placeholder", () => {
|
|
620
|
+
expect(renderTodoLines([], tagTheme)).toBe("[muted](no todos)[/]");
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
test("tints each glyph by status", () => {
|
|
624
|
+
const out = renderTodoLines(items, tagTheme);
|
|
625
|
+
expect(out).toContain("[success]●[/]"); // done
|
|
626
|
+
expect(out).toContain("[accent]◐[/]"); // in_progress
|
|
627
|
+
expect(out).toContain("[muted]○[/]"); // pending
|
|
628
|
+
expect(out).toContain("[error]⊘[/]"); // blocked
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test("highlights the in-progress row bold + accent", () => {
|
|
632
|
+
const out = renderTodoLines(items, tagTheme);
|
|
633
|
+
expect(out).toContain("<b>[accent]2. bravo[/]</b>");
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
test("dims completed rows and uses text color for active-but-not-running", () => {
|
|
637
|
+
const out = renderTodoLines(items, tagTheme);
|
|
638
|
+
expect(out).toContain("[muted]1. alpha[/]"); // done body muted
|
|
639
|
+
expect(out).toContain("[text]3. charlie[/]"); // pending body text
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
test("shows the done/total count header", () => {
|
|
643
|
+
const out = renderTodoLines(items, tagTheme);
|
|
644
|
+
expect(out).toContain("[muted]Todos 1/4 done:[/]");
|
|
645
|
+
});
|
|
646
|
+
});
|
package/src/tool/todo/todo.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
13
14
|
import { Type } from "typebox";
|
|
14
15
|
|
|
15
16
|
export type TodoStatus = "pending" | "in_progress" | "done" | "blocked";
|
|
@@ -27,6 +28,38 @@ const TODO_GLYPH: Record<TodoStatus, string> = {
|
|
|
27
28
|
blocked: "⊘",
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
/** Theme color key per status — drives both glyph and (for active) row tint. */
|
|
32
|
+
const TODO_COLOR: Record<TodoStatus, string> = {
|
|
33
|
+
pending: "muted",
|
|
34
|
+
in_progress: "accent",
|
|
35
|
+
done: "success",
|
|
36
|
+
blocked: "error",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type TodoTheme = {
|
|
40
|
+
fg: (color: string, text: string) => string;
|
|
41
|
+
bold: (text: string) => string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Colored checklist for the TUI: glyphs tinted by status, active row bold. */
|
|
45
|
+
export function renderTodoLines(items: TodoItem[], theme: TodoTheme): string {
|
|
46
|
+
if (!items.length) return theme.fg("muted", "(no todos)");
|
|
47
|
+
const done = items.filter((t) => t.status === "done").length;
|
|
48
|
+
const head = theme.fg("muted", `Todos ${done}/${items.length} done:`);
|
|
49
|
+
const lines = items.map((t) => {
|
|
50
|
+
const color = TODO_COLOR[t.status];
|
|
51
|
+
const glyph = theme.fg(color, TODO_GLYPH[t.status]);
|
|
52
|
+
const body = `${t.id}. ${t.text}`;
|
|
53
|
+
// Highlight the in-flight task so the eye lands on it first.
|
|
54
|
+
const label =
|
|
55
|
+
t.status === "in_progress"
|
|
56
|
+
? theme.bold(theme.fg("accent", body))
|
|
57
|
+
: theme.fg(t.status === "done" ? "muted" : "text", body);
|
|
58
|
+
return `${glyph} ${label}`;
|
|
59
|
+
});
|
|
60
|
+
return `${head}\n${lines.join("\n")}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
30
63
|
const parseItems = (raw: string): string[] =>
|
|
31
64
|
raw
|
|
32
65
|
.split("\n")
|
|
@@ -102,6 +135,10 @@ export default function registerTodo(pi: ExtensionAPI): void {
|
|
|
102
135
|
}),
|
|
103
136
|
),
|
|
104
137
|
}),
|
|
138
|
+
renderResult(_result, _options, theme) {
|
|
139
|
+
return new Text(renderTodoLines(todos, theme as TodoTheme), 0, 0);
|
|
140
|
+
},
|
|
141
|
+
|
|
105
142
|
async execute(_id, params) {
|
|
106
143
|
// AgentToolResult now requires a `details` field. These todo results have
|
|
107
144
|
// no structured details, so emit `undefined` via small local helpers.
|