Skip to content

Repository files navigation

SessionDeck

A sidebar tree of every Claude Code, Codex and Cursor agent session running on your machine, grouped by project, with a status icon on each row. A red bell means a session is waiting on you. Click the row and you land in its tab.

SessionDeck: live session tree

Status is inferred from transcripts and process state on disk, not reported by the agents themselves, so it can lag or misread an edge case. The rest of this page says where.

It's for people who run many agents at once and lose track of which one stopped to ask.

Install

VS Code (Visual Studio Marketplace):

code --install-extension len5ky.sessiondeck

Or search SessionDeck in the Extensions view.

Cursor (Open VSX): search SessionDeck in the Extensions panel, or run cursor --install-extension len5ky.sessiondeck. If Cursor can't find it, download sessiondeck-<version>.vsix from GitHub Releases and run Extensions: Install from VSIX....

Remote windows (WSL, Remote-SSH, dev containers): SessionDeck runs on the workspace side, so it goes into the remote, next to your sessions. The optional bridge for seeing several hosts in one tree goes on your local desktop instead, and installing SessionDeck from the Extensions view puts it there for you; see Other hosts.

Platform support

  • Linux and WSL: full support; this is what it's built and tested on.
  • macOS: works, minus the /proc features. Auto-detected config homes degrade, and discovery is limited to config homes it already knows about. Terminal matching, the ↗ badge and Move into Editor read the process table with ps.
  • Native Windows: the tree works on the 3-second poll, without hooks or approval alerts. Install Hooks isn't offered there, and running it from the palette writes a POSIX shell script SessionDeck doesn't support on Windows. Terminal matching, the ↗ badge and Move into Editor read the process table through PowerShell, so a new session can take a few seconds to get its badge.

Run Diagnostics reports what works on your machine. Trust it over this list.

First run

  1. Click the compass icon in the activity bar. That's the SessionDeck view; your sessions appear under Sessions, grouped by project.
  2. Run SessionDeck: Install Hooks (instant updates + approval alerts) from the command palette. Without hooks the tree still updates on a 3-second poll, but it can't see a session stuck on a permission prompt: that session just looks like it's working. Hooks are written into each Claude config home on the machine where SessionDeck runs, so do this once per machine (once per remote host, too). They apply to sessions started afterwards.
  3. If the tree is emptier than you expect, run SessionDeck: Run Diagnostics. It prints one line per subsystem and the fix for each problem, and changes nothing.

What a row tells you

Icon Meaning
spinning sync, green working
spinning sync, purple working, with subagents running
bell, red waiting for your approval or an answer
bell-dot, yellow finished, you haven't looked yet
check, blue finished and read
circle outline idle

A working row carries a short caption of what it's doing (2m · ⚙3 · Migrating schema, or just the tool, 2m · Bash). A finished row you haven't opened shows the start of its last message. If a working session goes quiet far longer than its current tool usually takes (5 minutes for Read or Edit, 15 for Bash), the row keeps spinning but adds · quiet 12m. It may be wedged; the status itself never changes on a guess.

The activity-bar icon and a status-bar chip both show how many sessions need you. Ctrl+Alt+] (Cmd+Alt+] on macOS) jumps to the next one, oldest blocked first; Ctrl+Alt+[ goes back.

Click to navigate: jumps to the session's window and tab

Clicking a row opens the session: its Claude tab, even in another editor window, or the integrated terminal it runs in. If there's nothing to focus (a session in tmux, say), you get a rendered copy of its last message instead.

Sessions outside the editor

A session started in another terminal, over SSH, or by an app through the Agent SDK has no tab or terminal here to jump to. Its row gets a small ↗ badge, and the hover says where it runs. Sessions in tmux and in this editor's own terminals and tabs are not marked, and neither is anything SessionDeck can't place for sure. The mark and the move work on Linux, macOS and Windows; Codex sessions are placed and moved on Linux only.

Move into Editor, inline on the row or in its right-click menu, brings it in. After you confirm, SessionDeck stops the outside process, waits for it to exit, and resumes the same session here. It never resumes while the old process is still running. If the process hasn't stopped after 5 seconds you're asked whether to force it, and if it still won't stop, nothing is resumed. Windows has no gentle stop for a terminal program, so there the session and the programs it started are ended at once, and the confirmation says so.

Stopping a session mid-turn cuts that turn off. A session run by another app (one built on the Agent SDK, say) is riskier: the app may start it again the next time a message is sent there, and then two programs write to one session, so stop it in that app first. The confirmation warns about each of these when it applies, and for app-run sessions Cancel is the default.

Where it lands is sessionDeck.moveTarget: a Claude Code tab when the Claude Code extension is installed and the session's folder is open in this window, otherwise a new terminal tab in this window's editor area, opened in the session's folder, running claude --resume <id>. Codex sessions resume the same way with codex resume <id>. It runs the same claude or codex program the stopped session used, found from that process before it is stopped, so it works even when the CLI isn't on the terminal's PATH. The terminal runs the CLI itself, not a shell, so your shell's startup files play no part, and it closes when you quit the session. Closing that tab ends the session as well, and SessionDeck then offers to resume it. Relaunching the terminal when the editor asks also ends it, without that offer. Cursor Agent sessions can't be moved.

Stop Session ends a session without moving it. It sits inline next to Move and in the right-click menu on any running Claude Code or Codex session SessionDeck can place, whether that's outside the editor, in this editor's terminals and tabs, or in tmux. After you confirm, it stops the process the same way Move does. It checks that the process still belongs to that session, asks it to stop, and offers to force it after 5 seconds. On Windows it ends the session and the programs it started right away, and names any it couldn't end. Nothing is resumed. The conversation is kept, so you can pick it up later. Leave It Running is the default button. For a row from another host, the SessionDeck window on that host asks you to confirm, and this window tells you how it went. That needs this version of SessionDeck on both hosts and of the Bridge on your desktop.

Show Activity Tree expands each session into its workflows, subagents and tasks.

Activity tree: workflows, subagents, tasks

How it works

SessionDeck reads files. Every few seconds it scans each Claude Code config home (~/.claude, CLAUDE_CONFIG_DIR, any you list in sessionDeck.extraConfigDirs, and on Linux any home a running claude process points at), reads the tail of each transcript, and checks the process in /proc to confirm it's still alive. The turn boundary comes from the transcript's stop_reason: end_turn means done, an interrupt marker means idle. So sessions started in a terminal, tmux or cron show up too.

Hooks make it faster. Install Hooks adds Stop, Notification and UserPromptSubmit entries to settings.json in every config home (backed up, safe to run twice) plus a small forwarder script at ~/.local/state/claude-overview/hook.sh (claude-overview is SessionDeck's former name; the folder keeps it so existing installs carry over). Each event appends a line to a spool file SessionDeck watches, so the row flips the moment a session finishes or hits a permission prompt. Remove Hooks takes them out.

When a session blocks, you get a VS Code notification naming what it wants ("needs your permission to use Bash", or the question text) with an Open button. One notification per incident. Three or more blocks within 45 seconds collapse into one notice with a Triage button. Notifications live inside the editor window, which is exactly where you aren't when they fire, so two settings (both off by default) reach you when it's unfocused: sessionDeck.unfocusedSound and sessionDeck.unfocusedOsNotification. They fire at most once per 30 seconds.

The extension makes no network calls, licensing included.

Codex and Cursor

Running codex, codex exec and cursor-agent sessions appear under their projects with working, idle and finished-unread states. Cursor's GUI Composer can be added with SessionDeck: Enable Cursor Monitoring, which writes a marked section to ~/.cursor/hooks.json.

None of these tools writes an approval or "waiting for you" state to disk. Their rows never show a red bell or send a notification; a Codex session waiting on approval shows as working or idle.

Other hosts (the bridge)

Cross-host: sessions from other machines

If you work in remote windows, each remote host can show up as its own section in your local tree. That needs a second, invisible extension, SessionDeck Bridge (len5ky.sessiondeck-bridge), installed on the local desktop side. Installing SessionDeck from the Marketplace or Open VSX installs it there too. Installed from a remote window, VS Code also copies the bridge onto that remote host, where it never runs; you can leave it there or uninstall it. It has no UI and no settings. It relays snapshots over VS Code's own command channel and serves tab titles to remote windows, which can't read local editor storage.

If everything runs in local windows on one machine you don't need it, and you can uninstall it. Without it, remote sessions also show project-hash names instead of tab titles.

Remote rows are read-only. A live one focuses the session when clicked; a closed one stays dimmed with a "last seen" age and leaves the tree after 24 hours. VS Code and Cursor each see only their own hosts.

Floating window

Open as Floating Window, in the ... menu at the top of the Sessions view or from the command palette, puts the same tree in a small always-on-top OS window you can park on a second monitor.

Install (for agents and scripts)

Run these in a shell on the host where the sessions live. In a remote setup that is the remote (WSL, SSH, container) for steps 1, 3 and 4; step 2 runs on the local desktop. Use cursor in place of code for Cursor.

# 1. Main extension (workspace side)
code --install-extension len5ky.sessiondeck
#    Offline: download sessiondeck-<version>.vsix from
#    https://github.com/len5ky/sessiondeck/releases, then
#    code --install-extension ./sessiondeck-<version>.vsix

# 2. Optional, cross-host view only: bridge on the LOCAL desktop.
#    Installing step 1 from the Extensions view brings it along; from a shell, run this on the desktop.
code --install-extension len5ky.sessiondeck-bridge

Step 3 is the hooks. Simplest: ask the user to run SessionDeck: Install Hooks (instant updates + approval alerts) once from the command palette. To do it from a shell instead, this reproduces what that command writes. It needs python3. Add any extra config homes to the for list.

# 3. Hooks: forwarder script, spool file, lease, and settings.json entries
STATE="$HOME/.local/state/claude-overview"
mkdir -p "$STATE"
cat > "$STATE/hook.sh" <<'EOF'
#!/bin/sh
# SessionDeck hook forwarder: appends Claude Code hook events to a spool file
# watched by SessionDeck. Safe to delete; reinstall via the
# "SessionDeck: Install Hooks" command. The claude-overview folder name is
# SessionDeck's former name, kept so existing installs keep working.
payload=$(cat)
lease="__STATE__/monitor.lease"
now=$(date +%s)
ok=0
if [ -f "$lease" ]; then
  lts=$(head -n1 "$lease" 2>/dev/null | tr -dc 0-9)
  [ -n "$lts" ] && [ "$lts" -gt 0 ] && \
    [ $((now - lts)) -lt 604800 ] && [ $((lts - now)) -lt 86400 ] && ok=1
fi
f="__STATE__/events.jsonl"
[ "$ok" = 1 ] && {
  size=$(wc -c < "$f" 2>/dev/null || echo 0)
  [ "$size" -gt 1048576 ] && : > "$f"
  printf '%s\n' "{\"event\":\"${1:-unknown}\",\"ts\":$((now*1000)),\"bridge\":\"${CLAUDE_CODE_BRIDGE_SESSION_ID:-}\",\"payload\":${payload:-null}}" >> "$f"
}
exit 0
EOF
python3 -c 'import sys; p, d = sys.argv[1:]; s = open(p).read(); open(p, "w").write(s.replace("__STATE__", d))' "$STATE/hook.sh" "$STATE"
chmod 755 "$STATE/hook.sh"
[ -e "$STATE/events.jsonl" ] || : > "$STATE/events.jsonl"
date +%s > "$STATE/monitor.lease"
for home in "$HOME/.claude" ${CLAUDE_CONFIG_DIR:+"$CLAUDE_CONFIG_DIR"}; do
  [ -d "$home" ] || continue
  python3 - "$home/settings.json" "$STATE/hook.sh" <<'PY'
import json, os, shutil, sys
path, script = sys.argv[1], sys.argv[2]
raw = open(path).read() if os.path.exists(path) else ""
s = json.loads(raw) if raw else {}
if raw and "claude-overview" not in raw:
    shutil.copyfile(path, path + ".claude-overview.bak")
hooks = s.setdefault("hooks", {})
for event, arg in (("Notification", "notification"), ("Stop", "stop"), ("UserPromptSubmit", "prompt")):
    entries = hooks.setdefault(event, [])
    if not any("claude-overview" in json.dumps(e) for e in entries):
        entries.append({"hooks": [{"type": "command", "command": f'"{script}" {arg}', "timeout": 5}]})
open(path, "w").write(json.dumps(s, indent=2, ensure_ascii=False) + "\n")
PY
done

The string claude-overview in each hook's command path is the marker the extension looks for, so it treats these entries as its own, and Remove Hooks cleans them up. The settings entries come out byte-identical to the command's; the script differs in one comment, and the extension swaps in its built-in copy on next start.

# 4. Verify (each line should print ok)
code --list-extensions | grep -qi '^len5ky\.sessiondeck$' && echo extension ok
grep -q claude-overview ~/.claude/settings.json && echo hooks ok    # repeat per config home
test -x ~/.local/state/claude-overview/hook.sh && echo hook-script ok

The end-to-end check is still a human one: start a Claude Code session, run SessionDeck: Run Diagnostics, and the config-home line should report at least one live session. A diagnostics file an agent can read from the shell is on the roadmap.

To uninstall cleanly, run SessionDeck: Remove All Integrations first (Claude hooks and Cursor monitoring), then uninstall the extension.

Settings

Setting Default What it does
sessionDeck.enableNavigation true Click focuses the session; off shows the last message instead
sessionDeck.moveTarget "auto" Where Move into Editor resumes a session: claudeTab, terminal, or auto (a Claude tab when possible)
sessionDeck.activityTree false Expand sessions into workflows, subagents and tasks
sessionDeck.density "comfortable" compact folds projects to one line, auto-expanding any that need you
sessionDeck.notifications "urgent" Notify when a session needs you; off silences them (chip stays)
sessionDeck.unfocusedSound false Sound when a session needs you and the window is unfocused
sessionDeck.unfocusedOsNotification false OS notification in the same case (notify-send on Linux, osascript on macOS; none on native Windows)
sessionDeck.showCursorAgents true Show Cursor Agent CLI sessions
sessionDeck.showCursorComposer true Show Cursor Composer sessions seen through Cursor hooks
sessionDeck.showCodexAgents true Show Codex CLI sessions
sessionDeck.crossHost true Publish and merge cross-host snapshots via the bridge
sessionDeck.floatAlwaysOnTop true Keep the floating window on top
sessionDeck.extraConfigDirs [] Extra Claude config homes to scan
sessionDeck.licenseKey "" License key; validated offline

Where it can be wrong

Status is a reading of private, undocumented file formats: Claude transcripts, Codex rollouts, Cursor's store.db, and the editor's title storage. A vendor update can change any of them. When that happens, rows go blank or fall back to plain names rather than showing wrong data, so SessionDeck watches for it: Run Diagnostics shows a line per format, and a dim "Format drift suspected" note appears in the tree once a format stops parsing. SessionDeck: Capture Drift Fixture saves an anonymized sample of the broken file for a bug report.

A turn killed before it wrote anything just drops off when its process dies.

Pricing

The first 3 days are unlimited. After that, 3 sessions stay free for good; subagents and remote hosts don't count. Neither needs a key or an account. A license ($5.99/month or $18.99 once, plus tax where applicable; per person, any number of machines) lifts the cap. Buy it at sessiondeck.dev, then run SessionDeck: Enter License Key or set sessionDeck.licenseKey. The key is checked offline. The bridge needs no license of its own.

License

Source-available, not open source. See LICENSE for the terms. The bridge companion is MIT.

SessionDeck is an independent tool, not affiliated with or endorsed by Anthropic, OpenAI, or Cursor. Product names belong to their owners.

About

SessionDeck — every coding-agent session in one tree, in VS Code and Cursor. Source-available.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages