feat: detect MSIX-virtualized Claude config path + warn (#7) #29
Reference in New Issue
Block a user
Delete Branch "feat/7-msix-detection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #7
Problem
When Claude Desktop is installed as an MSIX/Store package, Windows redirects filesystem writes to a per-package virtualized location (
%LOCALAPPDATA%\Packages\<PackageFamilyName>\LocalCache\Roaming\Claude\claude_desktop_config.json) instead of the normal%APPDATA%\Claude\claude_desktop_config.json. Edits BCC (or the user) writes to the plain path can be silently ignored by the running app. See anthropics/claude-code issues #26073, #29100, #38830.Changes
bcc_core.py(pure, unit-tested, no Qt):msix_config_paths(localappdata=None) -> list[Path]— globsPackages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.jsonunder the given (or env-derived)LOCALAPPDATAbase and returns every match that actually exists on disk. Platform-independent by design so it's directly testable viatmp_path.detect_msix_claude(appdata=None, localappdata=None) -> Path | None— the Windows-gated entry point (sys.platform.startswith("win")); safe no-op on macOS/Linux. Returns the first virtualized config found, orNone.msix_warning_text(appdata=None, localappdata=None) -> str | None— a one-line, paste-safe warning (paths only, no secrets/env values) for when a virtualized config was detected and it actually differs from the plain%APPDATA%\Claude\path.discover_profiles()now also callsdetect_msix_claude()and, when it finds a virtualized install not already covered by the normalClaude*scan, adds it as its own profile labeledClaude (Microsoft Store / MSIX)— so users can select and edit the file the app actually reads directly from the dropdown, not just be warned about it.bcc.py(GUI, minimal/defensive wiring):MainWindow._maybe_warn_msix(), called at the end ofreload_profiles(), wrapscore.msix_warning_text()in atry/except(must never block startup) and, when non-None, appends the warning to the status bar text + sets it as the tooltip. On non-Windows this is a guaranteed no-op via thesys.platformgate in core, so nothing changes for macOS/Linux users.Tests
Added 15 new tests in
tests/test_core.pycovering:msix_config_paths: finds a package config, ignores non-Claude packages, empty when package dir has no config yet, empty when noPackagesdir, readsLOCALAPPDATAenv by default.detect_msix_claude:Noneon non-Windows (even with a real match on disk), finds the config on "Windows" (sys.platformmonkeypatched towin32),Nonewhen nothing present.msix_warning_text:Noneon non-Windows,Nonewhen nothing detected, warns with both paths present in the message when they differ,Nonewhen the plain and virtualized paths happen to coincide.discover_profiles: adds the MSIX profile on "Windows" when detected, omits it when nothing's detected, omits it entirely on non-Windows.Test count: 108 → 123 in
tests/test_core.py(136 → 151 total incl.test_json_repair.py). All passing.Verification
ruff check .— cleanruff format --check .— cleanPYTHONPATH=. python -m pytest -q— all passbcc.pyvalidated viaruff+ast.parseonly (PySide6 not installed in the sandbox; GUI change is a small, defensive, try/except-wrapped status-bar append with no new imports).Caveats — needs real-Windows verification
Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json) is based on the publicly known MSIX virtualization layout; the actual Anthropic package family name/folder naming should be confirmed against a real Store install._maybe_warn_msix, the new profile entry showing up in the dropdown) has not been run against real PySide6/Qt or a real Windows+MSIX machine — only statically validated (ruff, AST parse, code review against existing GUI patterns inbcc.py).