Imported from chshzh/claude (
skills/chsh-sk-skill-maintain/SKILL.md). Install upstream withnpx skills add chshzh/claude --skill chsh-sk-skill-maintain. Copyright stays with the author.
chsh-sk-skill-maintain — Daily Skill Health Audit
Systematic review of all Agent Skills. Finds structural violations, duplication, stale content, missing cross-references, and security issues (credential leaks, sensitive config files not in .gitignore). Produces a prioritized fix list.
Rule authoring: This skill uses the rules defined in
chsh-sk-skill-createas the quality standard. Read that skill and itsprinciples.mdbefore evaluating any SKILL.md.
Step 0 — Orient (mandatory)
Read the vault's own conventions before any check:
VAULT=~/.claude/skills
read_file "$VAULT/SCHEMA.md" # quality thresholds, category taxonomy, link conventions
read_file "$VAULT/index.md" # full skill inventory
read_file "$VAULT/log.md" offset=<last 20> # recent create/update/delete activity
Capture from SCHEMA.md:
- Quality thresholds (line count, description length, required sections)
- Category taxonomy (for index.md completeness check)
- Link conventions
Early-exit: skip if nothing changed
LAST_AUDIT_COMMIT=$(grep '| (review) |' ~/.claude/skills/log.md | tail -1 | grep -oP '`\K[0-9a-f]{7,}(?=`)' || true)
CURRENT_COMMIT=$(git -C ~/.claude/skills rev-parse --short HEAD 2>/dev/null || echo "unknown")
DIRTY=$(git -C ~/.claude/skills status --porcelain | grep -v 'log\.md' || true)
if [ -n "$LAST_AUDIT_COMMIT" ] && [ "$LAST_AUDIT_COMMIT" = "$CURRENT_COMMIT" ] && [ -z "$DIRTY" ]; then
echo "Skills repo unchanged since last audit ($CURRENT_COMMIT). Nothing to do."
# exit early — no report needed
fi
If the commit matches and there are no uncommitted changes (other than log.md's expected dirty state from the previous audit), stop and report "no changes since last audit". Only proceed past this point if the repo has new commits or non-log.md uncommitted changes.
Step 0c — Discover All Skills
# Personal skills (primary target)
find ~/.claude/skills -name "SKILL.md" | sort
# Cursor built-in skills (read-only — for deduplication check only)
find ~/.cursor/skills-cursor -name "SKILL.md" | sort
Build a flat list of (skill_name, path, line_count) tuples.
Step 0d — Index Integrity
~/.claude/skills/index.md is the single source of truth for skill inventory.
INDEX=~/.claude/skills/index.md
# Extract skill directory names from index.md links: [name](dir/SKILL.md)
grep -oP '\]\(\K[^/]+(?=/SKILL\.md)' "$INDEX" | sort > /tmp/index_skills.txt
# Every skill listed in index.md must exist on disk with matching name: field
while read name; do
dir=~/.claude/skills/$name
[ -d "$dir" ] || { echo "P1 MISSING DIR: $name"; continue; }
[ -f "$dir/SKILL.md" ] || { echo "P1 MISSING SKILL.md: $name"; continue; }
declared=$(grep "^name:" "$dir/SKILL.md" | sed 's/^name:[[:space:]]*//')
[ "$declared" = "$name" ] || echo "P0 NAME MISMATCH: index=$name SKILL.md=$declared"
done < /tmp/index_skills.txt
# Every skill directory on disk must appear in index.md
find ~/.claude/skills -mindepth 2 -maxdepth 2 -name "SKILL.md" | while read f; do
name=$(basename "$(dirname "$f")")
grep -q "($name/SKILL.md)" "$INDEX" || echo "P1 NOT IN INDEX: $name"
done
| Issue | Severity |
|---|---|
| Skill in index, directory missing | P1 |
| Skill on disk, missing from index | P1 |
name: in SKILL.md doesn't match directory |
P0 |
| Index skill count in header wrong | P2 |
| Index "Last updated" older than newest SKILL.md | P2 |
Flag each finding as P1/P0. Apply index fixes in Step 9.
Step 1 — Structure Check (per skill)
For each personal skill, verify against chsh-sk-skill-create rules:
| Check | Pass condition | Severity |
|---|---|---|
| YAML frontmatter present | First 3 lines start with --- |
P0 |
name field present |
name: key exists |
P0 |
name ≤ 64 chars, lowercase + hyphens |
Regex ^[a-z0-9-]{1,64}$ |
P1 |
description ≤ 1024 chars |
Character count | P1 |
| Description uses routing trigger | Starts with "Load when" or equivalent routing phrase; does NOT describe what the skill does | P1 |
| Description ≤ 50 words | Word count | P1 |
| Skill has Gotchas section | ## Gotchas heading present in body |
P1 |
| No duplicate section headings | Each ## heading appears exactly once |
P1 |
Self-Update Policy follows canonical 3-step form |
Contains "end of each conversation", 3 numbered steps, step 2 uses AskQuestion with approve/select/skip options, ends with "Do not modify this skill mid-conversation" |
P1 |
Decision Flow present for workflow skills |
If skill has multiple modes or a sequential branching flow (detectable by ## Mode headings or a Step 0 detect-context block), a ## Decision Flow section with ASCII tree exists before the first --- |
P1 |
| SKILL.md body ≤ 500 lines | wc -l |
P2 |
| No Windows-style paths | No \ path separators |
P2 |
| No time-sensitive info | No phrases like "before August 202X" | P2 |
| File references one level deep | Linked files are direct children, not deeply nested | P2 |
| Pascal test (manual) | No section consisting of generic best practices the model already knows — flag and propose cutting | P2 |
No hardcoded /Users/ paths |
grep -r "/Users/" SKILL.md returns empty |
P0 |
No repeated ~/.claude/skills/<name>/ in multi-command blocks |
Multi-command blocks use a SKILL= variable; single-command lines may keep the full ~ path |
P1 |
| Own-file text refs are relative | Markdown text mentioning a file in the same skill uses relative path, not absolute | P1 |
Detect duplicate headings:
# Flag any ## heading that appears more than once in the same SKILL.md
find ~/.claude/skills -name "SKILL.md" | while read f; do
dupes=$(grep -E '^## ' "$f" | sort | uniq -d)
[ -n "$dupes" ] && echo "DUPLICATE HEADINGS in $f: $dupes"
done
For each duplicate found: keep the richer instance, remove the stub. A stub is a section with only a TODO comment or generic boilerplate.
Step 2 — Size Check
Skills over 500 lines are candidates for splitting.
For each oversized skill:
- Read the skill body
- Identify natural split points: separate workflow modes, distinct use cases, long reference tables
- Propose a split plan: which sections → which sub-skills
- Check if the skill already uses progressive disclosure (links to sub-files) — if yes, the main SKILL.md may still be fine even if total lines are high
Split threshold: 500 lines in SKILL.md. Sub-skill files (e.g. wifi/SKILL.md) are excluded from this count.
Step 3 — Deduplication Check
Identify skills that overlap in scope:
- For each skill, extract its purpose from the description + first 20 lines
- Compare against:
- All other personal skills
- Cursor built-in skills in
~/.cursor/skills-cursor/ - Active MCP servers (check
~/.cursor/mcp.jsonor workspace MCP config)
Flag when:
- Two skills cover the same task with no clear division (e.g. two "git commit" skills)
- A skill duplicates capability already in an MCP tool (e.g. a skill for GitHub operations when
user-githubMCP is enabled) - A skill covers functionality owned by another skill without referencing it
For each duplicate pair, suggest:
- Which skill should be the canonical owner
- What cross-reference to add to the non-owner
- Whether one can be deleted
Step 4 — Dead Link Check
Check every relative markdown link in every SKILL.md resolves to an existing file.
Quick Scan (per skill, bash)
# From the skill's directory, check that each linked file exists
grep -oP '\[.*?\]\(\K[^)]+' SKILL.md | while read -r f; do
[ -f "$f" ] || echo "DEAD: $f"
done
Comprehensive Scan (all skills, Python)
Run the reusable script at scripts/check-deadlinks.py from the skill directory:
python3 scripts/check-deadlinks.py
The script extracts every relative link, resolves it against the SKILL.md's own directory, and reports broken targets. It also marks links inside code blocks as likely-intentional examples.
Pitfalls
- Doubled-path links: When a SKILL.md lives in a subdirectory (e.g.
protocols/SKILL.md), a link like(protocols/webserver/SKILL.md)resolves toprotocols/protocols/webserver/SKILL.md— the author mistakenly wrote the path from the repo root instead of relative to the file's own directory. The correct link is(webserver/SKILL.md). - Code-block false positives: Links inside triple-backtick code blocks or
inline codeare illustrative examples, not real references. The scan should flag them but note they are likely intentional. - Cross-repo wiki links: Links pointing to
../../wiki/...may reference wiki pages that were never created or were deleted. These should be treated as P0 since the wiki and skills repos may drift independently. - Anchor-only links (
#section) are valid and should not be flagged.
Verification Pass
After fixing dead links, re-run the comprehensive scan and confirm zero broken links remain before declaring the fix complete.
Step 5 — Cross-Reference Gap Check
When skill A mentions a topic that skill B owns, A should reference B.
Patterns to detect:
- A skill mentions "debug" or "UART" without referencing
chsh-sk-ncs-3-dev-debug - A skill mentions "git commit" or "push" without referencing
chsh-sk-ncs-git-commit - A skill mentions "release" or "tag" without referencing
chsh-sk-ncs-git-release - A skill mentions "PRD" or "requirements" without referencing
chsh-sk-ncs-1-pm-prd - A skill mentions "specs" without referencing
chsh-sk-ncs-2-dev-spec - A skill talks about "QA" or "test report" without referencing
chsh-[REDACTED openai-key]
For each gap, suggest a one-line addition to the Related Skills table.
Also verify every backtick-referenced skill name in every SKILL.md resolves on disk:
# Extract all `chsh-sk-*` names from every SKILL.md and check they exist
find ~/.claude/skills -name "SKILL.md" | while read f; do
grep -oP '`\Kchsh-sk-[a-z0-9.-]+(?=`)' "$f" | while read ref; do
[ -d ~/.claude/skills/$ref ] || echo "DEAD REF in $f: $ref"
done
done
Flag unresolvable references as P1.
Step 6 — Freshness Check
Flag potentially stale content:
- Hard-coded NCS version strings (e.g.
v3.2.0) — check if the workspace uses a newer version - References to old board names (e.g.
nrf54l15dkinstead ofnrf54lm20dk) - Deprecated commands (e.g.
nrfjproginstead ofnrfutil device) Self-Update Policysection missing — this signals a skill that was never updated after creation
Step 7 — Security Scan
Run chsh-sk-security-scan across all skill files:
SKILL=~/.claude/skills/chsh-sk-security-scan
python3 $SKILL/scripts/scan.py dir ~/.claude/skills/
| Exit code | Verdict | Severity in report |
|---|---|---|
| 0 | CLEAN |
— |
| 1 | BLOCK |
P0 — must fix before next commit |
| 2 | WARN |
P1 — review required; may be intentional lab docs |
See chsh-sk-security-scan for the full pattern list and known false positives.
Config file audit
Skill directories may contain config.json (first-run user setup: API keys, server addresses). These are high-risk for accidental commits.
find ~/.claude/skills -name "config.json" | while read f; do
echo "=== $f ==="; cat "$f"
done
For each config.json found: verify it is listed in ~/.claude/.gitignore. If not, flag P0.
Gitignore coverage check
Verify ~/.claude/.gitignore contains at minimum:
skills/**/config.json— all first-run user setup files at any nesting levelskills/chsh-sk-skill-maintain/report-*.md— audit reports (may expose skill internals)- Any skill-specific known-sensitive files not already matched by
skills/**/config.json
Step 8 — Generate the Review Report
Produce a Markdown report at report-YYYY-MM-DD.md in this skill's directory:
# Skill Review — YYYY-MM-DD
## Summary
- Skills reviewed: N
- Issues found: N (P0: N, P1: N, P2: N)
- Recommended actions: N
## P0 Issues (must fix)
| Skill | Issue | Fix |
|-------|-------|-----|
| ... | ... | ... |
## P1 Issues (should fix)
...
## P2 Issues (nice to fix)
...
## Split Candidates
| Skill | Lines | Proposed sub-skills |
|-------|-------|---------------------|
| ... | ... | ... |
## Deduplication Candidates
| Skill A | Skill B | Overlap | Recommendation |
|---------|---------|---------|----------------|
| ... | ... | ... | ... |
## Cross-Reference Gaps
| Skill | Missing reference | Add to section |
|-------|------------------|----------------|
| ... | ... | ... |
## Dead Links
| Skill | Broken path |
|-------|-------------|
| ... | ... |
## Security Issues
| Skill / File | Pattern matched | Action required |
|-------------|----------------|----------------|
| ... | ... | ... |
Step 9 — Apply Fixes (with approval)
Present the report summary, then call AskQuestion:
AskQuestion:
prompt: "Apply fixes from the audit?"
options:
- "Apply P0 fixes only"
- "Apply P0 + P1 fixes"
- "Show me each fix for individual approval"
- "Report only — no changes"
For each approved fix:
- Apply using StrReplace (prefer) or Write
- Log what was changed in the report under
## Applied Fixes - Mark the issue resolved
- After all fixes are applied (but before committing), append a row to
~/.claude/skills/log.md:| YYYY-MM-DD | (review) | N issues fixed: <brief summary> | \` |Use` as a literal placeholder for now. - Commit all changed skill files and
log.mdtogether viachsh-sk-ncs-git-commit. - After the commit, get the new HEAD and update the placeholder in-place — do not commit this update:
Leave log.md dirty. The early-exit check on the next audit run excludes log.md from its dirty-state test, so this uncommitted hash update is the expected resting state.HASH=$(git -C ~/.claude/skills rev-parse --short HEAD) sed -i '' "s/<short-commit>/$HASH/" ~/.claude/skills/log.md
For split or delete operations, always ask per-item:
AskQuestion:
prompt: "<skill-name>: <proposed split/delete action>. Proceed?"
options:
- "Yes — apply"
- "No — skip"
- "Modify — describe change"
Fix Templates
P1-A — description doesn't start with routing trigger: Rewrite as two sentences: trigger first, then what the skill does.
OLD: "Processes PDF files and extracts form fields. Use when the user mentions PDF."
NEW: "Use when the user mentions PDF, form extraction, or document merging. Extracts form fields from PDF files using pdfplumber."
Rule: move the "Use when"/"Load when" clause to position 1. Preserve all original content; do not shorten or summarize.
P1-B — missing ## Gotchas section:
Insert before ## Self-Update Policy (or before ## Related Skills if no Self-Update exists):
## Gotchas
- TODO: add one entry per real observed failure or routing false-positive
P1-C — Self-Update Policy section is missing, abbreviated, or non-canonical:
Replace with the canonical template from chsh-sk-skill-create § Canonical Templates. Keep the domain-specific first paragraph; step 2 must use AskQuestion with approve/select/skip options (not plain text); the "Do not modify" line must be verbatim.
P2 — SKILL.md over 500 lines:
- Identify the largest self-contained section (workflow mode, reference table, long example).
- Create
references/<topic>.mdin the same skill directory. - Move the section content there verbatim.
- Replace the section in SKILL.md with a 3-5 line stub:
## Mode X — <Name> See [`references/<topic>.md`](references/<topic>.md) for <summary of what moved>. > **Quick tip**: <one-liner most commonly needed> - Verify
wc -l SKILL.mdis now under 500.
Brainstorm: What Else Can This Skill Do
The following are ideas for future expansion — not yet implemented:
Transcript Mining
Scan ~/.cursor/projects/*/agent-transcripts/*.jsonl for patterns:
- Tasks the agent struggled with → candidate for a new skill
- Repeated manual steps the agent improvised → candidate for documenting in an existing skill
- Corrections the user made frequently → candidate for a rule update
New Skill Proposals
Based on transcript analysis, propose new skills with:
- Title, description, trigger conditions
- Which existing skills or MCPs it would complement
Skill Usage Frequency
Track which skills appear in recent transcripts — skills never referenced may be stale or poorly described.
MCP Overlap Audit
Cross-check all enabled MCP tools with skill descriptions to find redundant skills that should be retired in favor of the MCP.
Terminology Consistency Report
Extract key terms from all skills and flag inconsistencies (e.g. "VCOM1" vs "VCOM port 1", "prj.conf" vs "project config").
Changelog Tracking
Check if skills with Self-Update Policy sections were last updated recently (via git blame). Flag skills that haven't been touched in >90 days but cover active workflows.
Related Skills
| Task | Skill |
|---|---|
| Author a new skill | chsh-sk-skill-create |
| Commit reviewed skill fixes | chsh-sk-ncs-git-commit |
| Push to claude repo | chsh-sk-ncs-git-commit (Step 5 — push) |
Gotchas
- False positives in security scan: Credential patterns inside illustrative code blocks (e.g.
password = "your-password-here") are documentation, not leaks. Always read the surrounding context before flagging. - Intentional placeholder links:
path.md,reference.md,examples.mdinchsh-sk-skill-createandchsh-sk-llm-wiki-maintainare illustrative template placeholders, not dead links. - P1-A rewrites must preserve content: When fixing a description to start with the routing trigger, keep all original information — do not shorten, summarize, or drop details.
- P2 requires extraction, not deletion: A 600-line skill is not fixed by cutting content; the content must move to
references/so agents can still load it when needed. - Private IPs in router-control/hermes-setup are intentional:
192.168.75.30is a lab VM address documented inchsh-sk-router-control. Flag it in the report but do not auto-remove.
Self-Update Policy
At the end of each conversation, review what was discovered and check whether any check criteria, severity levels, fix templates, or false-positive patterns need updating based on what was found in this audit.
If updates are warranted:
- Collect all proposed changes with a brief rationale for each.
- Call
AskQuestion:AskQuestion: prompt: "Apply these self-update changes to chsh-sk-skill-maintain?" options: - "Yes — apply all" - "Yes — apply selected (describe below)" - "No — skip for now" - Apply approved updates immediately.
Do not modify this skill mid-conversation unless the user explicitly asks.