claude-prospector 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. claude_prospector-0.7.0/LICENSE +21 -0
  2. claude_prospector-0.7.0/PKG-INFO +11 -0
  3. claude_prospector-0.7.0/README.md +335 -0
  4. claude_prospector-0.7.0/claude_prospector/__init__.py +10 -0
  5. claude_prospector-0.7.0/claude_prospector/__main__.py +53 -0
  6. claude_prospector-0.7.0/claude_prospector/aggregator.py +270 -0
  7. claude_prospector-0.7.0/claude_prospector/cli/__init__.py +1 -0
  8. claude_prospector-0.7.0/claude_prospector/cli/config.py +143 -0
  9. claude_prospector-0.7.0/claude_prospector/cli/dashboard.py +212 -0
  10. claude_prospector-0.7.0/claude_prospector/cli/session_summary.py +816 -0
  11. claude_prospector-0.7.0/claude_prospector/constants.py +17 -0
  12. claude_prospector-0.7.0/claude_prospector/models.py +99 -0
  13. claude_prospector-0.7.0/claude_prospector/parser.py +399 -0
  14. claude_prospector-0.7.0/claude_prospector/paths.py +223 -0
  15. claude_prospector-0.7.0/claude_prospector/renderer.py +77 -0
  16. claude_prospector-0.7.0/claude_prospector/skill_tracking.py +309 -0
  17. claude_prospector-0.7.0/claude_prospector.egg-info/PKG-INFO +11 -0
  18. claude_prospector-0.7.0/claude_prospector.egg-info/SOURCES.txt +39 -0
  19. claude_prospector-0.7.0/claude_prospector.egg-info/dependency_links.txt +1 -0
  20. claude_prospector-0.7.0/claude_prospector.egg-info/requires.txt +5 -0
  21. claude_prospector-0.7.0/claude_prospector.egg-info/top_level.txt +1 -0
  22. claude_prospector-0.7.0/pyproject.toml +24 -0
  23. claude_prospector-0.7.0/setup.cfg +4 -0
  24. claude_prospector-0.7.0/tests/test_aggregator.py +448 -0
  25. claude_prospector-0.7.0/tests/test_aggregator_adoption.py +159 -0
  26. claude_prospector-0.7.0/tests/test_cli.py +220 -0
  27. claude_prospector-0.7.0/tests/test_cli_config.py +165 -0
  28. claude_prospector-0.7.0/tests/test_cli_subcommands.py +47 -0
  29. claude_prospector-0.7.0/tests/test_dashboard_regen_hook.py +578 -0
  30. claude_prospector-0.7.0/tests/test_dashboard_snapshot.py +80 -0
  31. claude_prospector-0.7.0/tests/test_e2e.py +418 -0
  32. claude_prospector-0.7.0/tests/test_models.py +313 -0
  33. claude_prospector-0.7.0/tests/test_parser.py +969 -0
  34. claude_prospector-0.7.0/tests/test_paths.py +406 -0
  35. claude_prospector-0.7.0/tests/test_phase1_fixtures.py +243 -0
  36. claude_prospector-0.7.0/tests/test_renderer.py +281 -0
  37. claude_prospector-0.7.0/tests/test_session_summary.py +1683 -0
  38. claude_prospector-0.7.0/tests/test_skill_pipeline_sync.py +72 -0
  39. claude_prospector-0.7.0/tests/test_skill_tracker_hook.py +922 -0
  40. claude_prospector-0.7.0/tests/test_skill_tracking.py +249 -0
  41. claude_prospector-0.7.0/tests/test_version_flag.py +66 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christopher Beaulieu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: claude-prospector
3
+ Version: 0.7.0
4
+ Summary: Claude Code token usage analyzer with optimization recommendations. Surfaces what's eating your budget across the 5h / 7d / Sonnet-7d billing windows, with per-model and nested per-agent attribution and skill adoption tracking. Ships both an interactive dashboard and a conversational analysis skill.
5
+ Requires-Python: >=3.10
6
+ License-File: LICENSE
7
+ Requires-Dist: jinja2>=3.1
8
+ Provides-Extra: dev
9
+ Requires-Dist: ruff~=0.6.0; extra == "dev"
10
+ Requires-Dist: pytest~=8.0; extra == "dev"
11
+ Dynamic: license-file
@@ -0,0 +1,335 @@
1
+ # claude-prospector
2
+
3
+ Token usage analyzer for Claude Code that surfaces where your budget is going across all three billing windows, with per-model and per-agent attribution and concrete optimization recommendations.
4
+
5
+ ## Why
6
+
7
+ Claude Code tracks three billing buckets (5h rolling, 7d rolling, Sonnet-only 7d) but provides no per-agent or per-skill visibility. This tool reads Claude Code's local JSONL session files and generates a dashboard that breaks down where your tokens are going.
8
+
9
+ ## Install as a Claude Code plugin
10
+
11
+ The easiest way to use `claude-prospector` is as a Claude Code plugin — no manual
12
+ cloning or path configuration required.
13
+
14
+ ```bash
15
+ claude plugin marketplace add glitchwerks/plugins
16
+ claude plugin install claude-prospector@glitchwerks
17
+ ```
18
+
19
+ ### Prerequisite: Python package
20
+
21
+ The plugin invokes `python -m claude_prospector` under the hood, so the Python
22
+ package must be importable in the environment Claude Code uses. Install with either:
23
+
24
+ - **From a local clone**: `uv pip install -e .`
25
+ - **Directly from GitHub**: `uv pip install "git+https://github.com/glitchwerks/claude-prospector.git"`
26
+
27
+ > A future enhancement (#67) tracks eliminating this two-step install so `claude plugin update` is sufficient on its own.
28
+
29
+ ## First-run setup
30
+
31
+ After installing claude-prospector for the first time (or after a plugin
32
+ update), open a new Claude Code session. You'll see a banner:
33
+
34
+ > claude-prospector requires setup. Run /setup-prospector to materialise
35
+ > the Python venv.
36
+
37
+ Run `/setup-prospector` once. The skill will:
38
+
39
+ 1. Discover a Python 3.10+ interpreter on your system.
40
+ 2. Create a plugin-owned venv at `${CLAUDE_PLUGIN_DATA}/venv/`.
41
+ 3. Install `claude-prospector` from PyPI into that venv.
42
+ 4. Verify the install and record a setup-state flag.
43
+
44
+ After setup completes, open a new session — the banner will be gone and
45
+ the dashboard, skill-tracking, and usage-analysis features will work
46
+ normally.
47
+
48
+ You'll need to re-run `/setup-prospector` only when:
49
+
50
+ - The plugin updates to a new version (banner: "venv is for vX but
51
+ plugin is vY").
52
+ - The venv is corrupted or deleted (banner: "venv at <path> is
53
+ unreachable or corrupt").
54
+ - You move to a new machine (per-machine setup; flag is not portable
55
+ across machines).
56
+
57
+ **Note:** The plugin's hook scripts still run under the harness-provided
58
+ `python` and require a working harness-environment Python interpreter.
59
+ The venv created by `/setup-prospector` is used by the hooks for
60
+ subprocess spawning only.
61
+
62
+ ### Migration from v0.6.0
63
+
64
+ After upgrading to v0.7.0, open a new Claude Code session. A banner will
65
+ prompt you to run `/setup-prospector`. This is a one-time action per
66
+ machine.
67
+
68
+ If you previously installed `claude-prospector` into `~/.claude/.venv`,
69
+ you can leave that install in place — Pattern W hooks always use the
70
+ plugin-owned venv via an absolute path. To reclaim disk you may
71
+ `uv pip uninstall claude-prospector` from `~/.claude/.venv` after setup;
72
+ this is optional.
73
+
74
+ ## What the plugin provides
75
+
76
+ v0.4.0 ships the full plugin surface:
77
+
78
+ - **Interactive dashboard** — HTML report with three-bucket budget gauges (5h / 7d / Sonnet-7d), per-model donut and bar charts, per-agent token attribution with nested sub-agent tracing, skill-invocation counts, and per-project breakdowns.
79
+ - **`usage-analysis` skill** — conversational analysis with recommendations. Answers questions like "am I close to my Sonnet limit?", "where are my tokens going?", and "which agent uses the most?". Triggered by natural-language phrases.
80
+ - **`usage-dashboard` skill** — bare regeneration surface. Triggered by phrases like "regenerate the dashboard" or "rebuild my usage dashboard"; writes the HTML file and reports the path, without interpreting the data.
81
+ - **`skill-tracker` hook** (PreToolUse, always-on) — logs `Skill` tool-use events to the state directory for the `by_skill` and skill-passed-vs-invoked analyses.
82
+ - **`dashboard-regen` hook** (Stop, opt-in) — auto-regenerates the dashboard after every session when the `autoregen` user-config is enabled via the plugin manager (`/plugin reconfigure claude-prospector` or at install time).
83
+
84
+ ### Configuration (autoregen)
85
+
86
+ The `dashboard-regen` Stop hook is opt-in. Toggle it through the Claude Code
87
+ plugin manager — no manual file edits required:
88
+
89
+ ```
90
+ /plugin reconfigure claude-prospector
91
+ ```
92
+
93
+ You will be prompted to enable or disable `autoregen`. You can also set it at
94
+ install time when the plugin manager shows the initial configuration prompt.
95
+
96
+ To inspect the current plugin state, use the read-only CLI:
97
+
98
+ ```bash
99
+ python -m claude_prospector config --show
100
+ ```
101
+
102
+ This prints the legacy `config.json` contents if present. The authoritative
103
+ value is the `autoregen` setting shown in the plugin manager.
104
+
105
+ > **Upgrading from v0.4.x?** If you previously ran
106
+ > `python -m claude_prospector config --enable-autoregen`, your old
107
+ > `config.json` is still readable via `--show`. The hook will log a one-time
108
+ > advisory message to `hook.log` the first time it runs with the new
109
+ > user-config path. Re-toggle via `/plugin reconfigure claude-prospector` to
110
+ > move to the managed setting. The old `config.json` file is not deleted.
111
+
112
+ ### State storage
113
+
114
+ When running as a plugin, state (dashboard HTML, hook log, skill-tracking JSONL files) is stored under the `${CLAUDE_PLUGIN_DATA}` directory — the Anthropic-documented persistent state location that survives plugin updates.
115
+
116
+ 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.
117
+
118
+ For testing or non-plugin use, set `CLAUDE_PROSPECTOR_BASE_DIR` to override the location entirely (takes priority over `${CLAUDE_PLUGIN_DATA}`).
119
+
120
+ ## Development / Standalone CLI
121
+
122
+ For working on the package directly, or running it without the Claude Code plugin.
123
+
124
+ ### Install for development
125
+
126
+ ```bash
127
+ git clone https://github.com/cbeaulieu-gt/claude-prospector.git
128
+ cd claude-prospector
129
+ uv pip install -e ".[dev]" # installs runtime + ruff + pytest
130
+ ```
131
+
132
+ Requires Python 3.10+.
133
+
134
+ ### Run the CLI directly
135
+
136
+ ```bash
137
+ # Default: last 7 days, opens in browser
138
+ python -m claude_prospector
139
+
140
+ # Rolling window matching billing buckets
141
+ python -m claude_prospector --window 5h
142
+ python -m claude_prospector --window 7d
143
+
144
+ # Custom date range
145
+ python -m claude_prospector --from 2026-04-01 --to 2026-04-09
146
+
147
+ # Output to file instead of opening browser
148
+ python -m claude_prospector --output report.html --no-open
149
+
150
+ # Custom Claude data directory
151
+ python -m claude_prospector --data-dir "D:\other\.claude"
152
+
153
+ # Set budget limits for gauge percentages
154
+ python -m claude_prospector --limit-5h 600000 --limit-7d 4000000 --limit-sonnet-7d 2000000
155
+ ```
156
+
157
+ ### Subcommands
158
+
159
+ All functionality is accessed through named subcommands. Bare `claude-prospector` prints help and exits 0.
160
+
161
+ #### `dashboard` — interactive HTML dashboard
162
+
163
+ ```bash
164
+ # Default: last 7 days, opens in browser
165
+ claude-prospector dashboard
166
+
167
+ # Rolling window matching Claude billing buckets
168
+ claude-prospector dashboard --window 5h
169
+ claude-prospector dashboard --window 7d
170
+
171
+ # Custom date range
172
+ claude-prospector dashboard --from 2026-04-01 --to 2026-04-09
173
+
174
+ # Output to file instead of opening browser
175
+ claude-prospector dashboard --output report.html --no-open
176
+
177
+ # Custom Claude data directory
178
+ claude-prospector dashboard --data-dir "D:\other\.claude"
179
+
180
+ # Set budget limits for gauge percentages
181
+ claude-prospector dashboard --limit-5h 600000 --limit-7d 4000000 \
182
+ --limit-sonnet-7d 2000000
183
+
184
+ # Emit JSON (for scripting / CI)
185
+ claude-prospector dashboard --format json
186
+ ```
187
+
188
+ All flags are unchanged from the pre-refactor form — only their location
189
+ moved (now under the `dashboard` subparser).
190
+
191
+ #### `session-summary` — deterministic session recap (new in v0.2.0)
192
+
193
+ Reads a single Claude Code transcript JSONL file and emits a structured
194
+ JSON summary suitable for consumption by the `/whats-next` skill or any
195
+ other tool that needs to know what a session did.
196
+
197
+ ```bash
198
+ claude-prospector session-summary --path ~/.claude/projects/<hash>/<session>.jsonl
199
+ ```
200
+
201
+ **Flags:**
202
+
203
+ | Flag | Default | Description |
204
+ |---|---|---|
205
+ | `--path PATH` | *(required)* | Path to the transcript JSONL file |
206
+ | `--format {json,text}` | `json` | Output format. `json` is the machine-readable contract; `text` is a human-readable debug view |
207
+ | `--max-actions N` | `50` | Cap on emitted actions. `0` disables the cap |
208
+
209
+ **Sample output (`--format json`):**
210
+
211
+ ```json
212
+ {
213
+ "project": "claude-prospector",
214
+ "intent": "Implement the session-summary subcommand for the /whats-next skill",
215
+ "actions": [
216
+ "Edited claude_prospector/cli/session_summary.py",
217
+ "Created tests/test_session_summary.py",
218
+ "Ran pytest tests/test_session_summary.py -x",
219
+ "Dispatched code-reviewer sub-agent"
220
+ ],
221
+ "stoppedNaturally": true
222
+ }
223
+ ```
224
+
225
+ **Exit codes:**
226
+
227
+ | Code | Meaning | stderr |
228
+ |---|---|---|
229
+ | `0` | Success — JSON written to stdout | *(silent)* |
230
+ | `1` | IO failure reading `--path` (file missing, permission denied, etc.) | `session-summary: cannot read transcript at '<path>': <OSError class>: <message>` |
231
+ | `2` | File readable but contains no external user turns (empty session, zero-byte file, whitespace-only file) | `session-summary: transcript '<path>' contains no user turns` |
232
+ | `3` | File has content but none of it parses as JSONL | `session-summary: transcript '<path>' is not valid JSONL` |
233
+
234
+ On any non-zero exit, stdout is empty and stderr contains exactly one line.
235
+
236
+ #### Migration note
237
+
238
+ The old flag-only form **no longer works** after v0.2.0:
239
+
240
+ ```bash
241
+ # REMOVED — will print help and exit 0, not run the dashboard
242
+ claude-prospector --format json
243
+
244
+ # CORRECT — migrate all callers to:
245
+ claude-prospector dashboard --format json
246
+ ```
247
+
248
+ Any script, skill, or CI step that invokes `claude-prospector` with bare flags
249
+ (no subcommand) must be updated to use `claude-prospector dashboard [flags]`.
250
+
251
+ ### Testing
252
+
253
+ ```bash
254
+ pytest # ~151 tests, typically finishes in under 5 seconds
255
+ ```
256
+
257
+ ### Linting & formatting
258
+
259
+ ```bash
260
+ ruff check . # lint
261
+ ruff format . # autoformat in-place
262
+ ruff format --check . # format gate (used in CI — exits non-zero on drift)
263
+ ```
264
+
265
+ ### CI
266
+
267
+ GitHub Actions runs on every PR and push to `main`:
268
+
269
+ - **lint** (Ubuntu): `ruff check .` + `ruff format --check .`
270
+ - **test** (Ubuntu + Windows, Python 3.10): `pytest`
271
+
272
+ Both jobs must be green before a PR can merge.
273
+
274
+ ## Nested agent attribution
275
+
276
+ When Claude Code sessions dispatch sub-agents that themselves dispatch further
277
+ sub-agents, `claude-prospector` traces the full depth and attributes tokens to the
278
+ complete root-to-leaf chain rather than just the immediate leaf.
279
+
280
+ - **Data model.** Each `MessageRecord` carries an `agent_path: tuple[str, ...]`
281
+ field (e.g. `("general-purpose", "project-planner", "Explore")`) and a
282
+ parallel `agent_type: str` stored field. Both are populated together at parse
283
+ time; the parser enforces the invariant `agent_type == agent_path[-1]` (when
284
+ `agent_path` is non-empty). `agent_type` is not a computed property — it is a
285
+ plain dataclass field, so consumers that construct `MessageRecord` with an
286
+ explicit `agent_type=` argument continue to work without change. The two
287
+ fields are kept in sync by the parser, not by the dataclass itself.
288
+
289
+ - **`by_agent` keys.** The aggregator's `by_agent` dict is keyed by the full
290
+ path joined with U+2192 (`→`), for example
291
+ `"general-purpose→project-planner→Explore"`. Depth-1 sessions produce
292
+ single-segment keys identical to the pre-change shape, so existing
293
+ integrations are unaffected.
294
+
295
+ - **Per-session `agents` list.** Each session's `agents` list contains only
296
+ the deepest-leaf path per chain (e.g. a depth-3 chain
297
+ `general-purpose → project-planner → Explore` contributes one entry,
298
+ `"general-purpose→project-planner→Explore"`). Sibling chains that share a
299
+ leaf name but differ in their ancestor are both kept — neither is a prefix
300
+ of the other. This rule preserves the dashboard JS's per-agent token
301
+ apportionment, which divides session totals by `s.agents.length`.
302
+
303
+ - **Depth limit.** Path tuples may contain up to **10 segments** total —
304
+ the root agent plus up to 9 levels of nested sub-agents
305
+ (`_MAX_AGENT_PATH_LENGTH = 10`). Beyond that, the parser emits a single
306
+ `UserWarning` and stops descending; deeper messages are bucketed under the
307
+ last walked ancestor. The warning fires at most once per session parse (not
308
+ once per overflowing message), as do the cycle and OSError warnings below.
309
+ On Windows, junction-based cycles are caught by this same cap rather than
310
+ by the POSIX visited-set short-circuit.
311
+
312
+ - **Sanitization.** A literal `→` appearing inside an agent name is replaced
313
+ with `﹖` (U+FE56) at parse time and a `UserWarning` fires. The sanitized
314
+ name is used throughout (parse, aggregation, dashboard key) so attribution
315
+ data is preserved even when the invariant is violated.
316
+
317
+ - **Deferred.** Dashboard tree visualization (sunburst, indented tree,
318
+ expand/collapse) is out of scope for this release. The existing flat agent
319
+ list in the dashboard JS receives path-keyed entries but no hierarchical
320
+ rendering yet.
321
+
322
+ ## Dashboard
323
+
324
+ The generated HTML dashboard includes:
325
+
326
+ - **Budget gauges** - estimated usage against each billing bucket (5h, 7d, Sonnet-only 7d)
327
+ - **Model breakdown** - donut chart and daily stacked bar chart (Opus/Sonnet/Haiku)
328
+ - **Agent breakdown** - token usage per agent with model attribution
329
+ - **Skill usage** - invocation counts per skill
330
+ - **Project breakdown** - tokens per project
331
+ - **Session drill-down** - click a day to see individual sessions with agents, tokens, and model split
332
+
333
+ ## How It Works
334
+
335
+ Reads JSONL session files from `~/.claude/projects/`. Each session file contains timestamped assistant messages with model name and token usage. Subagent metadata (`.meta.json`) maps child agent tokens to their agent type. Skill invocations are extracted from `Skill` tool-use entries.
@@ -0,0 +1,10 @@
1
+ """Claude Prospector — Claude Code usage analytics dashboard."""
2
+
3
+ from __future__ import annotations
4
+
5
+ try:
6
+ import importlib.metadata as _meta
7
+
8
+ __version__: str = _meta.version("claude-prospector")
9
+ except Exception:
10
+ __version__ = "0.0.0+local"
@@ -0,0 +1,53 @@
1
+ """CLI entry point for claude-prospector — subparser dispatcher."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ import claude_prospector
8
+ from claude_prospector.cli import config, dashboard, session_summary
9
+
10
+
11
+ def main() -> None:
12
+ """Parse top-level subcommand and dispatch to the appropriate runner."""
13
+ import argparse
14
+
15
+ parser = argparse.ArgumentParser(
16
+ prog="claude-prospector",
17
+ description=(
18
+ "Claude Code token usage tools. "
19
+ "Run 'claude-prospector <subcommand> --help' for details."
20
+ ),
21
+ )
22
+ parser.add_argument(
23
+ "--version",
24
+ action="version",
25
+ version=f"claude-prospector {claude_prospector.__version__}",
26
+ )
27
+ subparsers = parser.add_subparsers(
28
+ dest="subcommand",
29
+ metavar="subcommand",
30
+ )
31
+
32
+ dashboard.build_parser(subparsers)
33
+ session_summary.build_parser(subparsers)
34
+ config.build_parser(subparsers)
35
+
36
+ args = parser.parse_args()
37
+
38
+ if args.subcommand is None:
39
+ parser.print_help()
40
+ sys.exit(0)
41
+
42
+ if args.subcommand == "dashboard":
43
+ sys.exit(dashboard.run(args))
44
+
45
+ if args.subcommand == "session-summary":
46
+ sys.exit(session_summary.run(args))
47
+
48
+ if args.subcommand == "config":
49
+ sys.exit(config.run(args))
50
+
51
+
52
+ if __name__ == "__main__":
53
+ main()