Plan Mode
Plan mode puts the agent into a read-only exploration state where file writes and destructive commands are blocked at the tool level. The agent can read code, search, and analyze — but cannot modify anything until a plan is reviewed and approved.
Activating Plan Mode
Section titled “Activating Plan Mode”There are two ways to enter plan mode:
| Method | Who uses it | How |
|---|---|---|
/plan slash command |
You (human) | Type /plan in the chat input (web UI or TUI) — toggles plan mode on/off |
toggle_plan_mode tool |
The agent | Agent calls toggle_plan_mode with enabled: true to enter or enabled: false to exit |
When plan mode activates, you’ll see a notification:
⏸ Plan mode ON — read-only exploration. Write/edit tools blocked.
When it deactivates:
▶ Plan mode OFF — full tool access restored.
What’s Blocked in Plan Mode
Section titled “What’s Blocked in Plan Mode”Plan mode enforces read-only access through two layers:
Tool-level blocking
Section titled “Tool-level blocking”Tool access in plan mode is an allowlist: a tool runs only if it is known to be read-only. Everything else is blocked until plan mode is exited.
Allowed:
- Built-in read tools:
read,grep,find,ls, andbash(with the command restrictions below) - Plan and interaction tools:
toggle_plan_mode,plan_mode,AskUserQuestion,update_todo - Read-only PizzaPi tools: for example
bash_output,memory_read,memory_list,list_tunnels,list_models,list_workflows,check_messages,web_search,web_fetch - Tool discovery and
codemode. Every tool call nested insidecodemodegoes through the same plan-mode check. - MCP tools that the server annotates as read-only (
annotations.readOnlyHint: truein the MCP tool listing)
Blocked:
edit,write,write_file: file modificationsubagent,spawn_session: spawn child contexts with full write access- MCP tools without
readOnlyHint: true, including tools with no annotations - Any other tool that is not known to be read-only. This includes tools that mutate state (
kill_shell,create_tunnel,memory_save,send_message,run_workflow, and so on),powershell, and tools from plugins or overlay packages.
MCP annotations come from the configured server, so PizzaPi trusts them to the same degree that it trusts the server. To allow a specific tool that you trust to be read-only, list its registered name in PIZZAPI_PLAN_MODE_ALLOWED_TOOLS. MCP tools are registered as mcp__<server>__<tool>.
PIZZAPI_PLAN_MODE_ALLOWED_TOOLS="mcp__linear__search_issues,my_overlay_lookup"The variable takes a comma-separated list and is empty by default. It cannot re-enable edit, write, write_file, subagent, or spawn_session.
Bash command restrictions
Section titled “Bash command restrictions”Bash commands go through a destructive-command detector. It normalizes each command segment before matching: paths and quotes are stripped from the executable (/usr/bin/curl, \kill, 'rm'), and it looks past leading (, {, !, if/then/do, variable assignments, and redirections. If the shell cannot know which executable runs, the segment is blocked. That covers $cmd, ${X}, $'…', globs in the command word, function definitions, case, trap, and hash.
Blocked commands:
-
Command, backtick, and process substitution, multi-line commands, and
/dev/tcp//dev/udpredirection -
Process and system control:
sudo,doas,kill,pkill,killall,reboot, mutatingsystemctl/service/launchctl,crontab,at,open/xdg-open -
Opaque code execution:
python,node,ruby,perl,php,deno,tsx, and other interpreters (except bare--version/--help);bash script.sh;eval/source;npx,bunx,uvx,bun -e,npm exec,pnpm dlx,go run,cargo run;parallel -
Network and remote mutations:
ssh,scp,rsync,nc,socat,telnet;curlwith a request body, a form, an upload, a config file, or a non-GET method;wget --post-*/--method; HTTPie writes; anyghcommand except read-only ones (list,view,status,diff,checks,search, andgh apiGET requests without fields);docker/podman/nerdctl,kubectl/oc, andhelmexcept read-only subcommands (known global options such as-n default,--context prod, ordocker compose -f file.ymlare skipped with their values; an unrecognised option written as a separate word before the subcommand is blocked, because it might take a value that hides the real subcommand; use the--option=valueform for options plan mode does not know;kubectl/ocwith--profile(other thannone),--profile-output,--log-file, or--log-diris blocked because it writes local files);npm publishand similar registry commands -
Hidden execution:
awksystem()and pipes,sedeandwcommands, andfind -exec/xargs/watchrunning a blocked command -
Dangerous inline environment variables:
PATH=…,LD_PRELOAD=…,GIT_*=…,*PAGER=…(exceptcat),NODE_OPTIONS=…, and others, whether set asVAR=x cmd,env VAR=x cmd, orexport VAR=x -
Git mutations: only read-only git subcommands are allowed (
status,log,diff,show,blame,grep,branch(listing),ls-files, etc.). Mutating operations likegit commit,git push,git checkout, andgit stash pushare blocked. -
File-mutating commands:
rm,mv,cp,mkdir,touch,chmod,ln,tee,truncate,dd,sed -i, etc. -
Package managers:
npm install,bun add,pip install,brew install, etc. -
Output redirection:
>,>>, and<>(except2>/dev/null) -
make(without-n), andfind -exec/-delete
What the agent can do
Section titled “What the agent can do”- Read any file (
readtool) - Run read-only bash commands (
ls,find,grep,rg,cat,head,wc,git log,git diff,curl -s <url>,gh pr view, etc.) - Use MCP tools annotated
readOnlyHint: true, and tools listed inPIZZAPI_PLAN_MODE_ALLOWED_TOOLS - Call
toggle_plan_modeto exit plan mode - Call
plan_modeto submit a structured plan for review
The Plan Review Flow
Section titled “The Plan Review Flow”The core workflow in plan mode follows this pattern:
-
You activate plan mode — via
/planor the agent enters it withtoggle_plan_mode. -
The agent explores — reads files, searches code, runs read-only commands to understand the problem.
-
The agent submits a plan — calls the
plan_modetool with a title, optional description, and a list of steps. -
You review the plan — a plan review card appears with the plan details and action buttons.
-
You respond — approve, suggest edits, or cancel.
-
The agent executes — if approved, plan mode exits automatically and the agent proceeds with full tool access.
The plan_mode tool
Section titled “The plan_mode tool”The agent submits a plan by calling plan_mode with these parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string |
✅ | Short title summarizing the plan |
description |
string |
No | Detailed description in markdown |
steps |
array |
No | Ordered list of steps, each with a title (required) and optional description |
The tool blocks until the user responds — the agent cannot continue until you act.
Your response options
Section titled “Your response options”When a plan is submitted, you see four actions:
| Action | What it does |
|---|---|
| Clear Context & Begin | Clears the conversation history and starts fresh execution of the plan. The agent gets a clean context with only the approved plan. |
| Begin | Approves the plan and proceeds with the current conversation context intact. |
| Suggest Edit | Opens a text input where you describe changes. The agent receives your feedback and should resubmit an updated plan. |
| Cancel | Rejects the plan entirely. The agent is told not to proceed. |
Plan Mode in the Web UI
Section titled “Plan Mode in the Web UI”In the PizzaPi web UI, the plan review appears as an interactive Plan Review panel that replaces the text input area:
- A blue-accented card shows the plan title, description, and numbered steps
- Steps auto-collapse when there are more than 5 (click to expand)
- Four action buttons are displayed in a responsive grid (2×2 on mobile, inline row on desktop)
- The Suggest Edit button reveals an inline text input — type your feedback and press Enter or click Send
- The panel is capped at 50% viewport height (60% on desktop) and scrolls internally so it never blocks the full screen
In the terminal TUI, the plan renders as an ANSI-styled box with numbered options (1–4). Type a number to choose, or type free text to submit it as an edit suggestion.
Execution Tracking
Section titled “Execution Tracking”After a plan is approved via the legacy TUI menu path, the agent enters execution mode. If the plan had steps, PizzaPi tracks progress:
- The agent marks steps complete by including
[DONE:n]tags in its responses (e.g.,[DONE:1]) - A checklist of plan steps is displayed, updating as steps are completed
- When all steps are marked done, a Plan Complete! ✓ message appears with all steps struck through
In the TUI, after the agent finishes exploring (if it hasn’t used the plan_mode tool), a menu appears:
- Execute the plan (track progress) — exits plan mode and starts execution with step tracking
- Stay in plan mode — keep exploring
- Refine the plan — opens an editor to provide feedback
Multi-Agent Plan Review
Section titled “Multi-Agent Plan Review”In multi-agent setups where a parent agent spawns child sessions, plan review integrates with the trigger system.
When a child session calls plan_mode, it fires a plan_review trigger to the parent session instead of waiting for a human response. The parent sees:
🔗 Child "my-child" submitted a plan for review:## Refactor Authentication Module
1. Extract token validation into shared utility2. Update all route handlers to use new utility3. Add unit tests for edge cases
Respond with `respond_to_trigger` using trigger ID `abc-123`.Use respond_to_trigger with action: "approve" to accept, "cancel" to reject, or "edit" with feedback.The parent agent can respond with:
respond_to_trigger(triggerId, "approve", "approve")— child proceeds with executionrespond_to_trigger(triggerId, "cancel", "cancel")— child stopsrespond_to_trigger(triggerId, "Your step 2 should also handle middleware", "edit")— child revises the planescalate_trigger(triggerId)— passes the decision to the human viewer
The toggle_plan_mode Tool
Section titled “The toggle_plan_mode Tool”Separate from plan_mode (which submits a plan for review), the toggle_plan_mode tool lets the agent enter or exit read-only exploration mode programmatically.
| Parameter | Type | Required | Description |
|---|---|---|---|
enabled |
boolean |
✅ | true to enter plan mode (read-only), false to exit (full access) |
Response examples:
"Plan mode is now ON (read-only)."— when entering"Plan mode is now OFF (full access)."— when exiting"Plan mode was already ON (read-only). No change."— if already in the requested state
If the agent exits plan mode without ever submitting a plan via plan_mode, it receives a soft nudge:
Note: you exited plan mode without submitting a plan for user review. Next time, use the plan_mode tool to present your plan before exiting.
Session Behavior
Section titled “Session Behavior”- State persists across resumes. Plan-mode state is saved in the session transcript. When the session starts, resumes, reloads, or forks (from the TUI, from
--continue, or from the web UI’s in-place switch), PizzaPi restores the latest saved state. It also reapplies the sandbox read-only overlay before any tool call can run. A session that was in plan mode stays read-only until plan mode is exited. In-progress execution steps are restored as well. - New sessions start with plan mode off. A new session has no saved state, so
/newand switching to a session that never used plan mode start with full access and clear the sandbox overlay. Plan mode from the previous session does not carry over. - Context filtering — when plan mode is off, the
[PLAN MODE ACTIVE]context messages are automatically stripped from the conversation to keep the context window clean.
Best Practices
Section titled “Best Practices”Use plan mode when:
- The task is complex and touches many files — you want to verify the approach before changes begin
- You’re working with an unfamiliar codebase and want the agent to explore first
- The change is risky or hard to reverse (database migrations, config changes, refactors)
- You’re using a multi-agent setup and want parent approval before child agents make changes
Skip plan mode when:
- The task is simple and well-defined (fix a typo, add a log line)
- You’ve already described the exact changes you want
- Speed matters more than review — you can always undo with git