Multi-agent ensemble planning CLI. Orchestrates multiple AI agents (Claude Code, OpenAI Codex) to collaboratively create implementation plans with human-in-the-loop review.
"A droid is only as good as its plan." Planning with multiple models produces more robust plans through:
- Diversity: Different models notice different things when exploring codebases
- Peer review: Agents critique each other's drafts before synthesis
- Iteration: Human feedback refines plans across multiple turns
From source:
bun install
bun run src/index.ts --help# Start a planning session (blocks until first turn is complete)
bun run src/index.ts init "Add user authentication"
# Review and edit the plan
# Edit .pl4n/sessions/<id>/turns/001.md
# Continue to next turn (blocks until agents complete)
bun run src/index.ts continue --session <session_id>
# Approve when satisfied
bun run src/index.ts approve --session <session_id>Pl4n can launch a lightweight web editor for reviewing and editing plans. When init or
continue completes, the server starts (if needed) and an edit_url is included in the
JSON output.
# Default behavior: start server and emit edit_url
bun run src/index.ts init "Add user authentication"
# Disable web editor
PL4N_WEB=0 bun run src/index.ts init "Add user authentication"Manual server control:
pl4n server start # start daemon
pl4n server stop # stop daemon
pl4n server status # check running status
pl4n server start --restart # kill existing server, then start
pl4n server start --foreground # run in foreground (dev)
pl4n server start --workspace /path/to/root # scan a specific workspace
pl4n server start --bind 127.0.0.1 # bind addressFor remote access (Tailscale/VPN), override the host used in URLs:
PL4N_HOST=100.x.x.x bun run src/index.ts init "Add user authentication"Troubleshooting:
- If the browser cannot connect, check local firewall settings and that the port is reachable.
- If port 3456 (or
PL4N_PORT) is taken, the server will try the next available port. - Clipboard copy is best-effort; the URL is always printed in JSON output.
Server configuration lives in ~/.pl4n/config.yaml:
workspaces:
- /Users/you/code
- /Users/you/work
bind: 0.0.0.0
port: 3456
auth:
mode: strictAuth modes:
strict(default) requires atquery param token for UI and API requests (the CLI providesedit_urlwith tokens).trustedskips token checks for allowlisted client IPs; other clients still need tokens.
For LAN + Tailscale access without tokens, use trusted mode with a CIDR allowlist:
auth:
mode: trusted
trusted_cidrs:
- 127.0.0.0/8
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 100.64.0.0/10If trusted_cidrs is omitted, Pl4n defaults to the list shown above. CIDRs must be IPv4
(::1 and IPv4-mapped IPv6 are supported; other IPv6 ranges are not).
You can also set a single workspace via PL4N_WORKSPACE=/path/to/root.
Server env overrides: PL4N_BIND=0.0.0.0, PL4N_HOME=/custom/.pl4n.
The server UI starts at /projects and links to project-scoped session lists and editors.
Each turn follows this flow:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ DRAFT │ -> │ PEER REVIEW │ -> │ SYNTHESIS │ -> │ USER REVIEW │
│ │ │ │ │ │ │ │
│ Each agent │ │ Agents │ │ Merge into │ │ Human edits │
│ writes plan │ │ review peer │ │ unified │ │ turns/001.md│
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
- Draft: Each agent explores the codebase and writes an independent plan
- Peer Review: Agents review each other's drafts and refine their own
- Synthesis: Plans are merged into a unified proposal
- User Review: Human edits
turns/NNN.md, then callscontinueorapprove
Agents maintain context across turns via CLI session continuation:
- Claude Code:
--resume <session_id> - Codex:
resume <thread_id>
This means agents accumulate codebase knowledge rather than starting fresh each turn.
When you edit turns/001.md and call continue, agents receive your changes as a diff. They interpret your edits naturally:
- Deletions = remove this
- Additions = add this requirement
- Comments = feedback to address
| Command | Description |
|---|---|
pl4n init "task" |
Start new planning session (blocks until first turn complete) |
pl4n status --session <id> |
Check progress without blocking |
pl4n continue --session <id> |
Start next turn after your edits (blocks until complete) |
pl4n approve --session <id> |
Lock plan as final |
pl4n list |
List all sessions |
pl4n clean --session <id> |
Remove session data |
pl4n diff --session <id> |
Show changes between turns |
| `pl4n server start | stop |
.pl4n/
├── pl4n.yaml # Agent/tool configuration
└── sessions/
└── swift-river/ # Human-friendly session ID
├── meta.yaml # Task description, timestamp
├── state.yaml # Turn, phase, agent_plan_ids mapping
├── plans/
│ ├── sunny-glade.md # Agent's persistent plan (plan_id)
│ └── amber-marsh.md # Another agent's plan
├── turns/
│ ├── 001.md # Turn 1 synthesis (USER EDITS THIS)
│ ├── 001.snapshot.md # Pre-edit snapshot (for diffing)
│ ├── 001-autosave.md # Web editor autosave (cleared on save/continue)
│ ├── 001/ # Debug snapshots
│ │ ├── sunny-glade-draft.md
│ │ └── sunny-glade-reviewed.md
│ ├── 002.md
│ └── ...
│
├── agents/
│ ├── sunny-glade/
│ │ ├── agent.log # Session-wide debug log (appended)
│ │ └── session.txt # CLI session ID for --resume
│ ├── amber-marsh/
│ │ ├── agent.log
│ │ └── session.txt
│ └── synthesizer/
│ ├── agent.log
│ └── session.txt
│
└── PLAN.md # Symlink to approved turn
Global server state:
~/.pl4n/
├── config.yaml # Server config (workspaces, bind, port)
├── token # Global auth token for the web UI
├── server.json # PID/port metadata for the daemon
└── server.log # Server logs
Key points:
- Plan IDs (
sunny-glade,amber-marsh) are opaque names mapped to agents instate.yaml - Agents read/write their persistent plan file (e.g.,
sunny-glade.md) - After synthesis, all agent plan files are synced to the canonical state
- Debug logs span the entire session (appended each turn)
meta.yamlstores aconfigsnapshot from.pl4n/pl4n.yamlat session init
Edit .pl4n/pl4n.yaml (or .pl4n/pl4n.yml) to configure agents:
claude:
allowed_tools:
- Read
- Edit
- Write
# See .pl4n/pl4n.yaml for the full default list
codex:
full_auto: true
search: true
agents:
- id: opus
type: claude
model: opus
enabled: true
- id: codex
type: codex
model: gpt-5.2-codex
thinking: xhigh
enabled: true
synthesizer:
id: synthesizer
type: claude
model: opus
enabled: trueIf the file is missing, defaults are used.
For Codex agents, thinking maps to model_reasoning_effort (valid values: none, minimal, low, medium, high, xhigh).
Codex constraints live under codex (defaults) or agents[].codex (overrides), with keys:
full_auto, sandbox, approval_policy, dangerously_bypass, add_dir, search, config, and mcp.
config and mcp are serialized to JSON and passed via --config.
For Claude agents, claude.allowed_tools maps to --allowedTools, and claude.add_dir maps to
--add-dir. Claude's allowlist is best-effort; shell commands can still write if they redirect
output. Override defaults per agent with agents[].claude or agents[].codex.
If you pass --pl4n-dir, the config is loaded from that directory.
src/
├── cli.ts # CLI commands (sade)
├── models.ts # Data models (SessionState, Phase, AgentConfig)
├── session.ts # Session lifecycle management
├── orchestrator.ts # Turn orchestration (draft -> peer review -> synthesis)
├── prompts.ts # Agent prompt templates
├── names.ts # Human-friendly name generator
└── adapters/
├── base.ts # AgentAdapter interface
├── claude.ts # Claude Code adapter (subprocess, --resume)
└── codex.ts # Codex CLI adapter (subprocess, resume)
bun installRun tests and checks:
bun run lint
bun run typecheck
bun run test
bun run buildBuild the web editor assets:
bun run build:web- File-based protocol: Agents communicate via files, making the system agent-agnostic
- Turn-based iteration: Each turn produces a numbered synthesis file
- Opaque plan IDs: Prevents models from confusing which file belongs to which agent
- Session continuation: Agents preserve codebase exploration context across turns
- No special syntax: Users edit plans naturally; agents interpret the diff