Multi-agent orchestrator for Claude Code. Start a normal Claude Code session that can fan out work to parallel autonomous agents, each in its own git worktree and tmux pane.
klausThat's it. Run it from a repo or from your home directory — either works. You're in an interactive Claude Code session. Talk to Claude normally — plan features, debug issues, review code. When there's work that can run in parallel, Claude (or you) spawns agents:
You: We need to fix the flaky auth test, add dark mode to settings,
and update the API docs. Can you launch agents for each?
Claude: [runs klaus launch for each task]
Three new tmux panes appear. Each agent works independently in its own worktree, pushes a branch, and opens a PR. Your coordinator session stays focused on the big picture.
- In a repo: a fresh git worktree is created from
origin/main. Anywhere else: a scratch workspace under~/.klaus/sessions/ - Claude Code starts interactively in that workspace
- You talk to Claude as usual — it has
klauson PATH - When Claude runs
klaus launch, a new tmux pane splits off with an autonomous agent - Agents push branches and open PRs —
klaus dashboardpicks them up automatically - The pipeline monitors CI, dispatches fix agents on failure, and auto-merges when approved
- Your job shifts from babysitting agents to reviewing and approving PRs
- When you're done,
klaus cleanup --alltears everything down
Klaus is repo-agnostic. You can run it from your home directory and target any repo with klaus launch --repo owner/repo or set a session default with klaus target owner/repo.
The session is the experience. The pipeline handles the rest.
Once an agent opens a PR, the dashboard's event-driven pipeline takes over:
PR created → CI pending → CI passed → Approved → Merged
↓ ↓ ↓
CI failed Changes Req. Conflicts?
↓ ↓ ↓
Fix agent Fix agent Rebase agent
- CI fails — a fix agent is dispatched automatically (via
--pr) to push a correction - Review comments — an agent is dispatched to address requested changes
- Approved + CI green + no conflicts — auto-merge (when
auto_merge_on_approvalis enabled) - Merge conflicts — a rebase agent resolves them before merging
When a pr-fix run is already active on a PR — including coordinator-launched runs from klaus launch --pr — the pipeline will not auto-dispatch additional fix or rebase agents on it. This prevents races during multi-step refactors where intermediate commits may fail CI before the run completes.
Pipeline stages per PR: ci_pending → ci_passed → approved → merged, with failure paths back through ci_failed or changes_requested. Budget-exhausted agents land in budget_paused — see Budget pause and resume below.
You can also drive the pipeline manually with klaus approve and klaus merge.
Pipeline events (PR created/approved/merged, CI passed/failed, agent errors) are appended to ~/.klaus/sessions/$KLAUS_SESSION_ID/events.jsonl and exposed as a streaming channel via klaus watch. It's designed for Claude Code's Monitor tool — each matching event becomes a notification injected into the coordinator's context between turns, so the coordinator can react to merges or CI failures without you having to mention them.
klaus watch # default filter, follow new events
klaus watch --since-start # replay everything in the file, then follow
klaus watch --filter pr:merged # narrow to a single event type (repeatable)
klaus watch --filter-out pr:approved # subtract from the default filter
klaus watch --json # raw JSONL passthrough
klaus watch --list-types # show live and reserved event typesThe coordinator session's system prompt automatically suggests arming a persistent Monitor on klaus watch at startup. The dashboard reads the same events.jsonl file independently — both consumers can run side by side.
When an agent exhausts its --budget cap, klaus does not silently kill the worktree. Instead it parks the in-progress work on GitHub:
- Commits any uncommitted changes in the worktree (
WIP from klaus run <id> (budget paused)). - Pushes the branch to
origin(with--force-with-lease). - Opens a draft PR — or updates the existing one — and applies the
klaus:budget-pausedlabel. - Posts a one-line PR comment explaining the pause and how to continue.
- Cleans up the worktree and tmux pane.
The draft PR + label is the persisted state — klaus does not keep an in-process "paused" status. The dashboard surfaces these as budget paused, awaiting decision via the pipeline FSM (which reads the label off GitHub).
To resume, dispatch a fresh agent against the PR's branch:
klaus launch --pr <num> "continue the work"The new agent sees the WIP commit and picks up from there. When the follow-up agent's _finalize runs, the klaus:budget-paused label is automatically removed and an agent:resumed event is emitted. If the follow-up agent also exhausts its budget, the cycle repeats (a new WIP commit, label re-applied).
By default, klaus launch --pr against a budget-paused PR does more than pick up the WIP commit: it continues the previous agent's Claude conversation instead of starting one cold. At finalize time klaus stores the resume-able conversation trajectory (the JSONL claude itself writes under ~/.claude/projects/…) on refs/klaus/data at sessions/<run-id>.jsonl. On resume it fetches that blob, restores it into the new worktree's project dir, and invokes claude --resume <uuid>. The conversation continues from the exact point of pause — no re-grepping or re-orienting — which is faster and cheaper than a fresh agent.
Replay is best-effort and falls back to a fresh agent when any of these hold:
- The trajectory was never pushed (skipped because
klausdetected potentially sensitive content), or the PR pre-dates this feature. - The trajectory is larger than the size threshold (default 300KB; replaying a very large trajectory can cost more than re-exploration). Configure with
replay_threshold_kbin~/.klaus/config.jsonor--replay-threshold-kb. - No Claude session UUID could be determined for the paused run.
- You pass
--no-replay.
Flags:
klaus launch --pr 42 "continue" # replay if eligible (default)
klaus launch --pr 42 --no-replay "continue" # force a fresh agent
klaus launch --pr 42 --replay "continue" # force replay, bypassing the size threshold
klaus launch --pr 42 --replay-threshold-kb 500 "..." # raise the per-launch thresholdStaleness semantics — what you opt into with replay. The restored conversation is a snapshot from when the previous agent paused, replayed into a new worktree at a different path:
cwd/ path mismatch. The trajectory's tool calls reference the original (now-deleted) worktree path; the resumed agent runs in a fresh worktree.claudere-anchors to its current working directory, so new file operations are correct, but the conversation history still mentions the old path.- Tool-result staleness. Cached
Read/grep results in the history reflect the files as they were at pause time. The branch head carries the WIP commit, so current contents may differ. This is the same staleness any long-livedclaude --resumehas; the agent is expected to re-read before relying on stale output. klaus adds no special mitigation beyond restoring the trajectory faithfully. - No prompt-cache benefit. Resumes happen long after the 5-minute cache TTL, so the replayed history is re-read as fresh input tokens. The size threshold exists precisely to keep that cost below the cost of a fresh re-exploration.
Note: replay relies on the conversation file being available where finalize runs. For agents executed on a remote
sandbox_host, the conversation file lives on the sandbox and is not synced back, so those runs fall back to a fresh agent.
To abandon the work, close the draft PR. To redirect, push manual commits to its branch.
There is no separate klaus resume or klaus finalize command — klaus launch --pr is the only resume path, and _finalize handles the WIP commit automatically.
Add as a flake input to your repo:
inputs.klaus = {
url = "github:patflynn/klaus";
inputs.nixpkgs.follows = "nixpkgs";
};Then add klaus.packages.${system}.default to your devShell's buildInputs.
git clone https://github.com/patflynn/klaus.git
cd klaus
go build -o ~/.local/bin/klaus ./cmd/klaus/make test # unit tests (go test ./...)
make test-e2e # end-to-end tests against a real, isolated tmux server
make vet # go vet ./...The e2e suite (e2e/, guarded by the e2e build tag so go test ./... is
unaffected) drives the real klaus binary against a private tmux server and
real git repositories, faking only claude/gh. It requires tmux on PATH.
See e2e/README.md for the design and how to add scenarios.
The coordinator session uses these — you generally don't run them directly:
| Command | Purpose |
|---|---|
klaus session |
Start an interactive coordinator session |
klaus launch "<prompt>" |
Spawn an autonomous agent |
klaus launch --repo owner/repo "<prompt>" |
Launch an agent against a different GitHub repo |
klaus launch --repo <project-name> "<prompt>" |
Launch an agent using a registered project |
klaus launch --pr <number> "<prompt>" |
Push fixes to an existing PR's branch |
klaus target owner/repo |
Set session-level default target repo |
klaus status |
Dashboard of all runs (with CI, conflict, and merge-readiness columns) |
klaus logs <id> |
View agent output (live, replay, or raw) |
klaus cleanup <id>|--all |
Tear down worktrees, panes, and state |
klaus push-log <id> |
Force-push a log held back for sensitivity |
klaus project add <owner/repo> |
Register a project (clones if needed) |
klaus project list |
Show registered projects |
klaus project remove <name> |
Unregister a project |
klaus project describe <name> <desc> |
Set a one-line description (empty string clears it) |
klaus project set-dir <path> |
Set the default projects directory |
klaus sync |
Fetch and fast-forward every registered project |
klaus new <project-name> |
Scaffold a new project using principles-based generation |
klaus webhook check |
Check registered projects for GitHub webhook configuration |
klaus webhook setup [project] |
Create missing webhooks for registered projects |
klaus dashboard |
Live TUI dashboard for monitoring agents and PRs |
klaus watch |
Stream pipeline events line-by-line (designed for Claude Code's Monitor tool) |
klaus approve <pr>... |
Approve PRs for merging |
klaus merge <pr>... |
Sequentially merge PRs with conflict resolution |
klaus init |
Scaffold .klaus/ config (optional, for customization) |
Push fixes to an existing PR's branch instead of creating a new PR. The agent checks out the PR's branch, makes changes, and pushes directly — the PR updates automatically. Useful for addressing review comments or fixing CI failures on an existing PR.
klaus launch --pr 42 "Address the review comments"
klaus launch --pr 42 --issue 10 "Fix the auth bug mentioned in review"The --pr and --issue flags can coexist (the agent may reference the issue in commits).
Launch an agent against a different GitHub repository. The repo is cloned (or fetched if already cached) and the agent gets its own worktree in that clone. State is still tracked in the host repo.
klaus launch --repo owner/repo "Fix the bug in their API"When sandbox_host is set in ~/.klaus/config.json, agents run remotely via SSH on the sandbox host instead of locally. The worktree is synced to the sandbox before launch, and results are synced back after completion. Log streaming, formatting, and finalization still happen locally.
{"sandbox_host": "klaus-worker-0"}If the sandbox is unreachable, execution falls back to local automatically. Use --local to force local execution, or --host <name> to override the configured sandbox host.
klaus launch --local "Run this locally"
klaus launch --host my-sandbox "Run on a specific host"The dashboard shows [sandbox] tags on remotely-executed agents and displays sandbox reachability status in the header. The status command includes a HOST column.
Set a session-level default target repo. When the coordinator session is not inside a git repo, this avoids needing --repo on every klaus launch. Accepts a registered project name or owner/repo.
klaus target owner/repo # set default by owner/repo
klaus target my-project # set default using registered project name
klaus target # show current target
klaus target --clear # remove defaultThe targeting priority for klaus launch is:
--repoflag — if it matches a registered project name (noowner/prefix), resolves to that project's local path--repoflag —owner/repoor full URL (clones/fetches from GitHub)- Current git repo (if in one)
- Session target (
klaus targetsetting) - Error with usage hint
The status dashboard shows these columns for each run:
| Column | Values | Meaning |
|---|---|---|
| CI | passing / failing / pending / unknown |
CI check status for the PR |
| CONFLICTS | none / yes / unknown |
Whether the PR has merge conflicts |
| MERGE | ready / blocked / pending |
Overall merge readiness (combines CI, conflicts, and review status) |
Live TUI view of the PR pipeline. Groups runs by repository, auto-refreshes via filesystem watching and GitHub polling every 30s. Keyboard shortcuts: j/k (or ↑/↓) move the PR selection, a approve the selected PR, d discuss the selected PR with the coordinator (pre-fills WRT PR#<num>: in the session pane and switches focus there), o open the selected PR in a browser, r force refresh, q quit.
When webhook mode is enabled, the data-source line shows a freshness indicator for the most recent webhook delivery (e.g. · last event 5m), color-coded as it ages (dim under 30m, yellow under 2h, red beyond). Note this surfaces the age of the last delivery, not delivery health — a long silence can mean a quiet repo or a broken delivery path, so it's a hint for a human to judge rather than a definitive check.
Mark PRs as approved for merging. By default, klaus merge requires approval before merging.
klaus approve 42 43 # approve specific PRs
klaus approve --all # approve all merge-ready PRs
klaus approve --run 20260328-1603-a3f2 # approve by run IDSequentially merges a list of PRs. Handles conflicts by rebasing onto main, verifying the build, and force-pushing. Waits for CI to pass before merging (up to 10 min).
By default, PRs must be approved with klaus approve before merging. Unapproved PRs trigger an interactive prompt (or are skipped with --yes).
klaus merge 42 43 44
klaus merge --dry-run 42
klaus merge --merge-method rebase --no-delete-branch 42
klaus merge --force 42 # bypass approval check
klaus merge --yes 42 43 # skip unapproved PRs without promptingFlags: --dry-run, --merge-method (squash/merge/rebase), --no-delete-branch, --force (bypass approval), --yes (skip unapproved).
Manage a persistent registry of projects. The registry maps short names to local paths and is stored in ~/.klaus/projects.json.
klaus project add owner/repo # clone into projects dir and register
klaus project add owner/repo --path . # register an existing local checkout
klaus project add owner/repo -d "CLI for X" # register with an explicit description
klaus project add my-tool # search your GitHub repos by name
klaus project list # show all registered projects
klaus project remove my-tool # unregister (does not delete the clone)
klaus project describe my-tool "CLI for X" # add or update the one-line description
klaus project describe my-tool "" # clear the description
klaus project set-dir ~/hack # set the default clone directoryWhen klaus project add is called without -d/--description, klaus tries gh repo view --json description to pick up the GitHub description automatically. If gh is unavailable or the repo has no description, the project is registered without one — you can backfill later with klaus project describe. Descriptions show up in klaus project list and in the session coordinator's ## Registered projects block.
Check and create GitHub webhooks for registered projects. Webhooks let the dashboard receive real-time events (CI status, PR reviews, pushes) instead of relying solely on polling.
klaus webhook check # show webhook status for all registered projects
klaus webhook setup # create missing webhooks for all projects
klaus webhook setup my-project # create a webhook for a specific projectBoth commands require webhook.relay_url in your config. setup also requires webhook.secret_file — a path to a file containing the HMAC secret used to verify incoming payloads.
{
"webhook": {
"relay_url": "https://your-relay.example.com",
"secret_file": "/path/to/webhook-secret"
}
}The created webhooks subscribe to push, check_run, check_suite, pull_request, and pull_request_review events.
Additional webhook config knobs:
poll_fallback(bool, defaultfalse): whentrue, the dashboard polls GitHub every 30s in addition to receiving webhooks.reconcile_interval_seconds(int, default300): in webhook-only mode (poll_fallback: false), a slow reconcile heartbeat runs a full PR status re-fetch on this interval. This bounds worst-case staleness if a webhook is ever dropped or missed — without reverting to full 30s polling. Set to a negative value to disable. Ignored whenpoll_fallbackistrue(polling already re-fetches every 30s).
{
"webhook": {
"relay_url": "https://your-relay.example.com",
"secret_file": "/path/to/webhook-secret",
"poll_fallback": false,
"reconcile_interval_seconds": 300
}
}Creates a new GitHub repo and launches a Claude agent to scaffold it. Reads principles from .klaus/principles.md (or built-in defaults). The agent makes all scaffolding decisions based on those principles — no templates. The new project is automatically registered in the project registry.
klaus new my-projectKlaus works out of the box with sensible defaults. To customize, run klaus init to scaffold a .klaus/ directory (or ~/.klaus/config.json when outside a repo), or create the files yourself. Configuration layers: defaults → ~/.klaus/config.json → .klaus/config.json.
.klaus/config.json — Override defaults:
{
"worktree_base": "/tmp/klaus-sessions",
"default_budget": "5.00",
"data_ref": "refs/klaus/data",
"default_branch": "main",
"trusted_reviewers": ["gemini-code-assist[bot]"],
"require_approval": true,
"auto_merge_on_approval": false
}.klaus/prompt.md — Custom system prompt for launched agents. Go template variables: {{.RunID}}, {{.Issue}}, {{.Branch}}, {{.RepoName}}. Customize this to match your repo's conventions, test commands, and PR workflow.
.klaus/session-prompt.md — Custom prompt for the coordinator session. Same template variables.
.klaus/pr-fix-prompt.md — Custom prompt for PR-fix agents. Additional variable: {{.PR}}.
- Worktrees isolate each agent — they can't step on each other or your working tree
- tmux panes give live visibility into each agent's progress
- JSONL logs are saved for replay and post-run analysis
- Sensitivity scanning checks logs for private IPs, SSH keys, and credentials before persisting
- State storage — session state lives in
~/.klaus/sessions/(ephemeral, machine-local), while finalized run artifacts sync to the repo's data ref - Data ref (
refs/klaus/data) stores run metadata without polluting your branch list
tmux(sessions run inside tmux)claude(Claude Code CLI)git(needed for agent worktrees; sessions can run without it)gh(GitHub CLI, for PR operations)