import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent"; import { Text } from "@earendil-works/pi-tui"; import { applyAction } from "./core"; import { groupByStatus } from "./query"; import { REPORT_ENTRY } from "./constants"; import type { SessionRecordStore } from "@leo-alvarenga/pi-ext-core"; import type { Todo, TodoState } from "./types"; import { getStyledTodo } from "./utils"; const renderList = (todos: Todo[], theme: Theme): string => { if (todos.length === 0) return ` ${theme.fg("dim", "No todos.")}`; const { completed, inProgress, pending } = groupByStatus(todos); const lines: string[] = []; const section = (label: string, items: Todo[]): void => { if (items.length === 0) return; lines.push(` ${theme.fg("muted", `${label} (${items.length})`)}`); for (const t of items) lines.push(getStyledTodo(t, todos, theme)); lines.push(""); }; section("Completed", completed); section("In progress", inProgress); section("Pending", pending); return lines.join("\n"); }; export function registerTodosCommand( pi: ExtensionAPI, store: SessionRecordStore, ): void { pi.registerCommand("todos", { description: "Show all todos grouped by status", handler: async (_args, ctx) => { if (!ctx.hasUI) { ctx.ui.notify("/todos requires interactive mode", "error"); return; } pi.appendEntry(REPORT_ENTRY, { todos: [...store.getState(ctx).todos] }); }, }); pi.registerCommand("todos-clear", { description: "Manually clears all todos", handler: async (_args, ctx) => { const state = store.getState(ctx); if (!state.todos.length) return; const result = applyAction(state, { action: "clear", }); if (!result.ok) { ctx.ui.notify("Could not clear todos", "error"); return; } store.commit(ctx, result.state); }, }); pi.registerEntryRenderer<{ todos: Todo[] }>( REPORT_ENTRY, (entry, _options, theme) => { return new Text(renderList(entry.data?.todos ?? [], theme), 0, 0); }, ); }