edit session

This commit is contained in:
2026-03-10 18:19:31 +00:00
parent 0001a15dde
commit d389bf9276
3 changed files with 108 additions and 1 deletions
@@ -7,8 +7,22 @@
import type { ExtensionAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@mariozechner/pi-coding-agent";
// Flag to track if we're in the middle of an edit-session reload
let skipNextSwitchConfirm = false;
export default function (pi: ExtensionAPI) {
// Listen for edit-session reload signal
pi.events.on("edit-session:reload", () => {
skipNextSwitchConfirm = true;
});
pi.on("session_before_switch", async (event: SessionBeforeSwitchEvent, ctx) => {
// Skip confirmation if this is an edit-session reload
if (skipNextSwitchConfirm) {
skipNextSwitchConfirm = false; // Reset the flag
return;
}
if (event.reason === "new") {
// Emit event for sound extensions (before hasUI check)
pi.events.emit("peon:input_required", { source: "confirm-destructive", action: "clear-session" });
+93
View File
@@ -0,0 +1,93 @@
/**
* Edit Session Extension
*
* Adds /edit-session command to open the current session JSONL in your default editor.
*
* Usage:
* /edit-session - Open current session file in $EDITOR
*
* The editor is determined by $VISUAL, then $EDITOR, then falls back to 'vi'.
*/
import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
import type { TUI, Theme, KeybindingsManager, Component } from "@mariozechner/pi-tui";
import { spawnSync } from "node:child_process";
export default function editSessionExtension(pi: ExtensionAPI) {
pi.registerCommand("edit-session", {
description: "Open session JSONL in default editor ($VISUAL/$EDITOR/vi)",
handler: async (_args: string, ctx: ExtensionCommandContext) => {
const sessionFile = ctx.sessionManager.getSessionFile();
if (!sessionFile) {
ctx.ui.notify("No session file (ephemeral mode)", "error");
return;
}
const editorCmd = process.env.VISUAL || process.env.EDITOR;
if (!editorCmd) {
ctx.ui.notify("No editor configured. Set $VISUAL or $EDITOR.", "error");
return;
}
// Factory function to create component and run editor
const factory = (
tui: TUI,
_theme: Theme,
_kb: KeybindingsManager,
done: (result: void) => void
): Component => {
// Stop TUI to release terminal (same as Ctrl+G)
tui.stop();
// Split editor command to support args (e.g., "code --wait")
const [editor, ...editorArgs] = editorCmd.split(" ");
// Spawn editor synchronously with inherited stdio
const result = spawnSync(editor, [...editorArgs, sessionFile], {
stdio: "inherit",
shell: process.platform === "win32",
});
// Restart TUI
tui.start();
// Force full re-render since external editor uses alternate screen
tui.requestRender(true);
if (result.status !== 0 && result.status !== null) {
ctx.ui.notify(`Editor exited with code ${result.status}`, "warning");
}
done();
// Return dummy component
return createDummyComponent();
};
// Use ctx.ui.custom to get access to the TUI
await ctx.ui.custom<void>(factory);
// Signal that we're about to reload the session (so confirm-destructive skips)
pi.events.emit("edit-session:reload");
// Reload the session by switching to the same file (forces re-read from disk)
ctx.ui.notify("Reloading session...", "info");
const result = await ctx.switchSession(sessionFile);
if (result.cancelled) {
// If still cancelled (by another extension), just notify without warning
ctx.ui.notify("Session reload skipped. Run /reload to apply changes.", "info");
} else {
ctx.ui.notify("Session reloaded with changes.", "info");
}
},
});
}
function createDummyComponent(): Component {
return {
render: (_width: number): string[] => [],
invalidate: (): void => {},
};
}