CLI tool to search and resume Claude Code conversations using a bubbletea TUI.
go build
go test -v -cover- Target: 60%+ statement coverage
- Current: ~69%
- All new features must include tests
- Break logic into testable functions when possible
- Refactor for testability (e.g., use variables instead of functions for dependency injection)
./ccs # search recent (60 days, <1GB files)
./ccs <query> # search with initial query
./ccs --max-age=7 # last 7 days only
./ccs --all # include everything
./ccs -- --plan # pass flags to claude
./ccs prune --dry-run # preview lossless size reduction of large files-
Update version in
main.go:const version = "X.Y.Z"
-
Commit changes:
git add -A && git commit -m "feat/fix: description"
-
Push and tag:
git push git tag vX.Y.Z git push origin vX.Y.Z
-
CI will run tests, then release via GoReleaser and update Homebrew tap
- Patch (0.0.X): Bug fixes, minor tweaks
- Minor (0.X.0): New features, backwards compatible
- Major (X.0.0): Breaking changes
main.go- Single file containing all logicmain_test.go- Unit tests.goreleaser.yaml- Release configuration.github/workflows/test.yaml- CI test workflow (reusable).github/workflows/release.yaml- Release workflow (calls test.yaml)
github.com/charmbracelet/bubbletea- TUI frameworkgithub.com/charmbracelet/bubbles/textinput- Text input componentgithub.com/charmbracelet/lipgloss- Styling
Conversation- Parsed conversation with messages, timestamps, cwdMessage- Single message (role, text, timestamp)listItem- Display item with conversation and search textmodel- Bubbletea application state (includes delete confirmation state)
getConversations(cutoff, maxSize)- Loads conversations from~/.claude/projects/with filtersparseConversationFile(path, cutoff, maxSize)- Parses JSONL files, skips by mtime/sizebuildItems()- Creates list items with searchable textinitialModel()- Sets up bubbletea TUIUpdate()- Handles keyboard/mouse input, including delete confirmationView()- Renders the TUI with delete confirmation promptrenderPreview()- Renders conversation preview with highlightspreviewLines()- MemoisedbuildPreviewLinesfor the selected conversation (rebuilt only when selection/query changes), avoids per-frame rescans of huge conversationshitCount()/countHits()- Memoised per-query HITS count (messages containing the query), keyed by SessionID, soformatListItemdoesn't rescan every visible row each frameformatListItem()- Formats a single list rowdeleteConversation()- Removes conversation file and updates UI statepruneConversation()- Prunes the selected conversation in place (Ctrl+R) and refreshes its sizegetTopic()- Extracts first user message as topicpruneFile()/pruneStream()- Shrink a conversation by dropping duplicate/redundant data (never touches user/assistant lines)runPrune()-ccs prunesubcommand driver
ccs · claude code search Resume:Enter Delete:Ctrl+D Prune:Ctrl+R Scroll:Ctrl+J/K Exit:Esc
> type to search... (N/total)
DATE PROJECT TOPIC MSGS HITS SIZE
──────────────────────────────────────────────────────────────────────────────────────
2024-01-08 15:04 project-name ✎ Refactor auth flow 42 3 1.2GB
> 2024-01-08 14:30 selected This one is selected 28 1 12MB
──────────────────────────────────────────────────────────────────────────────────────
Project: /path/to/project
Session: abc123...
2024-01-08 12:00 User:
message text here...
2024-01-08 12:01 Claude:
response text here...
- Use conventional commits (feat:, fix:, docs:, etc.)
- Run tests before releasing
- Keep it simple - single file is fine for this project