Skip to content

fix(skill): dedupe symlinked external skill paths#17294

Open
notzenco wants to merge 1 commit intoanomalyco:devfrom
notzenco:fix/17219-dedupe-skill-symlinks
Open

fix(skill): dedupe symlinked external skill paths#17294
notzenco wants to merge 1 commit intoanomalyco:devfrom
notzenco:fix/17219-dedupe-skill-symlinks

Conversation

@notzenco
Copy link

Issue for this PR

Closes #17219

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When the same external skill is discovered through both a real path and a symlinked path, we were treating them as separate entries while building the skill directory allowlist. That produced duplicate external_directory rules even though both paths pointed at the same physical skill.

This change resolves each discovered skill to its canonical path before storing it, skips repeated physical files, and stores canonical skill directories for the permission allowlist. That removes duplicate rules for symlinked skill installs while keeping distinct physical skills unchanged.

How did you verify your code works?

  • added a regression test that creates a symlinked .claude/skills entry pointing at .agents/skills and checks that Skill.all() and Skill.dirs() only report one canonical location
  • ran bun test test/skill/skill.test.ts test/tool/skill.test.ts
  • ran bun typecheck

Screenshots / recordings

N/A

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Copilot AI review requested due to automatic review settings March 13, 2026 04:35
@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Duplicate Found:

Why they might be related:
Both PRs address deduplication of symlinked skill paths. PR #17245 appears to be a related or overlapping fix in the core module, while the current PR #17294 specifically targets external skill paths. These could be addressing the same underlying issue from different angles, or one may supersede the other. You should verify if they're solving the same problem or if they're complementary fixes.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves skill discovery in packages/opencode by deduplicating skills that are reachable via multiple paths (e.g., symlinked .claude/skills and .agents/skills entries), ensuring Skill.all() / Skill.dirs() don’t double-count the same underlying skill.

Changes:

  • Canonicalize discovered skill file paths via Filesystem.resolve() and dedupe via a seen set.
  • Use canonical paths for location and directory tracking (Skill.dirs()).
  • Add a regression test covering symlinked skill directories deduplication.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/opencode/src/skill/skill.ts Canonicalizes skill paths and deduplicates loading/dirs by resolved path.
packages/opencode/test/skill/skill.test.ts Adds test ensuring symlinked skill dirs are deduped to one skill/dir.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 60 to +65
const addSkill = async (match: string) => {
const md = await ConfigMarkdown.parse(match).catch((err) => {
const file = Filesystem.resolve(match)
if (seen.has(file)) return
seen.add(file)

const md = await ConfigMarkdown.parse(file).catch((err) => {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Permission scanner does not deduplicate symlinked skill directories, doubling ruleset size and log volume

2 participants