Skip to content

Commit d007bc1

Browse files
tirth8205npkriami18
andcommitted
fix: detect installed GitHub Copilot clients
Extract the released-client-validated Copilot detection from PR tirth8205#658 while leaving the unvalidated Antigravity integration out. Co-authored-by: npkriami18 <pranaynannuri@gmail.com>
1 parent 329dbb9 commit d007bc1

3 files changed

Lines changed: 137 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
### Fixed
2020

21+
- GitHub Copilot auto-detection now requires the Copilot extension and also
22+
recognizes the extension bundled with released VS Code installations.
2123
- Added a post-index Python import resolver using unique module suffixes, so
2224
`src/` layouts produce canonical `CALLS` and `TESTED_BY` edges while duplicate
2325
package candidates remain explicitly unresolved (#720).

code_review_graph/skills.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,92 @@ def _zed_settings_path() -> Path:
3232
return Path.home() / ".config" / "zed" / "settings.json"
3333

3434

35+
def _copilot_vscode_detected() -> bool:
36+
"""Return whether a GitHub Copilot extension is installed for VS Code."""
37+
home = Path.home()
38+
extension_dirs = [
39+
home / ".vscode" / "extensions",
40+
home / ".vscode-insiders" / "extensions",
41+
]
42+
43+
system = platform.system()
44+
if system == "Darwin":
45+
for applications_dir in (Path("/Applications"), home / "Applications"):
46+
for app_name in (
47+
"Visual Studio Code.app",
48+
"Visual Studio Code - Insiders.app",
49+
):
50+
extension_dirs.append(
51+
applications_dir
52+
/ app_name
53+
/ "Contents"
54+
/ "Resources"
55+
/ "app"
56+
/ "extensions"
57+
)
58+
elif system == "Windows":
59+
for env_name in ("LOCALAPPDATA", "PROGRAMFILES", "PROGRAMFILES(X86)"):
60+
if install_root := os.environ.get(env_name):
61+
for app_name in ("Microsoft VS Code", "Microsoft VS Code Insiders"):
62+
extension_dirs.append(
63+
Path(install_root)
64+
/ app_name
65+
/ "resources"
66+
/ "app"
67+
/ "extensions"
68+
)
69+
elif system == "Linux":
70+
extension_dirs.extend(
71+
[
72+
Path("/usr/share/code/resources/app/extensions"),
73+
Path("/usr/share/code-insiders/resources/app/extensions"),
74+
Path("/usr/lib/code/resources/app/extensions"),
75+
Path("/opt/visual-studio-code/resources/app/extensions"),
76+
Path("/snap/code/current/usr/share/code/resources/app/extensions"),
77+
]
78+
)
79+
80+
for command in ("code", "code-insiders"):
81+
if executable := shutil.which(command):
82+
try:
83+
parents = Path(executable).resolve().parents[:6]
84+
except OSError:
85+
continue
86+
for parent in parents:
87+
extension_dirs.extend(
88+
[
89+
parent / "extensions",
90+
parent / "resources" / "app" / "extensions",
91+
parent / "Resources" / "app" / "extensions",
92+
]
93+
)
94+
95+
for extensions_dir in dict.fromkeys(extension_dirs):
96+
try:
97+
extension_paths = list(extensions_dir.iterdir())
98+
except OSError:
99+
continue
100+
for extension_path in extension_paths:
101+
name = extension_path.name.lower()
102+
if name.startswith("github.copilot-"):
103+
return True
104+
if "copilot" not in name:
105+
continue
106+
try:
107+
manifest = json.loads(
108+
(extension_path / "package.json").read_text(encoding="utf-8")
109+
)
110+
except (OSError, json.JSONDecodeError):
111+
continue
112+
if (
113+
str(manifest.get("publisher", "")).lower() == "github"
114+
and str(manifest.get("name", "")).lower()
115+
in {"copilot", "copilot-chat"}
116+
):
117+
return True
118+
return False
119+
120+
35121
def _opencode_config_path(repo_root: Path) -> Path:
36122
"""Return OpenCode's existing project config, preferring JSONC."""
37123
for name in ("opencode.jsonc", "opencode.json"):
@@ -142,7 +228,7 @@ def _opencode_config_path(repo_root: Path) -> Path:
142228
"name": "GitHub Copilot",
143229
"config_path": lambda root: root / ".vscode" / "mcp.json",
144230
"key": "servers",
145-
"detect": lambda: (Path.home() / ".vscode").exists(),
231+
"detect": _copilot_vscode_detected,
146232
"format": "object",
147233
"needs_type": True,
148234
},

