Terminal Deck 0.15.0

Docs

How the app works, part by part — written for somebody who has just installed it and wants to get something running.

Getting started

Terminal Deck does not install or authenticate an agent for you. Before it is of any use, at least one of Claude Code, Codex CLI or Gemini CLI has to be installed on your machine and already signed in — the sign-in happens in the CLI, in a terminal, exactly as it would without this app.

On first launch it checks what is there. If nothing usable is found you get a setup screen instead of an empty window; if something is, you go straight to the app. You can re-run that check any time from Settings → Coding AI, which prints the literal command it ran alongside what it concluded.

Open a project

⌘O picks a folder. Opening one starts a session in it straight away, so you are looking at a running agent rather than a configuration screen. Projects you have opened are remembered and listed in the Projects panel.

Start more sessions

⌘T starts another session in the current project. ⌘⇧T opens the new-session dialog instead, where you choose the agent, the profile it runs as, and whether it continues the last conversation in that folder rather than starting fresh.

Continuing runs the CLI’s own resume flag — claude --continue for Claude Code, codex resume --last for Codex. Gemini has no confirmed resume flag, so a Gemini session starts fresh rather than being handed a guess.

The layout

One strip of tabs in the window header holds your sessions and any browser tabs. A narrow rail down the left side switches the side panel between Overview, Files, Artifacts and Source control, then GitHub, AI readiness, MCP servers and Hooks, with Remote — the phones and computers that can reach this machine — in the rail's foot. ⌘B collapses the panel when you want the whole window for the terminal.

Sessions

What a session is, the four meanings of the status dot, scrollback, split panes, running every session on screen at once, how your CLI is found, and keeping two accounts apart.

Sessions →

The copilot

The assistant that works the app itself — what it can reach, what it will not touch, and the ten panels beside a session.

Panels →

Browser

Opening a page beside the agent, clicking an element to hand over a selector, checking your own site at any screen size, and letting an agent drive the page and hand it back.

The browser →

Git

The Source control panel shows the current branch and four lists kept apart because they mean different things: staged, unstaged, untracked and conflicted. Each entry carries the letter git printed for it, and its insertions and deletions.

Selecting a file shows its unified diff. Untracked files are diffed against /dev/null so a new file still has something to show, and a configured external difftool is bypassed rather than being launched at you.

A watcher keeps the panel current while an agent works, which is the point — it is how you notice a session has rewritten more than you expected.

Files

A tree of the project that loads one level at a time, and a viewer with syntax highlighting. ⌘P is quick open — fuzzy search across the project’s files.

  • Files over 2 MB are refused with a note rather than loaded, because loading them freezes the window.
  • A directory with more than 2,000 entries is truncated and says so. A generated folder can hold hundreds of thousands of files; a tree cannot.
  • node_modules and .git are always hidden, whatever your ignore files say.
  • A symlink that leaves the project root, loops back into its own ancestry, or points at nothing is marked as refused instead of being followed.
  • A .deckignore file in the project is read by the tree, layered over .gitignore and evaluated last-match-wins, so it can re-include something git hides. It is the tree only for now — quick open enumerates through git ls-files and a fixed list of build directories, and no watcher consults it.

Artifacts

Every file your agents wrote or changed in this project, with the diff of each change. It is the answer to the question you actually have after leaving four sessions running — not what did they say, but what did they touch.

It sits where Search used to be in the rail. A row called Search between Files and Source control promised to search files and did not, and its own results had never been clickable; searching past sessions moved into the command palette, which is where people look for search anyway.

GitHub

The GitHub panel runs your own gh CLI: open pull requests and open issues. There is no notification count — the endpoints that carry one accept only a classic personal access token, a GitHub App user token is not one, and no permission can be added to change that, so the bell was removed rather than left showing a number it could never fetch. It uses the same remote gh itself would pick, so this panel and gh pr list never disagree about which repository you are looking at.

Failures are kept apart rather than collapsed into “something went wrong”, because each has a different fix: gh not installed, not authenticated, an expired token, a missing scope, not a git repository, no remote, no GitHub remote, repository not found, no access, rate limited, network down, timed out. Each one names the command that fixes it, with the raw output behind a disclosure.

Comment bodies are deliberately not fetched — asking for them turns a twenty-row list into megabytes of text nobody asked to download.

MCP servers

The MCP inspector reads the configuration you already have rather than keeping a second list of its own: user scope from ~/.claude.json, plus the per-project servers and the approval gates in your Claude settings. A server you added with claude mcp add shows up here without being re-entered.

Connect to a server to see its tools, resources and prompts. A tool can be called from a form generated out of its own JSON schema, with required arguments enforced before the call goes out.

