[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

Status Open
Reported on v2.1.278
Maintainer reply None cached
Activity 0 comments · opened Sep 21, 2026

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:

  • /plugin shows a single (suppressed)@skills-dir entry
  • /reload-plugins reports 0 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

  1. Create a project at E:\Work\demo containing .claude\skills\my-plugin\.claude-plugin\plugin.json (a minimal valid manifest plus one skills/<skill>/SKILL.md), per the documented project-scope skills-directory layout.
  2. In PowerShell, cd E:\Work\demo, run claude, 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).

  1. Open the same folder in VS Code and start a Claude Code panel session. The extension spawns claude.exe with the workspace path from its lowercase URI → cwd is e:\Work\demo.
  2. 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\...].

View original on GitHub ↗