Skip to content

Commit 1f44aa8

Browse files
brtkwrclaude
andcommitted
feat: rewrite TUI with bubbletea
- Replace fzf dependency with native bubbletea TUI - Add column headers (DATE, PROJECT, TOPIC, MSGS, HITS) - Add message timestamps in preview - Add hit count per conversation (messages containing query) - Add mouse wheel scrolling (context-aware for list vs preview) - Add Ctrl+J/K for preview scrolling - Exact substring matching instead of fuzzy search - Right-aligned help text and result counts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 8aa8c63 commit 1f44aa8

6 files changed

Lines changed: 700 additions & 469 deletions

File tree

AGENTS.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@
22

33
## Overview
44

5-
CLI tool to search and resume Claude Code conversations using fzf.
5+
CLI tool to search and resume Claude Code conversations using a bubbletea TUI.
66

77
## Development
88

99
### Build & Test
1010

1111
```bash
12-
go build -o ccs .
12+
go build
1313
go test -v -cover
1414
```
1515

16-
### Install locally
16+
### Run locally
1717

1818
```bash
19-
cp ccs /usr/local/bin/ccs
19+
./ccs
20+
./ccs <query>
21+
./ccs -- --plan # pass flags to claude
2022
```
2123

2224
## Release Process
@@ -54,12 +56,50 @@ cp ccs /usr/local/bin/ccs
5456
- `.github/workflows/test.yaml` - CI test workflow (reusable)
5557
- `.github/workflows/release.yaml` - Release workflow (calls test.yaml)
5658

57-
## Key Functions
59+
### Dependencies
60+
61+
- `github.com/charmbracelet/bubbletea` - TUI framework
62+
- `github.com/charmbracelet/bubbles/textinput` - Text input component
63+
- `github.com/charmbracelet/lipgloss` - Styling
64+
65+
### Key Types
66+
67+
- `Conversation` - Parsed conversation with messages, timestamps, cwd
68+
- `Message` - Single message (role, text, timestamp)
69+
- `listItem` - Display item with conversation and search text
70+
- `model` - Bubbletea application state
71+
72+
### Key Functions
5873

5974
- `getConversations()` - Loads all conversations from `~/.claude/projects/`
60-
- `buildSearchLines()` - Creates fzf-compatible search lines (one per conversation)
61-
- `showPreview()` - Renders preview with matches highlighted
6275
- `parseConversationFile()` - Parses JSONL conversation files
76+
- `buildItems()` - Creates list items with searchable text
77+
- `initialModel()` - Sets up bubbletea TUI
78+
- `Update()` - Handles keyboard/mouse input
79+
- `View()` - Renders the TUI
80+
- `renderPreview()` - Renders conversation preview with highlights
81+
- `formatListItem()` - Formats a single list row
82+
83+
### TUI Layout
84+
85+
```
86+
ccs · claude code search ↑/↓ Enter Ctrl+J/K Esc
87+
> type to search... (N/total)
88+
89+
DATE PROJECT TOPIC MSGS HITS
90+
────────────────────────────────────────────────────────────────────────────
91+
2024-01-08 15:04 project-name First user message 42 3
92+
> 2024-01-08 14:30 selected This one is selected 28 1
93+
────────────────────────────────────────────────────────────────────────────
94+
Project: /path/to/project
95+
Session: abc123...
96+
97+
2024-01-08 12:00 User:
98+
message text here...
99+
100+
2024-01-08 12:01 Claude:
101+
response text here...
102+
```
63103

64104
## Conventions
65105

