⚠️ This tool is purposely built for myself and how I develop things at Grafana Labs (e.g. using Graft)
Run multiple Claude Code sessions in parallel on a single repo, each isolated in its own git worktree.
- Sessions survive terminal restarts and are automatically resumed.
- Jump into any project's shell, preview live changes with Graft, or commit and push from a single command.
- Fully isolated from your personal tmux — tulip runs its own tmux server.
tulip.mov
- tmux, or a Rex terminal
- Claude Code
The list grows to fill the terminal. Projects whose PR has merged are hidden by default (nothing is deleted); press m to show or hide them. Git status and graft state refresh in the background, so large repos with many projects stay responsive.
tulip runs each session in either tmux or Rex.
- tmux (default): sessions live in a dedicated tmux server; actions attach to it.
- Rex: used automatically when tulip runs inside a Rex terminal (
REX_SESSIONis set). The TUI stays open in its own tab, and each action opens or focuses a tab in the same Rex session:<branch> · claude,<branch> · shell,<branch> · watch. A running graft shows in the Claude tab's label (graft: activeorgraft: failed).
Force one with --backend=rex|tmux or TULIP_BACKEND=rex|tmux.
Tabs are named after the branch: the Claude tab is <branch>, and the others add a suffix (<branch> · shell, <branch> · watch). An optional per-repo prefix goes in front of the branch: tulip config tab-prefix "Synthetics: App / " gives Synthetics: App / <branch>. The prefix is used as-is, so include your own separator, and tulip config tab-prefix with no text clears it. With a prefix set, new tabs are placed right after the last existing tab with the same prefix.
curl -fsSL https://raw.githubusercontent.com/dgzlopes/tulip/main/install.sh | bashtulip # open the TUI
tulip reset # wipe all projects, sessions, and worktrees| Key | Action |
|---|---|
n |
New project |
d |
Delete selected project and worktree |
↑↓ / j k |
Navigate |
↵ |
Open project action menu |
? |
Help |
q |
Quit |
| Dot | Meaning |
|---|---|
| grey | No uncommitted changes |
| green | Uncommitted changes in worktree |
| red | Worktree error (e.g. missing path) |
| Icon | Meaning |
|---|---|
| 🐝 (green) | Yarn watch is running |
| 🐝 failed (red) | Watch exited unexpectedly |
Interact with projects by ID or branch name from any terminal:
tulip <id|branch> # interactive picker (arrow keys)
tulip claude <id|branch> # attach to the Claude session
tulip shell <id|branch> # open a shell in the worktree
tulip graft <id|branch> # start yarn watch for live preview (switches active graft)
tulip graft-debug <id|branch> # attach to the graft watch output
tulip graft-urls # list the Grafana Cloud pages Graft is configured to preview on
tulip vscode <id|branch> # open the worktree in VS Code
tulip publish <id|branch> <msg> # stage all, commit (signed), and push
tulip config graft-command <cmd> # set the command run when grafting (default: yarn install && yarn run watch)
tulip config graft-domain [domain] # set the default Graft domain (opens a picker if domain is omitted)The ↵ action menu also provides: difit (visual diff, requires difit) and open-pr (open PR in browser).
Every claude session tulip starts is launched with --chrome, so it can use Claude in Chrome to look at whatever Grafana Cloud page you have open — including a live Graft preview of that session's own branch. tulip graft-urls reads Graft's own global config (~/.config/graft) and shows every domain it's currently set up to intercept, plus its interception/plugin state; tulip graft <id|branch> prints the same list right after switching the preview. If more than one domain is enabled, tulip config graft-domain marks (and lets you change) which one is treated as the preview URL for this repo — shown first in graft-urls and used for the notification when grafting finishes.
MIT