ckforensics 0.2.1 → 0.2.3
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.
- package/.claude/skills/ckforensics/SKILL.md +206 -0
- package/.claude/skills/ckforensics/references/commands.md +108 -0
- package/.claude/skills/ckforensics/references/interpretation.md +81 -0
- package/.claude/skills/ckforensics/references/workflow-eod.md +56 -0
- package/.claude/skills/ckforensics/references/workflow-pre-commit.md +63 -0
- package/.claude/skills/ckforensics/references/workflow-weekly-retro.md +72 -0
- package/.claude/skills/ckforensics/scripts/session-overview.py +165 -0
- package/README.md +182 -76
- package/package.json +3 -1
- package/scripts/install-claudekit-skill.js +74 -0
- package/scripts/postinstall.js +16 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ck:forensics
|
|
3
|
+
description: "Forensic analysis of Claude Code sessions — token cost, context map, audit manifest, skill recommendations. Use when user asks 'what did Claude touch', 'where did my tokens go', 'how much did this session cost', 'show last session', 'what skill should I have used', or wants post-hoc review of CC work."
|
|
4
|
+
category: dev-tools
|
|
5
|
+
keywords: [claude-code, audit, forensics, observability, token-cost, context, skill-recommender, session-review]
|
|
6
|
+
argument-hint: "summary|sessions|audit|map|suggest|skills|ingest [args]"
|
|
7
|
+
metadata:
|
|
8
|
+
author: phong28zk
|
|
9
|
+
version: "0.2.1"
|
|
10
|
+
upstream: "https://github.com/phong28zk/ckforensics"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# ck:forensics — Claude Code Session Forensics
|
|
14
|
+
|
|
15
|
+
Read-only post-hoc analysis of `~/.claude/projects/**/*.jsonl` transcripts. Surfaces:
|
|
16
|
+
- **What was burned:** tokens, $, time, by tool/skill/file
|
|
17
|
+
- **What changed:** files touched, diff, reasoning trail, subagent attribution
|
|
18
|
+
- **What lives in context:** heatmap of items by category (tool_result, message, ...)
|
|
19
|
+
- **What you missed:** skills that would have replaced repeated manual sequences
|
|
20
|
+
|
|
21
|
+
Powered by the `ckforensics` CLI (npm `ckforensics@^0.2.1`). Local-only, no telemetry.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Default (No Arguments)
|
|
26
|
+
|
|
27
|
+
If invoked without arguments, use `AskUserQuestion` to present operations:
|
|
28
|
+
|
|
29
|
+
| Operation | Description |
|
|
30
|
+
|-----------|-------------|
|
|
31
|
+
| `summary` | Token + cost rollup (last 7d default) |
|
|
32
|
+
| `sessions` | List recent sessions with cost/duration |
|
|
33
|
+
| `audit` | Per-session change manifest (files, diff, reasoning) |
|
|
34
|
+
| `map` | Context-window heatmap (what eats tokens?) |
|
|
35
|
+
| `suggest` | Skills that would have helped (pattern detection) |
|
|
36
|
+
| `skills` | List/search indexed skills with usage stats |
|
|
37
|
+
| `ingest` | Refresh DB from `~/.claude/projects/` |
|
|
38
|
+
|
|
39
|
+
Present via `AskUserQuestion` with header "Forensics" and question "Which analysis?".
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Installation Check
|
|
44
|
+
|
|
45
|
+
Before running any command, ensure CLI is installed:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
command -v ckforensics >/dev/null || {
|
|
49
|
+
echo "ckforensics not installed. Install with: npm i -g ckforensics"
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If missing, ask user to install before proceeding.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Workflows
|
|
59
|
+
|
|
60
|
+
| Workflow | Reference |
|
|
61
|
+
|----------|-----------|
|
|
62
|
+
| End-of-day session review | `references/workflow-eod.md` |
|
|
63
|
+
| Pre-commit audit | `references/workflow-pre-commit.md` |
|
|
64
|
+
| Weekly cost retro | `references/workflow-weekly-retro.md` |
|
|
65
|
+
| Command reference | `references/commands.md` |
|
|
66
|
+
| Output interpretation | `references/interpretation.md` |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Core Commands
|
|
71
|
+
|
|
72
|
+
### `summary [--days N]`
|
|
73
|
+
|
|
74
|
+
Token/cost rollup. Default 7 days.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ckforensics summary # last 7 days
|
|
78
|
+
ckforensics summary --days 1 # today
|
|
79
|
+
ckforensics summary --days 30 # this month
|
|
80
|
+
ckforensics summary --json # for piping to jq
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Cost interpretation:** API-rate equivalent. Subscription users (Pro/Max) pay flat fee — treat as "value extracted", not actual bill. See `references/interpretation.md`.
|
|
84
|
+
|
|
85
|
+
### `audit [--last|<session-id>] [--out FILE]`
|
|
86
|
+
|
|
87
|
+
Per-session change manifest:
|
|
88
|
+
- Header (model, duration, cost, total events)
|
|
89
|
+
- Files changed (with chronological edits + reasoning quotes)
|
|
90
|
+
- Subagent dispatches + recursive cost breakdown
|
|
91
|
+
- Reasoning correlator: each tool call linked to preceding assistant text
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ckforensics audit --last # most recent session
|
|
95
|
+
ckforensics audit <session-id> --out review.md # for PR review
|
|
96
|
+
ckforensics audit --last --format json | jq # script-friendly
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `map [--last|<session-id>] [--top N]`
|
|
100
|
+
|
|
101
|
+
Context-window heatmap. Shows what categories eat the most tokens:
|
|
102
|
+
- `tool:Bash` → shell output
|
|
103
|
+
- `tool:Read` / `tool:Edit` → file ops
|
|
104
|
+
- `assistant` / `user` → messages
|
|
105
|
+
- `unknown` → unattributable
|
|
106
|
+
|
|
107
|
+
Use `--simulate-compact` to project what survives next auto-compact.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
ckforensics map --last --top 20
|
|
111
|
+
ckforensics map --simulate-compact --target-pct 50
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `suggest [--session ID|--last|--days N] [--min-confidence 70]`
|
|
115
|
+
|
|
116
|
+
Skill recommendations: detects repeated tool patterns and matches against `~/.claude/skills/` catalog. Suggests skills that would have replaced manual work.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
ckforensics suggest --last # for most recent session
|
|
120
|
+
ckforensics suggest --days 7 --min-confidence 30 # weekly retro
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `sessions [--project SLUG] [--limit N]`
|
|
124
|
+
|
|
125
|
+
List recent sessions with cost/duration. Filter by project.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
ckforensics sessions --limit 10
|
|
129
|
+
ckforensics sessions --project ckforensics --limit 50
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `skills [--unused|--used] [--search QUERY] [--refresh]`
|
|
133
|
+
|
|
134
|
+
List indexed skills. `--unused` shows skills you have but never invoked. Discoverability tool.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
ckforensics skills --refresh # re-scan ~/.claude/skills/
|
|
138
|
+
ckforensics skills --unused | head # what am I missing?
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `ingest`
|
|
142
|
+
|
|
143
|
+
Refresh DB from `~/.claude/projects/`. Incremental — only new jsonl content.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
ckforensics ingest # one-shot
|
|
147
|
+
ckforensics ingest --watch # poll every 5s
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## When to Use This Skill
|
|
153
|
+
|
|
154
|
+
**After CC session:** Run `audit --last` to review what Claude changed.
|
|
155
|
+
|
|
156
|
+
**Cost review:** Run `summary` to see token burn vs subscription value.
|
|
157
|
+
|
|
158
|
+
**Habit improvement:** Run `suggest --days 7` weekly to find skill opportunities.
|
|
159
|
+
|
|
160
|
+
**Context optimization:** Run `map --last` when session hits compaction wall — see what eats tokens.
|
|
161
|
+
|
|
162
|
+
**Pre-commit:** Run `audit --last --out review.md` then `redact review.md --in-place` before sharing.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Output Interpretation
|
|
167
|
+
|
|
168
|
+
| Pattern | Meaning |
|
|
169
|
+
|---------|---------|
|
|
170
|
+
| `tool:Bash` >40% of map | Heavy shell output; consider piping to `tail`/`head` |
|
|
171
|
+
| `tool:Read` repeats on same dir | `/ck:scout` candidate |
|
|
172
|
+
| Subagent cost > main cost | Runaway subagent — review prompt scope |
|
|
173
|
+
| Cost in red (>$10) | High-value session; worth saving manifest for retro |
|
|
174
|
+
| Suggest confidence <50% | Probably noise; only act on ≥70% |
|
|
175
|
+
|
|
176
|
+
See `references/interpretation.md` for full guide.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Important Notes
|
|
181
|
+
|
|
182
|
+
- **All operations are read-only.** Never modifies `~/.claude/projects/`.
|
|
183
|
+
- **Cost is API-rate estimate** (Nov 2025 Anthropic pricing); subscription users see flat fee.
|
|
184
|
+
- **±20% attribution margin** on context map (heuristic-based).
|
|
185
|
+
- **Cache local at** `~/.local/share/ckforensics/store.db` (mode 0600).
|
|
186
|
+
- **Logs at** `~/.local/state/ckforensics/logs/` (Linux), `~/Library/Logs/ckforensics/` (macOS).
|
|
187
|
+
|
|
188
|
+
## Token Efficiency
|
|
189
|
+
|
|
190
|
+
- Activate `ck:context-engineering` skill if running `audit` or `map` on very long sessions (output can be 100k+ tokens of markdown — pipe to file).
|
|
191
|
+
- Use `--json` for downstream tools; avoids markdown formatting overhead.
|
|
192
|
+
- For routine ingest, cron handles it: `0 * * * * ckforensics ingest >> ~/.local/state/ckforensics/logs/ingest.log 2>&1`.
|
|
193
|
+
|
|
194
|
+
**Sacrifice grammar for concision when reporting findings to user.**
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Quick Start (User's First Run)
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm i -g ckforensics # install
|
|
202
|
+
ckforensics doctor # verify
|
|
203
|
+
ckforensics ingest # populate DB (~20s for 1000+ jsonl)
|
|
204
|
+
ckforensics summary # see weekly totals
|
|
205
|
+
ckforensics audit --last | head -50 # review last session
|
|
206
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# ck:forensics — Command Reference
|
|
2
|
+
|
|
3
|
+
Quick reference for every `ckforensics` subcommand exposed via `/ck:forensics`.
|
|
4
|
+
|
|
5
|
+
## Global flags
|
|
6
|
+
|
|
7
|
+
| Flag | Default | Description |
|
|
8
|
+
|------|---------|-------------|
|
|
9
|
+
| `--db <path>` | XDG data dir | Override SQLite path |
|
|
10
|
+
| `--no-color` | off | Disable ANSI |
|
|
11
|
+
| `-v, --verbose` | off | Info-level logging to stderr |
|
|
12
|
+
| `--debug` | off | Debug-level logging |
|
|
13
|
+
| `--log-dir <path>` | XDG state dir | Override log dir |
|
|
14
|
+
| `--json` | off | Force JSON output globally |
|
|
15
|
+
|
|
16
|
+
## Subcommands
|
|
17
|
+
|
|
18
|
+
### `summary [--days N] [--format text|md|json]`
|
|
19
|
+
|
|
20
|
+
Aggregate token/cost/files across a rolling window.
|
|
21
|
+
|
|
22
|
+
**Output fields:**
|
|
23
|
+
- `sessionCount`
|
|
24
|
+
- `totalInputTokens`, `totalOutputTokens`, `totalCacheRead`, `totalCacheCreate`
|
|
25
|
+
- `estimatedCostUsd` (API-rate equivalent)
|
|
26
|
+
- `filesTouched`, `editOps`
|
|
27
|
+
- `oldestSession`, `newestSession`
|
|
28
|
+
|
|
29
|
+
### `sessions [--project SLUG] [--limit N] [--format text|md|csv|json]`
|
|
30
|
+
|
|
31
|
+
List recent sessions ordered by `started_at DESC`.
|
|
32
|
+
|
|
33
|
+
**Columns:** ID, Project, Started, Model, Events, Cost, Duration
|
|
34
|
+
|
|
35
|
+
Cost color-coded: red >$10, yellow $1-10, green <$1, dim — for null.
|
|
36
|
+
|
|
37
|
+
### `audit [<session-id>|--last] [--out FILE] [--format md|json]`
|
|
38
|
+
|
|
39
|
+
Per-session change manifest.
|
|
40
|
+
|
|
41
|
+
**Sections:**
|
|
42
|
+
1. Header (session ID, project, model, duration, cost, total events)
|
|
43
|
+
2. Summary (files changed, lines +/-, edit ops, tokens, cache read)
|
|
44
|
+
3. File Changes (chronological edits, reasoning quotes, unified diff)
|
|
45
|
+
4. Subagent Dispatches (each Agent() / Task() with input + duration)
|
|
46
|
+
5. Subagent Cost Breakdown (nested table with tokens + cost per agent)
|
|
47
|
+
|
|
48
|
+
Exit codes: 0 success, 3 session not found.
|
|
49
|
+
|
|
50
|
+
### `map [<session-id>|--last] [--top N=20] [--simulate-compact] [--target-pct N=50]`
|
|
51
|
+
|
|
52
|
+
Context window heatmap by category.
|
|
53
|
+
|
|
54
|
+
**Categories:** `tool:<Name>`, `assistant`, `user`, `system_prompt`, `memory`, `skill_load`, `unknown`
|
|
55
|
+
|
|
56
|
+
Each row: ASCII bar + percentage + token count + item count.
|
|
57
|
+
|
|
58
|
+
**`--simulate-compact`:** projects which items survive next auto-compact based on documented heuristic.
|
|
59
|
+
|
|
60
|
+
**Sub-commands:**
|
|
61
|
+
- `ckforensics map --save NAME` — persist snapshot
|
|
62
|
+
- `ckforensics map list` — list saved snapshots
|
|
63
|
+
- `ckforensics map diff <A> <B>` — token movement between snapshots
|
|
64
|
+
- `ckforensics map --pin ITEM_ID --emit-manifest [FILE]` — pinned items → paste-ready markdown
|
|
65
|
+
|
|
66
|
+
### `suggest [<session-id>|--last|--days N=7] [--min-confidence N=70] [--top N=3] [--format text|md|json]`
|
|
67
|
+
|
|
68
|
+
Detect repeated tool patterns and recommend skills.
|
|
69
|
+
|
|
70
|
+
**Patterns detected:**
|
|
71
|
+
- `read-fanout`: ≥5 Reads within 3 adjacent turns on same dir
|
|
72
|
+
- `test-loop`: ≥3 Bash test runs
|
|
73
|
+
- `manual-diff-cycle`: read→edit→read ≥2 cycles
|
|
74
|
+
- `grep-walk`: ≥4 Grep+Read pairs
|
|
75
|
+
- `subagent-skip`: ≥20 main-thread tool calls, no Agent/Task
|
|
76
|
+
|
|
77
|
+
**Output per rec:** invocation hint (`/ck:scout`), confidence %, est savings (tokens + USD), evidence turns.
|
|
78
|
+
|
|
79
|
+
### `skills [--unused|--used] [--search QUERY] [--refresh] [--format text|md|csv|json]`
|
|
80
|
+
|
|
81
|
+
List indexed skills from `~/.claude/skills/`.
|
|
82
|
+
|
|
83
|
+
- `--refresh` — re-scan SKILL.md frontmatter
|
|
84
|
+
- `--unused` — show skills you've never invoked
|
|
85
|
+
- `--used` — show skills you've activated, with usage count
|
|
86
|
+
- `--search "pattern"` — keyword search
|
|
87
|
+
|
|
88
|
+
### `ingest [--watch]`
|
|
89
|
+
|
|
90
|
+
Parse `~/.claude/projects/**/*.jsonl` → SQLite. Idempotent + incremental.
|
|
91
|
+
|
|
92
|
+
- `--watch` polls every 5s (for cron alternative)
|
|
93
|
+
|
|
94
|
+
### `redact <file> [--in-place] [--force]`
|
|
95
|
+
|
|
96
|
+
Apply 9 redaction rules (sk-ant, GH tokens, AWS, JWT, etc.) to a markdown/text file. Use before sharing audit exports.
|
|
97
|
+
|
|
98
|
+
### `export <view> --format md|json|csv [--out FILE]`
|
|
99
|
+
|
|
100
|
+
Pipe-friendly export: `summary`, `sessions`, `audit`.
|
|
101
|
+
|
|
102
|
+
### `doctor`
|
|
103
|
+
|
|
104
|
+
Health check: DB exists + version, JSONL dir readable, last ingest mtime, log dir writeable, Bun version.
|
|
105
|
+
|
|
106
|
+
### `path`
|
|
107
|
+
|
|
108
|
+
Print resolved paths (DB, data dir, JSONL source, log dir, config dir).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# ck:forensics — Output Interpretation Guide
|
|
2
|
+
|
|
3
|
+
How to read what ckforensics tells you, and when to act on it.
|
|
4
|
+
|
|
5
|
+
## Cost interpretation
|
|
6
|
+
|
|
7
|
+
| Display | Meaning |
|
|
8
|
+
|---------|---------|
|
|
9
|
+
| `$X.XX` (green) | <$1 — typical small session |
|
|
10
|
+
| `$X.XX` (yellow) | $1-10 — substantive work |
|
|
11
|
+
| `$X.XX` (red) | >$10 — high-value session, worth saving manifest |
|
|
12
|
+
| `unknown` / `—` | Cost not estimable (missing model in DB, pre-pricing-fix) |
|
|
13
|
+
| `*` footnote | API-rate equivalent, not actual subscription bill |
|
|
14
|
+
|
|
15
|
+
**Two cost numbers compared:**
|
|
16
|
+
- ckforensics `Estimated Cost`: token count × Anthropic published rates
|
|
17
|
+
- CC live statusline: real-time `cost.total_cost_usd` from CC's internal calc
|
|
18
|
+
|
|
19
|
+
Discrepancy expected (CC may exclude some cache pricing). Within 30% = normal.
|
|
20
|
+
|
|
21
|
+
**For subscription users:** the cost number = "value extracted from your $X/mo plan", not a bill.
|
|
22
|
+
|
|
23
|
+
## Map heatmap thresholds
|
|
24
|
+
|
|
25
|
+
| Pattern | Diagnostic | Action |
|
|
26
|
+
|---------|-----------|--------|
|
|
27
|
+
| `tool:Bash` >40% | Heavy shell output | Pipe to `head`/`tail`, redirect logs, or limit verbose flags |
|
|
28
|
+
| `tool:Read` >30% | Excessive file reads | Use `/ck:scout` for batched discovery |
|
|
29
|
+
| `tool:Edit` >30% | Lots of edits | Normal for code-heavy session; check for thrashing |
|
|
30
|
+
| `assistant` >40% | Very long replies | Verbose mode, summarisation overhead |
|
|
31
|
+
| `unknown` >10% | Attribution failure | Schema drift; report to ckforensics issues |
|
|
32
|
+
|
|
33
|
+
## Suggest confidence brackets
|
|
34
|
+
|
|
35
|
+
| Confidence | Meaning | Action |
|
|
36
|
+
|------------|---------|--------|
|
|
37
|
+
| 80-100% | Strong signal, multi-pattern match | Try the skill next session |
|
|
38
|
+
| 60-80% | Plausible, single pattern | Worth reading description |
|
|
39
|
+
| 40-60% | Weak — likely keyword coincidence | Skip unless desperate |
|
|
40
|
+
| <40% | Noise | Ignore |
|
|
41
|
+
|
|
42
|
+
Default threshold = 70. Lower with `--min-confidence 30` if seeing no recs.
|
|
43
|
+
|
|
44
|
+
## Audit manifest sections
|
|
45
|
+
|
|
46
|
+
### Files changed
|
|
47
|
+
|
|
48
|
+
Each file shows chronological edits with reasoning quote. **Read the reasoning** — it's the assistant's intent text before the tool call, not post-hoc explanation. Useful for:
|
|
49
|
+
- Understanding *why* an edit was made
|
|
50
|
+
- Catching scope creep (intent vs action mismatch)
|
|
51
|
+
- Composing PR descriptions (reasoning often is the PR body)
|
|
52
|
+
|
|
53
|
+
### Subagent Cost Breakdown
|
|
54
|
+
|
|
55
|
+
Nested table. Top-level rows = direct Agent()/Task() calls from main thread. Indented rows = nested (subagent inside subagent).
|
|
56
|
+
|
|
57
|
+
**Red flags:**
|
|
58
|
+
- Single subagent with >$5 cost in <10 tool calls = runaway
|
|
59
|
+
- "open" duration → subagent still running (session paused?)
|
|
60
|
+
- Many small subagents (≤2 calls each) = excessive fanout
|
|
61
|
+
|
|
62
|
+
### Reasoning correlator
|
|
63
|
+
|
|
64
|
+
Each tool call gets a "Reasoning:" quote. If reasoning is empty/unhelpful:
|
|
65
|
+
- Assistant didn't narrate (rare)
|
|
66
|
+
- Tool fired in middle of multi-block message (correlator picks last text block)
|
|
67
|
+
- Subagent invocation (reasoning belongs to parent dispatch)
|
|
68
|
+
|
|
69
|
+
## Sessions list signals
|
|
70
|
+
|
|
71
|
+
- Same project, multiple short sessions same day → context flushing too often
|
|
72
|
+
- Single 10+ hour session → consider checkpointing via context map
|
|
73
|
+
- Cost trending up week-over-week → instrument with `summary --days N`
|
|
74
|
+
|
|
75
|
+
## When ingest finds nothing
|
|
76
|
+
|
|
77
|
+
If `ckforensics ingest` reports 0 new events:
|
|
78
|
+
1. Check `ckforensics doctor` — last ingest mtime
|
|
79
|
+
2. Verify `~/.claude/projects/` has recent jsonl: `ls -lt ~/.claude/projects/*/*.jsonl | head`
|
|
80
|
+
3. CC may be on different config dir (CLAUDE_CONFIG_DIR env var) — set `--db` accordingly
|
|
81
|
+
4. Force re-ingest from scratch: `rm ~/.local/share/ckforensics/store.db && ckforensics ingest`
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# End-of-Day Session Review Workflow
|
|
2
|
+
|
|
3
|
+
Pattern: after a substantial Claude Code session, run forensics to know what shipped, what burned, what to remember.
|
|
4
|
+
|
|
5
|
+
## Flow
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
1. ckforensics ingest # refresh DB
|
|
9
|
+
2. ckforensics summary --days 1 # today's totals
|
|
10
|
+
3. ckforensics audit --last --out eod.md # capture manifest
|
|
11
|
+
4. ckforensics suggest --last # skills that would have helped
|
|
12
|
+
5. ckforensics map --last --top 15 # what ate the context
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## When Claude runs this skill
|
|
16
|
+
|
|
17
|
+
User says one of:
|
|
18
|
+
- "wrap up this session"
|
|
19
|
+
- "what did I do today"
|
|
20
|
+
- "end of day review"
|
|
21
|
+
- "summary of last session"
|
|
22
|
+
- "did Claude do anything weird"
|
|
23
|
+
|
|
24
|
+
## What Claude should do
|
|
25
|
+
|
|
26
|
+
1. Run the 5 commands above (or relevant subset).
|
|
27
|
+
2. **Summarise**, don't paste raw output to user. Surface:
|
|
28
|
+
- Total cost (API-rate) + duration
|
|
29
|
+
- Top 3 files changed (by lines or by importance from reasoning quotes)
|
|
30
|
+
- Any subagent that cost >$1 (flag for review)
|
|
31
|
+
- Top 1-2 skill recommendations with confidence ≥70
|
|
32
|
+
- Context map insight: what ate most tokens
|
|
33
|
+
3. **Offer next actions:**
|
|
34
|
+
- "Save audit manifest as PR description?" → use `eod.md`
|
|
35
|
+
- "Dismiss low-value skill recs?" → `~/.config/ckforensics/dismissed.json`
|
|
36
|
+
- "Schedule cron for daily ingest?" → crontab snippet
|
|
37
|
+
|
|
38
|
+
## Example response template
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Today's session burned $X.XX over Y hours. Modified Z files (top: A, B, C).
|
|
42
|
+
|
|
43
|
+
Notable:
|
|
44
|
+
- Subagent "P11 log infra" cost $28 — runaway, worth reviewing
|
|
45
|
+
- Skill /ck:scout would have replaced 23× Read on src/audit/* (saves ~12k tokens)
|
|
46
|
+
- Context map: tool:Bash 40%, tool:Edit 16% — heavy shell + editing
|
|
47
|
+
|
|
48
|
+
Manifest saved to eod.md. Want me to draft a commit message from it?
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Anti-patterns
|
|
52
|
+
|
|
53
|
+
- ❌ Paste full audit markdown (1000+ lines) to user
|
|
54
|
+
- ❌ Run all 5 commands when user only asked "cost today"
|
|
55
|
+
- ❌ Recommend skills with confidence <70 without flagging confidence
|
|
56
|
+
- ❌ Skip subagent cost breakdown — that's the highest-signal section
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Pre-Commit Audit Workflow
|
|
2
|
+
|
|
3
|
+
Pattern: before user runs `git commit`, generate a manifest of what Claude touched and use it as commit/PR body draft.
|
|
4
|
+
|
|
5
|
+
## Flow
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
1. ckforensics ingest # ensure DB current
|
|
9
|
+
2. ckforensics audit --last --out review.md # capture this session's manifest
|
|
10
|
+
3. ckforensics redact review.md --in-place # strip secrets before sharing
|
|
11
|
+
4. (user opens review.md, copies relevant sections to commit body)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## When Claude runs this skill
|
|
15
|
+
|
|
16
|
+
User says one of:
|
|
17
|
+
- "ready to commit"
|
|
18
|
+
- "draft a commit message from this session"
|
|
19
|
+
- "what should the PR description say"
|
|
20
|
+
- "review what I just did before committing"
|
|
21
|
+
|
|
22
|
+
## What Claude should do
|
|
23
|
+
|
|
24
|
+
1. Run `audit --last` to get the manifest.
|
|
25
|
+
2. Pipe through `redact` (always, never skip — secrets risk).
|
|
26
|
+
3. Extract from manifest:
|
|
27
|
+
- **Files changed** + line counts (for `git diff --stat` equivalent)
|
|
28
|
+
- **Reasoning quotes** per file (often makes the PR body)
|
|
29
|
+
- **Subagent contributions** (mention if subagents did significant work)
|
|
30
|
+
4. **Draft conventional commit**:
|
|
31
|
+
```
|
|
32
|
+
<type>(<scope>): <subject>
|
|
33
|
+
|
|
34
|
+
<body — TLDR of what changed and why, from reasoning>
|
|
35
|
+
|
|
36
|
+
<body — files affected, key decisions>
|
|
37
|
+
```
|
|
38
|
+
5. **Surface anything suspicious:**
|
|
39
|
+
- Files touched in unexpected directories
|
|
40
|
+
- Reasoning that doesn't match the actions (intent drift)
|
|
41
|
+
- Subagents with high cost (worth mentioning in PR)
|
|
42
|
+
|
|
43
|
+
## Integration with ck:git
|
|
44
|
+
|
|
45
|
+
After draft, hand off to `/ck:git cm` for execution:
|
|
46
|
+
```
|
|
47
|
+
ckforensics audit --last --out review.md
|
|
48
|
+
ckforensics redact review.md --in-place
|
|
49
|
+
# Claude reads review.md, drafts commit message
|
|
50
|
+
# Hand to /ck:git cm — user reviews + executes
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or pipe directly (v0.5 phase 12 will support this):
|
|
54
|
+
```
|
|
55
|
+
ckforensics commit --phase last | ck:git cm --message-stdin
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Anti-patterns
|
|
59
|
+
|
|
60
|
+
- ❌ Skip redaction — manifests can contain edited file contents = secrets risk
|
|
61
|
+
- ❌ Paste raw manifest as commit body (too long, unstructured)
|
|
62
|
+
- ❌ Auto-commit — always show draft, let user approve via /ck:git
|
|
63
|
+
- ❌ Forget to mention subagents — they did work that deserves credit/audit
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Weekly Retro Workflow
|
|
2
|
+
|
|
3
|
+
Pattern: every Sunday (or Monday morning), look back at the week — cost trends, skill discovery, runaway sessions.
|
|
4
|
+
|
|
5
|
+
## Flow
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
1. ckforensics ingest # ensure DB current
|
|
9
|
+
2. ckforensics summary --days 7 # weekly rollup
|
|
10
|
+
3. ckforensics sessions --limit 20 # week's sessions
|
|
11
|
+
4. ckforensics suggest --days 7 --min-confidence 50 # patterns to fix
|
|
12
|
+
5. ckforensics skills --unused | head -20 # discovery
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## When Claude runs this skill
|
|
16
|
+
|
|
17
|
+
User says one of:
|
|
18
|
+
- "weekly retro"
|
|
19
|
+
- "what did I spend on Claude Code this week"
|
|
20
|
+
- "any patterns I should fix in my workflow"
|
|
21
|
+
- "show me sessions ranked by cost"
|
|
22
|
+
- "what skills should I learn"
|
|
23
|
+
|
|
24
|
+
## What Claude should do
|
|
25
|
+
|
|
26
|
+
1. **Summary numbers first** — cost, session count, vs previous week if possible (`--days 14` then compute delta).
|
|
27
|
+
2. **Top 5 most expensive sessions** with project + duration. Flag anything over $50.
|
|
28
|
+
3. **Recurring patterns** from `suggest --days 7`:
|
|
29
|
+
- List top 3 skill recommendations
|
|
30
|
+
- Group by pattern type (e.g. "read-fanout detected 4× this week")
|
|
31
|
+
4. **Skill discovery**:
|
|
32
|
+
- From `skills --unused`, pick 3-5 that match user's project work (filter by keyword overlap with recent session topics)
|
|
33
|
+
- Format as "consider trying: /ck:xyz — <description>"
|
|
34
|
+
5. **Anomaly check**:
|
|
35
|
+
- Any single session >$100? Flag for review.
|
|
36
|
+
- Any subagent that ran >1h? Worth investigating.
|
|
37
|
+
6. **Optional: write retro to docs/**:
|
|
38
|
+
- `docs/retros/YYYY-MM-DD-week-N.md`
|
|
39
|
+
- Auto-fill summary + recommendations sections
|
|
40
|
+
- User edits notes manually after
|
|
41
|
+
|
|
42
|
+
## Example response template
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Weekly retro (Mon 2026-05-04 → Sun 2026-05-10):
|
|
46
|
+
|
|
47
|
+
📊 Totals: $733 API-equivalent across 19 sessions, 967M tokens
|
|
48
|
+
vs prev week: +180% (last week $260 / 12 sessions)
|
|
49
|
+
|
|
50
|
+
🔥 Top sessions:
|
|
51
|
+
1. project-noname 18h56m $1102 (Tue, 4082 events)
|
|
52
|
+
2. MQL5 2h21m $29 (Tue)
|
|
53
|
+
3. career-launchpad 19h $32 (Wed)
|
|
54
|
+
|
|
55
|
+
📚 Skill recommendations:
|
|
56
|
+
- /ck:scout (4× read-fanout patterns this week — 50k tok save)
|
|
57
|
+
- /ck:test (3× test-loop patterns — 15k tok save)
|
|
58
|
+
|
|
59
|
+
💡 Try these unused skills:
|
|
60
|
+
- /ck:retro — auto-generate this report (would replace this manual flow)
|
|
61
|
+
- /ck:journal — capture session reflections automatically
|
|
62
|
+
- /ck:debug — systematic debugging when stuck
|
|
63
|
+
|
|
64
|
+
⚠️ Notable: session #1 was 18h Opus — consider checkpointing context mid-way.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Anti-patterns
|
|
68
|
+
|
|
69
|
+
- ❌ Compare cost to subscription price as if it's the bill — it's API-equivalent, frame as "value extracted"
|
|
70
|
+
- ❌ Recommend ALL unused skills (150+) — filter to relevant per project context
|
|
71
|
+
- ❌ Skip anomaly check — outliers are the highest-signal items
|
|
72
|
+
- ❌ Long bullet lists — keep tight, 3-5 items per category max
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
session-overview.py — One-shot session report for ck:forensics skill.
|
|
4
|
+
|
|
5
|
+
Runs multiple `ckforensics` CLI commands and stitches a single JSON
|
|
6
|
+
report. Saves Claude from chaining 4-5 Bash calls when answering
|
|
7
|
+
"summarize this session" prompts.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python3 session-overview.py [--session-id ID] [--days N]
|
|
11
|
+
python3 session-overview.py --last # default — most recent session
|
|
12
|
+
|
|
13
|
+
Output: JSON on stdout with shape:
|
|
14
|
+
{
|
|
15
|
+
"schema": "ck-forensics-overview-v1",
|
|
16
|
+
"generatedAt": "ISO8601",
|
|
17
|
+
"scope": {"sessionId": "...", "days": 7, "lastOnly": true},
|
|
18
|
+
"summary": { ... }, # `ckforensics summary --json` data
|
|
19
|
+
"session": { ... }, # one row from `ckforensics sessions --json`
|
|
20
|
+
"audit": { ... }, # `ckforensics audit --json` (manifest)
|
|
21
|
+
"suggest": [ ... ], # top recs from `ckforensics suggest --json`
|
|
22
|
+
"map": { ... } # context map heatmap
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Errors are reported as {"schema": "error", "error": "..."} with exit 1.
|
|
26
|
+
|
|
27
|
+
Stdlib only — no third-party deps required. Compatible with Python 3.8+.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import argparse
|
|
31
|
+
import json
|
|
32
|
+
import shutil
|
|
33
|
+
import subprocess
|
|
34
|
+
import sys
|
|
35
|
+
from datetime import datetime, timezone
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_cli_installed() -> None:
|
|
39
|
+
"""Verify ckforensics CLI is on PATH; exit 1 with clear error if not."""
|
|
40
|
+
if shutil.which("ckforensics") is None:
|
|
41
|
+
emit_error(
|
|
42
|
+
"ckforensics CLI not installed. "
|
|
43
|
+
"Install with: npm i -g ckforensics"
|
|
44
|
+
)
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run_cli(args: list) -> dict:
|
|
49
|
+
"""
|
|
50
|
+
Run `ckforensics <args>` with --json and return parsed output.
|
|
51
|
+
Raises CalledProcessError on non-zero exit (caller decides whether to ignore).
|
|
52
|
+
"""
|
|
53
|
+
cmd = ["ckforensics", *args, "--json"]
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
cmd,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
check=False,
|
|
59
|
+
timeout=30,
|
|
60
|
+
)
|
|
61
|
+
if result.returncode != 0:
|
|
62
|
+
# Many subcommands exit 3 when no data — return empty rather than fail
|
|
63
|
+
if result.returncode == 3:
|
|
64
|
+
return {}
|
|
65
|
+
raise subprocess.CalledProcessError(
|
|
66
|
+
result.returncode, cmd, result.stdout, result.stderr
|
|
67
|
+
)
|
|
68
|
+
return json.loads(result.stdout) if result.stdout.strip() else {}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def emit_error(msg: str) -> None:
|
|
72
|
+
"""Write error envelope to stdout."""
|
|
73
|
+
print(json.dumps({"schema": "error", "error": msg}), flush=True)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def collect_overview(session_id: str | None, days: int, last: bool) -> dict:
|
|
77
|
+
"""Gather all sections; failures in individual sections become null."""
|
|
78
|
+
overview = {
|
|
79
|
+
"schema": "ck-forensics-overview-v1",
|
|
80
|
+
"generatedAt": datetime.now(timezone.utc).isoformat(),
|
|
81
|
+
"scope": {"sessionId": session_id, "days": days, "lastOnly": last},
|
|
82
|
+
"summary": None,
|
|
83
|
+
"session": None,
|
|
84
|
+
"audit": None,
|
|
85
|
+
"suggest": None,
|
|
86
|
+
"map": None,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# 1. Summary — rolling window
|
|
90
|
+
try:
|
|
91
|
+
overview["summary"] = run_cli(["summary", "--days", str(days)]).get("data")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
overview["summary"] = {"error": str(e)}
|
|
94
|
+
|
|
95
|
+
# 2. Session metadata — pick from list or by id
|
|
96
|
+
try:
|
|
97
|
+
sessions_envelope = run_cli(["sessions", "--limit", "1"])
|
|
98
|
+
sessions = sessions_envelope.get("data", [])
|
|
99
|
+
if session_id:
|
|
100
|
+
# Filter to specific session if requested
|
|
101
|
+
sessions = [s for s in sessions if s.get("id") == session_id]
|
|
102
|
+
overview["session"] = sessions[0] if sessions else None
|
|
103
|
+
except Exception as e:
|
|
104
|
+
overview["session"] = {"error": str(e)}
|
|
105
|
+
|
|
106
|
+
# 3. Audit manifest — uses --last unless explicit id given
|
|
107
|
+
audit_args = ["audit", "--last"] if last or not session_id else ["audit", session_id]
|
|
108
|
+
try:
|
|
109
|
+
overview["audit"] = run_cli(audit_args)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
overview["audit"] = {"error": str(e)}
|
|
112
|
+
|
|
113
|
+
# 4. Suggest — same scope as audit
|
|
114
|
+
suggest_args = ["suggest", "--last", "--min-confidence", "30", "--top", "3"]
|
|
115
|
+
if session_id and not last:
|
|
116
|
+
suggest_args = ["suggest", "--session", session_id, "--min-confidence", "30", "--top", "3"]
|
|
117
|
+
try:
|
|
118
|
+
overview["suggest"] = run_cli(suggest_args).get("data", [])
|
|
119
|
+
except Exception as e:
|
|
120
|
+
overview["suggest"] = {"error": str(e)}
|
|
121
|
+
|
|
122
|
+
# 5. Map — top-10 heatmap categories
|
|
123
|
+
map_args = ["map", "--last", "--top", "10"]
|
|
124
|
+
if session_id and not last:
|
|
125
|
+
map_args = ["map", session_id, "--top", "10"]
|
|
126
|
+
try:
|
|
127
|
+
overview["map"] = run_cli(map_args).get("data")
|
|
128
|
+
except Exception as e:
|
|
129
|
+
overview["map"] = {"error": str(e)}
|
|
130
|
+
|
|
131
|
+
return overview
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def parse_args() -> argparse.Namespace:
|
|
135
|
+
p = argparse.ArgumentParser(
|
|
136
|
+
prog="session-overview",
|
|
137
|
+
description="Unified session report — runs multiple ckforensics commands and merges output.",
|
|
138
|
+
)
|
|
139
|
+
p.add_argument("--session-id", help="Explicit session UUID (defaults to --last)")
|
|
140
|
+
p.add_argument("--days", type=int, default=7, help="Summary window in days (default 7)")
|
|
141
|
+
p.add_argument(
|
|
142
|
+
"--last",
|
|
143
|
+
action="store_true",
|
|
144
|
+
default=True,
|
|
145
|
+
help="Use most recent session (default behavior)",
|
|
146
|
+
)
|
|
147
|
+
return p.parse_args()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def main() -> int:
|
|
151
|
+
check_cli_installed()
|
|
152
|
+
args = parse_args()
|
|
153
|
+
last = args.last and not args.session_id
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
overview = collect_overview(args.session_id, args.days, last)
|
|
157
|
+
print(json.dumps(overview, indent=2, default=str))
|
|
158
|
+
return 0
|
|
159
|
+
except Exception as e:
|
|
160
|
+
emit_error(f"unexpected error: {e}")
|
|
161
|
+
return 1
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
sys.exit(main())
|
package/README.md
CHANGED
|
@@ -1,25 +1,93 @@
|
|
|
1
1
|
# ckforensics
|
|
2
2
|
|
|
3
|
-
**Forensic CLI for Claude Code sessions —
|
|
3
|
+
> 🔍 **Forensic CLI for Claude Code sessions** — see where your tokens went, what Claude touched, and which skills you missed.
|
|
4
4
|
|
|
5
5
|
[](https://github.com/phong28zk/ckforensics/actions/workflows/ci.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/ckforensics)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://bun.sh)
|
|
9
|
+
[](#claudekit-integration)
|
|
10
|
+
|
|
11
|
+
**One install. Two interfaces.** Standalone CLI **and** ClaudeKit `/ck:forensics` skill — bundled together.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -g ckforensics
|
|
15
|
+
```
|
|
9
16
|
|
|
10
17
|
---
|
|
11
18
|
|
|
12
|
-
##
|
|
19
|
+
## What does it do?
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
- Claude Code transcripts stored as raw JSONL — no way to summarize or audit them
|
|
16
|
-
- Sensitive keys / tokens leak into `.jsonl` files that get committed or shared
|
|
17
|
-
- No visibility into token usage, tool calls, or subagent orchestration patterns
|
|
21
|
+
After Claude Code finishes a session, ckforensics parses `~/.claude/projects/**/*.jsonl` and tells you:
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
```
|
|
24
|
+
╭─ ckforensics summary --days 7 ──────────────────────────────────╮
|
|
25
|
+
│ Sessions 19 │
|
|
26
|
+
│ Input tokens 69,190 │
|
|
27
|
+
│ Output tokens 3,088,275 │
|
|
28
|
+
│ Cache read 934,429,934 │
|
|
29
|
+
│ Total tokens 967,848,242 │
|
|
30
|
+
│ Estimated cost* $733.8981 ← value extracted from $100 Max │
|
|
31
|
+
│ Files touched 149 │
|
|
32
|
+
│ Edit operations 488 │
|
|
33
|
+
╰──────────────────────────────────────────────────────────────────╯
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
╭─ ckforensics map --last (where do my tokens go?) ──────────────╮
|
|
38
|
+
│ tool:Bash ████████ 40.5% 30,444 tok (293 calls) │
|
|
39
|
+
│ tool:Edit ███ 16.1% 12,396 tok (98) │
|
|
40
|
+
│ assistant ██ 11.9% 9,197 tok (563) │
|
|
41
|
+
│ tool:Read ██ 10.8% 8,327 tok (70) │
|
|
42
|
+
│ tool:Write ██ 9.3% 7,179 tok (42) │
|
|
43
|
+
│ │
|
|
44
|
+
│ Total: 77,204 tokens (±20% attribution margin) │
|
|
45
|
+
╰──────────────────────────────────────────────────────────────────╯
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
╭─ ckforensics suggest --last (what skills would have helped?) ──╮
|
|
50
|
+
│ #1 /ck:scout confidence: 85% │
|
|
51
|
+
│ You ran 23× Read on src/audit/* in turns 47-89 │
|
|
52
|
+
│ /ck:scout fans out file discovery in parallel │
|
|
53
|
+
│ Est. savings: 12k tokens ($0.14) │
|
|
54
|
+
│ │
|
|
55
|
+
│ #2 /ck:test confidence: 72% │
|
|
56
|
+
│ 8× `bun test` Bash calls in retry loop │
|
|
57
|
+
╰──────────────────────────────────────────────────────────────────╯
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
╭─ ckforensics audit --last (session change manifest) ──────────╮
|
|
62
|
+
│ ## Subagent Cost Breakdown │
|
|
63
|
+
│ │
|
|
64
|
+
│ │ Subagent │ Tokens │ Cost │ Tool calls │ Duration ││
|
|
65
|
+
│ ├────────────────────┼────────┼────────┼────────────┼──────────┤│
|
|
66
|
+
│ │ Agent: P11 log infra│ 28.9M │ $28.42 │ 20 │ open ││
|
|
67
|
+
│ │ └── nested forensics│ 28.2M │ $27.91 │ 19 │ open ││
|
|
68
|
+
│ │ Agent: P06 build │ 646k │ $0.61 │ 2 │ 3h0m ││
|
|
69
|
+
│ │ Agent: P07 ctx map │ 751k │ $0.47 │ 1 │ 12m28s ││
|
|
70
|
+
│ └────────────────────┴────────┴────────┴────────────┴──────────┘│
|
|
71
|
+
│ │
|
|
72
|
+
│ ↑ Identifies runaway subagents in long sessions │
|
|
73
|
+
╰──────────────────────────────────────────────────────────────────╯
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
\* API-rate equivalent (Anthropic Nov 2025 pricing). Subscription users (Pro/Max/Team) pay flat fee — treat as "value extracted from your plan", not actual billing.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Why does this exist?
|
|
81
|
+
|
|
82
|
+
| Pain | ckforensics |
|
|
83
|
+
|------|-------------|
|
|
84
|
+
| Claude Code stores transcripts as opaque JSONL | Parses into queryable SQLite |
|
|
85
|
+
| You can't see what subagents cost vs the parent | Recursive cost tree per Agent()/Task() |
|
|
86
|
+
| You don't know which skills you skipped using | Pattern detection → ranked recommendations |
|
|
87
|
+
| Auto-compact is opaque, drops context invisibly | `map` shows what eats your window |
|
|
88
|
+
| Sensitive keys leak into committed jsonl logs | 9 built-in redaction rules |
|
|
89
|
+
| "How much did Claude cost me this week?" | `summary` gives token + USD totals |
|
|
90
|
+
| "What did Claude actually change in that 14h session?" | `audit` produces signed-off manifest |
|
|
23
91
|
|
|
24
92
|
---
|
|
25
93
|
|
|
@@ -29,7 +97,15 @@
|
|
|
29
97
|
npm i -g ckforensics
|
|
30
98
|
```
|
|
31
99
|
|
|
32
|
-
|
|
100
|
+
Single command installs:
|
|
101
|
+
1. **Cross-platform binary** (Linux x64/arm64, macOS Apple Silicon, Windows x64) via npm postinstall download
|
|
102
|
+
2. **ClaudeKit skill** at `~/.claude/skills/ckforensics/` — auto-activates in next Claude Code session
|
|
103
|
+
|
|
104
|
+
> **Intel Mac:** build from source — `bun build --compile --target=bun-darwin-x64 src/cli/index.ts --outfile ckforensics`
|
|
105
|
+
>
|
|
106
|
+
> **Opt out of skill copy:** `CKFORENSICS_SKIP_SKILL_INSTALL=1 npm i -g ckforensics`
|
|
107
|
+
|
|
108
|
+
Or grab a binary directly:
|
|
33
109
|
|
|
34
110
|
```bash
|
|
35
111
|
# Linux x64
|
|
@@ -37,116 +113,146 @@ curl -L https://github.com/phong28zk/ckforensics/releases/latest/download/ckfore
|
|
|
37
113
|
-o /usr/local/bin/ckforensics && chmod +x /usr/local/bin/ckforensics
|
|
38
114
|
```
|
|
39
115
|
|
|
40
|
-
Platforms: `linux-x64`, `linux-arm64`, `macos-arm64` (Apple Silicon), `windows-x64`
|
|
41
|
-
|
|
42
|
-
> Intel Mac users: build from source — `bun build --compile --target=bun-darwin-x64 src/cli/index.ts --outfile ckforensics`
|
|
43
|
-
|
|
44
116
|
---
|
|
45
117
|
|
|
46
118
|
## Quick Start
|
|
47
119
|
|
|
48
120
|
```bash
|
|
49
|
-
#
|
|
50
|
-
ckforensics
|
|
121
|
+
ckforensics ingest # populate DB (~20s for 1000+ jsonl)
|
|
122
|
+
ckforensics summary # weekly totals
|
|
123
|
+
ckforensics audit --last # last session manifest
|
|
124
|
+
ckforensics map --last --top 10 # context heatmap
|
|
125
|
+
ckforensics suggest --last # skill recommendations
|
|
126
|
+
ckforensics doctor # health check
|
|
127
|
+
```
|
|
51
128
|
|
|
52
|
-
|
|
53
|
-
ckforensics summary
|
|
54
|
-
ckforensics summary --days 1 # today
|
|
55
|
-
ckforensics summary --days 30 # month
|
|
129
|
+
**Recommended cron** (hourly auto-ingest):
|
|
56
130
|
|
|
57
|
-
|
|
58
|
-
ckforensics
|
|
131
|
+
```bash
|
|
132
|
+
(crontab -l 2>/dev/null; echo "0 * * * * $(which ckforensics) ingest >> ~/.local/state/ckforensics/logs/ingest.log 2>&1") | crontab -
|
|
133
|
+
```
|
|
59
134
|
|
|
60
|
-
|
|
61
|
-
ckforensics audit --last
|
|
62
|
-
ckforensics audit <session-id> --out review.md
|
|
135
|
+
---
|
|
63
136
|
|
|
64
|
-
|
|
65
|
-
ckforensics redact review.md --in-place
|
|
137
|
+
## ClaudeKit integration
|
|
66
138
|
|
|
67
|
-
|
|
68
|
-
ckforensics export summary --format json | jq
|
|
69
|
-
ckforensics export sessions --format csv --out sessions.csv
|
|
139
|
+
When installed, `/ck:forensics` is available inside Claude Code:
|
|
70
140
|
|
|
71
|
-
|
|
72
|
-
|
|
141
|
+
```
|
|
142
|
+
You: "show me last session cost"
|
|
143
|
+
Claude: [auto-invokes /ck:forensics → reads SKILL.md → runs ckforensics audit --last]
|
|
144
|
+
Claude: "Last session was 18h, $1102 API-equivalent. Top files: ..."
|
|
73
145
|
```
|
|
74
146
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
147
|
+
**Triggers** (skill description picks these up):
|
|
148
|
+
- "what did Claude touch this session"
|
|
149
|
+
- "show me last session cost"
|
|
150
|
+
- "where did my tokens go"
|
|
151
|
+
- "audit last session"
|
|
152
|
+
- "what skill should I have used"
|
|
153
|
+
- `/ck:forensics summary` / `/ck:forensics audit` / etc
|
|
81
154
|
|
|
82
|
-
|
|
155
|
+
The skill ships with 3 workflows:
|
|
156
|
+
- **`workflow-eod.md`** — end-of-day session review
|
|
157
|
+
- **`workflow-pre-commit.md`** — generate commit message + redact before commit
|
|
158
|
+
- **`workflow-weekly-retro.md`** — Sunday retrospective
|
|
83
159
|
|
|
84
|
-
|
|
160
|
+
---
|
|
85
161
|
|
|
86
|
-
|
|
87
|
-
- **Subscription users (Pro / Max / Team):** flat plan fee covers this; treat the number as **"value extracted"** from your subscription
|
|
162
|
+
## Storage
|
|
88
163
|
|
|
89
|
-
|
|
164
|
+
DB at XDG-compliant path (mode `0600`):
|
|
90
165
|
|
|
91
|
-
|
|
166
|
+
| OS | Path |
|
|
167
|
+
|----|------|
|
|
168
|
+
| Linux | `~/.local/share/ckforensics/store.db` |
|
|
169
|
+
| macOS | `~/Library/Application Support/ckforensics/store.db` |
|
|
170
|
+
| Windows | `%APPDATA%\ckforensics\store.db` |
|
|
92
171
|
|
|
93
|
-
|
|
172
|
+
Logs (daily-rotated):
|
|
94
173
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
174
|
+
| OS | Path |
|
|
175
|
+
|----|------|
|
|
176
|
+
| Linux | `~/.local/state/ckforensics/logs/` |
|
|
177
|
+
| macOS | `~/Library/Logs/ckforensics/` |
|
|
178
|
+
| Windows | `%LOCALAPPDATA%\ckforensics\Logs\` |
|
|
179
|
+
|
|
180
|
+
**Nothing leaves your machine.** No telemetry. No cloud sync. Run `ckforensics path` to see resolved paths.
|
|
98
181
|
|
|
99
182
|
---
|
|
100
183
|
|
|
101
184
|
## Feature Matrix
|
|
102
185
|
|
|
103
|
-
| Feature | ckforensics | ccusage | Native CC |
|
|
104
|
-
|
|
186
|
+
| Feature | ckforensics | ccusage | Native CC `/usage` |
|
|
187
|
+
|---------|:-----------:|:-------:|:--------------------:|
|
|
105
188
|
| Local SQLite storage | ✅ | ✅ | ❌ |
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
189
|
+
| Token usage analytics | ✅ | ✅ | ✅ (live only) |
|
|
190
|
+
| Version-aware cost pricing | ✅ | 🟡 | ✅ |
|
|
191
|
+
| Subagent cost forensics (recursive) | ✅ | ❌ | ❌ |
|
|
192
|
+
| Context window heatmap | ✅ | ❌ | ❌ |
|
|
193
|
+
| Pre-compact simulation | ✅ | ❌ | ❌ |
|
|
194
|
+
| Skill recommendation engine | ✅ | ❌ | ❌ |
|
|
109
195
|
| Session change manifest (diff + reasoning) | ✅ | ❌ | ❌ |
|
|
110
|
-
|
|
|
111
|
-
| Markdown / JSON / CSV export | ✅ |
|
|
112
|
-
| Offline
|
|
196
|
+
| Secret redaction (9 rules) | ✅ | ❌ | ❌ |
|
|
197
|
+
| Markdown / JSON / CSV export | ✅ | 🟡 | ❌ |
|
|
198
|
+
| Offline, zero telemetry | ✅ | ✅ | ✅ |
|
|
113
199
|
| Cross-platform binaries | ✅ | ❌ | ✅ |
|
|
200
|
+
| ClaudeKit skill bundled | ✅ | ❌ | ❌ |
|
|
114
201
|
|
|
115
202
|
---
|
|
116
203
|
|
|
117
204
|
## Commands
|
|
118
205
|
|
|
119
|
-
| Command |
|
|
120
|
-
|
|
121
|
-
| `ingest` | Parse JSONL
|
|
122
|
-
| `summary` | Token + cost
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
206
|
+
| Command | Purpose |
|
|
207
|
+
|---------|---------|
|
|
208
|
+
| `ingest` | Parse JSONL → SQLite (idempotent + incremental, optional `--watch`) |
|
|
209
|
+
| `summary` | Token + cost rollup over rolling window (default 7d) |
|
|
210
|
+
| `sessions` | List sessions with cost, duration, model |
|
|
211
|
+
| `audit` | Per-session manifest: diffs + reasoning + subagent breakdown |
|
|
212
|
+
| `map` | Context-window heatmap by category, snapshot/diff/pin |
|
|
213
|
+
| `suggest` | Skill recommendations from detected tool patterns |
|
|
214
|
+
| `skills` | Browse ClaudeKit skill catalog with usage stats |
|
|
215
|
+
| `export` | Pipe-friendly export (markdown, JSON, CSV) |
|
|
216
|
+
| `redact` | Strip 9 secret patterns from a file |
|
|
217
|
+
| `doctor` | Health check (DB, paths, schema version, log activity) |
|
|
218
|
+
| `path` | Show all resolved file system paths |
|
|
219
|
+
|
|
220
|
+
Run `ckforensics <cmd> --help` for flags.
|
|
129
221
|
|
|
130
222
|
---
|
|
131
223
|
|
|
132
|
-
##
|
|
133
|
-
|
|
134
|
-
See [docs/architecture.md](docs/architecture.md) — module overview and data-flow diagram.
|
|
224
|
+
## About cost numbers
|
|
135
225
|
|
|
136
|
-
|
|
226
|
+
Cost is **API-rate equivalent** computed from token counts × Anthropic's published prices ([Nov 2025 snapshot](https://platform.claude.com/docs/en/about-claude/pricing)).
|
|
137
227
|
|
|
138
|
-
|
|
228
|
+
- **API users:** approximates your actual bill (±10-30% for cache-pricing edge cases)
|
|
229
|
+
- **Subscription users (Pro / Max / Team):** flat plan fee covers this — treat the number as **"value extracted"** from your subscription
|
|
139
230
|
|
|
140
|
-
|
|
231
|
+
Example: a 14h Opus 4.7 session showing `$166` means you'd pay ~$166 at API rates — your $100/mo Max plan covers it with positive ROI.
|
|
141
232
|
|
|
142
|
-
|
|
233
|
+
---
|
|
143
234
|
|
|
144
|
-
##
|
|
235
|
+
## Documentation
|
|
145
236
|
|
|
146
|
-
|
|
237
|
+
| Doc | Topic |
|
|
238
|
+
|-----|-------|
|
|
239
|
+
| [docs/architecture.md](docs/architecture.md) | Module overview, data flow |
|
|
240
|
+
| [docs/threat-model.md](docs/threat-model.md) | Privacy stance, redaction guarantees |
|
|
241
|
+
| [docs/jsonl-schema-v1.md](docs/jsonl-schema-v1.md) | Reverse-engineered JSONL event types |
|
|
242
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | Dev setup, PR checklist, fixture rules |
|
|
243
|
+
| [ROADMAP.md](ROADMAP.md) | v0.1.x → v0.6+ feature plans |
|
|
244
|
+
| [CHANGELOG.md](CHANGELOG.md) | Version history |
|
|
147
245
|
|
|
148
246
|
---
|
|
149
247
|
|
|
150
248
|
## License
|
|
151
249
|
|
|
152
250
|
MIT — [LICENSE](LICENSE) — Copyright (c) 2026 phong28zk
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Acknowledgements
|
|
255
|
+
|
|
256
|
+
- [ClaudeKit](https://github.com/phong28zk) ecosystem for the `/ck:*` skill pattern
|
|
257
|
+
- [Anthropic](https://anthropic.com) for Claude Code + the public JSONL transcript format
|
|
258
|
+
- [Bun](https://bun.sh) for single-binary compilation that makes cross-platform distribution painless
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckforensics",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"module": "index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"bin/",
|
|
18
18
|
"scripts/postinstall.js",
|
|
19
|
+
"scripts/install-claudekit-skill.js",
|
|
20
|
+
".claude/skills/",
|
|
19
21
|
"README.md",
|
|
20
22
|
"LICENSE"
|
|
21
23
|
],
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* install-claudekit-skill.js — Install the bundled `/ck:forensics` skill into
|
|
4
|
+
* the user's ClaudeKit skill directory (`~/.claude/skills/ckforensics/`).
|
|
5
|
+
*
|
|
6
|
+
* Called from postinstall.js. Designed to be silent on success and never
|
|
7
|
+
* fail the parent install — skill is a nice-to-have, not a hard dependency.
|
|
8
|
+
*
|
|
9
|
+
* Behavior:
|
|
10
|
+
* - Copies `<pkg>/.claude/skills/ckforensics/` to `~/.claude/skills/ckforensics/`.
|
|
11
|
+
* - Skip when source dir missing (e.g. dev install without the bundle).
|
|
12
|
+
* - Skip on opt-out: `CKFORENSICS_SKIP_SKILL_INSTALL=1`.
|
|
13
|
+
* - Existing skill dir is overwritten (idempotent re-install / version upgrade).
|
|
14
|
+
* - Warnings to stderr only; never exits non-zero.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync, mkdirSync, cpSync, statSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join, dirname } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const PKG_ROOT = join(__dirname, "..");
|
|
24
|
+
const SKILL_SOURCE = join(PKG_ROOT, ".claude", "skills", "ckforensics");
|
|
25
|
+
const SKILL_TARGET = join(homedir(), ".claude", "skills", "ckforensics");
|
|
26
|
+
|
|
27
|
+
function log(msg) {
|
|
28
|
+
// Single-line dim format to fit cleanly in npm install output
|
|
29
|
+
process.stderr.write(`[ckforensics:skill] ${msg}\n`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function shouldSkip() {
|
|
33
|
+
// Explicit opt-out
|
|
34
|
+
if (process.env.CKFORENSICS_SKIP_SKILL_INSTALL === "1") {
|
|
35
|
+
log("skipped via CKFORENSICS_SKIP_SKILL_INSTALL=1");
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
// Source missing — dev install or trimmed tarball
|
|
39
|
+
if (!existsSync(SKILL_SOURCE)) {
|
|
40
|
+
log(`skill source not bundled (${SKILL_SOURCE}); skipping`);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function copySkill() {
|
|
47
|
+
// Ensure parent dir exists; cpSync recursive handles the rest.
|
|
48
|
+
mkdirSync(dirname(SKILL_TARGET), { recursive: true });
|
|
49
|
+
|
|
50
|
+
// Detect upgrade vs first install for friendlier message
|
|
51
|
+
const existing = existsSync(SKILL_TARGET);
|
|
52
|
+
cpSync(SKILL_SOURCE, SKILL_TARGET, {
|
|
53
|
+
recursive: true,
|
|
54
|
+
force: true,
|
|
55
|
+
// Don't overwrite if user has customised it (mtime-based heuristic)
|
|
56
|
+
// — but that's risky and confusing. For now: always overwrite, matches
|
|
57
|
+
// CLI versioning semantics (skill version pinned to CLI version).
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const action = existing ? "updated" : "installed";
|
|
61
|
+
log(`${action} skill at ${SKILL_TARGET}`);
|
|
62
|
+
log("activate next Claude Code session: /ck:forensics");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
if (shouldSkip()) {
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
copySkill();
|
|
70
|
+
} catch (err) {
|
|
71
|
+
// Never fail parent install over skill copy
|
|
72
|
+
log(`warning: skill install failed (${err.message}); continuing`);
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -234,4 +234,19 @@ async function main() {
|
|
|
234
234
|
console.log(`[ckforensics] Run: ckforensics --help`);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
|
|
237
|
+
async function installSkill() {
|
|
238
|
+
// Best-effort: invoke the dedicated skill installer module. Failures here
|
|
239
|
+
// never block the binary install. Module handles its own opt-out + missing-
|
|
240
|
+
// source cases.
|
|
241
|
+
try {
|
|
242
|
+
await import("./install-claudekit-skill.js");
|
|
243
|
+
} catch (err) {
|
|
244
|
+
process.stderr.write(
|
|
245
|
+
`[ckforensics:skill] warning: failed to load skill installer (${err.message})\n`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
main()
|
|
251
|
+
.then(installSkill)
|
|
252
|
+
.catch((e) => fatal(String(e)));
|