AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻译准备中
Local-first browser cockpit for AI coding agents (Claude Code, Codex, Gemini) and shells: floating PTY windows, git worktree per agent, approval alerts, sessions that survive restarts.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
A local-first cockpit for your AI coding agents — private, and running in 30 seconds. Run Claude Code, Codex, Gemini and plain shells side by side as floating windows in your browser: a git worktree per agent, alerts when one needs your approval, and sessions that survive restarts. No cloud, no accounts, no API keys, no build step.
▶ See it in action — showcase page with a live demo GIF.查看完整 README ↗A local dashboard for running AI coding agents side by side.
termdeck runs Claude Code, Codex, Gemini and your own shells as windows in your browser, each agent in its own git branch so they never overwrite each other. It shows at a glance which agent is working, which is waiting for you, and which needs your approval — then lets you review its changes and merge them. Everything runs on your own machine: no cloud, no account, no API keys.
Three agents, each in its own worktree → one blocks on a permission prompt → approve it from the agent queue → review the diff → merge behind a passing npm test. Sample repo; agent events sent through termdeck's hook API.
Try the live demo (UI only, no install) · Showcase · All features
Quick start · Agent loop · Phone · Security · Features · Install · Configuration · Shortcuts
npx @kiril6/termdeck
Needs Node.js 20+. It opens http://127.0.0.1:3000 in your browser (localhost:3000 redirects there). Optional: install tmux (a terminal session manager that keeps shells alive across restarts; brew install tmux / sudo apt install tmux) so shells survive a server restart. Other ways to install are below.
Install as a desktop app: the installed app is only a window onto the running server, and it is tied to one address. So run termdeck from login on a fixed, uncommon port. Not 3000: dev servers often take it, and the app would then open their page instead.
npm i -g @kiril6/termdeck termdeck autostart install --port 17300 # starts it now and at every login (macOS, Linux) termdeck open # opens http://127.0.0.1:17300, signed inThen click the install icon in the address bar (Chrome, Edge) or use File → Add to Dock (Safari). termdeck opens in its own window without tabs or address bar. On Windows, autostart isn't available: run
termdeck --port 17300and keep it running. Already installed on:3000? Keep that port (--port 3000): a new one is a new address (⌘K → Export / Import settings moves your setup). Details in FEATURES.md.
127.0.0.1. Your shells and their output never leave your machine: no telemetry, no cloud service.| termdeck | tmux / terminal tabs | Browser terminals (ttyd, wetty) | |
|---|---|---|---|
| Layout | Floating windows, projects, file tree | Panes inside one terminal | One terminal per page |
| Knows which agent needs you | ✅ working / waiting / needs approval | — | — |
| Worktree per agent → review → merge | ✅ built in | By hand | — |
| Answer an agent's permission prompt from a list | ✅ Claude Code, Codex, Copilot (via hooks) | — | — |
termdeck uses tmux under the hood when it's installed, so you keep tmux's durability and get the dashboard on top.
1. Launch. Palette (⌘K) → New agent task…: pick the repository (each project remembers its own), a branch name, an agent and a prompt. termdeck creates a git worktree in a sibling <repo>-worktrees/ folder and opens it as its own project tab with the agent running in it.
2. Watch. Every agent tab shows its state — green working, amber waiting on you, red needs approval — and the agent queue lists all of them across projects, most urgent first. If two agents change the same file, the conflict radar warns you before merge time.
3. Unblock. With the optional agent hooks (small scripts your agent CLI runs to report what it's doing; termdeck hooks install sets them up and asks first), the queue shows the exact command an agent wants to run with Approve / Deny buttons. termdeck never answers on its own — only when you click, or through an allow-rule you created yourself (a saved pattern such as npm test that may run without asking).
4. Review. Review agent changes… shows a read-only diff of everything the agent changed since its branch was cut — committed and uncommitted work in one view. Run check… runs your test command in the worktree and shows pass or fail next to the diff (the last result is still there when you reopen it, and is marked out of date once the agent changes more), and files that run code or carry secrets (lockfiles, CI config, .env) are flagged for a closer look.
5. Land. Finish agent task… merges the branch or opens a pull request, optionally after a check like npm test that must pass first. Each option says why when it isn't available. Removing the worktree is a separate opt-in, and git still refuses to delete unmerged work.
Supported agents: Claude Code, Codex, Gemini, Copilot, Cursor Agent, OpenCode, Qwen Code, Antigravity, Aider — or any CLI you add as a preset. Hooks report exact state for Claude Code, Codex and GitHub Copilot CLI (Gemini CLI is supported but not yet verified); Approve / Deny works with Claude Code, Codex and Copilot. Without hooks, termdeck estimates state from terminal output.
Agents block on approvals while you're elsewhere. Two optional pieces cover that, and both stay off unless you set them up:
TD_NOTIFY_URL to an ntfy topic or any webhook. Tap the push, land on that terminal, tap Approve.TD_ALLOWED_HOSTS=mybox.tail1234.ts.net TD_NOTIFY_URL=https://ntfy.sh/my-unguessable-topic npx @kiril6/termdeck
tailscale serve --bg 3000
Needs the agent hooks for Approve and the exact state. Alerts leave out the command unless TD_NOTIFY_DETAIL=1. Full steps: remote access.
termdeck spawns real shells, so it is locked down by default:
rm -rf, sudo, force-push, curl-to-shell, reading secrets…) can never be allowed automatically. The deny-list is a safety net, not a sandbox. Every decision, manual or by rule, is recorded in an audit log (details).127.0.0.1 ("loopback"), so nothing else on your network can reach it.termdeck open, carries a link that sets a cookie. The secret lives in ~/.termdeck/token (readable only by you) and survives restarts, so this happens once per browser. Terminals never get it: each gets its own TD_HOOK_AUTH, which can only report that terminal's agent events. Approvals are a guard against mistakes, not a sandbox: an agent that can already run arbitrary code as you can read your files, so use your agent CLI's own sandbox for untrusted work.Origin and Host headers resolve to a known localhost name (or a TD_ALLOWED_HOSTS entry). This blocks cross-site and DNS-rebinding attacks, so a website can't connect to your shells or read your files.HOST=0.0.0.0 or a specific IP) and termdeck prints a warning and a tokenized URL (/?t=…). Opening it once sets a cookie for that browser; everything else gets 401. In this mode each restart creates a new token. For several users or the internet, put real authentication in front of it.See also Known limits. Found a vulnerability? Report it privately, see SECURITY.md (it also lists everything termdeck stores and how to remove it).
Terminals & layout
~/.termdeck/layouts.json; restoring starts fresh shells in the saved folders and never relaunches agents.⌥1–9 jumps to the Nth window.⌘B, drag its edge to resize) with a built-in file viewer (text, Markdown, CSV tables, images, PDF, audio and video); its ⋯ menu has Follow focused terminal, which reveals the focused window's live folder in the tree.file:line paths, search the terminal's history, GPU-accelerated rendering.Sessions
Agents
path:line reference to an agent's prompt (typed, never run).Productivity
⌘K), snippets, broadcast input to every shell in a project, safe multi-line paste.TD_NOTIFY_URL (e.g. an ntfy topic) and get a push when an agent is blocked on an approval — tap it to open that terminal. Setup.Privacy & effects
⌘⌥X, or the top bar's Privacy button) — for screen sharing and streaming: covers every terminal, file view and the file tree with a plain panel, a strong blur or Matrix rain, and can blur window names and commands too. Shield one tab or a whole project instead; a script can cover its own pane with printf '\e]7777;shield\a'. Optional walk-away shield after 2, 5 or 10 idle minutes. It's a visual cover, not security.Every feature, in detail: FEATURES.md.
| Way | Command | When |
|---|---|---|
| Run once | npx @kiril6/termdeck | Trying it out (npx may reuse a cached copy — see Updating) |
| Install globally | npm i -g @kiril6/termdeck, then termdeck | Daily use; needed for termdeck autostart |
| Clone | node install.js && npm start | Contributing, or trying unreleased changes |
NO_TMUX=1 turns it off.--no-open / NO_OPEN=1, pick one with BROWSER (e.g. BROWSER=firefox). Another browser, or a bookmark to a fresh one, shows a link this browser page: run termdeck open once.--port 4000 / PORT=4000 pins it.node-pty ships prebuilt binaries for macOS, Windows and Linux (x64/arm64).termdeck [options] # start the server (--port <n>, --host <h>, --no-open, -v, -h)
termdeck open # open the running dashboard in your browser, signed in (--no-open: print the link)
termdeck doctor # check Node, node-pty, shell, tmux, git, ports (offline)
termdeck hooks install # wire Claude Code, Gemini CLI, Codex and Copilot hooks (also: uninstall, --dry-run, --yes)
termdeck autostart install # start termdeck at login via launchd/systemd (also: uninstall, --port, --dry-run)
termdeck uninstall # remove hooks, start-at-login, termdeck's tmux sessions and ~/.termdeck (--dry-run, --yes)
termdeck doctor checks this machine without starting anything — Node version, node-pty, shell spawning, tmux (≥ 3.2 for agent hooks), git, free ports, network exposure and invalid TD_* settings — and prints a fix next to each problem. Exit code 0 = all good, 1 = warnings or failures.termdeck hooks install sets up agent hooks for Claude Code, Gemini CLI, Codex and GitHub Copilot CLI: merges into your existing config, backs it up first, and --dry-run previews without writing.termdeck autostart install adds a launchd agent (macOS) or systemd user unit (Linux) so the dashboard starts at login, without root. Needs a global install or a clone. Details: docs/autostart.Set via environment variable; --port, --host and --no-open also exist as flags (flags win). Invalid, zero or negative values for TD_GRACE_MS and TD_BUFFER fall back to the defaults.
| Variable / Flag | Default | Description |
|---|---|---|
PORT / --port <n> | 3000 | Port to listen on. If unset, moves to the next free port; if set, waits up to 5 s for it, then exits. |
HOST / --host <addr> | 127.0.0.1 | Bind address. Non-loopback uses a random per-start access token (/?t=…) instead of the local one. |
NO_OPEN / --no-open | 0 | 1 = don't open the browser on start. |
BROWSER | (default) | Browser to launch (e.g. Google Chrome, firefox). |
NO_TMUX | 0 | 1 = plain shells even if tmux is installed. |
TD_GRACE_MS | 60000 | How long a shell stays alive after the browser disconnects (ms). |
TD_BUFFER | 1000000 | Output replayed on reconnect, per terminal (bytes). |
TD_MAX_PANELS | 64 | Maximum live terminals on the server. Never blocks reattaching. |
TD_EDITOR | code | Executable for the file tree's Open in editor. |
TD_CHECK_TIMEOUT | 600 | Seconds a check may run: in Finish agent task (landing is blocked after that) and in Run check… in the review view. |
TD_LOG_DIR | (none) | Write session logs (and the approval audit log) here. Off by default. |
TD_ALLOWED_HOSTS | (none) | Extra exact hostnames for a reverse proxy (e.g. tailscale serve). Switches to a per-start access token. |
TD_NOTIFY_URL | (none) | Webhook POSTed when an agent needs you (ntfy-ready). Also TD_NOTIFY_EVENTS (default permission_request), (include the command). See . |
The running version is shown in the app's ? Help panel; see Releases for what's new.
termdeck update installs the latest release with npm and, if you set up start-at-login, restarts that service in place (same port, nothing to retype). It asks first; --check only reports, --dry-run shows the commands, --yes skips the question. Then reload the app window (⌘R / Ctrl+R): an update is new package → restart the server → reload the window, because the window only shows what the server serves. Shells keep running through the restart only if tmux is installed.termdeck update installs the package and tells you to stop termdeck (Ctrl+C) and start it again with the same --port: a different port is a different address, so your saved windows and projects don't show (⌘K → Export / Import settings moves them).npx -y @kiril6/termdeck@latest (stop the running one first). termdeck update doesn't apply: there is no installed copy to replace.npm i @kiril6/termdeck): npm update @kiril6/termdeck in that project, then restart. termdeck update only updates the global install.git pull && node install.js && npm startpowershell.exe. No tmux, so shells don't outlive the server.@homebridge/node-pty-prebuilt-multiarch prebuilt. If none exists for your distro/arch, node install.js compiles it, which needs build-essential python3. Reveal in file manager uses xdg-open (xdg-utils).termdeck doctor, or open http://localhost:3000/debug while the server runs — both try each shell and show exactly what failed.termdeck is a local tool: a Node process that spawns real shells. Static hosts (GitHub Pages, Netlify) can't run it — opened that way it falls back to a UI-only demo. To use it from another device, keep the default local-only bind and use a tunnel such as Tailscale Serve or SSH: see docs/remote-access.md.
What termdeck does not do, so none of it is a surprise:
TD_MAX_PANELS).On Windows/Linux, ⌘ = Ctrl and ⌥ = Alt. Press ? in the app for the full list.
Why some shortcuts use
⌥(Alt). The browser reserves the plain combos (⌘Topens a browser tab,⌘Wcloses it,⌘Ljumps to the address bar…) and a web page can't override them, so those actions use⌘⌥…/Ctrl+Alt+…instead.
Global
| Action | Shortcut |
|---|---|
| Command palette | ⌘K |
| New terminal | ⌘⌥T |
| New log panel | ⌘⌥L |
| New project | ⌘⇧M |
| Next / previous project | ⌘⇧] / ⌘⇧[ |
| Search sessions | ⌘F |
| Toggle directory tree | ⌘B |
| Tile all windows | ⌘⌥⇧T |
| Jump to the Nth window of the project | ⌥1–⌥9 (⌘⌥1–9 also inside a terminal) |
| Broadcast input to all shells | ⌘⌥B |
| Play game (Dino / Snake) | ⌘⌥G |
| Privacy shield on / off | ⌘⌥X |
| Open directory | ⌘⇧O |
| Open file | ⌘⌥O |
| New agent task | ⌘⌥R |
| Review agent changes | ⌘⌥D |
| Save snippet | ⌘⌥S |
| Theme picker | ⌘⇧P |
| Toggle pixel pets | ⌘⌥P |
| Close focused window | ⌘⌥W |
| Minimize focused | ⌘⌥M |
| Maximize / restore focused | ⌘⌥⇧M |
| Toggle fullscreen | F11 |
| Help | ? |
Tabs (in the focused window)
| Action | Shortcut |
|---|---|
| New tab | ⌘⌥A |
| Next / previous tab | ⌘] / ⌘[ |
In a terminal
| Action | Shortcut |
|---|---|
| Find in terminal | ⌘F |
| Font size up / down / reset | ⌘+ / ⌘- / ⌘0 |
⌘F searches the focused terminal; with no terminal focused it filters sessions.
No framework beyond Express, no build step.
| File | Role |
|---|---|
server.js | Entry point: loads node-pty, wires Express and the ws WebSocket server, picks the port. |
scripts/sessions.js | The terminals: one WebSocket per terminal, keyed by a client-supplied id, backed by a PTY (inside tmux when available). On disconnect the PTY is not killed — a grace timer holds it so reconnecting replays the buffer. |
scripts/http-security.js, *-api.js | Sign-in token, headers and the Origin/Host guard; the HTTP routes by area (files, layouts, git, agent events and approvals). |
public/index.html + js/ + app.css | The frontend: markup in index.html, JS in public/js/ (plain scripts, one per area), styles in app.css. No bundler. xterm.js is served locally from node_modules. State is saved to localStorage. |
install.js | One-shot installer that picks the right prebuilt node-pty for your platform. |
Core invariant: disconnect ≠ kill, reconnect replays the buffer. Any change to the WebSocket lifecycle must preserve it.
termdeck has no telemetry, so the only way to know what works for you is if you say so. Questions, ideas, or just "I use it for X" → Discussions. Bugs → Issues.
Contributions are welcome — see CONTRIBUTING.md for the ground rules (dependency-light backend, no build step, the reattach invariant) and the dev loop. New here? Try a good first issue.
If termdeck is useful to you, a ⭐ helps others find it. Support development:
MIT — see LICENSE.
termdeck is an independent project. It is not affiliated with or endorsed by Anthropic, OpenAI, Google, GitHub or the makers of the other tools it launches; their names are used only to say what it works with.
npm i -g @kiril6/termdeck, termdeck autostart install --port 17300 runs it now and at every login on an uncommon port (dev servers often take 3000); termdeck open then opens it signed in. With npx or a project install it works the same while the server runs, but you start it yourself each time on the same port (PORT=17300 npx -y @kiril6/termdeck@latest): there is no start-at-login, and with the server down the window shows a browser error.TD_NOTIFY_DETAIL=1XDG_CONFIG_HOME | ~/.config | Linux only: where termdeck autostart install writes its systemd user unit (systemd/user/termdeck.service). |