A terminal UI for managing Git worktrees, inspired by forest for macOS by @ricwo.
forestui brings Git worktree management to the terminal with a TUI built on ratatui, featuring deep integration with Claude Code.
- Repository Management: Add and track multiple Git repositories
- Worktree Operations: Create, rename, archive, and delete worktrees
- TUI Editor Integration: Opens TUI editors (vim, nvim, helix, etc.) in tmux windows
- Claude Code Integration: Track and resume Claude Code sessions per worktree
- Session Management: See which sessions are live in a tmux window, rename, pin, and delete sessions, and read each conversation's branch, token totals, and estimated cost off its card
- GitHub Issues: Create a worktree straight from an issue assigned to you
- Multi-Forest Support: Manage multiple forest directories via CLI argument
- tmux Native: Runs inside tmux for a cohesive terminal experience
- Single Binary: No runtime, no virtualenv — one static executable
- tmux
- gh (optional, for GitHub integration)
- Rust 1.88+ (only if you build from source)
Downloads the prebuilt binary for your platform and verifies its published checksum before installing.
curl -fsSL https://raw.githubusercontent.com/flipbit03/forestui/main/install.sh | shcargo install forestui --lockedPrebuilt binaries are published for:
| Platform | Target |
|---|---|
| Linux x86_64 | x86_64-unknown-linux-musl |
| Linux aarch64 | aarch64-unknown-linux-musl |
| macOS Apple silicon | aarch64-apple-darwin |
The Linux binaries are statically linked, so they carry no glibc floor and run
anywhere. Intel Macs and every other platform build from source with
cargo install forestui.
forestui keeps itself up to date. It checks for a new release in the background after the UI is up — never blocking startup — and tells you once a newer version is in place:
forestui v2.0.1 installed — restart to use it
A binary installed from a release replaces itself. One installed with
cargo install reports the new version instead of recompiling underneath you,
so you can update when it suits:
cargo install forestui --lockedPass --no-self-update to skip the check entirely. The result is cached for a
day, so this is not a network call on every launch, and a build from source
(version 0.0.0) never updates itself at all.
Migrating from the Python build. forestui was a Python/Textual application through v0.9.x. The Rust rewrite reads the same config files, so your repositories, worktrees, and settings carry over untouched. Remove the old install so the new binary wins:
uv tool uninstall forestui.
One installable plugin gives forestui two abilities: shared names and
liveness. Each tmux tab and the Claude session inside it stay under one name —
a session opened from forestui is named after its tab from the moment it
opens, renaming the tab renames the session, and /rename inside Claude
renames the tab; the name is used verbatim, no prefix added or stripped, and
resuming keeps it. And every session on the machine reports where it is
running, so forestui sees sessions it did not start — a claude -r typed
into a hand-made window, or a terminal with no tmux at all.
Every session card shows what forestui knows about the conversation: the
branch it last saw, its token totals with an estimated cost, and — when the
session is running — a live badge: the window's name when it sits in a tmux
window forestui can jump to, or "live elsewhere" when the plugin's heartbeat
finds it running in some other terminal entirely.
Resuming a session that is already open offers to
switch to its window instead of starting a second Claude on the same
transcript. Cards also carry Rename (a live session renames through its tmux
window, a stopped one through its transcript), Pin (pinned sessions stay at
the top of the list however old they get; reorder them with K/J while the
cursor is on a pinned card), and Del (permanent, confirmed, and refused
while a window holds the transcript open).
forestui --claude-plugin status # what an install would write
forestui --claude-plugin install
forestui --claude-plugin uninstallThis installs a Claude Code plugin as its own directory. Your
~/.claude/settings.json is not modified and your existing hooks are
untouched. Only windows forestui opened are affected — a claude you start
yourself, in or out of tmux, is left alone.
# Start with the default forest directory (~/forest)
forestui
# Start with a custom forest directory
forestui ~/my-projects
# Show help
forestui --help
# Stop asking the terminal to report pointer movement, or focus changes
# (see "If tmux's prefix key stops working")
forestui --no-hover
forestui --no-focus-eventsFocus moves between the sidebar and the detail pane with Tab. Inside either
pane, ↑/↓ move and Enter activates.
| Key | Action |
|---|---|
Tab |
Switch focus between sidebar and detail pane |
↑ / ↓ |
Move within the focused pane |
Enter |
Select a row / activate the focused control |
a |
Add repository |
w |
Add worktree |
e |
Open in editor |
t |
Open in terminal |
o |
Open in file manager |
n |
Start Claude session |
y |
Start Claude session (YOLO mode) |
h |
Toggle archive on the selected worktree |
A |
Show or hide the archived section |
d |
Delete |
s |
Settings |
r |
Refresh |
q |
Quit |
In modals: Tab / Shift+Tab move between fields, Enter activates, Esc
cancels. Confirmation dialogs also accept y and n. The custom-buttons
manager uses a add, e edit, d delete, K / J reorder, s save.
The mouse works everywhere the keyboard does. Controls light up as the pointer crosses them, so what is clickable is visible rather than guessed at:
- Click a repository or worktree in the sidebar, a control in the detail
pane, a field to focus it, any modal button, or a key in the footer bar —
clicking
s Settingsthere is the same as pressings. - The
▼/▶twisty beside a repository folds its worktrees away without changing what is selected. - The scroll wheel scrolls whichever pane the pointer is over, whether or not it holds the keyboard focus.
- The scrollbar can be dragged, and clicking its track pages to that point.
tmux drops a pending prefix when the next thing it receives is a key it cannot
find in its prefix key table, and a mouse or focus report is such a key. So
C-a followed by one of those silently does nothing, and the key you typed
after it is lost.
forestui asks the terminal for two reports that can land in that gap. Either can be turned off:
| Flag | You lose | The prefix stops being eaten by |
|---|---|---|
--no-hover |
controls no longer light up under the pointer; clicks, wheel and drag still work | pointer movement (?1003h), while forestui's pane is active |
--no-focus-events |
no refresh the moment you return to the window — r and the 30-second sweep still do it |
focus changes, in every window on that tmux server |
focus-events is a tmux server option, and forestui turns it on. It hands it
back on exit, including after a run that was killed before it could — but if
your own tmux config turns it on, it stays on and the flag cannot help with
that half.
When your default editor is a TUI editor (vim, nvim, helix, nano, etc.),
forestui opens it in a new tmux window named edit:<worktree>. This keeps your
editing session organized alongside forestui and any Claude sessions.
Supported TUI editors: vim, nvim, vi, emacs, nano, helix, hx,
micro, kakoune, kak
forestui stores its state (.forestui-config.json) in the forest directory
itself, so you can manage multiple independent forests:
forestui ~/work # Uses ~/work/.forestui-config.json
forestui ~/personal # Uses ~/personal/.forestui-config.jsonUser preferences (editor, theme, branch prefix, custom Claude buttons) are
stored globally in ~/.config/forestui/settings.json.
Settings → Theme opens a picker over 32 named palettes — Dracula, Nord,
Gruvbox, Solarized, the Catppuccin and Rosé Pine and Tokyo Night families,
GitHub, SynthWave '84, and more — with the app behind the dialog live-previewing the
highlighted theme. Enter applies, Esc reverts, Save persists. The default,
Forest Dark, is the palette forestui has always had. The chosen theme is
stored in theme_name; the legacy theme field (the old inert
System/Dark/Light choice) is preserved untouched so the settings file keeps
working in the Python build too.
Settings are stored in ~/.config/forestui/settings.json:
{
"default_editor": "nvim",
"default_terminal": "",
"branch_prefix": "feat/",
"theme": "system",
"theme_name": "forest-dark",
"custom_buttons": [
{
"label": "Opus",
"prefix": "opus",
"command": "claude --model opus"
}
]
}Press s in the app to open the settings modal.
Custom Claude buttons add extra entries to the CLAUDE section of the detail
pane. Each one opens a tmux window named <prefix>:<worktree> running its
command verbatim. A command containing --dangerously-skip-permissions is
styled red.
# Clone and enter the repo
git clone https://github.com/flipbit03/forestui.git
cd forestui
# Install the toolchain components
make dev
# Run checks (format, clippy, typecheck, tests)
make check
# Format code
make format
# Run the app
make runSee CLAUDE.md for AI-assisted development guidelines, and
doc/rust-rewrite/ for the specification, architecture,
migration plan, and the tu-driven acceptance playbook.
forestui is designed to coexist with forest for macOS:
- Both apps can share the same
~/forestdirectory for worktrees - Each app maintains its own state file:
- forest:
.forest-config.json(stored in~/.config/forest/) - forestui:
.forestui-config.json(stored in the forest folder itself)
- forest:
- Worktrees created by either app work seamlessly with both
Key difference: forestui stores its state inside the forest folder
(~/forest/.forestui-config.json) rather than in a global config directory.
This design enables multi-forest support — you can run forestui ~/work and
forestui ~/personal with completely independent state for each.
MIT
