Claude Code Kit
Recipes

Validate Skills in CI

Run validate-skills.sh and doctor.sh on every PR via GitHub Actions, so a broken skill never lands on main.

The kit ships two validation scripts:

  • ./scripts/validate-skills.sh — checks every .claude/skills/*/SKILL.md for required frontmatter, required sections, description length, and code examples.
  • ./scripts/doctor.sh — checks installation health (missing files, broken hooks, invalid settings).

Running them locally is good. Running them in CI on every PR is better — a broken skill never lands on main.

Goal

# Pull request opened → validate-skills.sh + doctor.sh run → ✅ or ❌

If either script fails, the PR is blocked from merging.

Recipe — GitHub Actions

Create .github/workflows/validate.yml:

name: Validate

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  validate:
    name: Validate kit installation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Make scripts executable
        run: chmod +x scripts/*.sh .claude/hooks/*.sh

      - name: Validate skills
        run: ./scripts/validate-skills.sh

      - name: Doctor check
        run: ./scripts/doctor.sh

That's it. ~25 lines, no dependencies, no external services.

What this catches

Type of breakCaught by
Skill missing name: or description: in frontmattervalidate-skills.sh
Skill description too long (>200 chars) — won't match well in semantic searchvalidate-skills.sh (warning)
Missing required ## When to Use / ## Process / ## Output Format sectionsvalidate-skills.sh
Skill has no code examplesvalidate-skills.sh (warning)
Hook script not executabledoctor.sh
.claude/settings.json invalid JSONdoctor.sh
Required directory missingdoctor.sh

Add CODEBASE_MAP validation

If you also want to verify CODEBASE_MAP.md placeholders aren't unfilled:

      - name: Validate CODEBASE_MAP
        run: ./scripts/validate.sh CODEBASE_MAP.md

This catches commits where someone added a section but forgot to fill in the [Your Project] placeholder.

Branch protection

Once the workflow is running, require it for merges:

  1. Settings → Branches → Add rule for main
  2. ✅ Require a pull request before merging
  3. ✅ Require status checks to pass before merging
  4. Add Validate kit installation (the job name) as required

Now PRs that fail validation can't merge until fixed.

Run on a schedule

If you want a daily sanity check (catches drift from manual edits to main):

on:
  schedule:
    - cron: '0 4 * * *'  # daily at 04:00 UTC
  pull_request:
    branches: [main]
  push:
    branches: [main]

Run locally as a pre-commit hook

If you don't want to wait for CI:

# .git/hooks/pre-commit (chmod +x after creating)
#!/usr/bin/env bash
./scripts/validate-skills.sh && ./scripts/doctor.sh

Now git commit blocks if validation fails. (.git/hooks/ isn't tracked — every contributor needs to set this up locally, or use husky to share.)

Run in npm scripts

If your repo has a package.json:

{
  "scripts": {
    "kit:validate": "./scripts/validate-skills.sh && ./scripts/doctor.sh",
    "precommit": "pnpm run kit:validate"
  }
}

Then pnpm run kit:validate (or your equivalent) runs both checks locally.

What if validate-skills.sh is too strict?

The validator is intentionally opinionated. If it's flagging skills you consider intentional:

  • Description length warnings — usually right, trim them
  • Missing required sections — add them or set user-invocable: false in frontmatter (validator is more lenient on non-user-invocable skills)
  • No code examples warning — add a \``text` example output block

If a check is genuinely wrong for your use case, fork validate-skills.sh to scripts/project/validate-skills.sh and adjust thresholds. Don't edit the kit-managed copy — it'll get overwritten on --upgrade.

Verification

After adding the workflow:

  1. Push a PR
  2. GitHub Actions tab → confirm "Validate kit installation" runs and passes
  3. Branch protection: confirm it's listed as a required check
  4. Try breaking a skill (delete a required section) → confirm CI fails