Pass context between coding agents like a baton.
baton is a terminal-native workspace for coding agents, built around durable, harness-independent sessions and inspired by tutti. Today, it lets you use Claude Code and Codex in one TUI and switch between them without carrying context. Because BatonSession is owned by baton rather than any harness, the same foundation can grow from agent handoffs into multi-agent collaboration and orchestration. Claude Code and Codex are the first bundled harnesses, not a closed support list.
Harness-native sessions are resume optimizations; BatonSession history remains available even when a native session cannot be resumed.
The most common shape of multi-agent work today is a human acting as a context courier: copying one agent's output to another, re-explaining background, hand-writing handoff documents. baton wants context to be an asset the user owns, not a by-product locked inside a single tool.
Two fundamentals are in place today:
- Context portability: a BatonSession is a durable, unified history owned by the user that outlives any single harness. Switching agents requires no context carrying; harness-native sessions only accelerate resume and are never a prerequisite for the history to survive.
- Native experience: baton preserves each agent's own input, completion, streaming, tool-call, and approval experience as much as possible, adding only a few commands of its own (such as
/codexand/claude).
The Plugin runtime adds a third principle:
- Layered loops: baton core stays domain-neutral and owns the shared input, context, permission, and harness-routing path. Baton Plugins own long-running domain loops and currently propose the next Harness input for the user to confirm, edit, or discard. Harnesses remain intelligent execution providers; Harness Plugins such as devloop constrain the smaller development loop inside a Harness.
On top of these, three product directions remain on the roadmap. The Plugin runtime now provides the Resource/Controller foundation for long-running loops, but these end-to-end experiences are not complete yet:
- Multi-harness collaboration: from relaying within one session toward dispatching the same task to multiple harnesses in parallel, with results merged back into one unified history. The near-term path is draft sessions — when a new idea strikes mid-task, fork a draft session (optionally on a different harness) to explore in parallel without interrupting the mainline.
- Context intake: the mainline is not a raw transcript of everything but the canonical history the user endorses. After a draft session produces results, the user decides whether to merge its conclusions into the mainline or discard them; discarding is not deletion — drafts stay durable and referenceable.
- Event-driven long-running loops: listen to external events such as pushed commits or merged PRs and wake the corresponding session to continue its work, so agents are no longer confined to an interactive terminal.
Start with the stable kernel: baton is one bidirectional pipeline. chat-tui carries intent/render only, the controller owns the Input lifecycle + the driven-turn queue, adapters translate each harness's wire to a single normalized event stream, and session.jsonl persists it. The event stream is the sole source of truth; the UI is a projection.
v2 keeps that pipeline and layers long-running domain loops around it. baton core remains domain-neutral and owns shared control; Baton Plugins reconcile domain Resources and propose work; Harnesses provide intelligent execution, while Harness Plugins constrain the smaller loop inside a Harness. Today, proposed work returns to the same Input, context, permission, and routing path for the user to approve, edit, or discard.
See docs/kernel.md for the stable kernel — core concepts, invariants, the v1 pipeline, and the harness extension contract. See docs/plugin.md for the v2 Plugin runtime and docs/loop-engineering.md for the layered loop model.
- Use Claude Code and Codex from the same terminal interface
- Switch directly with
/codexor/claude, and configure its model and reasoning effort separately - Open a previous BatonSession with
/sessions, or start a clean one with/new - Continue the latest session in a project with
baton -c, or open one by ID withbaton -s <id> - Search grouped
@context from built-in Session and Plugin ContextProviders, then inject it into the current turn - Record messages, thoughts, tool calls, file changes, plans, and token usage in a unified format
- Preserve harness startup interactions such as Codex hook trust, reusing unchanged trusted definitions with a visible notice
- Append events to a local
session.jsonlfor state reconstruction and future references - Reuse local Claude Code and Codex credentials without storing them in baton
- Use a headless REPL to debug agent integrations
- Register local or Git Plugin Marketplaces and install immutable Plugin Packages
- Run session-scoped Plugin Controllers over durable Resources, with Resource/cron Sources, requeue wakeups, Board projections, and user-approved Proposals
Install baton with npm. You also need at least one supported agent installed and authenticated (Codex CLI / Claude Code).
npm install -g @compforge/batonOr run it once without a global install:
npx @compforge/batonOn first run, baton creates ~/.baton/config.yaml:
defaultAgent: codex
codexCommand:
- codex
- app-server
mentionBudgetChars: 4096
showThoughts: trueSee config.yaml.example for all available options and usage notes.
Codex approvals follow Codex's own configuration by default — your ~/.codex/config.toml, profiles and any enterprise policy all apply, and Codex itself defaults to reviewing with you. Set codexApprovalReviewer: auto_review to delegate to its risk reviewer instead; Baton keeps that delegation visible in Harness Status and records each automatic decision beside its target tool.
If Claude Code uses a custom executable, set claudeExecutable in the configuration or override it temporarily with an environment variable (BATON_CLAUDE_BIN=/path/to/claude baton). Configuration precedence: environment variables > config.yaml > defaults.
Start the TUI and type a prompt to send it.
/claude or /cc Switch to Claude Code
/codex or /cx Switch to Codex
/cc <message> Switch to Claude Code and send the message immediately
/cx <message> Switch to Codex and send the message immediately
/cla <message> Unique harness-name prefixes work too
/model Open the model picker for the active harness
/model <id> Select the model used by subsequent turns
/effort Open the reasoning-effort picker for the active harness
/effort <level> Select the reasoning effort used by subsequent turns
/compact Ask the active harness to compact its context
/status Show the active harness/model context usage and session information
/sessions Open the BatonSession picker
/new Start a new BatonSession in the current project
@ Search grouped Session and Plugin context
Tab Complete a command or reference
Esc Interrupt the current turn
/exit Exit
Ambiguous prefixes such as /c <message> are not sent to a harness; baton reports the matching harnesses in the transcript.
Common CLI commands:
baton # Start the TUI
baton --cwd /path/to/project # Start in a specific project directory
baton -c # Continue the latest session in this directory
baton -s bs_01... # Open a specific BatonSession
baton repl --agent codex # Start the headless REPL with Codex (alias: cx)
baton repl --agent claude # Start the headless REPL with Claude (alias: cc)
baton sessions # List sessions available for reference
baton plugins marketplace add ./reqloop
baton plugins available
baton plugins install qiankun/requirement-loop
baton plugins list
baton help # Show full helpReference an ID returned by baton sessions in your prompt:
@bs_01... Implement this feature based on Claude's earlier analysis
baton reads the referenced session's compact summary and passes it to the active harness as context.
baton stores its data in ~/.baton/ by default:
~/.baton/
├── config.yaml
├── plugins/
│ ├── marketplaces.json
│ ├── marketplaces/<marketplace-name>/
│ └── packages/<encoded-plugin-id>/<version>/
└── projects/<project-key>/
├── project.json
└── sessions/<session-id>/
├── meta.json
├── session.jsonl
└── plugins/<plugin-instance-id>/
├── resources/
└── proposals/
Projects group sessions by working directory using a readable, collision-resistant key; project.json retains the original cwd. Plugin runtime data belongs to its BatonSession. session.jsonl is the durable logical history used for rendering, recovery, harness handoff, and cross-session references. Claude Code and Codex still manage their private native sessions; baton stores their IDs only to accelerate resume and never modifies their native session files.
Apache-2.0