CRITICAL: pi Code Review
This skill delegates a code review to the pi CLI tool (@earendil-works/pi-coding-agent). pi runs with read-only tools to prevent accidental edits — it analyzes code and returns findings as text.
CRITICAL: The default review target is the uncommitted working tree (git diff HEAD), and pi must NOT be able to explore the whole codebase or run git itself. pi's --tools flag is a hard allowlist — only listed tools are registered (pi's built-in tools are read, grep, find, ls, bash, edit, write; there is no standalone git tool, but bash can run git diff). By default restrict pi to read only so it can inspect files mentioned in the diff but cannot find/grep/ls the repo or run bash/git. Only expand to read,grep,find,ls when an explicit target (--branch, --diff, PR number) or --explore requires it.
Before Execution: Check Installation
command -v pi >/dev/null 2>&1
If not installed, tell the user and stop.
Persistent Settings
User preferences persist across invocations via JSON files. The resolution chain (highest priority first):
- CLI flag (from
$ARGUMENTS) .claude/pi.local.json— project-specific overrides, gitignored.claude/pi.json— project shared defaults, committed~/.claude/pi.local.json— global user-wide defaults- Built-in defaults (listed below)
Settings files use the format below. Load references/settings.md for the full format, reading logic, --edit-config, and --list-models:
{
"endpoints": {
"my-proxy": {
"provider": "openai",
"baseUrl": "http://10.10.0.195:8317/v1",
"models": ["gemini-3.6-flash-high", "gemini-3.6-pro"]
}
},
"defaultEndpoint": "my-proxy",
"defaultModel": "gemini-3.6-flash-high"
}
Each endpoint key has provider (required), optional baseUrl, and models. Values may reference env vars via $VAR. Read the merged settings with the Reading settings snippet in references/settings.md — it yields ENDPOINT, MODEL, THINKING, PROVIDER, BASE_URL, API_KEY, WITH_PACKAGES.
Before parsing review targets, handle the two settings-only flags from references/settings.md and stop (do not proceed to review):
$ARGUMENTSis exactly--edit-config(with optional scope flag) → open the settings file (seereferences/settings.md).$ARGUMENTSis exactly--list-models→ print configured endpoints and models (seereferences/settings.md).
Argument Parsing
Parse $ARGUMENTS to extract the review target and optional flags. The target is everything before the first -- flag. If no flags are present, the entire argument is the target.
| Flag | Description | Source Priority |
|---|---|---|
--endpoint | Endpoint key name (must match a key in settings endpoints) | CLI > settings > defaultEndpoint |
--model | Model ID to use for this review | CLI > settings > (endpoint's first model) |
--thinking | Thinking level (off/minimal/low/medium/high/xhigh/max) | CLI > settings > max |
--explore | Force full read-only exploration tools (read,grep,find,ls) even for the default working-tree review. Without this, the default review gets read only. | CLI flag |
--with-packages | Load the user's global pi packages/skills/extensions (default is clean mode: off) | CLI > settings withPackages > false |
Resolution order per flag
For each flag, resolve the value by checking CLI flag first, then settings file, then built-in default:
- Parse
$ARGUMENTSfor that flag. If present, use it. - Otherwise, read from
$CONFIG(the merged settings). If non-null/non-empty, use it. - Otherwise, use the built-in default.
Endpoint resolution
- If
--endpointis specified, use it as the key intoendpointsconfig. - If
--modelis specified without--endpoint, scan all endpoints for a model matching the ID — use the first match's endpoint. - Otherwise, use
defaultEndpointfrom settings. - If the resolved model is empty, use the first model in the resolved endpoint's model list.
- Resolve the pi provider from the endpoint's
providerfield (defaultopenai).
Base URL resolution
The pi-agent handles baseUrl — it conditionally writes it into the agent-dir models.json (pi has no --base-url flag) only when it differs from the existing value. The skill just passes the resolved BASE_URL to the agent; it does not write models.json itself.
Review Target
Determine what to review from the parsed arguments. The target can be:
| Pattern | What it reviews | pi tools |
|---|---|---|
| No target (default) | git diff HEAD — uncommitted working tree changes (staged + unstaged) | read only |
--branch <name> | git diff main...<branch> | read,grep,find,ls |
--diff <range> | git diff <range> | read,grep,find,ls |
@filepath | Specific file(s) | read,grep,find,ls |
| PR number | gh pr diff <n> | read,grep,find,ls |
--explore (any target) | Overrides the tool set to full read-only exploration | read,grep,find,ls |
Resolution logic
- CRITICAL: Do NOT use
@.— pi does not support passing a directory path as@.. It will error withEISDIR. - By default (no target), review uncommitted working tree changes — capture
git diff HEAD(staged + unstaged vs HEAD) and pass it to pi. If the diff is empty, report that the working tree is clean and stop. Restrict pi to--tools readso it cannot scan the codebase or run git. - Only pass
@filepathreferences when the user explicitly names specific files (target starts with@). - If the target is a number (e.g.
42), treat it as a GitHub PR number — fetch the diff withgh pr diff <n>. - If the target starts with
--branch, extract the branch name and capturegit diff main...<branch>. - If the target starts with
--diff, extract the range and capturegit diff <range>. - Otherwise (free-text task description, e.g.
/pi:review "check the auth flow"), pass the text as the task description and still capturegit diff HEADas context so pi reviews your current changes in service of the stated task. Free-text only applies when there is no explicit target and no@filereference — leading tokens up to the first--flag (socheck auth --explore→ task "check auth"). - Tool selection: default (no target) →
--tools read. Any explicit target (--branch,--diff,@filepath, PR number) →--tools read,grep,find,ls. If--exploreappears in$ARGUMENTS→ force--tools read,grep,find,lsregardless of target. Never includebash— the read-only review must not let pi rungitor edit files.
File references (when user specifies @filepath)
When the user passes @filepath, pass those file paths directly to pi as @file.ts arguments. pi will read them.
Review Rubric (Embedded in Prompt)
The review prompt given to pi must cover these dimensions. Embed them as part of the task description, not as --append-system-prompt. See references/rubric.md for the full five-dimension rubric — the TASK prompt in the agent launch embeds a condensed version of it.
Context Collection
1. Pass CLAUDE.md as System Prompt Context
Always pass the CLAUDE.md files as system prompt context so pi understands the project and user conventions. --append-system-prompt accepts file paths directly — pi reads them automatically.
CRITICAL: do not accumulate the flags into a space-joined variable and expand it unquoted. Under zsh (the default shell on macOS) that expansion is a single argument, so pi receives the literal string --append-system-prompt /path/CLAUDE.md as one token and appends it as text instead of reading the file. The pi-agent assembles the command as an array, so the skill only needs to hand the agent the resolved context files:
# Collect CLAUDE.md paths to pass to the agent as separate --append-system-prompt args
APPEND_PATHS=()
[ -f "$HOME/.claude/CLAUDE.md" ] && APPEND_PATHS+=("$HOME/.claude/CLAUDE.md")
[ -f "CLAUDE.md" ] && APPEND_PATHS+=("CLAUDE.md")
2. Git Context
git status --short
git diff --stat
git log --oneline -20
git branch --show-current
3. Capture the diff
Capture the diff for the resolved target into a temp file, then pass its path via --append-system-prompt (pi reads file paths directly):
DIFF_FILE=$(mktemp /tmp/pi-review-diff.XXXXXX)
HAS_EXPLICIT_TARGET=""
TASK_TEXT="" # free-text task description, if any
FILE_REFS=() # @file references, one per array element
# No target (default): uncommitted working tree changes (staged + unstaged vs HEAD)
git diff HEAD > "$DIFF_FILE"
# For --branch <name>: diff against main
if [[ "$ARGUMENTS" == *"--branch"* ]]; then
HAS_EXPLICIT_TARGET="1"
BRANCH_NAME=$(echo "$ARGUMENTS" | sed -n 's/.*--branch[= ]\([^ ]*\).*/\1/p')
git diff main...${BRANCH_NAME//\"/} > "$DIFF_FILE"
fi
# For --diff <range>
if [[ "$ARGUMENTS" == *"--diff"* ]]; then
HAS_EXPLICIT_TARGET="1"
RANGE=$(echo "$ARGUMENTS" | sed -n 's/.*--diff[= ]\([^ ]*\).*/\1/p')
git diff "${RANGE//\"/}" > "$DIFF_FILE"
fi
# For a PR number: only when the FIRST token of $ARGUMENTS is all digits
FIRST_TOKEN=$(echo "$ARGUMENTS" | awk '{print $1}')
if [[ "$FIRST_TOKEN" =~ ^[0-9]+$ ]]; then
HAS_EXPLICIT_TARGET="1"
gh pr diff "$FIRST_TOKEN" > "$DIFF_FILE"
fi
# For @filepath references (user-named files): explicit target, no diff to capture
if [[ "$ARGUMENTS" == *"@"* ]]; then
HAS_EXPLICIT_TARGET="1"
: > "$DIFF_FILE" # clear: @file reviews pass files, not a diff
# Read one @ref per line into the array (avoids zsh word-splitting pitfalls)
FILE_REFS=()
while IFS= read -r ref; do
[ -n "$ref" ] && FILE_REFS+=("$ref")
done < <(echo "$ARGUMENTS" | grep -oE '@[^ ]+' || true)
fi
# Free-text task description: only when there is no explicit target and no @file refs.
# Take the leading tokens up to the first `--` flag, so `check auth --explore` → "check auth",
# and `--model gemini-3.6-pro` → "" (starts with a flag).
if [ -z "$HAS_EXPLICIT_TARGET" ] && [[ "$ARGUMENTS" != *"@"* ]]; then
TASK_TEXT=$(echo "$ARGUMENTS" | sed -E 's/[[:space:]]*--.*$//' | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')
fi
Then build the diff context — this MUST be run as actual commands in the same shell, not left as prose. Collect context file paths into APPEND_PATHS (CLAUDE.md files from step 1, plus the diff when present) so the pi-agent emits each as its own --append-system-prompt argument:
# Context files: CLAUDE.md paths (from "Context Collection") plus the diff when non-empty
if [ -s "$DIFF_FILE" ]; then
APPEND_PATHS+=("$DIFF_FILE")
fi
Empty-diff guard (default target only): if git diff HEAD produces no output and no explicit target was given (HAS_EXPLICIT_TARGET empty), the working tree is clean — report "No uncommitted changes to review — the working tree is clean. Use /pi:review --branch <name>, /pi:review --diff <range>, or /pi:review <PR> to review committed code." and stop before invoking pi. Run the guard as a command:
if [ ! -s "$DIFF_FILE" ] && [ -z "$HAS_EXPLICIT_TARGET" ] && [ -z "$TASK_TEXT" ]; then
rm -f "$DIFF_FILE" # clean up temp files created above ($GIT_FILE is created later, after this guard)
echo "No uncommitted changes to review — the working tree is clean. Use --branch, --diff, or a PR number."
exit 0
fi
Execution
Do NOT run pi directly. After resolving settings and capturing the review target, launch the dedicated pi:pi-agent execution layer with the Task tool. It builds the pi command, runs it in the background, and returns pi's stdout.
PREREQUISITE: run the "Reading settings" snippet from references/settings.md first — it defines $PROVIDER, $MODEL, $API_KEY, $THINKING. Then capture the diff (see "Context Collection") into $DIFF_FILE/$GIT_FILE, and resolve $TOOLS:
# Tool selection — see "Review Target" resolution logic
# Default (no target): read only, so pi cannot scan the codebase or run git.
# Explicit target or --explore: read,grep,find,ls. Never include bash.
if [[ "$ARGUMENTS" == *"--explore"* ]] || [ -n "$HAS_EXPLICIT_TARGET" ]; then
TOOLS="read,grep,find,ls"
else
TOOLS="read"
fi
# Collect git context (status/stat/log/branch) into a temp file, passed like the diff
GIT_FILE=$(mktemp /tmp/pi-review-git.XXXXXX)
{ git status --short; git diff --stat; git log --oneline -20; git branch --show-current; } > "$GIT_FILE"
APPEND_PATHS+=("$GIT_FILE") # git context is a second append (besides CLAUDE.md + diff)
Then launch pi:pi-agent with the Task tool, passing:
MODE: review
TASK: Review the code in the provided diff${TASK_TEXT:+ (task: $TASK_TEXT)}. Use your tools only to read the files mentioned in the diff for context — do NOT search the rest of the codebase, do NOT run git. Focus on correctness, code quality, security, architecture, and testing. For each issue found, report: file:line: severity (HIGH/MEDIUM/LOW) + description + suggested fix. Group findings by severity. If no issues found, explicitly state that the code looks clean.
PROVIDER: <resolved from settings>
MODEL: <resolved from settings>
ENDPOINT: <resolved endpoint key or empty — lets the pi-agent read credentials from the right endpoint (non-secret)>
THINKING: <resolved, default max>
TOOLS: <$TOOLS — read, or read,grep,find,ls>
WITH_PACKAGES: <true if --with-packages or settings withPackages; empty/false = clean mode>
APPEND_PATHS: <$APPEND_PATHS — CLAUDE.md paths + GIT_FILE + DIFF_FILE; the agent emits each as its own --append-system-prompt>
FILE_REFS: <$FILE_REFS — @file args, one per line; omit if none>
CLEANUP_FILES: <$DIFF_FILE $GIT_FILE — the agent removes them when pi exits>
Do NOT pass API_KEY or BASE_URL — the pi-agent reads them from the settings files itself (so the actual key/URL never enters the model's context). The pi-agent emits --append-system-prompt for every APPEND_PATHS entry, writes baseUrl to the agent-dir models.json when it resolves one, runs pi in the background with --no-session --no-context-files --approve plus default clean mode (--no-extensions --no-skills unless WITH_PACKAGES=true), and reports pi's stdout — which is the review text.
Handling Output
CRITICAL: pi's stdout is the review text
Unlike /pi:delegate where pi edits files, review mode uses read-only tools (read, or read,grep,find,ls with an explicit target) — pi cannot write files or run bash. Its stdout IS the review output. The pi-agent returns this stdout; present it to the user.
On Success (exit code 0)
Present pi's output as the review findings. Format it clearly:
- If pi returned structured findings with severity, present them grouped by severity.
- If pi said "no issues found", report that the code looks clean.
- If stdout is empty but exit was 0, report: "pi completed the review but produced no output. This may indicate the model didn't understand the task. Consider retrying with a more specific prompt."
On Error (exit code 1+)
Show the error message from the pi-agent's report. Common causes:
- pi not configured (no API key)
- Provider/model not available
- Task interrupted or killed
Usage Examples
Review uncommitted working tree changes (default)
/pi:review
Reviews git diff HEAD — all staged and unstaged changes vs the last commit. If the working tree is clean, reports so and stops.
Review with a specific endpoint
/pi:review --endpoint openrouter
Review with a specific model
/pi:review --model gemini-3.6-pro
Review a specific branch
/pi:review --branch feat/new-widget --endpoint local-proxy --model gemini-3.6-flash-high
Review recent changes
/pi:review --diff HEAD~5..HEAD
Review a pull request
/pi:review 42
Review a specific file
/pi:review @src/core/agent.ts
Let pi freely explore the codebase (default review, expanded tools)
/pi:review --explore
Overrides the default read-only restriction to read,grep,find,ls for the working-tree review. pi still cannot edit files or run bash.
List configured endpoints
/pi:review --list-models
Edit project settings
/pi:review --edit-config
Important Notes
- pi runs with read-only tools — it cannot edit files or run bash.
- Default review is restricted to
--tools read— pi can read files mentioned in the diff but cannotgrep/find/lsthe codebase or rungit, so it cannot silently review the whole repo. Explicit targets (--branch,--diff,@filepath, PR number) or--exploreexpand it toread,grep,find,ls. - Default behavior reviews uncommitted working tree changes (
git diff HEAD, staged + unstaged). If the working tree is clean, the skill reports it and stops — use--branch <name>,--diff <range>, or a PR number to review committed code. - Never run pi directly — always delegate to
pi:pi-agent. The agent is the plugin's single execution path and owns backgrounding, verification, and error handling. - CLAUDE.md context is always passed by pi-agent via
--append-system-promptas file paths —~/.claude/CLAUDE.md(user global) and./CLAUDE.md(project). pi reads them automatically. - pi only knows built-in provider names (
openai,anthropic,google, etc.). The settingsendpointsmap is just for user convenience. The pi-agent writesbaseUrlto the agent-dirmodels.json(default~/.pi/agent/models.json, redirectable viaPI_CODING_AGENT_DIR/AGENT_DIR) under the endpoint'sproviderfield, then passes--provider <provider>to pi. - No shell
timeout— reviews can be heavy and should run to completion. - The review rubric is embedded in the task description, not
--append-system-prompt, to keep it in pi's context window. - Git context and diffs go into
--append-system-promptas structured context. - PR review requires
ghCLI to be installed and authenticated. - For large codebases, consider targeting a specific branch, diff range, or file to keep the review focused.
- Settings are shared with
/pi:delegatevia the same file chain (.claude/pi.local.json,~/.claude/pi.local.json). Both skills read the same files, but each uses its own format — you can keep both in the same file. - To configure pi (provider, model, base URL), run
/pi:setupinstead of passing flags manually.