skill-tracker

Check and apply upstream updates to installed skills. Reads pending-updates.json generated by the cron checker, spawns haiku agents to evaluate each diff for quality and safety, then spawns sonnet age

SKILL.md

GitHub

/skill-update — Skill Version Tracker & Auto-Updater

Reviews upstream updates for installed skills. Uses haiku agents to evaluate diffs, sonnet agents to apply smart merges. Never overwrites local customizations.


File Locations

${CLAUDE_SKILL_DIR}/
├── source-registry.json    ← tracked repos + last-known commit SHAs
├── pending-updates.json    ← generated by cron check-versions.ts
└── update-history.md       ← log of applied updates

Step 0: Check Pending Updates

cat ${CLAUDE_SKILL_DIR}/pending-updates.json 2>/dev/null

If the file is missing or pending array is empty:

  • Run the checker manually first: npx tsx ${CLAUDE_SKILL_DIR}/check-versions.ts
  • If still empty after running: output "All tracked skills are up to date." and stop.

Show a summary table of pending updates before proceeding:

SKILL                        REPO                          POLICY           COMMITS
humanizer                    blader/humanizer              haiku_evaluate   8b3a → def456 (2026-05-10)
lightpanda                   lightpanda-io/agent-skill     haiku_evaluate   bc56 → 9a3f (2026-05-08)
animejs (+ 12 more)         heygen-com/hyperframes        haiku_evaluate   94b8 → new (2026-05-09)

Step 1: Handle always_prompt Skills First

For any skill with update_policy: "always_prompt" (Vietnamese marketing skills):

  1. Download upstream SKILL.md for each one
  2. Show a unified diff in the terminal
  3. AskUserQuestion: "Update {skill-name}? Diff shown above." Options: ["Apply update", "Skip this one"]
  4. If Apply: proceed to sonnet merge (Step 4) for that skill
  5. If Skip: add to skipped list

Step 2: Handle notify_only Skills

For skills with update_policy: "notify_only" (e.g., markitdown — upstream is a library, not a SKILL.md):

  • Output: "⚠️ {repo} has upstream changes since {date}. SKILL.md is hand-written — review manually if needed."
  • Update installed_commit in registry to latest (so it stops appearing every cycle)
  • Do not attempt to update the SKILL.md

Step 3: Haiku Evaluation — Spawn in Parallel

For all skills with update_policy: "haiku_evaluate":

3a. For update_via: github_api skills — download upstream SKILL.md:

curl -sL "{upstream_skill_url}" -o /tmp/skill-upstream-{skill}.md

3b. For update_via: npx_skills skills — get the upstream content via npx dry-check:

# Check what npx skills update would download
npx skills update {skill-name} --dry-run 2>/dev/null

If dry-run output shows content, use it. Otherwise skip the haiku eval and trust npx.

3c. Spawn haiku agents in parallel (one per skill, all in one message):

Each haiku agent receives this prompt:

You are evaluating whether a skill update should be applied.

LOCAL VERSION (current):
---
{content of local_skill_path}
---

UPSTREAM VERSION (new):
---
{content of /tmp/skill-upstream-{skill}.md}
---

Score this update on 4 axes (1-10 each):
1. new_capabilities: Does upstream add new sections, commands, or patterns not in local?
2. bug_fixes: Does upstream fix incorrect instructions or broken patterns?
3. local_value: Does local have custom content NOT in upstream (integrations, project-specific notes)?
   IMPORTANT: If local_value is high (≥7), this argues AGAINST a FULL update — use MERGE instead.
4. regression_risk: Does upstream REMOVE useful content that local has?

overall_score = (new_capabilities + bug_fixes - regression_risk) / 3

Return ONLY valid JSON (no explanation):
{
  "skill": "{skill}",
  "new_capabilities": N,
  "bug_fixes": N,
  "local_value": N,
  "regression_risk": N,
  "overall_score": N,
  "verdict": "SKIP|MERGE|FULL",
  "reason": "one sentence",
  "new_sections": ["section title or description if any"],
  "preserve_sections": ["section title or description if any"]
}

Verdict rules:
- FULL: overall_score ≥ 7 AND local_value < 4 AND regression_risk < 3
- MERGE: overall_score ≥ 4 AND (local_value ≥ 4 OR regression_risk ≥ 3)  
- SKIP: overall_score < 4 OR (regression_risk ≥ 7 AND local_value ≥ 6)

