anthropics / anthropics/claude-code
[BUG] ~/.claude/commands/ (symlinked onto Windows drvfs) entirely invisible to non-interactively-launched sessions
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
## Environment
- Claude Code **v2.1.238**
- WSL2 (Ubuntu on Windows 11), NTFS via drvfs
- `~/.claude/commands` is a **symlink** to a Windows-side path: `/mnt/c/Users//.claude/commands` (a common WSL2 setup — the Windows-side Claude Code install and the WSL2-side one share one `.claude` profile via symlink)
## Summary
The entire `~/.claude/commands/*.md` directory is invisible to command loading for sessions launched non-interactively (via `tmux new-session -d ... claude ...`, no TTY attach at launch). Neither `/help → Custom commands`, nor direct autocomplete (`/housekeeping`, `/xyz`), nor fuzzy-match finds *any* file in that directory — including a brand-new, minimal, correctly-frontmattered test file added specifically to rule out a content-specific trigger.
This may be a different manifestation of the same root cause as #85786 (case-insensitive `SKILL.md` lookup on Windows filesystems swallowing sibling commands) — same environment class (Windows-mounted, case-insensitive `.claude/commands/`) — but the exact trigger differs: there is no `skill.md`/`SKILL.md` file anywhere in the affected directory, and the failure is total (every top-level file), not a "phantom package + hidden siblings" pattern.
## Repro
```bash
# On the WSL2 side, ~/.claude/commands is a symlink onto the Windows-side profile:
$ stat ~/.claude/commands
File: /home//.claude/commands -> /mnt/c/Users//.claude/commands
# Add a minimal, valid command file:
$ cat > ~/.claude/commands/probe.md <<'EOF'
---
description: Minimal probe command.
---
Reply with PROBE_OK.
EOF
# Launch a session non-interactively (no TTY at launch, as a driver/automation would):
$ tmux new-session -d -s probe-test -c /some/project/dir claude --dangerously-skip-permissions
# (accept the one-time Bypass Permissions dialog if prompted)
# Attach or send-keys to check:
$ tmux send-keys -t probe-test '/probe' Enter
# → "No commands match" — /probe is not found.
$ tmux send-keys -t probe-test '/help' Enter
# → Custom commands tab lists zero entries from ~/.claude/commands/.
```
A **project-level** `.claude/commands/probe.md` (a real, non-symlinked directory under the launched session's `project_dir`, on the same drvfs mount) loads correctly in the same environment, is found by both `/help` and direct autocomplete, and executes correctly — ruling out drvfs/case-insensitivity as a blanket explanation and pointing specifically at how the **symlinked, user-level** `~/.claude/commands` is (or isn't) resolved/scanned.
## What I've ruled out
- **Not file-count-limited**: only 27 of 28 files were missing; the one survivor (`/close-session`) turned out to be shadowed by an unrelated project-level override, not actually loaded from `~/.claude/commands/` at all.
- **Not a stale index from before the file existed**: reproduced with a session spawned *after* the probe file was created.
- **Not YAML/frontmatter malformation**: probe file frontmatter is minimal and valid; other pre-existing files in the same directory have valid frontmatter too (verified `name:`/`description:` present on all but one unrelated file).
- **Not the exact #85786 trigger**: no `skill.md`/`SKILL.md` file exists anywhere in the directory.
## Possibly relevant
- The symlink target itself is fully readable/valid from the WSL2 side — `cat`, `ls`, and file timestamps all work normally on `~/.claude/commands/*.md` from a shell. The failure is specific to whatever Claude Code's own command-discovery step does with that path.
- This is a very recently (same-day) auto-updated install (2.1.237 → 2.1.238 via background auto-update); I don't have a clean A/B on whether 2.1.237 exhibited the same behavior for non-interactively-launched sessions specifically, since a separate, unrelated bug (a stuck one-time "Bypass Permissions" confirmation dialog) was independently blocking this exact class of launch until immediately before this was found.
## Expected
`~/.claude/commands/*.md` should be discovered and loaded the same way for a `tmux`-launched, `--dangerously-skip-permissions` session as for a normal interactive terminal launch, regardless of whether the directory is reached via a symlink onto a case-insensitive filesystem.
## Workaround in use
Mirroring the affected command files into the session's project-level `.claude/commands/` (which loads reliably) instead of relying on the user-level directory.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reproducing the non-interactive tmux launch and compare discovery from the symlinked ~/.claude/commands directory with the project-level .claude/commands directory. Inspect the command-discovery path for user-level symlink resolution on WSL2/Windows-mounted storage; done means the /help listing and autocomplete find and execute the user-level .md files in the non-interactive session.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, python, ubuntu
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100