Plan Tool
Shared persistent scratchpad for multi-agent collaboration.
Overview
The plan tool gives agents a shared, persistent scratchpad of named documents. Any agent in a multi-agent config that loads the plan toolset can read and write the same plans, and those plans survive across sessions. This makes it straightforward to wire a planner agent that sketches work and one or more executor agents that consume it without any custom tool wiring.
Plans are stored as JSON files in the Docker Agent data directory (~/.cagent/plans/ by default). Agents that share a process serialize on a single mutex, and every write or delete additionally holds an advisory lock on a sentinel file in the plans directory, so writers in separate Docker Agent processes are serialized too: concurrent edits can never silently overwrite each other, and a stale revision always fails with a deterministic version conflict. Writes are atomic (temp file + rename), so a reader never observes partial content.
Configuration
toolsets:
- type: planNo additional options are required. All agents that include type: plan in their toolsets share the same plans.
Available Tools
| Tool | Description |
|---|---|
write_plan | Create or update a shared plan by name. Replaces the entire plan content — read it first to preserve what you want to keep. Each write bumps the revision number. |
read_plan | Read a shared plan by name, including its title, content, author, status, revision number, and last-updated timestamp. |
list_plans | List all shared plans with their name, title, author, status, revision, and last-updated timestamp. |
delete_plan | Delete a shared plan by name. |
update_plan_from_file | Create or update a plan, taking the new content from a file on disk instead of inline. Use it with export_plan_to_file to edit a large plan without re-sending its whole body. |
export_plan_to_file | Write a plan's content to a file. The content goes to disk and is not returned as tool output, so materialising a plan costs no tokens. |
set_plan_status | Set a plan's free-form status without rewriting its body. The plan must already exist. |
get_plan_status | Read a plan's status and current revision without fetching its body. |
Cheap edits with file-based revisions
Re-sending a whole plan on every revision is expensive. The file-based tools let an agent edit a plan without paying input-token cost for its body:
export_plan_to_filewrites the current plan content to a path. The content is written to disk and is not returned.- The agent edits that file in place with its filesystem tools.
update_plan_from_filecommits the file's new contents as the next revision.
Free-form status
Each plan carries a free-form status string. There is no fixed vocabulary:
define your own in the system prompt (e.g. idle, in-progress, blocked,
done, canceled). Read and write it independently of the body with
get_plan_status and set_plan_status, or pass status to write_plan and
update_plan_from_file. The TUI surfaces the status next to the plan title.
Optimistic locking
When several sessions edit the same plan, concurrent writes could silently
overwrite each other. Every read returns a revision number; pass the value you
last read as last_known_revision to write_plan, update_plan_from_file,
set_plan_status, or delete_plan. If the plan changed since (its current
revision no longer matches), the write is rejected with a version-conflict
error and the caller should re-read the plan and retry. The revision check and
the write happen under the storage's cross-process file lock, so the conflict
is detected reliably even when the competing writer runs in a different Docker
Agent process. Omit last_known_revision to write unconditionally (last
writer wins).
Plan Names
Plan names must match the pattern [a-z0-9][a-z0-9_-]* (lowercase letters, digits, -, _). This is enforced structurally so two different inputs can never collapse onto the same file and path-traversal is impossible by construction.
Plan Fields
Each plan document contains:
| Field | Description |
|---|---|
name | The plan's unique slug name |
title | A short human-readable title (optional) |
content | The full Markdown or free-form plan text |
author | Free-form label identifying who last wrote the plan |
status | Free-form lifecycle label (optional), e.g. in-progress |
revision | Monotonically increasing version counter, bumped on every write |
updatedAt | ISO 8601 timestamp of the last write |
Example
Two agents collaborate on a shared plan — the architect drafts it and the builder refines it:
agents:
root:
model: anthropic/claude-sonnet-4-5
description: Coordinator
instruction: |
Route work between the architect and the builder.
handoffs: [architect, builder]
architect:
model: anthropic/claude-sonnet-4-5
description: Drafts high-level plans
instruction: |
Use list_plans and read_plan to inspect existing plans, then write_plan
to create or revise one. Always read before writing. When done, hand off
to the builder.
toolsets:
- type: plan
handoffs: [builder]
builder:
model: openai/gpt-4o
description: Adds implementation steps to plans
instruction: |
Read the architect's plan with read_plan, then use write_plan to append
concrete implementation steps. Always read before writing. When done,
hand off back to root.
toolsets:
- type: plan
handoffs: [root]See examples/shared_plan.yaml for a complete working example.
Error Handling
read_planreturns a distinct "not found" error when a plan does not exist, as opposed to any other I/O error, so callers can tell "plan missing" from "plan unreadable."list_plansskips corrupt entries but reports them in awarningsfield so an agent can detect and recover from a bad state (e.g., by callingdelete_plan).delete_plancan remove a corrupt plan to recover from a bad state.
Managing plans from the host
Shared plans can also be inspected and managed outside a session with the docker agent plans command group: list, get, create, update, set status, export, and delete — with the same optimistic-locking semantics as the tools (--expected-version guards a write and a stale version fails with exit code 3; --force writes unconditionally). Session plans (the per-session "draft, review, execute" plan) can be listed, read, and exported through the same commands but stay owned by their session and cannot be mutated from the host.
$ docker agent plans list
$ docker agent plans get release > plan.md
$ docker agent plans update release --file ./plan.md --expected-version 1The /plans browser in the TUI
Inside the full-screen TUI, the /plans slash command (also in the Ctrl+K command palette) opens a plan browser over the same store the agents use, so changes made by agents mid-session appear immediately. The list shows every shared plan plus the current session's session plan, with each plan's scope, identity (name, or session ID for the session plan), status, version (- for the unversioned session plan), last update time, and title.
Keybindings:
| Key | Action |
|---|---|
| ↑/↓, mouse | Navigate; Enter or double-click opens a detail view with the full metadata and scrollable markdown content |
| / | Filter by name, title, status, or scope (Esc leaves filter mode) |
| r | Refresh from storage |
| x | Export the selected plan to <name>.md (shared) or session-plan-<short-id>.md (session) in the session's working directory. An existing file is never overwritten — the export fails with a notification instead |
| s | Set a shared plan's free-form status via a small input dialog |
| e | Edit a shared plan's content in $VISUAL/$EDITOR |
| n | Create a new shared plan: pick a name, then draft the content in $VISUAL/$EDITOR (an empty draft aborts) |
| d | Delete a shared plan after a confirmation that names the plan and its version |
| Esc | Close the detail view / the browser |
Every mutation is guarded by the version shown on screen (the same optimistic locking as last_known_revision): if an agent changed the plan in the meantime, the write is rejected, a notification reports the current version, the newer content is left intact and re-read into the browser, and an edit draft is kept in a temp file so nothing is lost. Session plans are read-only here — status, edit, and delete report why instead of attempting the write. The browser also refreshes live when agents in the same process write, re-status, or delete plans (and when this session's agent updates its session plan); in the lean TUI, which has no overlays, /plans is unavailable.
Related plan work
Plan support grew across several issues and pull requests; they are useful context for the design decisions on this page:
- #2788 — proposed
/plan, approval UX, and plan/execution separation; superseded by read-only planner sub-agents and the dedicated plan tools. - #3227 / #3237 — introduced the shared
plantoolset and its pluggable-storage direction. - #3239 — added the pluggable
Storageinterface. - #3263 / #3274 — file-based revisions, export, free-form status, and optimistic locking.
- #3292 / #3305 — the per-session markdown plan of the session plan toolset and its
exit_plan_modemarker, alongside the shared plan toolset. - #3140 / #3168 — related plan-mode and persona history.
- #3844 — the host-facing management layer: the
docker agent plansCLI and the TUI/plansbrowser documented above.
Tip