| ... | @@ -147,16 +147,18 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( | ... | @@ -147,16 +147,18 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( |
| 147 | if (!resolvedDirectory) { | 147 | if (!resolvedDirectory) { |
| 148 | return; | 148 | return; |
| 149 | } | 149 | } |
| 150 | if ( | 150 | if (visitedDirectories.has(resolvedDirectory)) { |
| 151 | visitedDirectories.has(resolvedDirectory) || | | |
| 152 | (resolvedDirectory !== rootDirectory && | | |
| 153 | !resolvedDirectory.startsWith(`${rootDirectory}${path.sep}`)) | | |
| 154 | ) { | | |
| 155 | return; | 151 | return; |
| 156 | } | 152 | } |
| 157 | visitedDirectories.add(resolvedDirectory); | 153 | visitedDirectories.add(resolvedDirectory); |
| | 154 | // A symlink whose target lives outside the root is a skill package |
| | 155 | // boundary: read its own SKILL.md so linked skill libraries show up, but |
| | 156 | // never walk the target tree. |
| | 157 | const insideRoot = |
| | 158 | resolvedDirectory === rootDirectory || |
| | 159 | resolvedDirectory.startsWith(`${rootDirectory}${path.sep}`); |
| 158 | | 160 | |
| 159 | const skillPath = path.join(resolvedDirectory, "SKILL.md"); | 161 | const skillPath = path.join(directory, "SKILL.md"); |
| 160 | const skillInfo = yield* orUndefined(fileSystem.stat(skillPath), input.budget); | 162 | const skillInfo = yield* orUndefined(fileSystem.stat(skillPath), input.budget); |
| 161 | if (skillInfo?.type === "File") { | 163 | if (skillInfo?.type === "File") { |
| 162 | let frontmatter: CursorSkillFrontmatter | undefined = { cliVisible: true }; | 164 | let frontmatter: CursorSkillFrontmatter | undefined = { cliVisible: true }; |
| ... | @@ -167,7 +169,7 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( | ... | @@ -167,7 +169,7 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( |
| 167 | frontmatter = parseSkillFrontmatter(contents); | 169 | frontmatter = parseSkillFrontmatter(contents); |
| 168 | } | 170 | } |
| 169 | } | 171 | } |
| 170 | const name = path.basename(resolvedDirectory).trim(); | 172 | const name = path.basename(directory).trim(); |
| 171 | if (frontmatter?.cliVisible && name) { | 173 | if (frontmatter?.cliVisible && name) { |
| 172 | skills.push({ | 174 | skills.push({ |
| 173 | name, | 175 | name, |
| ... | @@ -184,7 +186,10 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( | ... | @@ -184,7 +186,10 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( |
| 184 | } | 186 | } |
| 185 | } | 187 | } |
| 186 | | 188 | |
| 187 | const entries = yield* orUndefined(fileSystem.readDirectory(resolvedDirectory), input.budget); | 189 | if (!insideRoot) { |
| | 190 | return; |
| | 191 | } |
| | 192 | const entries = yield* orUndefined(fileSystem.readDirectory(directory), input.budget); |
| 188 | if (!entries) { | 193 | if (!entries) { |
| 189 | return; | 194 | return; |
| 190 | } | 195 | } |
| ... | @@ -194,7 +199,7 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( | ... | @@ -194,7 +199,7 @@ const discoverSkillsInRoot = Effect.fn("discoverCursorSkillsInRoot")(function* ( |
| 194 | return; | 199 | return; |
| 195 | } | 200 | } |
| 196 | input.budget.remainingEntries -= 1; | 201 | input.budget.remainingEntries -= 1; |
| 197 | const child = path.join(resolvedDirectory, entry); | 202 | const child = path.join(directory, entry); |
| 198 | const info = yield* orUndefined(fileSystem.stat(child), input.budget); | 203 | const info = yield* orUndefined(fileSystem.stat(child), input.budget); |
| 199 | if (info?.type !== "Directory") continue; | 204 | if (info?.type !== "Directory") continue; |
| 200 | if (depth >= MAX_SKILL_DEPTH) { | 205 | if (depth >= MAX_SKILL_DEPTH) { |