Skip to content

Instantly share code, notes, and snippets.

@phaer
Last active March 4, 2026 21:25
Show Gist options
  • Select an option

  • Save phaer/975b1e4490cf210a6cdcd7648cd8d8d1 to your computer and use it in GitHub Desktop.

Select an option

Save phaer/975b1e4490cf210a6cdcd7648cd8d8d1 to your computer and use it in GitHub Desktop.
tuicr
name description
tuicr
Review local git changes with tuicr TUI interactively. Use when the user wants to interactively review code changes, diffs, or AI-generated edits.

tuicr - TUI Change Reviewer

Launch tuicr full-screen to interactively review local git changes. The user reviews diffs, leaves comments, and exports structured feedback for the agent to act on.

Usage

Call the tuicr tool to launch the TUI. The pi TUI suspends while tuicr runs full-screen.

/skill:tuicr [directory]

Or simply mention wanting to review changes with tuicr.

Workflow

  1. Determine target directory based on context (cwd, recent file operations, or user request).

  2. Call the tuicr tool:

    • directory: path to the git repository (defaults to cwd)
    • revisions: optional commit range to review
  3. Process the result:

    • If instructions are returned: parse and execute them (code changes, fixes, etc.)
    • If no instructions: tell the user they can paste clipboard content

Example Invocations

User says: "review my changes" → Call tuicr tool with cwd

User says: "review the last 3 commits" → Call tuicr tool with revisions: "HEAD~3..HEAD"

User says: "/skill:tuicr ~/projects/myapp" → Call tuicr tool with directory: "~/projects/myapp"

When NOT to use

  • When user just wants git diff output (use git directly)
  • When reviewing remote/PR changes (use gh CLI or web)
  • When user explicitly asks for non-interactive review

~ ❯

/**
* tuicr Extension
*
* Registers a `tuicr` tool that the LLM can call to launch the tuicr TUI
* for interactive code review. Suspends the pi TUI and runs tuicr full-screen
* with terminal access. Captures exported instructions via --stdout.
*/
import { spawnSync, execSync } from "node:child_process";
import { mkdtempSync, readFileSync, unlinkSync, existsSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
export default function (pi: ExtensionAPI) {
pi.registerTool({
name: "tuicr",
label: "tuicr",
description:
"Launch tuicr TUI for interactive code review. The user reviews diffs, leaves comments, and exports structured feedback. Use when the user wants to interactively review code changes or AI-generated edits.",
parameters: Type.Object({
directory: Type.Optional(Type.String({ description: "Git repository directory to review (default: cwd)" })),
revisions: Type.Optional(Type.String({ description: "Commit range/revset to review (passed as -r flag)" })),
}),
async execute(toolCallId, params, signal, onUpdate, ctx) {
// Check tuicr is installed
try {
execSync("command -v tuicr", { stdio: "ignore" });
} catch {
return {
content: [
{
type: "text",
text: "tuicr is not installed. Install via: nix run github:agavra/tuicr, cargo install tuicr, or brew install agavra/tap/tuicr",
},
],
};
}
const targetDir = params.directory || ctx.cwd;
// Check it's a git repo
try {
execSync("git rev-parse --git-dir", { cwd: targetDir, stdio: "ignore" });
} catch {
return {
content: [{ type: "text", text: `Not a git repository: ${targetDir}` }],
};
}
if (!ctx.hasUI) {
return {
content: [{ type: "text", text: "(tuicr requires interactive TUI mode)" }],
};
}
// Build tuicr args
const tuicrArgs: string[] = [];
if (params.revisions) {
tuicrArgs.push("-r", params.revisions);
}
// Check if --stdout is supported for output capture
let hasStdout = false;
try {
const help = execSync("tuicr --help 2>&1", { encoding: "utf-8" });
hasStdout = help.includes("--stdout");
} catch {}
// With --stdout, tuicr writes export to stdout instead of clipboard.
// We redirect stdout to a file and give tuicr /dev/tty for its TUI via fd 3.
const outputFile = hasStdout ? join(mkdtempSync(join(tmpdir(), "tuicr-")), "output.md") : null;
if (hasStdout) {
tuicrArgs.push("--stdout");
}
// Run tuicr with full terminal access
const exitCode = await ctx.ui.custom<number | null>((tui, _theme, _kb, done) => {
tui.stop();
process.stdout.write("\x1b[2J\x1b[H");
let result;
if (hasStdout && outputFile) {
const { openSync, closeSync } = require("node:fs");
const fd = openSync(outputFile, "w");
result = spawnSync("tuicr", tuicrArgs, {
// stdin: terminal, stdout: file (export), stderr: terminal
stdio: ["inherit", fd, "inherit"],
env: process.env,
cwd: targetDir,
});
closeSync(fd);
} else {
result = spawnSync("tuicr", tuicrArgs, {
stdio: "inherit",
env: process.env,
cwd: targetDir,
});
}
tui.start();
tui.requestRender(true);
done(result.status);
return { render: () => [], invalidate: () => {} };
});
// Read captured instructions
let instructions = "";
if (outputFile && existsSync(outputFile)) {
try {
const content = readFileSync(outputFile, "utf-8").trim();
// Filter out terminal escape sequences (TUI noise)
const clean = content.replace(/\x1b\[[^a-zA-Z]*[a-zA-Z]/g, "").replace(/\x1b[^\x1b]{0,8}/g, "").trim();
if (clean) {
instructions = clean;
}
unlinkSync(outputFile);
} catch {}
}
if (instructions) {
return {
content: [
{
type: "text",
text: `tuicr review completed. The user exported the following instructions:\n\n${instructions}\n\nPlease address the comments above.`,
},
],
};
}
return {
content: [
{
type: "text",
text: "tuicr review completed. No instructions were exported. If the user exported to clipboard, they can paste the instructions here.",
},
],
};
},
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment