import type { ExtensionAPI, ExtensionContext, ReplacementSessionEntryLike } from "./pi-api.js"; import { getProjectRoot, getSessionId, getWorkingDirectory } from "./pi-api.js"; import { createSessionStore, selectSessionStoreBackend, type SessionStoreBackend } from "./runtime-capabilities.js"; export type TodoStatus = "pending" | "in_progress" | "completed" | "abandoned"; export interface TodoTask { content: string; status: TodoStatus; notes?: string[]; } export interface TodoPhase { name: string; tasks: TodoTask[]; } export interface TodoStateSnapshot { phases: TodoPhase[]; backend: "jsonl" | "pi-entry" | "memory"; diagnostics: string[]; } export interface TodoStateCommit { backend: SessionStoreBackend; diagnostics: string[]; } export async function loadTodoState(_pi: ExtensionAPI, ctx: ExtensionContext, fallbackPhases: TodoPhase[]): Promise { const backend = selectSessionStoreBackend(); if (backend === "jsonl") { const store = createSessionStore({ projectRoot: getProjectRoot(ctx), backend: "jsonl" }); const sessionId = ensureRuntimeSession(store, ctx); const latest = store.latestEntry(sessionId, "todo_write"); if (latest !== undefined) { return { phases: cloneTodoPhases(latest.payload.phases as TodoPhase[]), backend: "jsonl", diagnostics: "diagnostics" in store ? store.diagnostics : [], }; } } const latest = await getLatestPiTodoEntry(ctx); const data = latest?.data as { phases?: unknown } | undefined; if (data && Array.isArray(data.phases)) { return { phases: cloneTodoPhases(data.phases as TodoPhase[]), backend: "pi-entry", diagnostics: [] }; } return { phases: cloneTodoPhases(fallbackPhases), backend: "memory", diagnostics: [] }; } async function getLatestPiTodoEntry(ctx: ExtensionContext): Promise<{ data?: unknown } | undefined> { const entries = await ctx.sessionManager?.getEntries(); for (const entry of [...(entries ?? [])].reverse()) { const data = getTodoWriteEntryData(entry); if (data !== undefined) return { data }; } return undefined; } function getTodoWriteEntryData(entry: ReplacementSessionEntryLike): unknown { if (entry.type === "custom" && entry.customType === "todo_write") return entry.data; if (entry.type === "todo_write") return entry.data ?? entry.payload; return undefined; } export async function commitTodoState(pi: ExtensionAPI, ctx: ExtensionContext, phases: TodoPhase[]): Promise { const backend = selectSessionStoreBackend(); const cloned = cloneTodoPhases(phases); const diagnostics: string[] = []; if (backend === "jsonl") { const store = createSessionStore({ projectRoot: getProjectRoot(ctx), backend: "jsonl" }); const sessionId = ensureRuntimeSession(store, ctx); store.appendEntry(sessionId, { type: "todo_write", payload: { phases: cloned } }); if ("diagnostics" in store) diagnostics.push(...store.diagnostics); } await pi.appendEntry("todo_write", { phases: cloned }); return { backend, diagnostics }; } export function cloneTodoPhases(phases: TodoPhase[]): TodoPhase[] { return phases.map((phase) => ({ name: phase.name, tasks: phase.tasks.map((task) => ({ content: task.content, status: task.status, ...(task.notes && task.notes.length > 0 ? { notes: [...task.notes] } : {}), })), })); } function ensureRuntimeSession(store: ReturnType, ctx: ExtensionContext): string { const sessionId = getSessionId(ctx); if (store.getSession(sessionId) === undefined) { store.createSession({ id: sessionId, projectRoot: getProjectRoot(ctx), workingDirectory: getWorkingDirectory(ctx), }); } return sessionId; }