terminal ui for switching claude code sessions
When you use Claude Code, every conversation is saved. Over time you build up dozens of them, scattered across all your projects.
claude-session shows them all in one list — the most recent at the top,
grouped by today, yesterday, this week and older. Each one
gets a short AI-generated title so you can tell at a glance what it was about.
Pick a row, hit enter, and you're back in that conversation right where you left off.
Works on macOS and Linux.
git clone https://github.com/denhampreen/claude-session.git
cd claude-session
./install.shThis symlinks bin/claude-session into ~/.local/bin/claude-session and installs
a cs shorthand alongside it (skipped if you already have a cs binary or
alias). Override the install target with
CLAUDE_SESSION_INSTALL_DIR=/usr/local/bin ./install.sh.
claude-session is a single bash script. You can also just copy bin/claude-session anywhere on
your PATH and chmod +x it.
You can call the tool as either claude-session or the cs shorthand —
they're identical.
cs # open the picker
cs envio # only show sessions whose project path contains "envio"
cs -y # resume with --dangerously-skip-permissions
cs -T # skip AI titles for this run (use raw first message)
cs -y indexer # combine flags with a filter
cs --help
The first user message of a session is often vague ("hey", "ok continue", a pasted
URL). claude-session can replace the title column with a 4–7 word AI-generated summary.
Setup: nothing. Titles are generated by invoking your local claude CLI in
headless, non-persistent mode — it piggybacks on your existing Claude
subscription auth. No API key, no extra config.
- Lazy mode (default): on each
claude-sessionrun, any uncached session spawns a background title generation. This run shows the raw first-message fallback; the next run shows the generated title. No blocking, no setup. - Eager mode (opt-in): run
./install-hook.shonce. This registers a Claude CodeStophook that refreshes each session's title at the end of every turn, so titles stay current as sessions evolve.
Titles are cached at ${XDG_CACHE_HOME:-~/.cache}/claude-session/titles.tsv and keyed by
session ID + user-message count, so titles are refreshed as sessions grow (not
regenerated on every turn).
Generation uses haiku (cheap, fast) by default. Override with
CLAUDE_SESSION_TITLE_MODEL=sonnet etc.
| Env var | Effect |
|---|---|
CLAUDE_SESSION_SKIP_PERMISSIONS=1 |
Always resume with --dangerously-skip-permissions |
CLAUDE_SESSION_CLAUDE_BIN=/path/claude |
Override the claude binary |
CLAUDE_SESSION_PROJECTS_DIR=~/... |
Override ~/.claude/projects |
CLAUDE_SESSION_TITLE_MODEL |
Title model alias (default haiku) |
CLAUDE_SESSION_CACHE_DIR |
Title cache dir (default $XDG_CACHE_HOME/claude-session) |
Add to ~/.zshrc / ~/.bashrc if you want these as defaults.
Each Claude Code session is stored as a JSONL file at
~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonl. claude-session:
- Scans every
*.jsonl. - Extracts the first user message (as the title), the last user-message
timestamp (for sort order), and the
cwd/sessionIdfrom the file. - Filters out sidechain messages and system-injected prompts so titles are real user input.
- Groups and formats rows; pipes them to
fzf. - On selection,
cds into the session's originalcwdandexecsclaude --resume <id>.
Because the script cds inside a subprocess, your parent shell's working
directory is unaffected when Claude exits.
Claude Code's terminal UI doesn't support custom sidebars, so the desktop
app's "recent projects" column has no equivalent in the TUI. claude-session is the
closest shell-level approximation: one keystroke to jump between past
sessions across every project.
MIT — see LICENSE.
