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

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 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 (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 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 at lines 85-111, the render_template function loads the built-in 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 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:

> /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:

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. To override it, create a user-specific version:

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, 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 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 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 template via ExtensionManager::get_planning_prompt in 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.

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

The classify_planner_response function in 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →