Custom extensions, core packages, and configuration templates for Pi Coding Agent.
🕒 Last Updated: 2026-08-21 (KST)
custom-pi is built around 3 core infrastructure pillars to empower Pi with web capabilities, autonomous subagents, and high-performance LLM gateway connectivity:
- Package:
npm:pi-web-access - Tools:
web_search,fetch_content,get_search_content - 2-Tier Search Routing: Built-in Exa → Brave fallback chain configured via
config/web-search.json.example. - Curation: Set
"workflow": "none"to prevent browser timeout issues and ensure fast search results.
- Package:
npm:pi-subagents - Tools:
subagent,subagent_wait,subagent_supervisor,intercom - Multi-Agent Tasks: Enables parent Pi sessions to spawn background subagent tasks with bi-directional supervisor communication via native intercom channels.
⚠️ Local patch (2026-08-14, spinner fix; applied Studio·MBP 08-14, PICS/simlab 08-15): The async widget spinner froze / ran at ~1 fps during background runs — its 250 ms animation tick calledctx.ui.requestRender?.(), which does not exist on Pi'sExtensionUIContext(silent no-op), so the widget only repainted on job status changes (~1 s). Local patch applied to~/.pi/agent/npm/node_modules/pi-subagents/src/runs/background/async-job-tracker.ts:542(requestLastWidgetRender()→rerenderLastWidget()). Re-apply after package updates (PICS는 0.43.0→0.49.0 업그레이드 후 적용). Details & re-apply/rollback commands:docs/PI-SUBAGENTS-SPINNER-FIX.md.
- Gateway: Connects Pi to OpenCode Zen Go Gateway (
opencode-go) for unified access to DeepSeek V4, Kimi K3, GLM 5.2, MiniMax, Qwen 3.7 Max, Grok 4.5, etc. - DeepSeek Max Reasoning Effort: DeepSeek 모델에
thinkingLevelMap(reasoning: true,off/high/max)을models.json에 정의해 Pi TUI에서 Thinking Level을 고를 수 있게 한다. 2026-08-16 기준 배포 기본값은defaultThinkingLevel: "high"— 최대 추론이 필요하면 settings.json에서"max"로 올린다. (구 "max 기본값" 서술은 현재 파일과 불일치 — OpenCode 안전 계약의 max 정책은 OpenCode CLI 쪽에만 남아 있음) - 📖 See OpenCode Go Provider Integration Guide for detailed setup.
Surgical local patches to Pi's own core dependency packages (not custom-pi extensions) — applied directly to installed node_modules, documented and reapplied after upgrades.
- 🧊 Stream idle-timeout (2026-08-20, applied Studio·MBP·PICS):
@earendil-works/pi-agent-core'sagent-loop.jsconsumes each turn's model response viafor awaitover anEventStream. If a provider adapter's SSE stream closes without ever pushing a terminal"done"/"error"event, thatfor awaithangs forever with 0% CPU, 0 open sockets, no error — the turn never gets astopReason, the TUI freezes onThinking...indefinitely (root-caused live on a hung PICS session: full response already received and persisted, butstopReason: null). Patch races each stream event against an idle timer (PI_STREAM_IDLE_TIMEOUT_MS, default 180s) and force-completes the turn withstopReason: "error"on timeout — preserves any partial content already streamed, feeds into pi's existing error/auto-retry path. Provider-agnostic (single chokepoint all model calls pass through). The fleet startup policy sets it to 600s so a legitimate 200K-token cold prefill can finish; details, diff, and reapply/rollback commands:docs/PI-STREAM-IDLE-TIMEOUT-PATCH.md. ⚠️ pi-subagents spinner fix (2026-08-14) — see pillar 2 above anddocs/PI-SUBAGENTS-SPINNER-FIX.md.
custom-pi includes essential developer productivity tools:
- 🚪
core_commands.ts(Extra Slash Commands): Adds/exit(graceful Pi shutdown, same as/quitviactx.shutdown()) and/clear(start a new session, same as/newviactx.newSession()). Implemented as an extension so it survives Pi updates. - 🕐
bg.ts(Bash Background Runner): Long-running bash commands (downloads, builds) can be pushed to the background so the agent keeps working on other tasks.- 🏗 Redesigned (2026-08-20, plan) — backgrounding is now explicit at start and wrapper/tail-free: the extension
spawn()s a detached bash (own process group) directly, logging to/tmp/pi-bg/<jobid>/log. No wrapper script, notail -f→ no orphan tails, no pipe pollution, nopkillself-recursion, and zero overhead for ordinary commands (they pass through unwrapped)./bg <command>(user) — runs the command in the background and returns immediately; completion is auto-injected (below)./bg·/bglist·/bgkillare always registered.# bg:runmarker (agent, G8) — models cannot type slash commands, so appending# bg:runto a Bash tool command rewrites the call tospawnBackground(): the tool call returns immediately with[bg] started in background job=<id>, and the completion notice closes the loop with zero user intervention.# bg:quietcombines with it.ctrl+q— shows status of running/bgjobs (pid + last 10 log lines); with no jobs it hints to use/bg. (The old "send the running foreground command to background" mid-execution transition is retired — background at start instead.)bgnow {list|status <id>|kill <id>}— query/kill jobs from outside Pi (tmux panels). Legacybgnow [jobid]transition calls print a deprecation notice and fall back to status.
- Auto-injected completion notice (2026-08-12, replaces
[bg notice]) — jobs started with/bg/# bg:runare announced when they finish. Completion is detected event-driven (fs.watchon/tmp/pi-bg+ a 5s fallback sweep gated to relevant jobs; Linux uses polling only —FSWatcher.unref()is ineffective on Node 22) and injected into the agent's message queue viapi.sendMessage(customType: 'bg-complete',deliverAs: 'followUp'+triggerTurn: true): if the agent is idle the turn fires immediately, if user commands are queued it waits behind them. Concurrent completions are batched (1.5s debounce) into one message. The notice template marks the log as data, not instructions (prompt-injection guard). Other Pi sessions never receive notices; each job is announced once (notifiedmarker). Add# bg:quietto a command to skip its completion notice. /bglist//bgkill <id|all>— current-session background job status (⏳ running [bg],✓ done,✗ failed,💀 gone) and process-group kill (SIGTERM → 2s → SIGKILL, PGID-based with legacy-job fallback).- Notes: Every job records the owning Pi session ID and an explicit
backgroundedmarker; legacy records without these fields are ignored. The command is written to a file andeval'd in a subshell (multi-line safe;exit Nin the command still records the exit code). Rollback:git revert(legacy wrapper code removed in Phase 3) or remove the file +/reload(running jobs keep going, notices stop). Tests:tests/bg-redesign.test.mjs(PGID isolation, group kill, exit codes, multi-line cmds, auto-inject, quiet, unwrapped passthrough, no orphan tail). - 🛠
pi -pexit-hang fix (2026-08-15, applied Studio·MBP·PICS): the extension'sfs.watch+ sweep timer kept the event loop alive, so one-shotpi -pnever exited after answering. Fixed byunref()-ing the watcher and sweep timer, and — becauseFSWatcher.unref()is ineffective on Linux/Node 22 (verified 2026-08-15) — skippingfs.watchon Linux entirely and relying on the unref'd 5s polling sweep for completion detection (instant → ≤5s). macOS keeps the instantfs.watch. Note:pi -palso blocks when stdin stays open (all platforms) — pipe</dev/nullfor automation/ssh.
- 🏗 Redesigned (2026-08-20, plan) — backgrounding is now explicit at start and wrapper/tail-free: the extension
- 🔍
web_search_content.ts(OpenCode-style Raw Web Search): Port of OpenCode's built-inwebsearchtool (Exa MCPweb_search_exa). Returns RAW page content (up to 10k chars/result,livecrawl: fallbackfor fresh pages) instead of a synthesized answer — the model reads sources and answers directly with exact details. Usage split vsweb_search(pi-web-access):web_search_content= quick factual lookups needing ground truth (versions, params, errors, code);web_search= broad multi-query research / synthesized overviews;fetch_content= fetching a known URL. Compact one-line TUI rendering. UsesEXA_API_KEYenv (falls back to keyless Exa MCP free tier). - 🔄
auto_continue_compact.ts(Smart Auto-Continue on Compaction):- 2-Step Decision Gate: Automatically detects context compaction (
session_compact) and prompts the model to evaluate if output was cut off mid-task (STATUS: TRUNCATED). - Seamless Task Resume: Automatically sends a clean follow-up prompt to re-render broken tables/code blocks and resume interrupted tasks without manual intervention.
⚠️ Pending-queue guard (2026-08-20, applied Studio·MBP·PICS): If the user already queued their own follow-up message before/during compaction, the Step 1 decision prompt and Step 2 resume prompt are skipped (ctx.hasPendingMessages()check in bothsession_compactandagent_end) — the queued user message already drives the next turn, so injecting an auto prompt on top would jump ahead of (or pile onto) it.
- 2-Step Decision Gate: Automatically detects context compaction (
- 🛡️
abnormal-stop-watchdog.ts(Abnormal Stop Watchdog) — detects when the agent stops mid-task and resumes it:- Premature-stop detection: A turn that ends with
stopReason: stop, no tool calls, and a stub response (empty text, a bare checklist item like6. …, or short text without any closing summary) right after tool activity is flagged as a possible mid-task stop. - LLM verdict gate: Before acting, a cheap local judge call classifies the last response as
COMPLETE/INCOMPLETE(default endpointPI_WD_JUDGE_URL→http://127.0.0.1:8104/v1/chat/completions, modelPI_WD_JUDGE_MODEL→deepseek-v4-flash; reasoning disabled viareasoning_effort: none). - Auto-resume: On
INCOMPLETE, injects a "continue from where you stopped" user message viapi.sendUserMessage— capped atPI_WD_MAX_RESUMES(default 2) per user prompt, with duplicate-turn protection. - Guardrail: Appends a "never end mid-task" rule to the system prompt on every turn (
before_agent_start). - Observability:
/wd-statuscommand + event log at/tmp/pi-abnormal-stop-watchdog.log.
- Premature-stop detection: A turn that ends with
- 🔁
loop-guard.ts(Exact Tool-Failure Loop Guard, 2026-08-20) — prevents a deterministic agent loop before it consumes another full model turn. It records only hashes of the preceding assistant response, tool input, and tool error; when all three repeat for aneditorwritecall, it blocks the duplicate and tells the model to re-read the target or choose another strategy. A third identical attempt terminates that tool batch. Normal retries reset on a changed response/input, a successful mutation, or a new user prompt./loop-statusshows hash-only state.bashis deliberately excluded by default because transient shell failures may merit a retry; opt in withPI_LOOP_GUARD_TOOLS=edit,write,bash. - 🛑
generation-loop-watchdog.ts(Streaming Generation Loop Watchdog, 2026-08-21; deployed Studio·MBP·PICS) — watches only assistanttext_delta/thinking_deltaevents for a substantial exact token block repeated six times. It aborts the live request before a degenerate response consumes thousands of tokens, then waits foragent_settledand injects one different-strategy recovery prompt. If that recovery also loops, it stops and requires manual input instead of creating another loop. Provider deltas split inside words are handled; the MBP live-TUI validation aborted both the initial and recovery loops and queued no second recovery. Tool-call argument deltas are excluded; diagnostics contain hashes/counts only./generation-loop-statusreports state. SetPI_GENERATION_LOOP_GUARD=0for an emergency disable; thresholds can be tuned withPI_GENERATION_LOOP_{REPEATS,MIN_BLOCK_TOKENS,MAX_BLOCK_TOKENS}. - 👁️
imageread.ts(Local VLM Vision Bridge):- Why it's needed: High-performance coding models like DeepSeek V4 Flash or DeepSeek R1 are text-only models (
input: ["text"]).imagereadbridges this gap by allowing text models to inspect screenshots, charts, and image files via a local VLM endpoint. - Dynamic Tool Activation: Automatically activates when a text-only model is selected and hides itself when a native vision model (e.g. GPT-4o) is active.
- Smart High-Res Tiling & Normalized Crop: Supports
detail=low(~1MP overview),crop(normalized 0-1000 zoom-in for small text/code), anddetail=high(tiled full-resolution reading). - Configurable VLM Backend: Configured via environment variables (
IMAGEREAD_VLM_URL,IMAGEREAD_VLM_MODEL), using Qwen3.8 27B (2026-08-17~, 이전 Qwen3.6 35B A3B) served by a local VLM backend (localhost:8098via token proxy; token stats logged by the proxy). Remote hosts pointIMAGEREAD_VLM_URLat their own backend address. Auth key inIMAGEREAD_VLM_API_KEY.
- Why it's needed: High-performance coding models like DeepSeek V4 Flash or DeepSeek R1 are text-only models (
- 📝
todo.ts(Essential Task Tracker):- 3-State Task Status: Tracks task states (
pending,in_progress,completed). /todosCommand: Slash command to inspect active task lists during sessions.- Real-time Terminal UI: Live progress indicators (
X/Y completed) in the Pi terminal UI.
- 3-State Task Status: Tracks task states (
- ❓
question.ts(Interactive Prompts): Interactive multi-choice and write-in question modal UI.
Sanitized configuration templates in config/:
config/settings.json.example: Pre-configured withdefaultProvider: "deepseek",defaultThinkingLevel: "max", compaction reserves (reserveTokens: 49152,keepRecentTokens: 20000), and default packages.config/models.json.example: Complete provider schemas for direct Cloud APIs (DeepSeek Official, OpenAI) & OpenCode Go gateway.config/web-search.json.example: 2-tier search routing (Exa → Brave) template.
Clone the repository and run install.sh:
git clone https://github.com/hojin12312/custom-pi.git
cd custom-pi
./install.shNo private API keys, IP addresses, or secrets are tracked in this repository. Update settings.json, models.json, and web-search.json with your credentials after installation.
- Pi Coding Agent: Core terminal coding agent created by Mario Zechner & Earendil Works.
- pi-subagents & pi-web-access: Official subagent orchestration and web search extension packages for Pi.