From 461bc7f776b97a3e265438f09e5ad65b1b74edee Mon Sep 17 00:00:00 2001 From: Willem van den Ende Date: Sat, 13 Jun 2026 15:34:48 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20clear-export=20extension=20=E2=80=94=20?= =?UTF-8?q?tool=20info=20and=20global=20pkg=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pi/extensions/clear-export.ts | 82 ++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/.pi/extensions/clear-export.ts b/.pi/extensions/clear-export.ts index cb9375e..a5fd0c9 100644 --- a/.pi/extensions/clear-export.ts +++ b/.pi/extensions/clear-export.ts @@ -1,35 +1,49 @@ -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"; +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"); + 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)), + 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", + '.local', + 'share', + 'mise', + 'installs', + 'node', version, - "lib", - "node_modules", - "@earendil-works", - "pi-coding-agent", + 'lib', + 'node_modules', + '@earendil-works', + 'pi-coding-agent', ); if (fs.existsSync(candidate)) return candidate; } @@ -38,44 +52,44 @@ function findPkgDir(): string { 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"); + 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"); + 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 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", + 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"); + 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)", { + 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"); + 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`; + 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 transcriptsDir = path.join(ctx.cwd, 'transcripts'); const outputPath = path.join(transcriptsDir, fileName); // Ensure transcripts directory exists @@ -86,7 +100,7 @@ export default function (pi: ExtensionAPI) { // Get session file path const sessionFile = ctx.sessionManager.getSessionFile(); if (!sessionFile) { - ctx.ui.notify("No session to export", "error"); + ctx.ui.notify('No session to export', 'error'); return; } @@ -99,19 +113,19 @@ export default function (pi: ExtensionAPI) { // Export session to HTML with full in-memory state await exportSessionToHtml(ctx.sessionManager, state, { outputPath }); - ctx.ui.notify(`Exported to: transcripts/${fileName}`, "info"); + 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"); + replacementCtx.ui.notify('Session cleared and exported', 'info'); }, }); if (newResult.cancelled) { - ctx.ui.notify("New session cancelled", "info"); + ctx.ui.notify('New session cancelled', 'info'); } }, });