[BUG] Windows: project-scope `.claude/skills` plugins are silently suppressed in the VS Code extension when workspace trust was accepted from a terminal with a different drive-letter case
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On Windows, the per-project trust record in ~/.claude.json (projects → hasTrustDialogAccepted) is keyed by an exact-case path string, and the trust lookup for project-scope skills-directory plugins is case-sensitive — but NTFS paths are case-insensitive.
The VS Code extension always spawns panel sessions with a lowercase drive letter (VS Code workspace URIs normalize E: → e:, e.g. file:///e%3A/...). If the user accepted the trust dialog from a terminal where PowerShell preserved the uppercase drive (E:\...), the trust key is stored as E:/.... Every VS Code panel session then fails the trust lookup and silently suppresses all project-scope .claude/skills plugins:
/pluginshows a single(suppressed)@skills-direntry/reload-pluginsreports0 plugins · 0 skills- The session starts with zero skills injected from the plugins
- No trust prompt is ever shown by the extension, so there is no in-product way to recover
The same folder works perfectly in a terminal TUI session.
What Should Happen?
Path→trust (and generally path→project-config) lookups on Windows should be case-insensitive (at minimum for the drive letter, ideally normalized like pathlib/realpath does), matching filesystem semantics. After accepting trust once — from any surface — the VS Code extension panel should load project-scope .claude/skills plugins.
Secondary: when project-scope plugins are suppressed for trust reasons, the only surface is an opaque (suppressed)@skills-dir row whose Enable button does nothing. A hint ("workspace not trusted — accept the trust dialog from a terminal session in this folder") or an in-panel trust prompt would make this diagnosable in minutes instead of hours.
Error Messages/Logs
No error is produced anywhere — this is silent suppression. The divergence is only visible with `--debug --debug-to-stderr`, in the startup plugin scan (before any prompt is sent, no API interaction needed):
# cwd = E:\... # cwd = e:\...
[DEBUG] installed_plugins.json doesn't exist, ... [DEBUG] installed_plugins.json doesn't exist, ...
[DEBUG] Loading skills from: ... project=[E:\...\ [DEBUG] Loading skills from: ... project=[e:\...\
.claude\skills] .claude\skills]
[DEBUG] Found 2 plugins (2 enabled, 0 disabled) [DEBUG] Found 0 plugins (0 enabled, 0 disabled)
[DEBUG] Total plugin skills loaded: 19 (0 dup...) [DEBUG] Total plugin skills loaded: 0 (0 dup...)
Steps to Reproduce
- Create a project at
E:\Work\democontaining.claude\skills\my-plugin\.claude-plugin\plugin.json(a minimal valid manifest plus oneskills/<skill>/SKILL.md), per the documented project-scope skills-directory layout. - In PowerShell,
cd E:\Work\demo, runclaude, accept the "Do you trust the files in this folder?" prompt, then/exit.
→ ~/.claude.json now has projects["E:/Work/demo"].hasTrustDialogAccepted = true (uppercase E, forward slashes).
- Open the same folder in VS Code and start a Claude Code panel session. The extension spawns
claude.exewith the workspace path from its lowercase URI →cwdise:\Work\demo. - In the panel, run
/plugin(shows(suppressed)@skills-dir) or/reload-plugins(reports 0 skills), or ask the session which skills it can see.
The divergence happens at startup and needs no API interaction to observe — the plugin scan result is printed to --debug stderr. Deterministic script:
# repro.py — run on Windows with any Python 3
import subprocess
EXE = r"C:\Users\<you>\.vscode\extensions\anthropic.claude-code-2.1.278-win32-x64\resources\native-binary\claude.exe"
FLAGS = ["--output-format","stream-json","--input-format","stream-json",
"--setting-sources","user,project,local","--permission-mode","bypassPermissions",
"--include-partial-messages","--no-chrome","--debug","--debug-to-stderr"]
PROJ = r"E:\Work\demo" # project with .claude\skills\<plugin>\.claude-plugin\plugin.json
for label, cwd in [("uppercase-E", PROJ), ("lowercase-e", "e" + PROJ[1:])]:
p = subprocess.Popen([EXE] + FLAGS, stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, cwd=cwd)
for raw in p.stderr:
line = raw.decode("utf-8", "replace")
if "plugins (" in line and "Found" in line:
print(f"{label}: {line.split('] ', 1)[-1].strip()}")
break
p.kill()
Output on my machine:
uppercase-E: Found 2 plugins (2 enabled, 0 disabled)
lowercase-e: Found 0 plugins (0 enabled, 0 disabled)
Same binary, same flags, same registry state (installed_plugins.json doesn't exist in both), same folder — the only difference is the case of the drive letter in cwd.
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.278 (Claude Code)
Platform
Other
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
VS Code extension panel (anthropic.claude-code) — the affected surface; terminal-side steps use PowerShell on Windows.
Workaround — duplicate the trust entry with a lowercase drive letter in ~/.claude.json:
"projects": {
"E:/Work/demo": { "hasTrustDialogAccepted": true },
"e:/Work/demo": { "hasTrustDialogAccepted": true }
}
After adding the lowercase entry, the reproduction above prints Found 2 plugins for both casings, and the VS Code panel loads all plugin skills.
Evidence that VS Code passes a lowercase drive: the extension's workspace storage workspace.json records "folder": "file:///e%3A/Work/...", and the extension log for a panel session shows the child process starting with project=[e:\Work\...].