import type { ExtensionAPI, ToolInfo } from "@earendil-works/pi-coding-agent"; import { fileURLToPath } from "node:url"; import { homedir } from "node:os"; import * as fs from "node:fs"; import * as path from "node:path"; // Resolve the pi-coding-agent package directory // Search in: project node_modules, then global mise node_modules function findPkgDir(): string { // Try project node_modules first const projectPkg = path.join(process.cwd(), "node_modules", "@earendil-works", "pi-coding-agent"); if (fs.existsSync(projectPkg)) return projectPkg; // Try global mise node_modules const home = homedir(); const globalPkg = path.join(home, ".local", "share", "mise", "installs", "node", "*", "lib", "node_modules", "@earendil-works", "pi-coding-agent"); const matches = fs.readdirSync(path.join(home, ".local", "share", "mise", "installs", "node")).filter((d) => fs.existsSync(path.join(home, ".local", "share", "mise", "installs", "node", d)), ); for (const version of matches) { const candidate = path.join( home, ".local", "share", "mise", "installs", "node", version, "lib", "node_modules", "@earendil-works", "pi-coding-agent", ); if (fs.existsSync(candidate)) return candidate; } // Fallback: try the directory of the loaded module const extPath = fileURLToPath(import.meta.url); let dir = extPath; for (let i = 0; i < 20; i++) { const candidate = path.join(dir, "node_modules", "@earendil-works", "pi-coding-agent"); if (fs.existsSync(candidate)) return candidate; const parent = path.dirname(dir); if (parent === dir) break; dir = parent; } throw new Error("Could not find @earendil-works/pi-coding-agent package"); } const pkgDir = findPkgDir(); const exportHtmlModule = await import(path.join(pkgDir, "dist", "core", "export-html", "index.js")); const { exportSessionToHtml } = exportHtmlModule; export default function (pi: ExtensionAPI) { pi.registerCommand("clear", { description: "Export session to transcripts/ and start a new session", handler: async (_args, ctx) => { if (ctx.mode !== "tui") { ctx.ui.notify("clear requires interactive mode", "error"); return; } // Prompt for transcript name const name = await ctx.ui.input("Clear & Export", "Transcript name (spaces become dashes)", { timeout: 60000, }); if (!name || !name.trim()) { ctx.ui.notify("Clear & Export cancelled", "info"); return; } // Sanitize: spaces → dashes, ensure .html extension const sanitizedName = name.trim().replace(/\s+/g, "-"); const fileName = sanitizedName.endsWith(".html") ? sanitizedName : `${sanitizedName}.html`; // Build output path in transcripts/ const transcriptsDir = path.join(ctx.cwd, "transcripts"); const outputPath = path.join(transcriptsDir, fileName); // Ensure transcripts directory exists if (!fs.existsSync(transcriptsDir)) { fs.mkdirSync(transcriptsDir, { recursive: true }); } // Get session file path const sessionFile = ctx.sessionManager.getSessionFile(); if (!sessionFile) { ctx.ui.notify("No session to export", "error"); return; } // Build state from in-memory data (not available in a separate pi process) const state = { systemPrompt: ctx.getSystemPrompt(), tools: pi.getAllTools(), }; // Export session to HTML with full in-memory state await exportSessionToHtml(ctx.sessionManager, state, { outputPath }); ctx.ui.notify(`Exported to: transcripts/${fileName}`, "info"); // Start a new session const currentSessionFile = ctx.sessionManager.getSessionFile(); const newResult = await ctx.newSession({ parentSession: currentSessionFile, withSession: (replacementCtx) => { replacementCtx.ui.notify("Session cleared and exported", "info"); }, }); if (newResult.cancelled) { ctx.ui.notify("New session cancelled", "info"); } }, }); }