feat: clear-export extension — tool info and global pkg handling
This commit is contained in:
parent
fc2cf73b34
commit
461bc7f776
@ -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');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user