fix: cleanStaleSkills preserves git-tracked custom skills - #510
fix: cleanStaleSkills preserves git-tracked custom skills#510PulkitCozar wants to merge 1 commit into
Conversation
|
Run this through the fab-kit pipeline |
|
Example, check the skills here: https://github.com/sahil87/shll/tree/3263707ab2ad0bd3200b6f69a1ad2d820ddf0734/.claude/commands |
|
Checked the shll example (
So keeping this fix, rather than routing everyone to Also — re-running this through the fab-kit pipeline ( |
a676b59 to
0e44ddd
Compare
|
Ran this through fab-kit's own pipeline as requested — change
Same fix as before, now with the duplication cleaned up and docs updated. |
fab sync's stale-skill cleanup treats any .claude/skills (or .opencode/commands, .agents/skills, .gemini/skills) entry not in fab-kit's own canonical skill list as deployment cruft and deletes it — including a project-authored, git-tracked custom skill that was never fab-kit's to manage. This is destructive on every `fab sync` (including the one `wt create` runs automatically as its init script) whenever a repo commits its own skill alongside the fab-kit-deployed ones. cleanStaleSkills now checks whether the candidate entry is tracked by git in the consuming repo (`git ls-files --error-unmatch`) before removing it, via a shared isPreservedByGit helper used by both the directory- and flat-format branches, and skips deletion when tracked. Fails open (git absent/not a repo) to the prior behavior, so existing non-git cleanup semantics are unchanged. Run through fab-kit's own intake -> apply -> review -> hydrate -> ship pipeline (change 260720-4t2a-preserve-git-tracked-skills): a fresh sub-agent review pass flagged the two format branches' duplicated tracking check, folded into isPreservedByGit; hydrate updated docs/memory/distribution/kit-architecture.md to describe the new preservation behavior in the sync pipeline writeup.
0e44ddd to
c5826a9
Compare
Why
fab sync's stale-skill cleanup (cleanStaleSkillsininternal/skills.go) removes any entry under an agent's skill directory (.claude/skills/,.opencode/commands/,.agents/skills/,.gemini/skills/) whose name isn't in fab-kit's own canonical skill list. It doesn't distinguish "not fab-kit's" from "stale" — so a custom skill a project authors and commits to its own git history gets deleted on every sync, including the syncwt createruns automatically as its init script.Reproduced end-to-end against a real consuming repo (a Terraform infra monorepo) that commits one custom Claude Code skill alongside fab-kit's deployed ones: every
wt createsilently deleted the custom skill'sSKILL.md, andgit statusshowed it as a working-tree deletion of a tracked file.What
cleanStaleSkillsnow checks whether a candidate-for-removal entry is tracked by git in the consuming repo before deleting it, and preserves it when it is. Entries that are genuinely stale (not git-tracked — e.g. leftovers from a retired fab-kit skill) are still cleaned up exactly as before.How
New
isGitTracked(repoRoot, relPath string) boolhelper shells out togit ls-files --error-unmatch(sameexec.Command("git", ...)+ fail-open pattern already used elsewhere in this package, e.g.warnIfFabVersionIgnored). Fails open (returns false) when git is unavailable orrepoRootisn't a git work tree, so the historical cleanup behavior for non-git contexts is unchanged — the loosening only ever applies to entries git can positively confirm as tracked.Where
src/go/fab-kit/internal/skills.go—isGitTracked+cleanStaleSkills(both directory- and flat-format branches)src/go/fab-kit/internal/skills_test.go— two new tests mirroring the existingTestCleanStaleSkills_Directory/_Flatstyle, each asserting a git-tracked custom entry survives while an untracked stale entry is still removedVerification
gofmt -l .— cleango vet ./...— cleango test ./... -count=1 -race— all packages pass (cmd/fab,cmd/fab-kit,internal)skills.gofix with the new tests in place — both new tests failed for the expected reason (custom skill deleted); restored the fix — both passgo test): builtfab-kitfrom this branch, ranfab-kit synctwice against a temp repo shaped like the real reproduction case (git-tracked custom skill + one canonical stock skill) — first run preserved the custom skill and deployed the canonical one; second run (with an added untracked leftover skill) removed the untracked stale entry while still preserving the git-tracked custom skillFollow-ups / out of scope
None — this is scoped to
cleanStaleSkills's deletion check only.