Unified TUI and CLI to index and search your local coding agent session history across 11+ providers (Codex, Claude, Gemini, Cursor, Aider, etc.)
npx skills add https://github.com/dicklesworthstone/coding_agent_session_search --skill cassInstallez cette compétence avec la CLI et commencez à utiliser le flux de travail SKILL.md dans votre espace de travail.
Unified, high-performance TUI to index and search your local coding agent history.
Aggregates sessions from Codex, Claude Code, Gemini CLI, Cline, OpenCode, Amp, Cursor, ChatGPT, Aider, Pi-Agent, GitHub Copilot Chat, Copilot CLI, OpenClaw, Clawdbot, Vibe, Crush, Hermes, Kimi Code, Qwen Code, and Factory (Droid) into a single, searchable timeline.
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_session_search/main/install.sh?$(date +%s)" \
| bash -s -- --easy-mode --verify
# Windows (PowerShell)
& ([scriptblock]::Create((irm "https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_session_search/main/install.ps1"))) -EasyMode -Verify
Installs the latest release by default. Pass --version <tag> / -Version <tag> to pin a specific version.
Or via package managers:
# Homebrew (Apple Silicon macOS + Linux)
brew install dicklesworthstone/tap/cass
# Windows (Scoop)
scoop bucket add dicklesworthstone https://github.com/Dicklesworthstone/scoop-bucket
scoop install dicklesworthstone/cass
Homebrew bottles are currently published for Linux and Apple Silicon macOS. On Intel macOS, use the install script with --from-source.
⚠️ Never run bare cass in an agent context — it launches the interactive TUI. Always use --robot or --json.
# 1) One-shot agent triage. Follow next_command when present.
cass triage --json
# From zero context, `cass --json` and `cass --robot` also resolve to triage.
# 2) Search across all agent history. Default search is hybrid-preferred:
# lexical is the fast required path; semantic refinement joins when ready.
cass search "authentication error" --robot --limit 5 --fields minimal
# 3) Find the current or recent session for this workspace
cass sessions --current --json
cass sessions --workspace "$(pwd)" --json --limit 5
# 4) View + expand a hit (use source_path/line_number from search output)
cass view /path/to/session.jsonl -n 42 --json
cass expand /path/to/session.jsonl -n 42 -C 3 --json
# 5) Discover the full machine API
cass capabilities --json
cass robot-docs guide
cass robot-docs schemas
# 6) Exclude a noisy agent harness from future indexing
cass sources agents list --json
cass sources agents exclude openclaw
cass sources agents include openclaw
Output conventions
Search asset contract
--robot --robot-meta) reports the requested mode, realized mode, semantic refinement status, and any lexical fallback reason when semantic assets are not ready.cass models install downloads the requested embedder on explicit request; cass never auto-downloads. Three embedders are supported via --model <name>: all-minilm-l6-v2 (alias minilm, ~90 MB; the default), snowflake-arctic-s (~120 MB), and nomic-embed (~270 MB). Air-gapped installs use --from-file <dir>. While the chosen model is absent, search silently uses lexical-only and reports fallback_mode="lexical" in health/status.cass triage --json is the safest first command for agents: it combines readiness, next_command, recommended_commands[], docs/schema pointers, starter workflows, and accepted recoveries. cass health --json and cass status --json remain the narrower truth surfaces for readiness, active rebuilds, and recovery.Lexical publish durability (atomic-swap)
src/indexer/mod.rs::publish_staged_lexical_index.<data_dir>/index/.lexical-publish-backups/<dated>/ for a bounded retention window. Default cap is 1 (keep just the most-recent prior generation for one-step rollback); override via the CASS_LEXICAL_PUBLISH_BACKUP_RETENTION env var (0 disables retention entirely, higher N keeps deeper history). Pruning runs after every successful publish and emits structured tracing::info! events with freed_bytes + retention_limit for observability.recover_or_finalize_interrupted_lexical_publish_backup on the next startup, which moves any orphaned canonical sidecar (.<name>.publish-in-progress.bak) into .lexical-publish-backups/ before the next publish lands.Quarantine, GC, and the doctor/diag surface
cass diag --json --quarantine enumerates every quarantined artifact (failed seed bundles, retained publish backups, quarantined lexical generations) with size_bytes, age_seconds, safe_to_gc, and a human-readable gc_reason. The safe_to_gc flag is advisory — it reflects retention policy + cleanup dry-run eligibility and is not wired to any automatic deletion path.cass doctor --json surfaces the same quarantine summary plus checks[] status for every diagnostic the tool runs. Without --fix, doctor is read-only (auto_fix_applied=false, auto_fix_actions=[], issues_fixed=0); with --fix it applies only the repairs whose dry-run plans are proven safe (currently: Track A analytics rebuild, Track B rollup rebuild via rebuild_token_daily_stats when the token_usage ledger is intact).cass doctor --fix never have a generation reclaimed silently — every quarantine stays on disk until an explicit cass models backfill / cass index --full --force-rebuild replaces the source data.Schema stability guarantees
triage, capabilities, health, status, diag, models status, models verify, models check-update, introspect, doctor, api-version, stats, sessions, search, pack) are pinned by golden-file regression tests under tests/golden/robot/. A change to any field name, type, or nullability fails the golden test suite and requires a deliberate regeneration pass (UPDATE_GOLDENS=1 rch exec -- env CARGO_TARGET_DIR=/tmp/cass-golden-target cargo test --test golden_robot_json --test golden_robot_docs).cass introspect --json's response_schemas block enumerates every schema in a stable alphabetical order (BTreeMap-backed — see bead coding_agent_session_search-8sl73).{error: {code, kind, message, hint, retryable}}) have a fixed shape. kind values are kebab-case; branch on err.kind, not on the numeric code, for codes ≥ 10 (see the Error Handling section below).If your runtime does not expose built-in mcp-agent-mail tools (for example, list_mcp_resources is empty), you can still coordinate via direct MCP HTTP calls.
~/.local/pipx/venvs/mcp-agent-mail/bin/python -m mcp_agent_mail.cli serve-http --host 127.0.0.1 --port 8765
/mcp)curl -sS -X POST http://127.0.0.1:8765/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"health","method":"tools/call","params":{"name":"health_check","arguments":{}}}'
# Ensure project
curl -sS -X POST http://127.0.0.1:8765/mcp -H 'Content-Type: application/json' -d \
'{"jsonrpc":"2.0","id":"ensure","method":"tools/call","params":{"name":"ensure_project","arguments":{"human_key":"/data/projects/coding_agent_session_search"}}}'
# Register agent
curl -sS -X POST http://127.0.0.1:8765/mcp -H 'Content-Type: application/json' -d \
'{"jsonrpc":"2.0","id":"register","method":"tools/call","params":{"name":"register_agent","arguments":{"project_key":"/data/projects/coding_agent_session_search","program":"codex","model":"gpt-5","name":"YourAgentName"}}}'
# Send message
curl -sS -X POST http://127.0.0.1:8765/mcp -H 'Content-Type: application/json' -d \
'{"jsonrpc":"2.0","id":"send","method":"tools/call","params":{"name":"send_message","arguments":{"project_key":"/data/projects/coding_agent_session_search","sender_name":"YourAgentName","to":["PeerAgent"],"subject":"[coord] hello","thread_id":"coord-2026-02-13","ack_required":true,"body_md":"Online and starting work."}}}'
# Fetch inbox
curl -sS -X POST http://127.0.0.1:8765/mcp -H 'Content-Type: application/json' -d \
'{"jsonrpc":"2.0","id":"inbox","method":"tools/call","params":{"name":"fetch_inbox","arguments":{"project_key":"/data/projects/coding_agent_session_search","agent_name":"YourAgentName","limit":50,"include_bodies":true}}}'
# Acknowledge message id 42
curl -sS -X POST http://127.0.0.1:8765/mcp -H 'Content-Type: application/json' -d \
'{"jsonrpc":"2.0","id":"ack","method":"tools/call","params":{"name":"call_extended_tool","arguments":{"tool_name":"acknowledge_message","arguments":{"project_key":"/data/projects/coding_agent_session_search","agent_name":"YourAgentName","message_id":42}}}}'
mcp_agent_mail defaults to sqlite+aiosqlite:///./storage.sqlite3. That means the server working directory determines which mailbox database you are using. To avoid "project not found" confusion, start the server from the same directory your team expects for mailbox state.
Three-pane layout with semantic styling: filter bar with pills, results list with color-coded agents and score tiers, and syntax-highlighted detail preview with tab navigation
Full conversation rendering with markdown formatting, code blocks, headers, and structured content
Built-in help screen (press F1 or ?) with all shortcuts, filters, modes, and navigation tips
AI coding agents are transforming how we write software. Claude Code, Codex, Cursor, Copilot, Aider, Pi-Agent; each creates a trail of conversations, debugging sessions, and problem-solving attempts. But this wealth of knowledge is scattered and unsearchable:
cass treats your coding agent history as a unified knowledge base. It:
snake_case ("my_var" matches "my" and "var"), hyphenated terms, and code symbols (c++, foo.bar) correctly.reader.reload() ensures new messages appear in the search bar immediately without restarting.Local inference: Uses a FastEmbed embedder running ONNX on-device. Once the chosen model is installed, no network traffic is required to answer queries.
Opt-in acquisition: cass models install downloads the requested embedder from Hugging Face on explicit request and verifies SHA256 checksums. Nothing is fetched until you run the install command. Three embedders are supported:
all-minilm-l6-v2 — cass models install --model all-minilm-l6-v2 (alias: minilm). 384-dim. ~90 MB. The default; fastest. Best for general English semantic similarity.snowflake-arctic-s — cass models install --model snowflake-arctic-s. 384-dim. ~120 MB. Stronger MTEB scores than MiniLM at similar cost; good drop-in replacement for code-heavy corpora.nomic-embed — cass models install --model nomic-embed (alias: nomic-embed-text-v1.5). 768-dim. ~270 MB. Highest recall on long-context queries; trade off larger index footprint.Removal mirrors install: cass models remove --model <name> accepts the same alias set. The same alias map is honored by the daemon embedding worker (see src/daemon/worker.rs::resolve_embedder_kind) so background indexing accepts whatever the operator installed.
Air-gapped install: cass models install --model <name> --from-file <dir> accepts a pre-downloaded model directory so you can bring the assets in yourself.
Required files (all must be present after install; cass models verify checks them):
model.onnxtokenizer.jsonconfig.jsonspecial_tokens_map.jsontokenizer_config.jsonVector index: Stored as vector_index/index-<embedder>.fsvi in the data directory.
Lexical fail-open: While the model is absent, cass returns lexical-only results and reports fallback_mode="lexical" in health/status; search never blocks on semantic assets.
When ML model files are not installed, cass uses a deterministic hash-based embedder as a fallback. While not "truly" semantic (it captures lexical overlap rather than meaning), it provides useful functionality:
| Feature | ML Model (MiniLM) | Hash Embedder (FNV-1a) |
|---|---|---|
| Meaning Understanding | ✅ "car" ≈ "automobile" | ❌ Exact tokens only |
| Initialization Time | ~500ms (model loading) | <1ms (instant) |
| Network Dependency | None (after install) | None |
| Disk Footprint | ~90MB model files | 0 bytes |
| Deterministic | ✅ Same input = same output | ✅ Same input = same output |
Algorithm:
When to Use:
Override: Set CASS_SEMANTIC_EMBEDDER=hash to force hash mode even when ML model is available.
cass uses the frankensearch FSVI vector index format (.fsvi) for storing semantic embeddings.
Features:
f32 and f16 storage for smaller on-disk sizeIndex Location: ~/.local/share/coding-agent-search/vector_index/index-<embedder>.fsvi
cass supports three search modes, selectable via --mode flag or Alt+S in the TUI:
| Mode | Algorithm | Best For |
|---|---|---|
| Lexical | BM25 full-text | Exact term matching, code searches |
| Semantic | Vector similarity | Conceptual queries, "find similar" |
| Hybrid (default) | Reciprocal Rank Fusion with lexical fail-open | Balanced precision and recall |
Lexical Search: Uses Tantivy's BM25 implementation with edge n-grams for prefix matching. Best when you know the exact terms you're looking for. The lexical index is derived from SQLite; if it is missing, stale, or incompatible, cass reports the state and rebuilds through the normal indexing path from the canonical database.
Semantic Search: Computes vector similarity between query and indexed message embeddings. Finds conceptually related content even without exact term overlap. Requires either the ML model (MiniLM) or falls back to hash embedder.
Hybrid Search: The default. It combines lexical and semantic results using Reciprocal Rank Fusion (RRF) when semantic assets are ready, and it fails open to lexical when semantic enrichment is still catching up or disabled:
RRF_score = Σ 1 / (K + rank_i)
Where K=60 (tuning constant) and rank_i is the position in each result list. This balances the precision of lexical search with the recall of semantic search.
# CLI examples
cass search "authentication" --mode lexical --robot
cass search "how to handle user login" --mode semantic --robot
cass search "auth error handling" --mode hybrid --robot
foo* - Prefix match (finds "foobar", "foo123")*foo - Suffix match (finds "barfoo", "configfoo")*foo* - Substring match (finds "afoob", "configuration")*term* wildcards to broaden matches. Visual indicator shows when fallback is active.Up/Down arrows.F12) that prioritizes exact matches over wildcard/fuzzy results.--highlight in robot mode to wrap matching terms with markers (**bold** for text, <mark> for HTML output).Powered by FrankenTUI (ftui) — a high-performance Elm-architecture TUI framework with adaptive frame budgets, Bayesian diff selection, and spring-based animations.
📦 Indexing 150/2000 (7%) ▁▂▄▆█)—plus active filters.Ctrl+Enter, then open all in your editor with Ctrl+O. Confirmation prompt for large batches (≥12 items)./ to search within the detail pane; matches highlighted with n/N navigation.recent/balanced/relevance/quality with F12; quality mode penalizes fuzzy matches.A.cass tui --inline to keep terminal scrollback intact. The UI anchors to a region of the terminal while logs scroll normally. Configure with --ui-height <rows> and --anchor top|bottom.cass tui --record-macro session.macro for reproducible bug reports and workflow automation. Events are saved as human-readable JSONL with full timing data.cass tui --asciicast demo.cast.
Export conversations as styled, portable HTML files with optional encryption:
@tailwindcss/browser) and Prism.js syntax-highlighting assets are loaded from cdn.jsdelivr.net for full fidelity.onerror="...no-prism" — code blocks remain readable offline in plain monospace. Tailwind CDN does not currently have a built-in fallback: layout utilities require network on first open (the page is still legible but unstyled). Air-gapped archival users should note this limitation.TUI Usage: Press e in the detail view to open the export modal, or Ctrl+E for quick export with defaults.
CLI Usage:
# Basic export
cass export-html /path/to/session.jsonl
# With encryption
printf '%s\n' "secret" | cass export-html /path/to/session.jsonl --encrypt --password-stdin
# Custom output location
cass export-html session.jsonl --output-dir ~/exports --filename "my-session"
# Open in browser after export
cass export-html session.jsonl --open
# Robot mode (JSON output)
cass export-html session.jsonl --json
Ingests history from 20 local agents, normalizing them into a unified Conversation -> Message -> Snippet model:
~/.codex/sessions (Rollout JSONL)~/.gemini/tmp (Chat JSON)~/.claude/projects (Session JSONL)~/.clawdbot/sessions (Session JSONL)~/.vibe/logs/session/*/messages.jsonl (Session JSONL).opencode directories (SQLite)~/.local/share/amp & VS Code storage~/Library/Application Support/Cursor/User/ global + workspace storage (SQLite state.vscdb)~/Library/Application Support/com.openai.chat (v1 unencrypted JSON; v2/v3 encrypted—see Environment)~/.aider.chat.history.md and per-project .aider.chat.history.md files (Markdown)~/.pi/agent/sessions (Session JSONL with thinking content)github.copilot-chat (JSON)~/.copilot/session-state, legacy ~/.copilot/history-session-state, and gh copilot config paths (JSONL/JSON)~/.openclaw/agents/*/sessions (Session JSONL)~/.crush/crush.db and per-project .crush/crush.db (SQLite)~/.hermes/state.db and project-local .hermes/state.db (SQLite)~/.kimi/sessions/*/*/wire.jsonl (Session JSONL)~/.qwen/tmp/*/chats/session-*.json (Chat JSON)~/.factory/sessions (JSONL files organized by workspace slug)Pi-Agent parses JSONL session files with rich event structure:
~/.pi/agent/sessions/ (override with PI_CODING_AGENT_DIR env var)session_start, message, model_change, thinking_level_change*_*.jsonl pattern in sessions directoryOpenCode reads SQLite databases from workspace directories:
.opencode/ directories (scans recursively from home).opencode containing database filesSearch across agent sessions from multiple machines—your laptop, desktop, and remote servers—all from a single unified index. cass uses SSH/rsync to efficiently sync session data, tracking provenance so you know where each conversation originated.
The easiest way to configure multi-machine search is the interactive setup wizard:
cass sources setup
What the wizard does:
~/.ssh/configsources.toml with correct paths and mappingsWizard options:
| Flag | Purpose |
|---|---|
--hosts <names> |
Configure only specific hosts (comma-separated) |
--dry-run |
Preview changes without applying them |
--non-interactive |
Use auto-detected defaults for scripting |
--skip-install |
Don't install cass on remotes |
--skip-index |
Don't run indexing on remotes |
--skip-sync |
Don't sync data after setup |
--resume |
Resume an interrupted setup |
--json |
Output progress as JSON (for automation) |
Examples:
# Full interactive wizard
cass sources setup
# Configure specific hosts only
cass sources setup --hosts css,csd,yto
# Preview without making changes
cass sources setup --dry-run
# Resume interrupted setup
cass sources setup --resume
# Non-interactive for CI/CD
cass sources setup --non-interactive --hosts myserver --skip-install
Resumable state: If setup is interrupted (Ctrl+C, connection lost), state is saved to the cache directory (~/.cache/cass/setup_state.json on Linux). Resume with --resume.
When the wizard installs cass on remote machines, it uses an intelligent fallback chain:
| Priority | Method | Speed | Requirements |
|---|---|---|---|
| 1 | cargo-binstall | ~30s | cargo-binstall pre-installed, compatible release binary |
| 2 | Pre-built binary | ~10s | curl/wget, GitHub access, compatible release binary |
| 3 | cargo install | ~5min | Rust toolchain, 1GB disk, 2GB RAM |
| 4 | Full bootstrap | ~10min | curl, 1GB disk, 2GB RAM (installs rustup) |
Resource Requirements:
What Gets Installed:
cass binary (location depends on method: ~/.cargo/bin/cass for cargo-based, ~/.local/bin/cass for pre-built binary)Installation Progress: The wizard shows real-time progress for each stage:
Installing cass on laptop...
[1/4] Checking environment... ✓
[2/4] Downloading binary... ████████░░ 80%
[3/4] Verifying checksum... ✓
[4/4] Setting up PATH... ✓
Use --skip-install if you prefer to install manually on remotes.
The setup wizard automatically discovers SSH hosts from your configuration:
Discovery Sources:
~/.ssh/config (parses Host entries)*, ?) are automatically excludedProbe Results (for each discovered host):
| Check | Purpose |
|---|---|
| Connectivity | Can we establish SSH connection? |
| cass Version | Is cass already installed? What version? |
| Agent Data | Which agents have session data? |
| Session Count | How many conversations exist? |
| System Info | OS, architecture, disk space, memory |
Probe Caching: Results are cached for 5 minutes to speed up repeated setup attempts. Cache clears automatically on expiry.
For manual configuration without the wizard:
# Add a remote machine using platform presets
cass sources add [email protected] --preset macos-defaults
# Or specify paths explicitly
cass sources add dev@workstation --path ~/.claude/projects --path ~/.codex/sessions
# Sync sessions from all configured sources
cass sources sync
# Check source health and connectivity
cass sources doctor
Remote source diagnostics are intentionally local-only. cass triage --json,
cass doctor --json, cass health --json, and cass status --json report the
remote_source_sync summary from cass-owned evidence: sources.toml,
sync_status.json, the local remotes/<source>/mirror/ copy, and archive DB
provenance rows. They do not open SSH sessions, mutate remote machines, or
rewrite provider session logs while classifying source gaps.
This matters because agent harnesses can prune their own logs. If a laptop is
retired, a remote path disappears, or a provider truncates older sessions, the
cass archive DB and cass-owned local mirror may be the only remaining evidence
for those conversations. Treat gap names such as remote_source_unavailable,
remote_source_pruned, local_archive_ahead_of_remote, and
remote_copy_ahead_verified as preservation signals first: keep the archive and
mirror intact, then run the recommended cass sources sync --all --json or
source-specific sync command after reviewing the reported evidence.
Raw-mirror retention is explicit and audited. Use cass mirror prune --older-than 90d --json or cass mirror prune --max-size 100GB --json to get a
dry-run plan; add --apply only after reviewing the manifest/blob list. Add
--keep-tag <tag> to pin captures linked to tagged conversations. prune
holds down blobs referenced by captures from the last 7 days by default, writes
raw-mirror/v1/pruned.jsonl for every non-empty plan, and refuses apply mode
while an index/watch job is active.
Sources are configured in the platform config directory (Linux: ~/.config/cass/sources.toml, macOS: ~/Library/Application Support/cass/sources.toml):
[[sources]]
name = "laptop"
type = "ssh"
host = "[email protected]"
paths = ["~/.claude/projects", "~/.codex/sessions"]
sync_schedule = "manual"
[[sources]]
name = "workstation"
type = "ssh"
host = "[email protected]"
paths = ["~/.claude/projects"]
sync_schedule = "daily"
# Path mappings rewrite remote paths to local equivalents
[[sources.path_mappings]]
from = "/home/dev/projects"
to = "/Users/me/projects"
# Agent-specific mappings
[[sources.path_mappings]]
from = "/opt/work"
to = "/Volumes/Work"
agents = ["claude_code"]
Configuration Fields:
| Field | Description |
|---|---|
name |
Friendly identifier (becomes source_id) |
type |
Connection type: ssh or local |
host |
SSH host (user@hostname) |
paths |
Paths to sync (supports ~ expansion) |
sync_schedule |
manual, hourly, or daily |
path_mappings |
Rewrite remote paths to local equivalents |
# List configured sources
cass sources list [--verbose] [--json]
# Add a new source
cass sources add <user@host> [--name <name>] [--preset macos-defaults|linux-defaults] [--path <path>...] [--no-test]
# Remove a source
cass sources remove <name> [--purge] [-y]
# Check connectivity and config
cass sources doctor [--source <name>] [--json]
# Sync sessions
cass sources sync [--source <name>] [--no-index] [--verbose] [--dry-run] [--json]
If one harness is generating mostly junk or looped output, you can disable it persistently even if its files remain on disk:
# Inspect current include/exclude state
cass sources agents list --json
# Stop indexing this harness in future runs
cass sources agents exclude openclaw
# Re-enable it later
cass sources agents include openclaw
cass stores this preference in sources.toml (~/.config/cass/sources.toml on Linux, ~/Library/Application Support/cass/sources.toml on macOS), so future scans, syncs, and watch-mode updates remember it automatically.
By default, cass sources agents exclude <agent> also removes already archived local data for that agent and rebuilds the lexical index so the exclusion frees space instead of only blocking future imports.
If you want to block future indexing but keep the data already archived:
cass sources agents exclude openclaw --keep-indexed-data
The sync engine uses rsync over SSH for efficient delta transfers, with automatic SFTP fallback:
Transfer Methods (auto-detected):
| Method | When Used | Characteristics |
|---|---|---|
| rsync | rsync available on both ends | Delta transfers, compression, progress stats |
| SFTP | rsync unavailable | Full file transfers via SSH native protocol |
Safety Guarantees:
--delete flag—remote deletions never propagate locallyTransfer Configuration:
| Setting | Default | Purpose |
|---|---|---|
| Connection timeout | 10s | Fail fast on unreachable hosts |
| Transfer timeout | 5 min | Allow large initial syncs |
| Compression | Enabled | Reduce bandwidth for text-heavy sessions |
| Partial transfers | Enabled | Resume interrupted syncs |
rsync Flags Used:
-avz --stats --partial --protect-args --timeout=300 \
-e "ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new"
Where -avz = archive mode + verbose + compression.
Data Flow:
Remote: ~/.claude/projects/
↓ (rsync over SSH)
Local: ~/.local/share/coding-agent-search/remotes/<source>/<path>/
↓ (connector scan)
Index: agent_search.db + tantivy_index/
Where <path> is a filesystem-safe version of the remote path (e.g., .claude_projects).
Sessions from remotes are indexed alongside local sessions, with provenance tracking to identify origin.
When viewing sessions from remote machines, workspace paths may not exist locally. Path mappings rewrite these paths so file links work on your local machine:
# List current mappings
cass sources mappings list laptop
# Add a mapping
cass sources mappings add laptop --from /home/user/projects --to /Users/me/projects
# Test how a path would be rewritten
cass sources mappings test laptop /home/user/projects/myapp/src/main.rs
# Output: /Users/me/projects/myapp/src/main.rs
# Agent-specific mappings (only apply for certain agents)
cass sources mappings add laptop --from /opt/work --to /Volumes/Work --agents claude_code,codex
# Remove a mapping by index
cass sources mappings remove laptop 0
In the TUI, filter sessions by origin:
Remote sessions display with a source indicator (e.g., [laptop]) in the results list.
Each conversation tracks its origin:
source_id: Machine identifier (e.g., "laptop", "workstation")source_kind: local or remoteworkspace_original: Original path on the remote machine (before path mapping)These fields appear in JSON/robot output and enable filtering:
cass search "auth error" --source laptop --json
cass timeline --days 7 --source remote
cass stats --by-source
cass is purpose-built for consumption by AI coding agents—not just as an afterthought, but as a first-class design goal. When you're an AI agent working on a codebase, your own session history and those of other agents become an invaluable knowledge base: solutions to similar problems, context about design decisions, debugging approaches that worked, and institutional memory that would otherwise be lost.
Imagine you're Claude Code working on a React authentication bug. With cass, you can instantly search across:
This cross-pollination of knowledge across different AI agents is transformative. Each agent has different strengths, different context windows, and encounters different problems. cass unifies all this collective intelligence into a single, searchable index.
cass teaches agents how to use it—no external documentation required:
# First-stop capability contract for agents
cass triage --json
cass capabilities --json
# → {"version": "...", "workflows": [...], "mistake_recoveries": [...], "commands": [...], "exit_codes": [...], "env_vars": [...]}
# Full API schema with argument types, defaults, and response shapes
cass introspect --json
# Topic-based help optimized for LLM consumption
cass robot-docs commands # All commands and flags
cass robot-docs schemas # Response JSON schemas
cass robot-docs examples # Copy-paste invocations
cass robot-docs exit-codes # Error handling guide
cass robot-docs guide # Quick-start walkthrough
AI agents sometimes make syntax mistakes. cass aggressively normalizes input to maximize acceptance when intent is clear:
| What you type | What cass understands |
Correction note |
|---|---|---|
cass -robot --limit=5 |
cass --robot --limit=5 |
Single-dash long flags normalized |
cass --Robot --LIMIT 5 |
cass --robot --limit 5 |
Case normalized |
cass search "auth" --max_results 5 |
cass search "auth" --limit 5 |
Snake-case long flag normalized before alias recovery |
cass find "auth" |
cass search "auth" |
find/query/q → search via alias table |
cass --robot-docs |
cass robot-docs |
Flag-as-subcommand detected |
cass commands --json |
cass robot-docs commands |
Robot-docs topic shorthand detected |
cass schemas --json |
cass robot-docs schemas |
Robot-docs topic shorthand detected |
cass ready --json |
cass triage --json |
One-shot triage alias |
cass preflight --json |
cass triage --json |
One-shot triage alias |
cass --json |
cass triage --json |
Top-level robot request defaults to safe preflight |
cass --robot |
cass triage --json |
Top-level robot request defaults to safe preflight |
cass --json search "auth" |
cass search "auth" --json |
Leading structured flag moved to the robot-capable subcommand |
cass --robot status |
cass status --json |
Leading robot flag canonicalized to JSON output |
cass answer "auth" --json |
cass pack "auth" --json |
Cited-handoff aliases normalized to answer pack |
cass why auth failed --json --max-evidence 3 |
cass pack "auth failed" --json --max-evidence 3 |
Question/RC prompt aliases normalized to answer pack |
cass auth failed --json --max-evidence 3 |
cass pack "auth failed" --json --max-evidence 3 |
Bare robot queries with pack-only flags become answer packs |
cass search auth failed --json --max-evidence 3 |
cass pack "auth failed" --json --max-evidence 3 |
Explicit robot search with pack-only flags becomes an answer pack |
cass html-export session.jsonl --json |
cass export-html session.jsonl --json |
Reversed HTML export aliases normalized to the archive exporter |
cass current --json |
cass sessions --current --json |
Current-session shorthand normalized to session discovery |
cass sessions current --json |
cass sessions --current --json |
Positional current accepted as the sessions current flag |
cass search --query "auth" --json |
cass search "auth" --json |
Named query option converted to required positional query |
cass search --q "auth" --json |
cass search "auth" --json |
Short/familiar query aliases converted to required positional query |
cass search auth error --json |
cass search "auth error" --json |
Adjacent unquoted query words folded into one search |
cass auth error --json |
cass search "auth error" --json |
Unquoted robot-mode query words folded into search |
cass search --agent codex --limit 5 auth error --json |
cass search "auth error" --agent codex --limit 5 --json |
Query moved before leading search filters |
cass view --path session.jsonl --line 42 --json |
cass view session.jsonl --line 42 --json |
Named path option converted to required positional path |
cass view session.jsonl --line-number 42 --json |
cass view session.jsonl --line 42 --json |
Search result field name accepted as a line alias |
cass view session.jsonl line_number=42 --json |
cass view session.jsonl --line 42 --json |
Search result field assignment accepted as a line option |
cass view source_path=session.jsonl source_id=local line_number=42 --json |
cass view session.jsonl --source local --line 42 --json |
Search hit field bundle accepted as a follow-up command |
cass search "auth" --format json |
cass search "auth" --robot-format json |
Familiar format spelling converted to robot format |
cass search "auth" --output json |
cass search "auth" --robot-format json |
Familiar output spelling converted to robot format |
cass help search --json |
cass robot-docs commands |
Structured help intent routed to the machine-readable command reference |
cass --format json status |
cass status --robot-format json |
Leading format request moved to the target subcommand |
cass search "auth" --max-results 5 |
cass search "auth" --limit 5 |
Result-count alias converted to canonical limit |
cass search "auth" -n 5 |
cass search "auth" --limit 5 |
Familiar short count flag converted to canonical limit |
cass search "auth" --last 7 --before now |
cass search "auth" --since -7d --until now |
Familiar time-window aliases converted to canonical filters |
cass search "auth" last=7d before=now |
cass search "auth" --since -7d --until now |
Bare time-window assignments converted to canonical filters |
cass search "auth" --provider codex |
cass search "auth" --agent codex |
Provider/tool/connector aliases converted to canonical agent filter |
cass search "auth" provider=codex |
cass search "auth" --agent codex |
Bare provider assignment converted to canonical agent filter |
cass search auth provider codex limit 5 |
cass search auth --agent codex --limit 5 |
Bare filter key/value pairs after a query converted to canonical flags |
cass search --limt 5 |
cass search --limit 5 |
Flag typos within Levenshtein distance ≤2 corrected |
The CLI applies multiple normalization layers:
--limt → --limit). Subcommand typos are NOT fuzzy-corrected — use one of the documented aliases instead (see layer 5 below). A typo that isn't a known alias will produce a clap usage error with the canonical form in the hint.--Robot, --LIMIT → --robot, --limit--max_results, --data_dir, and other known snake_case long flags become canonical kebab-case before alias recovery runs-robot → --robot (common LLM mistake)ready/preflight → triage; find/query/q/grep/lookup → search; answer/evidence/bundle/handoff/why/explain/rca/root-cause/summarize → pack; html-export/html_export/exporthtml → export-html; ls/list/info/summary → stats; st/state → status; reindex/idx/rebuild → index; show/get/read → view; docs/help-robot/robotdocs → robot-docscommands, schemas, examples, exit-codes, and guide become robot-docs <topic> instead of falling through to search; command topics such as doctor and sources use structured help (cass help doctor --json, cass sources --help --json)cass --json, cass --robot, or cass --robot-format json with no subcommand runs read-only triage--json/--robot before a robot-capable subcommand is moved onto that subcommand--query/--q/--text/--pattern for search/pack and --path/--source-path/--file/--session for drill-down/export commands become the required positional argumentsearch/pack become one query positional--format json|jsonl|compact|sessions|toon, --output json|jsonl|compact|sessions|toon, and --output-format ... are accepted as --robot-format ... on robot-capable commands; export --format ... and export --output <file> keep their export meaningshelp --json, help commands --json, and search --help --json route to robot-docs guide / robot-docs commands; plain --help stays native clap help--max-results, --num-results, --results, --count, --top-k, and -n become --limit on commands with result limits--last 7, --before now, last=7d, and before=now become canonical --since/--until filters--provider, --tool, --connector, and matching assignments become canonical --agent filters on search-like commandsprovider codex, limit 5, and last 7d become canonical filter flags before the remaining words are folded into the querysearch with pack-only flags such as --max-evidence, --max-sessions, or --freshness-policy becomes pack, not implicit or explicit search--line-number, --line_number, and line_number=42 become the canonical drill-down --line optionsource_path=... source_id=... line_number=... can be pasted into follow-up view/expand commands and becomes the canonical path/source/line formsearch query unless they look like a subcommand typocurrent, current-session, and sessions current become sessions --currentWhen corrections are applied, cass emits a teaching note to stderr so agents learn the canonical syntax.
Every command supports machine-readable output:
# Pretty-printed JSON (default robot mode)
cass search "error" --robot
# Streaming JSONL: one hit per line. Add --robot-meta to prepend a
# _meta header line (elapsed_ms, next_cursor, state, index_freshness).
cass search "error" --robot-format jsonl # hits only
cass search "error" --robot-format jsonl --robot-meta # 1 _meta header + hits
# Compact single-line JSON (minimal bytes)
cass search "error" --robot-format compact
# Include performance metadata
cass search "error" --robot --robot-meta
# → { "hits": [...], "_meta": { "elapsed_ms": 12, "cache_hit": true, "wildcard_fallback": false, ... } }
# Deterministic answer pack for handoff prompts
cass pack "why did checkout fail" --robot --max-tokens 12000 --limit 40
# Freshness-sensitive pack: fail if selected evidence is outside the window
cass pack "checkout timeout after redirect" --robot \
--freshness-policy strict --freshness-window-seconds 604800 \
--max-tokens 12000 --require-evidence
# Token-budgeted pack for pasting into another agent
cass pack "checkout timeout after redirect" --robot \
--max-tokens 4000 --max-evidence 8 --max-sessions 3 --max-excerpt-chars 600
# Pipeline from broad search to a bounded cited handoff
cass search "checkout timeout" --robot-format sessions \
| cass pack "checkout timeout root cause" --robot --sessions-from -
Design principle: stdout contains only parseable JSON data; all diagnostics, warnings, and progress go to stderr.
Use search when you are still exploring candidate sessions. Use pack when
you need a compact, cited, extractive artifact to hand to another agent or a
human operator. Use status/health before trusting freshness-sensitive output,
and use doctor only for diagnostics or safe repair workflows. Use
export-html when you need a full browsable session archive; packs are
token-budgeted evidence bundles, not full exports and not external
summarization.
Pack robot output includes health, freshness, privacy, and warnings.
Warnings such as privacy_redactions_applied, semantic_fallback_lexical,
or no_evidence_found are data, not prose; branch on the JSON fields before
copying the pack into another tool. Stale selected evidence is structural:
inspect freshness.stale_evidence_count.
LLMs have context limits. cass provides multiple levers to control output size:
| Flag | Effect |
|---|---|
--fields minimal |
Only source_path, line_number, agent |
--fields summary |
Adds title, score |
--fields score,title,snippet |
Custom field selection |
--max-content-length 500 |
Truncate long fields (UTF-8 safe, adds "...") |
--max-tokens 2000 |
Soft budget (~4 chars/token); adjusts truncation dynamically |
--limit 5 |
Cap number of results |
cass pack "query" --robot |
Build a cited handoff pack from selected search evidence |
pack --max-tokens N |
Set the pack planner's soft budget |
pack --max-evidence N |
Cap evidence items selected into the pack |
pack --max-sessions N |
Limit how many sessions can contribute evidence |
pack --max-excerpt-chars N |
Shorten each cited excerpt before token estimation |
pack --fields summary |
Return top-level summary fields for a smaller JSON envelope |
pack --freshness-policy strict --freshness-window-seconds N |
Reject stale evidence instead of silently mixing it into a pack |
pack --sessions-from FILE |
Restrict pack evidence to newline-delimited session paths; use - for stdin |
Truncated fields include a *_truncated: true indicator so agents know when they're seeing partial content.
Contributor verification for docs or contract changes should use rch, for example:
rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_cass_answer_pack_docs \
cargo test --test golden_robot_docs
Errors are structured, actionable, and include recovery hints. A real sample from cass search foo --robot against a fresh data dir:
{
"error": {
"code": 3,
"kind": "missing-index",
"message": "cass has not been initialized in <data_dir> yet, so search cannot run until the first index completes.",
"hint": "Run 'cass index --full' once to discover local sessions and build the initial archive.",
"retryable": true
}
}
Kind names are kebab-case (e.g. missing-index, missing-db, semantic-unavailable, embedder-unavailable, ambiguous-source, timeout, config, lock-busy, network). Agents that branch on err.kind should treat them as stable identifiers. The full set (~50 kinds as of 0.3.x) is defined in src/lib.rs; the canonical way to discover a kind programmatically is to trigger the condition and inspect err.kind from the JSON envelope.
Exit codes follow a semantic convention:
| Code | Meaning | Typical action |
|---|---|---|
| 0 | Success | Parse stdout |
| 1 | Health check failed | Run cass index --full |
| 2 | Usage error | Fix syntax (hint provided) |
| 3 | Index/DB missing | Run cass index --full (retryable: true) |
| 4 | Network error | Check connectivity |
| 5 | Data corruption | Run cass index --full --force-rebuild |
| 6 | Incompatible version | Update cass |
| 7 | Lock/busy | Retry later |
| 8 | Partial result | Increase --timeout or reduce scope |
| 9 | Unknown error | Check retryable flag |
| 10 | Config / timeout | Depends on err.kind |
| 11 | Config validation | Fix config |
| 12 | Source / SSH | Check remote host |
| 13 | Mapping / not-found | Depends on err.kind |
| 14 | I/O / mapping | Retry or inspect path |
| 15 | Semantic / embedder unavailable | Install model or --mode lexical |
| 20-21 | Model acquisition | Check err.kind, err.hint |
| 22 | I/O during model handling | Retry |
| 23 | Model download | Retry or use --from-file |
| 24 | I/O during model verify/install | Retry |
Codes ≥ 10 are domain-specific and the numeric value alone is ambiguous (e.g. code 10 maps to either config or timeout kinds depending on context). Agents should branch on err.kind from the JSON error envelope — not on the numeric code — when handling codes ≥ 10. See the Error Handling section above for the canonical kind list.
The retryable field tells agents whether a retry might succeed (e.g., transient I/O) vs. guaranteed failure (e.g., invalid path).
Beyond search, cass provides commands for deep-diving into specific sessions:
# Discover the current session for this workspace
cass sessions --current --json
# List recent sessions for a specific project
cass sessions --workspace /path/to/project --json --limit 5
# Export full conversation to shareable format
cass export /path/to/session.jsonl --format markdown -o conversation.md
cass export /path/to/session.jsonl --format json --include-tools
# Export as self-contained HTML with encryption (recommended for sharing)
cass export-html /path/to/session.jsonl # To Downloads folder
printf '%s\n' "pwd" | cass export-html session.jsonl --encrypt --password-stdin
cass export-html session.jsonl --open --json # Open in browser, JSON output
# Common agent flow: find current session, then export it
cass export-html "$(cass sessions --current --json | jq -r '.sessions[0].path')" --json
# Expand context around a specific line (from search result)
cass expand /path/to/session.jsonl -n 42 -C 5 --json
# → Shows 5 messages before and after line 42
# Activity timeline: when were agents active?
cass timeline --today --json --group-by hour
cass timeline --since 7d --agent claude --json
# → Grouped activity counts, useful for understanding work patterns
Aggregate search results server-side to get counts and distributions without transferring full result data:
# Count results by agent
cass search "error" --robot --aggregate agent
# → { "aggregations": { "agent": { "buckets": [{"key": "claude_code", "count": 45}, ...] } } }
# Multi-field aggregation
cass search "bug" --robot --aggregate agent,workspace,date
# Combine with filters
cass search "TODO" --agent claude --robot --aggregate workspace
Aggregation Fields:
| Field | Description |
|---|---|
agent |
Group by agent type (claude_code, codex, cursor, etc.) |
workspace |
Group by workspace/project path |
date |
Group by date (YYYY-MM-DD) |
match_type |
Group by match quality (exact, prefix, fuzzy) |
Response Format:
{
"aggregations": {
"agent": {
"buckets": [
{"key": "claude_code", "count": 120},
{"key": "codex", "count": 85}
],
"other_count": 15
}
}
}
Top 10 buckets are returned per field, with other_count for remaining items.
Chain multiple searches together by piping session paths from one search to another:
# Find sessions mentioning "auth", then search within those for "token"
cass search "authentication" --robot-format sessions | \
cass search "refresh token" --sessions-from - --robot
# Build a filtered corpus from today's work
cass search --today --robot-format sessions > today_sessions.txt
cass search "bug fix" --sessions-from today_sessions.txt --robot
How It Works:
--robot-format sessions outputs one session path per line--sessions-from <file> restricts search to those sessions- to read from stdin for true pipingUse Cases:
The --highlight flag wraps matching terms for visual/programmatic identification:
cass search "authentication error" --robot --highlight
# In text output: **authentication** and **error** are bold-wrapped
# In HTML export: <mark>authentication</mark> and <mark>error</mark>
Highlighting is query-aware: quoted phrases like "auth error" highlight as a unit; individual terms highlight separately.
For large result sets, use cursor-based pagination:
# First page
cass search "TODO" --robot --robot-meta --limit 20
# → { "hits": [...], "_meta": { "next_cursor": "eyJ..." } }
# Next page
cass search "TODO" --robot --robot-meta --limit 20 --cursor "eyJ..."
Cursors are opaque tokens encoding the pagination state. They remain valid as long as the index isn't rebuilt.
For debugging and logging, attach a request ID:
cass search "bug" --robot --request-id "req-12345"
# → { "hits": [...], "_meta": { "request_id": "req-12345" } }
For safe retries (e.g., in CI pipelines or flaky networks):
cass index --full --idempotency-key "build-$(date +%Y%m%d)"
# If same key + params were used in last 24h, returns cached result
Debug why a search returned unexpected results:
cass search "auth*" --robot --explain
# → Includes parsed query AST, term expansion, cost estimates
cass search "auth error" --robot --dry-run
# → Validates query syntax without executing
For debugging agent pipelines:
cass search "error" --robot --trace-file /tmp/cass-trace.json
# Appends execution span with timing, exit code, and command details
cass index --full --json --robot-trace-ingest 2>/tmp/cass-ingest-trace.jsonl
# Streams one NDJSON record per ingest batch with wall_ms, batch_msgs,
# inserted_messages, and duplicate-lookup counters for perf bisects
| Flag | Purpose |
|---|---|
--robot / --json |
JSON output (pretty-printed) |
--robot-format jsonl|compact |
Streaming or single-line JSON |
--robot-meta |
Include _meta block (elapsed_ms, cache stats, index freshness) |
--fields minimal|summary|<list> |
Reduce payload size |
--max-content-length N |
Truncate content fields to N chars |
--max-tokens N |
Truncate content fields to N chars |
--timeout N |
Timeout in milliseconds; returns partial results on expiry |
--cursor <token> |
Cursor-based pagination (from _meta.next_cursor) |
--request-id ID |
Echoed in response for correlation |
--aggregate agent,workspace,date |
Server-side aggregations |
--explain |
Include query analysis (parsed query, cost estimate) |
--dry-run |
Validate query without executing |
--source <source> |
Filter by source: local, remote, all, or specific source ID |
--highlight |
Highlight matching terms in output |
| Flag | Purpose |
|---|---|
--idempotency-key KEY |
Safe retries: same key + params returns cached result (24h TTL) |
--json |
JSON output with stats |
For machine-readable documentation, use cass robot-docs <topic>:
| Topic | Content |
|---|---|
commands |
Full command reference with all flags |
env |
Environment variables and defaults |
paths |
Data directory locations per platform |
guide |
Quick start guide for automation |
schemas |
JSON response schemas |
exit-codes |
Exit code meanings and retry guidance |
examples |
Copy-paste usage examples |
contracts |
API contract version and stability |
sources |
Remote sources configuration guide |
# Get documentation programmatically
cass robot-docs guide
cass robot-docs schemas
cass robot-docs exit-codes
# Machine-first help (wide output, no TUI assumptions)
cass --robot-help
cass maintains a stable API contract for automation:
cass api-version --json
# → { "version": "0.4.0", "contract_version": "1", "breaking_changes": [] }
cass introspect --json
# → Full schema: all commands, arguments, response types
Contract Version: Currently 1. Increments only on breaking changes.
Guaranteed Stable:
--robot output_meta block format🔎 cass — Search All Your Agent History
What: cass indexes conversations from Claude Code, Codex, Cursor, Gemini, Aider, ChatGPT, and more into a unified, searchable index. Before solving a problem from scratch, check if any agent already solved something similar.
⚠️ NEVER run bare cass — it launches an interactive TUI. Always use --robot or --json.
Quick Start
# One-shot agent triage (read next_command when present)
cass triage --json
# Search across all agent histories
cass search "authentication error" --robot --limit 5
# Build a cited handoff pack from search evidence
cass pack "authentication error root cause" --robot --max-tokens 12000 --limit 40
# Tight handoff budget with freshness and privacy metadata
cass pack "authentication error root cause" --robot --max-tokens 4000 --max-evidence 8 --fields summary
# View a specific result (from search output)
cass view /path/to/session.jsonl -n 42 --json
# Expand context around a line
cass expand /path/to/session.jsonl -n 42 -C 3 --json
# Learn the full API
cass capabilities --json # Static agent self-description
cass robot-docs guide # LLM-optimized docs
Why Use It
- Cross-agent knowledge: Find solutions from Codex when using Claude, or vice versa
- Forgiving syntax: Typos and wrong flags are auto-corrected with teaching notes
- Token-efficient: --fields minimal returns only essential data; pack budgets cite only selected evidence
- Copy-safe handoffs: pack warnings include freshness and privacy/redaction status
Key Flags
| Flag | Purpose |
|------------------|--------------------------------------------------------|
| --robot / --json | Machine-readable JSON output (required!) |
| --fields minimal | Reduce payload: source_path, line_number, agent only |
| pack --max-tokens N | Budget a cited handoff pack |
| --limit N | Cap result count |
| --agent NAME | Filter to specific agent (claude, codex, cursor, etc.) |
| --days N | Limit to recent N days |
stdout = data only, stderr = diagnostics. Exit 0 = success.
cass supports a rich query syntax designed for both humans and machines.
| Query | Matches |
|---|---|
error |
Messages containing "error" (case-insensitive) |
python error |
Messages containing both "python" AND "error" |
"authentication failed" |
Exact phrase match |
auth fail |
Both terms, in any order |
Combine terms with explicit operators for complex queries:
| Operator | Example | Meaning |
|---|---|---|
AND |
python AND error |
Both terms required (default) |
OR |
error OR warning |
Either term matches |
NOT |
error NOT test |
First term, excluding second |
- |
error -test |
Shorthand for NOT |
Operator Precedence: NOT binds tightest, then AND, then OR. Use parentheses (in robot mode) for explicit grouping.
# Complex boolean query
cass search "authentication AND (error OR failure) NOT test" --robot
# Exclude test files
cass search "bug fix -test -spec" --robot
# Either error type
cass search "TypeError OR ValueError" --robot
Wrap terms in double quotes for exact phrase matching:
| Query | Matches |
|---|---|
"file not found" |
Exact sequence "file not found" |
"cannot read property" |
Exact JavaScript error message |
"def test_" |
Function definitions starting with test_ |
Phrases respect word order and proximity. Useful for error messages, code patterns, and specific terminology.
| Pattern | Type | Matches | Performance |
|---|---|---|---|
auth* |
Prefix | "auth", "authentication", "authorize" | Fast (uses edge n-grams) |
*tion |
Suffix | "authentication", "function", "exception" | Slower (regex scan) |
*config* |
Substring | "reconfigure", "config.json", "misconfigured" | Slowest (full regex) |
test_* |
Prefix | "test_user", "test_auth", "test_helpers" | Fast |
Tip: Prefix wildcards (foo*) are optimized via pre-computed edge n-grams. Suffix and substring wildcards fall back to regex and are slower on large indexes.
# Field-specific search (in robot mode)
cass search "error" --agent claude --workspace /path/to/project
# Time-bounded search
cass search "bug" --since 2024-01-01 --until 2024-01-31
cass search "bug" --today
cass search "bug" --days 7
# Combined filters
cass search "authentication" --agent codex --workspace myproject --week
cass accepts a wide variety of time/date formats for filtering:
| Format | Examples | Description |
|---|---|---|
| Relative | -7d, -24h, -30m, -1w |
Days, hours, minutes, weeks ago |
| Keywords | now, today, yesterday |
Named reference points |
| ISO 8601 | 2024-11-25, 2024-11-25T14:30:00Z |
Standard datetime |
| US Dates | 11/25/2024, 11-25-2024 |
Month/Day/Year |
| Unix Timestamp | 1732579200 |
Seconds since epoch |
| Unix Millis | 1732579200000 |
Milliseconds (auto-detected) |
Intelligent Heuristics:
# All equivalent for "last week"
cass search "bug" --since -7d
cass search "bug" --since "-1w"
cass search "bug" --days 7
# Date range
cass search "feature" --since 2024-01-01 --until 2024-01-31
# Mix formats
cass search "error" --since yesterday --until now
Search results include a match_type indicator:
| Type | Meaning | Score Boost |
|---|---|---|
exact |
Query terms found verbatim | Highest |
prefix |
Matched via prefix expansion (e.g., auth*) |
High |
suffix |
Matched via suffix pattern | Medium |
substring |
Matched via substring pattern | Lower |
fuzzy |
Auto-fallback match when exact results sparse | Lowest |
When an exact query returns fewer than 3 results, cass automatically retries with wildcard expansion:
auth → *auth*wildcard_fallback: true in robot mode| Key | Action |
|---|---|
Ctrl+C |
Quit |
F1 or ? |
Toggle help screen |
F2 |
Toggle dark/light theme |
Ctrl+B |
Toggle border style (rounded/plain) |
Ctrl+Shift+R |
Force re-index |
Ctrl+Shift+Del |
Reset all TUI state |
| Key | Action |
|---|---|
| Type | Live search as you type |
Enter |
Submit query immediately (if query is empty, edits last filter chip) |
Esc |
Clear query / exit search |
Up/Down |
Navigate query history |
Ctrl+R |
Cycle through query history |
Backspace |
Delete character; if empty, remove last filter chip |
| Key | Action |
|---|---|
Up/Down |
Move selection in results list |
Enter |
Open selected result in detail modal (Messages tab by default) |
Left/Right |
Switch focus between results and detail pane |
Tab/Shift+Tab |
Cycle focus: search → results → detail |
PageUp/PageDown |
Scroll by page |
Home/End |
Jump to first/last result |
Alt+h/j/k/l |
Vim-style navigation (left/down/up/right) |
| Key | Action |
|---|---|
F3 |
Open agent filter palette |
F4 |
Open workspace filter palette |
F5 |
Set "from" time filter |
F6 |
Set "to" time filter |
Shift+F3 |
Scope to currently selected result's agent |
Shift+F4 |
Clear workspace filter |
Shift+F5 |
Cycle time presets: 24h → 7d → 30d → all |
Ctrl+Del |
Clear all active filters |
| Key | Action |
|---|---|
F7 |
Cycle context window size: S → M → L → XL |
F9 |
Toggle match mode: prefix (default) ↔ standard |
F12 |
Cycle ranking: recent → balanced → relevance → quality → newest → oldest |
Shift+/= |
Increase items per pane (density) |
- |
Decrease items per pane |
| Key | Action |
|---|---|
Ctrl+M / Ctrl+X |
Toggle selection on current result |
Ctrl+A |
Select/deselect all visible results |
A |
Open bulk actions menu (when items selected) |
Ctrl+Enter |
Add to multi-open queue |
Ctrl+O |
Open all queued items in editor |
y |
Copy current item (path or content to clipboard) |
Ctrl+Y |
Copy all selected items |
| Key | Action |
|---|---|
Space |
Toggle full-screen detail view |
/ |
Start find-in-detail search |
n |
Jump to next match (in find mode) |
N |
Jump to previous match |
g |
Scroll to top (in full-screen) |
G |
Scroll to bottom (in full-screen) |
c |
Copy visible content |
o |
Open in external viewer |
[ / ] |
Switch detail tabs (Messages/Snippets/Raw) |
F7 |
Cycle context window size |
Ctrl+Space |
Momentary "peek" to XL context |
The detail pane has three tabs, switchable with [ and ]:
| Tab | Content | Best For |
|---|---|---|
| Messages | Full conversation with markdown rendering | Reading full context |
| Snippets | Keyword-extracted summaries | Quick scanning |
| Raw | Unformatted JSON/text | Debugging, copying exact content |
Control how much content shows in the detail preview. Cycle with F7:
| Size | Characters | Use Case |
|---|---|---|
| Small | ~200 | Quick scanning, narrow terminals |
| Medium | ~400 | Default balanced view |
| Large | ~800 | Reading longer passages |
| XLarge | ~1600 | Full context, code review |
Peek Mode (Ctrl+Space): Temporarily expand to XL context. Press again to restore previous size. Useful for quick deep-dives without changing your preferred default.
Efficiently work with multiple search results at once:
Multi-Select Mode:
Ctrl+M (or Ctrl+X) to toggle selection on current result (checkbox appears)Ctrl+M or Ctrl+X againCtrl+A to select/deselect all visible resultsBulk Actions Menu (A when items selected):
| Action | Description |
|---|---|
| Open All | Open all selected files in editor |
| Copy Paths | Copy all file paths to clipboard |
| Export | Export selected results to file |
| Clear Selection | Deselect all items |
Multi-Open Queue:
For opening many files without navigating away:
Ctrl+Enter to add current result to queueCtrl+O to open all queued itemsClipboard Operations:
y - Copy current item (cycles: path → snippet → full content)Ctrl+Y - Copy all selected items (paths on separate lines)Cycle through modes with F12:
Recent Heavy (default): Strongly favors recent conversations
text_relevance × 0.3 + recency × 0.7Balanced: Equal weight to relevance and recency
text_relevance × 0.5 + recency × 0.5Relevance: Prioritizes text match quality
text_relevance × 0.8 + recency × 0.2Match Quality: Penalizes fuzzy/wildcard matches
text_relevance × 0.7 + recency × 0.2 + match_exactness × 0.1Date Newest: Pure chronological order (newest first)
Date Oldest: Pure reverse chronological order (oldest first)
Text Relevance (BM25): Tantivy's implementation of Okapi BM25, considering:
Recency: Exponential decay from current time
Match Exactness: Bonus for exact matches vs wildcards
The final score combines all components using mode-specific weights:
Final_Score = BM25_Score × Match_Quality + α × Recency_Factor
Alpha (α) by Ranking Mode:
| Mode | α Value | Effect |
|---|---|---|
| Recent Heavy | 1.0 | Recency dominates |
| Balanced | 0.4 | Moderate recency boost |
| Relevance Heavy | 0.1 | BM25 dominates |
| Match Quality | 0.0 | Pure text matching |
| Date Newest/Oldest | N/A | Pure chronological sort |
Match Quality Factors:
| Match Type | Factor | Applied When |
|---|---|---|
| Exact | 1.0 | "exact phrase" |
| Prefix | 0.9 | auth* |
| Suffix | 0.8 | *tion |
| Substring | 0.6 | *config* |
| Implicit Wildcard | 0.4 | Auto-fallback expansion |
Recency Factor: timestamp / max_timestamp normalized to [0, 1].
This formula ensures that "Recent Heavy" mode (default) surfaces your most recent work, while "Relevance Heavy" finds the best explanations regardless of age.
Each connector transforms agent-specific formats into a unified schema:
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Agent Files │ ──▶ │ Connector │ ──▶ │ Normalized │
│ (proprietary) │ │ (per-agent) │ │ Conversation │
└─────────────────┘ └──────────────────┘ └─────────────────┘
JSONL detect() agent_slug
SQLite scan() workspace
Markdown messages[]
JSON created_at
Different agents use different role names:
| Agent | Original | Normalized |
|---|---|---|
| Claude Code | human, assistant |
user, assistant |
| Codex | user, assistant |
user, assistant |
| ChatGPT | user, assistant, system |
user, assistant, system |
| Cursor | user, assistant |
user, assistant |
| Aider | (markdown headers) | user, assistant |
Agents store timestamps inconsistently:
| Format | Example | Handling |
|---|---|---|
| Unix milliseconds | 1699900000000 |
Direct conversion |
| Unix seconds | 1699900000 |
Multiply by 1000 |
| ISO 8601 | 2024-01-15T10:30:00Z |
Parse with chrono |
| Missing | null |
Use file modification time |
Tool calls, code blocks, and nested structures are flattened for searchability:
// Original (Claude Code)
{"type": "tool_use", "name": "Read", "input": {"path": "/foo/bar.rs"}}
// Flattened for indexing
"[Tool: Read] path=/foo/bar.rs"
The same conversation content can appear multiple times due to:
cass uses a multi-layer deduplication strategy:
Message Hash: BLAKE3 of (role + content + timestamp)
Conversation Fingerprint: Hash of first N message hashes
Search-Time Dedup: Results are deduplicated by content similarity
Common low-value content is filtered from results:
# Find past solutions for similar errors
cass search "TypeError: Cannot read property" --days 30
# In TUI: F12 to switch to "relevance" mode for best matches
# What has ANY agent said about authentication in this project?
cass search "authentication" --workspace /path/to/project
# Export findings for a new agent's context
cass export /path/to/relevant/session.jsonl --format markdown
# What did I work on today?
cass timeline --today --json | jq '.groups[].conversations'
# TUI: Press Shift+F5 to cycle through time filters
# Find all debugging sessions for a specific file
cass search "debug src/auth/login.rs" --agent claude
# Expand context around a specific line in a session
cass expand /path/to/session.jsonl -n 150 -C 10
# Current agent searches what previous agents learned
cass search "database migration strategy" --robot --fields minimal
# Get full context for a relevant session
cass view /path/to/session.jsonl -n 42 --json
# Export high-quality problem-solving sessions
cass search "bug fix" --robot --limit 100 | \
jq '.hits[] | select(.score > 0.8)' > training_candidates.json
Press Ctrl+P to open the command palette—a fuzzy-searchable menu of all available actions.
| Command | Description |
|---|---|
| Toggle theme | Switch between dark/light mode |
| Toggle density | Cycle Compact → Cozy → Spacious |
| Toggle help strip | Pin/unpin the contextual help bar |
| Check updates | Show update assistant banner |
| Filter: agent | Open agent filter picker |
| Filter: workspace | Open workspace filter picker |
| Filter: today | Restrict results to today |
| Filter: last 7 days | Restrict results to past week |
| Filter: date range | Prompt for custom since/until |
| Saved views | List and manage saved view slots |
| Save view to slot N | Save current filters to slot 1-9 |
| Load view from slot N | Restore filters from slot 1-9 |
| Bulk actions | Open bulk menu (when items selected) |
| Reload index/view | Refresh the search reader |
Ctrl+P to openUp/Down to navigateEnter to executeEsc to closeSave your current filter configuration to one of 9 slots for instant recall.
| Key | Action |
|---|---|
Shift+1 through Shift+9 |
Save current view to slot |
1 through 9 |
Load view from slot |
Ctrl+P → "Save view to slot N"Ctrl+P → "Load view from slot N"Ctrl+P → "Saved views" to list all slotsViews are stored in tui_state.json and persist across sessions. Clear all saved views with Ctrl+Shift+Del (resets all TUI state).
Control how many lines each search result occupies. Cycle with Shift+D or via the command palette.
| Mode | Lines per Result | Best For |
|---|---|---|
| Compact | 3 | Maximum results visible, scanning many items |
| Cozy (default) | 5 | Balanced view with context |
| Spacious | 8 | Detailed preview, fewer results |
The pane automatically adjusts how many results fit based on terminal height and density mode.
cass includes a sophisticated theming system with multiple presets, accessibility-aware color choices, and adaptive styling.
Cycle through 19 built-in theme presets with F2:
| Theme | Description | Best For |
|---|---|---|
| Tokyo Night (default) | Deep blues with restrained contrast | Low-light environments, extended sessions |
| Daylight | High-contrast light background | Bright environments, presentations |
| Catppuccin Mocha | Warm pastels, reduced eye strain | All-day coding, aesthetic preference |
| Dracula | Purple-accented dark theme | Popular among developers, familiar feel |
| Nord | Arctic-inspired cool tones | Calm, focused work sessions |
| Solarized Dark | Precisely tuned low-contrast palette | Long editing sessions, monitor-agnostic |
| Solarized Light | Solarized on a cream background | Paper-style readability in bright rooms |
| Monokai | Classic warm dark palette | Familiar Sublime/TextMate feel |
| Gruvbox Dark | Retro earth tones on dark | Warmer alternative to Tokyo Night |
| One Dark | Atom's signature balanced dark | Moderate contrast, friendly defaults |
| Rosé Pine | Soho-inspired muted roses | Gentle contrast, boutique look |
| Everforest | Forest-inspired green-brown palette | Calm, nature-adjacent mood |
| Kanagawa | Japanese ink-and-paper theme | Artistic, quietly distinctive |
| Ayu Mirage | Ayu's balanced muted dark | Blue-teal accents, relaxed contrast |
| Nightfox | Fox-inspired warm dark | Deep violets with orange highlights |
| Cyberpunk Aurora | Neon aurora on obsidian | Showy, high-saturation dark |
| Synthwave '84 | Retro neon magenta/cyan | 80s aesthetic, fun demos |
| High Contrast | Maximum readability | Accessibility needs, bright monitors |
| Colorblind | Deuteranopia/protanopia-safe palette | Color-vision-deficient users |
All theme colors are validated against WCAG (Web Content Accessibility Guidelines) contrast requirements:
The theming engine calculates relative luminance and contrast ratios at runtime to ensure readability across all color combinations.
Conversation messages are color-coded by role for quick visual parsing:
| Role | Visual Treatment | Purpose |
|---|---|---|
| User | Blue-tinted background, bold | Your input, easy to scan |
| Assistant | Green-tinted background | AI responses |
| System | Gray/muted background | Context, instructions |
| Tool | Orange-tinted background | Tool calls, file operations |
Each agent type (Claude, Codex, Cursor, etc.) also receives a subtle tint, making multi-agent result lists instantly scannable.
Border decorations automatically adapt to terminal width:
| Width | Style | Example |
|---|---|---|
| Narrow (<80 cols) | Minimal Unicode | │ content │ |
| Normal (80-120) | Rounded corners | ╭─ content ─╮ |
| Wide (>120) | Full decorations | Double-line headers |
Toggle between rounded Unicode and plain ASCII borders with Ctrl+B.
Save important search results with notes and tags for later reference.
bookmarks.db (SQLite){
"id": 1,
"title": "Auth bug fix discussion",
"source_path": "/path/to/session.jsonl",
"line_number": 42,
"agent": "claude_code",
"workspace": "/projects/myapp",
"note": "Good explanation of JWT refresh flow",
"tags": "auth, jwt, important",
"snippet": "The token refresh logic should..."
}
Bookmarks are stored separately from the main index:
~/.local/share/coding-agent-search/bookmarks.db~/Library/Application Support/coding-agent-search/bookmarks.db%APPDATA%\coding-agent-search\bookmarks.dbcass uses a non-intrusive toast notification system for transient feedback—operations complete, errors occur, or state changes without modal dialogs interrupting your workflow.
| Type | Icon | Auto-Dismiss | Use Case |
|---|---|---|---|
| Info | ℹ️ | 3 seconds | Status updates, tips |
| Success | ✓ | 2 seconds | Operations completed |
| Warning | ⚠ | 4 seconds | Non-critical issues |
| Error | ✗ | 6 seconds | Failures requiring attention |
Toasts feature:
| Trigger | Toast |
|---|---|
| Index rebuild complete | ✓ "Index rebuilt: 2,500 conversations" |
| Export complete | ✓ "Exported to conversation.md" |
| Copy to clipboard | ✓ "Copied to clipboard" |
| Search timeout | ⚠ "Search timed out, showing partial results" |
| Connector error | ✗ "Failed to scan ChatGPT: encrypted files" |
| Update available | ℹ️ "Version 0.5.0 available" |
To achieve sub-60ms latency on large datasets, cass implements a multi-tier caching strategy in src/search/query.rs:
prefix_cache is split into shards (default 256 entries each) to reduce mutex contention during concurrent reads/writes from the async searcher.WarmJob thread watches the input. When the user pauses typing, it triggers a lightweight "warm-up" query against the Tantivy reader to pre-load relevant index segments into the OS page cache.The system is designed for extensibility via the Connector trait (src/connectors/mod.rs). This allows cass to treat disparate log formats as a uniform stream of events.
classDiagram
class Connector {
<<interface>>
+detect() DetectionResult
+scan(ScanContext) Vec~NormalizedConversation~
}
class NormalizedConversation {
+agent_slug String
+messages Vec~NormalizedMessage~
}
Connector <|-- CodexConnector
Connector <|-- ClineConnector
Connector <|-- ClaudeCodeConnector
Connector <|-- GeminiConnector
Connector <|-- ClawdbotConnector
Connector <|-- VibeConnector
Connector <|-- OpenCodeConnector
Connector <|-- AmpConnector
Connector <|-- CursorConnector
Connector <|-- ChatGptConnector
Connector <|-- AiderConnector
Connector <|-- PiAgentConnector
Connector <|-- FactoryConnector
Connector <|-- CopilotConnector
Connector <|-- CopilotCliConnector
Connector <|-- OpenClawConnector
Connector <|-- CrushConnector
Connector <|-- HermesConnector
Connector <|-- KimiConnector
Connector <|-- QwenConnector
CodexConnector ..> NormalizedConversation : emits
ClineConnector ..> NormalizedConversation : emits
ClaudeCodeConnector ..> NormalizedConversation : emits
GeminiConnector ..> NormalizedConversation : emits
ClawdbotConnector ..> NormalizedConversation : emits
VibeConnector ..> NormalizedConversation : emits
OpenCodeConnector ..> NormalizedConversation : emits
AmpConnector ..> NormalizedConversation : emits
CursorConnector ..> NormalizedConversation : emits
ChatGptConnector ..> NormalizedConversation : emits
AiderConnector ..> NormalizedConversation : emits
PiAgentConnector ..> NormalizedConversation : emits
FactoryConnector ..> NormalizedConversation : emits
CopilotConnector ..> NormalizedConversation : emits
CopilotCliConnector ..> NormalizedConversation : emits
OpenClawConnector ..> NormalizedConversation : emits
CrushConnector ..> NormalizedConversation : emits
HermesConnector ..> NormalizedConversation : emits
KimiConnector ..> NormalizedConversation : emits
QwenConnector ..> NormalizedConversation : emits
Box<dyn Connector> instances that are unaware of each other's underlying file formats (JSONL, SQLite, specialized JSON).cass uses frankensqlite as the durable source of truth and frankensearch as a derived speed layer, powered by a suite of integrated "franken" libraries.
messages, conversations, agents) via frankensqlite — a pure-Rust SQLite reimplementation with BEGIN CONCURRENT support for MVCC multi-writer transactions.title, content, agent, workspace, created_at.title_prefix and content_prefix use Index-Time Edge N-Grams (not stored on disk to save space) for instant prefix matching.flowchart LR
classDef pastel fill:#f4f2ff,stroke:#c2b5ff,color:#2e2963;
classDef pastel2 fill:#e6f7ff,stroke:#9bd5f5,color:#0f3a4d;
classDef pastel3 fill:#e8fff3,stroke:#9fe3c5,color:#0f3d28;
classDef pastel4 fill:#fff7e6,stroke:#f2c27f,color:#4d350f;
classDef pastel5 fill:#ffeef2,stroke:#f5b0c2,color:#4d1f2c;
subgraph Sources["Local Sources"]
A1[Codex]:::pastel
A2[Cline]:::pastel
A3[Gemini]:::pastel
A4[Claude]:::pastel
A5[OpenCode]:::pastel
A6[Amp]:::pastel
A7[Cursor]:::pastel
A8[ChatGPT]:::pastel
A9[Aider]:::pastel
A10[Pi-Agent]:::pastel
A11[Factory]:::pastel
A12[Copilot Chat]:::pastel
A13[Copilot CLI]:::pastel
A14[OpenClaw]:::pastel
A15[Clawdbot]:::pastel
A16[Vibe]:::pastel
A17[Crush]:::pastel
A18[Hermes]:::pastel
A19[Kimi]:::pastel
A20[Qwen]:::pastel
end
subgraph Remote["Remote Sources"]
R1["sources.toml"]:::pastel
R2["SSH/rsync\nSync Engine"]:::pastel2
R3["remotes/\nSynced Data"]:::pastel3
end
subgraph "Ingestion Layer"
C1["franken_agent_detection\nAuto-Discover & Scan\nNormalize & Dedupe"]:::pastel2
end
subgraph "Storage + Search"
S1["frankensqlite (WAL)\nSource of Truth\nBEGIN CONCURRENT\nMigrations"]:::pastel3
T1["frankensearch\nBM25 + Semantic\nRRF Fusion\nReranking"]:::pastel4
end
subgraph "Presentation"
U1["TUI (FrankenTUI)\nElm Architecture\nAnalytics Dashboard\nAsync Search"]:::pastel5
U2["CLI / Robot\nJSON Output\nAutomation"]:::pastel5
end
A1 --> C1
A2 --> C1
A3 --> C1
A4 --> C1
A5 --> C1
A6 --> C1
A7 --> C1
A8 --> C1
A9 --> C1
A10 --> C1
A11 --> C1
A12 --> C1
A13 --> C1
A14 --> C1
A15 --> C1
A16 --> C1
A17 --> C1
A18 --> C1
A19 --> C1
A20 --> C1
R1 --> R2
R2 --> R3
R3 --> C1
C1 -->|Persist| S1
C1 -->|Index| T1
S1 -.->|Rebuild| T1
T1 -->|Query| U1
T1 -->|Query| U2
notify) to detect changes in agent logs. When you save a file or an agent replies, cass re-indexes just that conversation and refreshes the search view automatically.The interactive interface (src/ui/app.rs) uses FrankenTUI (ftui), a Rust TUI framework implementing the Elm architecture (Model-View-Update). The runtime handles terminal lifecycle, event polling, rendering, and cleanup.
CassMsg variant. The update() function produces Cmd effects (async tasks, ticks, quit).view() function renders the current state to an ftui Frame. The runtime diff engine minimizes terminal writes using Bayesian strategy selection.Cmd::Task, with results delivered as messages.graph TD
Input([User Input]) -->|Key/Mouse/Tick| Runtime
Runtime -->|CassMsg| Update[Model::update]
Update -->|Cmd| Runtime
Update -->|State Change| View[Model::view]
View -->|Frame| DiffEngine[Bayesian Diff]
DiffEngine -->|Minimal Writes| Terminal
Update -->|Cmd::Task| Background[Background Thread]
Background -->|Result Msg| Runtime
Data integrity is paramount. cass treats the SQLite database (src/storage/sqlite.rs, powered by frankensqlite) as an append-only log for conversations:
schema_version meta-table and strict migration path ensure that upgrades (like the recent move to v3) are safe and atomic.cass treats search indexes as derived assets. The SQLite archive is authoritative; lexical and semantic search data can be rebuilt from it.
Every Tantivy index stores a schema_hash.json file containing the schema version:
{"schema_hash":"tantivy-schema-v4-edge-ngram-agent-string"}
| Scenario | Detection | Recovery |
|---|---|---|
| First run | No SQLite archive and no lexical index | cass index --full discovers sessions and creates both |
| Missing lexical index | No readable lexical asset | Rebuild from SQLite into scratch space, then publish |
| Schema mismatch | Hash differs from current | Rebuild derived lexical asset from SQLite |
| Corrupted metadata | Invalid or missing lexical metadata | Ignore the broken derivative and rebuild from SQLite |
| Semantic not ready | Model/vector assets absent or still backfilling | Continue lexical search and report semantic fallback/readiness |
# Check the current truth surface first
cass triage --json
cass health --json
cass status --json
# If not ready, run the first targeted command from recommended_commands[].
# For a fresh data dir this is usually:
cass index --full --json --no-progress-events --data-dir <same-data-dir>
Manual rebuild commands are for first setup, explicit operator refresh, or cases where recommended_commands[] asks for them. A normal missing/stale lexical asset should be repaired as derived state from SQLite, not treated as lost user data.
cass only reads agent files, never modifies themcass maintains multiple layers of redundancy to recover from corruption or schema changes:
Schema Hash Versioning:
Each Tantivy index stores a schema_hash.json file containing a hash of the current schema definition. On startup:
This ensures that version upgrades with schema changes can rebuild the lexical derivative without user intervention.
Automatic Rebuild Triggers:
| Condition | Detection | Action |
|---|---|---|
| Schema version change | Hash mismatch in schema_hash.json |
Full rebuild |
Missing meta.json |
Tantivy can't open index | Rebuild and publish a fresh derivative |
| Corrupted index files | Index::open_in_dir() fails |
Rebuild and publish a fresh derivative |
| Explicit request | --force-rebuild flag |
Clean slate rebuild |
SQLite as Ground Truth:
The SQLite database serves as the authoritative data store. Lexical rebuilds reconstruct the Tantivy index from SQLite:
// Iterate all conversations from SQLite
// Re-index each message into fresh Tantivy index
// Progress tracked via IndexingProgress for UI feedback
This means corrupted lexical data is a repairable derivative-state problem. Operators should start with cass triage --json for the exact next command, or read cass health --json / cass status --json for the narrower readiness snapshot.
The SQLite database uses versioned schema migrations:
| Version | Changes |
|---|---|
| v1 | Initial schema: agents, workspaces, conversations, messages, snippets, tags |
| v2 | Added FTS5 full-text search |
| v3 | Added source provenance tracking |
| v4 | Added vector embeddings support |
| v5 | Current: Added remote sources |
Migration Process:
cass checks schema_version in the databaseSafe Files (never deleted during rebuild):
bookmarks.db - Your saved bookmarkstui_state.json - UI preferencessources.toml - Remote source configuration.env - Environment configurationBackup and Retention Policy: Migration/rebuild backups preserve user data and
are not treated as disposable source evidence. Derived lexical publish backups
use the bounded retention policy documented above, while quarantined artifacts
and repair candidates persist until an operator runs an explicit, fingerprinted
cleanup flow.
The --watch flag enables real-time index updates as agent files change.
File change detected
↓
[2 second debounce window] ← Accumulate more changes
↓
[5 second max wait] ← Force flush if changes keep coming
↓
Re-index affected files
Each file system event is routed to the appropriate connector:
~/.claude/projects/foo.jsonl → ClaudeCodeConnector
~/.codex/sessions/rollout-*.jsonl → CodexConnector
~/.aider.chat.history.md → AiderConnector
Watch mode maintains watch_state.json:
{
"last_scan_ts": 1699900000000,
"watched_paths": [
"~/.claude/projects",
"~/.codex/sessions"
]
}
Codex event_msg token_count usage is attached to the nearest preceding assistant turn during indexing.
If you indexed Codex sessions before this behavior existed, backfill usage coverage with:
cass index --full
cass analytics rebuild --track a
Generate tab-completion scripts for your shell.
Bash:
cass completions bash > ~/.local/share/bash-completion/completions/cass
# Or: cass completions bash >> ~/.bashrc
Zsh:
cass completions zsh > "${fpath[1]}/_cass"
# Or add to ~/.zshrc: eval "$(cass completions zsh)"
Fish:
cass completions fish > ~/.config/fish/completions/cass.fish
PowerShell:
cass completions powershell >> $PROFILE
search, index, stats, etc.)--robot, --agent, --limit)SIGILL (illegal instruction) signal. The cass binary includes a runtime check and will print a clear error message if AVX is not detected, but note that ONNX Runtime may be loaded before this check in some code paths.cargo install --git https://github.com/Dicklesworthstone/coding_agent_session_search. This requirement exists because CI builds target ubuntu-24.04 to access newer kernel features used by the frankensqlite storage engine.Recommended: Homebrew (Apple Silicon macOS + Linux)
brew install dicklesworthstone/tap/cass
# Update later
brew upgrade cass
Homebrew bottles are currently published for Linux and Apple Silicon macOS. On Intel macOS, use the install script with --from-source.
Windows: Scoop
scoop bucket add dicklesworthstone https://github.com/Dicklesworthstone/scoop-bucket
scoop install dicklesworthstone/cass
Alternative: Install Script
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_session_search/main/install.sh?$(date +%s)" \
| bash -s -- --easy-mode --verify
Alternative: GitHub Release Binaries
SHA256SUMS.txt against the downloaded archive.cass into your PATH.Example (Linux x86_64, replace VERSION with an explicit release tag):
VERSION=v0.2.0 # e.g. v0.2.0
curl -L -o cass-linux-amd64.tar.gz \
"https://github.com/Dicklesworthstone/coding_agent_session_search/releases/download/${VERSION}/cass-linux-amd64.tar.gz"
curl -L -o SHA256SUMS.txt \
"https://github.com/Dicklesworthstone/coding_agent_session_search/releases/download/${VERSION}/SHA256SUMS.txt"
sha256sum -c SHA256SUMS.txt
tar -xzf cass-linux-amd64.tar.gz
install -m 755 cass ~/.local/bin/cass
cass
On first run, cass performs a full index. You'll see progress in the footer. Search works immediately (falling back to SQLite or partial results until complete).
foo* (prefix), *foo (suffix), or *foo* (contains) for flexible matching.Up/Down to select, Right to focus detail pane. Up/Down in search bar navigates query history.F3: Filter by Agent (e.g., "codex").F4: Filter by Workspace/Project.F5/F6: Time filters (Today, Week, etc.).F2: Toggle Dark/Light theme.F12: Cycle ranking mode (recent → balanced → relevance → quality → newest → oldest).Ctrl+B: Toggle rounded/plain borders.Enter: Open selected result in contextual detail modal (defaults to Messages tab).Enter with no selected hit: submit query behavior (no-op if empty).F8: Open selected hit in $EDITOR.Ctrl+Enter: Add current result to queue (multi-open).Ctrl+O: Open all queued results in editor.Ctrl+M / Ctrl+X: Toggle selection on current item.A: Bulk actions menu (when items selected).y: Copy file path or snippet to clipboard./: Find text within detail pane; n/N cycle matches; Esc exits find before closing modal.Ctrl+Shift+R: Trigger manual re-index (refresh search results).Ctrl+Shift+Del: Reset TUI state (clear history, filters, layout).Aggregate sessions from your other machines into a unified index:
# Add a remote machine
cass sources add [email protected] --preset macos-defaults
# Sync sessions from all sources
cass sources sync
# Check source health and connectivity
cass sources doctor
See Remote Sources (Multi-Machine Search) for full documentation.
The cass binary supports both interactive use and automation.
# Interactive
cass [tui] [--data-dir DIR] [--once] [--asciicast FILE]
# Indexing
cass index [--full] [--watch] [--data-dir DIR] [--idempotency-key KEY]
# Search
cass search "query" --robot --limit 5 [--timeout 5000] [--explain] [--dry-run]
cass search "error" --robot --aggregate agent,workspace --fields minimal
cass pack "query" --robot --max-tokens 12000 [--limit 40] [--sessions-from FILE|-]
cass pack "query" --robot --freshness-policy strict --freshness-window-seconds 604800 --require-evidence
cass pack "query" --robot --max-tokens 4000 --max-evidence 8 --max-sessions 3 --max-excerpt-chars 600
# Inspection & Health
cass triage --json # One-shot agent preflight with exact next command
cass status --json # Quick health snapshot
cass health # Minimal pre-flight check (<50ms)
cass capabilities --json # First-stop agent self-description
cass introspect --json # Full API schema
cass context /path/to/session --json # Find related sessions
cass view /path/to/file -n 42 --json # View source at line
# Session Analysis
cass export /path/to/session --format markdown -o out.md # Export conversation
cass expand /path/to/session -n 42 -C 5 --json # Context around line
cass timeline --today --json # Activity timeline
# Remote Sources
cass sources add user@host --preset macos-defaults # Add machine
cass sources sync # Sync sessions
cass sources doctor # Check connectivity
cass sources mappings list laptop # View path mappings
# Utilities
cass stats --json
cass completions bash > ~/.bash_completion.d/cass
| Command | Purpose |
|---|---|
cass (default) |
Start TUI + background watcher |
cass tui --asciicast FILE |
Run TUI and save terminal output as asciicast v2 |
index --full |
Discover sessions and refresh the canonical DB plus derived search assets |
index --watch |
Daemon mode: watch for file changes, reindex automatically |
search --robot |
JSON output for automation pipelines |
pack --robot |
Deterministic cited answer packs for agent/human handoffs; reports health, freshness, privacy, and warnings |
triage / ready / preflight |
One-shot agent preflight: readiness, exact next command, docs, schemas, workflows, and recoveries |
status / state |
Health snapshot: index freshness, DB stats, recommended action |
health |
Minimal health check (<50ms), exit 0=healthy, 1=unhealthy |
capabilities |
First-stop agent self-description: workflow recipes, mistake recoveries, commands, global flags, exit codes, env vars, and limits |
introspect |
Full API schema: commands, arguments, response shapes |
sessions [--workspace DIR] [--current] |
Discover recent session files for follow-up actions |
context <path> |
Find related sessions by workspace, day, or agent |
view <path> -n N |
View source file at specific line (follow-up on search) |
export <path> |
Export conversation to markdown/JSON |
export-html <path> |
Export as self-contained HTML with optional encryption |
expand <path> -n N |
Show messages around a specific line number |
timeline |
Activity timeline with grouping by hour/day |
sources |
Manage remote sources: add/list/remove/doctor/sync/mappings |
doctor |
Diagnose and repair installation issues (safe, never deletes data) |
| Tool | Purpose |
|---|---|
cass tui --asciicast FILE |
Record TUI output as an asciicast v2 artifact; there is no separate cass cast subcommand |
scripts/bakeoff/cass_validation_e2e.sh |
Run the bake-off validation harness for lexical, semantic, hybrid, and reranked search scenarios |
scripts/bakeoff/cass_embedder_e2e.sh |
Exercise embedder bake-off flows against a generated validation corpus |
scripts/bakeoff/cass_rerank_e2e.sh |
Exercise reranker bake-off flows and append results to the bake-off log |
Commands for troubleshooting, debugging, and understanding system state:
# One-shot agent preflight
cass triage --json
# → { "surface": "triage", "status": "healthy", "next_command": null, ... }
# Health check (fast, <50ms)
cass health --json
# → { "healthy": true, "index_age_seconds": 120, "message_count": 5000 }
# Detailed status with recommendations
cass status --json
# → Includes index freshness, staleness threshold, recommended action
# System diagnostics
cass diag --verbose --json
# → Database stats, index info, connector status, environment
# Query explanation (debug why results are what they are)
cass search "auth" --explain --dry-run --robot
# → Shows parsed query, index strategy, cost estimate without executing
# Find related sessions
cass context /path/to/session.jsonl --json
# → Sessions from same workspace, same day, or same agent
# Archive-first diagnostic check
cass doctor check --json
# → Read-only checks for archive coverage, source authority, locks, backups,
# storage pressure, semantic fallback, and recommended next action
# Fingerprinted repair plan and apply
cass doctor repair --dry-run --json
cass doctor repair --yes --plan-fingerprint <plan_fingerprint> --json
# → Builds candidates and applies only the inspected matching fingerprint
# Legacy safe auto-run for low-risk derived repairs
cass doctor --fix --json
# → Emits operation_outcome and receipts; fails closed on archive/source risk
cass doctor is a comprehensive diagnostic and repair tool designed for troubleshooting installation and data issues. Its current recovery model is archive-first: preserve cass-owned evidence, prove source authority and coverage, then repair through candidates and receipts. The full operator runbook is docs/planning/RECOVERY_RUNBOOK.md.
What it checks:
| Surface | Purpose | Mutation Policy |
|---|---|---|
cass doctor check --json |
Read-only truth surface for archive coverage, source authority, locks, storage pressure, semantic fallback, and recommended action | Never mutates |
cass doctor archive-scan --json |
Read-only source inventory, raw mirror, coverage, sole-copy, and remote sync gap inspection | Never mutates |
cass doctor repair --dry-run --json |
Builds a fingerprinted repair plan and candidate/promotion gates | Read-only plan |
cass doctor repair --yes --plan-fingerprint <fp> --json |
Applies exactly the inspected repair fingerprint | Candidate-based, receipt-backed |
cass doctor backups list/verify/restore ... --json |
Lists backups, verifies manifests, rehearses restore, then applies by fingerprint | Restore apply requires a matching rehearsal fingerprint |
cass doctor cleanup --json |
Plans cleanup for derived or explicitly reclaimable assets | Apply requires a matching fingerprint |
cass doctor support-bundle --json |
Creates a scrubbed diagnostic handoff bundle | Redacted by default; not a backup |
Safety guarantees:
sources.toml are not cleanup targets.plan_fingerprint.Recommended support checklist:
cass doctor check --json
cass doctor baseline diff <baseline_id> --json
cass doctor support-bundle --json
cass doctor support-bundle verify <bundle_or_manifest_path> --json
Send the doctor JSON, latest failure_context.json if present, support-bundle
manifest.json, any baseline diff, relevant artifact_manifest_path and
event_log_path values, and the exact command/exit code. Do not attach raw
sessions, full SQLite archives, private source files, or encrypted payloads
unless the user explicitly opts into sensitive evidence attachment.
Diagnostic Flags:
| Flag | Available On | Effect |
|---|---|---|
--explain |
search | Show query parsing and strategy |
--dry-run |
search | Validate without executing |
--verbose |
most commands | Extra detail in output |
--trace-file |
all | Append execution trace to file |
--robot-trace-ingest |
index | Emit per-ingest-batch NDJSON timing and lookup counters on stderr |
Commands for managing the semantic search ML model:
# Check current model status (abbreviated schema — real output also
# includes cache_lifecycle, files[], revision, license, and more):
cass models status --json
# → {
# "model_id": "all-minilm-l6-v2",
# "model_dir": "~/.local/share/coding-agent-search/models/all-MiniLM-L6-v2",
# "installed": false,
# "state": "not_acquired",
# "state_detail": "model not acquired (user consent required); missing ...",
# "next_step": "Run `cass models install`, or keep using lexical search.",
# "lexical_fail_open": true,
# "revision": "c9745ed1...",
# "license": "Apache-2.0",
# "total_size_bytes": 90872535,
# "installed_size_bytes": 0,
# "observed_file_bytes": 0,
# "policy_source": "semantic_policy"
# }
# Install model (downloads ~90MB from Hugging Face on explicit request)
cass models install
# → Downloads from Hugging Face, verifies checksum
# Install from local directory (air-gapped environments)
cass models install --from-file /path/to/model-dir
# Verify model integrity
cass models verify --json
# → all_valid bool + per-file SHA-256 checks (see `cass models verify --help`)
# Check for model updates
cass models check-update --json
# → { "update_available": bool, "reason": str,
# "current_revision": str|null, "latest_revision": str }
Model Files (stored in $CASS_DATA_DIR/models/all-MiniLM-L6-v2/):
model.onnx - The neural network weights (~90MB)tokenizer.json - Vocabulary and tokenization rulesconfig.json - Model configurationspecial_tokens_map.json - Special token definitionstokenizer_config.json - Tokenizer settingsVerified Install: The installer enforces SHA256 checksums.
Sandboxed Data: All indexes/DBs live in standard platform data directories (~/.local/share/coding-agent-search on Linux).
Read-Only Source: cass never modifies your agent log files. It only reads them.
cass uses crash-safe atomic write patterns throughout to prevent data corruption:
TUI State Persistence (tui_state.json):
1. Serialize state to JSON
2. Write to temporary file (tui_state.json.tmp)
3. Atomic rename: temp → final
If a crash occurs during step 2, the original file is untouched. The rename operation (step 3) is atomic on all modern filesystems—it either completes fully or not at all.
ML Model Installation (models/all-MiniLM-L6-v2/):
1. Download to temp directory (models/all-MiniLM-L6-v2.tmp/)
2. Verify all checksums
3. If existing model present: rename to backup (models/all-MiniLM-L6-v2.bak/)
4. Atomic rename: temp → final
5. On success: remove backup
6. On failure: restore from backup
This backup-rename-cleanup pattern ensures that either the old model or new model is always available—never a half-installed state.
Configuration Files (sources.toml, watch_state.json):
All configuration writes follow the same temp-file-then-rename pattern, ensuring consistency even during power loss or unexpected termination.
Why This Matters:
The project ships with a robust installer (install.sh / install.ps1) designed for CI/CD and local use:
Checksum Verification: Validates artifacts against a .sha256 file or explicit --checksum flag.
Rustup Bootstrap: Automatically installs the nightly toolchain if missing.
Easy Mode: --easy-mode automates installation to ~/.local/bin without prompts.
Platform Agnostic: Detects OS/Arch (Linux/macOS/Windows, x86_64/arm64) and fetches the correct binary.
cass includes a built-in update checker that notifies you when new versions are available, without interrupting your workflow.
When a new version is available, a notification appears in the TUI with:
Selecting "Update Now" runs the same verified installer used for initial installation:
macOS/Linux:
curl -fsSL https://...install.sh | bash -s -- --easy-mode --verify
Windows (PowerShell):
& ([scriptblock]::Create((irm "https://...install.ps1"))) -EasyMode -Verify
The update process:
cassIf you're not ready to update, "Skip This Version" records the skipped version in persistent state. That specific version won't trigger notifications again, but future versions will.
For automated environments or personal preference:
# Environment variable
export CODING_AGENT_SEARCH_NO_UPDATE_PROMPT=1
# Or for fully headless operation
export TUI_HEADLESS=1
Update check state is stored in the data directory:
last_update_check: Timestamp of last check (for rate limiting)skipped_version: Version user chose to skiptui_state.jsonConfig: Loads .env via dotenvy::dotenv().ok(); configure API/base paths there. Do not overwrite .env.
Data Location: Defaults to standard platform data directories (e.g., ~/.local/share/coding-agent-search). Override with CASS_DATA_DIR or --data-dir.
ChatGPT Support: The ChatGPT macOS app stores conversations in versioned formats:
conversations-{uuid}/ — fully indexed.Encrypted conversations require keychain access which isn't available to third-party apps. Legacy unencrypted conversations are indexed automatically.
Logs: Written to cass.log (daily rotating) in the data directory.
Updates: Interactive TUI checks for GitHub releases on startup. Skip with CODING_AGENT_SEARCH_NO_UPDATE_PROMPT=1 or TUI_HEADLESS=1.
Cache tuning: CASS_CACHE_SHARD_CAP (per-shard entries, default 256) and CASS_CACHE_TOTAL_CAP (total cached hits across shards, default 2048) control prefix cache size; raise cautiously to avoid memory bloat.
Cache debug: set CASS_DEBUG_CACHE_METRICS=1 to emit cache hit/miss/shortfall/reload stats via tracing (debug level).
Watch testing (dev only): cass index --watch --watch-once path1,path2 triggers a single reindex without filesystem notify (also respects CASS_TEST_WATCH_PATHS for backward compatibility); useful for deterministic tests/smoke runs.
| Variable | Default | Description |
|---|---|---|
| Core | ||
CASS_DATA_DIR |
Platform default | Override data directory |
CASS_DB_PATH |
$CASS_DATA_DIR/agent_search.db |
Override database path |
CASS_NO_COLOR |
unset | Force monochrome TUI output |
NO_COLOR |
unset | Honored by TUI only when CASS_RESPECT_NO_COLOR=1 |
CASS_RESPECT_NO_COLOR |
unset | Make TUI inherit global NO_COLOR |
| Search & Cache | ||
CASS_CACHE_SHARD_CAP |
256 | Per-shard LRU cache entries |
CASS_CACHE_TOTAL_CAP |
2048 | Total cached search hits |
CASS_CACHE_BYTE_CAP |
10485760 | Cache byte limit (10MB) |
CASS_WARM_DEBOUNCE_MS |
120 | Warm-up search debounce |
CASS_DEBUG_CACHE_METRICS |
unset | Enable cache hit/miss logging |
| Semantic Search | ||
CASS_SEMANTIC_EMBEDDER |
auto | Force embedder: hash or minilm |
| TUI | ||
TUI_HEADLESS |
unset | Disable interactive features |
CASS_ALLOW_DUMB_TERM |
unset | Allow TUI startup even when TERM=dumb |
CASS_UI_METRICS |
unset | Enable UI interaction tracing |
CASS_DISABLE_ANIMATIONS |
unset | Disable UI animations |
EDITOR |
$VISUAL or vi |
External editor command |
EDITOR_LINE_FLAG |
+ |
Line number flag (e.g., +42) |
| Updates | ||
CODING_AGENT_SEARCH_NO_UPDATE_PROMPT |
unset | Disable update notifications |
| Connector Overrides | ||
CASS_AIDER_DATA_ROOT |
~/.aider.chat.history.md |
Aider history location |
PI_CODING_AGENT_DIR |
~/.pi/agent/sessions |
Pi-Agent sessions |
CODEX_HOME |
~/.codex |
Codex data directory |
GEMINI_HOME |
~/.gemini |
Gemini CLI directory |
OPENCODE_STORAGE_ROOT |
(scans home) | OpenCode storage |
CHATGPT_ENCRYPTION_KEY |
unset | Base64-encoded AES key for ChatGPT v2/v3 |
cass pins git revisions in Cargo.toml for asupersync, frankensqlite/fsqlite-types, franken-agent-detection, frankensearch, frankentui, and toon (tru). The repo also commits local [patch] overrides for frankensqlite, franken-agent-detection, and frankensearch; the remaining sibling repos can be switched to /data/projects/* checkouts during local development.
| Dependency | Pinned revision |
|---|---|
frankensqlite / fsqlite-types |
266dc98f |
franken-agent-detection |
3ad1970 |
asupersync |
0.3.1 |
frankensearch |
831b3b13 |
frankentui |
5f78cfa0 |
toon (tru) |
5669b72a |
Build-time validation
build.rs validates the active local overrides against the expected package name, package version, patch path, and Cargo feature/default-features contract.rch exec -- env CARGO_TARGET_DIR=/tmp/cass-strict-target cargo check --features strict-path-dep-validation or rch exec -- env CARGO_TARGET_DIR=/tmp/cass-strict-target CASS_STRICT_PATH_DEP_VALIDATION=1 cargo check. Strict mode upgrades drift warnings to hard errors and also validates the optional sibling repos before you switch them to local path overrides.Expected interface contract
frankensqlite (fsqlite): Connection, params!, and compat::{ConnectionExt, RowExt} with row.get_typed(...).franken-agent-detection: AgentDetectOptions and detect_installed_agents(...).frankensearch: lexical::cass_open_search_reader, lexical::ReloadPolicy, ModelCategory, and ModelTier.frankentui: ftui::Frame, GraphemePool, Style, ftui-runtime, ftui-tty, and the ftui-extras features enabled by cass.asupersync: runtime::RuntimeBuilder and http::h1::HttpClient::builder().toon (tru): toon::encode(...).When intentionally updating one of these sibling crates, update the manifest pin, the build.rs contract, and the compile-contract test together.
TUI looks monochrome / “1981 mode”: Check TERM and NO_COLOR.
Full-style launch example:
env -u NO_COLOR TERM=xterm-256color COLORTERM=truecolor cass
If you intentionally want monochrome, use CASS_NO_COLOR=1 cass.
If a wrapper keeps forcing TERM=dumb and UI still degrades, either fix TERM or force raw mode explicitly with CASS_ALLOW_DUMB_TERM=1 cass.
Checksum mismatch: Ensure .sha256 is reachable or pass --checksum explicitly. Check proxies/firewalls.
Binary not on PATH: Append ~/.local/bin (or your --dest) to PATH; re-open shell.
Rust toolchain missing in CI: Set RUSTUP_INIT_SKIP=1 if the pinned toolchain is preinstalled; otherwise allow installer to run rustup.
Watch mode not triggering: Confirm watch_state.json updates and that connector roots are accessible; notify relies on OS file events (inotify/FSEvents).
Reset TUI state: Run cass tui --reset-state (or press Ctrl+Shift+Del in the TUI) to delete tui_state.json and restore defaults.
We target stable Rust as pinned by rust-toolchain.toml. Agents should
offload build, test, lint, and snapshot commands with rch.
# Format & Lint
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-dev-target cargo fmt --check
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-dev-target cargo clippy --all-targets -- -D warnings
# Build & Test
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-dev-target cargo build --release
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-dev-target cargo test
# Run End-to-End Tests
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-dev-target cargo test --test e2e_index_tui
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-dev-target cargo test --test install_scripts
Use targeted snapshot runs; do not blindly bless everything:
# Verify current baselines
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-snapshot-target cargo test snapshot_baseline_ -- --nocapture
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-snapshot-target cargo test snapshot_search_surface_ -- --nocapture
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-snapshot-target cargo test --test ftui_harness_snapshots -- --nocapture
# Regenerate only the suite you intentionally changed
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-snapshot-target BLESS=1 cargo test snapshot_baseline_ -- --nocapture
The full regeneration/review protocol (required reviewer checklist, behavioral guard tests,
and quality gates) lives in docs/planning/TESTING.md under
Snapshot Baseline Regeneration & Review (FrankenTUI).
Playwright E2E runs emit a setup metadata file at tests/e2e/exports/setup-metadata.json and
export its path as TEST_EXPORT_SETUP_LOG in tests/e2e/.env.test. On failures, tests attach
per-test browser logs (console/pageerror/requestfailed). Set E2E_LOG_ALWAYS=1 to attach logs
for every test. These paths are generated run artifacts and are intentionally ignored; durable
logging schema documentation lives in docs/reference/E2E_LOGGING_SCHEMA.md.
The release profile is aggressively optimized for binary size and performance:
[profile.release]
lto = true # Link-time optimization across all crates
codegen-units = 1 # Single codegen unit for better optimization
strip = true # Remove debug symbols from binary
panic = "abort" # Smaller panic handling (no unwinding)
opt-level = "z" # Optimize for size over speed
Trade-offs:
The CI pipeline (.github/workflows/ci.yml) runs on every PR and push to main:
| Job | Purpose | Artifacts |
|---|---|---|
check |
fmt, clippy, tests, benches, UBS scan | None |
e2e |
Integration tests (install, index, filters) | test-artifacts-e2e (traces, logs) |
coverage |
Code coverage with llvm-cov | coverage-report (lcov.info, summary) |
Coverage Reports:
lcov.info - LCOV format for tools like codecovcoverage-summary.txt - Human-readable summaryTest Artifacts:
--trace-file runs# Generate coverage through rch
# Ensure cargo-llvm-cov is already installed before agent-run gates.
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-coverage-target cargo llvm-cov --all-features --workspace --text
# Run specific e2e tests
rch exec -- env CARGO_TARGET_DIR=/tmp/cass-e2e-target cargo test --test e2e_filters -- --test-threads=1
About Contributions: Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via
ghand independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.