ccr 0.2.2

CLI Code Resume — one TUI session picker across Claude Code, Codex, and Gemini CLI
ccr-0.2.2 is not a library.

What it does

Scans every supported tool's session store, ranks each session by last activity, and resumes the one you pick — in its original working directory, with its original session ID, via the right CLI.

Each row's subtitle is the last user message in that session, so you see what you were working on most recently — not how the chat opened. Press n on any row to add a yellow nickname; nicknamed rows expand to a third line that keeps the auto-derived title visible (dim) for context, and the nickname is searched by / too.

┌─ ccr — 211 sessions  (2 possibly live) ──────────────────────────────────┐
│ Sessions                              │ Preview                          │
│ ▶ [claude] api-service        12m ago │ nick:    panic hotfix            │
│     panic hotfix                      │ tool:    claude                  │
│     actually let's also add a regr…   │ cwd:     ~/projects/api-service  │
│   [codex]  web-app         1h ● live  │ last:    2026-05-23 14:00  (12m) │
│     use exponential backoff capped…   │ msgs:    47                      │
│   [gemini] cli-tool             3d    │ id:      a1b2c3d4-5e6f-…         │
│     update the migration guide se…    │                                  │
│   [claude] docs-site            1w    │ ── recent turns ──               │
│     tighten the intro paragraph a…    │ ❯ user                           │
│                                       │ the panic reproduces only when…  │
│                                       │ ◆ asst                           │
│                                       │ Let me add a None check in the…  │
└──────────────────────────────────────────────────────────────────────────┘
  ↑↓/jk · Enter resume · b bookmark · n nickname · / filter · ? help · q

Top row is nicknamedpanic hotfix shows in yellow, the auto-derived last-message title stays visible underneath in dim gray. Rows without a nickname are two lines (tags + auto-title), the same as before.

If a selected session is already running elsewhere (detected via pgrep -f <session-id>), a confirmation modal appears before spawning a second attachment:

        ┌─ Confirm resume ──────────────────────────────────────┐
        │ ⚠  Session may already be running                     │
        │                                                       │
        │ tool:    claude                                       │
        │ session: a1b2c3d4-5e6f-7890-abcd-…                    │
        │ cwd:     ~/projects/web-app                           │
        │                                                       │
        │ matching processes:                                   │
        │ 42318 claude --resume a1b2c3d4-5e6f-…                 │
        │                                                       │
        │ Resuming may interleave writes and corrupt session.   │
        │                                                       │
        │ [y] resume anyway    [n] cancel                       │
        └───────────────────────────────────────────────────────┘

Install

cargo install ccr

Or from source:

cargo install --path .

Use

ccr
Key Action
/ k up
/ j down
g / Home jump to top
G / End jump to bottom
PgUp/Dn page up/down (10 rows)
/ filter: title, cwd, tool, nickname, or content
Enter resume selected session (live-checked)
v open session in agx (timeline viewer)
b toggle bookmark (★ marker, persisted)
n set / edit / clear session nickname (yellow label)
? / F1 help overlay
q / Esc quit

On Enter, ccr runs pgrep -f <session-id> first. If a matching process is found, a confirmation modal warns before spawning a second attachment (which would interleave JSONL writes and corrupt the session). Otherwise it execs the tool's resume command with the session's original cwd.

CLI subcommands

Command Action
ccr list plain-text dump of all sessions (tool id date title)
ccr path <id> absolute path to session file (pipes well)
ccr show <id> raw file contents (same as cat $(ccr path <id>))
ccr export <id> full-turn markdown dump (or --format json)
ccr stats totals, per-tool, per-project, 30-day activity histogram

ccr never modifies your session files. If you want to delete one, delete it where its tool stored it (~/.claude/projects/..., ~/.codex/sessions/..., or ~/.gemini/tmp/...).

Environment variables

Variable Purpose
CCR_CLAUDE_DIR Full path to Claude's projects/ dir
CLAUDE_CONFIG_DIR Claude Code's native override; ccr appends projects
CCR_CODEX_DIR Full path to Codex's sessions/ dir
CCR_GEMINI_DIR Full path to Gemini's ~/.gemini root
CCR_BOOKMARKS_FILE Override the ~/.ccr/bookmarks.json location
CCR_NICKNAMES_FILE Override the ~/.ccr/nicknames.json location

How it works

Each backend knows where its tool stores sessions and how to resume one.

Tool Storage Resume invocation
Claude Code ~/.claude/projects/<encoded-cwd>/<uuid>.jsonl claude --resume <uuid> (cwd set)
Codex ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl codex resume <uuid> (cwd set)
Gemini CLI ~/.gemini/tmp/<project>/chats/session-*.json + projects.json gemini --resume <N> — ccr looks up the 1-based index at runtime via gemini --list-sessions

For each session ccr extracts:

  • cwd — working directory the session was started in
  • title — last user message in the session, truncated to 80 chars (so the list shows what you were working on most recently, not how the chat opened)
  • last_activity — most recent turn timestamp (drives the sort and the ● live flag)
  • message_count — user + assistant turns only
  • preview — last 6 turns, rendered in the right pane

Read-only at rest. ccr never touches your session files. The only state it writes is ~/.ccr/bookmarks.json (when you press b) and ~/.ccr/nicknames.json (when you press n). Both are plain JSON.

Adding a backend

A backend is any type that implements:

pub trait Backend: Send + Sync {
    fn name(&self) -> &'static str;
    fn scan(&self) -> Result<Vec<Session>>;
    fn resume(&self, s: &Session) -> std::process::Command;
    fn all_turns(&self, s: &Session) -> Result<Vec<Turn>>;  // for ccr export

    // Optional override with a useful default:
    fn running(&self, s: &Session) -> Vec<String>;   // default: pgrep -f <id>
}

Register it in backends::all() and sessions surface in the shared TUI with a [tool] tag in the left column.

Links

License

MIT