File picker doesn't follow symlinks in git repositories
Summary
The @ file picker fails to enumerate contents of symlinked directories when the current working directory is a git repository. The same symlink works correctly in non-git directories.
Environment
- OS: macOS (Darwin 25.1.0)
- Claude Code version: Latest (Opus 4.5)
- Shell: zsh
Steps to Reproduce
- Create a symlink to an external directory:
```bash
mkdir ~/src/test-no-git
mkdir ~/src/test-with-git
mkdir ~/external-content
echo "hello" > ~/external-content/file.txt
ln -s ~/external-content ~/src/test-no-git/linked
ln -s ~/external-content ~/src/test-with-git/linked
cd ~/src/test-with-git && git init
```
- Open Claude Code in the non-git directory:
``bash``
cd ~/src/test-no-git
claude
- Type
@linked/in the input - works correctly, showsfile.txt
- Open Claude Code in the git directory:
``bash``
cd ~/src/test-with-git
claude
- Type
@linked/in the input - fails, doesn't show contents
Expected Behavior
The file picker should enumerate the contents of symlinked directories regardless of whether the current directory is a git repository.
Actual Behavior
- Without
.git: Symlink contents are shown in the file picker - With
.git: Symlink contents are NOT shown in the file picker
Additional Observations
- The "respect .gitignore in file picker" setting does NOT fix this.
- Toggled via
/config - Disabling it has no effect on this bug
- The symlink is not in
.gitignore(shows as??untracked ingit status)
- The symlink itself appears in the picker - you can see
linked/as an option, but selecting it or typing@linked/doesn't show its contents.
- Workarounds that DO work:
- Using absolute paths:
@/full/path/to/linked/file.txt - Using the symlink target directly:
@/path/to/external-content/file.txt - Removing
.gitdirectory (not practical)
Root Cause Hypothesis
The file picker appears to use two different enumeration strategies:
if (isGitRepo) {
// Git-based enumeration (git ls-files, git status, etc.)
// Doesn't follow symlinks pointing outside the worktree
} else {
// Filesystem-based enumeration (fs.readdir)
// Naturally follows symlinks
}
Git intentionally doesn't recurse into symlinks pointing outside the repository. The file picker inherits this limitation when in git mode.
The "respect .gitignore" toggle likely only controls filtering behavior within the git enumeration path - it doesn't switch between git-based and filesystem-based enumeration.
Suggested Fix
When enumerating directory contents for the file picker:
- Use
lstat()to detect symlinks - If an entry is a symlink pointing to a directory, use filesystem-based enumeration for that subtree (even in git mode)
- This preserves git-aware behavior for regular files while properly handling symlinks
Alternatively, provide a separate toggle for "git-aware file picker" vs "filesystem file picker" independent of the gitignore setting.
Use Case
This affects developers who symlink external directories into their projects, such as:
- Linking Obsidian vault folders for project documentation
- Linking shared configuration directories
- Linking monorepo packages during development
- Any workflow where symlinks bridge repository boundaries
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