Guidelines for AI coding agents working in this Rust codebase.
If I tell you to do something, even if it goes against what follows below, YOU MUST LISTEN TO ME. I AM IN CHARGE, NOT YOU.
YOU ARE NEVER ALLOWED TO DELETE A FILE WITHOUT EXPRESS PERMISSION. Even a new file that you yourself created, such as a test code file. You have a horrible track record of deleting critically important files or otherwise throwing away tons of expensive work. As a result, you have permanently lost any and all rights to determine that a file or folder should be deleted.
YOU MUST ALWAYS ASK AND RECEIVE CLEAR, WRITTEN PERMISSION BEFORE EVER DELETING A FILE OR FOLDER OF ANY KIND.
- Absolutely forbidden commands:
git reset --hard,git clean -fd,rm -rf, or any command that can delete or overwrite code/data must never be run unless the user explicitly provides the exact command and states, in the same message, that they understand and want the irreversible consequences. - No guessing: If there is any uncertainty about what a command might delete or overwrite, stop immediately and ask the user for specific approval. "I think it's safe" is never acceptable.
- Safer alternatives first: When cleanup or rollbacks are needed, request permission to use non-destructive options (
git status,git diff,git stash, copying to backups) before ever considering a destructive command. - Mandatory explicit plan: Even after explicit user authorization, restate the command verbatim, list exactly what will be affected, and wait for a confirmation that your understanding is correct. Only then may you execute it—if anything remains ambiguous, refuse and escalate.
- Document the confirmation: When running any approved destructive command, record (in the session notes / final response) the exact user text that authorized it, the command actually run, and the execution time. If that record is absent, the operation did not happen.
The default branch is main. The master branch exists only for legacy URL compatibility.
- All work happens on
main— commits, PRs, feature branches all merge tomain - Never reference
masterin code or docs — if you seemasteranywhere, it's a bug that needs fixing - The
masterbranch must stay synchronized withmain— after pushing tomain, also push tomaster:git push origin main:master
If you see master referenced anywhere:
- Update it to
main - Ensure
masteris synchronized:git push origin main:master
THIS IS A HARD, NON-NEGOTIABLE RULE. IT HAS BEEN VIOLATED OVER 10 TIMES AND THE OWNER IS DONE TOLERATING IT.
- ALL new SQLite code MUST use
frankensqlite(fsqlite). This applies to CASS, franken_agent_detection, and ANY project in this ecosystem. - NEVER add
rusqliteas a dependency. NEVER writeuse rusqlite::. NEVER userusqlite::Connection. Not even "temporarily." Not even "to match the existing pattern." - The existing rusqlite usage is LEGACY DEBT, not a pattern to follow. Do NOT copy it. Do NOT extend it.
- If frankensqlite is missing a feature you need, FIX FRANKENSQLITE. It lives at
/data/projects/frankensqlite. Add the missing API there. That is the entire point of the project. - The frankensqlite API uses
frankensqlite::Connection,fsqlite::compat::{ConnectionExt, RowExt, OpenFlags, open_with_flags, ParamValue}, androw.get_typed::<T>(idx)instead of rusqlite'srow.get(idx). - For franken_agent_detection connectors: use
frankensqlitewith the compat layer. Import pattern:use frankensqlite::Connection as FrankenConnection; use frankensqlite::compat::{ConnectionExt, RowExt, OpenFlags, open_with_flags, param_slice_to_values};
If you are an AI agent reading this and you write rusqlite in new code, you are wasting the owner's time and money. This rule exists because agents have repeatedly ignored it. DO NOT BE THE NEXT ONE.
We only use Cargo in this project, NEVER any other package manager.
- Edition: Rust 2024 (stable — see
rust-toolchain.toml) - Dependency versions: Wildcard constraints (
*) for all crates - Configuration: Cargo.toml only (single-crate project, no workspace)
- Unsafe code: Forbidden
This project uses asupersync as its async runtime. It provides RuntimeBuilder, spawn_blocking, fs ops, net, signal, and structured concurrency via Cx.
We load all configuration from .env via the dotenvy crate. NEVER use std::env::var() directly.
use dotenvy::dotenv;
use std::env;
// Load .env file at startup (typically in main())
dotenv().ok();
// Configuration with fallback
let api_base_url = env::var("API_BASE_URL")
.unwrap_or_else(|_| "http://localhost:8007".to_string());The .env file exists and MUST NEVER be overwritten.
| Crate | Purpose |
|---|---|
asupersync |
Async runtime (multi-thread, fs, spawn_blocking, signals) |
clap |
CLI argument parsing with derive macros |
serde + serde_json |
Serialization |
frankensqlite (fsqlite) |
Pure-Rust SQLite reimplementation — primary storage backend |
rusqlite |
SQLite database (bundled) — legacy, retained during frankensqlite migration |
frankensearch |
Unified search engine: lexical BM25 + semantic + RRF fusion |
franken_agent_detection |
Agent session auto-detection across 15+ providers |
frankentorch (via frankensearch) |
Pure-Rust native MiniLM embeddings and reranking |
hnsw_rs |
HNSW approximate nearest neighbors |
half + wide + memmap2 |
f16 quantized vectors, portable SIMD, memory-mapped I/O |
ftui + ftui-extras |
FrankenTUI terminal interface |
toon |
Terminal rendering library |
reqwest |
HTTP client (rustls-tls, blocking + async) |
rayon |
Data parallelism for CPU-bound work |
colored + indicatif + console |
Colorful, informative console output |
notify |
Filesystem watching |
walkdir + glob |
Directory traversal and pattern matching |
blake3 + sha2 |
Cryptographic hashing |
aes-gcm + ring + pbkdf2 + argon2 |
Encryption (ChatGPT conversations, HTML export) |
ssh2 |
SFTP fallback for multi-machine sync |
dialoguer |
Interactive terminal prompts (setup wizard) |
syntect |
Syntax highlighting |
thiserror |
Ergonomic error type derivation |
tracing |
Structured logging and diagnostics |
unicode-normalization |
NFC text canonicalization |
Optional local sibling checkouts (under /data/projects/, via commented local patches only):
frankensqlite— Pure-Rust SQLite with BEGIN CONCURRENT (MVCC multi-writer)frankensearch— Unified search: BM25 lexical + semantic embeddings + RRF fusion + rerankingfranken_agent_detection— Auto-discovers agent sessions from 15+ providersfrankentui(ftui+ftui-extras+ftui-runtime+ftui-tty) — Terminal UI frameworkasupersync— Async runtime (multi-thread, fs, spawn_blocking, signals)toon— Token-optimized serialization
Dependency source contract:
| Dependency | Pinned source |
|---|---|
frankensqlite / fsqlite-types |
crates.io =0.3.1 (0.3.0 asupersync-0.4.3 migration + GH#333/GH#334 fix wave incl. cass#393 st_dev namespace-sidecar repair, + 0.3.1 allocator/freelist/concurrent-writer correctness wave; carries the FTS5 overlong-term skip cap [cass#362]) |
franken-agent-detection |
57d2789e (fsqlite 0.3.0 + asupersync 0.4.3 lockstep bump) |
asupersync |
=0.4.4 (fsqlite 0.3.x requires the 0.4.x line; asupersync 0.3.x and 0.4.x are non-interchangeable) |
frankensearch |
14d1480a (remote head 2026-08-13, aligned with fsqlite 0.3.0 + asupersync 0.4.3; explicit cass-compat → lexical-tantivy; pure-Rust native feature: frankentorch NativeEmbedder + NativeReranker; frankentorch pinned by git rev inside frankensearch — cass #308, bd-8nqz.5) |
frankentui |
5f78cfa0 |
toon (tru) |
5669b72a |
The release build optimizes for speed with LTO + single-codegen-unit + stripped binary:
[profile.release]
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for better optimization
strip = true # Remove debug symbols
panic = "abort" # Abort on panic (smaller binary)
opt-level = 3 # Maximum optimization for speedA profiling profile is also available:
[profile.profiling]
inherits = "release"
debug = true # Keep debug symbols for flamegraphs
strip = falseA bench profile is used for .bench-history/ pass-over-pass ratchet runs (per
the gauntlet keep-gate rules; never run benches against --release directly):
[profile.release-perf]
inherits = "release"
debug = "line-tables-only" # frame-pointer attribution without rebuild
strip = false
# Pair with: RUSTFLAGS="-C force-frame-pointers=yes" cargo bench --profile release-perfNEVER run a script that processes/changes code files in this repo. Brittle regex-based transformations create far more problems than they solve.
- Always make code changes manually, even when there are many instances
- For many simple changes: use parallel subagents
- For subtle/complex changes: do them methodically yourself
If you want to change something or add a feature, revise existing code files in place.
NEVER create variations like:
document_processorV2.rsdocument_processor_improved.rsdocument_processor_enhanced.rs
New files are reserved for genuinely new functionality that makes zero sense to include in any existing file. The bar for creating new files is incredibly high.
We do not care about backwards compatibility—we're in early development with no users. We want to do things the RIGHT way with NO TECH DEBT.
- Never create "compatibility shims"
- Never create wrapper functions for deprecated APIs
- Just fix the code directly
All console output should be informative, detailed, stylish, and colorful by leveraging:
colored— ANSI color formattingindicatif— Progress bars and spinnersconsole— Terminal utilities
After any substantive code changes, you MUST verify no errors were introduced:
# Check for compiler errors and warnings
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-check-target cargo check --all-targets
# Check for clippy lints
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-check-target cargo clippy --all-targets -- -D warnings
# Verify formatting
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-check-target cargo fmt --checkIf you see errors, carefully understand and resolve each issue. Read sufficient context to fix them the RIGHT way.
Per coding_agent_session_search-dpfvr, every PR runs ubs --ci --fail-on-warning against the changed files in CI (.github/workflows/ci.yml::ubs-changed-files). The gate is blocking — warnings stop merges.
Local pre-flight before pushing:
ubs $(git diff --name-only origin/main...HEAD)Or, scoped to staged files:
ubs $(git diff --name-only --cached)If a known-acceptable warning needs to ship despite the gate, suppress at the UBS config level (tests/policies/no_mock_allowlist.json or per-file inline pragma) — never bypass by removing the gate.
The pinned UBS version lives in .github/workflows/ubs-version.txt; the CI installer reads that file. Local installs should match.
The project is migrating from rusqlite to frankensqlite. Both are available:
frankensqlite(import asfsqlite) — Pure-Rust SQLite with BEGIN CONCURRENT supportrusqlite— C-binding SQLite, retained as fallback during migration
use frankensqlite::Connection;
// Open with WAL mode (REQUIRED for concurrent access)
let conn = Connection::open(path)?;
conn.execute("PRAGMA journal_mode = WAL;")?;
conn.execute("PRAGMA busy_timeout = 5000;")?;
// Use params! macro (needs explicit import)
use fsqlite::params;
conn.execute_with_params("INSERT INTO t (a) VALUES (?1)", params![42])?;frankensqlite::Connection::open() can open and read standard SQLite database files created by SQLite/rusqlite. That includes external app databases such as Cursor state.vscdb, OpenCode opencode.db, and historical cass databases.
- Do not add
rusqlitejust to read an existing SQLite file. - If a specific query shape fails against one of these files, treat it as a targeted engine/query bug and file a reproducer instead of assuming the file format is unsupported.
Use FrankenConnectionManager for concurrent access:
- Reader pool (multiple concurrent readers)
- Writer token (single writer at a time via
WriterGuard) WriterGuardauto-rollbacks on drop (RAII safety)
- Always use WAL mode — without it, concurrent writes corrupt the DB
- Use jittered exponential backoff on
BusySnapshot/WriteConflicterrors - Batch writes — 10-20 rows per transaction (not 1 row per commit)
- Limit concurrent writers to 4 threads (matches production rayon parallelism)
- Retryable errors:
Busy,BusyRecovery,BusySnapshot,WriteConflict,SerializationFailure,DatabaseCorrupt
- File format interop: As of rev
9cedb30b, frankensqlite databases are readable by C SQLite (rusqlite) and vice versa. Historical bundle salvage still uses rusqlite as a proven read bridge for pre-migration databases. PRAGMA writable_schema: Not supported for write operations (INSERT/UPDATE on sqlite_master). SELECT from sqlite_master works.
Do:
- Create connection pools and reuse across the application
- Use
?placeholders for parameters (prevents SQL injection) - Keep one database transaction per logical operation
- Handle migrations properly
- Use strong typing for database columns
Don't:
- Share a single transaction across concurrent tasks
- Use string concatenation to build SQL queries
- Ignore
Option<T>for nullable columns - Mix sync and async database operations
- Use
unwrap()on database results in production code
IMPORTANT: E2E browser tests (Playwright) should only be run on GitHub Actions CI, NOT locally.
Running browser tests locally:
- Consumes significant system resources (spawns browser instances)
- Can freeze or slow down the development machine
- May have different results than CI due to environment differences
Push to a branch and let GitHub Actions run the tests. The CI workflow in .github/workflows/browser-tests.yml handles:
- Installing browsers
- Running tests in parallel across Chromium, Firefox, and WebKit
- Uploading test artifacts and reports
If you need to debug a specific test, use test.only() and run a single spec file, but prefer CI for full test runs.
Tests must cover:
- Happy path
- Edge cases (empty input, max values, boundary conditions)
- Error conditions
Integration and E2E tests live in the tests/ directory. Benchmarks live in benches/.
Stack floor: always run tests with
RUST_MIN_STACK=16777216. fsqlite 0.3.x's async engine builds deep debug-mode futures and the default 2 MiB test-thread stack overflows in storage-touching unit tests (SIGABRT with "has overflowed its stack"). CI sets this workflow-wide.
# Run all tests
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-test-target RUST_MIN_STACK=16777216 cargo test
# Run with output
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-test-target cargo test -- --nocapture
# Run a specific test
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-test-target cargo test test_name
# Run tests with all features enabled
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-test-target cargo test --all-features| Directory / File | Focus Areas |
|---|---|
tests/connector_*.rs |
Per-provider session parsing (Claude, Codex, Cursor, Gemini, Aider, Amp, Cline, OpenCode, Pi Agent, Copilot, OpenClaw, ClawdBot, Vibe) |
tests/search_*.rs |
Search pipeline, caching, filters, wildcard fallback |
tests/semantic_integration.rs |
Semantic search, embeddings, two-tier search |
tests/e2e_*.rs |
End-to-end CLI flows, filters, search, sources, TUI, deploy |
tests/cli_*.rs |
CLI dispatch coverage, robot mode, index, stats |
tests/tui_*.rs |
TUI headless smoke tests, snapshot tests |
tests/tui_integration_smoke.rs |
TUI + full integrated stack (frankensqlite + frankensearch + FAD) |
tests/frankensqlite_*.rs |
frankensqlite compat gates, concurrent stress tests |
tests/agent_detection_completeness.rs |
franken_agent_detection connector completeness |
tests/html_export*.rs |
HTML export pipeline, encryption |
tests/storage*.rs |
SQLite storage, migration safety |
tests/performance/ |
Performance regression tests |
benches/ |
Criterion benchmarks (index, runtime, search, crypto, db, export, cache, regex, integration_regression) |
Fixtures are in tests/fixtures/ and cover multiple agent session formats for cross-connector validation.
If you aren't 100% sure how to use a third-party library, SEARCH ONLINE to find the latest documentation and current best practices.
This is the project you're working on. cass indexes conversations from Claude Code, Codex, Cursor, Gemini, Aider, Amp, Cline, OpenCode, Pi Agent, Copilot, OpenClaw, ClawdBot, Vibe, and more into a unified, searchable index with a TUI and robot-mode CLI.
NEVER run bare cass — it launches an interactive TUI. Always use --robot or --json.
Provides unified full-text and semantic search across all local coding agent session histories, with a rich TUI, robot-mode JSON API, multi-machine sync, HTML export with optional encryption, and analytics.
- SQLite is the source of truth. Search indexes are derived assets; do not treat a broken lexical or semantic artifact as lost user data.
- Lexical search is required and self-healing. Missing, stale, schema-drifted, or corrupt lexical assets should rebuild from SQLite through scratch-build and atomic-publish semantics.
- Hybrid is the default search intent. Semantic refinement joins when ready; default hybrid search must fail open to lexical with truthful robot metadata when semantic assets are unavailable.
- Semantic enrichment is opportunistic. Lexical-only behavior is expected during first indexing, semantic backfill, disabled semantic policy, missing model files, or vector catch-up.
- Semantic model acquisition is opt-in.
cass models installdownloads the MiniLM model (~90 MB) on explicit operator request. cass never auto-downloads. Air-gapped installs use--from-file <dir>. While the model is absent,fallback_mode="lexical"is reported in health/status and queries silently degrade to lexical-only. - Truth surfaces:
cass health --json,cass status --json, and search--robot-metaexpose readiness, active rebuilds, realized search mode, fallback tier, and recommended action. Follow those fields instead of hard-coded manual repair rituals.
- Every lexical publish is a single atomic swap: on Linux
renameat2(RENAME_EXCHANGE)exchanges the staged and live index trees in one syscall; non-Linux platforms use a parked-rename + restore-on-failure dance. Readers never see a half-torn index — either the old or the new generation is visible, never a mix. - The prior-live generation is retained under
<data_dir>/index/.lexical-publish-backups/<dated>/for a bounded retention window. Default cap:1(one-step rollback). Override viaCASS_LEXICAL_PUBLISH_BACKUP_RETENTIONenv var:0disables retention,Nkeeps the N most-recent backups. Pruning runs after every successful publish and emitstracing::info!withfreed_bytes+retention_limit. - Crash recovery is automatic. If cass crashes between the atomic swap and the retain-rename, the next startup's
recover_or_finalize_interrupted_lexical_publish_backupfinds the canonical sidecar (.<name>.publish-in-progress.bak) and completes the retain step before the next publish. See src/indexer/mod.rs::publish_staged_lexical_index. - Do not handwrite "rebuild lexical" recipes. Call
cass index --fullor trust stale-refresh; the publish + atomic-swap + retention pipeline is the only blessed path. Anything that removes<data_dir>/index/directly outside publish is off-contract.
- Corrupt or failed-validation assets are quarantined, not deleted. Failed seed bundles, quarantined lexical generations, and superseded retained publish backups persist on disk until explicitly reclaimed.
cass diag --json --quarantineenumerates every quarantined artifact with{path, size_bytes, age_seconds, last_read_at_ms, safe_to_gc, gc_reason}. Thesafe_to_gcflag is advisory only — it reports retention-policy + cleanup-dry-run eligibility; no automatic deletion path consumes it.cass doctor --jsonsurfaces the same quarantine summary pluschecks[]for every diagnostic. Without--fixdoctor is read-only:auto_fix_applied=false,auto_fix_actions=[],issues_fixed=0. With--fixit applies only repairs whose dry-run plans are proven safe — currently Track A analytics rebuild and Track B rollup rebuild viarebuild_token_daily_stats(when thetoken_usageledger is intact; see bead m7xrw).- Agents should not race doctor. Running
cass doctor --fixagainst an in-flight rebuild is safe (cass uses an advisory file lock), but concurrent--fixfrom two processes is undefined. Prefercass health --jsonfor pre-flight checks andcass doctor --json(no--fix) for inspection.
- Every JSON contract surface is pinned by golden-file regression tests under
tests/golden/robot/(JSON) andtests/golden/robot_docs/(plain-text docs topics). The full set: capabilities, health, status, diag, diag_quarantine, models_status, models_verify, models_check_update, introspect, doctor, doctor_quarantine, api_version, stats (missing-db error envelope), robot_docs topics (paths, env, exit-codes, schemas, guide, robot_help). - If you add a new field or change a type, run
UPDATE_GOLDENS=1 rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-golden-target cargo test --test golden_robot_json --test golden_robot_docs, review the diff viagit diff tests/golden/, and commit both the code + golden in one change. Do not regenerate goldens without reviewing — every diff is either an intentional schema change or a bug. cass introspect --json'sresponse_schemasisBTreeMap-backed so the serialized key order is alphabetical and deterministic (bead 8sl73).- Error envelopes use kebab-case
err.kindvalues. For codes 0-9 the numeric code is sufficient; for codes ≥ 10 the code is ambiguous (e.g. 10 covers bothconfigandtimeout) — always branch onerr.kind. Full taxonomy in src/lib.rsCliErrorliterals + the Exit Codes table below (bead wan21).
coding_agent_session_search/
├── Cargo.toml # Single-crate project
├── src/
│ ├── main.rs # Entry point (binary: cass)
│ ├── lib.rs # Library root
│ ├── connectors/ # Per-agent session parsers
│ │ ├── mod.rs # Connector trait + registry
│ │ ├── claude_code.rs # Claude Code sessions
│ │ ├── codex.rs # Codex sessions
│ │ ├── cursor.rs # Cursor sessions
│ │ ├── gemini.rs # Gemini sessions
│ │ ├── grok.rs # Grok Build sessions
│ │ ├── aider.rs # Aider sessions
│ │ ├── amp.rs # Amp sessions
│ │ ├── chatgpt.rs # ChatGPT sessions (encrypted)
│ │ ├── cline.rs # Cline sessions
│ │ ├── opencode.rs # OpenCode sessions
│ │ ├── pi_agent.rs # Pi Agent sessions
│ │ ├── copilot.rs # Copilot sessions
│ │ ├── copilot_cli.rs # Copilot CLI sessions
│ │ ├── openclaw.rs # OpenClaw sessions
│ │ ├── clawdbot.rs # ClawdBot sessions
│ │ ├── vibe.rs # Vibe sessions
│ │ ├── crush.rs # Crush sessions
│ │ ├── hermes.rs # Hermes sessions
│ │ ├── kimi.rs # Kimi Code sessions
│ │ ├── qwen.rs # Qwen Code sessions
│ │ ├── openhands.rs # OpenHands sessions
│ │ ├── antigravity.rs # Antigravity sessions
│ │ └── factory.rs # Factory (Droid) sessions
│ ├── search/ # Search engine (delegates to frankensearch)
│ │ ├── query.rs # Query parsing and execution
│ │ ├── tantivy.rs # BM25 full-text search (via frankensearch)
│ │ ├── vector_index.rs # Vector similarity search
│ │ ├── two_tier_search.rs # Progressive 2-tier hybrid search
│ │ ├── ann_index.rs # HNSW approximate nearest neighbors
│ │ ├── hash_embedder.rs # FNV-1a hash embedder (fast, zero-dep)
│ │ ├── fastembed_embedder.rs # Native MiniLM quality embedder (legacy filename)
│ │ ├── embedder.rs # Embedder trait
│ │ ├── embedder_registry.rs # Embedder auto-detection
│ │ ├── reranker.rs # Cross-encoder reranking
│ │ ├── reranker_registry.rs # Reranker management
│ │ ├── model_download.rs # Model download management
│ │ ├── model_manager.rs # Model lifecycle management
│ │ ├── canonicalize.rs # Query canonicalization
│ │ └── daemon_client.rs # Search daemon RPC client
│ ├── indexer/ # Session indexing pipeline
│ ├── storage/ # SQLite persistence (frankensqlite + rusqlite)
│ ├── ui/ # TUI components
│ ├── pages/ # Web pages generation
│ ├── pages_assets/ # Static assets for pages
│ ├── html_export/ # Self-contained HTML export
│ ├── analytics/ # Usage analytics
│ ├── daemon/ # Background search daemon
│ ├── sources/ # Multi-machine source management
│ ├── model/ # Data models
│ ├── bookmarks.rs # Session bookmarking
│ ├── bakeoff.rs # Embedder comparison tool
│ ├── encryption.rs # AES-GCM encryption
│ ├── export.rs # Export pipeline
│ ├── update_check.rs # Auto-update checking
│ └── tui_asciicast.rs # Terminal recording
├── tests/ # Integration + E2E tests
├── benches/ # Criterion benchmarks
├── scripts/ # Helper scripts
├── web/ # Web assets
├── docs/ # Documentation
└── fuzz/ # Fuzz testing
# Check readiness and recommended action (fresh installs may need index --full)
cass health
# Search across all agent histories (default: hybrid-preferred, lexical fail-open)
cass search "authentication error" --robot --limit 5
# 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
# Export session as self-contained HTML
cass export-html /path/to/session.jsonl --json
cass export-html session.jsonl --encrypt --password "secret" --json
# Learn the full API
cass capabilities --json # Feature discovery
cass robot-docs guide # LLM-optimized docs| Provider | Connector | Session Format |
|---|---|---|
| Claude Code | claude_code.rs |
JSONL |
| Codex | codex.rs |
JSONL |
| Cursor | cursor.rs |
JSONL / SQLite |
| Gemini | gemini.rs |
JSONL |
| Aider | aider.rs |
Markdown / JSONL |
| Amp | amp.rs |
JSONL |
| ChatGPT | chatgpt.rs |
Encrypted JSON |
| Cline | cline.rs |
JSONL |
| OpenCode | opencode.rs |
JSONL |
| Pi Agent | pi_agent.rs |
JSONL |
| Copilot | copilot.rs |
JSONL |
| Copilot CLI | copilot_cli.rs |
JSONL |
| OpenClaw | openclaw.rs |
JSONL |
| ClawdBot | clawdbot.rs |
JSONL |
| Vibe | vibe.rs |
JSONL |
| Crush | crush.rs |
JSONL |
| Hermes | hermes.rs |
JSONL |
| Kimi Code | kimi.rs |
JSONL |
| Qwen Code | qwen.rs |
JSONL |
| Factory (Droid) | factory.rs |
JSONL |
| OpenHands | openhands.rs |
JSON event stream |
| Antigravity | antigravity.rs |
JSONL / SQLite |
| Grok Build | grok.rs |
ACP updates JSONL |
Export conversations as self-contained HTML files with optional encryption:
# Basic export (outputs to Downloads folder)
cass export-html /path/to/session.jsonl --json
# With encryption
cass export-html session.jsonl --encrypt --password "secret" --json
# Password from stdin (secure)
echo "secret" | cass export-html session.jsonl --encrypt --password-stdin --json
# Custom output
cass export-html session.jsonl --output-dir /tmp --filename "export" --jsonRobot mode JSON output:
{
"success": true,
"output_path": "/home/user/Downloads/claude_2026-01-25_session.html",
"file_size": 145623,
"encrypted": false,
"message_count": 42
}Error codes:
| Code | Kind | Description |
|---|---|---|
| 3 | session_not_found | Session file doesn't exist |
| 4 | output_not_writable | Cannot write to output directory |
| 5 | encryption_error | Encryption failed |
| 6 | password_required | --encrypt used without password |
| Flag | Purpose |
|---|---|
--robot / --json |
Machine-readable JSON output (required!) |
--fields minimal |
Reduce payload: source_path, line_number, agent only |
--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.
- Prefer
cass --robot-helpandcass robot-docs <topic>for machine-first docs - The CLI is forgiving: globals placed before/after subcommand are auto-normalized
- If parsing fails, follow the actionable errors with examples
- Use
--color=neverin non-TTY automation for ANSI-free output
| Mistake | Correction | Note |
|---|---|---|
-robot |
--robot |
Long flags need double-dash |
--Robot, --LIMIT |
--robot, --limit |
Flags are lowercase |
find "query" |
search "query" |
find is an alias |
--robot-docs |
robot-docs |
It's a subcommand |
Full alias list:
- Search:
find,query,q,lookup,grep->search - Stats:
ls,list,info,summary->stats - Status:
st,state->status - Index:
reindex,idx,rebuild->index - View:
show,get,read->view - Robot-docs:
docs,help-robot,robotdocs->robot-docs
cass health --jsonReturns in <50ms:
- Exit 0: Healthy — proceed with queries
- Exit 1: Not ready — inspect
status,rebuild,semantic, andrecommended_action. Fresh installs usually needcass index --full; active rebuilds usually need bounded waiting; semantic-only gaps usually mean lexical fallback is expected.
| Code | Meaning | Retryable |
|---|---|---|
| 0 | Success | N/A |
| 1 | Health check failed | Yes — inspect recommended_action |
| 2 | Usage/parsing error | No — fix syntax |
| 3 | Index/DB missing | Yes — run cass index --full |
| 4 | Network error | Yes — check connectivity |
| 5 | Data corruption | Yes — inspect health/status, then rebuild derived assets if recommended |
| 6 | Incompatible version | No — update cass |
| 7 | Lock/busy | Yes — retry later |
| 8 | Partial result | Yes — increase timeout |
| 9 | Unknown error | Maybe |
| 10 | Config / timeout (domain-specific) | Depends on err.kind |
| 11 | Config validation | No — fix config |
| 12 | Source / SSH problem | Maybe — check remote host |
| 13 | Mapping / not-found | Depends on err.kind |
| 14 | I/O / mapping | Maybe |
| 15 | Semantic / embedder unavailable | Yes — install model or fall back to --mode lexical |
| 20-21 | Model acquisition failure | Maybe — check err.kind, err.hint |
| 22 | I/O during model handling | Maybe |
| 23 | Download failure | Yes — retry or use --from-file |
| 24 | I/O during model verify/install | Maybe |
Codes ≥ 10 are domain-specific. The numeric code alone is ambiguous (e.g. code 10 covers both config and timeout kinds). Agents should branch on err.kind from the JSON error envelope, not on the numeric code, when handling codes ≥ 10. Kind names are kebab-case (examples: missing-index, missing-db, semantic-unavailable, embedder-unavailable, ambiguous-source, timeout, config, lock-busy, network, model, download, io). The full set (~50 kinds) lives in src/lib.rs.
cass can search across agent sessions from multiple machines. Use the interactive setup wizard for the easiest configuration:
cass sources setup- Discovers SSH hosts from your ~/.ssh/config
- Probes each host to check for:
- Existing cass installation (and version)
- Agent session data (Claude, Codex, Cursor, Gemini)
- System resources (disk, memory)
- Lets you select which hosts to configure
- Installs cass on remotes if needed
- Indexes existing sessions on remotes
- Configures sources.toml with correct paths
- Syncs data to your local machine
cass sources setup --non-interactive --hosts css,csd,yto
cass sources setup --json --hosts css # JSON output for parsing| Flag | Purpose |
|---|---|
--hosts <names> |
Configure only these hosts (comma-separated) |
--dry-run |
Preview without making changes |
--resume |
Resume interrupted setup |
--skip-install |
Don't install cass on remotes |
--skip-index |
Don't run remote indexing |
--skip-sync |
Don't sync after setup |
--json |
Output progress as JSON |
# Search across all sources
cass search "database migration"
# Sync latest data
cass sources sync --all
# List configured sources
cass sources listIf you prefer manual setup, edit ~/.config/cass/sources.toml:
[[sources]]
name = "my-server"
type = "ssh"
host = "user@server.example.com"
paths = ["~/.claude/projects"]
[[sources.path_mappings]]
from = "/home/user/projects"
to = "/Users/me/projects"- Host unreachable: Verify SSH config with
ssh <host>manually - Permission denied: Load SSH key with
ssh-add ~/.ssh/id_rsa - cargo not found: Use
--skip-installand install manually - Interrupted setup: Resume with
cass sources setup --resume
For machine-readable docs: cass robot-docs sources
[features]
default = ["qr", "encryption"]
qr = ["dep:qrcode", "dep:image"] # QR code generation for recovery secret
encryption = [] # HTML export encryption (deps included for ChatGPT)
backtrace = [] # Enhanced backtracesA mail-like layer that lets coding agents coordinate asynchronously via MCP tools and resources. Provides identities, inbox/outbox, searchable threads, and advisory file reservations with human-auditable artifacts in Git.
- Prevents conflicts: Explicit file reservations (leases) for files/globs
- Token-efficient: Messages stored in per-project archive, not in context
- Quick reads:
resource://inbox/...,resource://thread/...
-
Register identity:
ensure_project(project_key=<abs-path>) register_agent(project_key, program, model) -
Reserve files before editing:
file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true) -
Communicate with threads:
send_message(..., thread_id="FEAT-123") fetch_inbox(project_key, agent_name) acknowledge_message(project_key, agent_name, message_id) -
Quick reads:
resource://inbox/{Agent}?project=<abs-path>&limit=20 resource://thread/{id}?project=<abs-path>&include_bodies=true
- Prefer macros for speed:
macro_start_session,macro_prepare_thread,macro_file_reservation_cycle,macro_contact_handshake - Use granular tools for control:
register_agent,file_reservation_paths,send_message,fetch_inbox,acknowledge_message
"from_agent not registered": Alwaysregister_agentin the correctproject_keyfirst"FILE_RESERVATION_CONFLICT": Adjust patterns, wait for expiry, or use non-exclusive reservation- Auth errors: If JWT+JWKS enabled, include bearer token with matching
kid
Beads provides a lightweight, dependency-aware issue database and CLI (br - beads_rust) for selecting "ready work," setting priorities, and tracking status. It complements MCP Agent Mail's messaging and file reservations.
Important: br is non-invasive—it NEVER runs git commands automatically. You must manually commit changes after br sync --flush-only.
- Single source of truth: Beads for task status/priority/dependencies; Agent Mail for conversation and audit
- Shared identifiers: Use Beads issue ID (e.g.,
br-123) as Mailthread_idand prefix subjects with[br-123] - Reservations: When starting a task, call
file_reservation_paths()with the issue ID inreason
-
Pick ready work (Beads):
br ready --json # Choose highest priority, no blockers -
Reserve edit surface (Mail):
file_reservation_paths(project_key, agent_name, ["src/**"], ttl_seconds=3600, exclusive=true, reason="br-123") -
Announce start (Mail):
send_message(..., thread_id="br-123", subject="[br-123] Start: <title>", ack_required=true) -
Work and update: Reply in-thread with progress
-
Complete and release:
br close 123 --reason "Completed" br sync --flush-only # Export to JSONL (no git operations)
release_file_reservations(project_key, agent_name, paths=["src/**"])Final Mail reply:
[br-123] Completedwith summary
| Concept | Value |
|---|---|
Mail thread_id |
br-### |
| Mail subject | [br-###] ... |
File reservation reason |
br-### |
| Commit messages | Include br-### for traceability |
bv is a graph-aware triage engine for Beads projects (.beads/beads.jsonl). It computes PageRank, betweenness, critical path, cycles, HITS, eigenvector, and k-core metrics deterministically.
Scope boundary: bv handles what to work on (triage, priority, planning). For agent-to-agent coordination (messaging, work claiming, file reservations), use MCP Agent Mail.
CRITICAL: Use ONLY --robot-* flags. Bare bv launches an interactive TUI that blocks your session.
bv --robot-triage is your single entry point. It returns:
quick_ref: at-a-glance counts + top 3 picksrecommendations: ranked actionable items with scores, reasons, unblock infoquick_wins: low-effort high-impact itemsblockers_to_clear: items that unblock the most downstream workproject_health: status/type/priority distributions, graph metricscommands: copy-paste shell commands for next steps
bv --robot-triage # THE MEGA-COMMAND: start here
bv --robot-next # Minimal: just the single top pick + claim commandPlanning:
| Command | Returns |
|---|---|
--robot-plan |
Parallel execution tracks with unblocks lists |
--robot-priority |
Priority misalignment detection with confidence |
Graph Analysis:
| Command | Returns |
|---|---|
--robot-insights |
Full metrics: PageRank, betweenness, HITS, eigenvector, critical path, cycles, k-core, articulation points, slack |
--robot-label-health |
Per-label health: health_level, velocity_score, staleness, blocked_count |
--robot-label-flow |
Cross-label dependency: flow_matrix, dependencies, bottleneck_labels |
--robot-label-attention [--attention-limit=N] |
Attention-ranked labels |
History & Change Tracking:
| Command | Returns |
|---|---|
--robot-history |
Bead-to-commit correlations |
--robot-diff --diff-since <ref> |
Changes since ref: new/closed/modified issues, cycles |
Other:
| Command | Returns |
|---|---|
--robot-burndown <sprint> |
Sprint burndown, scope changes, at-risk items |
--robot-forecast <id|all> |
ETA predictions with dependency-aware scheduling |
--robot-alerts |
Stale issues, blocking cascades, priority mismatches |
--robot-suggest |
Hygiene: duplicates, missing deps, label suggestions |
--robot-graph [--graph-format=json|dot|mermaid] |
Dependency graph export |
--export-graph <file.html> |
Interactive HTML visualization |
bv --robot-plan --label backend # Scope to label's subgraph
bv --robot-insights --as-of HEAD~30 # Historical point-in-time
bv --recipe actionable --robot-plan # Pre-filter: ready to work
bv --recipe high-impact --robot-triage # Pre-filter: top PageRank
bv --robot-triage --robot-triage-by-track # Group by parallel work streams
bv --robot-triage --robot-triage-by-label # Group by domainAll robot JSON includes:
data_hash— Fingerprint of source beads.jsonlstatus— Per-metric state:computed|approx|timeout|skipped+ elapsed msas_of/as_of_commit— Present when using--as-of
Two-phase analysis:
- Phase 1 (instant): degree, topo sort, density
- Phase 2 (async, 500ms timeout): PageRank, betweenness, HITS, eigenvector, cycles
bv --robot-triage | jq '.quick_ref' # At-a-glance summary
bv --robot-triage | jq '.recommendations[0]' # Top recommendation
bv --robot-plan | jq '.plan.summary.highest_impact' # Best unblock target
bv --robot-insights | jq '.status' # Check metric readiness
bv --robot-insights | jq '.Cycles' # Circular deps (must fix!)Golden Rule: ubs <changed-files> before every commit. Exit 0 = safe. Exit >0 = fix & re-run.
ubs file.rs file2.rs # Specific files (< 1s) — USE THIS
ubs $(git diff --name-only --cached) # Staged files — before commit
ubs --only=rust,toml src/ # Language filter (3-5x faster)
ubs --ci --fail-on-warning . # CI mode — before PR
ubs . # Whole project (ignores target/, Cargo.lock)Warning Category (N errors)
file.rs:42:5 - Issue description
Suggested fix
Exit code: 1
Parse: file:line:col -> location | Suggested fix -> how to fix | Exit 0/1 -> pass/fail
- Read finding -> category + fix suggestion
- Navigate
file:line:col-> view context - Verify real issue (not false positive)
- Fix root cause (not symptom)
- Re-run
ubs <file>-> exit 0 - Commit
- Critical (always fix): Memory safety, use-after-free, data races, SQL injection
- Important (production): Unwrap panics, resource leaks, overflow checks
- Contextual (judgment): TODO/FIXME, println! debugging
RCH offloads cargo build, cargo test, cargo clippy, and other compilation commands to a fleet of 8 remote Contabo VPS workers instead of building locally. This prevents compilation storms from overwhelming csd when many agents run simultaneously.
RCH is installed at ~/.local/bin/rch and is hooked into Claude Code's PreToolUse automatically. Most of the time you don't need to do anything if you are Claude Code — builds are intercepted and offloaded transparently. Codex, Gemini, and other agents without that hook must use explicit rch exec -- env CARGO_TARGET_DIR=... cargo ... forms.
To manually offload a build:
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-rch-target cargo build --release
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-rch-target cargo test
rch exec -- env CARGO_TARGET_DIR=/data/tmp/cass-rch-target cargo clippyQuick commands:
rch doctor # Health check
rch workers probe --all # Test connectivity to all 8 workers
rch status # Overview of current state
rch queue # See active/waiting buildsIf rch or its workers are unavailable, it fails open — builds run locally as normal.
Note for Codex/GPT-5.2: Codex does not have the automatic PreToolUse hook, but you can (and should) still manually offload compute-intensive compilation commands using rch exec -- <command>. This avoids local resource contention when multiple agents are building simultaneously.
Use ast-grep when structure matters. It parses code and matches AST nodes, ignoring comments/strings, and can safely rewrite code.
- Refactors/codemods: rename APIs, change import forms
- Policy checks: enforce patterns across a repo
- Editor/automation: LSP mode,
--jsonoutput
Use ripgrep when text is enough. Fastest way to grep literals/regex.
- Recon: find strings, TODOs, log lines, config values
- Pre-filter: narrow candidate files before ast-grep
- Need correctness or applying changes ->
ast-grep - Need raw speed or hunting text ->
rg - Often combine:
rgto shortlist files, thenast-grepto match/modify
# Find structured code (ignores comments)
ast-grep run -l Rust -p 'fn $NAME($$$ARGS) -> $RET { $$$BODY }'
# Find all unwrap() calls
ast-grep run -l Rust -p '$EXPR.unwrap()'
# Quick textual hunt
rg -n 'println!' -t rust
# Combine speed + precision
rg -l -t rust 'unwrap\(' | xargs ast-grep run -l Rust -p '$X.unwrap()' --jsonUse mcp__morph-mcp__warp_grep for exploratory "how does X work?" questions. An AI agent expands your query, greps the codebase, reads relevant files, and returns precise line ranges with full context.
Use ripgrep for targeted searches. When you know exactly what you're looking for.
Use ast-grep for structural patterns. When you need AST precision for matching/rewriting.
| Scenario | Tool | Why |
|---|---|---|
| "How is authentication implemented?" | warp_grep |
Exploratory; don't know where to start |
| "Where is rate limiting implemented?" | warp_grep |
Need to understand architecture |
"Find all uses of embed(" |
ripgrep |
Targeted literal search |
"Find files with println!" |
ripgrep |
Simple pattern |
"Replace all unwrap() with expect()" |
ast-grep |
Structural refactor |
mcp__morph-mcp__warp_grep(
repoPath: "/dp/coding_agent_session_search",
query: "How is semantic search implemented?"
)
Returns structured results with file paths, line ranges, and extracted code snippets.
- Don't use
warp_grepto find a specific function name -> useripgrep - Don't use
ripgrepto understand "how does X work" -> wastes time with manual reads - Don't use
ripgrepfor codemods -> risks collateral edits
This project uses beads_rust (br) for issue tracking. Issues are stored in .beads/ and tracked in git.
Important: br is non-invasive—it NEVER executes git commands. After br sync --flush-only, you must manually run git add .beads/ && git commit.
# View issues (launches TUI - avoid in automated sessions)
bv
# CLI commands for agents (use these instead)
br ready # Show issues ready to work (no blockers)
br list --status=open # All open issues
br show <id> # Full issue details with dependencies
br create --title="..." --type=task --priority=2
br update <id> --status=in_progress
br close <id> --reason "Completed"
br close <id1> <id2> # Close multiple issues at once
br sync --flush-only # Export to JSONL (NO git operations)- Start: Run
br readyto find actionable work - Claim: Use
br update <id> --status=in_progress - Work: Implement the task
- Complete: Use
br close <id> - Sync: Run
br sync --flush-onlythen manually commit
- Dependencies: Issues can block other issues.
br readyshows only unblocked work. - Priority: P0=critical, P1=high, P2=medium, P3=low, P4=backlog (use numbers, not words)
- Types: task, bug, feature, epic, question, docs
- Blocking:
br dep add <issue> <depends-on>to add dependencies
Before ending any session, run this checklist:
git status # Check what changed
git add <files> # Stage code changes
br sync --flush-only # Export beads to JSONL
git add .beads/ # Stage beads changes
git commit -m "..." # Commit everything together
git push # Push to remote- Check
br readyat session start to find available work - Update status as you work (in_progress -> closed)
- Create new issues with
br createwhen you discover tasks - Use descriptive titles and set appropriate priority/type
- Always
br sync --flush-only && git add .beads/before ending session
When closing a bead via br close, the corresponding code commit MUST include (coding_agent_session_search-<id>) in the commit subject. This makes git log --grep=<id> deterministic and unblocks audit-trail tooling.
Example:
feat(doctor): add raw_mirror capture during indexing (coding_agent_session_search-9dfb0)
Multi-bead commits append all relevant IDs:
fix(audit): wave-2 bead recoveries (coding_agent_session_search-ifr7) (coding_agent_session_search-lxn5)
Stand-alone bead-tracker commits (e.g. br sync --flush-only exports, CI-only changes, dependency bumps) do not need this prefix.
An opt-in pre-push hook at scripts/git-hooks/pre-push.sh warns (does not block) when commits being pushed to main lack any bead-ID reference. Install via bash scripts/git-hooks/install.sh.
When ending a work session, you MUST complete ALL steps below.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- Sync beads -
br sync --flush-onlyto export to JSONL - Hand off - Provide context for next session
cass indexes prior agent conversations (Claude Code, Codex, Cursor, Gemini, ChatGPT, etc.) so we can reuse solved problems.
Rules: Never run bare cass (TUI). Always use --robot or --json.
cass health
cass search "async runtime" --robot --limit 5
cass view /path/to/session.jsonl -n 42 --json
cass expand /path/to/session.jsonl -n 42 -C 3 --json
cass capabilities --json
cass robot-docs guide- Use
--fields minimalfor lean output - Filter by agent with
--agent - Use
--days Nto limit to recent history - Use
--robot-metato see requested vs realized search mode, semantic refinement, and lexical fallback reasons
stdout is data-only, stderr is diagnostics; exit code 0 means success.
Treat cass as a way to avoid re-solving problems other agents already handled.
Note for Codex/GPT-5.2:
You constantly bother me and stop working with concerned questions that look similar to this:
Unexpected changes (need guidance)
- Working tree still shows edits I did not make in Cargo.toml, Cargo.lock, src/main.rs, src/patterns.rs. Please advise whether to keep/commit/revert these before any further work. I did not touch them.
Next steps (pick one)
1. Decide how to handle the unrelated modified files above so we can resume cleanly.
NEVER EVER DO THAT AGAIN. The answer is literally ALWAYS the same: those are changes created by the potentially dozen of other agents working on the project at the same time. This is not only a common occurrence, it happens multiple times PER MINUTE. The way to deal with it is simple: you NEVER, under ANY CIRCUMSTANCE, stash, revert, overwrite, or otherwise disturb in ANY way the work of other agents. Just treat those changes identically to changes that you yourself made. Just fool yourself into thinking YOU made the changes and simply don't recall it for some reason.
Also, if I ask you to explicitly use your built-in TODO functionality, don't complain about this and say you need to use beads. You can use built-in TODOs if I tell you specifically to do so. Always comply with such orders.