← Back to PRs

#3564: fix(plugins): follow symlinks in plugin and hook discovery

by mollywires open 2026-01-28 20:46 View on GitHub →
Symlinked extension and hook directories weren't being discovered. `readdir` with `withFileTypes` returns `Dirent` objects where `isDirectory()` is false for symlinks, even when the target is a directory — so they get silently skipped. Added `isSymbolicLink()` checks in three places: - `src/plugins/discovery.ts` — plugin/extension discovery - `src/hooks/workspace.ts` — hook discovery - `src/security/audit-extra.ts` — extension security audit Other `isDirectory()` calls in `src/` either use `fs.statSync` (follows symlinks already) or operate on internal state dirs that wouldn't be symlinked. Dangling or non-directory symlinks are handled gracefully by existing null checks downstream (`readPackageManifest` returns null on any failure). Related: #2961 - [x] AI-assisted - [x] Tested locally with symlinked extensions <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR updates plugin/extension and hook discovery to treat symlinked entries returned by `readdir({ withFileTypes: true })` as candidates, since `Dirent.isDirectory()` is `false` for symlinks even when they point at directories. The same adjustment is applied to the security audit’s scan of the extensions directory so symlinked extension directories are counted. Overall this brings discovery behavior closer to codepaths that already rely on `stat` (which follows symlinks), improving support for monorepos and setups that symlink extension/hook directories into the expected locations. <h3>Confidence Score: 4/5</h3> - This PR is likely safe to merge and should fix the reported symlinked-directory discovery gap. - Changes are small and localized (additional `isSymbolicLink()` checks) and align with the stated root cause. The main remaining risk is behavioral edge cases (symlink-to-file vs symlink-to-dir) and a possible false positive in the security audit count, but no obvious runtime-breakers were introduced. - src/plugins/discovery.ts and src/security/audit-extra.ts <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> **Context used:** - Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8)) - Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13)) <!-- /greptile_comment -->

Most Similar PRs