{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAoDhF","sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\nimport { extname, resolve } from \"node:path\";\nimport type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";\nimport { buildTodoPlanMessageContent } from \"./plan-message.ts\";\nimport type { TodoRuntime } from \"./runtime.ts\";\n\nexport function registerTodoCommand(pi: ExtensionAPI, runtime: TodoRuntime): void {\n\tpi.registerCommand(\"todo\", {\n\t\tdescription: \"List, inspect, import, delete, or clear persistent todo tasks\",\n\t\thandler: async (args, ctx) => {\n\t\t\tawait runtime.reconcileOwners();\n\t\t\tconst input = args.trim();\n\t\t\tif (!input || input === \"list\") {\n\t\t\t\tconst view = await runtime.view();\n\t\t\t\tctx.ui.notify(view ? formatTodoList(view) : \"No todo list is active\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst [action, ...rest] = input.split(/\\s+/);\n\t\t\tif (action === \"inspect\") {\n\t\t\t\tconst id = rest.join(\" \");\n\t\t\t\tconst task = await runtime.getTask(id);\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\ttask ? JSON.stringify(task, null, 2) : `Todo \"${id}\" does not exist`,\n\t\t\t\t\ttask ? \"info\" : \"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (action === \"delete\") {\n\t\t\t\tconst id = rest.join(\" \");\n\t\t\t\tif (!id) {\n\t\t\t\t\tctx.ui.notify(\"Usage: /todo delete <task-id>\", \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!(await ctx.ui.confirm(\"Delete todo\", `Delete ${id} and clean its dependency edges?`))) return;\n\t\t\t\tawait runtime.delete(id);\n\t\t\t\tctx.ui.notify(`Deleted ${id}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (action === \"clear\") {\n\t\t\t\tif (!(await ctx.ui.confirm(\"Clear todo list\", \"Delete the active task list permanently?\"))) return;\n\t\t\t\tawait runtime.clear();\n\t\t\t\tctx.ui.notify(\"Cleared the active todo list\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst planInput = action === \"import\" ? rest.join(\" \") : input;\n\t\t\tif (!planInput) {\n\t\t\t\tctx.ui.notify(\"Usage: /todo import <markdown-path-or-inline-plan>\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst { plan, source } = await resolvePlan(planInput, ctx.cwd);\n\t\t\tpi.sendMessage(\n\t\t\t\t{ customType: \"pi-todo-plan\", content: buildTodoPlanMessageContent({ source, plan }), display: true },\n\t\t\t\t{ triggerTurn: true },\n\t\t\t);\n\t\t},\n\t});\n}\n\nfunction formatTodoList(view: NonNullable<Awaited<ReturnType<TodoRuntime[\"view\"]>>>): string {\n\tconst summary = `${view.summary.completed}/${view.summary.total} completed, ${view.summary.in_progress} active, ${view.summary.ready} ready, ${view.summary.blocked} blocked`;\n\tconst visible = view.list.tasks.slice(0, 20).map((task) => {\n\t\tconst owner = task.owner ? ` @${task.owner}` : \"\";\n\t\tconst dependencies = task.depends_on.length ? ` <- ${task.depends_on.join(\",\")}` : \"\";\n\t\treturn `[${task.status}] ${task.id}: ${truncate(task.subject, 300)}${owner}${dependencies}`;\n\t});\n\tif (view.list.tasks.length > visible.length)\n\t\tvisible.push(`... ${view.list.tasks.length - visible.length} more tasks`);\n\treturn `${summary}\\n${visible.join(\"\\n\")}`;\n}\n\nfunction truncate(value: string, limit: number): string {\n\treturn value.length <= limit ? value : `${value.slice(0, Math.max(0, limit - 3))}...`;\n}\n\nasync function resolvePlan(input: string, cwd: string): Promise<{ plan: string; source: string }> {\n\tconst candidate = resolve(cwd, input);\n\ttry {\n\t\tconst file = await stat(candidate);\n\t\tif (!file.isFile()) return { plan: input, source: \"inline plan\" };\n\t\tif (extname(candidate).toLowerCase() !== \".md\") throw new Error(\"Todo plan files must use the .md extension\");\n\t\treturn { plan: await readFile(candidate, \"utf8\"), source: candidate };\n\t} catch (error) {\n\t\tif (extname(input).toLowerCase() === \".md\") throw error;\n\t\treturn { plan: input, source: \"inline plan\" };\n\t}\n}\n"]}