Introduce a "Plan Mode" to plan complex code changes by blocking the use of write tools
What would you like to be added?
One of the absolute best features of Claude Code is "Plan Mode", which can be enabled by pressing Shift+Tab twice (you can see it in the bottom-left corner, under the textbox field):
bishop-py3.12➜ ava git:(master) claude
╭───────────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code! │
│ │
│ /help for help, /status for your current setup │
│ │
│ cwd: /Users/vzegna/repos/ava │
╰───────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > Try "create a util logging.py that..." │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
⏸ plan mode on (shift+tab to cycle) ⧉ In claude-context.md
When in this mode, Claude Code receives specific instructions that steer it into thinking hard about a problem and making a comprehensive implementation plan to present to the user before jumping in the actual implementation.
The following is injected in the context after each message during Plan Mode, to steer Claude's behavior:
Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received (for example, to make edits). Instead, you should:
1. Answer the user's query comprehensively
2. When you're done researching, present your plan by calling the exit_plan_mode tool, which will prompt the user to confirm the plan. Do NOT make any file changes or run any tools that modify the system state in any way until the user has confirmed the plan.
This is an example:
[System Instructions]
↓
[Tool Definitions]
↓
[Base Claude Code Instructions]
↓
[Environment Info]
↓
<system-reminder> ← First injection point
[CLAUDE.md content]
</system-reminder>
↓
[User Message]
↓
<system-reminder> ← Second injection point
Plan mode is active...
</system-reminder>
↓
[Assistant Response]
↓
[After any tool use like Read]
↓
<system-reminder> ← Third injection point
Plan mode is active...
</system-reminder>
In Plan Mode, every tool that can create, update or delete files is blocked, so Claude can only use Read tools and WebFetch/WebSearch.
Morever, a dedicated "exit_plan_mode" tool is used, which works as follows:
exit_plan_mode Tool
Description: Use this tool when you are in plan mode and have finished presenting your plan and are ready to code. This will prompt the user to exit plan mode. IMPORTANT: Only use this tool when the task requires planning the implementation steps of a task that requires writing code. For research tasks where you're gathering information, searching files, reading files or in general trying to understand the codebase - do NOT use this tool.
Eg.
- Initial task: "Search for and understand the implementation of vim mode in the codebase" - Do not use the exit plan mode tool because you are not planning the implementation steps of a task.
- Initial task: "Help me implement yank mode for vim" - Use the exit plan mode tool after you have finished planning the implementation steps of the task.
When the user accepts a plan, the tool starts working on it. This is an example of a plan that Claude made for me when I asked: How can I develop a local MCP tool for you to use?
│ Ready to code? │
│ │
│ Here is Claude's plan: │
│ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
│ │ How to Develop a Local MCP Tool in Python for Claude Code │ │
│ │ │ │
│ │ 1. Setup Project Structure │ │
│ │ │ │
│ │ my-mcp-tool/ │ │
│ │ ├── __main__.py # Entry point for the MCP server │ │
│ │ ├── server.py # Your MCP server implementation │ │
│ │ ├── pyproject.toml # Project configuration │ │
│ │ └── README.md # Documentation │ │
│ │ │ │
│ │ 2. Install Dependencies │ │
│ │ │ │
│ │ # Using pip │ │
│ │ pip install mcp[cli] │ │
│ │ │ │
│ │ # Or using uv (recommended) │ │
│ │ uv add mcp[cli] │ │
│ │ │ │
│ │ 3. Create Your MCP Server (server.py) │ │
│ │ │ │
│ │ from mcp.server.fastmcp import FastMCP │ │
│ │ from typing import Optional, Dict, Any │ │
│ │ │ │
│ │ # Initialize your MCP server │ │
│ │ mcp = FastMCP("my-tool") │ │
│ │ │ │
│ │ # Example 1: Simple tool │ │
│ │ @mcp.tool() │ │
│ │ def calculate_sum(a: int, b: int) -> int: │ │
│ │ """Add two numbers together""" │ │
│ │ return a + b │ │
│ │ │ │
│ │ # Example 2: Tool with complex logic │ │
│ │ @mcp.tool() │ │
│ │ def process_text(text: str, operation: str = "uppercase") -> str: │ │
│ │ """Process text with various operations""" │ │
│ │ if operation == "uppercase": │ │
│ │ return text.upper() │ │
│ │ elif operation == "lowercase": │ │
│ │ return text.lower() │ │
│ │ elif operation == "reverse": │ │
│ │ return text[::-1] │ │
│ │ else: │ │
│ │ return text │ │
│ │ │ │
│ │ # Example 3: Async tool for external APIs │ │
│ │ @mcp.tool() │ │
│ │ async def fetch_data(url: str) -> Dict[str, Any]: │ │
│ │ """Fetch data from an external source""" │ │
│ │ import httpx │ │
│ │ async with httpx.AsyncClient() as client: │ │
│ │ response = await client.get(url) │ │
│ │ return response.json() │ │
│ │ │ │
│ │ # Example 4: Resource endpoint │ │
│ │ @mcp.resource("config://{key}") │ │
│ │ def get_config(key: str) -> str: │ │
│ │ """Get configuration values""" │ │
│ │ configs = { │ │
│ │ "api_version": "1.0.0", │ │
│ │ "max_retries": "3", │ │
│ │ "timeout": "30" │ │
│ │ } │ │
│ │ return configs.get(key, "Not found") │ │
│ │ │ │
│ │ # Example 5: Context manager for stateful operations │ │
│ │ @mcp.context() │ │
│ │ def database_connection(): │ │
│ │ """Provide database context""" │ │
│ │ # Setup │ │
│ │ conn = {"status": "connected"} │ │
│ │ yield conn │ │
│ │ # Teardown │ │
│ │ conn["status"] = "disconnected" │ │
│ │ │ │
│ │ 4. Create Entry Point (main.py) │ │
│ │ │ │
│ │ #!/usr/bin/env python3 │ │
│ │ from server import mcp │ │
│ │ │ │
│ │ if __name__ == "__main__": │ │
│ │ # Run the MCP server using stdio transport │ │
│ │ mcp.run() │ │
│ │ │ │
│ │ 5. Configure Project (pyproject.toml) │ │
│ │ │ │
│ │ [project] │ │
│ │ name = "my-mcp-tool" │ │
│ │ version = "1.0.0" │ │
│ │ description = "My custom MCP tool for Claude Code" │ │
│ │ requires-python = ">=3.10" │ │
│ │ dependencies = [ │ │
│ │ "mcp[cli]", │ │
│ │ "httpx", # if using async HTTP requests │ │
│ │ ] │ │
│ │ │ │
│ │ [project.scripts] │ │
│ │ my-mcp-tool = "my_mcp_tool.__main__:main" │ │
│ │ │ │
│ │ 6. Make It Executable │ │
│ │ │ │
│ │ # Make the script executable │ │
│ │ chmod +x __main__.py │ │
│ │ │ │
│ │ # Test locally │ │
│ │ python __main__.py │ │
│ │ │ │
│ │ 7. Add to Claude Code │ │
│ │ │ │
│ │ # Add using absolute path │ │
│ │ claude mcp add my-tool /path/to/my-mcp-tool/__main__.py │ │
│ │ │ │
│ │ # Or if installed as package │ │
│ │ claude mcp add my-tool python -m my_mcp_tool │ │
│ │ │ │
│ │ # With configuration parameters │ │
│ │ claude mcp add my-tool python /path/to/__main__.py --config-file /path/to/config.json │ │
│ │ │ │
│ │ 8. Advanced Features │ │
│ │ │ │
│ │ Environment Variables: │ │
│ │ import os │ │
│ │ │ │
│ │ @mcp.tool() │ │
│ │ def get_env_info(var_name: str) -> Optional[str]: │ │
│ │ """Get environment variable value""" │ │
│ │ return os.environ.get(var_name) │ │
│ │ │ │
│ │ State Management: │ │
│ │ class ToolState: │ │
│ │ def __init__(self): │ │
│ │ self.cache = {} │ │
│ │ self.counter = 0 │ │
│ │ │ │
│ │ state = ToolState() │ │
│ │ │ │
│ │ @mcp.tool() │ │
│ │ def increment_counter() -> int: │ │
│ │ """Increment and return counter""" │ │
│ │ state.counter += 1 │ │
│ │ return state.counter │ │
│ │ │ │
│ │ Error Handling: │ │
│ │ @mcp.tool() │ │
│ │ def safe_operation(input_data: str) -> Dict[str, Any]: │ │
│ │ """Perform operation with error handling""" │ │
│ │ try: │ │
│ │ # Your logic here │ │
│ │ result = process(input_data) │ │
│ │ return {"success": True, "data": result} │ │
│ │ except Exception as e: │ │
│ │ return {"success": False, "error": str(e)} │ │
│ │ │ │
│ │ 9. Testing Your MCP Server │ │
│ │ │ │
│ │ # test_server.py │ │
│ │ import pytest │ │
│ │ from server import calculate_sum, process_text │ │
│ │ │ │
│ │ def test_calculate_sum(): │ │
│ │ assert calculate_sum(2, 3) == 5 │ │
│ │ │ │
│ │ def test_process_text(): │ │
│ │ assert process_text("hello", "uppercase") == "HELLO" │ │
│ │ │ │
│ │ 10. Best Practices │ │
│ │ │ │
│ │ - Use type hints for all parameters and returns │ │
│ │ - Provide clear docstrings for each tool │ │
│ │ - Handle errors gracefully │ │
│ │ - Use async for I/O operations │ │
│ │ - Keep tools focused on single responsibilities │ │
│ │ - Log important operations for debugging │ │
│ │ │ │
│ │ Once implemented, Claude Code will be able to use your tools by referencing them with the mcp__ prefix! │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ Would you like to proceed? │
│ │
│ ❯ 1. Yes, and auto-accept edits │
│ 2. Yes, and manually approve edits │
│ 3. No, keep planning │
│
Note that NO files changes were made, Claude Code only presented a comprehensive plan with all the changes it plans to make, showing them to me and explaining them. I can then accept the plan with auto-edits, accept with manual edits, and decline the plan, asking follow up question or telling what to do differently; it will revise the plan based on my feedback and propose a new plan. This process can keep going indefinitely until I approve the plan.
Why is this needed?
This feature is incredibly useful because it helps define a comprehensive and revised plan on the fly, without the need to write requirement files, without having to give the model custom instructions like "make a plan but don't start writing code yet" every single time a plan is needed, and it blocks the write tools so the model cannot use them not even by mistake.
I use Plan Mode literally all the times, because it forces the model to think very hard through a problem and come up with an actual plan, rather than starting to implement changes that can break things. Plus, I can review and modify the plan before the model starts making changes, so I am always in the loop.
The results are always much better than without using Plan Mode.
Additional context
No response
See: https://github.com/eyaltoledano/claude-task-master, https://github.com/Manamama/servers_forked/tree/main/src/sequentialthinking ...
This is a separate MCP tool, it doesn't replace an integrated Plan Mode.
Absolutely essential feature, thanks for the detailed initiative.
for some reason its not working by me shift+tab . i use windows and i tried it ob vscode and on seprate terminal . but not working .
@SaidThaher this feature doesn't exist yet, it's a suggestion. Currently shift+tab only toggles auto-accept edits (no visual feedback).
I would love to implement this and plan to open a PR soon. Since this is a pretty core feature, I'm writing this comment in case an external PR like mine conflicts with internal roadmaps.
It is already possible with extensions + extension commands.
A Plan Mode is so fundamentally important that it must be an integrated feature of the CLI, not an optional add-on.
You can use extensions + extension commands and Philipp Schmid has already provided prompts for planing and coding prompts. So what's the problem? You should be able to write your own planing prompts by yourself.
BTW, extension commands are only available since version 0.1.16.
A "plan mode" is different from a "planning prompt" because it allows you to send multiple requests having the peace of mind that you are still in plan mode, and to me that is the most efficient way to iterate on and refine a plan before it gets implemented. Anything different than that will always feel like an after thought. I think Gemini CLI is great and it has the potential to become better than Claude Code but it needs to support features that represent the reason why Claude Code is in the lead at the moment, and plan mode is one of those features.
Since this discussion is still hot I'm going to plug my PR and implementation outline again #5345 to see if I can get some community support ty
The 1st PR lays the foundation while enhancing current planning The 2nd PR (the outline is in #5345 ) would implement a robust toggleable Plan mode a la Claude Code, allowing the user to iterate on the plan until the mode is exited
What do you all think?
For me this is the biggest thing stopping me frm usng gemini-cli over Claude-code (even tho 2.5 pro is arguably a higher IQ in sme respects than sonnet). I just can't take the risk with random execution by gemini. Plan mode is solely needed.
A "plan mode" is different from a "planning prompt" because it allows you to send multiple requests having the peace of mind that you are still in plan mode, and to me that is the most efficient way to iterate on and refine a plan before it gets implemented.
I’m not entirely sure what additional functionality Plan Mode provides beyond using a system prompt that defines general, iterative steps for creating PR prompts across multiple requests. A custom prompt could already describe the specific software, and tool calling seems like it could be integrated in either case. Given that, I’m not clear on what unique value Plan Mode adds. Could you clarify how it differs from the system prompt approach?
A "plan mode" is different from a "planning prompt" because it allows you to send multiple requests having the peace of mind that you are still in plan mode, and to me that is the most efficient way to iterate on and refine a plan before it gets implemented.
I’m not sure I fully understand what Plan Mode provides beyond a system prompt that already defines general, iterative steps for creating PR prompts. A custom prompt could also be used to describe the specific software, and tool calling could be integrated into either approach. Given that, I’m not certain what additional functionality Plan Mode introduces. Could you clarify how it differs or what unique value it adds?
Just chiming in here:
- Planning Prompts Planning prompts, as I'm sure we both agree, has been relatively proven to improve an LLM's ability to complete tasks of increasing complexity. You could say that planning prompts scaffold for architectural weaknesses of LLMs that will likely not be completely solved within a few model generations; it's likely that planning prompts will still be useful for GPT-6, Gemini 3, and so on.
In general, having an inherent planning prompt is an extremely elegant addition to an AI application like this one -- there is already a necessity for built in prompts, and planning has been shown throughout the industry (and in research) to be a net positive for all LLM based assistants.
This is why a planning prompt is already inherently built in to the Gemini CLI, as it is for all robust AI code assistants.
-
Planning Tools While planning prompts enhance the outcomes of tasks performed by LLMs, there is another elegant addition beyond that. For complicated plans, having the plan exist outside of the LLM's context reduces the impact of occurrences of hallucinations about the plan itself. Using the environment as a source of truth has been the industry standard for LLM assistants for a long while, and this was notably cemented in Anthropic's famous Building Effective Agents.
-
Plan Mode The LLM's knowledge of whether it is in Plan Mode, paired with deterministic (AND prompt engineered) guardrails for which tools it can use sustain a user experience of iterating on the plan until it is satisfactory. Since the plan is held on disk and accessed through tools, the user can have some assurance that once a worthy plan is created, it will be followed.
None of this directly answers your question because it is written to illustrate the complimentary nature of prompt planning, planning tools, and plan mode within LLM based assistants.
Why not just let people use extensions?
If an application layer technique is this inherently complimentary to the architecture and performance of an LLM, considered an industry standard across all other popular LLM based coding assistants, and in very high demand from the user base, then the entire point of having application layer code is to include those techniques. The purpose of extensions is to extend beyond that.
This is my opinion
A "plan mode" is different from a "planning prompt" because it allows you to send multiple requests having the peace of mind that you are still in plan mode, and to me that is the most efficient way to iterate on and refine a plan before it gets implemented.
I’m not entirely sure what additional functionality Plan Mode provides beyond using a system prompt that defines general, iterative steps for creating PR prompts across multiple requests. A custom prompt could already describe the specific software, and tool calling seems like it could be integrated in either case. Given that, I’m not clear on what unique value Plan Mode adds. Could you clarify how it differs from the system prompt approach?
Imagine the scenario below using a planning prompt or a custom command for planning instead...
(sorry for the capital letters below...)
I want to implement feature A. SWITCH TO PLAN MODE. I explain to the agent what I want. I see the plan. I want to make changes to the plan. Actually, we iterate several times to arrive at the plan I like. SWITCH TO IMPLEMENTATION MODE. I test it. I find an issue. We are still in the same chat session with all the relevant context. SWITCH TO PLAN MODE. I provide some extra context to the agent about the issue and ask for a fix. The agent presents the plan. We again iterate on that plan together. SWITCH TO IMPLEMENTATION MODE. I test it again. It works. I'm happy. I can clear the context and move on to the next feature.
Sorry for the capital letters, but I just I wanted to make it clear where we would switch the modes. Also, I used conceptual names for modes just to illustrate.
It can definitely be implemented with custom prompts. But you have a productivity boost when switching a mode is just at a keyboard shortcut away. It is like going through different types of iterations within a single session. It is mutually exclusive to being in YOLO mode for instance, so that would be just one of the modes available. Claude Code actually has 4 modes: the default (where any step needs to confirmed by the user), changes allowed (this the default mode after planning), plan mode and YOLO mode.
But just one step back: planning is necessary because the AI agents more often than not go off a tangent (more often than not due to our bad context engineering), so it is a very good way to make sure we are always going in the right direction. So, if we all agree that planning is essential and it should happen often, then an AI agent that has that built-in will always have an edge.
But this is the type of thing that might also be personal preference. But regardless, if you have not had the chance to try plan mode in Claude Code, please try. Even though the end result might be the same using a custom prompt vs plan mode, I believe that plan mode is a more efficient way to get the result you want. But that's my personal opinion... 😊
Ok, maybe the answer to this, and to make everyone happy, is to have a feature that GitHub Copilot already has: custom chat modes. That way, users can create any modes they want, including "plan mode". Examples of modes created by the community: https://github.com/github/awesome-copilot?tab=readme-ov-file#-custom-chat-modes
But I'm not sure how well that is implemented in GitHub Copilot as I have not used it yet. I just wanted to share the concept with the group.
Ok, maybe the answer to this, and to make everyone happy, is to have a feature that GitHub Copilot already has: custom chat modes. That way, users can create any modes they want, including "plan mode". Examples of modes created by the community: https://github.com/github/awesome-copilot?tab=readme-ov-file#-custom-chat-modes
But I'm not sure how well that is implemented in GitHub Copilot as I have not used it yet. I just wanted to share the concept with the group.
The prompts I’m using (Markdown files passed either as system prompts or as extensions) are quite similar to those used in Copilot’s chat modes. I’m not entirely sure whether PR #5345 introduces a new feature or if it primarily adds logic for managing these prompts in a more convenient way.
Ok, maybe the answer to this, and to make everyone happy, is to have a feature that GitHub Copilot already has: custom chat modes. That way, users can create any modes they want, including "plan mode". Examples of modes created by the community: https://github.com/github/awesome-copilot?tab=readme-ov-file#-custom-chat-modes But I'm not sure how well that is implemented in GitHub Copilot as I have not used it yet. I just wanted to share the concept with the group.
The prompts I’m using (Markdown files passed either as system prompts or as extensions) are quite similar to those used in Copilot’s chat modes. I’m not entirely sure whether PR #5345 introduces a new feature or if it primarily adds logic for managing these prompts in a more convenient way.
#5345 introduces tools so session plans from all prompts are maintained on disk as per industry standard to reduce hallucinations for complex plans. This is a foundational step in implementing a plan mode, but it is also a net positive on its own. It would yield no regressions to your extension based workflows and only improve performance for complex plans.
I'm not a developer but I just wanted to chime in. I found some success by adding the following line to the end of any prompt of which I don't want Gemini to get into instant editing mode. He then gives me a report of the intended changes then asks for my approval .I know It doesn't compare to the plan mode in Claude Code but at least you can have some clarity on what he's about to do before approving it.
"Before making any changes please confirm your understanding of my requests So I can approve them"
For me this will suffice until the real plan mode is created
Any timeline around this?
Can we please have the option to choose model for plan mode. even if sonnet is good I would love to have gemini In plan mode.
+1 to having native support for plan mode. Other TUI's like Claude Code do this quite well! Would be awesome for gemini-cli to support it, similarly.
Just pointing out — there’s already an early PR #1742 for Plan Mode.
@ManiaciaChao its closed already, unfortunately :(
Plan mode appeared! But I think it works only with pro model.
@P-rgb-max what do you mean appeared? There's no integrated planning mode in gemini-cli (yet)
I continued work on PR to integrate Planning mode into gemeni-cli as part of #13366
Plan mode should also integrate nicely with the existing TODO support. Currently I find it quite hard to trigger generating TODOs unless I explicitly ask for it. In plan mode you should always end up with a list of TODOs at the end of creating the plan and the TODOs UI should potentially auto-expand on completion of planning.