Releases: devsuitup/switchboard
Release list
0.0.87
New
- A remote host's project header now shows what the host supports: hover its status dot to see the highest capability reached (observe, liveness, inject, attach) and, for each one above it, why it is missing. (#218)
- A session's Changes panel also lists the changes in the worktrees its subagents are working in, under a header naming the agent and its branch. Those rows open as read-only diffs; a subagent that works in the session's own directory adds nothing. (#303)
- A live session on a remote host that is not open in a terminal has a Send a prompt… button on its row: type a text and it is written to the running session as a new prompt, without attaching. It needs
ncator an OpenBSDncon the host, and is refused for a Windows host. The dialog says "Sent": the session's own status shows whether it picked the prompt up. (#219) - A remote session that is not open in a tab and waits on a dialog on its host, such as a permission prompt or a question, shows the orange attention state, and its status line says what it waits for. It appears and clears with the next refresh of the host. (#394)
Changed
- After three failed refreshes of a remote host in a row, a row that would have attached opens its transcript and says why in its tooltip, instead of failing when clicked. Stop is never disabled: it runs its own ssh. (#218)
Fixed
- A step of a trigger chain, the first one included, is no longer typed while the CLI reads busy or waiting on a dialog: it waits for the CLI to be at its prompt, up to the step's deadline, then fails cleanly with a reason instead of being written; a step whose Enter did not start a turn is retried once, or stops the chain when that retry is withheld because the CLI is busy or waiting on a dialog, or you have typed input pending, and is reported as "not confirmed submitted" instead of "sent". Without a readable CLI descriptor a step is still written as before, but no longer once its own deadline has passed. Single triggers are not covered. (#407, #360)
- After an upgrade, schedules keep running in a project that has settings of its own and in a git checkout that already holds a schedule; any other project, opened before the upgrade or not, runs no schedule until you open a session in it or add it. (#385)
- Stopping a terminal twice in quick succession, or resizing it while it is being stopped, no longer closes the Windows pseudo console twice, which could kill the whole app with no error. (#405)
- A sandboxed session, or a sandboxed schedule, whose Additional Directories include a
.claudeor.gitdirectory, or a path inside one, is now refused instead of binding it read-write over its read-only protection; add the project directory instead. A session started in a.claudeor.gitdirectory is refused too, except below.claude/worktrees, and Additional Directories naming your home directory or a parent of it are refused however the path is written. A relativeadd-dirsentry in a schedule is taken from the schedule's directory. (#385) - A session that has exited no longer keeps a busy dot in the sidebar, and the status bar's running count drops as soon as the session ends instead of waiting for the next refresh. (#375)
- Quitting, closing the window or reloading while a file in the file panel has unsaved edits now asks first, in any session, kept-aside tabs included: Save writes them (a file that changed on disk is not overwritten), Discard drops them, Cancel stays. If Switchboard does not answer within a few seconds, it closes anyway. (#373)
- The IDE Emulation label in a session's terminal header now says whether the CLI is connected: it reads "IDE Emulation" only while it is, "IDE Emulation: waiting for CLI" when Switchboard is listening but the CLI has not connected, and "IDE Emulation: failed" when it could not start for that session, with the reason in its tooltip. A session whose IDE Emulation port was already taken no longer shows the label as if it worked. (#320)
- On Windows, the file panel no longer opens or saves a credential file (such as one under
.ssh) through its 8.3 short name or a\\?\path. (#390)
Full changelog: v0.0.85...v0.0.87
0.0.85
New
- After an update, a "What's new" dialog lists the changes of every version since the one you last ran. Help → What's new opens it again for the current version. (#363)
- An untracked file's line counts show in the Changes list without opening it, and count in the header total. A row that cannot be counted says why: binary, too large, not counted, or count on open for a remote session. (#350)
Changed
- The session header's controls sit in one row with one look: Sandbox and IDE Emulation are plain indicators, Shell and Changes are icon buttons that show when they are on, and Stop comes last. (#348)
- Whether the session runs is a dot before its name, and its tooltip says how the process ended: stopped, killed, or exited with a code. (#348)
- The Changes panel and its editor use the app's own look: icon buttons, the dark surfaces of the sidebar, a badge for each file's state, and softer diff colours. (#351)
Fixed
- Saving from the file panel, the Memory panel or the MCP diff tab no longer overwrites a file that changed on disk since you opened it. You are told, and asked before your edits replace it. (#355)
- When an open file changes on disk, the panel reloads it if you have no unsaved edits, and otherwise keeps them and offers Reload or Keep my edits. It keeps noticing changes after the file is replaced, deleted or recreated. (#355)
- Switching tabs or sessions no longer drops a file tab's unsaved edits. (#355)
- A save that fails says "Save failed" instead of failing silently, including one that finishes after you switched to another tab. (#355)
- Once an MCP diff has been accepted or rejected, its tab's Save button is disabled, and its tooltip says why. (#355)
- Undo no longer brings back an edit made in another file, or before the file was reloaded. (#355)
- The Changes panel works when a session's directory is below the repository root; tracked files' diffs came back empty there. (#350)
- A file with a merge conflict is badged
U(Unmerged) in the Changes list, instead of Added or Deleted. (#351) - A plain terminal no longer types its
claudeshim into the shell, so the line stays out of your shell history and off the screen. (#352) - Archiving a session no longer moves its subagents into "Orphan subagents". (#354)
Full changelog: v0.0.84...v0.0.85
0.0.84
New
- The window draws its own title bar. The frame and the menu bar are gone: the sidebar's top row of icons is the title bar, with a ☰ button for the menu, and the window controls sit in the top-right corner. The window gains 67 pixels of height on a 1440-pixel screen. Every menu shortcut still works. (#338, #341)
- Zoom from the keyboard works on any layout. Ctrl+- and Ctrl+0 on AZERTY, and the numeric keypad's +, - and 0, now zoom. (#338)
Fixes
- A session running in another process is no longer resumed automatically. That covers another Switchboard instance or a CLI in a terminal. On a reload or a working-set restore, the session is skipped and kept in the saved set, and a notice names the process. Opening it by hand asks for confirmation first. (#337, fixes #331)
- The Windows test flake on a capped git read is fixed at its cause: the git process is now drained instead of killed. (#336, fixes #332)
0.0.83
New
- Activity reporting to ActivityWatch (opt-in, off by default — Settings → Activity Reporting). Two separate buckets on the local ActivityWatch server: the session you are looking at (
aw-watcher-switchboard_<host>, shown in ActivityWatch's Editor view) and every session that runs, scheduled runs included (aw-watcher-switchboard-running_<host>). Each event carries the project, the session id and its title; running sessions are updated every minute. No prompt or transcript content is sent. (#326)
Fixes
- A right-click in a session's terminal no longer pastes the clipboard into Claude Code's prompt outside Native mode, and the terminal keeps its focus. (#330, fixes #329)
- The activity trace no longer leaves a rotated segment behind when a stream's open lands after the prune (Windows). (#328, fixes #323)
- A test no longer fails on a host whose
~/.claude/CLAUDE.mdis a symlink. (#333)
0.0.82
Any path in terminal output is now a link
Print a filename, a relative path, an absolute path or a path:line anywhere in a terminal — from a build log, grep -n, a stack trace, or an agent saying what it just edited — and it opens in the side panel, at the right line.
Bare filenames count too, extensionless ones included: Makefile, LICENSE, README. The consequence is deliberate — in a project with a file named plan, the word "plan" becomes a link.
What decides the underline is not what the text looks like, it is whether the file can actually be opened. Every candidate is checked in the main process first — the path is resolved on disk, run past the same denylist the file panel already enforces, and rejected if it is a directory, a device, larger than the panel's ceiling, or binary. Only what survives becomes a link. The alternative — underline on shape, fail on click — teaches you that an underline means nothing, and that is the defect this is built against.
Relative paths resolve against the session's own recorded working directory, in the main process. The renderer sends the session id and the matched text, and never learns where a session lives; it gets an absolute path back only for a file that has already been accepted. A remote session is refused, because its paths name files on another host. A shell opened inside the file panel resolves through the session that owns it.
Existing file:// links and http URLs are untouched — they keep their current handlers.
One table of setting defaults
The main process and the settings panel each carried their own defaults, and they disagreed on three settings. The visible one: IDE emulation showed as on while sessions were being started with it off, because the panel defaulted it to true and the process that spawns the session defaulted it to false. Permission mode and the visible-session count diverged the same way.
There is now one table, public/setting-defaults.js, read by both. A control that shows you a value now shows the value a new session will actually get.
Also in this release
- The Changes panel's file watchers are released when the window closes, instead of leaking.
- The sidebar no longer scrolls invisibly under the status bar.
- The PTY debug lines are built only when debug logging is on.
docs/automation.mdnow says that a trigger chain'stimeout_msis one budget for the whole chain rather than one per step, and how to read a truncated chain'ssteps[]— deriving the resume point fromsteps_completedsends the first command twice.docs/testing-a-pr.mdcovers testing a packaged build, and the hot-reloader trap that makes an edit inside the app's own repository look like a bug in the app.
Full changelog: v0.0.81...v0.0.82
0.0.81
What's changed
The right-hand panel stops being a place you read and becomes a place you work. It lists everything a session changed — staged, unstaged and untracked alike — lets you fix a file without leaving it, and carries a shell running in that session's own directory.
Edit a changed file where you review it
Click a changed file and it opens in an editor below the list, with the diff recomputed as you type. The list stays on screen, so moving between files is one click and there is nothing to navigate back from — the editor has a Close.
- Three views, remembered per panel: inline (the default), side by side, and plain with no diff at all. The panel's choice is its own; the MCP diff tab keeps yours.
- Save is inactive when there is nothing to save, and
Ctrl/Cmd+Sworks in all three views. - A file link in the terminal opens the diff rather than the plain viewer, whenever the file is one the session changed.
The editor refuses rather than guesses, because it sits next to a session writing the same files:
- A save that would land on a file which changed underneath you is refused, not merged — you are told, and the editor can re-read.
- Line endings, a byte-order mark and the file's encoding are preserved exactly. A file that genuinely mixes CRLF and LF is refused instead of being silently normalised, which would have manufactured a whole-file diff on a file nobody touched. A file that is not valid UTF-8 is refused instead of being written back with replacement characters.
- Oversized and binary files are refused, never truncated.
- Nothing under
.git/is editable, a row that is a symlink is refused, and so is a file with more than one name on disk — a hard link inside the repository can point at a file outside it, and saving through it would change that file too.
If the session takes the panel over while you are typing, your edits are kept and restored with a notice, not discarded.
A shell under the file list
The panel carries a terminal below the Changes list, both visible at once with a handle between them (#300). It runs in the session's own resolved working directory — worktree included, the same one the file list reads — so a test or a git command runs against exactly the tree you are looking at. With no file list above it, the shell now fills the panel instead of sitting under an empty block (#305).
It is a shell attached to the panel, not a session: it never appears in the sidebar, never counts towards the running-session count, and closing it leaves nothing behind (#305).
Untracked files are changes too
A file the session just created is listed with the others, with a real diff and real counts, instead of appearing as a name with nothing behind it (#298). Very large untracked trees fall back to a folder-level summary rather than listing tens of thousands of rows.
The panel says what it found, instead of disappearing
Opening Changes always tells you something. There is a file list when there are changes, No changes when the work tree is clean, and a plain note when the directory is not a git repository at all — where the panel used to put git's own error, or its entire 150-line usage page, in git's language (#298, #305, #310).
A repository that git refuses is not the same thing as no repository, and the panel no longer confuses the two. Dubious ownership, an unreadable .git, an unsupported repository format, a broken worktree link: these show git's own message, bounded, instead of claiming there is no repository here. Exit code 128 is git's generic fatal code, so the difference is established from the filesystem rather than from git's exit status. On a remote session there is no filesystem to check against, so a remote failure reports what git said.
Start a repository under a session that had none and the next refresh picks it up; delete one under an open tab and the tab says so rather than closing itself.
Fixed
- A plain terminal whose entry had gone reopened as a Claude session resumed against an id with no transcript. A terminal stays a terminal, and reopening an exited one no longer mints a second sidebar row (#305).
- The editor keeps a file's untracked line counts after a save, so the diff does not appear to vanish (#302).
- Side-by-side is no longer the panel's default: at the panel's width it rendered two 225-pixel columns and clipped code mid-token (#302).
- The sidebar was sized to the window rather than to the area above the status bar, so it overflowed its container by exactly the status bar's height. The app could end up scrolled by those 22 pixels, clipping the top row of toolbar icons — with no scrollbar to bring it back, since the container hides its overflow. It stayed clipped until the window was reloaded (#306).
- Closing the window released the terminal, Changes and subagent watchers but not the file watchers behind the Memory and Work Files panels, so every file a panel had open kept its descriptor for the life of the process (#308).
Other
- The test suite no longer takes every core: stage one runs at most four workers, overridable with
SWITCHBOARD_TEST_CONCURRENCY(#299).
Full changelog: v0.0.80...v0.0.81
0.0.80
What's changed
Memory and schedules now follow symlinks — read the first note if you keep your configuration in a repo
- A
schedule-*.mdsymlinked into a project's.claude/commandsis listed in the brain tab and runnable from it. The cron loop always read through the link; the UI did not, so a scheduled task could run every week while the tab showed the project had no schedules at all (#294). - The same for
CLAUDE.md, memory notes and.claude/*.mdreached through a link.
Behaviour change. A project-root CLAUDE.md, GEMINI.md or agents.md that is a symlink pointing outside every known project root and outside ~/.claude is no longer listed. The Memory panel could never open those files — it refuses the same path — so the row opened blank and failed to save. If that symlink was a project's only memory file, the project itself no longer appears in the tab. Keeping the link's target inside a directory you have opened as a project restores it.
Fixed
- A project holding a FIFO, a socket or a device named
CLAUDE.mdfroze the app permanently: the read is synchronous on the main process and never returns. Non-regular files are now refused (#296). - Files reached through a symlink are checked against the credential denylist and the project allowlist before being read, so a repository that links a
.mdat a key or an.envno longer puts its content in the searchable memory index (#296, #294). - The "run now" button on a schedule checks what the link points at, not only where it sits, so a file that is not a schedule cannot become the prompt of a headless run (#294).
- An unreadable file in a scanned directory no longer hides every file listed after it.
- Opening the brain tab no longer rebuilds the known-project set once per file scanned (#296).
Other
- The CLI's title is forwarded through tmux on attach, so an attached remote row is no longer mute (#293).
- Subagent activity is traced from its single write path, with its source (#292).
Full changelog: v0.0.79...v0.0.80
0.0.79
What's changed
- (session-state): subagent activity feeds the same running set and the same rung for every source (#288)
- (sidebar): keep session-meta children in a fixed order whether the status slot is populated or not (#287)
- test: let session C release session A instead of a 3 s timer (#283)
- (session-state): local-pty rows carry a persistent state like the other adapters (#282)
Full changelog: v0.0.78...v0.0.79
0.0.78
What's changed
- (panel): read-only Changes view from git status, same for local and remote sessions (#280)
- (remote): show a live remote session before its first transcript exists (#279)
Full changelog: v0.0.77...v0.0.78
0.0.77
What's changed
- (sidebar): archive and delete stop a live remote session on its host first (#276)
- (session-state): one owner per remote row — local-pty while attached, the adapter otherwise (#275)
- (remote): fetch a grown transcript by byte range, stop one large file from stalling the cycle (#274)
- (remote): stop a remote session for real, behind the same dialog as a local one (#272)
- (subagents): attribute subagent activity to its parent from every source (#270)
- (session-state): local-transcript adapter for sessions launched outside the app (#269)
- (session-state): one icon slot per sidebar row, resolved by the domain module (#268)
- (session-state): feed remote sessions through the remote-ssh adapter (#267)
- (session-state): pure domain module, single DOM writer, enforced by lint (#266)
Full changelog: v0.0.76...v0.0.77