From d389bf92761bbf3429384c9609eb17e1b84d2dad Mon Sep 17 00:00:00 2001 From: "thomas g. lopes" Date: Tue, 10 Mar 2026 18:19:31 +0000 Subject: [PATCH] edit session --- .../agent/extensions/confirm-destructive.ts | 14 +++ pi/files/agent/extensions/edit-session.ts | 93 +++++++++++++++++++ pi/files/agent/settings.json | 2 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 pi/files/agent/extensions/edit-session.ts diff --git a/pi/files/agent/extensions/confirm-destructive.ts b/pi/files/agent/extensions/confirm-destructive.ts index b61a3f8..4174c7f 100644 --- a/pi/files/agent/extensions/confirm-destructive.ts +++ b/pi/files/agent/extensions/confirm-destructive.ts @@ -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" }); diff --git a/pi/files/agent/extensions/edit-session.ts b/pi/files/agent/extensions/edit-session.ts new file mode 100644 index 0000000..5887b0f --- /dev/null +++ b/pi/files/agent/extensions/edit-session.ts @@ -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(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 => {}, + }; +} \ No newline at end of file diff --git a/pi/files/agent/settings.json b/pi/files/agent/settings.json index 248bbe2..256e22d 100644 --- a/pi/files/agent/settings.json +++ b/pi/files/agent/settings.json @@ -2,7 +2,7 @@ "lastChangelogVersion": "0.57.1", "defaultProvider": "opencode-go", "defaultModel": "kimi-k2.5", - "defaultThinkingLevel": "low", + "defaultThinkingLevel": "off", "theme": "matugen", "lsp": { "hookMode": "edit_write"