Skip to content

Instantly share code, notes, and snippets.

@gabrielmontagne
Created February 17, 2026 10:05
Show Gist options
  • Select an option

  • Save gabrielmontagne/852b24d3329f01a53479e1d2d993bc76 to your computer and use it in GitHub Desktop.

Select an option

Save gabrielmontagne/852b24d3329f01a53479e1d2d993bc76 to your computer and use it in GitHub Desktop.
review gate for pi
/**
* Review Gate Extension
*
* Toggle with /review-gate. When active, intercepts edit/write tool calls:
* writes /tmp/review-a, /tmp/review-b, and /tmp/proposed.diff, then prompts
* inside pi. Empty input = approve, text = block with reason.
*/
import type { ExtensionAPI, EditToolCallEvent, WriteToolCallEvent } from "@mariozechner/pi-coding-agent";
import { readFile, writeFile } from "fs/promises";
import { resolve } from "path";
export default function (pi: ExtensionAPI) {
let enabled = true;
pi.registerCommand("review-gate", {
description: "Toggle review gate for edit/write tools",
handler: async (_args, ctx) => {
enabled = !enabled;
ctx.ui.notify(`Review gate ${enabled ? "on" : "off"}`, "info");
},
});
pi.on("tool_call", async (event, ctx) => {
if (!enabled) return undefined;
if (event.toolName !== "edit" && event.toolName !== "write") return undefined;
if (!ctx.hasUI) return undefined;
const path = resolve(ctx.cwd, event.input.path as string);
let a: string;
let b: string;
if (event.toolName === "edit") {
const input = event.input as EditToolCallEvent["input"];
a = input.oldText;
b = input.newText;
} else {
const input = event.input as WriteToolCallEvent["input"];
try {
a = await readFile(path, "utf-8");
} catch {
a = "";
}
b = input.content;
}
await writeFile("/tmp/review-a", a);
await writeFile("/tmp/review-b", b);
const label = event.input.path as string;
const diff = await pi.exec("diff", [
"-u",
"--label",
`a/${label}`,
"--label",
`b/${label}`,
"/tmp/review-a",
"/tmp/review-b",
]);
const diffText = diff.stdout || "(no differences)\n";
await writeFile("/tmp/proposed.diff", diffText);
const response = await ctx.ui.input(`${event.toolName} ${label}`);
if (response === undefined) {
return { block: true, reason: "Review cancelled" };
}
if (response.trim() === "") return undefined;
return { block: true, reason: response.trim() };
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment