A small collection of extensions for pi-coding-agent.
| File | What it does | How to use | Requirements |
|---|---|---|---|
bash-timeout.ts |
Enforces a 60-second default and 300-second maximum for agent Bash calls | Auto-runs for agent Bash tool calls | None |
auth-backup.ts |
Manages backups of ~/.pi/agent/auth.json through a single interactive command |
Run /auth-backup |
Interactive UI |
branch-pr-widget.ts |
Shows the GitHub PR for the current branch | Auto-runs on session start and after agent turns | gh installed, current repo branch associated with a PR |
continue.ts |
Sends literal continue or approve user messages after the agent stops |
Press Ctrl+J for continue or Ctrl+R for approve while Pi is idle |
Free those keys from their built-in actions in keybindings.json |
docs-changes.ts |
Shows changed files under agent-docs/ (fallback: docs/) as a widget |
Auto-runs on session start and after agent turns | Git repo with an agent-docs/ or docs/ directory |
export-dialogue.ts |
Exports the current branch to a dated, LLM-titled JSONL file | Run /xp |
Active model credentials; optional PI_XP_PATH |
git-diff-stats.ts |
Appends whole-branch +added -deleted line counts, plus an uncommitted marker, after the branch name on the footer's cwd line |
Auto-runs on session start, after agent turns, and after mutating tools | Git repo; TUI mode |
disable-find.ts |
Blocks agent Bash invocations of find and directs the agent to use fd or rg |
Auto-runs for agent Bash tool calls | fd and rg in Pi's managed tool path |
replace-pi-with-claude-code.ts |
Rewrites pi to claude code in the system prompt |
Auto-runs before each agent start and before each provider request | None |
read-url.ts |
Adds a read_url tool that reads public URLs as Markdown through Jina Reader |
Agent calls read_url when it needs external docs |
Optional JINA_API_KEY for authenticated Jina quota |
usage-widget.ts |
Shows Anthropic/legacy Codex usage bars or an OpenAI subscription quota-unavailable notice | Auto-runs on session start, model change, and after agent turns | Anthropic OAuth, legacy OpenAI Codex auth, or OpenAI subscription OAuth |
tps.ts |
Reports output tokens per second and token usage after each agent run | Auto-runs after agent turns | Interactive UI |
fix-anthropic-thinking-block-drop.ts |
Reinjects signed thinking blocks that pi-ai drops, avoiding Anthropic 400 errors on Opus/Sonnet 4.8 |
Auto-runs before each Anthropic provider request | Anthropic model with thinking enabled |
Copy any extension file into your pi extensions directory:
cp bash-timeout.ts ~/.pi/agent/extensions/
cp auth-backup.ts ~/.pi/agent/extensions/
cp branch-pr-widget.ts ~/.pi/agent/extensions/
cp continue.ts ~/.pi/agent/extensions/
cp docs-changes.ts ~/.pi/agent/extensions/
cp export-dialogue.ts ~/.pi/agent/extensions/
cp git-diff-stats.ts ~/.pi/agent/extensions/
cp disable-find.ts ~/.pi/agent/extensions/
mkdir -p ~/.pi/agent/extensions/blocked-commands/find
cp blocked-commands/find/find ~/.pi/agent/extensions/blocked-commands/find/
cp blocked-commands/find/message.txt ~/.pi/agent/extensions/blocked-commands/find/
chmod +x ~/.pi/agent/extensions/blocked-commands/find/find
cp replace-pi-with-claude-code.ts ~/.pi/agent/extensions/
cp read-url.ts ~/.pi/agent/extensions/
cp usage-widget.ts ~/.pi/agent/extensions/
cp tps.ts ~/.pi/agent/extensions/
cp fix-anthropic-thinking-block-drop.ts ~/.pi/agent/extensions/Then reload pi:
/reload
You can also load a file directly for testing:
pi -e ./auth-backup.tsLimits agent Bash tool timeouts through the tool_call hook, without replacing the built-in tool.
- Defaults omitted timeouts to 60 seconds
- Preserves finite positive numeric timeouts up to and including 300 seconds
- Rejects explicit invalid or excessive values before execution, without clamping (schema validation may reject invalid types earlier)
- Rejection explains that the command was not executed, gives the supplied value and legal range, and asks the agent to correct its request
- Rejects direct shell
timeout/gtimeoutinvocations before the entire Bash call executes, including common wrappers, paths, pipelines and command lists; use the tool's timeout parameter instead - Rejection explicitly says no search was performed, preventing a hidden
command not founderror from looking like an empty search result - Allows ordinary arguments/search terms, quoted text, comments, heredoc bodies, and availability checks such as
command -v timeout - Adds system-prompt guidance describing the policy; no result rewriting or per-call tracking is needed
- Covers nested Bash calls through
ctx.executeTool, including codemode - Preserves native shell settings, cancellation, process cleanup, output, and rendering
- Coexists with
disable-find.ts; only omitted timeouts are filled in, and native tool results are unchanged - Does not affect user
!/!!commands or extension-owned subprocesses - Limits each call, not cumulative retries or detached background jobs; this is not a sandbox
- Shell detection is a lexical guard, not a complete Bash parser: it does not resolve aliases/variables, inspect scripts passed to
bash -c/eval, or expand substitutions inside quoted strings/heredocs. Complex wrapper options and shell grammar are not fully modeled
Run its tests with:
node --test tests/bash-timeout.test.mjsInteractive auth backup manager for ~/.pi/agent/auth.json.
Behavior:
- Stores backups under
~/.pi/agent/auth-backups/ - Uses one command:
/auth-backup - Shows an interactive list with:
+ New auth backup- existing backups with creation time and provider summary
- For an existing backup, opens an action menu:
Backup current auth hereRestore this backupDelete this backup
Restore overwrites the full ~/.pi/agent/auth.json and reloads pi.
Use it when:
- you switch between multiple auth setups
- you want to save the current login state before replacing it
- you want to restore a previous full auth state quickly
Backup list:
Action menu:
Shows the GitHub PR number and URL for the current branch.
Behavior:
- Runs
gh pr view --json number,url - Displays a widget when a PR is found
- Refreshes on:
session_startagent_end
- Refreshes in the background, so
ghnever delays startup or turn completion
Use it when:
- you work in a GitHub repo with branch-to-PR mapping
- you want the active PR visible in the UI
Adds idle-only shortcuts for sending common quick responses.
Behavior:
- Registers
Ctrl+Jto sendcontinue - Registers
Ctrl+Rto sendapprove - Does nothing while Pi is running, retrying, compacting, or handling queued messages
- Sends the selected response as a visible, persistent user message once Pi is idle
- Starts a new agent turn using the existing conversation context; it does not replay the last message or resume the interrupted provider stream
Pi commonly binds Ctrl+J to tui.input.newLine and Ctrl+R to
app.session.rename. Free both shortcuts in ~/.pi/agent/keybindings.json and keep
Shift+Enter for inserting newlines:
{
"tui.input.newLine": ["shift+enter"],
"app.session.rename": []
}This removes the rename shortcut from Pi's session selector; /name remains available for
renaming the active session.
Use it when:
- a network retry, cancelled tool call, or manual abort leaves the agent stopped
- you want one keystroke to send the same
continuemessage you would otherwise type manually - you want to approve a proposed action without typing the response
Shows changed files in agent-docs/ as a widget, falling back to docs/ if agent-docs/ does not exist.
Behavior:
- Prefers
agent-docs/; falls back only when the directory is missing, not when it has no changes - Reads tracked changes from
git diff --name-status HEAD -- <selected-directory>/ - Reads untracked files from
git ls-files --others --exclude-standard -- <selected-directory>/ - Ignores root and nested
index.mdin the selected directory - Refreshes on:
session_startagent_end
- Refreshes in the background, so Git inspection never delays startup or turn completion
Use it when:
- you are editing documentation alongside code
- you want a compact docs change summary visible at all times
Shows how much the current branch diverges from the integration branch, inline on the footer's cwd line:
~/.pi/agent/extensions (feature-x) +42 -7 *
The trailing * means part of that work is not committed yet, the same mark shell prompts use
for a dirty tree. It is a state flag rather than a second pair of numbers on purpose: "how big is
my PR" is an occasional deliberate lookup that needs a figure, while "is anything uncommitted"
is an ambient yes/no, and four digits in a footer become something you parse instead of absorb.
It appears for unstaged edits, staged edits, and untracked files, and can show on its own when
edits happen to net out to zero lines.
It renders in dim, matching the usage bars rather than standing out. An unclean tree is an
ordinary resting state, not a condition to flag, so the marker deliberately sits below the
counts in the visual hierarchy instead of competing with them.
The glyph is a plain ASCII *. Filled round glyphs were tried first and rejected in use: they
read as a bullet or a status LED and keep pulling the eye, which is wrong for something meant to
sit quietly. ASCII also removes the font risk the Unicode candidates carried, since it cannot
render as a replacement box and is unambiguously single width in every terminal.
The counts always mean one thing: everything you have that the integration branch does not. There is no per-branch special case, so the number never silently changes meaning as you switch branches. On a feature branch it keeps growing as you commit, staying a live estimate of the eventual PR size.
| Where you are | What shows up |
|---|---|
| Feature branch | Branch commits + staged + unstaged + untracked |
| Integration branch, work pushed | Staged + unstaged + untracked |
| Integration branch, local commits not pushed | Those commits + staged + unstaged + untracked |
The last row falls out of the same rule rather than being special-cased, which is why unpushed
work on main is visible instead of silently reading as zero.
That line is rendered by pi's built-in footer, and ctx.ui.setFooter() replaces the footer
wholesale. Instead of reimplementing it, the extension constructs the built-in FooterComponent
over a small ExtensionContext adapter and rewrites only line 0 of its output, so the
stats/model line keeps upstream behavior.
Base branch resolution:
- Reads the remote's advertised default branch via
git symbolic-ref refs/remotes/origin/HEAD, so repos ondeveloportrunkwork without configuration - Falls back to probing
origin/main,origin/master,main,masterin that order, preferring remote refs because a localmainis often stale - Never fetches. Resolution is local-only, so the footer never blocks on the network
Behavior:
- Diffs against
git merge-base <base> HEAD, not the base branch tip. Diffing the tip would fold commits that landed on the base branch after the fork point into your count as deletions - Passing a single commit to
git diffcompares it against the working tree, so branch commits and uncommitted edits are counted in one call - On the integration branch the merge base collapses to
HEADonce your commits are pushed, so the general rule degrades to uncommitted-only without needing a branch check - Falls back to
HEADon unrelated histories, on shallow clones missing the fork point, and when no base branch exists; then to--cachedon a repo with no commits - Adds untracked file line counts from
git ls-files --others --exclude-standard(setINCLUDE_UNTRACKED = falseto skip) - Skips binary and oversized untracked files, and caps the untracked scan at 200 files
- Omits a side entirely when it is zero, so a clean-of-deletions tree shows
+42, not+42 -0 - Detects an unclean tree with
git diff --quiet, which writes nothing and exits 1 on a difference, so the check stays cheap no matter how large the diff is - Refreshes on:
session_startagent_endtool_resultforbash/edit/write/multi_edit/apply_patch, throttled to 1.5s- git branch changes, via
footerData.onBranchChange
- All Git inspection runs in the background, so it never delays startup, tool results, or turn completion
Use it when:
- you commit repeatedly on a branch and want a running total of the eventual PR size
- you want churn visible without running
git diff --stat - you review agent-made edits before committing
Exports the active session branch to JSONL with a filename generated by the active model.
Behavior:
- Registers
/xp - Waits for the current agent run to settle, then snapshots the active branch
- Uses the current model and compaction-aware session context to generate a concise title without appending the title request to the session
- Writes
{YYYY-MM-DD}-{title}.jsonl - Re-chains branch entry parent IDs into a linear JSONL session
- Overwrites an existing file with the same name
- Reads the destination directory from
PI_XP_PATH, falling back to pi's current working directory - Requires the destination directory to exist and be writable
Optional destination configuration:
export PI_XP_PATH="/Users/sukit/Mars/life-engineering/5. Dialogues"Use it when:
- you want a portable JSONL transcript of the current branch
- you want dialogue exports named by topic instead of session UUID
Blocks find in agent-initiated Bash tool calls and directs the agent toward the faster managed search tools already provided by Pi.
Behavior:
- Adds an up-front system-prompt rule to use
fdfor file-name/path searches andrgfor content searches - Uses the
tool_callhook without replacing Pi's built-in Bash tool, preserving configured shell settings and session cwd - Prepends the dedicated
blocked-commands/find/shim directory to agent Bash calls - Blocks explicit-path invocations such as
/usr/bin/find ...before execution - Loads both block paths from the same message file and fails extension initialization when either supporting asset is missing
- Does not attempt to translate
findarguments because the two CLIs are not compatible - Does not restrict directory access
- Does not affect user-initiated
!find ...or!!find ...commands inside Pi
Run its tests with:
node --test tests/disable-find.test.mjsRewrites occurrences of pi in the system prompt to claude code before each run.
Behavior:
- Hooks
before_agent_startfor turns started by typed input orsendUserMessage - Hooks
before_provider_requestand rewrites the serializedsystemblocks, covering turns triggered by extension custom messages (pi.sendMessagewithtriggerTurn), which never firebefore_agent_start - Replaces
picase-insensitively withclaude code - Preserves
cache_controland other block fields; leaves message content and non-Anthropic payload shapes untouched - Idempotent: an already-rewritten prompt passes through unchanged
Use it when:
- you want the agent framed as Claude Code instead of pi
- you use Anthropic subscription OAuth, whose billing classifier rejects requests that do not look like Claude Code with 400 "You're out of extra usage" even when quota remains
Adds a read_url tool for reading public HTTPS URLs as LLM-friendly Markdown using Jina Reader.
Behavior:
- Registers a
read_urltool callable by the agent - Uses anonymous Jina Reader requests first
- Falls back to
JINA_API_KEYwhen anonymous quota is exhausted and the environment variable is set - Caches successful fetches for 30 days under
~/.pi/agent/caches/read-url/ - Stores each cached document as:
content.mdmeta.json
- Uses readable cache directory names, with a short URL hash suffix to avoid collisions
- Canonicalizes document URLs by default:
- removes fragments
- strips query parameters
- removes non-root trailing slashes
- Supports line-based pagination with
offsetandlimit - Provides compact TUI rendering, with expandable results
- Returns actionable error messages for Jina rate limits, missing API keys, invalid API keys, insufficient balance, and stale-cache fallback
Tool parameters:
| Parameter | Default | Description |
|---|---|---|
url |
required | HTTPS URL to read. Non-HTTPS URLs are refused |
offset |
1 |
1-based line offset for pagination |
limit |
300 |
Number of lines to return, max 1000 |
refresh |
false |
Force re-fetch and overwrite cache. Do not use by default |
preserveQuery |
false |
Preserve query parameters when they are required for page content |
Cache example:
~/.pi/agent/caches/read-url/
openai.com--index-introducing-trusted-contact-in-chatgpt--178cf0649d10/
content.md
meta.json
Optional authenticated quota:
export JINA_API_KEY="..."Use it when:
- you want the agent to inspect public documentation, blog posts, changelogs, or API references
- you want cached URL reading with pagination instead of manually pasting Markdown into the prompt
- you want anonymous Jina usage by default, with API-key fallback only when needed
Security note:
read_url puts external webpage content into the agent context. Treat every fetched page as untrusted input. Do not read random links, suspicious pages, or user-generated content you do not trust. A page can contain prompt injection text that tells the agent to ignore previous instructions, reveal secrets, call tools, run commands, or follow links.
The extension reduces this risk in a few ways:
- Tool output labels fetched content as untrusted external content
- Fetched content is wrapped inside a
<document>boundary - The tool guidelines tell the agent not to follow instructions inside fetched pages
- The tool does not use browser cookies or your logged-in Chrome session
- The tool refuses non-HTTPS URLs, so fetched content is not retrieved over unauthenticated HTTP transport
These are prompt-level mitigations, not a security boundary. They do not guarantee that the agent will never be influenced by malicious content. Only read URLs from sources you trust, such as official documentation, vendor docs, repository docs, and known technical blogs.
Limitations:
- Does not use browser cookies or your logged-in Chrome session
- Does not read non-HTTPS URLs
- Does not read private documents unless Jina Reader can access them publicly
- Query parameters are stripped by default; pass
preserveQuery: truefor search, pagination, filters, or pages where query parameters define the content - Prompt injection remains possible if the fetched page contains malicious instructions
Shows usage information or an availability notice for the active provider.
Handled providers:
anthropic: subscription usage barsopenai-codex: legacy Codex subscription usage barsopencode-go: OpenCode Go 5h / 7d / 30d usage barsopenai: ChatGPT subscription OAuth shows a quota-unavailable notice andhttps://chatgpt.com/settings/usage; API-key authentication keeps the widget hidden
Behavior:
- Displays usage bars for primary and secondary windows when quota data is available
- Shows reset times
- Computes a 7-day pace delta for Anthropic-style usage windows
- Caches usage briefly to avoid excessive requests
- Refreshes on:
session_startmodel_selectagent_end
- Fetches in the background, so provider APIs never delay startup, model selection, or turn completion
Data sources:
- Anthropic:
https://api.anthropic.com/api/oauth/usage - Legacy Codex:
https://chatgpt.com/backend-api/wham/usage - OpenCode Go:
https://opencode.ai/console/api/go/status, the same endpoint the console UI reads - Native OpenAI subscription: no supported quota API; the widget makes no usage requests and does not resolve or refresh access tokens
Pi added native OpenAI subscription sign-in in v0.99.0. Its direct OAuth tokens target api.openai.com and are rejected by the legacy ChatGPT usage endpoint. OpenAI confirmed that quota reporting is not currently supported. The widget does not borrow another account's legacy Codex credentials, read browser cookies, or estimate remaining quota from local token counts. Native subscription limits may include app-specific allowances, so legacy plan quota is not a replacement. See OpenAI's errors and recovery guidance.
OpenCode Go meters are micro-cent integers, so percentages are computed as usedMicroCents / limitMicroCents. The older https://opencode.ai/zen/go/v1/usage endpoint returns the same ratios truncated to whole percentages (0.91% becomes 0), which disagrees with the console, so the widget queries the status endpoint instead.
Use it when:
- you want quota visibility while working
- you switch between Anthropic and Codex models
Notifies the interactive UI after an agent run with output tokens per second, input and output tokens, total tokens, cache-hit percentage, and elapsed time. Cache hit is cache-read tokens divided by all prompt tokens (uncached input + cache reads + cache writes), rounded to a whole percentage unless a partial hit would round to 100%; runs with no prompt tokens omit the percentage. The rate divides output tokens by the entire agent run duration, including tool execution and other delays; it is not the model's pure generation speed. Runs with no output tokens produce no notification.
Workaround for an Anthropic 400 error seen on Opus/Sonnet 4.8:
thinking or redacted_thinking blocks in the latest assistant message cannot be modified
The root cause is in pi-ai: when building the Anthropic payload it drops any thinking block whose visible text is empty, even when the block is signed. With adaptive (summarized) thinking the model often emits signed thinking blocks with an empty summary, and Opus/Sonnet 4.8 rejects the replay when one is missing.
Behavior:
- Hooks
before_provider_requestand only acts on theanthropic-messagesAPI - Aligns each assistant message in the outgoing payload with the original session message from the tail
- Reinjects any signed, empty-text thinking block that pi-ai dropped, at its correct position
- Repairs same-model turns only; cross-model thinking drops are intentional and left untouched
- Bails out without modifying the payload if its model of pi-ai's transform diverges from the actual payload
- Sets a status line reporting how many blocks were reinjected
Use it when:
- you run Anthropic Opus/Sonnet 4.8 with adaptive (
summarized) thinking - you hit
thinking ... blocks in the latest assistant message cannot be modified400 errors - you want a stopgap until upstream pi-ai keeps signed thinking blocks regardless of text emptiness
| Extension | Notes |
|---|---|
auth-backup.ts |
Requires interactive UI. Restore replaces the full auth file, not a single provider entry. |
branch-pr-widget.ts |
Hidden when no PR is associated with the current branch or gh is unavailable. |
docs-changes.ts |
Hidden when neither agent-docs/ nor docs/ exists, or the selected directory has no matching changes. |
export-dialogue.ts |
/xp makes a separate title-generation request with the active model. The request is not persisted in the exported session. |
git-diff-stats.ts |
Replaces the footer, so it conflicts with any other setFooter extension (last one to run wins). It reuses the built-in FooterComponent, but cannot read the auto-compaction flag, so the stats line always shows (auto). The base branch is resolved from local refs only; after a long gap without fetching, a branch rebased onto newer upstream commits can report a stale merge base. Shows nothing outside a git repo or when the branch has no net change. |
disable-find.ts |
Applies only to agent Bash tool calls. User-initiated ! and !! Bash commands in Pi remain unrestricted. The extension fails to load if its dedicated shim or shared message file is missing. |
replace-pi-with-claude-code.ts |
Only affects system prompt text (agent state and serialized provider payload), not UI labels, command names, or message content. |
read-url.ts |
Reads public URLs through Jina Reader. Set JINA_API_KEY only if you want authenticated fallback after anonymous quota is exhausted. |
usage-widget.ts |
Hidden for unsupported providers and OpenAI API keys. Native OpenAI subscription OAuth shows a quota-unavailable notice; Anthropic/legacy Codex refresh failures show sanitized errors, retaining stale data when available. |
tps.ts |
Requires an interactive UI and a completed agent run with output tokens. The reported rate includes tool execution time; cache-hit percentage is omitted when there are no prompt tokens. |
fix-anthropic-thinking-block-drop.ts |
Workaround for a pi-ai thinking-block drop bug. Acts only on the anthropic-messages API and same-model turns. Remove once pi-ai keeps signed empty-text thinking blocks. |
MIT License


