ckforensics 0.2.0 → 0.2.2
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/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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckforensics",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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)));
|