Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ All documented in `website/guide/installation.md`:
▼ store::SessionStore (discovery + path decoding)
▼ parser::parse_session / stream_records (streaming JSONL → SessionStats)
~/.claudex/index.db (SQLite, schema_version=2, created on demand)
~/.claudex/index.db (SQLite, schema_version=3, created on demand)
▼ index::IndexStore::ensure_fresh / sync_now / force_rebuild
Expand All @@ -151,9 +151,10 @@ commands::<name>::run → stdout (tables + palette via ui module, JSON via --j
- `src/store.rs` — locates session files, decodes project directory names (`/.hidden` ↔ `--hidden`, `/seg` ↔ `-seg`), and canonicalises worktree paths (`…/.claude/worktrees/<branch>` aggregates to the parent project). `SessionStore::at(path)` is a test-only constructor.
- `src/parser.rs` — `SessionStats` accumulator; `stream_records` reads JSONL one record at a time so large sessions don't balloon memory.
- `src/types.rs` — `TokenUsage` and `ModelPricing` (Opus/Sonnet/Haiku pricing tiers; default is Sonnet). `cost_for_model` is the single source of truth for pricing math.
- `src/stats.rs` — small numeric helpers shared across commands (e.g. `percentile_sorted` used by `turns` and the session drill-down).
- `src/index.rs` — `IndexStore` (SQLite via `rusqlite`, bundled). Tables: `sessions`, `token_usage`, `tool_calls`, `turn_durations`, `pr_links`, `file_modifications`, `thinking_usage`, `stop_reasons`, `attachments`, `permission_changes`, plus an FTS virtual table `messages_fts`. Incremental sync keys on `(file_path, file_size, file_mtime)`. `IndexStore::open_at(path)` is a test-only constructor.
- `src/ui.rs` — **single home for every presentation concern**: palette (semantic helpers like `project`, `cost`, `cell_project`, `cell_cost`), `table()` builder (minimal style, dynamic width via `terminal_size`), `Spinner` (TTY-gated, stderr), number formatters (`fmt_cost` → `$12,345.67` with sub-cent fallback to 4 decimals, `fmt_count` → `326,297`), and `ColorChoice` / `apply_color_choice`.
- `src/commands/*.rs` — one module per subcommand: `sessions`, `cost`, `search`, `tools`, `watch`, `summary`, `export`, `index`, `turns`, `prs`, `files`, `models`, `update`, `completions` (via helper in `main.rs`).
- `src/commands/*.rs` — one module per subcommand: `sessions`, `session`, `cost`, `search`, `tools`, `watch`, `summary`, `export`, `index`, `turns`, `prs`, `files`, `models`, `update`, `codex`. (`completions` is generated by a helper directly in `main.rs`, not a module here.)
- `tests/index_tests.rs` — unit-style tests against parser/types/store.
- `tests/index_store_tests.rs` — integration tests against every `IndexStore` query method using `TempDir` + `open_at`/`at`.
- `tests/cli_tests.rs` — end-to-end subprocess tests against the compiled binary with a fixture `$HOME`. Exercises every subcommand's indexed and `--no-index` paths, JSON and text output, and the `--color` flag.
Expand All @@ -166,6 +167,7 @@ commands::<name>::run → stdout (tables + palette via ui module, JSON via --j
- **Schema migrations**: bumping `SCHEMA_VERSION` in `src/index.rs` triggers a rebuild on next open. Add new columns/tables inside the `CREATE TABLE IF NOT EXISTS` block and bump the version.
- **Worktree aggregation**: always key on `canonical_project_path(&decoded)` when grouping by project, and use `display_project_name` for user-facing labels (renders worktree sessions as `"projectname (worktree)"`).
- **Pricing math lives in `types.rs`**. Do not inline per-token multipliers in commands — call `TokenUsage::cost_for_model` so the Opus/Sonnet/Haiku tiers stay consistent.
- **`codex` is the one exception to the index pipeline.** It scans `~/.codex/sessions/**` (and `~/.codex/archived_sessions/`) directly on every invocation, plus an optional read-only open of `~/.codex/state_5.sqlite` and `~/.codex/session_index.jsonl` — no `~/.claudex/` writes, no `--no-index` flag, no staleness window. Keep it that way unless you also build a parallel ingest path; do not silently route Codex data through `IndexStore`.

### Adding a new subcommand

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Source builds require Rust 1.95+. Prebuilt binaries have no runtime dependencies
| Command | What it does |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| [`summary`](https://utensils.io/claudex/commands/summary) | Dashboard — sessions, cost, top projects/tools, model mix |
| `codex` | Codex CLI session/state stats from `~/.codex` |
| [`codex`](https://utensils.io/claudex/commands/codex) | Codex CLI session/state stats from `~/.codex` |
| [`sessions`](https://utensils.io/claudex/commands/sessions) | List sessions grouped by project |
| [`session <selector>`](https://utensils.io/claudex/commands/session) | Drill into one session: cost, tools, files, PRs, turns, stop reasons |
| [`cost`](https://utensils.io/claudex/commands/cost) | Token usage and approximate cost per project or per session |
Expand Down
19 changes: 10 additions & 9 deletions website/guide/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ ui::table() / palette — comfy-table with dynamic width + owo-colors

## Modules

| Module | Purpose |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/main.rs` | clap parser, dispatches to `commands::*::run`. Pre-parses `--color` from argv before `Cli::parse()` so clap-generated help/errors honor the flag. |
| `src/store.rs` | Locates session files, decodes project-directory names (`/.hidden` ↔ `--hidden`, `/seg` ↔ `-seg`), canonicalizes worktree paths (`…/.claude/worktrees/<branch>` aggregates to the parent project). |
| `src/parser.rs` | `SessionStats` accumulator; `stream_records` reads JSONL one record at a time. |
| `src/types.rs` | `TokenUsage`, `ModelPricing` (Opus/Sonnet/Haiku tiers). `cost_for_model` is the single source of truth for pricing math. |
| `src/index.rs` | `IndexStore` (SQLite). Relational report tables plus an FTS5 virtual table. Incremental sync keyed on `(file_path, file_size, file_mtime)`. |
| `src/ui.rs` | Palette, `table()` builder, number formatters (`fmt_cost`, `fmt_count`), `Spinner`, `ColorChoice`. Everything presentation. |
| `src/commands/*.rs` | One file per subcommand: `sessions`, `cost`, `search`, `tools`, `watch`, `summary`, `session`, `export`, `index`, `turns`, `prs`, `files`, `models`, `codex`, `update`, `completions`. |
| Module | Purpose |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/main.rs` | clap parser, dispatches to `commands::*::run`. Pre-parses `--color` from argv before `Cli::parse()` so clap-generated help/errors honor the flag. |
| `src/store.rs` | Locates session files, decodes project-directory names (`/.hidden` ↔ `--hidden`, `/seg` ↔ `-seg`), canonicalizes worktree paths (`…/.claude/worktrees/<branch>` aggregates to the parent project). |
| `src/parser.rs` | `SessionStats` accumulator; `stream_records` reads JSONL one record at a time. |
| `src/types.rs` | `TokenUsage`, `ModelPricing` (Opus/Sonnet/Haiku tiers). `cost_for_model` is the single source of truth for pricing math. |
| `src/stats.rs` | Small numeric helpers shared across commands — `percentile_sorted` powers the p50/p95/max columns in `turns` and the per-session drill-down. |
| `src/index.rs` | `IndexStore` (SQLite). Relational report tables plus an FTS5 virtual table. Incremental sync keyed on `(file_path, file_size, file_mtime)`. |
| `src/ui.rs` | Palette, `table()` builder, number formatters (`fmt_cost`, `fmt_count`), `Spinner`, `ColorChoice`. Everything presentation. |
| `src/commands/*.rs` | One file per subcommand: `sessions`, `cost`, `search`, `tools`, `watch`, `summary`, `session`, `export`, `index`, `turns`, `prs`, `files`, `models`, `codex`, `update`. (`completions` is generated by a helper in `main.rs`, not a module here.) |

## Key invariants

Expand Down
14 changes: 11 additions & 3 deletions website/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ questions of_.

Every read command:

- Uses the index by default (incremental sync, 5-minute staleness window).
- Supports `--no-index` to bypass the index and scan JSONL files directly — the
fallback path always matches the indexed path.
- Supports `--json` to emit a stable, machine-readable shape.
- Honors `--color auto|always|never` (and `NO_COLOR`) for color output.

Most Claude Code reports also:

- Use the index by default (incremental sync, 5-minute staleness window).
- Support `--no-index` to bypass the index and scan JSONL files directly — the
fallback path always matches the indexed path.

Two caveats: `models`, `turns`, `prs`, and `files` are derived from the index
only and don't accept `--no-index`. And `claudex codex` is a separate reader —
it scans `~/.codex/` directly and never touches the Claude Code index. See the
[flag support matrix](/commands/) for the per-command breakdown.

## Who is it for?

You already use Claude Code and want to:
Expand Down
Loading