Help us improve
Share bugs, ideas, or general feedback.
From skill-sync
Keep locally installed Claude Code skills in sync with their GitHub repos. Use when the user says "sync my skills", "push skill updates", "which skills are out of date?", "skill-sync", "push my skill changes", "are my skills in sync?", "register my skills", or "init skill registry". Companion to publish-skill (publish = first-time, skill-sync = ongoing maintenance). Do NOT use for creating new skills (use skill-creator), first-time publishing (use publish-skill), or improving skill quality (use schliff).
npx claudepluginhub wan-huiyan/skill-sync --plugin skill-syncHow this skill is triggered — by the user, by Claude, or both
Slash command
/skill-sync:skill-syncThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Detect which locally installed skills have drifted from their GitHub repos,
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Detect which locally installed skills have drifted from their GitHub repos, then push updates with a single command.
/publish-skill/skill-sync init to register the new skill./skill-sync (default: status)Scan all registered skills and show a status table:
Skill Repo Type Status
─────────────────────────────────────────────────────────────────────────────
causal-impact-campaign wan-huiyan/causal-impact-campaign authored IN SYNC
permutation-validation wan-huiyan/permutation-validation authored DIRTY (SKILL.md)
field-notes wan-huiyan/field-notes fork IN SYNC
agent-review-panel wan-huiyan/agent-review-panel authored DIRTY (SKILL.md, eval-suite.json)
/skill-sync initPopulate the registry by scanning GitHub repos + local skill directories:
gh repo list {username} --json name,isFork,url --limit 100~/.claude/skills/*/ directories~/.claude/skills/{name}/gh api repos/{repo}/contents --jq '.[].name'references/, skills/{name}/, .claude-plugin/, docs/, tests/tracked_files (deduplicated, paths relative to repo root)gh repo view {repo} --json parent~/.claude/skill-sync-registry.jsonWhy merge both sources: A file can exist in the GitHub repo but not locally (e.g. README.md generated by
publish-skillbut never pulled). Listing only the local dir silently omits these, causing them to never be synced.
/skill-sync pushPush ALL dirty authored skills to their GitHub repos.
/skill-sync push <skill-name>Push a single skill.
/skill-sync validate or /skill-sync validate <skill-name>Run the test suite before pushing. Acts as a pre-push quality gate.
For each dirty skill (or the named skill):
tests/ directory and package.json exist in the local repocd {repo} && npm test~/Documents/skill-test-templates/backfill.sh {repo} to generate tests, or push with --no-validate"/skill-sync backfill-testsGenerate tests for all registered skills that don't have them yet.
For each skill in the registry that has eval-suite.json but no tests/ directory:
~/Documents/skill-test-templates/backfill.sh {repo}npm test to verifytest: add automated test suite + push to default branchTemplates location: ~/Documents/skill-test-templates/
Stored at ~/.claude/skill-sync-registry.json:
{
"github_username": "wan-huiyan",
"updated_at": "2026-04-02T10:00:00Z",
"skills": {
"causal-impact-campaign": {
"repo": "wan-huiyan/causal-impact-campaign",
"type": "authored",
"tracked_files": ["SKILL.md", "eval-suite.json", "references/code_templates.md"]
},
"field-notes": {
"repo": "wan-huiyan/field-notes",
"type": "fork",
"upstream": "wentingwang21/field-notes",
"tracked_files": ["SKILL.md"]
}
}
}
type values:
authored — user created this skill, push directlyfork — user forked someone else's skill, warn about upstream PR when pushingFor each registered skill:
/tmp/skill-sync-{name} (shallow: --depth 1)diff local_file repo_file/tmp clone after comparisonFor each dirty skill:
tests/ and package.json exist in the repo, run npm test. If tests fail, stop and report. Skip with --no-validate flag./tmp/skill-sync-{name} (full clone for committing)~/.claude/skills/{name}/ to the cloned repo:
SKILL.md → repo root SKILL.mdSKILL.md → skills/{name}/SKILL.md (if that path exists in repo)git add changed filesdocs: sync {name} from local skill updatesgit push origin main/tmp clonedocs: sync {skill-name} from local skill updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When pushing a version update (not just content edits), ensure ALL copies are consistent:
~/.claude/skills/{name}/SKILL.md — local installed copy (frontmatter version)SKILL.md — matches localplugins/{name}/SKILL.md — nested/plugin copy (often forgotten, diverges silently).claude-plugin/plugin.json — version field + keywords.claude-plugin/marketplace.json (or root marketplace.json) — version field per plugin entrypackage.json — version field (if repo has npm tests)eval-suite.json (or plugins/{name}/eval-suite.json) — version fieldREADME.md — version history tableREADME.md — badge row version (if badges exist)references/changelog.md — per-plugin changelog (if exists)CHANGELOG.md — repo-level changelog (if exists alongside references/ copy)ROADMAP.md — version history table (if exists)Common miss: repos with a plugins/ layout have version in BOTH
plugins/{name}/.claude-plugin/plugin.json AND root .claude-plugin/marketplace.json.
The eval-suite.json version is also easy to miss — it lives next to SKILL.md but
has its own independent version field. Run npm test after bumping — manifest
consistency tests will catch any mismatches.
Dynamic badges (github/v/release, github/license, github/last-commit) auto-update
from GitHub API — no action needed on version bump. Just create a new GitHub release:
gh release create v${NEW_VER} --repo ${USERNAME}/${SKILL_NAME} \
--title "v${NEW_VER}" --notes "Release notes here"
Static badges (eval assertions, papers, python version) need manual update:
# Update eval assertion count if eval-suite.json changed
ASSERTION_COUNT=$(python3 -c "import json; print(len(json.load(open('eval-suite.json')).get('assertions',[])))" 2>/dev/null)
if [ -n "$ASSERTION_COUNT" ]; then
sed -i '' "s|badge/eval_assertions-[0-9]*_passed|badge/eval_assertions-${ASSERTION_COUNT}_passed|g" README.md
fi
# Update paper count if bibliography changed
PAPER_COUNT=$(grep -c '^\- \[' references/bibliography.md 2>/dev/null)
if [ -n "$PAPER_COUNT" ]; then
sed -i '' "s|badge/grounded_in-[0-9]*%2B_papers|badge/grounded_in-${PAPER_COUNT}%2B_papers|g" README.md
fi
Detecting badge type: Dynamic badges contain github/v/release or github/license
in the URL. Static badges contain badge/version- or badge/license-. If a repo still
uses static version/license badges, suggest upgrading to dynamic ones.
Verify no stale version strings remain:
grep -rn "old_version" /tmp/skill-sync-{name}/
tracked_files missing GitHub-only files — init scanned only local dir; files created by publish-skill (README.md, marketplace.json, etc.) that were never pulled locally will be silently omitted. Fix: re-run /skill-sync init (which now merges local + remote) or manually add to registry.skills/ copy forgotten — diverges silently from root copy. The sync workflow copies to both locations automatically.~/.claude/skills/ copy N versions behind — if more than 1 version behind, do a full overwrite rather than incremental patchingplugin.json says v1.5 but SKILL.md says v1.0 signals broken process.gh CLI not authenticated: report error, suggest gh auth login/publish-skill first/skill-sync initpublish-skill), creating skills (use skill-creator), or improving quality (use schliff)publish-skill if the skill has no GitHub repo yetSync assumes two same-named copies should be identical and will overwrite one
with the other. That's wrong when the two copies are meant to differ — e.g. a
general skill in ~/.claude/skills/<name> and a domain-specialized fork published
in a marketplace repo (different framing, examples, trigger scope). Syncing would
clobber the specialization.
The fix is not to sync them — give them distinct names. Two skills with the
same name but different bodies is a latent bug regardless of sync:
/plugin install-ed
while the personal copy exists, both land under one identifier — ambiguous which
loads when invoked.So: a specialization gets its own name + a bidirectional See also cross-ref to
the general one (e.g. code-reviewer-subagent-no-bash-blocked-on-pr-diff general ↔
overnight-review-panel-blocked-reviewer-reads-as-clean overnight). Mirrors the
subagent-driven-development ↔ subagent-review-tier-calibration-for-overnight-pr-chains
pattern. For which repo a skill should live in and copy-vs-cross-link, see
skill-portfolio-repo-placement-scan. Only sync copies that are genuinely the SAME skill.