1616
1717_HOME = Path .home ()
1818
19+
20+ def _exists_or_denied (path : Path ) -> bool :
21+ """Distinguish between existence and permission issues."""
22+ try :
23+ path .stat ()
24+ except FileNotFoundError :
25+ return False
26+ # PermissionError is a subclass of OSError
27+ # which is why this looks the way it does.
28+ except PermissionError :
29+ return True
30+ except OSError :
31+ return False
32+ return True
33+
34+
1935Action = Literal ["created" , "updated" , "unchanged" , "not-found" , "removed" , "error" , "skipped" ]
2036Mode = Literal ["install" , "uninstall" ]
2137
@@ -33,15 +49,7 @@ class IntegrationType(str, Enum):
3349
3450
3551def semble_pin () -> str :
36- """Return the uvx --from specifier for the semble MCP server.
37-
38- Version-pinned for normal installs (rerunning `semble install` after an
39- upgrade rewrites this pin to match). For an editable or local-directory
40- install, pins to the local source path instead, so generated configs
41- launch the checkout being developed rather than the released package.
42- For a non-editable git install, pins to the exact installed commit, since
43- that source may not correspond to any released PyPI version at all.
44- """
52+ """Return the uvx --from specifier for the semble MCP server."""
4553 try :
4654 raw = importlib .metadata .distribution ("semble" ).read_text ("direct_url.json" )
4755 if raw :
@@ -159,7 +167,7 @@ def _opencode_mcp_path() -> Path:
159167 base = Path (xdg ) / "opencode" if xdg else _HOME / ".config" / "opencode"
160168 jsonc = base / "opencode.jsonc"
161169 json_ = base / "opencode.json"
162- return jsonc if jsonc . exists ( ) else (json_ if json_ . exists ( ) else jsonc )
170+ return jsonc if _exists_or_denied ( jsonc ) else (json_ if _exists_or_denied ( json_ ) else jsonc )
163171
164172
165173def _vscode_mcp_path () -> Path :
@@ -316,4 +324,4 @@ def is_detected(agent: AgentTarget) -> bool:
316324 """Return True if the agent appears to be installed."""
317325 if agent .binary and shutil .which (agent .binary ):
318326 return True
319- return bool (agent .config_dir and agent .config_dir . exists ( ))
327+ return bool (agent .config_dir and _exists_or_denied ( agent .config_dir ))
0 commit comments