edit session
This commit is contained in:
@@ -7,8 +7,22 @@
|
|||||||
|
|
||||||
import type { ExtensionAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@mariozechner/pi-coding-agent";
|
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) {
|
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) => {
|
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") {
|
if (event.reason === "new") {
|
||||||
// Emit event for sound extensions (before hasUI check)
|
// Emit event for sound extensions (before hasUI check)
|
||||||
pi.events.emit("peon:input_required", { source: "confirm-destructive", action: "clear-session" });
|
pi.events.emit("peon:input_required", { source: "confirm-destructive", action: "clear-session" });
|
||||||
|
|||||||
@@ -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 => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"lastChangelogVersion": "0.57.1",
|
"lastChangelogVersion": "0.57.1",
|
||||||
"defaultProvider": "opencode-go",
|
"defaultProvider": "opencode-go",
|
||||||
"defaultModel": "kimi-k2.5",
|
"defaultModel": "kimi-k2.5",
|
||||||
"defaultThinkingLevel": "low",
|
"defaultThinkingLevel": "off",
|
||||||
"theme": "matugen",
|
"theme": "matugen",
|
||||||
"lsp": {
|
"lsp": {
|
||||||
"hookMode": "edit_write"
|
"hookMode": "edit_write"
|
||||||
|
|||||||
Reference in New Issue
Block a user