# How Goose Planning Mode Creates Structured Step-by-Step Plans

> Discover how Goose planning mode transforms user requests into structured step-by-step plans using a dedicated reasoner model. Learn more about actionable plan generation.

- Repository: [Block Open Source/goose](https://github.com/block/goose)
- Tags: how-to-guide
- Published: 2026-04-05

---

**Goose planning mode converts informal user requests into machine-readable, numbered plans by using a dedicated reasoner model that analyzes available tools, renders a specialized system prompt, and classifies the output as either an actionable plan or clarifying questions.**

The planning mode in the `block/goose` repository provides a structured workflow for breaking down complex tasks before execution. By invoking the `/plan` slash command, users trigger a stateless three-stage pipeline that generates a step-by-step roadmap referencing available tools, which can then be executed automatically or refined through further conversation.

## The Three-Stage Planning Pipeline

### Stage 1: Initiating Plan Mode

When a user types `/plan` followed by an optional prompt, the CLI immediately transitions into planning mode. In [`crates/goose-cli/src/session/mod.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/session/mod.rs) at lines 63-71, the `Session::handle_plan_mode` method sets `self.run_mode = RunMode::Plan` and prints a UI banner via `output::render_enter_plan_mode`.

This stage establishes a clean context for planning. The optional text following the `/plan` command is preserved as the initial user message, while the session prepares to invoke a reasoner model rather than the standard execution agent.

### Stage 2: Generating the Structured Plan

The core planning logic resides in the agent layer. The `Agent::get_plan_prompt` method defined in [`crates/goose/src/agents/agent.rs`](https://github.com/block/goose/blob/main/crates/goose/src/agents/agent.rs) (lines 8-30) orchestrates the collection of tool metadata and prompt rendering.

First, `ExtensionManager::get_prefixed_tools` retrieves a list of `ToolInfo` structs containing each tool’s **name**, **description**, and **parameter names**. This metadata is passed to `ExtensionManager::get_planning_prompt` in [`crates/goose/src/agents/extension_manager.rs`](https://github.com/block/goose/blob/main/crates/goose/src/agents/extension_manager.rs) at lines 73-79, which constructs a `HashMap` with a single entry `"tools"` mapped to the JSON-encoded tool list.

The template engine then renders the system prompt. In [`crates/goose/src/prompt_template.rs`](https://github.com/block/goose/blob/main/crates/goose/src/prompt_template.rs) at lines 85-111, the `render_template` function loads the built-in [`plan.md`](https://github.com/block/goose/blob/main/plan.md) template and interpolates the tool context. The resulting string becomes the system prompt for the reasoner model.

Finally, `Session::plan_with_reasoner_model` invokes the selected Provider’s `complete` method with the rendered prompt and user messages. The model returns a single `Message` containing either a numbered plan or a bullet list of clarifying questions.

### Stage 3: Classifying and Executing the Response

Once the reasoner generates output, the system must determine whether the user can act immediately or needs to provide more information. In [`crates/goose-cli/src/session/mod.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/session/mod.rs) at lines 200-232, the `classify_planner_response` function sends the raw model text to a lightweight classifier LLM with a prompt asking it to categorize the output as either **"plan"** or **"clarifying questions"**.

The classifier returns a case-insensitive string that maps to `PlannerResponseType::Plan` or `PlannerResponseType::ClarifyingQuestions`.

**If classified as a plan:**

The CLI prompts the user with `cliclack::confirm` asking whether to clear history and act on the plan. Upon confirmation:

- The current `self.messages` buffer is cleared
- The generated plan is inserted as a **user** message using `Message::user().with_text(plan)`
- `process_agent_response` is invoked in normal run mode, causing the execution AI to follow the plan step-by-step

If the user declines, the plan is appended as an **assistant** message, allowing iterative refinement.

**If classified as clarifying questions:**

The question list is appended as an assistant message, pausing the workflow until the user provides answers.

## Practical Usage Examples

### Interactive CLI Workflow

Trigger planning mode by typing the slash command followed by your objective:

```text
> /plan Summarize the latest 5 GitHub issues and email the summary.

```

The terminal displays:

```

Entering plan mode. You can provide instructions to create a plan...

```

After the model generates the plan, Goose asks:

```

Do you want to clear message history & act on this plan? (Y/n)

```

Selecting **Y** clears the conversation history, injects the plan as a user message, and begins execution immediately. Selecting **n** preserves the plan as an assistant message for editing.

### Programmatic Implementation in Rust

You can invoke the planning pipeline programmatically using the internal APIs:

```rust
use goose::agents::Agent;
use goose::providers::Provider;
use anyhow::Result;

async fn create_plan(agent: &Agent, session_id: &str) -> Result<String> {
    // Retrieve the planning prompt with tool context
    let plan_prompt = agent.get_plan_prompt(session_id).await?;
    
    // Initialize the reasoner provider
    let provider: Arc<dyn Provider> = agent.provider().await?;
    let model_cfg = provider.get_model_config();
    
    // Generate the plan
    let (resp, _) = provider.complete(
        &model_cfg,
        session_id,
        &plan_prompt,
        &[goose::Message::user().with_text("Create a deployment script.")],
        &[],
    ).await?;
    
    // Classify the response type
    let resp_type = goose_cli::session::classify_planner_response(
        session_id,
        resp.as_concat_text(),
        provider.clone(),
    ).await?;
    
    // Return formatted results
    Ok(match resp_type {
        goose_cli::session::PlannerResponseType::Plan => resp.as_concat_text(),
        goose_cli::session::PlannerResponseType::ClarifyingQuestions => {
            format!("Need clarification:\n{}", resp.as_concat_text())
        }
    })
}

```

This mirrors the internal workflow: fetch prompt → invoke reasoner → classify output.

### Customizing the Plan Template

The default system prompt lives in [`crates/goose/src/prompts/plan.md`](https://github.com/block/goose/blob/main/crates/goose/src/prompts/plan.md). To override it, create a user-specific version:

```bash
mkdir -p ~/.config/goose/prompts
cat > ~/.config/goose/prompts/plan.md <<'EOF'
You are a planner. Generate a JSON array where each element is:
{ "step": <number>, "instruction": "<text>" }
Include any required tool calls as `"tool": "<tool_name>"`.
EOF

```

Goose automatically loads the user override instead of the built-in template when `prompt_template::render_template` is called.

## Summary

- **Goose planning mode** uses a three-stage pipeline to transform informal requests into structured, numbered plans.
- The workflow begins with the `/plan` slash command, handled in [`crates/goose-cli/src/session/mod.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/session/mod.rs), which sets the session to `RunMode::Plan`.
- **Tool metadata** is collected via `Agent::get_plan_prompt` and serialized as JSON for the reasoner model.
- The **plan.md** template in [`crates/goose/src/prompt_template.rs`](https://github.com/block/goose/blob/main/crates/goose/src/prompt_template.rs) receives the tool list and generates the system prompt.
- A **classifier LLM** in `classify_planner_response` distinguishes between executable plans and clarifying questions.
- Confirmed plans are injected as user messages and executed via `process_agent_response`, while questions are appended as assistant messages for user response.

## Frequently Asked Questions

### What file handles the initial `/plan` command parsing?

The slash command is processed in [`crates/goose-cli/src/session/mod.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/session/mod.rs) at lines 63-71, where `Session::handle_plan_mode` sets the run mode and renders the entry banner.

### How does Goose know which tools are available during planning?

The `ExtensionManager::get_prefixed_tools` method collects `ToolInfo` structs containing tool names, descriptions, and parameters. This list is JSON-encoded and passed to the [`plan.md`](https://github.com/block/goose/blob/main/plan.md) template via `ExtensionManager::get_planning_prompt` in [`crates/goose/src/agents/extension_manager.rs`](https://github.com/block/goose/blob/main/crates/goose/src/agents/extension_manager.rs).

### Can I customize the planning system prompt?

Yes. Create a file at `~/.config/goose/prompts/plan.md` to override the built-in template. The `prompt_template::render_template` function automatically prefers user-defined templates over the defaults packaged in [`crates/goose/src/prompts/plan.md`](https://github.com/block/goose/blob/main/crates/goose/src/prompts/plan.md).

### What happens if the model returns questions instead of a plan?

The `classify_planner_response` function in [`crates/goose-cli/src/session/mod.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/session/mod.rs) (lines 200-232) detects clarifying questions using a secondary LLM classifier. These questions are appended as an assistant message, allowing the user to provide answers before regenerating the plan.