Repository navigation
fix(skills): warn and skip the sweep of a symlinked skills root instead of failing - #3299
Merged
Merged
Conversation
generate --delete threw when a tool's skills root was a symbolic link, even one staying inside the output root (a dotfiles checkout linked from the home directory), failing the run after the writes had landed. The root's orphan sweep is now skipped with a warning instead; it is still not swept through the link, so entries outside rulesync-managed outputs are never deleted. Closes #3284 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4MwhqMc6s4ct5MzMReGJj
Skipping the skills root in the directory sweep let the per-directory orphan file sweep run, and it still swept generated skill directories through the linked root, deleting hand-placed files the warning said were kept. Guard each candidate's root there as well. Apply the same warn and skip to a symlinked subagents root, which still failed the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4MwhqMc6s4ct5MzMReGJj
Owner
Author
|
@dyoshikawa Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
rulesync generate --global --deleteexited 1 when a tool's skills root was a symbolic link, even one that stays inside the output root (e.g.~/.cursor/skills -> ~/dotfiles/cursor-skills). The generated skills were already written through the link, and then the orphan sweep threwRefusing to write through a symbolic link, failing the run (also under--dry-run).SkillsProcessor.loadExistingSkillsRootsnow catches the guard's refusal, logsSkipping the orphan sweep for <root>; nothing under it is deleted: ..., and leaves that root out of both halves of the sweep. The run exits 0 and writes still land.The per-directory orphan file sweep (
DirFeatureProcessor.removeOrphanFilesInAiDirs) now applies the same guard to each candidate's root, not only to the candidate directory. Without that, it would still sweep the generated skill directories through the linked root. The same setup for subagents (~/.claude/agentslinked into a dotfiles checkout) also failed the run, soSubagentsProcessorgets the same warn-and-skip.Decision: keep refusing to sweep through the link (option 2 from the issue thread)
The issue thread offered two options: (1) sweep through a link that stays inside the output root, or (2) keep refusing, but warn and skip that root's sweep instead of failing the run. This PR implements option 2.
Rationale: a link inside the output root can still point at a directory rulesync does not manage, such as a project folder or another tool's skills tree. Sweeping through it would delete those entries as orphans. The e2e test from #2363 (
.kimi-code/skillslinked to a project directory) pins that deletion as forbidden. Option 2 keeps that safety property. It only changes how the refusal is reported: a warning instead of a fatal error raised after the writes have already landed. This also matches how a symlinked skill directory inside a real skills root is already handled (warn and skip).Changes
src/features/skills/skills-processor.ts: warn and skip a skills root that failsassertWritablePathInsideRootinstead of throwing.src/types/dir-feature-processor.ts: guard the candidate's root as well before sweeping files inside a generated directory.src/features/subagents/subagents-processor.ts: warn and skip a symlinked subagents root instead of throwing.src/e2e/e2e-skills.spec.ts: the feat: add Kimi Code support #2363 test now expects the run to succeed with the warning, and still checks that the protected skill is kept. A new global e2e test covers the issue's reproduction (.cursor/skillslinked inside the home dir) with and without--dry-run: exit 0, the warning is logged, the stale skill and a hand-placed file inside a generated skill directory are kept, and the new skill is written only without--dry-run.src/e2e/e2e-subagents.spec.ts: global e2e test for a linked~/.claude/agents.src/e2e/e2e-helper.ts:runGenerategains adryRunoption.src/features/skills/skills-processor.test.ts: unit test for the skipped root.docs/reference/file-formats.md(+ regenerateddocs-content.ts): documents the--deletebehavior for a linked skills or subagents directory.Out of scope: when a parent directory is the link (e.g.
~/.cursor -> ~/dotfiles/cursor), or when the link is a commands directory, the guard still sweeps through it. That behavior predates this PR and is unchanged here.Closes #3284
🤖 Generated with Claude Code
https://claude.ai/code/session_01Y4MwhqMc6s4ct5MzMReGJj