skillvitals 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skillvitals-0.1.0/.gitignore +17 -0
- skillvitals-0.1.0/.python-version +1 -0
- skillvitals-0.1.0/LICENSE +21 -0
- skillvitals-0.1.0/PKG-INFO +178 -0
- skillvitals-0.1.0/PUBLISHING.md +71 -0
- skillvitals-0.1.0/README.md +149 -0
- skillvitals-0.1.0/docs/sample-dashboard.html +205 -0
- skillvitals-0.1.0/docs/superpowers/plans/2026-05-25-skillvitals.md +151 -0
- skillvitals-0.1.0/pyproject.toml +65 -0
- skillvitals-0.1.0/scripts/gen_demo.py +70 -0
- skillvitals-0.1.0/src/skillvitals/__init__.py +3 -0
- skillvitals-0.1.0/src/skillvitals/__main__.py +4 -0
- skillvitals-0.1.0/src/skillvitals/analysis.py +139 -0
- skillvitals-0.1.0/src/skillvitals/cli.py +283 -0
- skillvitals-0.1.0/src/skillvitals/config.py +73 -0
- skillvitals-0.1.0/src/skillvitals/dashboard.py +84 -0
- skillvitals-0.1.0/src/skillvitals/hooks.py +49 -0
- skillvitals-0.1.0/src/skillvitals/logparser.py +196 -0
- skillvitals-0.1.0/src/skillvitals/models.py +158 -0
- skillvitals-0.1.0/src/skillvitals/pipeline.py +58 -0
- skillvitals-0.1.0/src/skillvitals/prescribe.py +148 -0
- skillvitals-0.1.0/src/skillvitals/registry.py +210 -0
- skillvitals-0.1.0/src/skillvitals/report.py +107 -0
- skillvitals-0.1.0/src/skillvitals/server.py +125 -0
- skillvitals-0.1.0/src/skillvitals/storage.py +158 -0
- skillvitals-0.1.0/src/skillvitals/templates/dashboard.html.j2 +113 -0
- skillvitals-0.1.0/src/skillvitals/testharness.py +145 -0
- skillvitals-0.1.0/src/skillvitals/tokens.py +26 -0
- skillvitals-0.1.0/tests/__init__.py +0 -0
- skillvitals-0.1.0/tests/conftest.py +35 -0
- skillvitals-0.1.0/tests/test_analysis.py +72 -0
- skillvitals-0.1.0/tests/test_cli.py +59 -0
- skillvitals-0.1.0/tests/test_dashboard.py +42 -0
- skillvitals-0.1.0/tests/test_logparser.py +95 -0
- skillvitals-0.1.0/tests/test_prescribe.py +87 -0
- skillvitals-0.1.0/tests/test_registry.py +93 -0
- skillvitals-0.1.0/tests/test_report.py +41 -0
- skillvitals-0.1.0/tests/test_server.py +36 -0
- skillvitals-0.1.0/tests/test_storage.py +65 -0
- skillvitals-0.1.0/tests/test_testharness.py +72 -0
- skillvitals-0.1.0/tests/test_tokens.py +16 -0
- skillvitals-0.1.0/uv.lock +1887 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: skillvitals
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Skill observability for Claude Code — see which skills fire, which are dormant, what they cost in context, and what's broken.
|
|
5
|
+
Project-URL: Homepage, https://github.com/PraveenKumarSridhar/skillvitals
|
|
6
|
+
Project-URL: Repository, https://github.com/PraveenKumarSridhar/skillvitals
|
|
7
|
+
Project-URL: Issues, https://github.com/PraveenKumarSridhar/skillvitals/issues
|
|
8
|
+
Author: Pk
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: anthropic,claude,claude-code,mcp,observability,skills
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: click>=8.1
|
|
22
|
+
Requires-Dist: fastmcp>=2.0
|
|
23
|
+
Requires-Dist: jinja2>=3.1
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: rich>=13.0
|
|
26
|
+
Provides-Extra: llm
|
|
27
|
+
Requires-Dist: anthropic>=0.40; extra == 'llm'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# skillvitals
|
|
31
|
+
|
|
32
|
+
**Skill observability for Claude Code.** See which of your skills fire, which are
|
|
33
|
+
dormant, what they cost in context, and what's broken.
|
|
34
|
+
|
|
35
|
+
Claude Code skills are supposed to auto-activate. In practice many never fire —
|
|
36
|
+
they just sit in every session burning context tokens. The ecosystem has plenty
|
|
37
|
+
of tools to *generate* skill-activation hooks. `skillvitals` is the missing
|
|
38
|
+
diagnostic layer: it treats every installed skill as a monitored service and
|
|
39
|
+
tells you *did it fire? when? at what context cost? is it dead weight?*
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
$ skillvitals scan
|
|
43
|
+
|
|
44
|
+
skillvitals
|
|
45
|
+
┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
|
|
46
|
+
┃ skill ┃ fires ┃ engaged ┃ ctx ┃ last seen ┃ status ┃
|
|
47
|
+
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
|
|
48
|
+
│ frontend-design │ 31 │ 140 │ 6.4k │ today │ ✅ healthy │
|
|
49
|
+
│ docx │ 47 │ 120 │ 2.1k │ today │ ✅ healthy │
|
|
50
|
+
│ pdf │ 23 │ 60 │ 1.8k │ 1d ago │ ✅ healthy │
|
|
51
|
+
│ sql-tuner │ 4 │ 9 │ 2.6k │ 9d ago │ ✅ healthy │
|
|
52
|
+
│ ab-test-coach │ 2 │ 2 │ 5.7k │ 3d ago │ ⚠️ misfiring │
|
|
53
|
+
│ leakcheck │ 1 │ 3 │ 3.1k │ 40d ago │ ⚠️ dormant │
|
|
54
|
+
│ data-analysis │ 0 │ 0 │ 4.2k │ never │ 💤 never-fired │
|
|
55
|
+
│ changelog-writer │ 0 │ 0 │ 1.4k │ never │ 💤 never-fired │
|
|
56
|
+
└────────────────────┴───────┴─────────┴──────┴───────────┴────────────────┘
|
|
57
|
+
|
|
58
|
+
3 dormant/never-fired skills are costing you 8.7k tokens per session.
|
|
59
|
+
Run `skillvitals prescribe` for fixes.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
That last line is the point: **most people have thousands of tokens of dead
|
|
63
|
+
weight in every single session and no way to see it.**
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uvx skillvitals scan # run without installing
|
|
69
|
+
# or
|
|
70
|
+
pip install skillvitals
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Requires Python 3.11+. Reads your existing Claude Code data under `~/.claude` —
|
|
74
|
+
nothing is sent anywhere (see [Privacy](#privacy)).
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
| Command | What it does |
|
|
79
|
+
|---------|--------------|
|
|
80
|
+
| `skillvitals scan` | Headline table: fires, engagement, context cost, health, per skill. |
|
|
81
|
+
| `skillvitals report` | Markdown report (`-o report.md` to save / share). |
|
|
82
|
+
| `skillvitals history` | Per-skill activation history across sessions. |
|
|
83
|
+
| `skillvitals dormancy` | Skills inactive for N days and the tokens they cost (`--days 14`). |
|
|
84
|
+
| `skillvitals prescribe` | Concrete fixes for weak/dormant/redundant skills (`--rewrite` for LLM rewrites). |
|
|
85
|
+
| `skillvitals test --skill X` | Synthetic activation-test prompts (`--live` runs them through headless Claude Code). |
|
|
86
|
+
| `skillvitals dashboard --open` | Self-contained HTML dashboard at `~/.skillvitals/dashboard.html`. |
|
|
87
|
+
| `skillvitals serve` | Run as an MCP server. |
|
|
88
|
+
|
|
89
|
+
## Use it as an MCP server
|
|
90
|
+
|
|
91
|
+
`skillvitals` is also an MCP server, so you can ask Claude Code about your skills
|
|
92
|
+
in plain language ("which of my skills are dormant?").
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
claude mcp add skillvitals -- uvx skillvitals serve
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or add it to your MCP config manually:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"skillvitals": { "command": "uvx", "args": ["skillvitals", "serve"] }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Exposed tools: `vitals_scan`, `vitals_history`, `vitals_dormancy`,
|
|
109
|
+
`vitals_report`, `vitals_prescribe`, `vitals_test`, `vitals_dashboard`.
|
|
110
|
+
|
|
111
|
+
## How it works
|
|
112
|
+
|
|
113
|
+
skillvitals reads two things, entirely locally:
|
|
114
|
+
|
|
115
|
+
1. **Your installed skills** — every `SKILL.md` under `~/.claude/skills`, the
|
|
116
|
+
plugin cache, and the current project's `.claude/skills`. It parses the
|
|
117
|
+
frontmatter, estimates the context cost (tokens of the loaded `SKILL.md`),
|
|
118
|
+
and scores description quality.
|
|
119
|
+
|
|
120
|
+
2. **Your session logs** — the JSONL transcripts under `~/.claude/projects`. It
|
|
121
|
+
extracts two activation signals per skill:
|
|
122
|
+
- **fires** — explicit `Skill()` invocations (the skill was activated).
|
|
123
|
+
- **engaged** — assistant messages tagged with that skill's `attributionSkill`
|
|
124
|
+
(how much the skill was actually leaned on afterward).
|
|
125
|
+
|
|
126
|
+
Joining the two gives each skill a health status:
|
|
127
|
+
|
|
128
|
+
| status | meaning |
|
|
129
|
+
|--------|---------|
|
|
130
|
+
| ✅ **healthy** | activated recently with real follow-through |
|
|
131
|
+
| ⚠️ **misfiring** | invoked but barely used afterward — may be matching the wrong prompts |
|
|
132
|
+
| ⚠️ **dormant** | activated before, but not within the window |
|
|
133
|
+
| 💤 **never-fired** | installed, costs tokens, has never activated |
|
|
134
|
+
| ❓ **orphan** | appears in logs but is no longer installed |
|
|
135
|
+
|
|
136
|
+
These are honest heuristics, not ground truth — the thresholds are documented in
|
|
137
|
+
`analysis.py` and `prescribe.py`.
|
|
138
|
+
|
|
139
|
+
## Privacy
|
|
140
|
+
|
|
141
|
+
100% local. skillvitals only reads files already on your machine under
|
|
142
|
+
`~/.claude`, writes a local SQLite cache to `~/.skillvitals/db.sqlite`, and a
|
|
143
|
+
local HTML file. **No network calls, no telemetry, nothing leaves your machine** —
|
|
144
|
+
with two explicit, opt-in exceptions:
|
|
145
|
+
|
|
146
|
+
- `skillvitals test --live` spawns headless Claude Code to measure real activation.
|
|
147
|
+
- `skillvitals prescribe --rewrite` calls the Anthropic API to rewrite weak
|
|
148
|
+
descriptions. Requires `pip install 'skillvitals[llm]'` and `ANTHROPIC_API_KEY`.
|
|
149
|
+
|
|
150
|
+
Both are off by default.
|
|
151
|
+
|
|
152
|
+
## What it deliberately doesn't do
|
|
153
|
+
|
|
154
|
+
- **Generate activation hooks.** That space is well covered (`skills-hook`,
|
|
155
|
+
`claude-skills-supercharged`, …). Pair skillvitals with one of those — it
|
|
156
|
+
*reports* whether a `UserPromptSubmit` hook exists, it doesn't write one.
|
|
157
|
+
- **Auto-apply fixes.** v1 shows prescriptions; it doesn't edit your skills.
|
|
158
|
+
- **Phone home.** No cloud, no accounts, no team aggregation.
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
Environment variables (all optional):
|
|
163
|
+
|
|
164
|
+
- `SKILLVITALS_CLAUDE_HOME` — the `.claude` dir (default `~/.claude`).
|
|
165
|
+
- `SKILLVITALS_HOME` — where the db + dashboard live (default `~/.skillvitals`).
|
|
166
|
+
- `ANTHROPIC_API_KEY` — only needed for the opt-in LLM features.
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
uv sync --extra llm
|
|
172
|
+
uv run pytest # 47 tests
|
|
173
|
+
uv run ruff check src tests
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT © 2026 Pk
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Publishing skillvitals
|
|
2
|
+
|
|
3
|
+
Everything here is **left for you to run** — I built and validated the package
|
|
4
|
+
locally but deliberately did not publish to PyPI or push to a public GitHub
|
|
5
|
+
repo, because those are irreversible, outward-facing, and tied to your identity
|
|
6
|
+
and credentials. Do these when you're awake and ready to launch.
|
|
7
|
+
|
|
8
|
+
## 0. Pre-flight (already done / verify)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
uv run pytest # 47 passing
|
|
12
|
+
uv run ruff check src tests # clean
|
|
13
|
+
uv build # builds dist/*.whl and dist/*.tar.gz
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A clean-room install of the built wheel has been verified to expose the
|
|
17
|
+
`skillvitals` console script and run `skillvitals scan` (see
|
|
18
|
+
`docs/superpowers/plans/` notes).
|
|
19
|
+
|
|
20
|
+
## 1. Set the real homepage URLs
|
|
21
|
+
|
|
22
|
+
`pyproject.toml` currently points `Homepage`/`Repository`/`Issues` at
|
|
23
|
+
`github.com/pk/skillvitals`. Update those to your actual GitHub username/org
|
|
24
|
+
before publishing.
|
|
25
|
+
|
|
26
|
+
## 2. Create the GitHub repo
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
gh repo create skillvitals --public --source=. --remote=origin \
|
|
30
|
+
--description "Skill observability for Claude Code"
|
|
31
|
+
git push -u origin main
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
(Record a demo GIF of `skillvitals scan` and drop it in the README — the
|
|
35
|
+
dormant-token line is the viral asset.)
|
|
36
|
+
|
|
37
|
+
## 3. Publish to PyPI
|
|
38
|
+
|
|
39
|
+
Test on TestPyPI first:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv publish --publish-url https://test.pypi.org/legacy/ --token <test-token>
|
|
43
|
+
uvx --index-url https://test.pypi.org/simple/ skillvitals scan # smoke test
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then the real thing:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv publish --token <pypi-token> # uses dist/ from `uv build`
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Verify:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uvx skillvitals scan
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 4. Register with MCP directories
|
|
59
|
+
|
|
60
|
+
- Glama, mcpmarket.com (per the PRD launch plan).
|
|
61
|
+
|
|
62
|
+
## 5. Launch posts
|
|
63
|
+
|
|
64
|
+
Drafts/checklist live in the PRD (Show HN, r/ClaudeAI, LinkedIn, X thread, the
|
|
65
|
+
DM tour). The headline is always the dead-token number from your own machine.
|
|
66
|
+
|
|
67
|
+
## Name availability (from the PRD, last checked 2026-05-25)
|
|
68
|
+
|
|
69
|
+
`skillvitals` was reported available on PyPI / npm / GitHub and `skillvitals.dev`
|
|
70
|
+
was free. **Re-check immediately before publishing** — these can change.
|
|
71
|
+
Fallbacks: `skillscope`, `skillmon`, `skill-doctor`.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# skillvitals
|
|
2
|
+
|
|
3
|
+
**Skill observability for Claude Code.** See which of your skills fire, which are
|
|
4
|
+
dormant, what they cost in context, and what's broken.
|
|
5
|
+
|
|
6
|
+
Claude Code skills are supposed to auto-activate. In practice many never fire —
|
|
7
|
+
they just sit in every session burning context tokens. The ecosystem has plenty
|
|
8
|
+
of tools to *generate* skill-activation hooks. `skillvitals` is the missing
|
|
9
|
+
diagnostic layer: it treats every installed skill as a monitored service and
|
|
10
|
+
tells you *did it fire? when? at what context cost? is it dead weight?*
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
$ skillvitals scan
|
|
14
|
+
|
|
15
|
+
skillvitals
|
|
16
|
+
┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
|
|
17
|
+
┃ skill ┃ fires ┃ engaged ┃ ctx ┃ last seen ┃ status ┃
|
|
18
|
+
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
|
|
19
|
+
│ frontend-design │ 31 │ 140 │ 6.4k │ today │ ✅ healthy │
|
|
20
|
+
│ docx │ 47 │ 120 │ 2.1k │ today │ ✅ healthy │
|
|
21
|
+
│ pdf │ 23 │ 60 │ 1.8k │ 1d ago │ ✅ healthy │
|
|
22
|
+
│ sql-tuner │ 4 │ 9 │ 2.6k │ 9d ago │ ✅ healthy │
|
|
23
|
+
│ ab-test-coach │ 2 │ 2 │ 5.7k │ 3d ago │ ⚠️ misfiring │
|
|
24
|
+
│ leakcheck │ 1 │ 3 │ 3.1k │ 40d ago │ ⚠️ dormant │
|
|
25
|
+
│ data-analysis │ 0 │ 0 │ 4.2k │ never │ 💤 never-fired │
|
|
26
|
+
│ changelog-writer │ 0 │ 0 │ 1.4k │ never │ 💤 never-fired │
|
|
27
|
+
└────────────────────┴───────┴─────────┴──────┴───────────┴────────────────┘
|
|
28
|
+
|
|
29
|
+
3 dormant/never-fired skills are costing you 8.7k tokens per session.
|
|
30
|
+
Run `skillvitals prescribe` for fixes.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That last line is the point: **most people have thousands of tokens of dead
|
|
34
|
+
weight in every single session and no way to see it.**
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uvx skillvitals scan # run without installing
|
|
40
|
+
# or
|
|
41
|
+
pip install skillvitals
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Requires Python 3.11+. Reads your existing Claude Code data under `~/.claude` —
|
|
45
|
+
nothing is sent anywhere (see [Privacy](#privacy)).
|
|
46
|
+
|
|
47
|
+
## Commands
|
|
48
|
+
|
|
49
|
+
| Command | What it does |
|
|
50
|
+
|---------|--------------|
|
|
51
|
+
| `skillvitals scan` | Headline table: fires, engagement, context cost, health, per skill. |
|
|
52
|
+
| `skillvitals report` | Markdown report (`-o report.md` to save / share). |
|
|
53
|
+
| `skillvitals history` | Per-skill activation history across sessions. |
|
|
54
|
+
| `skillvitals dormancy` | Skills inactive for N days and the tokens they cost (`--days 14`). |
|
|
55
|
+
| `skillvitals prescribe` | Concrete fixes for weak/dormant/redundant skills (`--rewrite` for LLM rewrites). |
|
|
56
|
+
| `skillvitals test --skill X` | Synthetic activation-test prompts (`--live` runs them through headless Claude Code). |
|
|
57
|
+
| `skillvitals dashboard --open` | Self-contained HTML dashboard at `~/.skillvitals/dashboard.html`. |
|
|
58
|
+
| `skillvitals serve` | Run as an MCP server. |
|
|
59
|
+
|
|
60
|
+
## Use it as an MCP server
|
|
61
|
+
|
|
62
|
+
`skillvitals` is also an MCP server, so you can ask Claude Code about your skills
|
|
63
|
+
in plain language ("which of my skills are dormant?").
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
claude mcp add skillvitals -- uvx skillvitals serve
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or add it to your MCP config manually:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"skillvitals": { "command": "uvx", "args": ["skillvitals", "serve"] }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Exposed tools: `vitals_scan`, `vitals_history`, `vitals_dormancy`,
|
|
80
|
+
`vitals_report`, `vitals_prescribe`, `vitals_test`, `vitals_dashboard`.
|
|
81
|
+
|
|
82
|
+
## How it works
|
|
83
|
+
|
|
84
|
+
skillvitals reads two things, entirely locally:
|
|
85
|
+
|
|
86
|
+
1. **Your installed skills** — every `SKILL.md` under `~/.claude/skills`, the
|
|
87
|
+
plugin cache, and the current project's `.claude/skills`. It parses the
|
|
88
|
+
frontmatter, estimates the context cost (tokens of the loaded `SKILL.md`),
|
|
89
|
+
and scores description quality.
|
|
90
|
+
|
|
91
|
+
2. **Your session logs** — the JSONL transcripts under `~/.claude/projects`. It
|
|
92
|
+
extracts two activation signals per skill:
|
|
93
|
+
- **fires** — explicit `Skill()` invocations (the skill was activated).
|
|
94
|
+
- **engaged** — assistant messages tagged with that skill's `attributionSkill`
|
|
95
|
+
(how much the skill was actually leaned on afterward).
|
|
96
|
+
|
|
97
|
+
Joining the two gives each skill a health status:
|
|
98
|
+
|
|
99
|
+
| status | meaning |
|
|
100
|
+
|--------|---------|
|
|
101
|
+
| ✅ **healthy** | activated recently with real follow-through |
|
|
102
|
+
| ⚠️ **misfiring** | invoked but barely used afterward — may be matching the wrong prompts |
|
|
103
|
+
| ⚠️ **dormant** | activated before, but not within the window |
|
|
104
|
+
| 💤 **never-fired** | installed, costs tokens, has never activated |
|
|
105
|
+
| ❓ **orphan** | appears in logs but is no longer installed |
|
|
106
|
+
|
|
107
|
+
These are honest heuristics, not ground truth — the thresholds are documented in
|
|
108
|
+
`analysis.py` and `prescribe.py`.
|
|
109
|
+
|
|
110
|
+
## Privacy
|
|
111
|
+
|
|
112
|
+
100% local. skillvitals only reads files already on your machine under
|
|
113
|
+
`~/.claude`, writes a local SQLite cache to `~/.skillvitals/db.sqlite`, and a
|
|
114
|
+
local HTML file. **No network calls, no telemetry, nothing leaves your machine** —
|
|
115
|
+
with two explicit, opt-in exceptions:
|
|
116
|
+
|
|
117
|
+
- `skillvitals test --live` spawns headless Claude Code to measure real activation.
|
|
118
|
+
- `skillvitals prescribe --rewrite` calls the Anthropic API to rewrite weak
|
|
119
|
+
descriptions. Requires `pip install 'skillvitals[llm]'` and `ANTHROPIC_API_KEY`.
|
|
120
|
+
|
|
121
|
+
Both are off by default.
|
|
122
|
+
|
|
123
|
+
## What it deliberately doesn't do
|
|
124
|
+
|
|
125
|
+
- **Generate activation hooks.** That space is well covered (`skills-hook`,
|
|
126
|
+
`claude-skills-supercharged`, …). Pair skillvitals with one of those — it
|
|
127
|
+
*reports* whether a `UserPromptSubmit` hook exists, it doesn't write one.
|
|
128
|
+
- **Auto-apply fixes.** v1 shows prescriptions; it doesn't edit your skills.
|
|
129
|
+
- **Phone home.** No cloud, no accounts, no team aggregation.
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
Environment variables (all optional):
|
|
134
|
+
|
|
135
|
+
- `SKILLVITALS_CLAUDE_HOME` — the `.claude` dir (default `~/.claude`).
|
|
136
|
+
- `SKILLVITALS_HOME` — where the db + dashboard live (default `~/.skillvitals`).
|
|
137
|
+
- `ANTHROPIC_API_KEY` — only needed for the opt-in LLM features.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uv sync --extra llm
|
|
143
|
+
uv run pytest # 47 tests
|
|
144
|
+
uv run ruff check src tests
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT © 2026 Pk
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>skillvitals dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { --bg:#0d1117; --card:#161b22; --line:#30363d; --fg:#e6edf3; --dim:#8b949e;
|
|
9
|
+
--green:#3fb950; --yellow:#d29922; --red:#f85149; --accent:#58a6ff; }
|
|
10
|
+
* { box-sizing: border-box; }
|
|
11
|
+
body { margin:0; background:var(--bg); color:var(--fg);
|
|
12
|
+
font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; }
|
|
13
|
+
.wrap { max-width: 1100px; margin: 0 auto; padding: 32px 20px 64px; }
|
|
14
|
+
h1 { font-size: 22px; margin: 0 0 4px; }
|
|
15
|
+
.sub { color: var(--dim); margin: 0 0 24px; }
|
|
16
|
+
.cards { display:flex; gap:14px; flex-wrap:wrap; margin-bottom:28px; }
|
|
17
|
+
.card { background:var(--card); border:1px solid var(--line); border-radius:10px;
|
|
18
|
+
padding:16px 18px; min-width:150px; flex:1; }
|
|
19
|
+
.card .n { font-size:26px; font-weight:700; }
|
|
20
|
+
.card .l { color:var(--dim); font-size:12px; text-transform:uppercase; letter-spacing:.04em; }
|
|
21
|
+
.card.alert .n { color: var(--yellow); }
|
|
22
|
+
table { width:100%; border-collapse:collapse; background:var(--card);
|
|
23
|
+
border:1px solid var(--line); border-radius:10px; overflow:hidden; }
|
|
24
|
+
th,td { text-align:left; padding:10px 14px; border-bottom:1px solid var(--line); }
|
|
25
|
+
th { cursor:pointer; user-select:none; color:var(--dim); font-weight:600;
|
|
26
|
+
font-size:12px; text-transform:uppercase; letter-spacing:.03em; }
|
|
27
|
+
th:hover { color: var(--fg); }
|
|
28
|
+
tbody tr:last-child td { border-bottom:none; }
|
|
29
|
+
tbody tr:hover { background: #1c2330; }
|
|
30
|
+
td.num { text-align:right; font-variant-numeric: tabular-nums; }
|
|
31
|
+
.pill { display:inline-block; padding:2px 8px; border-radius:999px; font-size:12px; font-weight:600; }
|
|
32
|
+
.healthy { color:var(--green); } .dormant,.misfiring { color:var(--yellow); }
|
|
33
|
+
.never-fired { color:var(--dim); } .orphan { color:var(--red); }
|
|
34
|
+
.bar { height:6px; background:#21262d; border-radius:4px; overflow:hidden; min-width:60px; }
|
|
35
|
+
.bar > i { display:block; height:100%; background:var(--accent); }
|
|
36
|
+
.rx { margin-top:28px; }
|
|
37
|
+
.rx h2 { font-size:16px; }
|
|
38
|
+
.rx li { margin:6px 0; color:var(--dim); }
|
|
39
|
+
.rx b { color:var(--fg); }
|
|
40
|
+
footer { margin-top:30px; color:var(--dim); font-size:12px; }
|
|
41
|
+
code { background:#21262d; padding:1px 6px; border-radius:5px; }
|
|
42
|
+
</style>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<div class="wrap">
|
|
46
|
+
<h1>skillvitals</h1>
|
|
47
|
+
<p class="sub">8 skills scanned · generated 2026-05-25 (demo data)</p>
|
|
48
|
+
|
|
49
|
+
<div class="cards">
|
|
50
|
+
<div class="card"><div class="n">8</div><div class="l">skills</div></div>
|
|
51
|
+
<div class="card"><div class="n">4</div><div class="l">healthy</div></div>
|
|
52
|
+
<div class="card alert"><div class="n">3</div><div class="l">dormant / never-fired</div></div>
|
|
53
|
+
<div class="card alert"><div class="n">8.7k</div><div class="l">dead tokens / session</div></div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<table id="vitals">
|
|
57
|
+
<thead><tr>
|
|
58
|
+
<th onclick="sortTable(0,'s')">skill</th>
|
|
59
|
+
<th onclick="sortTable(1,'n')">fires</th>
|
|
60
|
+
<th onclick="sortTable(2,'n')">engaged</th>
|
|
61
|
+
<th onclick="sortTable(3,'n')">ctx tokens</th>
|
|
62
|
+
<th onclick="sortTable(4,'n')">quality</th>
|
|
63
|
+
<th onclick="sortTable(5,'n')">last seen</th>
|
|
64
|
+
<th onclick="sortTable(6,'s')">status</th>
|
|
65
|
+
</tr></thead>
|
|
66
|
+
<tbody>
|
|
67
|
+
|
|
68
|
+
<tr class="skill-row">
|
|
69
|
+
<td>frontend-design</td>
|
|
70
|
+
<td class="num" data-v="31">31</td>
|
|
71
|
+
<td class="num" data-v="140">140</td>
|
|
72
|
+
<td class="num" data-v="6400">6.4k</td>
|
|
73
|
+
<td class="num" data-v="88">
|
|
74
|
+
<div class="bar" title="88/100"><i style="width:88%"></i></div>
|
|
75
|
+
</td>
|
|
76
|
+
<td class="num" data-v="0">today</td>
|
|
77
|
+
<td><span class="pill healthy">healthy</span></td>
|
|
78
|
+
</tr>
|
|
79
|
+
|
|
80
|
+
<tr class="skill-row">
|
|
81
|
+
<td>docx</td>
|
|
82
|
+
<td class="num" data-v="47">47</td>
|
|
83
|
+
<td class="num" data-v="120">120</td>
|
|
84
|
+
<td class="num" data-v="2100">2.1k</td>
|
|
85
|
+
<td class="num" data-v="82">
|
|
86
|
+
<div class="bar" title="82/100"><i style="width:82%"></i></div>
|
|
87
|
+
</td>
|
|
88
|
+
<td class="num" data-v="0">today</td>
|
|
89
|
+
<td><span class="pill healthy">healthy</span></td>
|
|
90
|
+
</tr>
|
|
91
|
+
|
|
92
|
+
<tr class="skill-row">
|
|
93
|
+
<td>pdf</td>
|
|
94
|
+
<td class="num" data-v="23">23</td>
|
|
95
|
+
<td class="num" data-v="60">60</td>
|
|
96
|
+
<td class="num" data-v="1800">1.8k</td>
|
|
97
|
+
<td class="num" data-v="80">
|
|
98
|
+
<div class="bar" title="80/100"><i style="width:80%"></i></div>
|
|
99
|
+
</td>
|
|
100
|
+
<td class="num" data-v="1">1d ago</td>
|
|
101
|
+
<td><span class="pill healthy">healthy</span></td>
|
|
102
|
+
</tr>
|
|
103
|
+
|
|
104
|
+
<tr class="skill-row">
|
|
105
|
+
<td>sql-tuner</td>
|
|
106
|
+
<td class="num" data-v="4">4</td>
|
|
107
|
+
<td class="num" data-v="9">9</td>
|
|
108
|
+
<td class="num" data-v="2600">2.6k</td>
|
|
109
|
+
<td class="num" data-v="72">
|
|
110
|
+
<div class="bar" title="72/100"><i style="width:72%"></i></div>
|
|
111
|
+
</td>
|
|
112
|
+
<td class="num" data-v="9">9d ago</td>
|
|
113
|
+
<td><span class="pill healthy">healthy</span></td>
|
|
114
|
+
</tr>
|
|
115
|
+
|
|
116
|
+
<tr class="skill-row">
|
|
117
|
+
<td>ab-test-coach</td>
|
|
118
|
+
<td class="num" data-v="2">2</td>
|
|
119
|
+
<td class="num" data-v="2">2</td>
|
|
120
|
+
<td class="num" data-v="5700">5.7k</td>
|
|
121
|
+
<td class="num" data-v="70">
|
|
122
|
+
<div class="bar" title="70/100"><i style="width:70%"></i></div>
|
|
123
|
+
</td>
|
|
124
|
+
<td class="num" data-v="3">3d ago</td>
|
|
125
|
+
<td><span class="pill misfiring">misfiring</span></td>
|
|
126
|
+
</tr>
|
|
127
|
+
|
|
128
|
+
<tr class="skill-row">
|
|
129
|
+
<td>leakcheck</td>
|
|
130
|
+
<td class="num" data-v="1">1</td>
|
|
131
|
+
<td class="num" data-v="3">3</td>
|
|
132
|
+
<td class="num" data-v="3100">3.1k</td>
|
|
133
|
+
<td class="num" data-v="76">
|
|
134
|
+
<div class="bar" title="76/100"><i style="width:76%"></i></div>
|
|
135
|
+
</td>
|
|
136
|
+
<td class="num" data-v="40">40d ago</td>
|
|
137
|
+
<td><span class="pill dormant">dormant</span></td>
|
|
138
|
+
</tr>
|
|
139
|
+
|
|
140
|
+
<tr class="skill-row">
|
|
141
|
+
<td>data-analysis</td>
|
|
142
|
+
<td class="num" data-v="0">0</td>
|
|
143
|
+
<td class="num" data-v="0">0</td>
|
|
144
|
+
<td class="num" data-v="4200">4.2k</td>
|
|
145
|
+
<td class="num" data-v="34">
|
|
146
|
+
<div class="bar" title="34/100"><i style="width:34%"></i></div>
|
|
147
|
+
</td>
|
|
148
|
+
<td class="num" data-v="-1">never</td>
|
|
149
|
+
<td><span class="pill never-fired">never-fired</span></td>
|
|
150
|
+
</tr>
|
|
151
|
+
|
|
152
|
+
<tr class="skill-row">
|
|
153
|
+
<td>changelog-writer</td>
|
|
154
|
+
<td class="num" data-v="0">0</td>
|
|
155
|
+
<td class="num" data-v="0">0</td>
|
|
156
|
+
<td class="num" data-v="1400">1.4k</td>
|
|
157
|
+
<td class="num" data-v="64">
|
|
158
|
+
<div class="bar" title="64/100"><i style="width:64%"></i></div>
|
|
159
|
+
</td>
|
|
160
|
+
<td class="num" data-v="-1">never</td>
|
|
161
|
+
<td><span class="pill never-fired">never-fired</span></td>
|
|
162
|
+
</tr>
|
|
163
|
+
|
|
164
|
+
</tbody>
|
|
165
|
+
</table>
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
<div class="rx">
|
|
169
|
+
<h2>Prescriptions (5)</h2>
|
|
170
|
+
<ul>
|
|
171
|
+
|
|
172
|
+
<li>[warn] <b>ab-test-coach</b> — Invoked 2× but barely used afterward (engagement 1.0). The description may match prompts it shouldn't — tighten it to the cases it actually handles.</li>
|
|
173
|
+
|
|
174
|
+
<li>[warn] <b>data-analysis</b> — Description has no trigger phrasing ('Use when…', 'when the user…'). Activation relies on the model matching intent — make it explicit.</li>
|
|
175
|
+
|
|
176
|
+
<li>[info] <b>data-analysis</b> — Description quality score is 34/100. Add specifics: concrete nouns, examples, the triggering situation.</li>
|
|
177
|
+
|
|
178
|
+
<li>[warn] <b>data-analysis</b> — Never fired but costs ~4200 ctx tokens every session. Improve its description so it activates, or remove it to reclaim the budget.</li>
|
|
179
|
+
|
|
180
|
+
<li>[warn] <b>leakcheck</b> — Dormant 40d but costs ~3100 ctx tokens every session. Improve its description so it activates, or remove it to reclaim the budget.</li>
|
|
181
|
+
|
|
182
|
+
</ul>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
<footer>
|
|
187
|
+
skillvitals · local-only · reads <code>~/.claude</code> · no data leaves your machine
|
|
188
|
+
</footer>
|
|
189
|
+
</div>
|
|
190
|
+
<script>
|
|
191
|
+
function sortTable(col, type){
|
|
192
|
+
var tb=document.querySelector('#vitals tbody');
|
|
193
|
+
var rows=Array.prototype.slice.call(tb.querySelectorAll('tr'));
|
|
194
|
+
var asc = tb.getAttribute('data-col')==col && tb.getAttribute('data-dir')!='asc';
|
|
195
|
+
rows.sort(function(a,b){
|
|
196
|
+
var x=a.children[col], y=b.children[col];
|
|
197
|
+
if(type=='n'){ x=parseFloat(x.getAttribute('data-v')||0); y=parseFloat(y.getAttribute('data-v')||0); return asc?x-y:y-x; }
|
|
198
|
+
x=x.textContent.trim(); y=y.textContent.trim(); return asc?x.localeCompare(y):y.localeCompare(x);
|
|
199
|
+
});
|
|
200
|
+
rows.forEach(function(r){ tb.appendChild(r); });
|
|
201
|
+
tb.setAttribute('data-col',col); tb.setAttribute('data-dir',asc?'asc':'desc');
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
</body>
|
|
205
|
+
</html>
|