#E1, #E2 placeholders resolved at runtime with the outputs of previous steps. Combined, they avoid the expensive pattern of “dispatch an agent, wait, think again, dispatch another”.
When it fires:
/plan <task> forces it; CHATCLI_QUALITY_PLAN_FIRST_MODE=always always; auto (default) triggers when ComplexityScore(task) >= 6. To review the plan before executing, use /plan preview <task> (dry-run).Plan format
ThePlannerAgent, when it receives the PlannerStructuredOutputDirective marker at the start of the task, emits strict JSON:
ReWOO placeholders
Any step can inject the output of earlier steps via#E<n>:
| Syntax | Effect |
|---|---|
#E1 | Replaces with the trimmed full output |
#E1.summary | First non-empty line of the output |
#E1.head=200 | First 200 runes with … if truncated |
#E1.last=200 | Last 200 runes with leading … |
Complexity heuristic
ComplexityScore(task) → int[0,10] is calibrated to fire Plan-First only when it pays off. The score blends three signals:
| Signal | Cap | What counts |
|---|---|---|
| Action verbs | 5 | implement, add, create, fix, refactor, write, test, deploy, build, run, update, … — bilingual (en + pt-BR: implementar, adicionar, criar, corrigir, escrever, …) |
| File artefacts | 3 | Matches on \b[\w./-]+\.(go|ts|py|md|yaml|…)\b + Dockerfile|Makefile|… |
| Sequencers | 2 | then, after, finally, e depois, e em seguida, por fim, após, … |
Examples
- Trivial (score 1)
- Multi-action (score 6+)
- PT-BR (score 6+)
auto does not fire Plan-First. Orchestrator handles directly.Execution flow
runPlanFirstIfApplicable
Checks
cli.pendingPlanFirst (one-shot) or quality.ShouldPlanFirst(cfg, userQuery).Planner dispatch
agentDispatcher.Dispatch([{Agent: planner, Task: PlannerStructuredOutputDirective + userQuery}]).ParsePlan
Tolerant to markdown code fences and trailing prose. Validates: unique IDs, deps point to earlier steps,
#E<n> placeholders point to declared IDs.TopologicalOrder + Execute
Stable topological order (lex sort on ties) guarantees reproducibility. Each step: resolve placeholders → dispatcher.Dispatch → store output.
Inject report
Two synthetic messages go into
cli.history:- assistant: the plan JSON (the model sees what was attempted)
- user: deterministic
FinalReportwith task/agent/status/output per step + handoff
The second message is role=user (not
system) since the April 2026 fix. Models like Claude Sonnet 4.6 refuse completion when the conversation ends on assistant (“This model does not support assistant message prefill”). The synthetic user turn closes the conversation correctly and gives the orchestrator an explicit anchor to finalize.Fault tolerance
A failing step does not abort the run. The behavior is “continue downstream with substituted error”:HadErrors flag is set so Reflexion can decide to escalate.
/plan — manual invocation
/plan accepts six forms. Autocomplete (Tab) offers the subcommands.
Behavior matrix
| Form | Enters | Executes steps? | Calls orchestrator? | When to use |
|---|---|---|---|---|
/plan | — (arms flag) | depends on next /agent or /coder | yes | Want manual control of the consumer mode |
/plan <task> | agent | yes | yes | Default flow, fastest |
/plan agent <task> | agent | yes | yes | Explicit equivalent (symmetry with coder) |
/plan coder <task> | coder | yes | yes | Engineering tasks (edits, builds, tests) |
/plan preview <task> | agent (temporary) | no | no | Review plan before running |
/plan dry <task> | same as preview | no | no | Alias |
cli.pendingPlanFirst flag is consumed and cleared on the first subsequent /agent or /coder invocation. The cli.pendingPlanDryRun flag (exclusive to preview/dry modes) is cleared at the same point and makes AgentMode.Run return before the ReAct loop via planDryRunHandled.
Environment variables
| Env var | Default | Values | Effect |
|---|---|---|---|
CHATCLI_QUALITY_PLAN_FIRST_MODE | auto | off|auto|always | Trigger mode |
CHATCLI_QUALITY_PLAN_FIRST_THRESHOLD | 6 | 0..10 | Minimum score for auto to fire |
Override via persona (CHATCLI_AGENT_PLANNER_*)
PlannerAgent respects the usual per-agent overrides:
Observability
Each run emits structured logs:Spinner during Plan-First
The “planning structured steps…” spinner shows only during the purePlannerAgent call (step 1). During PlanRunner execution (step 2), the spinner is intentionally off and a static status line is emitted:
Dry-run: what gets rendered
/plan preview <task> renders:
ParsePlan fails (planner returned malformed JSON or prose), the raw output is printed with a warning — never silent.
When to turn off
See also
#3 Reflexion
Reflexion consumes
HadErrors from the plan runner to generate lessons when steps fail.Multi-Agent Orchestration
The dispatcher that PlanRunner reuses is the same as the standard orchestrator.
PlannerAgent (pre-PR)
The agent existed before the pipeline — this pattern formalizes how to invoke it deterministically.
Full configuration
All env vars and slashes in one place.