Last active
June 19, 2026 11:40
-
-
Save rstacruz/a4f0503a5d4d8910fb81bf11d78beb8b to your computer and use it in GitHub Desktop.
pi /edit extension for editing Markdown plans
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Adds `/edit` to pi. | |
| // | |
| // Useful for plans: pi writes the Markdown, then you refine it in your editor. | |
| // | |
| // How it works: | |
| // - remembers the last `.md` file written via the `write` tool | |
| // - suggests `/edit` after writing a Markdown file | |
| // - opens that file in `$EDITOR` | |
| // | |
| import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; | |
| import { spawnSync } from "node:child_process"; | |
| import { resolve } from "node:path"; | |
| export default function (pi: ExtensionAPI) { | |
| let lastMarkdownFile: string | null = null; | |
| pi.on("tool_result", async (event, ctx) => { | |
| if (event.toolName !== "write") return; | |
| const path = event.input.path as string; | |
| if (!path?.endsWith(".md")) return; | |
| lastMarkdownFile = resolve(ctx.cwd, path); | |
| const editor = process.env.EDITOR || "nvim"; | |
| ctx.ui.notify(`Use /edit to open the file in ${editor}`, "info"); | |
| }); | |
| pi.registerCommand("edit", { | |
| description: "Open a file in a text editor. No arg = last written .md file", | |
| handler: async (args, ctx) => { | |
| await ctx.waitForIdle(); | |
| const editor = process.env.EDITOR || "nvim"; | |
| const filePath = args.trim() | |
| ? resolve(ctx.cwd, args.trim()) | |
| : lastMarkdownFile; | |
| if (!filePath) { | |
| ctx.ui.notify("No file specified and no recent .md file written", "error"); | |
| return; | |
| } | |
| if (!ctx.hasUI) { | |
| ctx.ui.notify("/edit requires TUI mode", "error"); | |
| return; | |
| } | |
| const exitCode = await ctx.ui.custom<number | null>((tui, _theme, _kb, done) => { | |
| tui.stop(); | |
| process.stdout.write("\x1b[2J\x1b[H"); | |
| const [cmd, ...editorArgs] = editor.split(/\s+/); | |
| const result = spawnSync(cmd!, [...editorArgs, filePath], { | |
| stdio: "inherit", | |
| env: process.env, | |
| }); | |
| tui.start(); | |
| tui.requestRender(true); | |
| done(result.status); | |
| return { render: () => [], invalidate: () => { } }; | |
| }); | |
| if (exitCode !== 0 && exitCode !== null) { | |
| ctx.ui.notify(`Editor exited with code ${exitCode}`, "info"); | |
| } | |
| }, | |
| }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment