Skill Usage Profiler
Generate skill usage reports from tracked data stored at ~/.claude/skill-usage.jsonl.
Generating Reports
Run the bundled report script:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/profile-skills/scripts/report.py" [OPTIONS]
Options:
--period day|week|month|all— Filter by time period (default: all)--top N— Show only top N skills--detail— Open an HTML visualization report in the browser (bar charts + heatmap)
Examples:
- All-time stats:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/profile-skills/scripts/report.py" - This week only:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/profile-skills/scripts/report.py" --period week - Top 5 skills this month:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/profile-skills/scripts/report.py" --period month --top 5 - Visual report:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/profile-skills/scripts/report.py" --detail - Visual report (this week):
python3 "${CLAUDE_PLUGIN_ROOT}/skills/profile-skills/scripts/report.py" --detail --period week
Output Rules
Run the script in a single Bash call — nothing else. The script auto-detects the user's Claude Code verbose setting (~/.claude/settings.json, with ~/.claude/settings.local.json taking precedence) and routes its own output:
verbose: true→ prints the report to stdout. The Bash result panel shows the full table directly.verbose !== true→ writes the report to~/.claude/.cache/skills-cleaner-profile.txtand prints only that path to stdout (so the Bash panel stays compact).
After the script returns:
- If stdout contained the table (the line
Skill Usage Reportappears in the Bash output), stay silent — do not re-paste, summarize, or reformat. - If stdout was just the cache-file path (one line starting with
wrote:), Read that file and paste its contents verbatim as a fenced code block. This becomes the user's only visible output. Don't rephrase or reformat.
Never read ~/.claude/settings.json yourself, never run grep/cat beforehand, and never add commentary before or after unless the user follows up. The script handles verbose detection internally so the user only sees the result.
--out may still be passed explicitly to override the destination path; it skips the verbose auto-routing. The --detail flag opens a browser and keeps a server alive — run it directly, confirm the URL and Ctrl+C to stop, and don't paste anything.
Token / Model / Duration Tracking
The Stop hook records one entry per turn after collecting all skill invocations that fired in that turn. The first invocation in a turn is the root skill; any further skill calls that happened during the same turn are recorded as sub-skills nested under the root.
For each skill (root or sub) the hook captures its own segment:
output_tokens— assistant output tokens whose timestamps fall between this skill's invocation and the next skill's invocation (or the turn's end). Non-overlapping, so summing across rows in a report gives the true total.input_tokens/cache_read_input_tokens/cache_creation_input_tokens— input-side counters from the sameusageblock, summed per segment with the same boundaries. Cache-read and cache-write are kept separate because their effective pricing differs (read ~0.1×, write ~1.25×).model— first model seen in the segment (typically the Claude model ID for the turn).duration_ms— elapsed time from this skill's invocation to the next boundary (next sub-skill invocation, orStopfiring for the last segment).
The report displays a parent's total inclusive of its sub-skills (e.g. 7.0K (brainstorming: 1.2K, writing-plans: 500)) — the parenthesised breakdown is computed from the sub_skills array.
If No Data Found
The tracking hooks log skill invocations to ~/.claude/skill-usage.jsonl. If the file is missing or empty, the hooks may not be configured. Check that ~/.claude/settings.json (or the plugin's plugin.json) has:
- A
PostToolUsehook withSkillmatcher — tracks Claude-initiated skill calls - A
UserPromptSubmithook — tracks user-initiated/skill-namecalls
Both hooks are bundled with this plugin and registered automatically via plugin.json.
Log Format
Each line in skill-usage.jsonl is one JSON object per turn. A turn with only one skill invocation produces a flat entry; a turn with sub-skills nests them under sub_skills.
{"skill":"list-skills","ts":"2026-04-10T03:00:00Z","session":"def456","source":"user","model":"claude-sonnet-4-6","duration_ms":2100,"input_tokens":4,"cache_creation_input_tokens":0,"cache_read_input_tokens":21000,"output_tokens":1234}
{"skill":"skill-creator:skill-creator","ts":"2026-04-27T10:00:00Z","session":"abc","source":"user","model":"claude-opus-4-7","duration_ms":12000,"input_tokens":12,"cache_creation_input_tokens":26000,"cache_read_input_tokens":16700,"output_tokens":4000,"sub_skills":[{"skill":"superpowers:brainstorming","ts":"2026-04-27T10:01:00Z","source":"claude","model":"claude-opus-4-7","duration_ms":10000,"input_tokens":3,"cache_creation_input_tokens":500,"cache_read_input_tokens":40000,"output_tokens":800}]}
source: "claude"— Claude invoked the skill via the Skill toolsource: "user"— User typed/skill-namedirectly- All token / duration fields are own-segment values, not inclusive of sub-skills (sum the row +
sub_skills[*]to get the turn total). sub_skills— present only when more than one skill fired in the turn; ordered by invocation time.
Older log lines without sub_skills or input-side fields are still readable; the report treats missing token fields as zero (rendered as -).