tests/test_skills.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from code_review_graph.skills import (
2020
_CLAUDE_MD_SECTION_MARKER,
2121
PLATFORMS,
22+
_copilot_vscode_detected,
2223
_cursor_hook_scripts,
2324
_detect_serve_command,
2425
_in_poetry_project,
@@ -1669,15 +1670,59 @@ def test_copilot_writes_only_copilot_instructions(self, tmp_path):
16691670
assert not (tmp_path / "QODER.md").exists()
16701671

16711672
def test_copilot_included_in_all_when_detected(self, tmp_path):
1672-
"""install_platform_configs with target='all' includes Copilot when ~/.vscode exists."""
1673+
"""Auto-detection requires the Copilot extension, not only VS Code."""
16731674
fake_home = tmp_path / "fakehome"
1674-
(fake_home / ".vscode").mkdir(parents=True)
1675-
with patch("code_review_graph.skills.Path.home", return_value=fake_home):
1675+
(fake_home / ".vscode" / "extensions" / "github.copilot-1.2.3").mkdir(
1676+
parents=True
1677+
)
1678+
with (
1679+
patch("code_review_graph.skills.Path.home", return_value=fake_home),
1680+
patch("code_review_graph.skills.platform.system", return_value="Unknown"),
1681+
patch("code_review_graph.skills.shutil.which", return_value=None),
1682+
):
16761683
configured = install_platform_configs(tmp_path, target="all")
16771684
assert "GitHub Copilot" in configured
16781685
config_path = tmp_path / ".vscode" / "mcp.json"
16791686
assert config_path.exists()
16801687

1688+
def test_copilot_detects_vscode_bundled_extension(self, tmp_path):
1689+
"""Current VS Code bundles Copilot under its application extensions."""
1690+
fake_home = tmp_path / "fakehome"
1691+
app_root = tmp_path / "vscode" / "resources" / "app"
1692+
code_cli = app_root / "bin" / "code"
1693+
code_cli.parent.mkdir(parents=True)
1694+
code_cli.write_text("", encoding="utf-8")
1695+
manifest = app_root / "extensions" / "copilot" / "package.json"
1696+
manifest.parent.mkdir(parents=True)
1697+
manifest.write_text(
1698+
json.dumps({"publisher": "GitHub", "name": "copilot-chat"}),
1699+
encoding="utf-8",
1700+
)
1701+
1702+
def _which(command):
1703+
return str(code_cli) if command == "code" else None
1704+
1705+
with (
1706+
patch("code_review_graph.skills.Path.home", return_value=fake_home),
1707+
patch("code_review_graph.skills.shutil.which", side_effect=_which),
1708+
):
1709+
assert _copilot_vscode_detected() is True
1710+
1711+
def test_copilot_not_detected_from_vscode_alone(self, tmp_path):
1712+
"""An unrelated VS Code install must not trigger Copilot configuration."""
1713+
fake_home = tmp_path / "fakehome"
1714+
(fake_home / ".vscode" / "extensions" / "ms-python.python-1.0.0").mkdir(
1715+
parents=True
1716+
)
1717+
with (
1718+
patch("code_review_graph.skills.Path.home", return_value=fake_home),
1719+
patch("code_review_graph.skills.platform.system", return_value="Unknown"),
1720+
patch("code_review_graph.skills.shutil.which", return_value=None),
1721+
):
1722+
configured = install_platform_configs(tmp_path, target="all")
1723+
assert "GitHub Copilot" not in configured
1724+
assert not (tmp_path / ".vscode" / "mcp.json").exists()
1725+
16811726

16821727
class TestCopilotCLIPlatform:
16831728
"""Tests for GitHub Copilot CLI platform support."""

0 commit comments

Comments
 (0)