README.md

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@
44
[![Release](https://img.shields.io/github/v/release/agentic-utils/ccs)](https://github.com/agentic-utils/ccs/releases/latest)
55
[![License](https://img.shields.io/github/license/agentic-utils/ccs)](LICENSE)
66

7-
Globally search and resume [Claude Code](https://claude.ai/claude-code) conversations using fzf.
7+
Globally search and resume [Claude Code](https://claude.ai/claude-code) conversations.
88

99
![Demo](demo.gif)
1010

1111
## Features
1212

13-
- Fuzzy search through all your Claude Code conversations
14-
- Preview conversation context with syntax highlighting
15-
- Search term highlighting in preview
16-
- Code block rendering
13+
- Search through all your Claude Code conversations
14+
- Preview conversation context with search term highlighting
15+
- See message counts and hit counts per conversation
1716
- Resume conversations directly from the search interface
18-
- Pass flags through to `claude` (e.g., `--dangerously-skip-permissions`)
17+
- Pass flags through to `claude` (e.g., `--plan`)
18+
- Mouse wheel scrolling support
1919

2020
## Installation
2121

@@ -39,22 +39,6 @@ Download the binary from [releases](https://github.com/agentic-utils/ccs/release
3939

4040
## Requirements
4141

42-
- [fzf](https://github.com/junegunn/fzf)
43-
44-
```bash
45-
# macOS
46-
brew install fzf
47-
48-
# Debian/Ubuntu
49-
sudo apt install fzf
50-
51-
# Fedora
52-
sudo dnf install fzf
53-
54-
# Arch
55-
sudo pacman -S fzf
56-
```
57-
5842
- [Claude Code](https://claude.ai/claude-code) - must be installed and used at least once
5943

6044
## Usage
@@ -63,19 +47,28 @@ Download the binary from [releases](https://github.com/agentic-utils/ccs/release
6347
# Search and resume a conversation
6448
ccs
6549

66-
# Resume with auto-accept permissions
67-
ccs --allow-dangerously-skip-permissions
50+
# Search with initial query
51+
ccs buyer
52+
53+
# Resume with plan mode
54+
ccs -- --plan
55+
56+
# Combined: search "buyer", resume with plan mode
57+
ccs buyer -- --plan
6858
```
6959

7060
### Keybindings
7161

62+
- `↑/↓` or `Ctrl+P/N` - Navigate list
7263
- `Enter` - Resume selected conversation
64+
- `Ctrl+J/K` - Scroll preview
65+
- `Mouse wheel` - Scroll list or preview (context-aware)
66+
- `Ctrl+U` - Clear search
7367
- `Esc` / `Ctrl+C` - Quit
74-
- Type to fuzzy search
7568

7669
## How it works
7770

78-
ccs reads conversation history from `~/.claude/projects/` and presents them in fzf. When you select a conversation, it changes to the original project directory and runs `claude --resume <session-id>`.
71+
ccs reads conversation history from `~/.claude/projects/` and presents them in an interactive TUI. When you select a conversation, it changes to the original project directory and runs `claude --resume <session-id>`.
7972

8073
## License
8174

go.mod

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
11
module github.com/brtkwr/ccs
22

33
go 1.24.0
4+
5+
require (
6+
github.com/charmbracelet/bubbles v0.21.0
7+
github.com/charmbracelet/bubbletea v1.3.10
8+
github.com/charmbracelet/lipgloss v1.1.0
9+
)
10+
11+
require (
12+
github.com/atotto/clipboard v0.1.4 // indirect
13+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
14+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
15+
github.com/charmbracelet/x/ansi v0.10.1 // indirect
16+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
17+
github.com/charmbracelet/x/term v0.2.1 // indirect
18+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
19+
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
20+
github.com/mattn/go-isatty v0.0.20 // indirect
21+
github.com/mattn/go-localereader v0.0.1 // indirect
22+
github.com/mattn/go-runewidth v0.0.16 // indirect
23+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
24+
github.com/muesli/cancelreader v0.2.2 // indirect
25+
github.com/muesli/termenv v0.16.0 // indirect
26+
github.com/rivo/uniseg v0.4.7 // indirect
27+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
28+
golang.org/x/sys v0.36.0 // indirect
29+
golang.org/x/text v0.3.8 // indirect
30+
)

go.sum

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
2+
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
3+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
4+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
5+
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
6+
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
7+
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
8+
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
9+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
10+
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
11+
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
12+
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
13+
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
14+
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
15+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
16+
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
17+
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
18+
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
19+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
20+
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
21+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
22+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
23+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
24+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
25+
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
26+
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
27+
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
28+
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
29+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
30+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
31+
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
32+
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
33+
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
34+
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
35+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
36+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
37+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
38+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
39+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
40+
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
41+
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
42+
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
43+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44+
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
45+
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
46+
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
47+
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=

0 commit comments

Comments
 (0)