@crafter/skillkit 0.1.2 → 0.1.4
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/README.md +113 -0
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/commands/health.ts +20 -22
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# skillkit
|
|
2
|
+
|
|
3
|
+
Local-first analytics for AI agent skills. Track usage, measure context budget, and prune what you don't use.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
AI coding agents load skills into their context window on every session. More skills = less room for your actual code. But which skills do you actually use? Which ones are wasting context budget?
|
|
8
|
+
|
|
9
|
+
**skillkit** answers these questions by scanning your session files, tracking invocations, and surfacing actionable insights - all locally on your machine.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @crafter/skillkit scan
|
|
15
|
+
npx @crafter/skillkit stats
|
|
16
|
+
npx @crafter/skillkit health
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install globally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i -g @crafter/skillkit
|
|
23
|
+
skillkit scan
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
| Command | Description |
|
|
29
|
+
|---------|-------------|
|
|
30
|
+
| `skillkit scan` | Discover installed skills and index session data |
|
|
31
|
+
| `skillkit list` | List installed skills with size and context budget |
|
|
32
|
+
| `skillkit stats` | Usage analytics with sparklines (last 30 days) |
|
|
33
|
+
| `skillkit health` | Health check: unused skills, context budget, DB |
|
|
34
|
+
| `skillkit prune` | Remove unused skills to reclaim context budget |
|
|
35
|
+
|
|
36
|
+
Install skills via [skills.sh](https://skills.sh): `npx skills add <owner/repo>`
|
|
37
|
+
|
|
38
|
+
## How It Works
|
|
39
|
+
|
|
40
|
+
### Scan
|
|
41
|
+
|
|
42
|
+
Discovers skills from `~/.claude/skills/` and indexes session files from `~/.claude/projects/`. Detects whether each skill was installed via skills.sh or manually.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
$ skillkit scan
|
|
46
|
+
Scanning ~/.claude/skills/ ...
|
|
47
|
+
Found 12 skills (8 via skills.sh, 4 manual)
|
|
48
|
+
Scanning sessions...
|
|
49
|
+
Indexed 211 sessions · 1,847 invocations
|
|
50
|
+
|
|
51
|
+
Ready. Run skillkit stats to see usage.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Stats
|
|
55
|
+
|
|
56
|
+
Parses JSONL session files for `Skill` tool_use blocks and shows sparkline trends.
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
$ skillkit stats
|
|
60
|
+
SKILL ████████████████████ 42 ▂▃▅▇█▆▅▇█
|
|
61
|
+
commit ████████████████████ 42 ▂▃▅▇█▆▅▇█
|
|
62
|
+
review ████████████████ 38 ▁▃▅▆▇▇▆▅▃
|
|
63
|
+
deploy ████████████ 27 ▁▁▂▃▅▇█▇▅
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Health
|
|
67
|
+
|
|
68
|
+
Checks metadata budget usage (name + description loaded at startup) and flags unused skills.
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
$ skillkit health
|
|
72
|
+
[████████░░] 78% metadata budget (12.5K / 16.0K)
|
|
73
|
+
! 3 skills unused in 30d — run skillkit prune
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Prune
|
|
77
|
+
|
|
78
|
+
Removes skills that haven't been used in the last 30 days.
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
$ skillkit prune
|
|
82
|
+
x scaffold (0.9K)
|
|
83
|
+
x lint (2.1K)
|
|
84
|
+
|
|
85
|
+
2 skills · 3.0K context reclaimable
|
|
86
|
+
|
|
87
|
+
Run with --yes to confirm deletion.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Data Storage
|
|
91
|
+
|
|
92
|
+
All data stays on your machine. No telemetry. No signup.
|
|
93
|
+
|
|
94
|
+
| Path | Purpose |
|
|
95
|
+
|------|---------|
|
|
96
|
+
| `~/.skillkit/analytics.db` | SQLite database with invocation history |
|
|
97
|
+
| `~/.claude/skills/` | Installed skills (read-only) |
|
|
98
|
+
| `~/.claude/projects/**/*.jsonl` | Session files (read-only) |
|
|
99
|
+
|
|
100
|
+
## Supported Agents
|
|
101
|
+
|
|
102
|
+
Works with any agent that logs tool use in JSONL session files:
|
|
103
|
+
|
|
104
|
+
- Claude Code
|
|
105
|
+
- Cursor
|
|
106
|
+
- Codex
|
|
107
|
+
- VS Code (via extensions)
|
|
108
|
+
- Windsurf
|
|
109
|
+
- Gemini CLI
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
package/package.json
CHANGED
package/src/bin.ts
CHANGED
package/src/commands/health.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { getTopSkills } from "../db/queries";
|
|
|
5
5
|
import { getDb } from "../db/schema";
|
|
6
6
|
import { scanInstalledSkills } from "../scanner/skills";
|
|
7
7
|
import { bold, dim, green, red, yellow } from "../tui/colors";
|
|
8
|
-
import { healthGauge } from "../tui/health";
|
|
9
8
|
|
|
10
9
|
const METADATA_BUDGET = 16000;
|
|
11
10
|
const BODY_LINE_LIMIT = 500;
|
|
@@ -31,7 +30,7 @@ function parseFrontmatter(content: string): {
|
|
|
31
30
|
if (!match)
|
|
32
31
|
return { name: "", description: "", bodyLines: content.split("\n").length };
|
|
33
32
|
|
|
34
|
-
const yaml = match[1];
|
|
33
|
+
const yaml = match[1] ?? "";
|
|
35
34
|
const body = content.slice(match[0].length);
|
|
36
35
|
const bodyLines = body.trim() ? body.trim().split("\n").length : 0;
|
|
37
36
|
|
|
@@ -39,10 +38,11 @@ function parseFrontmatter(content: string): {
|
|
|
39
38
|
let description = "";
|
|
40
39
|
|
|
41
40
|
const nameMatch = yaml.match(/^name:\s*(.+)$/m);
|
|
42
|
-
if (nameMatch) name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
41
|
+
if (nameMatch?.[1]) name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
43
42
|
|
|
44
43
|
const descMatch = yaml.match(/^description:\s*(.+)$/m);
|
|
45
|
-
if (descMatch
|
|
44
|
+
if (descMatch?.[1])
|
|
45
|
+
description = descMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
46
46
|
|
|
47
47
|
return { name, description, bodyLines };
|
|
48
48
|
}
|
|
@@ -138,28 +138,26 @@ export async function runHealth(): Promise<void> {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
console.log();
|
|
141
|
-
const gaugeStr = healthGauge(100 - metadataPct);
|
|
142
141
|
const metaKb = (totalMetadataChars / 1000).toFixed(1);
|
|
143
142
|
const budgetKb = (METADATA_BUDGET / 1000).toFixed(1);
|
|
144
143
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
console.log(` ${gaugeStr}`);
|
|
144
|
+
const filled = Math.round(Math.min(metadataPct, 100) / 10);
|
|
145
|
+
const empty = 10 - filled;
|
|
146
|
+
const barFill = "█".repeat(filled);
|
|
147
|
+
const barEmpty = "░".repeat(empty);
|
|
148
|
+
const colorBar =
|
|
149
|
+
metadataPct >= 90
|
|
150
|
+
? red(barFill)
|
|
151
|
+
: metadataPct >= 70
|
|
152
|
+
? yellow(barFill)
|
|
153
|
+
: green(barFill);
|
|
154
|
+
const bar = `[${colorBar}${dim(barEmpty)}]`;
|
|
155
|
+
|
|
156
|
+
console.log(
|
|
157
|
+
` ${bar} ${bold(`${metadataPct}%`)} metadata budget ${dim(`(${metaKb}K / ${budgetKb}K)`)}`,
|
|
158
|
+
);
|
|
161
159
|
console.log(
|
|
162
|
-
` ${dim(
|
|
160
|
+
` ${dim("name + description of each skill, loaded at startup")}`,
|
|
163
161
|
);
|
|
164
162
|
|
|
165
163
|
const bodyKb = (totalBodyChars / 1000).toFixed(1);
|