codecompass-mcp 6.0.0__tar.gz → 6.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/PKG-INFO +1 -1
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/PKG-INFO +1 -1
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/pi_setup.py +50 -9
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/pyproject.toml +1 -1
- codecompass_mcp-6.0.2/tests/test_pi_setup.py +93 -0
- codecompass_mcp-6.0.0/tests/test_pi_setup.py +0 -42
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/LICENSE +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/README.md +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/SOURCES.txt +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/dependency_links.txt +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/entry_points.txt +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/requires.txt +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/top_level.txt +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/config.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/graph/__init__.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/graph/code_graph_client.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/graph/code_queries.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/graph/vector_store.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/__init__.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/agent_writes.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/code_normalizer.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/code_parser.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/file_watcher.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/hierarchy_builder.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/ingestion/source_context.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/main.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/mcp_server.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/models/__init__.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/models/code_types.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/setup.cfg +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_accuracy_fixes.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_agent_writes.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_block_hook.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_code_parser.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_descriptions.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_ingest_progress.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_init.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_lit_css_parser.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_mcp_server.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_php_parser.py +0 -0
- {codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/tests/test_vector_store.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecompass-mcp
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.2
|
|
4
4
|
Summary: Structural code context for AI coding agents — a local code knowledge graph for blast radius, impact, deps, dead code, and flow tracing
|
|
5
5
|
Author: CodeCompass contributors
|
|
6
6
|
License: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codecompass-mcp
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.2
|
|
4
4
|
Summary: Structural code context for AI coding agents — a local code knowledge graph for blast radius, impact, deps, dead code, and flow tracing
|
|
5
5
|
Author: CodeCompass contributors
|
|
6
6
|
License: MIT
|
|
@@ -20,6 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import json
|
|
21
21
|
import shutil
|
|
22
22
|
import subprocess
|
|
23
|
+
import sys
|
|
23
24
|
from pathlib import Path
|
|
24
25
|
|
|
25
26
|
# Where pi loads user-global skills and pi-mcp-adapter reads user-global servers.
|
|
@@ -30,21 +31,47 @@ _MCP_CONFIG = Path.home() / ".config" / "mcp" / "mcp.json"
|
|
|
30
31
|
_ADAPTER_PKG = "npm:pi-mcp-adapter"
|
|
31
32
|
_ADAPTER_NAME = "pi-mcp-adapter"
|
|
32
33
|
|
|
34
|
+
def _server_command() -> str:
|
|
35
|
+
"""Absolute path to the codecompass-mcp console script, when we can find it.
|
|
36
|
+
|
|
37
|
+
A bare "codecompass-mcp" only resolves if pi inherits a PATH that reaches
|
|
38
|
+
this interpreter's bin dir — which it often doesn't. Under pyenv it is
|
|
39
|
+
worse than missing: the shim resolves a *different* Python depending on the
|
|
40
|
+
launch directory's .python-version, so pi silently gets "command not found"
|
|
41
|
+
while the same name works in a shell inside the project. The script sitting
|
|
42
|
+
next to sys.executable is the one belonging to the environment codecompass
|
|
43
|
+
is actually installed in.
|
|
44
|
+
"""
|
|
45
|
+
script = Path(sys.executable).with_name("codecompass-mcp")
|
|
46
|
+
return str(script) if script.exists() else "codecompass-mcp"
|
|
47
|
+
|
|
48
|
+
|
|
33
49
|
# The codecompass-mcp server entry merged into the user-global mcp.json.
|
|
34
|
-
|
|
50
|
+
def _server_config() -> dict:
|
|
51
|
+
return {"command": _server_command()}
|
|
35
52
|
|
|
36
53
|
# Shipped as the pi skill. pi has the MCP tools natively via pi-mcp-adapter;
|
|
37
54
|
# this teaches the orient-first discipline and lists the capabilities.
|
|
38
55
|
#
|
|
39
|
-
# The marker
|
|
56
|
+
# The marker is what makes the skill self-updating: init-style, we only
|
|
40
57
|
# overwrite files we wrote. Strip the line to take ownership of your copy.
|
|
58
|
+
# It goes in the BODY, never above the frontmatter — pi requires the opening
|
|
59
|
+
# `---` on line 1 and reports "description is required" for anything else.
|
|
41
60
|
_SKILL_MARKER = "<!-- Installed by `codecompass setup-pi` — rewritten on upgrade. -->"
|
|
42
61
|
|
|
43
|
-
|
|
62
|
+
# Copies installed before the marker existed. A file carrying one of these is
|
|
63
|
+
# ours to replace, even without the marker — otherwise every pre-6.0.0 install
|
|
64
|
+
# would be pinned to its original skill text forever.
|
|
65
|
+
_LEGACY_SIGNATURES = (
|
|
66
|
+
"CodeCompass maps a repo into a queryable graph",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
_SKILL_MD = """\
|
|
44
70
|
---
|
|
45
71
|
name: codecompass
|
|
46
72
|
description: Orient in any indexed repo through the CodeCompass code graph before reading files. Use for discovery, impact/dependency traces, dead-code checks, and flow analysis in any repository with a .codecompass/graph.json index.
|
|
47
73
|
---
|
|
74
|
+
""" + _SKILL_MARKER + """
|
|
48
75
|
|
|
49
76
|
# CodeCompass
|
|
50
77
|
|
|
@@ -114,8 +141,10 @@ def _install_adapter() -> None:
|
|
|
114
141
|
subprocess.run(["pi", "install", _ADAPTER_PKG], check=False, timeout=300)
|
|
115
142
|
|
|
116
143
|
|
|
117
|
-
def _write_mcp_config() ->
|
|
118
|
-
"""Merge the codecompass server into the user-global mcp.json, preserving
|
|
144
|
+
def _write_mcp_config() -> bool:
|
|
145
|
+
"""Merge the codecompass server into the user-global mcp.json, preserving
|
|
146
|
+
others. Returns True if the file changed — a no-op when already correct, so
|
|
147
|
+
this is cheap enough to run on every invocation."""
|
|
119
148
|
config: dict = {}
|
|
120
149
|
if _MCP_CONFIG.exists():
|
|
121
150
|
try:
|
|
@@ -123,19 +152,26 @@ def _write_mcp_config() -> None:
|
|
|
123
152
|
except (json.JSONDecodeError, OSError):
|
|
124
153
|
config = {}
|
|
125
154
|
servers = config.setdefault("mcpServers", {})
|
|
126
|
-
|
|
155
|
+
wanted = _server_config()
|
|
156
|
+
if servers.get("codecompass") == wanted:
|
|
157
|
+
return False
|
|
158
|
+
servers["codecompass"] = wanted
|
|
127
159
|
_MCP_CONFIG.parent.mkdir(parents=True, exist_ok=True)
|
|
128
160
|
_MCP_CONFIG.write_text(json.dumps(config, indent=2) + "\n")
|
|
161
|
+
return True
|
|
129
162
|
|
|
130
163
|
|
|
131
164
|
def _skill_is_current() -> bool:
|
|
132
165
|
"""True when the installed skill needs no write — either it is already our
|
|
133
|
-
latest text, or the user
|
|
166
|
+
latest text, or it is a file the user wrote and we must not touch."""
|
|
134
167
|
try:
|
|
135
168
|
existing = _SKILL_FILE.read_text()
|
|
136
169
|
except OSError:
|
|
137
170
|
return False
|
|
138
|
-
|
|
171
|
+
if existing == _SKILL_MD:
|
|
172
|
+
return True
|
|
173
|
+
ours = _SKILL_MARKER in existing or any(s in existing for s in _LEGACY_SIGNATURES)
|
|
174
|
+
return not ours
|
|
139
175
|
|
|
140
176
|
|
|
141
177
|
def setup_pi(force: bool = False, quiet: bool = False) -> bool:
|
|
@@ -155,6 +191,12 @@ def setup_pi(force: bool = False, quiet: bool = False) -> bool:
|
|
|
155
191
|
say("pi not installed; skipping CodeCompass pi setup.")
|
|
156
192
|
return False
|
|
157
193
|
|
|
194
|
+
# Always reconcile the server entry: the path it should point at changes
|
|
195
|
+
# when codecompass is reinstalled into a different environment, and a stale
|
|
196
|
+
# one leaves pi with a server it cannot spawn.
|
|
197
|
+
if _write_mcp_config():
|
|
198
|
+
say(f"Pointed pi at {_server_command()} in {_MCP_CONFIG}")
|
|
199
|
+
|
|
158
200
|
if _skill_is_current() and not force:
|
|
159
201
|
return True
|
|
160
202
|
|
|
@@ -164,7 +206,6 @@ def setup_pi(force: bool = False, quiet: bool = False) -> bool:
|
|
|
164
206
|
|
|
165
207
|
_SKILL_DIR.mkdir(parents=True, exist_ok=True)
|
|
166
208
|
_SKILL_FILE.write_text(_SKILL_MD)
|
|
167
|
-
_write_mcp_config()
|
|
168
209
|
say(f"CodeCompass wired into pi: {_SKILL_FILE}, {_MCP_CONFIG}")
|
|
169
210
|
return True
|
|
170
211
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codecompass-mcp"
|
|
3
|
-
version = "6.0.
|
|
3
|
+
version = "6.0.2"
|
|
4
4
|
description = "Structural code context for AI coding agents — a local code knowledge graph for blast radius, impact, deps, dead code, and flow tracing"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""The pi skill file self-updates on upgrade, but never clobbers a user's copy;
|
|
2
|
+
the mcp.json entry always points at a command pi can actually spawn."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import pi_setup
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_skill_frontmatter_is_first():
|
|
10
|
+
"""pi parses YAML frontmatter only when `---` is line 1 — anything above it
|
|
11
|
+
(like our generated-by marker) makes it report "description is required"."""
|
|
12
|
+
lines = pi_setup._SKILL_MD.splitlines()
|
|
13
|
+
assert lines[0] == "---"
|
|
14
|
+
assert any(line.startswith("description:") for line in lines[:5])
|
|
15
|
+
closing = lines.index("---", 1)
|
|
16
|
+
assert pi_setup._SKILL_MARKER in lines[closing + 1] # marker sits in the body
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _install(tmp_path, monkeypatch):
|
|
20
|
+
"""Point pi_setup at a temp home and pretend pi + adapter are installed."""
|
|
21
|
+
skill_file = tmp_path / "SKILL.md"
|
|
22
|
+
monkeypatch.setattr(pi_setup, "_SKILL_DIR", tmp_path)
|
|
23
|
+
monkeypatch.setattr(pi_setup, "_SKILL_FILE", skill_file)
|
|
24
|
+
monkeypatch.setattr(pi_setup, "_MCP_CONFIG", tmp_path / "mcp.json")
|
|
25
|
+
monkeypatch.setattr(pi_setup, "_pi_available", lambda: True)
|
|
26
|
+
monkeypatch.setattr(pi_setup, "_adapter_installed", lambda: True)
|
|
27
|
+
return skill_file
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_stale_skill_is_rewritten_but_user_edits_are_kept(tmp_path, monkeypatch):
|
|
31
|
+
skill_file = _install(tmp_path, monkeypatch)
|
|
32
|
+
|
|
33
|
+
assert pi_setup.setup_pi(quiet=True) is True
|
|
34
|
+
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
35
|
+
|
|
36
|
+
# An older release's text — marker present, content stale -> rewritten.
|
|
37
|
+
skill_file.write_text(pi_setup._SKILL_MARKER + "\nold instructions\n")
|
|
38
|
+
pi_setup.setup_pi(quiet=True)
|
|
39
|
+
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
40
|
+
|
|
41
|
+
# The user took ownership by stripping the marker -> left alone.
|
|
42
|
+
skill_file.write_text("my own skill\n")
|
|
43
|
+
pi_setup.setup_pi(quiet=True)
|
|
44
|
+
assert skill_file.read_text() == "my own skill\n"
|
|
45
|
+
|
|
46
|
+
# ...unless explicitly forced.
|
|
47
|
+
pi_setup.setup_pi(force=True, quiet=True)
|
|
48
|
+
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_pre_marker_skill_is_adopted_and_updated(tmp_path, monkeypatch):
|
|
52
|
+
"""A copy installed before the marker existed is still ours to replace."""
|
|
53
|
+
skill_file = _install(tmp_path, monkeypatch)
|
|
54
|
+
skill_file.write_text(
|
|
55
|
+
"---\nname: codecompass\n---\n\n"
|
|
56
|
+
"CodeCompass maps a repo into a queryable graph so you orient...\n"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
pi_setup.setup_pi(quiet=True)
|
|
60
|
+
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_mcp_config_points_at_an_absolute_command(tmp_path, monkeypatch):
|
|
64
|
+
"""A bare name resolves through PATH/pyenv and can land on a Python that
|
|
65
|
+
doesn't have codecompass installed — pin the interpreter's own script."""
|
|
66
|
+
_install(tmp_path, monkeypatch)
|
|
67
|
+
config = tmp_path / "mcp.json"
|
|
68
|
+
|
|
69
|
+
script = tmp_path / "codecompass-mcp"
|
|
70
|
+
script.write_text("#!/bin/sh\n")
|
|
71
|
+
monkeypatch.setattr(pi_setup.sys, "executable", str(tmp_path / "python"))
|
|
72
|
+
|
|
73
|
+
pi_setup.setup_pi(quiet=True)
|
|
74
|
+
entry = json.loads(config.read_text())["mcpServers"]["codecompass"]
|
|
75
|
+
assert entry == {"command": str(script)}
|
|
76
|
+
|
|
77
|
+
# Other servers in the file are preserved, and a stale codecompass entry is
|
|
78
|
+
# corrected even when the skill file is already up to date.
|
|
79
|
+
config.write_text(json.dumps({"mcpServers": {
|
|
80
|
+
"other": {"command": "keep-me"},
|
|
81
|
+
"codecompass": {"command": "codecompass-mcp"},
|
|
82
|
+
}}))
|
|
83
|
+
pi_setup.setup_pi(quiet=True)
|
|
84
|
+
servers = json.loads(config.read_text())["mcpServers"]
|
|
85
|
+
assert servers["other"] == {"command": "keep-me"}
|
|
86
|
+
assert servers["codecompass"] == {"command": str(script)}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_no_pi_no_write(tmp_path, monkeypatch):
|
|
90
|
+
skill_file = _install(tmp_path, monkeypatch)
|
|
91
|
+
monkeypatch.setattr(pi_setup, "_pi_available", lambda: False)
|
|
92
|
+
assert pi_setup.setup_pi(quiet=True) is False
|
|
93
|
+
assert not skill_file.exists()
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"""The pi skill file self-updates on upgrade, but never clobbers a user's copy."""
|
|
2
|
-
|
|
3
|
-
import pi_setup
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def _install(tmp_path, monkeypatch):
|
|
7
|
-
"""Point pi_setup at a temp home and pretend pi + adapter are installed."""
|
|
8
|
-
skill_file = tmp_path / "SKILL.md"
|
|
9
|
-
monkeypatch.setattr(pi_setup, "_SKILL_DIR", tmp_path)
|
|
10
|
-
monkeypatch.setattr(pi_setup, "_SKILL_FILE", skill_file)
|
|
11
|
-
monkeypatch.setattr(pi_setup, "_MCP_CONFIG", tmp_path / "mcp.json")
|
|
12
|
-
monkeypatch.setattr(pi_setup, "_pi_available", lambda: True)
|
|
13
|
-
monkeypatch.setattr(pi_setup, "_adapter_installed", lambda: True)
|
|
14
|
-
return skill_file
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def test_stale_skill_is_rewritten_but_user_edits_are_kept(tmp_path, monkeypatch):
|
|
18
|
-
skill_file = _install(tmp_path, monkeypatch)
|
|
19
|
-
|
|
20
|
-
assert pi_setup.setup_pi(quiet=True) is True
|
|
21
|
-
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
22
|
-
|
|
23
|
-
# An older release's text — marker present, content stale -> rewritten.
|
|
24
|
-
skill_file.write_text(pi_setup._SKILL_MARKER + "\nold instructions\n")
|
|
25
|
-
pi_setup.setup_pi(quiet=True)
|
|
26
|
-
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
27
|
-
|
|
28
|
-
# The user took ownership by stripping the marker -> left alone.
|
|
29
|
-
skill_file.write_text("my own skill\n")
|
|
30
|
-
pi_setup.setup_pi(quiet=True)
|
|
31
|
-
assert skill_file.read_text() == "my own skill\n"
|
|
32
|
-
|
|
33
|
-
# ...unless explicitly forced.
|
|
34
|
-
pi_setup.setup_pi(force=True, quiet=True)
|
|
35
|
-
assert skill_file.read_text() == pi_setup._SKILL_MD
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_no_pi_no_write(tmp_path, monkeypatch):
|
|
39
|
-
skill_file = _install(tmp_path, monkeypatch)
|
|
40
|
-
monkeypatch.setattr(pi_setup, "_pi_available", lambda: False)
|
|
41
|
-
assert pi_setup.setup_pi(quiet=True) is False
|
|
42
|
-
assert not skill_file.exists()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecompass_mcp-6.0.0 → codecompass_mcp-6.0.2}/codecompass_mcp.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|