Bug
agents pull deletes all files from the central ~/.agents/skills/ directory on every run.
Root Cause
When an agent's version-specific skills directory is a symlink to the central ~/.agents/skills/ directory (e.g. Gemini's skills -> ~/.agents/skills), the syncResourcesToVersion function in src/lib/versions.ts follows the symlink and destroys the source files.
The problematic code path (around line 1306):
for (const skill of skillsToSync) {
const srcDir = path.join(centralSkills, skill); // ~/.agents/skills/linear
if (!fs.existsSync(srcDir)) continue;
const destDir = path.join(skillsTarget, skill); // symlink -> ~/.agents/skills/linear (SAME directory!)
removePath(destDir); // deletes the CENTRAL copy through the symlink
copyDir(srcDir, destDir); // srcDir is now gone, copies nothing
syncedSkills.push(skill);
}
Sequence:
removePath(destDir) resolves the symlink and deletes ~/.agents/skills/{skill}/ (the central copy)
copyDir(srcDir, destDir) tries to copy from central — but it was just deleted
- Result: empty skill directories in both central and the version home
This affects every skill on every agents pull invocation. The files are git-tracked so they show up as unstaged deletions (git status shows D skills/*/SKILL.md etc.), and the next pull would auto-commit these deletions via the git add -A in pullRepo.
Reproduction
- Have a Gemini version installed where
skills/ is a symlink to central
- Run
agents pull -y
- Check
~/.agents/skills/ — all SKILL.md and script files are gone
git status in ~/.agents/ shows all skill files as deleted
Suggested Fix
In syncResourcesToVersion, before processing skills, check if the target skills directory (or individual skill subdirectory) is a symlink that resolves to the central source. If so, either:
- Skip the sync (it's already pointing at the source — no copy needed)
- Replace the symlink with a real copy before doing the remove+copy cycle
Something like:
const destDir = path.join(skillsTarget, skill);
const srcDir = path.join(centralSkills, skill);
// If dest is (inside) a symlink to source, skip — already in sync
if (fs.lstatSync(skillsTarget).isSymbolicLink()) {
const resolved = fs.realpathSync(skillsTarget);
if (resolved === fs.realpathSync(centralSkills)) {
continue; // symlinked to source, nothing to do
}
}
removePath(destDir);
copyDir(srcDir, destDir);
Or more defensively, check at the parent level before entering the loop:
if (fs.lstatSync(skillsTarget).isSymbolicLink()) {
// Skills dir is a symlink — don't sync, it already points to central
} else {
// ... existing sync loop
}
Impact
- All skill files (SKILL.md, scripts, reference data) deleted from central on every pull
- Skills stop working until manually restored (
git checkout -- skills/)
- If not caught, the next
pullRepo auto-commits the deletions, making them permanent
Bug
agents pulldeletes all files from the central~/.agents/skills/directory on every run.Root Cause
When an agent's version-specific skills directory is a symlink to the central
~/.agents/skills/directory (e.g. Gemini'sskills -> ~/.agents/skills), thesyncResourcesToVersionfunction insrc/lib/versions.tsfollows the symlink and destroys the source files.The problematic code path (around line 1306):
Sequence:
removePath(destDir)resolves the symlink and deletes~/.agents/skills/{skill}/(the central copy)copyDir(srcDir, destDir)tries to copy from central — but it was just deletedThis affects every skill on every
agents pullinvocation. The files are git-tracked so they show up as unstaged deletions (git statusshowsD skills/*/SKILL.mdetc.), and the nextpullwould auto-commit these deletions via thegit add -AinpullRepo.Reproduction
skills/is a symlink to centralagents pull -y~/.agents/skills/— all SKILL.md and script files are gonegit statusin~/.agents/shows all skill files as deletedSuggested Fix
In
syncResourcesToVersion, before processing skills, check if the target skills directory (or individual skill subdirectory) is a symlink that resolves to the central source. If so, either:Something like:
Or more defensively, check at the parent level before entering the loop:
Impact
git checkout -- skills/)pullRepoauto-commits the deletions, making them permanent