✻ Claude Sessions 3 sessions · sort:recency
╭───────────────────────────────╮───────────────────────────────────────────╮
│ ❯ Fix auth middleware 18 2h │ ✻ Fix auth middleware │
│ Add rate limiting 7 yest│ ──────────────────────────────────── │
│ │ │
│ ~/Desktop/misc/tui │ path ~/work/api-service │
│ Picker design 31 5m │ 18 msgs · 2h ago · main · opus-4-8 │
│ │ │
│ │ ✎ first │
│ │ auth expiry uses < not <= │
│ │ │
│ │ ✦ last │
│ │ fixed, all tests pass │
╰───────────────────────────────╯───────────────────────────────────────────╯
↵ resume · / filter · space mark · d delete · . scope · q quit
made with ♥ by ThirdEyeSqueegee and Claude
- 🗂️ Grouped by project — every session under
~/.claude/projects, organized by repo - 🔍 Fuzzy filter — match across title, path, and first message (space = AND)
- 👀 Live detail pane — first/last message, branch, model, message count, relative age
- ⚡ Instant resume —
chdirs into the project and relaunches Claude where you left off - 🎯 Project scope — one key to show only the current repo's sessions
- 🧹 Safe bulk delete — multi-select, UUID-validated, path-confined to
~/.claude - 🎨 Warm theme + kitty tab coloring — fully configurable via TOML
- 🚀 Fast cold start — transcripts parsed in parallel, off the UI thread
mise use -g github:ThirdEyeSqueegee/claude-session-tuiGrab the archive for your platform from the
latest release,
verify it against *_checksums.txt, and drop cst on your PATH.
go install github.com/ThirdEyeSqueegee/claude-session-tui@latestOr clone and use the Makefile (builds a version-stamped binary into ~/.local/bin):
git clone https://github.com/ThirdEyeSqueegee/claude-session-tui
cd claude-session-tui
make installRequirements: Go 1.26+ to build. Runs on macOS and Linux.
Just run it:
cstPick a session with ↵ and cst chdirs into that session's project directory,
then launches the resume command (default: a plain claude --resume <id>),
waits for it, and restores your terminal afterward.
| Key | Action |
|---|---|
↵ |
Resume the selected session |
j / k, ↑ / ↓ |
Move (skips group headers) |
ctrl-d / ctrl-u |
Half-page jump |
g / G |
Jump to top / bottom |
/ |
Fuzzy filter (title + path + first message) |
space |
Mark / unmark a row for bulk delete |
A |
Clear all marks |
. |
Toggle scope to the launch repo only |
s |
Cycle sort: recency → project → msgs |
p |
Preview the full transcript |
d |
Delete (marked rows, or the cursor row) |
q / esc |
Quit |
| Flag | Behavior |
|---|---|
-p, --print |
Print the chosen session id and exit |
-o, --output <file> |
Write the chosen id to <file> and exit |
-c, --config <path> |
Use a specific config file |
-C, --print-config |
Print the resolved effective config and exit |
-v, --version |
Print the build version |
-h, --help |
Show help |
| Command | Behavior |
|---|---|
prune (or p) |
Sweep orphaned session state, asking before removing |
--print / --output let a wrapper own the launch instead of cst. Exit code
130 means you quit without choosing.
cst reads an optional TOML file, resolved in this order:
--config <path>$CST_CONFIG$XDG_CONFIG_HOME/cst/config.toml~/.config/cst/config.toml
Every key is optional — a missing file means all defaults. A bad value (malformed
hex, an unknown sort) keeps the default and shows a config ⚠ note in the title
bar; run cst --print-config to dump the resolved config. The full annotated
schema lives in config.example.toml.
[resume]
command = "claude" # the binary to launch
args = [] # extra flags, e.g. ["--dangerously-skip-permissions"]
[tab_color]
enabled = true
per_project = true # distinct kitty tab hue per repo
[ui]
sort = "recency" # "recency" | "project" | "msgs"
default_scope = "all" # "all" | "cwd"Press . to toggle a view of just the sessions whose project directory matches
the directory cst was launched from — the inline equivalent of a per-repo
resume picker. Both paths are symlink-resolved, so jj worktrees and symlinked
checkouts still match. Start scoped with default_scope = "cwd".
space marks rows (shown with a ✓ in the gutter); d then deletes all marked
sessions behind a single delete N chats forever? confirm, or just the cursor
row when nothing is marked.
Deleting removes the transcript plus its satellite state: session-env,
file-history, subagent project dirs, the short-id-keyed tasks/session-<id>,
jobs/<id>, and teams/session-<id> dirs, the sessions/<pid>.json metadata
file (matched on its inner sessionId, not its pid filename), and paste-cache
/ tasks / todos entries keyed by the session id. When the transcript was the last one in its project directory, that
now-empty projects/<encoded-cwd> directory is removed too. Every id is
validated as a UUID and every removed path is confirmed to live strictly under
~/.claude before deletion, so a malformed id can never escape that tree. A row
is dropped from the list only when its on-disk delete actually succeeds;
failures stay marked and are reported in the help bar.
Crashes, manual deletes, and moved transcripts can leave session state behind
with no transcript backing it. cst prune sweeps for it — the inverse of a
normal delete: it walks session-env, file-history, subagent dirs,
tasks/session-<id>, jobs/<id>, teams/session-<id>, sessions/*.json, and
empty projects/<encoded-cwd> husks, and flags everything whose id isn't in the
live transcript set.
cst prune # list orphans, then ask y / N before removing
cst p # same, short aliasIt lists everything it found first, then asks for confirmation; a bare enter or
n removes nothing. Every removal goes through the same ~/.claude-confined
path check as a normal delete.
Inside kitty, cst tints the terminal tab while a session runs and restores it
on exit (via kitten @ set-tab-color). That's why cst waits on the child
rather than replacing itself with it. Set per_project = true under
[tab_color] to derive a stable, distinct hue per repo so a wall of concurrent
tabs is scannable at a glance. Outside kitty — or if remote control is off — it
silently no-ops.
Each session is parsed straight from its .jsonl transcript:
- Title — custom title, else AI-generated title, else the first user message
- Project path — the session's cwd, home-collapsed
- Message count — real user messages only (sidechain / meta excluded)
- Detail — branch, model, first message, last assistant reply
Sessions load asynchronously behind a spinner, so a heavy ~/.claude/projects
never stalls startup. Unreadable or truncated transcripts surface as an advisory
count in the title bar instead of silently vanishing. On a terminal too narrow
for two panes, the layout collapses to a single full-width list.
Built with Bubble Tea, Bubbles, and Lip Gloss.
make build # version-stamped binary
make test # go test ./...
make race # go test -race ./...
make vet # go vet ./...
make fmt # gofmt -w .CI runs on every push and PR: a go test -race matrix on macOS + Linux,
golangci-lint, govulncheck, actionlint, and a gofmt gate — each under
harden-runner with a
least-privilege token. Actions are pinned to major versions and bumped by
Dependabot.
Releases are cut by GoReleaser on a pushed semver tag,
producing cross-compiled darwin/linux × amd64/arm64 archives plus a
checksums file:
git tag -a v0.1.0 -m v0.1.0
git push origin v0.1.0Issues and PRs welcome. Before opening a PR, please run make fmt, make vet,
and make race — CI enforces all three.
MIT © ThirdEyeSqueegee