Only stdio servers can be dialled from here. HTTP and SSE servers are listed with a note saying so, rather than being hidden as though your config were wrong. Every call has a wall-clock timeout: an MCP server is an arbitrary process, and it can fail to spawn, spawn and never speak, or die halfway through a listing — none of which may hang the app.

Hooks

Provider hooks let the app know what a session is doing from the CLI itself rather than by watching its screen. The Hooks panel installs them into each provider’s real settings file:

Each provider names its lifecycle differently — Gemini uses Before/After where Claude uses Pre/Post — so these are three different lists, not one list written out three times. Codex additionally needs codex_hooks = true under [features] in ~/.codex/config.toml, or it ignores the file.
Provider File Events
Claude Code ~/.claude/settings.json SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, Notification, Stop, StopFailure, SessionEnd
Codex CLI ~/.codex/hooks.json SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop
Gemini CLI ~/.gemini/settings.json SessionStart, BeforeAgent, BeforeTool, AfterTool, AfterAgent, Notification, SessionEnd

Every entry written ends with the comment # terminaldeck-hook, and that marker is the only thing that identifies an entry as ours. Nothing is removed for looking like ours or pointing at our port — a machine that also runs another agent workspace has that tool’s hooks in the same file, and eating them would break a working install of somebody else’s product.

Writes are atomic and the file is copied to a backup before it is first touched. A settings file that cannot be parsed as strict JSON is left completely alone: a config the app does not understand is a config it must not rewrite. File modes are carried across, so a private 0600 settings file is not quietly widened.

Hooks report to a small HTTP endpoint bound to 127.0.0.1 only, carrying a token generated fresh on every launch and never written to disk by the app. That stops confused software and drive-by browser requests. It is not a defence against something already running as you and reading your home directory, and the app does not pretend otherwise.

AI readiness

A weighted score for how well a project is set up for an agent to work in it, from ten independent checks:

  • No secrets committed
  • A CLAUDE.md
  • A README
  • A test script, a typecheck script and a lint script
  • A .gitignore
  • A git repository, and a clean working tree
  • A lockfile

The score lands in one of four bands — strong from 85, fair from 65, weak from 40, at risk below that. The secrets check is a gate rather than a heavy weight: a project that has committed a .env is capped below every band however good the rest of it is. A tidy repository that leaks its API keys is not “87% ready”.

Some checks offer a fix — creating a CLAUDE.md, a README or a .gitignore, or adding a missing script. A scan never applies one. You see what the fix will do and which files it touches, and it runs only when you ask for it by name.

Alerts

The handful of things about a project worth interrupting for: context bloat, a session about to hit it, a session blocked on a question, a session that moved far more tokens than this project's usual, an agent CLI that is missing, a working tree left dirty. Each alert names the thing to do about it.

Every rule requires positive evidence — sessions that actually ran, a provider actually in use, enough sessions with tokens on them for a median to mean anything — so a brand-new project is silent. A panel that greets an empty folder with five warnings is ignored within a day, and then the one real alert is ignored too.

Context thresholds come from the same module the session inspector uses, so the meter and the alert can never disagree.

Overview

Overview is a live board of the sessions running right now — what each agent is doing, how long it has been at it, and which one is waiting on you — over a per-project grid of drag-and-drop widgets: Sessions, Usage, Git, AI Readiness and GitHub. The layout is saved per project. There are deliberately no progress bars, because an agent does not report progress.

There is no task board. One was built — three columns, drag and drop, cards that could start a session — and it was removed, page, store, widget, menu item and shortcut together. A board is a thing you keep up to date by hand, and nothing else in this app asks that of you.

Remote — your phone, and any browser

Pairing with six digits, how the two machines find each other, what runs on a phone today, and reaching your dev server from it.

From your phone →

Accounts

An account is a separate agent login, and it belongs to one agent — so adding one asks which. The mechanism is the same idea in each case: the CLI keeps everything about who you are inside one configuration directory, and an environment variable moves that directory, so two sessions pointed at two directories are two different logins with separate history and separate transcripts.

Which agents that works for was measured against the real CLIs rather than assumed, because the variable that relocates an agent’s configuration is not always the one that relocates its login, and getting that wrong does not fail loudly — it produces two account names quietly sharing one credential.

  • Claude Code — CLAUDE_CONFIG_DIR. On macOS the credential is in the login Keychain under a service name derived from the directory, so a second sign-in cannot overwrite the first one’s token, and deleting a directory does not log it out.
  • Codex CLI — CODEX_HOME. The credential is a file inside that directory, so it moves with the directory by construction.
  • Gemini CLI — listed and refused, with the reason on the row. Its token lives in a single keychain slot that no configuration directory moves, so a second sign-in would overwrite the first rather than sit beside it.

