Created
January 16, 2026 14:02
-
-
Save badlogic/30aef35d686483ffce22cc2aad99f3ff to your computer and use it in GitHub Desktop.
Pi extension: /review - branch from root with UI side effects documented
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
| import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; | |
| /** | |
| * /review command - Create a review branch from root, return with summary | |
| * | |
| * Usage: | |
| * /review - Start review branch from root (null parent) | |
| * /end-review - Return to origin with auto-summary | |
| * /end-review <text> - Return with custom summary text | |
| * | |
| * How it works: | |
| * - Stores your current position | |
| * - Navigates to the first user message (sets leaf to null/root) | |
| * - New entries will have parentId: null (new root branch) | |
| * - User works in review branch | |
| * - Returns to origin with summary of review work | |
| * | |
| * Note: Navigating to root will put the first user message in the editor | |
| * and re-render the chat. This is unavoidable with current API. | |
| */ | |
| export default function (pi: ExtensionAPI) { | |
| // Store where we branched from | |
| let reviewOriginId: string | undefined; | |
| pi.registerCommand("review", { | |
| description: "Start a review branch from root, return when done", | |
| handler: async (args, ctx) => { | |
| // Store current position (where we'll return to) | |
| reviewOriginId = ctx.sessionManager.getLeafId(); | |
| // Save current editor text to restore after navigation | |
| const savedEditorText = ctx.ui.getEditorText(); | |
| // Find the first user message (has parentId: null) | |
| const entries = ctx.sessionManager.getEntries(); | |
| const firstUserMessage = entries.find( | |
| e => e.type === "message" && | |
| e.message.role === "user" && | |
| e.parentId === null | |
| ); | |
| if (!firstUserMessage) { | |
| ctx.ui.notify("No root user message found in session", "error"); | |
| return; | |
| } | |
| // Navigate to first user message | |
| // This will: | |
| // - Set leaf = null | |
| // - Clear chat and re-render | |
| // - Put first message text in editor | |
| const result = await ctx.navigateTree(firstUserMessage.id, { summarize: false }); | |
| if (result.cancelled) return; | |
| // Restore editor text (or clear it) | |
| ctx.ui.setEditorText(savedEditorText); | |
| // Add marker entry at start of review branch | |
| // This entry will have parentId: null (root branch) | |
| pi.appendEntry("review_start", { | |
| originId: reviewOriginId, | |
| startedAt: new Date().toISOString(), | |
| }); | |
| ctx.ui.notify( | |
| "Review branch started from root.\n" + | |
| "Chat cleared. Work normally, then use /end-review.", | |
| "info" | |
| ); | |
| }, | |
| }); | |
| pi.registerCommand("end-review", { | |
| description: "Complete review and return to original position with summary", | |
| handler: async (args, ctx) => { | |
| if (!reviewOriginId) { | |
| ctx.ui.notify("Not in a review branch (use /review first)", "error"); | |
| return; | |
| } | |
| // Get review branch to count entries | |
| const reviewBranch = ctx.sessionManager.getBranch(); | |
| // Navigate back to origin | |
| // This will also re-render chat and may put text in editor | |
| const result = await ctx.navigateTree(reviewOriginId, { | |
| summarize: true, | |
| customInstructions: args || undefined, | |
| }); | |
| if (result.cancelled) { | |
| return; | |
| } | |
| // Add completion metadata | |
| pi.appendEntry("review_complete", { | |
| reviewEntryCount: reviewBranch.length, | |
| completedAt: new Date().toISOString(), | |
| customInstructions: args || undefined, | |
| }); | |
| ctx.ui.notify( | |
| `Review complete! Returned to original position.\n` + | |
| `Review had ${reviewBranch.length} entries.`, | |
| "info" | |
| ); | |
| reviewOriginId = undefined; | |
| }, | |
| }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment