Skip to content

Skill health dashboard

src/skill_health.py scans ~/.claude/skills/ and ~/.claude/agents/ for structural and catalog issues, then produces a JSON or human-readable dashboard. It also self-heals catalog drift — without ever modifying a SKILL.md.

What it checks

For each skill (~/.claude/skills/<name>/SKILL.md) and each agent (~/.claude/agents/<name>.md):

Code Severity Condition
missing-file error Skill directory has no SKILL.md
unreadable error File exists but can't be decoded as UTF-8
no-frontmatter error Missing or malformed --- YAML fence
frontmatter-missing-name error Frontmatter has no name: field
frontmatter-missing-description warning Missing description: (router relevance suffers)
empty-body error Fewer than min_body_lines non-blank lines
over-threshold warning Line count exceeds line_threshold (default 180)

Drift detection

DriftReport cross-references three sources:

  • on-disk entities (skills + agents),
  • ~/.claude/skill-manifest.jsonload[].skill entries,
  • ~/.claude/pending-skills.jsongraph_suggestions[].name and unmatched_signals[].

Anything in the manifest or pending file that doesn't exist on disk becomes an orphan. Orphans are the only thing heal is allowed to touch.

Self-healing

ctx-skill-health heal
  • drops orphaned entries from skill-manifest.json
  • drops orphaned entries from pending-skills.json
  • writes atomically (tempfile.mkstemp + os.replace)
  • never modifies SKILL.md files or agent .md files

If nothing needs healing, prints [heal] nothing to do. and exits 0.

CLI

# Emit a full JSON report
ctx-skill-health scan

# Pretty dashboard
ctx-skill-health dashboard

# CI gate: exit 2 if any error-severity issue or drift is present
ctx-skill-health check --strict

# Apply safe autofixes to manifest + pending
ctx-skill-health heal

Data model

@dataclass(frozen=True)
class Issue:
    code: str
    severity: str      # "warning" | "error"
    message: str

@dataclass(frozen=True)
class EntityHealth:
    name: str
    kind: str          # "skill" | "agent"
    path: str
    lines: int
    has_frontmatter: bool
    issues: tuple[Issue, ...] = ()

@dataclass(frozen=True)
class DriftReport:
    orphaned_manifest: tuple[str, ...] = ()
    orphaned_pending: tuple[str, ...] = ()

@dataclass(frozen=True)
class HealthReport:
    generated_at: float
    entities: tuple[EntityHealth, ...]
    drift: DriftReport
    totals: dict[str, int]

HealthReport.has_errors is true when any entity has severity error or drift is non-empty — that's the single predicate behind check --strict's exit code.

  • Memory anchoring — dead-reference detection for auto-memory notes.
  • src/skill_quality.py (v0.5.0+) — the four-signal quality scorer (telemetry 0.40, intake 0.20, graph 0.25, routing 0.15) that writes per-entity sidecars and surfaces A/B/C/D/F grades. The skill_health CLI above focuses on structural correctness; skill_quality focuses on behavioral quality over time.