The most dangerous thing an AI agent can do is act without telling you what it is about to do. Plan mode (--permission-mode plan) forces Claude to think through a task, present a structured plan, and wait for your approval before touching anything. This three-phase workflow — plan, review, execute — is the foundation of safe autonomous coding.
Step 1: Create a Plan
Pass --permission-mode plan to lock Claude into planning-only mode. All action tools (Write, Edit, Bash) are blocked. Claude can still read files, search the codebase, and reason about the task, but it cannot change anything.
Here is what happens internally: Claude receives the prompt, thinks through the task, writes a plan to ~/.claude/plans/<random-name>.md, and calls ExitPlanMode with the plan text. Because plan mode blocks that tool, the call appears in permission_denials instead of executing. Any action tools Claude attempted (like Write) are also blocked and appear in the same array.
Step 2: Read the Plan
The plan is not in result. The result field in plan mode contains a generic status message like “Permission was denied…” The actual structured plan lives in permission_denials, specifically inside the ExitPlanMode entry’s tool_input.plan field.
Two things to extract from this response. First, the plan itself:
const planText = data.permission_denials .find(d => d.tool_name === 'ExitPlanMode') ?.tool_input.plan;Second, the pending actions — what Claude wants to do once approved:
const pendingWrites = data.permission_denials .filter(d => d.tool_name === 'Write') .map(d => ({ path: d.tool_input.file_path, content: d.tool_input.content }));This gives you a preview of every file Claude intends to create or modify, before it has permission to touch anything.
Step 3: Execute
Once you have reviewed the plan, resume the session and switch the permission mode to approve execution. The --resume flag picks up the exact conversational context — Claude already has the plan in memory and does not need to re-analyze anything.
The --permission-mode bypassPermissions flag tells Claude to run every tool without asking for individual approvals. This is safe here because you already reviewed the plan and know exactly what it will do. If you want Claude to still ask before each destructive action, use --permission-mode default instead.
Modify Before Executing
The resume message is free-form text. You are not limited to “yes” or “no” — you can send any instruction and Claude will revise the plan accordingly.
Notice the --permission-mode plan on the resume. This keeps Claude in planning mode so it generates a revised plan instead of executing. You can iterate as many times as you need. When you are satisfied, resume one final time with --permission-mode bypassPermissions to execute.
What You Can Send on Resume
| Resume Message | What Happens |
|---|---|
”yes, proceed” | Executes the plan as-is |
”no, do X instead of Y” | Revises the plan with your change |
”add a step to also do Z” | Amends the plan with an additional step |
”explain step 2 more” | Stays in plan mode, elaborates on the plan |
”actually, forget it — do something else entirely” | Generates a completely new plan in the same session |
After a revision, permission_denials only contains the current plan. Previous plans are gone from the response. The session ID stays the same, and costs accumulate across every turn.
Flag vs Tool
There are two ways to enter plan mode: the --permission-mode plan CLI flag, and the EnterPlanMode tool that Claude can call on its own. They serve different use cases.
--permission-mode plan vs EnterPlanMode Tool
| Scenario | Use |
|---|---|
| Entire session should be plan-only | —permission-mode plan |
| Claude should decide when planning is needed | EnterPlanMode tool (automatic) |
| CI pipeline: plan, human review, then execute | —permission-mode plan + —resume |
| Interactive session with optional planning | Let Claude call EnterPlanMode naturally |
The flag is deterministic — you know the session will be plan-only before it starts. The tool is adaptive — Claude decides based on task complexity. For automation and CI pipelines, use the flag. For interactive workflows where you want Claude to plan only when the task is complex enough, prompt it via --append-system-prompt:
claude -p "your task" \ --append-system-prompt "When the user brings a list of changes, multiple tasks, \or a complex feature request, immediately call EnterPlanMode and research the \codebase before presenting a plan."Without either the flag or an explicit instruction, Claude does not plan automatically. It just executes.
Plans are in permission_denials, not result. The result field contains a generic status message like “Permission was denied. Please approve the write…” The actual plan text is at permission_denials[0].tool_input.plan where tool_name is “ExitPlanMode”. If you parse result in your automation, you will get a useless string instead of the plan. This also means you must use —output-format json — text mode only returns result, so the plan is invisible.
Each plan-mode turn can have multiple entries in permission_denials. The first is typically the ExitPlanMode call containing the plan text. Subsequent entries are action tools Claude attempted (Write, Edit, Bash) that were blocked. These blocked actions are a preview of what Claude will do on approval — inspect them to verify the plan matches the proposed changes. Do not assume the array has only one element.
Sonnet calls ExitPlanMode with a structured plan field. Opus may use AskUserQuestion instead, embedding the plan in a question. If you are building a parser, check for both ExitPlanMode and AskUserQuestion in permission_denials.
Resuming a plan session with the wrong —permission-mode is valid but changes behavior entirely. Resuming with bypassPermissions executes everything without approval. Resuming with plan keeps you in review. Be intentional about which mode you pass on each resume.