Collect all haiku verdicts.

3d. Show verdict table:

SKILL          SCORE  VERDICT  REASON
humanizer       6.3   MERGE    Upstream adds 3 new AI-tell patterns; local has org-specific rules
lightpanda      8.1   FULL     Major update: new MCP server config, CDP examples; local is vanilla
animejs         3.2   SKIP     Only timestamp changes, no new content

AskUserQuestion: "Proceed with applying {N} updates? (FULL: {n}, MERGE: {n}, SKIP: {n} already excluded)" Options: ["Yes, apply all", "Review each one", "Skip all for now"]

If "Review each one": for each MERGE/FULL skill, show diff and ask individually.


Step 4: Apply Updates — Sonnet Agents in Parallel

For each skill with verdict MERGE or FULL (after user confirmation):

Spawn one sonnet agent per skill (all in a single message):

You are applying a skill update for "{skill}".

VERDICT: {FULL|MERGE}

LOCAL PATH: {local_skill_path}
UPSTREAM URL: {upstream_skill_url}

LOCAL CONTENT:
---
{local content}
---

UPSTREAM CONTENT:
---
{upstream content}
---

PRESERVE SECTIONS (identified by haiku): {preserve_sections}
NEW SECTIONS (to add from upstream): {new_sections}

INSTRUCTIONS:
1. BACKUP first: copy local file to ~/.claude/skills/_sync/backups/{skill}/{ISO-timestamp}/SKILL.md
2. If verdict=FULL: write upstream content as-is to {local_skill_path}
3. If verdict=MERGE:
   a. Start with upstream as the base
   b. Identify sections in local that are NOT in upstream (local customizations)
   c. Append those local-only sections at the end of the upstream content, under a heading:
      "## Local Additions (preserved from previous version)"
   d. Write the merged content to {local_skill_path}
4. Verify the file was written correctly (read it back)
5. Report: DONE — {skill}: {FULL|MERGE} applied, {n} local sections preserved

DO NOT:
- Delete any section that existed in local and has meaningful content
- Modify the frontmatter name/description/triggers fields without good reason
- Add "## Local Additions" header if there are no local-only sections

Step 5: Update Registry + Log

After all sonnet agents complete:

  1. For each updated skill, find its repo in source-registry.json:

    • Set installed_commit = upstream_commit from pending-updates.json
    • Set installed_commit_date = upstream_commit_date
    • Set latest_upstream_commit = null (cleared — no longer pending)
    • Set last_checked = today
  2. Clear pending-updates.json (set pending: [])

  3. Append to ${CLAUDE_SKILL_DIR}/update-history.md:

## {YYYY-MM-DD}

| Skill | Repo | Verdict | Action |
|---|---|---|---|
| humanizer | blader/humanizer | MERGE | 3 local sections preserved, 2 new patterns added |
| lightpanda | lightpanda-io/agent-skill | FULL | Updated to latest |
| animejs | heygen-com/hyperframes | SKIP | No meaningful changes |

Step 6: Final Summary

✅ SKILL UPDATE COMPLETE

Updated:  humanizer (MERGE), lightpanda (FULL)
Skipped:  animejs, css-animations (no meaningful changes)
Prompted: marketing/01-lich-noi-dung (user skipped)

Registry updated. Next check in ~4 days (cron: 0 2 */4 * *).
Backups at: ~/.claude/skills/_sync/backups/

Manual Run (outside cron)

To check versions right now without waiting for cron:

npx tsx ${CLAUDE_SKILL_DIR}/check-versions.ts

Then invoke /skill-update to review.


Adding New Repos to Track

Edit ${CLAUDE_SKILL_DIR}/source-registry.json:

  1. Add a new entry under repos with the GitHub repo slug as key
  2. Set track: true, update_via: "github_api" or "npx_skills"
  3. Set installed_commit by running: curl -sL "https://api.github.com/repos/{repo}/commits?per_page=1" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d[0]['sha'][:12])"
  4. Run /skill-update to pick it up immediately

What's Not Tracked

  • VoltAgent (56 skills): upstream repo returns 404 — cannot track
  • manual/auto-registered (135 skills): hand-written, no upstream
  • gstack core (53 bundled): use /gstack-upgrade for those