Skip to content
ƒtsforgev0.52.0
19

Plan mode

4 min read

Plan mode is the default interactive posture. The model can read your repo and propose a plan, but it cannot edit until you approve. On approve, tsforge validates the model’s fenced plan JSON, writes a session-bound plan file, shows it in the Tasks rail, and the same session implements it.

plan mode (default)
→ discuss or paste
→ model calls present_plan { goal, items }
→ TUI renders PLAN card (+ Tasks preview)
→ approve / go / lgtm
→ <cwd>/.tsforge/worklist/plans/<planId>.json
→ session.activePlanId binds that plan
→ implement in-session (task_* tools + /gate as usual)
→ finished = gate green AND checklist done
→ tsforge --continue resumes the session (+ activePlanId + rail)

The status bar shows ◆ plan / ◆ normal.

  • Press Shift+Tab to cycle the mode (plan → normal → …), or type /plan to toggle it
  • When the plan looks right, reply approve, go, or lgtm

There is no disable flag: it’s a mode you cycle with Shift+Tab. (tsforge --plan forces plan mode on for an interactive session even in a repo that configured an autonomous policy.mode. One-shot and headless runs are autonomous regardless.)

When the plan is ready, the model calls present_plan with a structured { goal, items } tree (nested children, optional detail / files / verify / kind). The harness validates it, holds it as a pending proposal, and renders a PLAN card in the TUI (not a JSON dump in chat). Revise by calling present_plan again.

Decomposition rules the planner should follow (soft warnings may appear in the tool result if it strays):

  1. Order — contracts/types → implementation → sibling tests (nest tests as children when useful)
  2. One outcome per item — actionable titles (Create src/notes.ts), not vague prose
  3. File bounds — when known, set files to 1–3 relative paths; split by module boundary if more
  4. Prefer parent + children over one mega-item covering the whole feature
  5. No gate chores — never an item for “run tests / lint / the gate”; the harness gate validates every task_complete
  6. verify / kind — optional hints only (kind: investigate | create | modify | test); not harness-executed commands

Approve (approve / go / lgtm):

  1. Takes the pending proposal from present_plan (fenced JSON in chat is a fallback only)
  2. Writes <project>/.tsforge/worklist/plans/<planId>.json, updates index.json, sets the session’s activePlanId
  3. Updates the Tasks side rail
  4. Turns plan mode off and continues in the same session

If there is no pending plan (and no valid fenced JSON fallback), approve is refused — stay in plan mode.

Concurrent sessions in one project each bind their own activePlanId; plan files do not clobber.

Resume with tsforge --continue (or --resume <id>): the conversation and activePlanId return from the session store, and the Tasks rail reloads that plan.

When activePlanId is set:

  • task_list — nested tree with ids
  • task_focus — set the active item
  • task_complete / task_uncomplete — status changes (tools only; not invented in prose)
  • task_add — append discovered work (optional parent_id to nest)
  • task_update — edit an item’s title/detail/files/verify/kind

The checklist is living: if you or the model discover work the approved plan missed, it should go on the list via task_add (not only in chat). Drift in an item’s scope → task_update; done work that must be redone → task_uncomplete.

task_complete runs the acceptance gate and only marks the item done when green. If the gate is red, the item stays open and the errors are returned — fix, then complete again. Do not put “run tests / lint / the gate” as a checklist item; that is the harness’s job on every complete. A session cannot claim finished while the bound plan still has open items.

Read tools only:

  • read, search, and LSP navigation (symbol_search, find_references, go_to_definition, type_at, impact, symbol_context, diagnostics)
  • git_context: structured, read-only repo history/diffs (see Git context)
  • run for read-only shell commands (no installs, no writes)

Blocked until approval: edit, create, edit_lines, scaffolders, and the task_* tools (offered only after a plan is bound).

Plan mode is for interactive work in a repo. Building a new app from scratch is a separate headless loop: tsforge scaffold then tsforge --greenfield, which keeps its own checklist under .tsforge/greenfield/. That path does not use plan-mode approve or the Tasks rail plan files.

  • Shift+Tab (or /plan) drops to normal mode for hands-on edits
  • One-shot runs (tsforge "task" --accept …) and headless/eval runs are autonomous already

Greenfield builds · Interactive CLI