https://github.com/glitchwerks/claude-prospector
Claude Code efficiency and hygiene toolkit — token spend analysis across the 5h / 7d / Sonnet-7d billing windows plus configuration overlap auditing. Ships as a Claude Code plugin with three skills (usage-analysis, usage-dashboard, claude-audit) backed by a Python CLI.
https://github.com/glitchwerks/claude-prospector
audit claude-code claude-code-plugins claude-skill cli dashboard developer-tools observability python token-tracking
Last synced: 1 day ago
JSON representation
Claude Code efficiency and hygiene toolkit — token spend analysis across the 5h / 7d / Sonnet-7d billing windows plus configuration overlap auditing. Ships as a Claude Code plugin with three skills (usage-analysis, usage-dashboard, claude-audit) backed by a Python CLI.
- Host: GitHub
- URL: https://github.com/glitchwerks/claude-prospector
- Owner: glitchwerks
- License: mit
- Created: 2026-04-09T17:32:36.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-13T14:41:38.000Z (1 day ago)
- Last Synced: 2026-06-13T16:24:45.967Z (1 day ago)
- Topics: audit, claude-code, claude-code-plugins, claude-skill, cli, dashboard, developer-tools, observability, python, token-tracking
- Language: Python
- Homepage: https://pypi.org/project/claude-prospector/
- Size: 838 KB
- Stars: 1
- Watchers: 1
- Forks: 1
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# claude-prospector
Claude Code efficiency and hygiene toolkit. Surfaces token spend across the three billing windows with per-model / per-agent / per-skill attribution, and audits your effective Claude Code configuration for agent and skill overlap.
## Why
`claude-prospector` bundles a set of skills that target distinct angles of "is my Claude Code setup healthy?":
| Skill | Angle |
|---|---|
| `usage-analysis` | where your tokens are going |
| `usage-dashboard` | regenerate the cost dashboard surface |
| `claude-audit` | where your config has agent / skill overlap or bloat |
| `session-analysis` | whether a session stayed on task (opt-in LLM judgment) |
Claude Code's built-in `/usage` shows current-session token totals and — for Max/Pro subscribers — plan-usage bars on the same screen. It doesn't surface multi-day history, per-agent attribution with sub-agent nesting, per-skill invocation counts, or per-project breakdowns, and there's no way to ask "where are my Sonnet-7d tokens going this week?" from inside the session. There's also no built-in way to detect when two installed plugins ship overlapping `code-reviewer` agents or near-duplicate skills.
`claude-prospector` reads Claude Code's local JSONL session files to break tokens down by model, agent (with sub-agent nesting), skill, and project across all three billing windows (5h rolling, 7d rolling, Sonnet-only 7d), and inventories your custom + plugin-provided agents and skills to produce a structured overlap / conflict report with keep / modify / drop recommendations.
## Install
### 1. Add the marketplace and install the plugin
```bash
claude plugin marketplace add glitchwerks/plugins
claude plugin install claude-prospector@glitchwerks
```
### 2. First-run setup
After installing (or after a plugin update), open a new Claude Code session. You will see a banner:
> claude-prospector requires setup. Run /setup-prospector to materialise the Python venv. After setup completes, open a new session to activate the dashboard, skill-tracking, and usage-analysis features.
Run `/setup-prospector` once. The skill will:
1. Discover a Python 3.10+ interpreter on your system.
2. Create a plugin-owned venv at `${CLAUDE_PLUGIN_DATA}/venv/`.
3. Install `claude-prospector` from PyPI into that venv.
4. Verify the install and record a setup-state flag.
After setup completes, open a new session — the banner will be gone and all features will work normally.
You will need to re-run `/setup-prospector` only when:
- The plugin updates to a new version (banner: "venv is for vX but plugin is vY").
- The venv is corrupted or deleted (banner: "venv at `` is unreachable or corrupt").
- You move to a new machine (setup is per-machine; the flag is not portable).
## What you can do
### `usage-analysis` skill
Conversational analysis with recommendations. Triggered by natural-language phrases such as:
- "am I close to my Sonnet limit?"
- "where are my tokens going?"
- "which agent uses the most tokens?"
- "give me a usage analysis"
The skill reads your session data and responds inline — no browser required.
### `usage-dashboard` skill
Bare dashboard regeneration. Triggered by phrases like "regenerate the dashboard" or "rebuild my usage dashboard". Writes the HTML file and reports the path, without interpreting the data.
The generated HTML dashboard includes:
- **Budget gauges** — estimated usage against each billing bucket (5h / 7d / Sonnet-only 7d)
- **Model breakdown** — donut chart and daily stacked bar chart (Opus / Sonnet / Haiku)
- **Agent breakdown** — token usage per agent with model attribution and nested sub-agent tracing
- **Skill usage** — invocation counts per skill
- **Project breakdown** — tokens per project
- **Session drill-down** — click a day to see individual sessions with agents, tokens, and model split
### `claude-audit` skill
Audits your project's effective Claude Code configuration — custom and plugin-provided agents and skills together — and produces a structured overlap / conflict report with keep / modify / drop recommendations scoped to the project's stated objectives. Triggered by `/claude-prospector:claude-audit` or natural-language phrases such as:
- "audit my claude config"
- "find overlap in my agents"
- "check for skill conflicts"
- "are any of my agents duplicates"
- "what's redundant in my setup"
The skill is read-only — it does not modify any files. All recommendations are presented for your review.
### `session-analysis` skill
The interpretive (LLM) complement to the deterministic `session-audit` CLI. Where `session-audit` extracts ask-vs-done for free, `session-analysis` adds the judgment layer: did the agent stay on the original ask, and what did it acknowledge skipping?
The skill loads `session-audit`'s extract (the 1a fields), then has the agent produce two judgment fields — `Variance` and `What was NOT done` — and persists a combined record via the `variance-save` subcommand.
**Key constraints:**
- **Opt-in, not automatic.** Costs ~1-3k tokens of the current session; run it selectively on sessions you suspect drifted, not on every session.
- **Sonnet or stronger recommended.** Judgment quality depends on the model.
Trigger phrases: `/session-analysis`, "did this session stay on task", "analyze session drift", "did the agent do what I asked", "what did this session skip".
Cross-references:
- `session-audit` — free deterministic ask/actions extract (run this first)
- `usage-analysis` — token-spend insights
- `claude-audit` — agent/skill config overlap
The full skill definition is at `skills/session-analysis/SKILL.md`.
### `setup-prospector` skill
First-run and post-update setup. Triggered by `/setup-prospector` or phrases like "set up claude-prospector", "fix prospector", or "prospector isn't working". See [Install](#install) for the full walkthrough.
### SessionStart hook (`check-prospector-setup.py`)
Fires once at the beginning of every session. Checks setup state and emits a banner when setup is missing, stale, or broken. Silent when everything is valid. This hook never blocks the session.
### `skill-tracker` hook (`skill-tracker.py`, PreToolUse)
Logs `Skill` and `Agent` tool-use events to the state directory for the `by_skill` and skill-passed-vs-invoked analyses. Gated on VALID setup state — if you skip `/setup-prospector`, skill-tracking is silently inactive until setup is complete.
### `dashboard-regen` hook (Stop, opt-in)
Auto-regenerates the dashboard after every session when `autoregen` is enabled. Off by default; toggle via the plugin manager (see [Configuration](#configuration)).
## Configuration
The `dashboard-regen` Stop hook is opt-in. Toggle it through the Claude Code plugin manager — no manual file edits required:
```
/plugin reconfigure claude-prospector
```
You will be prompted to enable or disable `autoregen`. You can also set it at install time when the plugin manager shows the initial configuration prompt.
To inspect the current plugin configuration, use the read-only CLI:
```bash
python -m claude_prospector config --show
```
When a config file exists, this prints its contents as pretty-printed JSON to stdout.
When no config file exists, it prints `(no config file yet)` and a redirect note to stderr, and `{}` to stdout. Exit code is 0 in both cases.
The authoritative `autoregen` value is whatever is set in the plugin manager — not the legacy `config.json`.
### Hiding noise projects from the dashboard
Claude Code creates session directories for every working directory it opens,
including Electron app internals, Warp terminal worktrees, and other
non-project paths. You can hide these from the dashboard's project view by
adding a `project_exclude_patterns` list to your `config.json`:
```json
{
"project_exclude_patterns": [
"AppData\\Local\\Programs",
"warp\\Warp\\data\\worktrees",
"AppData\\Roaming\\Open-Design"
]
}
```
Each entry is a **case-sensitive substring** matched against the full project
path (the `cwd` field from the session, or the decoded directory slug when no
`cwd` is available). A session is hidden when its project path contains any
listed pattern. The default is an empty list — no projects are hidden.
The config file is located at `base_dir() / "config.json"` (override with
`CLAUDE_PROSPECTOR_CONFIG`). Edit it with any text editor; it is read on every
`dashboard` invocation.
### Project labels in the dashboard
The dashboard now shows the **leaf directory name** (e.g. `claude-prospector`)
as the project label instead of the full encoded slug. Hover over any project
name to see the full path in a tooltip.
The leaf name is derived from the `cwd` field recorded in the session when
available (most accurate), falling back to the last segment of the encoded
directory name.
## Environment variables
| Variable | Controls | Notes |
|---|---|---|
| `CLAUDE_PLUGIN_DATA` | Venv placement and default state/dashboard storage | Set by the Claude Code plugin host; do not override in normal use |
| `CLAUDE_PROSPECTOR_BASE_DIR` | State and dashboard storage for hooks and CLI | Overrides `CLAUDE_PLUGIN_DATA` for hooks/CLI only; does not affect the venv location |
| `CLAUDE_PROSPECTOR_PIP_SPEC` | The pip spec used by `/setup-prospector` | Overrides the default `claude-prospector==` — used in CI and dev to install from TestPyPI or a local checkout |
## Troubleshooting
The SessionStart hook emits one of four banner states. Use the banner text to decide what to do:
**MISSING** — No setup-state flag found, or the previous venv failed the per-session import probe.
> claude-prospector requires setup. Run /setup-prospector to materialise the Python venv. After setup completes, open a new session to activate the dashboard, skill-tracking, and usage-analysis features.
Action: run `/setup-prospector`, then open a new session.
**STALE** — The flag records a different plugin version than the one currently installed.
> claude-prospector venv is for v`` but plugin is v``. Run /setup-prospector to refresh the venv.
Action: run `/setup-prospector` to rebuild the venv for the new version.
**BROKEN** — The flag exists and the version matches, but the venv path is unreachable or corrupt.
> claude-prospector venv at `` is unreachable or corrupt. Run /setup-prospector to recreate it.
Action: run `/setup-prospector` to recreate the venv.
**VALID (probe failed)** — The flag looks valid but the per-session `import claude_prospector` probe failed. The hook downgrades state to MISSING and emits the MISSING banner.
Action: same as MISSING — run `/setup-prospector`, then open a new session.
**Silent session** — No banner emitted. Setup is valid and the import probe passed. All features are active.
## Subcommands
All functionality is accessed through named subcommands. Bare `claude-prospector` (no subcommand) prints help and exits 0.
### `dashboard` — interactive HTML dashboard
```bash
# Default: last 7 days, opens in browser
python -m claude_prospector dashboard
# Rolling window matching Claude billing buckets
python -m claude_prospector dashboard --window 5h
python -m claude_prospector dashboard --window 7d
# Custom date range
python -m claude_prospector dashboard --from 2026-04-01 --to 2026-04-09
# Output to file instead of opening browser
python -m claude_prospector dashboard --output report.html --no-open
# Custom Claude data directory
python -m claude_prospector dashboard --data-dir "D:\other\.claude"
# Set budget limits for gauge percentages
python -m claude_prospector dashboard --limit-5h 600000 --limit-7d 4000000 --limit-sonnet-7d 2000000
# Emit JSON for scripting or CI
python -m claude_prospector dashboard --format json
```
### `session-summary` — deterministic session recap
Reads a single Claude Code transcript JSONL file and emits a structured JSON summary suitable for consumption by the `/whats-next` skill or any other tool that needs to know what a session did.
```bash
python -m claude_prospector session-summary --path ~/.claude/projects//.jsonl
```
**Flags:**
| Flag | Default | Description |
|---|---|---|
| `--path PATH` | *(required)* | Path to the transcript JSONL file |
| `--format {json,text}` | `json` | Output format. `json` is the machine-readable contract; `text` is a human-readable debug view |
| `--max-actions N` | `50` | Cap on emitted actions. `0` disables the cap |
**Sample output (`--format json`):**
```json
{
"project": "claude-prospector",
"intent": "Implement the session-summary subcommand for the /whats-next skill",
"actions": [
"Edited claude_prospector/cli/session_summary.py",
"Created tests/test_session_summary.py",
"Ran pytest tests/test_session_summary.py -x",
"Dispatched code-reviewer sub-agent"
],
"stoppedNaturally": true
}
```
**Exit codes:**
| Code | Meaning | stderr |
|---|---|---|
| `0` | Success — JSON written to stdout | *(silent)* |
| `1` | IO failure reading `--path` (file missing, permission denied, etc.) | `session-summary: cannot read transcript at '': : ` |
| `2` | File readable but contains no external user turns | `session-summary: transcript '' contains no user turns` |
| `3` | File has content but none of it parses as JSONL | `session-summary: transcript '' is not valid JSONL` |
On any non-zero exit, stdout is empty and stderr contains exactly one line.
### `session-audit` — ask-vs-action extraction at zero LLM cost
```bash
# By path
python -m claude_prospector session-audit --path
# By session-id (resolves transcript under ~/.claude/projects/)
python -m claude_prospector session-audit --session-id
```
Reads a single Claude Code session transcript (`.jsonl`) and emits
structured data capturing what was asked and what file edits were made,
with **no API calls**.
#### Flags
| Flag | Default | Description |
|---|---|---|
| `--path ` | *(mutually exclusive with `--session-id`)* | Path to the transcript JSONL file |
| `--session-id ` | *(mutually exclusive with `--path`)* | Session id; the transcript is resolved under `~/.claude/projects/` (override root with `--data-dir`) |
| `--format json\|text` | `json` | Output format |
| `--batch` | off | *(not yet implemented)* Walk `~/.claude/projects/**/*.jsonl` and emit per-session array |
#### JSON schema (`--format json`)
```json
{
"original_ask": "",
"prior_asks": ["", "..."],
"actions": [
{"tool": "", "file_path": ""},
"..."
]
}
```
| Field | Type | Description |
|---|---|---|
| `original_ask` | `string \| null` | Verbatim text of the **first** non-system, non-tool-result external user message. `null` when absent. |
| `prior_asks` | `string[]` | Verbatim text of each subsequent distinct user ask, in transcript order. Empty array for single-ask sessions. |
| `actions` | `object[]` | Chronologically ordered `Edit`/`Write`/`NotebookEdit` tool_use events. Bash invocations excluded. |
#### Example output
```json
{
"original_ask": "Add a --dry-run flag to the CLI.",
"prior_asks": [
"Now also write tests for it.",
"close the pr"
],
"actions": [
{"tool": "Edit", "file_path": "src/claude_prospector/__main__.py"},
{"tool": "Write", "file_path": "tests/test_dry_run.py"}
]
}
```
#### Exit codes
| Code | Meaning | stderr |
|---|---|---|
| `0` | Success — output written to stdout | *(silent)* |
| `1` | IO failure reading `--path` (file missing, permission denied, etc.) | `session-audit: cannot read transcript at '': : ` |
| `2` | File readable but contains no external user turns | `session-audit: transcript '' contains no user turns` |
| `3` | File has content but none of it parses as JSONL | `session-audit: transcript '' is not valid JSONL` |
On any non-zero exit, stdout is empty and stderr contains exactly one line.
---
### `variance-save` — persist combined audit + judgment
```bash
# Judgment supplied as a file
python -m claude_prospector variance-save --session-id --judgment-file
# Judgment supplied on stdin (--judgment-file omitted)
python -m claude_prospector variance-save --session-id < judgment.json
```
Re-runs `session-audit` internally (1a), merges the result with the supplied judgment JSON, and writes a combined record. Transcript search and output location are independent — the transcript is resolved under `~/.claude/projects/` (override with `--data-dir`); output goes to `/variance/.json` by default (override with `--out `). Prints the written path on success.
#### Judgment input shape
```json
{"variance": "", "not_done": "", "severity": }
```
`severity` is optional (0 = on task, 1 = minor drift, 2 = notable scope or skipped ask, 3 = session largely off task).
#### Combined output schema
```json
{
"session_id": "",
"original_ask": "",
"prior_asks": [""],
"actions": [{"tool": "", "file_path": ""}],
"variance": "",
"not_done": "",
"severity": ""
}
```
This artifact is the intended input for future drift-aggregation work (see issue #63).
| Flag | Default | Description |
|---|---|---|
| `--session-id ` | *(required)* | Session id; transcript resolved under `--data-dir` |
| `--judgment-file ` | stdin | Path to the judgment JSON file; omit to read from stdin |
| `--data-dir ` | `~/.claude` | Root under which `projects/` is searched for the transcript |
| `--out ` | `/variance/.json` | Override the output path |
#### Exit codes
| Code | Meaning | stderr |
|---|---|---|
| `0` | Success — combined record written; path printed to stdout | *(silent)* |
| `1` | IO failure (transcript missing, judgment unreadable, output unwritable) | `variance-save: ` |
| `2` | Transcript found but contains no user turns | `variance-save: transcript '' contains no user turns` |
| `3` | Judgment input is not valid JSON or missing required fields | `variance-save: judgment: ` |
---
### `audit` — agent/skill inventory and hygiene report
```bash
python -m claude_prospector audit
```
Deterministically inventories all agents and skills visible in the effective
Claude Code configuration (custom and plugin-provided), then reports:
- **Name collisions** — agents or skills with identical names loaded from
different sources
- **Jaccard semantic overlap** — pairs whose description tokens overlap above a
configurable threshold, signalling potential duplicate capability
- **Tool-coupling mismatches** — agents declared without the tools they call,
or with tools they never reference
- **Cache hygiene issues** — stale plugin cache entries that may shadow the
live install
The subcommand is read-only — it never modifies files. Output is written to
stdout; pipe to a file or redirect as needed. The `/claude-prospector:claude-audit`
skill wraps this subcommand for conversational use inside Claude Code sessions.
### `config` — inspect configuration
```bash
python -m claude_prospector config --show
```
Prints current `config.json` contents, or `{}` when no config file exists. See [Configuration](#configuration) for full details.
## Migration
### v0.6.0 → v0.7.0 (Pattern W)
After upgrading to v0.7.0, open a new Claude Code session. A banner will prompt you to run `/setup-prospector`. This is a one-time action per machine.
If you previously installed `claude-prospector` into `~/.claude/.venv`, you can leave that install in place — Pattern W hooks always use the plugin-owned venv via an absolute path. To reclaim disk space you may `uv pip uninstall claude-prospector` from `~/.claude/.venv` after setup; this is optional.
### Pre-v0.2.0 CLI callers
The bare flag form **no longer works** after v0.2.0:
```bash
# REMOVED — will print help and exit 0, not run the dashboard
claude-prospector --format json
# CORRECT
claude-prospector dashboard --format json
```
Any script, skill, or CI step that invokes `claude-prospector` with bare flags (no subcommand) must be updated to use `claude-prospector dashboard [flags]`.
### Upgrading from v0.4.x (autoregen config)
If you previously ran `python -m claude_prospector config --enable-autoregen`, your old `config.json` is still readable via `--show`. Re-toggle via `/plugin reconfigure claude-prospector` to move to the managed setting. The old `config.json` file is not deleted.
## Internals
### Nested agent attribution
When Claude Code sessions dispatch sub-agents that themselves dispatch further sub-agents, `claude-prospector` traces the full depth and attributes tokens to the complete root-to-leaf chain rather than just the immediate leaf.
- **Data model.** Each `MessageRecord` carries an `agent_path: tuple[str, ...]` field (e.g. `("general-purpose", "project-planner", "Explore")`) and a parallel `agent_type: str` stored field. Both are populated at parse time; the parser enforces the invariant `agent_type == agent_path[-1]` when `agent_path` is non-empty. The two fields are kept in sync by the parser, not by the dataclass itself.
- **`by_agent` keys.** The aggregator's `by_agent` dict is keyed by the full path joined with U+2192 (`→`), for example `"general-purpose→project-planner→Explore"`. Depth-1 sessions produce single-segment keys identical to the pre-change shape.
- **Per-session `agents` list.** Each session's `agents` list contains only the deepest-leaf path per chain. Sibling chains that share a leaf name but differ in their ancestor are both kept. This rule preserves the dashboard JS's per-agent token apportionment, which divides session totals by `s.agents.length`.
- **Depth limit.** Path tuples may contain up to 10 segments total (`_MAX_AGENT_PATH_LENGTH = 10`). Beyond that, the parser emits a single `UserWarning` and stops descending; deeper messages are bucketed under the last walked ancestor.
- **Sanitization.** A literal `→` appearing inside an agent name is replaced with `﹖` (U+FE56) at parse time and a `UserWarning` fires. The sanitized name is used throughout.
- **Deferred.** Dashboard tree visualization (sunburst, indented tree, expand/collapse) is out of scope for the current release. The existing flat agent list in the dashboard JS receives path-keyed entries but no hierarchical rendering yet.
### State storage
When running as a plugin, state (dashboard HTML, hook log, skill-tracking JSONL files) is stored under `${CLAUDE_PLUGIN_DATA}` — the Anthropic-documented persistent state location that survives plugin updates.
Users upgrading from v0.4.0 get a one-time automatic migration: on the first session after upgrade, any existing files from `~/.claude/claude-prospector/` are moved into `${CLAUDE_PLUGIN_DATA}` and the legacy directory is removed.
## Development
### Install for development
```bash
git clone https://github.com/glitchwerks/claude-prospector.git
cd claude-prospector
uv sync # installs runtime + ruff + pytest; creates .venv with Python 3.12
```
Requires Python 3.12 (pinned via `.python-version`).
If `uv sync` creates the venv with the wrong interpreter (e.g. you have a newer Python on PATH), delete `.venv/` and re-run `uv sync`. The `.python-version` file pins Python 3.12 and `uv` will locate or download it automatically.
### Testing
```bash
pytest # 358 tests, typically finishes in under 5 seconds
```
### Linting and formatting
```bash
ruff check . # lint
ruff format . # autoformat in-place
ruff format --check . # format gate (used in CI — exits non-zero on drift)
```
### CI
GitHub Actions runs on every PR and push to `main`:
- **lint** (Ubuntu): `ruff check .` + `ruff format --check .`
- **test** (Ubuntu + Windows, Python 3.10): `pytest`
Both jobs must be green before a PR can merge.
### Future enhancements
Issue #67 tracks making `claude plugin update` handle the Python venv refresh automatically, so that `/setup-prospector` would not need to be run manually after updates. Until that lands, re-run `/setup-prospector` after each plugin update when prompted by the SessionStart banner.
## Contributing
To release a new version, see [docs/release-process.md](docs/release-process.md).
## License
MIT — see [LICENSE](LICENSE).