이 저장소가 대체 무엇인가.
개발자가 Claude Code로 버그를 고치고, Codex로 API를 설계하고, Cursor로 리팩터링을 하면 대화 로그가 각자 다른 경로에 다른 형식으로 쌓인다. cass는 이 로그를 20가지 에이전트에서 자동 수집해 SQLite와 Tantivy 풀텍스트 인덱스로 통합한 뒤, 60ms 이내에 키워드·의미(시맨틱) 검색을 제공한다.
터미널에서 cass를 치면 3분할 TUI가 뜨고, 타이핑하는 즉시 과거 대화가 점수순으로 나타난다. 로봇 모드(--robot)로 에이전트가 자신의 과거 세션을 조회하는 것도 지원한다.
~/.claude/projects/에, Codex는 Rollout JSONL로 ~/.codex/sessions/에, Cursor는 SQLite로 저장한다. 형식이 제각각이다.트렌딩 이유 · 문제 배경 · 경쟁 대비 장점.
코딩 AI 에이전트가 폭발적으로 늘어난 2025~2026년, 개발자의 하루는 여러 에이전트와 수십 번의 대화로 채워진다. 그런데 "그때 그 해결책이 어디 있었더라?"라는 질문에 답할 도구가 없었다. 각 에이전트 앱은 자기 로그만 보여주고, 앱 간 교차 검색은 불가능하다.
| 문제 상황 | 기존 방법의 한계 | cass의 접근 |
|---|---|---|
| 로그 포맷 파편화 | 에이전트마다 JSONL·JSON·SQLite·Markdown 혼재 | 커넥터(connector)로 20개 포맷 표준화 후 단일 스키마 저장 |
| 앱 간 검색 불가 | Claude Code 앱 안에서만 찾을 수 있음 | 단일 Tantivy 인덱스 + 에이전트 필터로 교차 검색 |
| 자연어 쿼리 불가 | grep/파일탐색기는 정확한 단어만 일치 | 로컬 ML 임베딩(MiniLM 등) 시맨틱 검색 선택 가능 |
| 다중 머신 분산 | 노트북·서버·데스크탑 로그가 제각각 | SSH rsync 소스로 원격 머신 로그도 로컬 통합 |
| 프라이버시 걱정 | 클라우드 동기화 서비스에 민감 대화 업로드 | 완전 로컬 실행, 외부 서버 없음 |
시맨틱 검색을 위한 ML 모델도 Hugging Face에서 한 번 다운로드 후 로컬 ONNX로 추론한다. 쿼리 때 네트워크가 필요 없다. 또한 cass search "..." --robot --json으로 AI 에이전트 자신이 과거 기억을 조회하는 "메타 에이전트 패턴"을 지원한다.
v0.6.19 현재 status: alpha 배지를 달고 있다. API·스키마·파일 경로가 버전 간에 바뀔 수 있다. 프로덕션 파이프라인에 바로 연결하기보다 개인 개발 환경에서 탐색적으로 사용하는 것을 권장한다.
Rust 생태계를 중심으로 한 고성능 로컬 검색 도구.
| 항목 | 기술 | 역할 / 비고 |
|---|---|---|
| 주 언어 | Rust (edition 2024) | 안전한 메모리, 제로코스트 추상, 크로스플랫폼(Linux·macOS·Windows) 단일 바이너리 |
| 비동기 | Tokio (내부 dep) | 비동기 I/O, 백그라운드 인덱서 워커 |
| 병렬처리 | Rayon | CPU 병렬 커넥터 파싱, 인덱싱 fan-out |
| 패키지 | Cargo | workspace 모노레포 구조, nextest 테스트 |
| 항목 | 기술 | 역할 / 비고 |
|---|---|---|
| 정규화 DB | FrankenSQLite (자체 fork, FTS5 포함) | Conversation·Message·Snippet 정규화 저장. 모든 파생 인덱스의 단일 진실원(SSOT) |
| 풀텍스트 인덱스 | Tantivy (frankensearch 래퍼) | BM25 랭킹, 엣지 N-gram, snake_case 토크나이저. 60ms 이내 검색 |
| 시맨틱 벡터 DB | FSVI (frankensearch 독자 포맷) | 메모리맵 가능한 f32/f16 벡터 파일. HNSW 근사 최근접 탐색(hnsw_rs) |
| 임베딩 엔진 | FastEmbed + ONNX Runtime | MiniLM-L6-v2 (~90 MB), Snowflake-Arctic-S (~120 MB), Nomic-Embed (~270 MB) 선택 가능 |
| 해시 임베딩(폴백) | FNV-1a 해시 → 384-dim 벡터 | ML 모델 없을 때 즉시 가동. 의미 유사도는 없지만 토큰 중복 잡음. |
| 항목 | 기술 | 역할 / 비고 |
|---|---|---|
| TUI 엔진 | FrankenTUI (ftui) | 자체 제작 Elm 아키텍처 TUI. 스프링 애니메이션·반응형 레이아웃·Bayesian diff |
| 터미널 백엔드 | crossterm-compat | 크로스플랫폼 터미널 입출력 |
| 신택스 하이라이팅 | syntect + Prism.js (HTML 익스포트) | TUI 코드 블록 채색, HTML 익스포트용 |
| 마크다운 파싱 | pulldown-cmark | 대화 내 마크다운 렌더 |
| 크레이트 | 용도 |
|---|---|
| franken-agent-detection | 커넥터 파서 라이브러리 (Claude·Codex·Cursor 등 20개 포맷 파싱) |
| frankensearch | Tantivy 래퍼 + FSVI 벡터 인덱스 + 렉시컬/시맨틱 검색 통합 |
| frankensqlite | FTS5 활성화 SQLite 래퍼 (frankensqlite = fsqlite) |
| clap | CLI 인자 파싱 (derive 매크로) |
| serde / serde_json | JSON 직렬화 (세션 로그 파싱, 로봇 모드 출력) |
| notify | 파일시스템 와처 (세션 로그 실시간 갱신 감지) |
| blake3 / xxhash-rust | 컨텐츠 해싱 (중복 제거·인덱스 무결성) |
| half + memmap2 + bytemuck | f16 벡터 저장, 메모리맵 벡터 인덱스 |
| hnsw_rs | HNSW 근사 최근접 이웃 (시맨틱 검색 가속) |
| rayon + crossbeam-channel | 병렬 인덱싱 파이프라인 |
| walkdir + glob | 세션 로그 파일트리 탐색 |
세션 로그가 검색 결과가 되기까지의 7단계 파이프라인.
UI는 Elm 아키텍처를 따른다. Redux처럼 단방향 데이터 흐름이다.
키 입력 → CassMsg → CassApp::update() 순수 상태 전환 → Cmd(사이드이펙트 선언) → view() 렌더.
화면을 그릴 때 상태를 바꾸는 코드가 없으므로 버그 추적이 쉽다.
// Elm 아키텍처 요약 (src/ui/app.rs 기반)
Event (key/mouse/resize/tick)
↓
CassMsg (메시지 타입)
↓
CassApp::update(&mut self, msg) -> Cmd<CassMsg>
├─ 순수 상태 변경 → Cmd::none()
├─ 비동기 검색 → Cmd::task(SearchService::execute)
├─ 에디터 실행 → Cmd::task(EditorService::open)
└─ HTML 익스포트 → Cmd::task(ExportService::export)
↓
CassApp::view(&self, frame) ← 렌더만, 상태 변경 없음
도서관이 새 책 목록을 만들 때 기존 목록을 먼저 없애고 새 목록을 채우면 그 사이 이용자가 찾을 수 없다. cass는 새 인덱스를 완전히 완성한 뒤 한 번에 교체(renameat2)한다. 이용자(검색 요청)는 언제나 완성된 인덱스 중 하나만 본다 — 중간 상태는 없다.
렉시컬(BM25)과 시맨틱(벡터 유사도)은 점수 척도가 달라서 단순 합산이 불가능하다. cass는 Reciprocal Rank Fusion (RRF)으로 두 결과를 합친다. 각 결과의 순위(rank)만 사용하므로 점수 척도가 달라도 안전하다.
// RRF 공식 (K=60은 경험적 튜닝 상수)
RRF_score = Σ 1 / (60 + rank_i)
// 예시: 렉시컬 1위 + 시맨틱 3위인 문서
RRF = 1/(60+1) + 1/(60+3) = 0.01639 + 0.01587 = 0.03226
주요 폴더와 파일이 각각 무슨 역할을 하는가.
주목할 점: src/ui/app.rs는 47,556줄짜리 단일 파일이다.
FrankenTUI Elm 아키텍처의 전체 Model·Update·View가 여기에 집중돼 있다.
초보자가 전체를 읽기보다는 CassMsg 열거형부터 파악하면 흐름이 보인다.
이 레포에서 무엇을 배울 수 있는가.
구글처럼 키워드를 빠르게 찾는 기반 기술이다. 단어 → 문서 목록 매핑을 미리 만들어두는 역색인(inverted index), BM25 랭킹, 엣지 N-gram 인덱싱이 어떻게 "타이핑하는 즉시" 결과가 나오게 만드는지 Tantivy 코드에서 확인할 수 있다.
실습 아이디어: src/search/tantivy.rs에서 CassDocument 구조체를 찾아 어떤 필드가 색인되는지 파악하기.
"인증 오류" 대신 "authentication error"를 검색해도 같은 한국어 결과가 나오려면 단어 너머의 의미를 수치화해야 한다. 문장을 384차원 숫자 배열(벡터)로 만드는 임베딩, 그 벡터 간 코사인 유사도, HNSW 근사 탐색이 핵심이다.
실습 아이디어: cass models install로 MiniLM 설치 후 cass search "function to handle login" --mode semantic --robot으로 결과 확인.
렉시컬과 시맨틱의 강점을 합치는 방법이다. 각 랭킹의 순위만 쓰는 RRF 공식은 단순하지만 실제 검색 품질 향상 효과가 크다. Elasticsearch·Weaviate 같은 상용 검색 엔진도 같은 기법을 쓴다.
React의 단방향 데이터 흐름을 터미널 앱에 적용한 패턴이다. 상태(Model) → 이벤트(Msg) → 업데이트(update) → 렌더(view)의 순환이 버그 없는 UI를 만드는 이유를 src/ui/app.rs의 CassApp으로 체험할 수 있다.
JSONL·JSON·SQLite·Markdown 등 20가지 다른 포맷을 동일한 NormalizedConversation으로 변환하는 커넥터 패턴은 "어댑터 패턴"의 실전 사례다. 새 에이전트 지원을 추가하려면 Connector 트레이트를 구현하면 된다.
인덱스 재빌드 중에도 검색이 끊기지 않게 하는 기법이다. Linux의 renameat2(RENAME_EXCHANGE)로 두 디렉토리를 원자적으로 교환하는 코드는 src/indexer/mod.rs에서 찾을 수 있다. 데이터베이스·파일 기반 서비스의 핫-스왑 패턴으로 업계 전반에 쓰인다.
cass를 돌리려면 무엇이 필요한가.
| 항목 | 최소 / 권장 | 비고 |
|---|---|---|
| OS | Linux · macOS · Windows | Homebrew, Scoop, 원라인 curl 설치 지원 |
| CPU | 시맨틱 검색 시 AVX2 권장 | Sandy/Ivy Bridge (pre-AVX2) AMD 구형 CPU는 시맨틱 피처 없는 -baseline 바이너리 필요 |
| RAM | 기본 512 MB / 시맨틱 활성 시 1-2 GB | 벡터 인덱스는 메모리맵(mmap) → OS가 필요시 언로드 |
| 디스크 | 설치: 최소 200 MB / 시맨틱 모델: +90~270 MB | MiniLM 90 MB, Snowflake 120 MB, Nomic 270 MB. 벡터 인덱스는 메시지 수에 비례 (메시지당 ~1.5 KB f16) |
| Rust 툴체인 | 소스 설치 시 stable + 2 GB RAM | pre-built 바이너리 설치 시 Rust 불필요 |
| Linux glibc | 2.38 이상 | 구형 배포판·musl·NixOS는 소스 설치 폴백 |
| SSH (원격 소스) | 키 기반 인증 | cass sources setup으로 원격 머신 자동 프로브 |
메시지 10만 개(활발한 6개월 사용 기준)라면 Tantivy 렉시컬 인덱스 ~150 MB, 시맨틱 벡터(f16) ~150 MB 정도를 예상할 수 있다. SSD 256 GB 노트북이면 걱정할 수준이 아니다. 반면 RAM은 HNSW 그래프 탐색 시 일시적으로 치솟을 수 있어 메모리맵 설계가 중요하다.
난이도별 3~5개. 설치 후 바로 시작 가능한 것부터.
# Linux / macOS
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_session_search/main/install.sh" | bash -s -- --easy-mode
cass # TUI 실행 (타이핑으로 즉시 검색)
cass search "authentication" --robot --json | head -20 # JSON 출력 확인
Claude Code (~/.claude/projects)나 Codex (~/.codex/sessions) 세션이 있으면 바로 검색된다. 없으면 cass가 "세션 없음"을 알려준다.
cass models install --model all-minilm-l6-v2 # ~90 MB 다운로드
cass models verify # 설치 확인
# 렉시컬 vs 시맨틱 결과 비교
cass search "how to fix null pointer" --mode lexical --robot --limit 5
cass search "how to fix null pointer" --mode semantic --robot --limit 5
렉시컬은 "null", "pointer" 단어가 있는 결과, 시맨틱은 "NullPointerException", "dereference failed" 같은 표현도 매칭한다.
#!/bin/bash
# 현재 작업 디렉토리의 과거 세션 5개 요약
WORKSPACE="$(pwd)"
cass sessions --workspace "$WORKSPACE" --json --limit 5 | \
jq '.sessions[] | {title, agent, updated_at}'
# 특정 에러 메시지로 과거 해결책 찾기
cass search "ECONNREFUSED" --robot --json --limit 3 | \
jq '.results[] | {score, agent, snippet}'
AI 에이전트 시스템 프롬프트에 cass triage --json을 추가하면 에이전트가 자신의 과거 세션을 읽는 "자기참조 메모리"를 구현할 수 있다.
franken-agent-detection 라이브러리의 Connector 트레이트를 구현하는 구조체를 만들면 된다.
src/connectors/aider.rs를 참고해 Markdown 파서를 보면 패턴이 보인다.
// 개념 스케치 (실제 구현은 franken-agent-detection 참조)
struct MyAgentConnector;
impl Connector for MyAgentConnector {
fn scan_roots(&self, ctx: &ScanContext) -> Vec<ScanRoot> { ... }
fn parse(&self, path: &Path) -> Result<Vec<NormalizedConversation>> { ... }
}
# 서버의 SSH config가 ~/.ssh/config에 있을 때
cass sources setup # 대화형 마법사 실행
# 또는 수동 설정
cass sources add user@myserver.com --preset linux-defaults
cass sources sync --source myserver # rsync로 가져오기
cass search "docker compose" --robot --json # 두 머신 통합 검색
소스 ID가 검색 결과에 포함되어 "이 대화는 노트북 세션"과 "서버 세션"을 구분할 수 있다.
한 주씩 따라가는 "검색·임베딩·시스템 프로그래밍" 코스.
| 주차 | 주제 | 학습 자료 / 할 일 |
|---|---|---|
| 1주차 | 전문검색 기초 — 역색인·BM25 | Tantivy 공식 문서 정독. "The Art of Tokenization" 블로그 검색. cass search로 N-gram 매칭 체험 |
| 2주차 | SQLite 심화 — FTS5·WAL 모드 | SQLite 공식 FTS5 문서. PRAGMA journal_mode=WAL 효과 실험. frankensqlite 소스 읽기 |
| 3주차 | 벡터 임베딩 수학 기초 | 코사인 유사도 공식, L2 정규화 이유. Andrej Karpathy의 임베딩 강의. numpy로 간단 유사도 계산 실습 |
| 4주차 | HNSW — 근사 최근접 이웃 | HNSW 논문(arXiv:1603.09320) 요약. hnswlib Python 바인딩으로 100만 벡터 검색 실습 |
| 5주차 | 하이브리드 검색 — RRF 구현 | RRF 공식 논문(Cormack 2009). Python으로 두 랭킹 리스트를 RRF로 합치는 함수 직접 작성 |
| 6주차 | ONNX Runtime — 로컬 ML 추론 | ONNX 공식 문서. onnxruntime-python으로 MiniLM 모델 직접 로드 후 문장 인코딩 실습 |
| 7주차 | Rust TUI 프로그래밍 | ratatui 공식 예제. FrankenTUI 소스의 Elm 구조 분석. 간단한 검색 TUI 직접 만들기 |
| 8주차 | Rust 시스템 프로그래밍 — 원자적 파일 조작 | renameat2·rename 원자성 보장 원리. Rust std::fs + tempfile 크레이트로 안전한 파일 교체 패턴 실습 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| TUI | Terminal User Interface. 터미널에서 동작하는 그래픽 인터페이스. cass는 ftui 프레임워크로 구현. |
| 세션 로그 | 코딩 에이전트가 대화를 기록하는 파일. 포맷이 에이전트마다 다름(JSONL·JSON·SQLite·Markdown). |
| JSONL | JSON Lines. 각 줄이 하나의 JSON 객체인 포맷. Claude Code·Codex 세션 파일 형식. |
| 커넥터 (Connector) | 특정 에이전트 로그를 파싱해 NormalizedConversation으로 변환하는 어댑터 컴포넌트. |
| 역색인 (Inverted Index) | 단어 → 해당 단어를 포함하는 문서 목록 매핑. 검색 엔진의 핵심 자료구조. |
| BM25 | Best Match 25. TF-IDF 기반 전문검색 랭킹 알고리즘. Lucene·Tantivy·Elasticsearch 기본 사용. |
| 엣지 N-gram | "cal"로 "calculate"를 찾는 접두어 색인 기법. 인덱싱 때 연산해두어 검색 시 O(1) 조회. |
| Tantivy | Rust 기반 풀텍스트 검색 라이브러리. Java Lucene의 Rust 대응체. cass의 렉시컬 인덱스 엔진. |
| 시맨틱 검색 | 단어 일치가 아닌 의미 유사도 기반 검색. 문장을 벡터로 변환 후 코사인 유사도 비교. |
| 임베딩 (Embedding) | 텍스트를 고차원 숫자 배열(벡터)로 변환한 것. 의미적으로 비슷한 텍스트는 벡터 공간에서 가까움. |
| ONNX Runtime | ML 모델 추론 엔진. PyTorch·TensorFlow 모델을 플랫폼 독립적으로 실행. cass는 이것으로 MiniLM 로컬 추론. |
| FastEmbed | Qdrant가 만든 경량 임베딩 라이브러리. ONNX 바이너리를 내장 배포해 Rust에서 ML 추론 간편화. |
| HNSW | Hierarchical Navigable Small World. 수백만 벡터 중 가장 유사한 것을 빠르게 찾는 근사 알고리즘. 정확한 탐색보다 10-100배 빠름. |
| FSVI | frankensearch Vector Index. cass의 독자 벡터 인덱스 파일 포맷. f16 양자화·메모리맵 지원. |
| RRF (Reciprocal Rank Fusion) | 여러 랭킹 결과를 순위 기반으로 합치는 알고리즘. score = Σ 1/(K + rank). 점수 척도가 달라도 사용 가능. |
| 하이브리드 검색 | 렉시컬(BM25)과 시맨틱(벡터 유사도) 검색 결과를 RRF로 융합하는 방식. cass의 기본 검색 모드. |
| Elm 아키텍처 | Model-Update-View 단방향 데이터 흐름 패턴. React·Redux의 근원. cass TUI에 적용됨. |
| 원자적 교체 (Atomic Swap) | 구 파일과 신 파일을 한 번에 교환하는 OS 연산(renameat2). 교환 중간 상태가 없어 무중단 인덱스 갱신 가능. |
| 로봇 모드 (Robot Mode) | cass --robot / --json 옵션. stdout에 순수 JSON, stderr에 진단을 출력해 AI 에이전트나 스크립트가 파싱하기 쉽게 함. |
| FTS5 | SQLite의 내장 전문검색 확장. 기본 SQLite에 MATCH 쿼리와 역색인을 추가. cass는 FrankenSQLite로 FTS5를 활성화. |
| 쿼런틴 (Quarantine) | 손상되거나 검증 실패한 인덱스 파일을 격리해두는 영역. 자동 삭제 없이 보관하고 수동 GC를 지원. |