plan — Think Before You Edit
TL;DR
/plan's value isn't one more command — it's a regime of "research before building": writing tools temporarily confiscated, plan visible/approvable, execution progress tracked.
Overview
Letting an agent edit directly fails not because it "can't write code", but because it acts too soon (editing before mapping the repo), loses scope (a small ask balloons into half a refactor), hides progress, and loses state on compaction. Plan Mode solves a workflow problem: read-only exploration → an approvable numbered plan → full-tool execution after approval → progress via [DONE:n].
How it works
Two modes, two tool boundaries: Plan mode keeps only read/bash(limited)/grep/find/ls, disabling edit/write — not via prompt pleading but by actually removing write tools from active tools. Execution mode restores all tools.
bash runs a whitelist + shell lexer (default-deny, rule-based allow): split on ;|&&, reject redirects/subshells/command-substitution/var-expansion/glob outright; read-only commands pass, git/find/sed/npm get dedicated arg validators. Shifting from "block known-bad" to "allow known-good" collapses the bypass surface to the whitelist itself.
Plan and questions go through structured tools plan_complete/plan_question, taken directly from tool params (regex Plan: as fallback). State survives compaction via appendEntry, the plan lands in PLAN.md; instructions go through systemPrompt rebuilt every turn, leaving no plan artifacts in message history.
Usage
Install
pi install npm:@piex-dev/plan
Source: extensions/plan
Configuration
plan has extra peer deps: @earendil-works/pi-tui, @earendil-works/pi-agent-core, @earendil-works/pi-ai (TUI integration), unlike other extensions that only need pi-coding-agent.
Usage
/plan
→ read-only exploration (plan_question clarifies key decisions)
→ plan_complete submits the full plan (with Plan: numbered steps)
→ UI offers Execute / Stay / Refine
→ Execute: opens all tools, step by step
→ [DONE:n] advances
→ all done → Plan Complete
Aux commands: /todos toggles the todo widget; CLI flag plan starts in plan mode.
Verify
pi -e ./extensions/plan/src/plan.ts -p "what is 1+1" --no-session
Implementation
A ~1430-line single file. One of the packages that "uses the Extension API most fully" in piex: registerCommand/registerTool/setActiveTools/on("tool_call") blocking/on("before_agent_start") systemPrompt injection/on("turn_end") [DONE:n] parsing/appendEntry persistence/ctx.ui.select interaction.
Tool-set merge: entering plan remembers toolsBeforePlanMode; the plan tool set = (prior active minus edit/write) ∪ read-only set; on exit it restores while keeping other extensions' tools, coexisting with hashline/lsp/dap without collateral kill.
| omp | piex plan |
|---|---|
| fullscreen plan-review overlay | select three-option interaction |
| Plan TOC sidebar | none (pi API limit) |
| sub-agent plan handoff | none (awaiting subagent) |
| write disabled | same |
Lightness pays off: small dependency surface, predictable behavior, same-source as upstream pi example, easy to maintain.
Design notes
| Project | Mechanism | piex choice |
|---|---|---|
| pi official plan-mode example | registerCommand("plan") + setActiveTools + tool_call bash interception | Adopted core API approach. Enhanced: appendEntry persistence + PLAN.md + UI widget |
| oh-my-pi plan | fullscreen plan-review overlay + TOC sidebar + sub-agent handoff | Not adopted: pi API has no fullscreen overlay, sub-agent not ready. Borrowed: step approval, configurable tool whitelist, /review integration |
| pi-extensions pi-plan-mode | shell-lexer bash whitelist, structured tools, systemPrompt injection, context artifact cleanup | Adopted all four; not adopted: settings file & legacy migration, fixed thinking level, dropping progress tracking |
Core trade-off: stay on the pi-example-same-source route (light, predictable, maintainable), drop omp-style heavy UI; use appendEntry against compaction.
Changelog
Roadmap
| Direction | Plan |
|---|---|
[DONE:n] relies on honesty | weak verify: if a step's named files are untouched in the working tree, warn "marked DONE but no change" |
| Refine is shallow | strengthen approval within pi API limits, not a fullscreen overlay |
| Whitelist ≠ sandbox | configurable whitelist (allow lsp in plan phase, tighten bash); document "risk reduction ≠ isolation" |
| Single-session sequential | first link with /review (plan→exec→review); parallel execution awaits sub-agent |
Version history
| Version | Date | Changes |
|---|---|---|
| 0.1.1 | 2026-07-19 | regex-blacklist bash interception; in-body Plan: regex extraction; context message injection |
| 0.2.0 | 2026-07-21 | shell-lexer whitelist; plan_complete/plan_question structured tools; systemPrompt injection (default-deny rule-based; plan/questions via dedicated tool params; instructions rebuilt per turn, no plan artifacts in history) |
0.1.1 lesson: regex blacklists miss equivalent bypasses; in-body regex extraction drops plans when the model doesn't follow format; context message injection gets eaten by compaction. 0.2.0 switches to structured: whitelist collapses bypass surface, tool params replace body parsing, systemPrompt replaces message injection.
Appendix: pi plan ecosystem capability comparison
Objective capability differences across four projects (omp / pi official example / pi-extensions pi-plan-mode / piex plan).
| Capability | omp | pi example | pi-extensions | piex |
|---|---|---|---|---|
| Dangerous bash interception | ✅ regex | ✅ | ✅ lexer whitelist | ✅ lexer whitelist (ported) |
| Structured plan/question tools | ❌ | ❌ | ✅ | ✅ |
| Instruction injection | context msg | context msg | systemPrompt | systemPrompt |
| Progress tracking | ✅ [DONE:n] | ✅ | ❌ disabled | ✅ [DONE:n] + widget |
| Plan approval popup | ✅ fullscreen overlay | ❌ | ✅ select | ❌ select instead |
| Sub-agent plan handoff | ✅ plan-handoff | ❌ | ❌ | ❌ awaits subagent |
| Compaction protection | ✅ plan file | ❌ | ✅ appendEntry | ✅ appendEntry |
Source Markdown: docs/packages/plan.md