feat: detect MSIX-virtualized Claude config path + warn (#7) #29

Merged
the_og merged 1 commits from feat/7-msix-detection into main 2026-07-08 11:55:25 -04:00
Owner

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] — globs Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json under the given (or env-derived) LOCALAPPDATA base and returns every match that actually exists on disk. Platform-independent by design so it's directly testable via tmp_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, or None.
  • 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 calls detect_msix_claude() and, when it finds a virtualized install not already covered by the normal Claude* scan, adds it as its own profile labeled Claude (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 of reload_profiles(), wraps core.msix_warning_text() in a try/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 the sys.platform gate in core, so nothing changes for macOS/Linux users.

Tests

Added 15 new tests in tests/test_core.py covering:

  • msix_config_paths: finds a package config, ignores non-Claude packages, empty when package dir has no config yet, empty when no Packages dir, reads LOCALAPPDATA env by default.
  • detect_msix_claude: None on non-Windows (even with a real match on disk), finds the config on "Windows" (sys.platform monkeypatched to win32), None when nothing present.
  • msix_warning_text: None on non-Windows, None when nothing detected, warns with both paths present in the message when they differ, None when 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 . — clean
  • ruff format --check . — clean
  • PYTHONPATH=. python -m pytest -q — all pass
  • bcc.py validated via ruff + ast.parse only (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

  • The detection glob pattern (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.
  • GUI wiring (_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 in bcc.py).
  • No attempt was made to auto-migrate or offer to copy servers from the plain path into the virtualized one — this PR only detects and surfaces, per the issue's scope.
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]` — globs `Packages/*Claude*/LocalCache/Roaming/Claude/claude_desktop_config.json` under the given (or env-derived) `LOCALAPPDATA` base and returns every match that actually exists on disk. Platform-independent by design so it's directly testable via `tmp_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, or `None`. - `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 calls `detect_msix_claude()` and, when it finds a virtualized install not already covered by the normal `Claude*` scan, adds it as its own profile labeled `Claude (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 of `reload_profiles()`, wraps `core.msix_warning_text()` in a `try/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 the `sys.platform` gate in core, so nothing changes for macOS/Linux users. ## Tests Added 15 new tests in `tests/test_core.py` covering: - `msix_config_paths`: finds a package config, ignores non-Claude packages, empty when package dir has no config yet, empty when no `Packages` dir, reads `LOCALAPPDATA` env by default. - `detect_msix_claude`: `None` on non-Windows (even with a real match on disk), finds the config on "Windows" (`sys.platform` monkeypatched to `win32`), `None` when nothing present. - `msix_warning_text`: `None` on non-Windows, `None` when nothing detected, warns with both paths present in the message when they differ, `None` when 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 .` — clean - `ruff format --check .` — clean - `PYTHONPATH=. python -m pytest -q` — all pass - `bcc.py` validated via `ruff` + `ast.parse` only (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 - The detection glob pattern (`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. - GUI wiring (`_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 in `bcc.py`). - No attempt was made to auto-migrate or offer to copy servers from the plain path into the virtualized one — this PR only detects and surfaces, per the issue's scope.
the_og added the P1 label 2026-07-08 11:51:44 -04:00
the_og added 1 commit 2026-07-08 11:51:44 -04:00
feat: detect MSIX-virtualized Claude config path + warn (#7)
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10) (pull_request) Successful in 8s
CI / Tests (py3.12) (pull_request) Successful in 8s
8c456c9a89
the_og merged commit 5169b7276e into main 2026-07-08 11:55:25 -04:00
the_og deleted branch feat/7-msix-detection 2026-07-08 11:55:25 -04:00
Sign in to join this conversation.