Repository navigation
0.1.0-prerelease.5 - 2026-09-26
Pre-release
Pre-release
Release Notes
Fixed (the copy chord no longer overwrites a program's own copy)
- In a pane whose program reads the mouse — Claude Code in full-screen mode,
vim, lazygit — the copy chord used to write the last text termherd had copied
over the clipboard, on every press, once anything had been copied anywhere:
the program's own copy of a drag was silently replaced by stale text (#316).
The shell no longer keeps a copy cache; the terminal reports whether it holds
a selection at all (including one scrolled out of view), and the chord asks
it to copy or does nothing. Over MCP,copywith nothing selected now
reportsinert/no-contextin that pane too.
Fixed (the stdio server could choose what termherd executes)
set_optionlistedshell.program/shell.argsas writable beside
theme— and their value is what every shell session runs at the next
launch, so any client oftermherd-mcpcould set it unattended. Both are now
read-only over MCP;list_optionsand the schema resource carry a
writableflag per id, so a model can tell before it tries (#283).- A value that does not fit an option's kind (a non-array palette, a theme
outsidedark/light) is now refused with a JSON-RPC error and nothing
is written. It used to be written anyway, with a warning in the answer; that
warningstext is gone.
Fixed (the mouse reaches a program that reads it)
- A full-screen program with mouse reporting on — Claude Code's
/diffand
/resume, vim, lazygit, fzf, less, tmux — now receives clicks, drags and
(when it asked for them) bare moves, in the SGR or X10 encoding it
negotiated. Only wheel notches were ever forwarded before; a press over such
a program was consumed as a local text selection and the program never heard
of it (#155). While a program reads the mouse the terminal draws no selection
of its own and a right-click reaches the program instead of pasting; hold
Shift to select text out of it, and the link modifier still opens a path
or URL. mouse_in_sessionanswersforwardedfor such a program, beside
selectionandignored, and an agent can now drive a mouse-mode TUI
through the act → wait → observe loop. It rejects a session that has
exited rather than claiming its terminal did something.- A drag that leaves the pane still ends with its release, on the border
cell; a modifier pressed or released mid-drag no longer hands half the
gesture to the other owner; and motion is reported to the program once
per cell rather than once per pixel.
Added (a pointer inside a terminal, over MCP)
mouse_in_sessionplaces one mouse event at a cell of a session's
terminal — the pointer counterpart ofrun_in_session(#300).kindis
press/release/click/drag/move,col/roware 0-based
cells of the visible screen,buttonis optional. A cell
outside the pane's geometry, or an unknown word, rejects the whole call
before anything applies. The answer'spointerfield says what the terminal
did:selection(a press then a drag selects the text between them, both
cells included; a bare click clears it) orignored.- The event travels the wheel's path end to end (
Event::TerminalPointer→
PtyHost::pointer→ the terminal thread); the gesture rule lives once in
core, read by the shell to answer and by the terminal to place the
selection with its live offset. It landed before #155 (above) so that fix
could be verified by the agent that wrote it.
Added (hidden terminal hyperlinks)
- A link whose label hides its URL — an OSC 8 hyperlink, the way
gh,
ls --hyperlinkand rendered markdown print#76over an issue URL — now
underlines on hover and opens on Cmd/Ctrl+click like a printed URL (#84).
TheScreensnapshot carries every hyperlink as a span over its cells, and
the pointer answers that span before it scans the visible text; a
hyperlink laid over text that is itself URL-shaped opens the hyperlink.
Fixed (list_sessions over MCP)
list_sessionsanswered a bare JSON array, which MCP clients reject on their
schema check — every call failed before the caller saw a row. The sessions now
ride in asessionsfield, like every other tool's answer (#299).- Every tool now shapes its structured answer at one seam, which refuses
anything but an object, and a sweep reads the tool list from the router so a
tool added later cannot slip past the rule.
Added (a repository in the sidebar, before it has any session)
- A sidebar row is no longer born only from a session. + Add a repo opens
the native folder picker, and dropping a folder on the window does the same
thing — a dropped file is ignored (#279). The row carries the usual$and
🤖 launch buttons, saysNo sessions yetuntil it has one, and sorts to the
top until then. A ✕ takes it back out; it appears only on rows you added,
since a discovered project has no declaration to drop. - Hand-added repositories live in
~/.termherd/metadata.json, beside stars and
renames. Nothing is written under~/.claude. - Over MCP:
add_repoandforget_repo, answering about the sidebar row —
membership, not what the window happens to be drawing — anddeclaredon
each project insnapshotand in the ⌘⇧S capture.
Changed (clipboard mouse gestures)
- The two classic terminal clipboard conventions are settings now, both off by
default:terminal.copy_on_selectandterminal.paste_on_right_click(#36).
Copy-on-select changes behaviour. It was already there and
unconditional — a drag release and a double-click both copied — so what this
adds is the off position, and off is the default. Set
"terminal": { "copy_on_select": true }to keep what you had. A value that
is neithertruenorfalseleaves its own gesture off with a warning
rather than failing the whole file, like a malformed colour. - The right-click paste is addressed to the pane under the pointer, not the
focused one, and focuses it; a confirmation prompt refuses it exactly as it
refuses the paste chord.
Fixed (copy chord)
- The copy chord reads the terminal's live on-screen selection, and prefers it
to the cache of the last copied text (#36). The cache was previously the only
thing it read, and only an automatic copy filled it — so with copy-on-select
off, a mouse selection could not be copied by any means. Preferring the live
highlight also fixes the older half of the bug: with a stale cache and a
fresh selection, the chord used to copy what had been copied before.
Fixed (nested launches)
- A shell opened by a termherd that was itself launched from a termherd shell
now starts (#244). The shell integration hands zsh a privateZDOTDIR, so
everything descending from that shell — including another termherd — inherited
it and read it as the user's own home. Session ids restart at 1 with each run,
so the inherited directory was usually the same one the new session was
about to write: its generated.zshenvsourced itself and the shell died on
job table full or recursion limit exceededwithout reaching a prompt,
leaving its session onstarting. Where the ids differed it started, but
replayed another session's hooks and never reached the user's own.zshrc.
A directory termherd generated is no longer replayed from — recognised by its
name, since a nested instance can run under a differentTMPDIR— and the zsh
recipe now exportsTERMHERD_ORIG_ZDOTDIRbeside the private one, so a
ZDOTDIRthe user set themselves survives any depth of nesting rather than
degrading to$HOME.
Fixed (session status)
- Sessions no longer sit on
startingforever, which madewait_for_status
settle only by expiring and left the close confirmation unarmed for a shell
(#236). Two independent holes, both reproduced outside termherd on a raw PTY,
so neither was an artefact of a debug build:- A plain shell had no status source at all. The fold only understood
Claude's dialect (glyph titles, OSC 9), and a shell emits none of it. Every
session now runs with an OSC 133 shell-integration snippet — zsh via a
privateZDOTDIR, bash via--rcfile, fish via--init-command, each
replaying the user's own startup files first — and its prompt/command marks
fold into the sameSessionStatus. Where the injection cannot apply (an
unknown shell, an unwritable temp directory) the PTY's foreground process
group stands in, retired for good by the first mark or Claude signal so it
can never contradict what the terminal says about itself. The bash and fish
recipes need a command-line argument, which is never added to the platform's
default program — it would demote the login shell to an ordinary one and
change which startup files run — so those two are integrated only when the
shell is named in the settings; zsh needs no argument and always is. ConPTY
exposes no foreground group, so that fallback is silent on Windows. - The Claude channel was switchable off by the user. With
CLAUDE_CODE_DISABLE_TERMINAL_TITLEset in~/.claude/settings.jsonthe
CLI emits no OSC title at all — and thatenvblock outranks the
environment termherd spawns with, so exporting the variable back had no
effect. A Claude launch now passes a private--settingsoverlay, which
outranks it in turn and merges with (never replaces) the user's settings.
--settingsarrived in Claude Code 1.0.61, which the README now states as
the CLI floor for launching Claude sessions — an older one rejects the flag
and the launch fails rather than merely losing its status.
- A plain shell had no status source at all. The fold only understood
- The CLI's own product name (
✳ Claude Code), which it reports as its title
until it has something session-specific to say, no longer renames the hosting
tab — a tab would have traded its project name for the program's. - A session's private temp files — its shell-integration directory, its mcp
config with the live bridge's bearer token, its settings overlay — are now
deleted when the session is torn down instead of accumulating for good.
Added (tab reorder)
- Open session tabs can be reordered by drag-and-drop (#25, FR5). Press a tab
and drag it onto another slot; the carried tab fades and the drop slot is
outlined, and the reorder commits on release (a plain click still just
activates the tab). The order lives solely incore::Workspace::move_tab(a
pure mutator that keeps the active tab pointed at the same tab); the iced tab
strip holds only transient pointer state, so there is no rival tab tree.
Fixed (scroll proptest)
- Relaxed the
emitted_lines_never_drift_more_than_one_linescroll proptest
(#98): it reconstructed the cumulative input inf32, so rounding noise over
a long delta stream could nudge the drift just past one line (~1e-6) and fail
a sound accumulator. It now sums inf64with a small epsilon; the found
counterexample is kept as a regression seed.
Added (search snippet)
- Search results now show what matched (#58). A content hit (in a session's
first prompt, indexed transcript text, or slug) renders the matched line in
muted text under the session row, windowed around the hit and clipped to the
sidebar; title-only hits stay single-line. The location is computed by a pure
core::browser::content_snippetreturning the line plus the match's byte
range, unit-tested for content/title/slug hits and long-line truncation.
Fixed (tab status)
- Tab activity dots (and the focused-terminal badge) stayed frozen on the
status a session had when it opened (#18). Claude CLI only emits its
busy/idle/attention OSC status stream when it detects iTerm2 as the host, so
the PTY adapter now advertisesTERM_PROGRAM=iTerm.app(with a version) on
every spawned session. The headless status plumbing was already correct; it
simply never received any signals to react to.
Added (terminal links)
- Terminal links are now clickable (#28). URLs in terminal output
(http/https/file/ftp) are detected per row; holding Ctrl/Cmd
underlines the link under the pointer and shows a hand cursor, and
Ctrl/Cmd+click opens it in the OS default handler. Detection is a pure
core::linksscan (trailing punctuation and unbalanced brackets trimmed);
the open is a newEffect::OpenUrlperformed by the shell.
Install termherd-app 0.1.0-prerelease.5
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/Termherd/termherd/releases/download/v0.1.0-prerelease.5/termherd-app-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/Termherd/termherd/releases/download/v0.1.0-prerelease.5/termherd-app-installer.ps1 | iex"Download termherd-app 0.1.0-prerelease.5
| File | Platform | Checksum |
|---|---|---|
| termherd-app-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| termherd-app-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| termherd-app-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| termherd-app-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| termherd-app-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |