A Rust replatform experiment for a Claude Code session workspace. Native, terminal-multiplexer-style (tabs + splits + keyboard driven), with the quality bar the predecessor lacked.
Inspired by doctly/switchboard, the
Electron app it replatforms; see docs/background/ for
the full reasoning archive.
This is an early scaffold. Status, scope, and design live in:
docs/PRD.md— Product Requirements Documentdocs/ARCHITECTURE.md— ArchitectureROADMAP.md— feature buckets (MoSCoW)CHANGELOG.md
Each tagged release publishes desktop installers on the Releases page. Pick the one for your platform:
- macOS — download
TermHerd_<version>_<arch>.dmg, open it, and drag TermHerd into Applications. The build is not yet notarized (signing is pending, see the roadmap), so on first launch right-click the app and choose Open, or clear the quarantine flag:xattr -dr com.apple.quarantine /Applications/TermHerd.app. - Windows — run the
*-setup.exe(NSIS installer). Because it is unsigned for now, SmartScreen may warn — choose More info → Run anyway. - Linux — install the
.deb(sudo apt install ./termherd_<version>_amd64.deb), or download the.AppImage,chmod +xit, and run it directly.
Prefer a bare command-line binary? The same releases carry one-line installers
that drop termherd into your Cargo bin directory:
# macOS / Linux
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/Termherd/termherd/releases/latest/download/termherd-installer.sh | sh# Windows
powershell -c "irm https://github.com/Termherd/termherd/releases/latest/download/termherd-installer.ps1 | iex"Linux release binaries carry a sigstore keyless build-provenance attestation
(no signing key — the signer is the release workflow, via GitHub OIDC, logged
in the public Rekor transparency log). Verify a download with the gh CLI:
gh attestation verify termherd-x86_64-unknown-linux-gnu.tar.xz \
--repo Termherd/termherdA successful check proves both integrity and that the artifact was built by
this repository's CI. A SHA256SUMS file is also attached to each release.
cargo run -p termherd-appOptional user settings live in ~/.termherd/settings.json (on Windows,
%USERPROFILE%\.termherd\settings.json). The file is read at startup; if it
is missing or invalid, TermHerd falls back to defaults rather than refusing to
start. There is no in-app settings panel yet — edit the file and restart.
{
"shell": { "program": "pwsh", "args": [] },
"theme": "dark",
"close": { "tab": "confirmWhenActive", "app": "confirmWhenActive" },
"keys": {
"copy": "ctrl+c",
"paste": ["ctrl+v", "ctrl+shift+v"],
"next-tab": "ctrl+tab",
"activate-tab-1": "ctrl+1"
}
}shell— the shell launched for each session. Omit it (or set it tonull) to use the platform default login shell;argsis optional.theme—"dark"(default) or"light", for the GUI chrome (sidebar, tab strip, buttons). The terminal grid keeps its own colours.close— whether closing prompts first, set per action.tab(closing a tab) andapp(quitting) each take one of:"alwaysConfirm"(always ask),"confirmWhenActive"(ask only while a session is running a foreground process — a working shell or any live Claude; an idle shell closes/quits silently), or"noConfirmation"(close immediately). Both default to"confirmWhenActive"; either field may be omitted to keep its default.keys— keyboard overrides. Each entry binds an action to one chord or a list of chords ("ctrl+shift+c", order/case-insensitive; modifiersctrl,shift,alt,cmd). An entry replaces that action's default; unspecified actions keep their per-platform defaults. Unknown actions and bad chords are ignored. Actions:copy,paste,next-tab,prev-tab,close-focused,focus-search,toggle-sidebar(hide / restore the session browser, Ctrl/Cmd+B),scroll-top/scroll-bottom(top / bottom of the scrollback, Ctrl/Cmd+Up / Ctrl/Cmd+Down),new-shell-here(a shell in the focused session's directory, or home when nothing is open, Ctrl/Cmd+T),new-claude-session-here(a fresh Claude session in the focused session's repo, Ctrl/Cmd+Alt+T),reopen-closed-tab(restore the last closed tab, Ctrl/Cmd+Shift+T), andactivate-tab-1…activate-tab-9(jump straight to the Nth open tab).
Window size and position persist separately to ~/.termherd/window.json (a
position left off every connected monitor — e.g. on a screen since unplugged —
is dropped so the window re-centers instead of opening out of reach), and
session stars / archives / custom titles to ~/.termherd/metadata.json (an
overlay — TermHerd never writes under ~/.claude). Star (★), archive (⊟) and
rename (✎) are buttons on each sidebar row.
All shortcuts are configurable via the keys section of the config file
(above); the table lists the defaults. With a terminal focused:
| Action | Windows / Linux | macOS |
|---|---|---|
| Copy selection | Ctrl+Shift+C |
Cmd+C |
| Paste | Ctrl+V / Ctrl+Shift+V |
Cmd+V |
| Next / prev tab | Ctrl+Tab / Ctrl+Shift+Tab |
(same) |
| Jump to tab 1–9 | Ctrl+1 … Ctrl+9 |
Cmd+1 … Cmd+9 |
| Scroll top/bottom | Ctrl+Up / Ctrl+Down |
Cmd+Up / Cmd+Down |
| New shell here | Ctrl+T |
Cmd+T |
| New Claude here | Ctrl+Alt+T |
Cmd+Alt+T |
| Reopen closed tab | Ctrl+Shift+T |
Cmd+Shift+T |
| Close tab | Ctrl+W |
Cmd+W |
| Focus search | Ctrl+F |
Cmd+F |
| Interrupt (SIGINT) | Ctrl+C |
Ctrl+C |
Jump-to-tab (Ctrl/Cmd+1–9) is matched by physical key position, so it
lands on the same number-row keys on every layout — including AZERTY and QWERTZ,
where those keys produce &, é, … without Shift.
Dragging a selection with the mouse also copies it on release, and the wheel
scrolls back through history. In the sidebar, click a project or session to
open it; a tab's × also closes it. Hovering a tab shows the session's fuller
description (the same card the sidebar shows).
termherd-mcp is a small MCP server that
exposes termherd's own configuration to a Claude session, so you can ask "what
can I configure here?" from inside the conversation termherd already hosts. This
first slice is read-only: one tool, list_options, and the option schema
as a resource, both reflecting ~/.termherd/settings.json. Writing settings
(set_option) and workspace orchestration (open session, split, focus, …) are
planned follow-ups (F-mcp-control-surface, #90).
It speaks JSON-RPC over stdio. Register it with Claude Code by adding it to your
mcpServers config (point command at the built binary):
{
"mcpServers": {
"termherd": { "command": "/path/to/termherd-mcp" }
}
}Build the binary with cargo build -p termherd-mcp (it lands in target/).
cargo test --workspacecargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo deny check # if cargo-deny installedPinned to rust 1.95.0 via rust-toolchain.toml. Edition 2024.
crates/core — domain, headless App, workspace (pane tree), keymap, ports
crates/claude — Claude CLI format codec (path encode/derive, JSONL) [pure]
crates/app — iced GUI shell (M3+); currently a tracing+single-instance stub
The hexagonal dependency rule: app → core ← adapters (and core →
claude). core depends on nothing concrete.