Search and resume conversations across Claude Code, Codex, and more, all from a single place.
Coding agents are really good right now, so I'm using a bunch of them. Sometimes I remember I, or the LLM, mentioned something specific in a previous session, and I want to go back to it.
The problem is that currently, agents do have a resume feature, but either they don't support searching, or the search is very basic (e.g., title only).
That's why I built fast-resume: a command-line tool that aggregates all your coding agent sessions into a single searchable index, so you can quickly find and resume any session.
- Unified Search: One search box to find sessions across all your coding agents
- Full-Text Search: Search not just titles, but the entire conversation content (user messages and assistant responses)
- Very fast: Built on the Rust-powered Tantivy search engine for blazing-fast indexing and searching
- Fuzzy Matching: Typo-tolerant search with smart ranking (exact matches boosted)
- Direct Resume: Select, Enter, you're back in your session
- Beautiful TUI: fzf-style interface with agent icons, color-coded results, and live preview
- Update Notifications: Get notified when a new version is available
| Agent | Data Location | Resume Command |
|---|---|---|
| Claude Code | ~/.claude/projects/ |
claude --resume <id> |
| Codex CLI | ~/.codex/sessions/ |
codex resume <id> |
| Copilot CLI | ~/.copilot/session-state/ |
copilot --resume <id> |
| VS Code Copilot | ~/Library/Application Support/Code/ (macOS) |
code <directory> |
| Crush | ~/.local/share/crush/projects.json |
(interactive only) |
| OpenCode | ~/.local/share/opencode/storage/ |
opencode <dir> --session <id> |
| Vibe | ~/.vibe/logs/session/ |
vibe --resume <id> |
# Run directly (no install needed)
uvx --from fast-resume fr
# Or install permanently
uv tool install fast-resume
fr# Open the TUI with all sessions
fr
# Pre-filter search query
fr "authentication bug"
# Filter by agent
fr -a claude
fr -a codex
# Filter by directory
fr -d myproject
# Combine filters
fr -a claude -d backend "api error"# List sessions in terminal (no TUI)
fr --no-tui
# Just list, don't offer to resume
fr --list
# Force rebuild the index
fr --rebuild
# View your usage statistics
fr --statsResume sessions with auto-approve / skip-permissions flags:
| Agent | Flag Added | Auto-detected |
|---|---|---|
| Claude | --dangerously-skip-permissions |
No |
| Codex | --dangerously-bypass-approvals-and-sandbox |
Yes |
| Copilot CLI | --allow-all-tools --allow-all-paths |
No |
| Vibe | --auto-approve |
Yes |
| OpenCode | (config-based) | β |
| Crush | (no CLI resume) | β |
| VS Code Copilot | (n/a) | β |
Auto-detection: Codex and Vibe store the permissions mode in their session files. Sessions originally started in yolo mode are automatically resumed in yolo mode.
Interactive prompt: For agents that support yolo but don't store it (Claude, Copilot CLI), you'll see a modal asking whether to resume in yolo mode. Use Tab to toggle, Enter to confirm.
Force yolo: Use fr --yolo to skip the prompt and always resume in yolo mode, if supported.
Usage: fr [OPTIONS] [QUERY]
Arguments:
QUERY Search query (optional)
Options:
-a, --agent [claude|codex|copilot-cli|copilot-vscode|crush|opencode|vibe]
Filter by agent
-d, --directory TEXT Filter by directory (substring match)
--no-tui Output list to stdout instead of TUI
--list Just list sessions, don't resume
--rebuild Force rebuild the session index
--stats Show index statistics
--yolo Resume with auto-approve/skip-permissions flags
--version Show version
--help Show this message and exit
| Key | Action |
|---|---|
β / β |
Move selection up/down |
j / k |
Move selection up/down (vim-style) |
Page Up / Page Down |
Move by 10 rows |
Enter |
Resume selected session |
/ |
Focus search input |
| Key | Action |
|---|---|
| `Ctrl+`` | Toggle preview pane |
+ / - |
Resize preview pane |
Tab |
Cycle through agent filters |
c |
Copy full resume command to clipboard |
Ctrl+P |
Open command palette |
q/Esc |
Quit |
| Key | Action |
|---|---|
Tab / β β |
Toggle selection |
Enter |
Confirm selection |
y |
Select Yolo |
n |
Select No |
Esc |
Cancel |
Run fr --stats to see analytics about your coding sessions:
Index Statistics
Total sessions 751
Total messages 13,799
Avg messages/session 18.4
Index size 15.5 MB
Index location ~/.cache/fast-resume/tantivy_index
Date range 2023-11-15 to 2025-12-22
Data by Agent
ββββββββββββββββββ¬ββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬βββββββββββ¬ββββββββββββββ
β Agent β Files β Disk β Sessions β Messages β Content β Data Dir β
ββββββββββββββββββΌββββββββΌβββββββββββΌβββββββββββΌβββββββββββΌβββββββββββΌββββββββββββββ€
β claude β 477 β 312.9 MB β 377 β 10,415 β 3.1 MB β ~/.claude/β¦ β
β copilot-vscode β 191 β 146.0 MB β 189 β 954 β 1.4 MB β ~/Library/β¦ β
β codex β 107 β 23.6 MB β 89 β 321 β 890.6 kB β ~/.codex/β¦ β
β opencode β 9275 β 46.3 MB β 72 β 1,912 β 597.7 kB β ~/.local/β¦ β
β vibe β 12 β 858.2 kB β 12 β 138 β 380.0 kB β ~/.vibe/β¦ β
β crush β 3 β 1.0 MB β 7 β 44 β 15.2 kB β ~/.local/β¦ β
β copilot-cli β 5 β 417.1 kB β 5 β 15 β 6.9 kB β ~/.copilotβ¦ β
ββββββββββββββββββ΄ββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ΄ββββββββββββββ
Activity by Day
Mon ββββββββββ 89
Tue ββββββββββ 86
Wed βββββ 44
Thu ββββββββββββββ 115
Fri βββββββββββββ 112
Sat ββββββββββββββββββββ 163
Sun βββββββββββββββββ 142
Activity by Hour
0h ββ βββ
βββββββββ
β
β 23h
Peak hours: 23:00 (99), 22:00 (63), 12:00 (63)
Top Directories
βββββββββββββββββββββββββ¬βββββββββββ¬βββββββββββ
β Directory β Sessions β Messages β
βββββββββββββββββββββββββΌβββββββββββΌβββββββββββ€
β ~/git/openvpn-install β 234 β 5,597 β
β ~/lab/larafeed β 158 β 2,590 β
β ~/lab/fast-resume β 81 β 2,027 β
β ... β β β
βββββββββββββββββββββββββ΄βββββββββββ΄βββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SessionSearch β
β β
β β’ Orchestrates adapters in parallel (ThreadPoolExecutor) β
β β’ Compares file mtimes to detect changes (incremental updates) β
β β’ Delegates search queries to Tantivy index β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
ββββββββββββββ΄βββββββββββββ β
βΌ βΌ βΌ
ββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TantivyIndex β β Adapters β
β β β ββββββββββ βββββββββ βββββββββ βββββββββββ βββββββββ ββββββββββ ββββββ β
β β’ Fuzzy search ββββββ β Claude β β Codex β βCopilotβ β Copilot β β Crush β βOpenCodeβ βVibeβ β
β β’ mtime tracking β β β β β β β CLI β β VS Code β β β β β β β β
β β β βββββ¬βββββ βββββ¬ββββ βββββ¬ββββ ββββββ¬βββββ βββββ¬ββββ βββββ¬βββββ βββ¬βββ β
β ~/.cache/ β β β β β β β β β β
β fast-resume/ β ββββββββΌβββββββββββΌββββββββββΌβββββββββββΌβββββββββββΌββββββββββΌβββββββββΌββββββββββββ
ββββββββββββββββββββ βΌ βΌ βΌ βΌ βΌ βΌ βΌ
~/.claude/ ~/.codex/ ~/.copilot/ VS Code/ crush.db opencode/ ~/.vibe/
Each agent stores sessions differently. Adapters normalize them into a common Session structure:
| Agent | Format | Parsing Strategy |
|---|---|---|
| Claude Code | JSONL in ~/.claude/projects/<project>/*.jsonl |
Stream line-by-line, extract user/assistant messages, skip agent-* subprocess files |
| Codex | JSONL in ~/.codex/sessions/**/*.jsonl |
Line-by-line parsing, extract from session_meta, response_item, and event_msg entries |
| Copilot CLI | JSONL in ~/.copilot/session-state/*.jsonl |
Line-by-line parsing, extract user.message and assistant.message types |
| Copilot VSCode | JSON in VS Code's workspaceStorage/*/chatSessions/ |
Parse requests array with message text and response values |
| Crush | SQLite DB at <project>/crush.db |
Query sessions and messages tables directly, parse JSON parts column |
| OpenCode | Split JSON in ~/.local/share/opencode/storage/ |
Join session/<hash>/ses_*.json + message/<id>/msg_*.json + part/<id>/*.json |
| Vibe | JSON in ~/.vibe/logs/session/session_*.json |
Parse messages array with role-based content |
The normalized Session structure:
@dataclass
class Session:
id: str # Unique identifier (usually filename or UUID)
agent: str # "claude", "codex", "copilot-cli", "copilot-vscode", "crush", "opencode", "vibe"
title: str # Summary or first user message (max 100 chars)
directory: str # Working directory where session was created
timestamp: datetime # Last modified time
preview: str # First 500 chars for preview pane
content: str # Full conversation text (Β» user, β£β£ assistant)
message_count: int # Conversation turns (user + assistant, excludes tool results)
mtime: float # File mtime for incremental update detectionWhat gets indexed:
- User text messages (the actual prompts you typed)
- Assistant text responses
What's excluded from indexing:
- Tool results (file contents, command outputs, API responses)
- Tool use/calls (function invocations)
- Meta messages (system prompts, context summaries)
- Local command outputs (slash commands like
/context)
This keeps the index focused on the actual conversation and avoids bloating it with large tool outputs that are rarely useful for search.
Incremental updates avoid re-parsing on every launch:
- Load known sessions from Tantivy index with their
mtimevalues - Scan session files, compare mtimes against known values
- Only parse files where
current_mtime > known_mtime + 0.001 - Detect deleted sessions (in index but not on disk)
- Apply changes atomically: delete removed, upsert modified
Parallel loading via ThreadPoolExecutor:
with ThreadPoolExecutor(max_workers=len(self.adapters)) as executor:
futures = {executor.submit(get_incremental, a): a for a in self.adapters}
for future in as_completed(futures):
new_or_modified, deleted_ids = future.result()
self._index.update_sessions(new_or_modified)
on_progress() # TUI updates as each adapter completesSchema versioning: A .schema_version file tracks the index schema. If it doesn't match the code's SCHEMA_VERSION constant, the entire index is deleted and rebuilt. This prevents deserialization errors after upgrades.
Tantivy is a Rust full-text search library (powers Quickwit, similar to Lucene). We use it via tantivy-py.
Fuzzy matching handles typos with edit distance 1 and prefix matching:
for term in query.split():
fuzzy_title = tantivy.Query.fuzzy_term_query(schema, "title", term, distance=1, prefix=True)
fuzzy_content = tantivy.Query.fuzzy_term_query(schema, "content", term, distance=1, prefix=True)
# Term can match in either field (OR), all terms must match (AND)
term_query = tantivy.Query.boolean_query([
(tantivy.Occur.Should, fuzzy_title),
(tantivy.Occur.Should, fuzzy_content),
])
query_parts.append((tantivy.Occur.Must, term_query))So auth midleware (typo) matches "authentication middleware".
Query lifecycle:
βββββββββββββββ 50ms βββββββββββββββ background βββββββββββββββ
β Keystroke β βββββββββΊ β Debounce β ββββββββββββΊ β Worker β
βββββββββββββββ timer βββββββββββββββ thread ββββββββ¬βββββββ
β
βββββββββββββββ ββββββββΌβββββββ
β Render β ββββββββββββ β Tantivy β
β Table β results β Query β
βββββββββββββββ βββββββββββββββ
Streaming results: Sessions appear as each adapter completes, not after all finish.
- Fast path: Index up-to-date β load synchronously, no spinner
- Slow path: Changes detected β spinner, stream results via
on_progress()callback
Preview context: When searching, the preview pane jumps to the matching portion:
for term in query.lower().split():
pos = content.lower().find(term)
if pos != -1:
start = max(0, pos - 100) # Show ~100 chars before match
preview_text = content[start:start + 1500]
breakMatching terms are highlighted with Rich's Text.stylize().
When you press Enter on a session, fast-resume hands off to the original agent:
# In cli.py after TUI exits
resume_cmd, resume_dir = run_tui(query=query, agent_filter=agent)
if resume_cmd:
# 1. Change to the session's original working directory
os.chdir(resume_dir)
# 2. Replace current process with agent's resume command
os.execvp(resume_cmd[0], resume_cmd)os.execvp() replaces the Python process entirely with the agent CLI. This means:
- No subprocess overhead
- Shell history shows
claude --resume xyz, notfr - Agent inherits the correct working directory
- fast-resume process is gone after handoff
Each adapter returns the appropriate command:
| Agent | Resume Command | With --yolo |
|---|---|---|
| Claude | claude --resume <id> |
claude --dangerously-skip-permissions --resume <id> |
| Codex | codex resume <id> |
codex --dangerously-bypass-approvals-and-sandbox resume <id> |
| Copilot CLI | copilot --resume <id> |
copilot --allow-all-tools --allow-all-paths --resume <id> |
| Copilot VSCode | code <directory> |
(no change) |
| OpenCode | opencode <dir> --session <id> |
(no change) |
| Vibe | vibe --resume <id> |
vibe --auto-approve --resume <id> |
| Crush | crush |
(no change) |
Why fast-resume feels instant:
- Tantivy (Rust): Search engine written in Rust, accessed via Python bindings. Handles fuzzy queries over 10k+ sessions in <10ms
- Incremental updates: Only re-parse files where
mtimechanged. Second launch with no changes: ~50ms total - Parallel adapters: All adapters run simultaneously in ThreadPoolExecutor. Total time = slowest adapter, not sum
- Debounced search: 50ms debounce prevents wasteful searches while typing
- Background workers: Search runs in thread, UI never blocks
- orjson: Rust-based JSON parsing, ~10x faster than stdlib json
- Streaming results: Sessions appear as each adapter completes, not after all finish
Typical performance on a machine with ~500 sessions:
- Cold start (empty index): ~2s
- Warm start (no changes): ~50ms
- Search query: <10ms
# Clone and setup
git clone https://github.com/angristan/fast-resume.git
cd fast-resume
uv sync
# Run locally
uv run fr
# Install pre-commit hooks
uv run pre-commit install
# Run tests
uv run pytest -v
# Lint and format
uv run ruff check .
uv run ruff format .fast-resume/
βββ src/fast_resume/
β βββ cli.py # Click CLI entry point
β βββ config.py # Constants, colors, paths
β βββ index.py # TantivyIndex - search engine
β βββ search.py # SessionSearch - adapter orchestration
β βββ tui.py # Textual TUI application
β βββ assets/ # Agent icons (PNG)
β βββ adapters/
β βββ base.py # Session dataclass, AgentAdapter protocol
β βββ claude.py # Claude Code adapter
β βββ codex.py # Codex CLI adapter
β βββ copilot.py # GitHub Copilot CLI adapter
β βββ copilot_vscode.py # VS Code Copilot Chat adapter
β βββ crush.py # Crush adapter
β βββ opencode.py # OpenCode adapter
β βββ vibe.py # Vibe adapter
βββ tests/ # pytest test suite
βββ pyproject.toml # Dependencies and build config
βββ README.md
| Component | Library |
|---|---|
| TUI Framework | Textual |
| Terminal Formatting | Rich |
| CLI Framework | Click |
| Search Engine | Tantivy (via tantivy-py) |
| JSON Parsing | orjson (fast) |
| Date Formatting | humanize |
fast-resume uses sensible defaults and requires no configuration.
To clear the index and rebuild from scratch:
rm -rf ~/.cache/fast-resume/
fr --rebuildMIT