Choose an account per session in the new-session dialog, set a default per project, or set one globally in Settings → Coding AI. Your existing login appears as an account the app will not rename, move or delete, and sessions running as it spawn with the variable unset rather than set to its own path — setting it to the default path is not a no-op, because the CLI would then look one directory deeper than a default install keeps its configuration, and your normal login would look unconfigured.

Settings

⌘, opens the settings window: a list of sections on the left, one section on the right, and no OK button anywhere. Every change is written as you make it, so pressing Escape can never lose anything. If a write fails the footer says so rather than silently reverting the control under your finger.

Section What is in it
GeneralHow sessions behave day to day.
AppearanceTheme, density and the terminal typeface.
NotificationsWhat the app tells you, and how, with a test button.
Coding AIWhat runs your sessions, the logins it uses, and what is installed.
ToolsExtra tools a session can use.
LinuxWhich Linux a session in a Linux folder runs inside. Windows only.
BrowserThe built-in browser tab and what it remembers.
ScrapingWorkers, request rules, capture and the checks on what came back.
CopilotIts files, its memory, what it did, and what it can reach.
PowerKeep this machine running when you close it.
AdvancedDiagnostics, files on disk, and starting over.
HelpVersion, licence and updates.

Several things used to be sections here and are not. Shortcuts is a popover off the rail's foot — it was the longest pane in the window and the one nobody scrolls twice, which is the shape of a reference rather than a settings screen. Setup and Accounts were folded into Coding AI, and About is now the card at the top of Help. Every link that used to open any of them still lands on the pane its contents went to, including the application menu's own About item.

The Help section checks the release feed and offers what it finds; on your say-so it downloads the release, verifies it and swaps the app, through to the relaunch. Nothing downloads or installs on its own, and if this build cannot update itself — the portable Windows executable is the one that cannot — it says so rather than offering an update it cannot install.

Keyboard shortcuts

These are the chords the app dispatches today, on both platforms. Everything else reaches the agent — CtrlC interrupts it and Esc stops what it is doing, exactly as they would in your own terminal, because neither is claimed by anything below.

On Windows, a focused terminal keeps the plain Ctrl chords. CtrlW, CtrlK, CtrlP and the rest are readline and tmux keys, and a terminal that lost them would be a broken terminal. So while a session has focus the app only claims the chords carrying Shift or Alt — which is why the command palette answers to CtrlShiftP there, and to ⌘K on a Mac, where ⌘ belongs to the application and Control belongs to the terminal. Click away from the terminal and every chord in this table works on either platform.

This note used to say that Control was treated as a second ⌘ because the key handler tested metaKey || ctrlKey. That was true and is not: the matcher compares every modifier exactly, so CtrlW on a Mac no longer closes a tab on its way to the shell.

macOS Windows What it does
⌘O CtrlO Open a project
⌘T CtrlT New session in the current project
⌘⇧T CtrlShiftT New session dialog — agent, profile, and whether to continue
⌘W CtrlW Close the active tab
⌘1 – ⌘9 Ctrl1 – Ctrl9 Jump to a tab
⌘K CtrlK Command palette
⌘⇧P CtrlShiftP Command palette
⌘P CtrlP Quick open a file
⌘B CtrlB Collapse or show the side panel
⌘\ Ctrl\ Swarm view
⌘⇧F CtrlShiftF Search past sessions
⌘⇧I CtrlShiftI Session inspector
⌘, Ctrl, Settings
⌘/ Ctrl/ The shortcut sheet
CtrlC CtrlC Interrupt the agent — passed straight through, never intercepted
Esc Esc Stop what the agent is doing — passed straight through
Esc Esc Close the open dialog, when one is open

The sheet and this table are the same list. Pressing ⌘/ — Ctrl/ on Windows — renders the app's keymap, and a test fails the build if any chord in that keymap has nothing that answers it. This note used to say the opposite, and list a dozen chords the app declared and did not dispatch, including three that named a split view the app could not open. The split view is real now — ⌘D splits the window, ⌘⌥← and ⌘⌥→ move between panes — and the chords in the table are the ones that answer.

The menu bar and the command palette (⌘K, or CtrlShiftP on Windows) dispatch the same command ids, so most menu items and their shortcuts stay in step. Two qualifications, because “cannot drift” was too strong:

  • ⌘W and ⌘B are in the menu bar but not the palette, and ⌘1–9 is in neither.
  • The palette writes down no chord of its own. Each row asks the keymap what its command is bound to on the platform the window is running on, so a Windows user reads CtrlT where a Mac user reads ⌘T, and a row the keymap does not bind prints nothing rather than something invented. It used to carry seventeen hand-typed ⌘ strings, one of which was beside the wrong command.