Review PRs and ship features in parallel — each one in its own Claude-ready worktree.
You're reviewing more PRs and starting more features than ever, because Claude Code makes each one faster. But your IDE and your shell still want one branch at a time. Zen fixes that mismatch.
For every PR in your review queue and every feature you're working on, zen creates a dedicated git worktree with Claude pre-armed: PR context injected as CLAUDE.local.md (never touching the repo's own CLAUDE.md), the right slash command installed, terminal tab opened on demand. A background daemon watches GitHub, prepares review worktrees silently as PRs come in, fast-forwards them when the PR head moves, and removes them a few days after merge. Open a tab when you're ready — everything is already set up.
- Quick start
- What needs your attention?
- Review a PR
- Work on a feature
- Where am I?
- How it works
- Configuration
- MCP server
- Context injection
- Prerequisites
- Building
git clone https://github.com/mgreau/zen.git && cd zen
flox activate # Go 1.25, git, gh, make — or install those yourself
make build && mv zen ~/bin/ # or anywhere on your PATH
gh auth login
zen setup # interactive: repos, authors, daemon settings
zen watch start # background daemon polls GitHub for PRs
zen inbox # see what needs your attention- Alternatively, skip
mv zen ~/binand install with Nix.
When a PR shows up in your inbox, zen review <number> opens that PR in a new terminal tab with Claude pre-armed and the PR context loaded. See Prerequisites for what to install first.
zen inbox is your daily triage. Three classes of PRs land here:
- Reviews waiting on you — PRs from configured authors, plus any explicit review requests.
- Your own approved-but-unmerged PRs — signed off, ready to land.
- Open PRs touching paths you watch — for staying aware of areas you care about.
zen inbox # everything, filtered by configured authors
zen inbox --all # from all authors
zen inbox --path pkg/sts # PRs touching specific paths
zen inbox --repo other-repo # different repo
zen inbox --ignore-drafts # skip drafts on this run (or set ignore_drafts: true in config)Drafts are shown by default. To skip drafts permanently, set ignore_drafts: true in your config — see docs/configuration.md. The --ignore-drafts flag overrides the config for a single run.
Example output:
───────────────────────────────────────────────────────────────
Legend W = Worktree
* = local worktree exists
zen review resume <number> to open | zen review <number> to create
2 Pending PR Reviews — app
Authors: alice bob charlie dave
═══════════════════════════════════════════════════════════════
PR Author Title Link
────── ──────────────────── ────────────────────────────────────────── ────────────────────────
#1042 alice api: Add pagination to ListUsers endpoi... https://github.com/acme/app/pull/1042
#1038 bob fix(auth): Handle expired refresh tokens https://github.com/acme/app/pull/1038
zen review 42 fetches the PR branch, creates a worktree, injects CLAUDE.local.md, installs the /review-pr slash command, and opens a terminal tab with Claude. When --repo is omitted, zen auto-detects by querying GitHub — if the PR number exists in multiple configured repos, it prefers the one where you're a requested reviewer or asks you to choose.
zen review 42 # create worktree + open terminal tab
zen review 42 --repo other # specify repo explicitly
zen review 42 --no-terminal # create worktree only, print command
zen review 42 --model opus # pick model (agent-specific, e.g. opus or gpt-5-codex)
zen review 42 --agent codex # use Codex for this review (overrides config)
zen review resume 42 # open existing worktree in new terminal tab
zen review resume 42 --list # list available sessions
zen review resume 42 --session 2 # resume specific session
zen review delete 42 # remove a PR review worktree (with confirmation)If the worktree already exists, zen catches it up to the current PR head and then resumes. It will not merge over local edits or a live agent. If the author force-pushed, you are asked before the checkout is reset. zen review resume offers to create the worktree if it is missing.
Same isolation model, your own branches. zen work new and zen work resume open the worktree in a new terminal tab by default; pass --no-terminal to skip.
zen work # list feature worktrees
zen work new <repo> <branch> # create new feature worktree
zen work new app my-feature "initial prompt" # with Claude prompt
zen work new app my-feature --model opus # pick Claude model
zen work resume <name> # resume a feature session in new tab
zen work resume <name> --model opus # resume with a specific model
zen work delete <name> # delete worktree (cleans Claude sessions too)Feature branches are prefixed by branch_prefix from config — see docs/configuration.md.
Different lenses on what you're doing across worktrees.
zen who-am-i # all repos, last 7 days
zen who-am-i -r app -p 30d # specific repo, last 30 days
zen who-am-i --merged # only merged & deployed PRs with descriptions
zen who-am-i --merged -r app -p 7d # merged PRs in app, last 7 daysThree sections: Merged & Deployed, In Progress, PR Reviews. Period formats: 1d, 7d, 30d, 2w, 1m. Also exposed via MCP as zen_who_am_i.
zen status # alias: zen dashboardWorktree counts, PR reviews (with remote state and cleanup ETA), feature work, and daemon state.
zen board # interactive, auto-refreshes every 30sTwo live tables:
- My Pull Requests — grouped by status (ready to merge, failing CI, changes requested, in review, in flight, draft). PRs stacked on one of your other open PRs (same repo, base branch = another PR's head branch) are shown together with a
|----marker instead of being scattered by status. - Needs Your Review — across all configured repos, unlike
zen inboxnot filtered by theauthorsconfig, so nothing slips through. Sorted into three tiers: PRs from someone in yourauthorsconfig, then PRs touching awatch_pathsentry, then everyone else — newest first within each tier.
Keys: tab switches tables, enter/o opens the selected PR in your browser, v starts/resumes a review for the selected row (same as zen review <number>), s shows/hides the first 20 lines of the selected PR's description (fetched on demand), r refreshes now, q quits.
zen reviews # PR reviews from past 7 days
zen reviews --days 30 # past 30 dayszen search 42 # by PR number
zen search oidc # by branch/name
zen search --type pr <term> # filter: pr, featurezen agent status # all worktrees
zen agent status --running # only running sessions
zen agent status --full # full token usage scan (slower)Session ID, model, token usage, and last activity per worktree.
GitHub ──── zen watch ──── Worktrees Ready ──── You Review ──────── Cleanup
PRs (daemon) (silent prep) (zen review resume) (automatic)
Two loops keep zen useful.
The automated loop is the daemon. It polls GitHub and notifies you of new review requests. Worktrees for authors: are created silently, with context already injected, and existing review checkouts fast-forward when the PR head moves. Local edits and live agents are left alone; a force-push waits for zen review, which asks before resetting. After a successful catch-up you get a quieter “PR #N updated”. Merged worktrees are removed a few days later. The daemon never opens terminal tabs.
The manual loop is yours: check what needs your attention, open a worktree in a new tab with Claude, do the work.
zen watch start # start background daemon
zen watch stop # stop daemon
zen watch status # show daemon status + last check
zen watch logs # tail daemon log output
zen watch logs search 42 # search logs for a PR, worktree, or keywordReact to a Slack message with :claudecode: (or whatever emoji you configure) and the daemon picks it up: acks in-thread, creates a feature worktree seeded with the discussion as the initial prompt, and opens it in a terminal tab right away — unlike the PR-review flow, which only prepares worktrees silently. When that session goes idle, you get a Slack DM back with a resume command and a link to the thread. Off by default; see docs/configuration.md for setup (a Slack token with a handful of scopes, via ZEN_SLACK_TOKEN).
Manual cleanup, in case you want it (the daemon handles merged PRs automatically, 5+ days after merge):
zen cleanup # find stale worktrees
zen cleanup --days 14 # custom age threshold
zen cleanup --delete # interactive deletionDaemon internals are in docs/architecture.md. After installing a new zen, restart zen watch so the new binary is what polls GitHub — existing worktrees are not rebuilt. See docs/configuration.md.
Minimal ~/.zen/config.yaml:
repos:
app:
full_name: octo-sts/app
base_path: ~/git/repo-octo-sts-app
authors:
- mattmoor
- wlynchzen setup walks you through this interactively. The daemon hot-reloads config on every poll tick — no restart needed.
By default zen drives Claude Code. Set agent: codex in config (or pass --agent codex to zen review/zen work) to use OpenAI's Codex CLI instead. zen adapts the launch/resume commands, the injected context file (CLAUDE.local.md vs AGENTS.md), where slash-command prompts are installed, and how it discovers sessions and token usage. See docs/configuration.md.
Started a review with one agent and want to continue with the other? Resume with the other agent and zen offers to carry the session over:
zen review resume 42 --agent codex # started with Claude? zen offers to migrate
zen work resume my-feature --agent claude --migrate # skip the promptWhen the selected agent has no session for the worktree but the other agent does, zen migrates the most recent one and resumes it:
- Claude → Codex uses Codex's own importer (driven over
codex app-server), so the thread is a first-class Codex session. Already-imported sessions are detected via Codex's import ledger and resumed directly. Codex only detects recent sessions (last 30 days, ~50 most recent). - Codex → Claude translates the rollout into a Claude session file: text and shell commands carry over (shell calls become
Bashtool history, other tools become annotated text); Codex's encrypted reasoning cannot be migrated. The session opens with a note that it was migrated.
Both directions are lossy where the agents' formats don't overlap — treat the migrated session as carried-over context, not a bit-perfect transcript. Full mechanics in docs/session-migration.md.
Full reference (poll intervals, terminal selection, branch prefix, multi-repo disambiguation, state file paths) in docs/configuration.md.
zen mcp serve
Speaks Model Context Protocol over stdio so a running Claude session can call zen tools directly: list worktrees, check inbox, fetch PR details, open reviews. Register once:
claude mcp add --scope user zen -- zen mcp serve
Tool inventory and usage in docs/mcp.md.
The daemon writes a CLAUDE.local.md file into each PR worktree with the PR title, author, changed files, and review instructions. The repo's own CLAUDE.md is never touched — there's no risk of accidental commits. When the PR head moves, the daemon (and zen review on an existing directory) rewrite that context automatically. zen context inject remains the manual escape hatch:
zen context inject <path> --pr 42 --repo app
| Requirement | Why |
|---|---|
| macOS or Linux | On macOS, iTerm2/Ghostty/Terminal.app tab management and notifications use AppleScript; on Linux, use kitty for tabs and notify-send (libnotify) for notifications |
| Git | Worktree creation, fetching PR branches, cleanup |
GitHub CLI (gh) |
Authentication and GitHub API access — must be logged in (gh auth login) |
| iTerm2, Ghostty, kitty, or Terminal.app | Opens review/work sessions in new tabs (iTerm2/Ghostty/Terminal.app) or OS windows (kitty). Set terminal: macos for the built-in macOS Terminal. Ghostty and Terminal.app need accessibility permissions for tab creation and fall back to new windows otherwise (see docs/configuration.md) |
Claude Code (claude) or Codex (codex) |
AI-assisted PR reviews and coding sessions — pick one with agent: in config (see Configuration) |
| Go 1.25+ | Building from source — or flox activate for a pinned toolchain (see Building) |
| Flox (optional) | Reproducible local env: flox activate then make build, or flox build zen for a hermetic binary |
flox activate # optional: pinned Go 1.25.7, git, gh, make
make build
flox build zen is the same Nix expression the flake uses, built against the Flox catalog (./result-zen/bin/zen). It does not put zen on PATH; that is still mv zen ~/bin or Install with Nix.
For people who already use Nix: a flake in this repo builds zen for Linux and macOS (packages.default, with git/gh wrapped in). This is an alternative to mv zen ~/bin, not a required dependency. zen setup still writes ~/.zen/config.yaml.
NixOS or nix-darwin (environment.systemPackages):
# flake.nix
inputs.zen.url = "github:mgreau/zen";
# configuration.nix / darwin/packages.nix
environment.systemPackages = [
inputs.zen.packages.${pkgs.system}.default
];home-manager: home.packages = [ inputs.zen.packages.${pkgs.system}.default ];
Any Nix install:
nix profile install github:mgreau/zen
# or one-off: nix run github:mgreau/zen -- versionSee CONTRIBUTING.md for testing and architecture pointers.
I was watching The Last Dance when naming this tool. Phil Jackson — the "Zen Master" — and his coaching philosophy resonated: orchestrate the system, trust the players, stay calm while everything moves around you. That's what this tool does: silently prepares worktrees, injects context, cleans up after itself, and lets you focus on the actual review when you're ready.
