I’ve accumulated hundreds of Claude Code sessions and kept losing track of where I solved specific problems. The built-in /resume shows recent sessions, but anything older than a few days? Gone.

So I built CCS (Claude Code Search) to fix that.

TLDR Link to heading

brew install agentic-utils/tap/ccs
ccs

Type to search, Enter to resume. That’s it.

The problem Link to heading

Claude Code stores conversations in ~/.claude/projects/ as JSONL files. Each session gets its own file:

{"type":"user","cwd":"/path/to/project","message":{"content":"fix the auth bug"},"timestamp":"2024-01-15T10:00:00Z"}
{"type":"assistant","message":{"content":"I'll help you fix that..."},"timestamp":"2024-01-15T10:01:00Z"}

I was grepping through them manually which was painful. I wanted something like fzf but for my Claude history.

How it works Link to heading

CCS parses all your conversation files in parallel (20 goroutines - parsing ~200 files takes about 200ms), shows one row per session sorted by most recent activity, and lets you fuzzy search across everything you’ve said.

One row per conversation - Early versions showed one row per message which was noisy. Now each conversation appears once, showing the first message but searching across all messages. So you can find a conversation by something you said in the middle of it.

Smart preview - The preview shows:

  • First 2 messages (how the conversation started)
  • Last 2 messages (most recent context)
  • All messages matching your search query
  • Gaps collapsed with “… N messages …”

Search terms get highlighted in yellow.

Sorted by recency, not match score - I use --no-sort in fzf so results stay in timestamp order. When searching history, recency usually matters more than fuzzy match quality.

When you select a conversation, it resumes in the original working directory. You can also pass flags through to Claude Code, e.g. ccs --allow-dangerously-skip-permissions if you like to live dangerously (excellent article, worth the read).

Why Go and fzf? Link to heading

Go gives me a single binary with no runtime dependencies and fast startup - important for CLI tools. Easy to cross-compile for Homebrew too.

Instead of building a TUI from scratch, I shell out to fzf. Battle-tested fuzzy matching, familiar keybindings, preview window with ANSI colours - zero UI code to maintain.

The preview needs to be fast, so CCS writes a cache to /tmp/ccs-cache.json after parsing. The preview command reads from cache, making navigation instant.

Built with Claude Code Link to heading

The entire thing was written in Claude Code sessions. I’d describe what I wanted, test it, refine. Features like the smart preview with gap collapsing and the parallel parsing all emerged from this back-and-forth.

The irony of using Claude Code to build a Claude Code search tool isn’t lost on me. Even this blog post was drafted in a Claude Code session.

It’s ~500 lines of Go. Nothing fancy, but it solves my problem. As of writing, we’re on v0.3.1.

  • GitHub
  • brew install agentic-utils/tap/ccs

MIT licensed. Contributions welcome.