import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { isToolCallEventType } from "@earendil-works/pi-coding-agent"; import { matchesKey, Key } from "@earendil-works/pi-tui"; import { resolve } from "node:path"; import { readFile, access } from "node:fs/promises"; import { addFile, clearFiles, getFiles, getFileCount, rebuildFromSession } from "./state.js"; import { FileViewer, BorderFrame } from "./viewer.js"; export default function (pi: ExtensionAPI) { pi.on("session_start", async (_event, ctx) => { rebuildFromSession(ctx.sessionManager); }); pi.on("tool_call", async (event, ctx) => { if (isToolCallEventType("read", event)) { const path = resolve(ctx.cwd, event.input.path); addFile(path, Date.now()); pi.appendEntry("reader-file", { path, readAt: Date.now() }); } }); pi.registerCommand("reader", { description: "Browse files pi has read", handler: async (args, ctx) => { if (args) { const path = resolve(ctx.cwd, args.trim()); addFile(path, Date.now()); pi.appendEntry("reader-file", { path, readAt: Date.now() }); } if (getFileCount() === 0) { ctx.ui.notify("No files tracked yet", "warning"); return; } const allFiles = Array.from(getFiles().values()); const contentCache = new Map(); const files: typeof allFiles = []; await Promise.all( allFiles.map(async (file) => { try { await access(file.path); contentCache.set(file.path, await readFile(file.path, "utf-8")); files.push(file); } catch {} }) ); const selected = await ctx.ui.custom( (tui, theme, _kb, done) => { const viewer = new FileViewer(files, ctx.cwd, contentCache, tui, theme); const framed = new BorderFrame(viewer, (text) => theme.fg("accent", text)); return { render: (width: number) => framed.render(width), invalidate: () => framed.invalidate(), handleInput: (data: string) => { viewer.handleInput(data); tui.requestRender(); if (matchesKey(data, Key.enter)) { done(viewer.getSelectedFile()); return; } if (matchesKey(data, Key.escape)) { done(null); } }, }; }, { overlay: true, overlayOptions: { anchor: "center", width: "90%", maxHeight: "90%", margin: 1, }, }, ); if (selected) { pi.sendMessage({ customType: "reader", content: `File: ${selected}\n\n${contentCache.get(selected) ?? ""}`, display: true, }); } }, }); }