diff --git a/extensions/dashboard.ts b/extensions/dashboard.ts index d89f181..a72fd32 100644 --- a/extensions/dashboard.ts +++ b/extensions/dashboard.ts @@ -51,15 +51,46 @@ const AUTO_ZELLIJ_NAME = /^[a-z]+-[a-z]+$/; // pi-subagents names subagent sessions "#<8-hex>" (e.g. "Explore#fb073d02") const SUBAGENT_NAME = /^.+?#[0-9a-f]{8}$/i; -// Project-local task file: lives in the pi project's .pi/ directory -// Mirrors how pi sessions are organized by working directory -function projectTasksDir(): string { - const dir = join(process.cwd(), ".pi", "dashboard", "tasks"); - ensureDir(dir); - return dir; +// Project task file: lives at the root of the pi project's working directory +// so it is visible and discoverable. In git repos it is automatically added to +// .gitignore so task churn never pollutes commits or Hunk reviews. +function gitRoot(dir: string): string | null { + let cur = dir; + for (;;) { + if (existsSync(join(cur, ".git"))) return cur; + const parent = join(cur, ".."); + if (parent === cur) return null; + cur = parent; + } } + +function ensureTodoGitignored() { + const root = gitRoot(cwd()); + if (!root) return; // not a git repo — nothing to ignore + const gi = join(root, ".gitignore"); + let content = ""; + if (existsSync(gi)) { + try { + content = readFileSync(gi, "utf-8"); + } catch { + return; + } + } + const lines = content.split("\n"); + if (lines.some((l) => l.trim() === "todo.txt" || l.trim() === "/todo.txt")) { + return; + } + const line = content === "" || content.endsWith("\n") ? "todo.txt\n" : "\ntodo.txt\n"; + try { + writeFileSync(gi, content + line, "utf-8"); + } catch { + // read-only repo — ignore; tasks still work, they just may show in git + } +} + function projectTodotxt(): string { - return join(projectTasksDir(), "todo.txt"); + ensureTodoGitignored(); + return join(cwd(), "todo.txt"); } // ── Types ───────────────────────────────────────────────────────── @@ -168,7 +199,6 @@ function appendToTodo( priority?: string, agent?: string ) { - ensureDir(projectTasksDir()); const pri = priority ? `(${priority.toUpperCase()}) ` : ""; const proj = project ? ` +project:${project}` : ""; const agentTag = agent ? ` +agent:${agent}` : ""; @@ -208,7 +238,6 @@ function readTodoList() { export default function (pi: ExtensionAPI) { ensureDir(DASHBOARD_DIR); - ensureDir(projectTasksDir()); const machine = hostname(); const inTmux = !!process.env.TMUX;