codecortex-context-engine 0.1.0a1__py3-none-any.whl
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.
- codecortex/__init__.py +3 -0
- codecortex/architecture/__init__.py +23 -0
- codecortex/architecture/drift.py +182 -0
- codecortex/architecture/inference.py +160 -0
- codecortex/backends/__init__.py +35 -0
- codecortex/backends/base.py +38 -0
- codecortex/backends/context.py +118 -0
- codecortex/backends/contracts.py +65 -0
- codecortex/backends/factory.py +60 -0
- codecortex/backends/graph.py +107 -0
- codecortex/backends/manager.py +303 -0
- codecortex/backends/mcp_client.py +196 -0
- codecortex/backends/pool.py +128 -0
- codecortex/backends/spec.py +83 -0
- codecortex/backends/symbols.py +189 -0
- codecortex/benchmark.py +211 -0
- codecortex/cli.py +409 -0
- codecortex/config.py +27 -0
- codecortex/context/__init__.py +13 -0
- codecortex/context/budget.py +62 -0
- codecortex/context/integrated.py +60 -0
- codecortex/context/pipeline.py +223 -0
- codecortex/core/__init__.py +1 -0
- codecortex/core/contracts.py +45 -0
- codecortex/core/errors.py +17 -0
- codecortex/core/models.py +69 -0
- codecortex/dashboard.py +259 -0
- codecortex/editing.py +41 -0
- codecortex/engines/__init__.py +5 -0
- codecortex/engines/builtin/__init__.py +5 -0
- codecortex/engines/builtin/factory.py +26 -0
- codecortex/engines/builtin/memory.py +35 -0
- codecortex/engines/builtin/repository.py +73 -0
- codecortex/engines/builtin/symbols.py +89 -0
- codecortex/engines/builtin/validation.py +54 -0
- codecortex/engines/registry.py +26 -0
- codecortex/entrypoint.py +266 -0
- codecortex/evaluation/__init__.py +55 -0
- codecortex/evaluation/external.py +265 -0
- codecortex/evaluation/production.py +670 -0
- codecortex/evaluation/regression.py +188 -0
- codecortex/gateway.py +38 -0
- codecortex/git_intelligence.py +252 -0
- codecortex/indexing/__init__.py +6 -0
- codecortex/indexing/graph.py +78 -0
- codecortex/indexing/impact.py +127 -0
- codecortex/indexing/incremental.py +163 -0
- codecortex/indexing/incremental_graph.py +189 -0
- codecortex/indexing/indexer.py +172 -0
- codecortex/indexing/relationships.py +179 -0
- codecortex/indexing/resolution.py +88 -0
- codecortex/integrations/__init__.py +5 -0
- codecortex/integrations/agents.py +235 -0
- codecortex/interfaces/__init__.py +1 -0
- codecortex/interfaces/mcp_bridge.py +66 -0
- codecortex/languages/__init__.py +5 -0
- codecortex/languages/native.py +166 -0
- codecortex/languages/registry.py +232 -0
- codecortex/mcp/__init__.py +5 -0
- codecortex/mcp/extended.py +114 -0
- codecortex/mcp/server.py +473 -0
- codecortex/memory/__init__.py +11 -0
- codecortex/memory/json_store.py +52 -0
- codecortex/memory/knowledge.py +193 -0
- codecortex/memory/team_store.py +193 -0
- codecortex/orchestrator.py +153 -0
- codecortex/pr_intelligence.py +214 -0
- codecortex/retrieval/__init__.py +16 -0
- codecortex/retrieval/hybrid.py +67 -0
- codecortex/retrieval/index.py +135 -0
- codecortex/retrieval/providers.py +67 -0
- codecortex/retrieval/repository.py +94 -0
- codecortex/router/__init__.py +5 -0
- codecortex/router/router.py +79 -0
- codecortex/runtime.py +69 -0
- codecortex/setup.py +100 -0
- codecortex/symbols/__init__.py +5 -0
- codecortex/symbols/providers.py +192 -0
- codecortex/telemetry/__init__.py +5 -0
- codecortex/telemetry/collector.py +43 -0
- codecortex/tracing/__init__.py +9 -0
- codecortex/tracing/task_trace.py +235 -0
- codecortex/workspace/__init__.py +9 -0
- codecortex/workspace/federation.py +173 -0
- codecortex_context_engine-0.1.0a1.dist-info/METADATA +381 -0
- codecortex_context_engine-0.1.0a1.dist-info/RECORD +90 -0
- codecortex_context_engine-0.1.0a1.dist-info/WHEEL +4 -0
- codecortex_context_engine-0.1.0a1.dist-info/entry_points.txt +3 -0
- codecortex_context_engine-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- codecortex_context_engine-0.1.0a1.dist-info/licenses/NOTICE +2 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Language-aware parsing, type discovery, and structural extraction."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import re
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from codecortex.languages.native import TreeSitterParserProvider
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True, slots=True)
|
|
14
|
+
class ParsedUnit:
|
|
15
|
+
name: str
|
|
16
|
+
kind: str
|
|
17
|
+
line: int
|
|
18
|
+
end_line: int | None = None
|
|
19
|
+
signature: str | None = None
|
|
20
|
+
return_type: str | None = None
|
|
21
|
+
type_parameters: tuple[str, ...] = ()
|
|
22
|
+
bases: tuple[str, ...] = ()
|
|
23
|
+
modifiers: tuple[str, ...] = ()
|
|
24
|
+
references: tuple[str, ...] = ()
|
|
25
|
+
annotations: dict[str, str] = field(default_factory=dict)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True, slots=True)
|
|
29
|
+
class LanguageSpec:
|
|
30
|
+
name: str
|
|
31
|
+
suffixes: tuple[str, ...]
|
|
32
|
+
parser: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LanguageRegistry:
|
|
36
|
+
"""Use precise Python AST, optional Tree-sitter grammars, then conservative fallback."""
|
|
37
|
+
|
|
38
|
+
_SPECS = (
|
|
39
|
+
LanguageSpec("python", (".py",), "python_ast"),
|
|
40
|
+
LanguageSpec("typescript", (".ts", ".tsx"), "native"),
|
|
41
|
+
LanguageSpec("javascript", (".js", ".jsx", ".mjs", ".cjs"), "native"),
|
|
42
|
+
LanguageSpec("go", (".go",), "native"),
|
|
43
|
+
LanguageSpec("rust", (".rs",), "native"),
|
|
44
|
+
LanguageSpec("java", (".java",), "native"),
|
|
45
|
+
LanguageSpec("c", (".c", ".h"), "native"),
|
|
46
|
+
LanguageSpec("cpp", (".cc", ".cpp", ".cxx", ".hpp", ".hh"), "native"),
|
|
47
|
+
LanguageSpec("csharp", (".cs",), "native"),
|
|
48
|
+
LanguageSpec("php", (".php",), "native"),
|
|
49
|
+
LanguageSpec("ruby", (".rb",), "native"),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def __init__(self, *, native: bool = True) -> None:
|
|
53
|
+
self.native = (
|
|
54
|
+
TreeSitterParserProvider()
|
|
55
|
+
if native and TreeSitterParserProvider.available()
|
|
56
|
+
else None
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def language_for(self, path: Path) -> LanguageSpec | None:
|
|
60
|
+
suffix = path.suffix.lower()
|
|
61
|
+
return next((spec for spec in self._SPECS if suffix in spec.suffixes), None)
|
|
62
|
+
|
|
63
|
+
def parse(self, path: Path, source: str) -> list[ParsedUnit]:
|
|
64
|
+
spec = self.language_for(path)
|
|
65
|
+
if spec is None:
|
|
66
|
+
return []
|
|
67
|
+
if spec.parser == "python_ast":
|
|
68
|
+
return self._parse_python(source)
|
|
69
|
+
if self.native is not None:
|
|
70
|
+
try:
|
|
71
|
+
units = self.native.parse(spec.name, source)
|
|
72
|
+
except Exception:
|
|
73
|
+
units = []
|
|
74
|
+
if units:
|
|
75
|
+
return [
|
|
76
|
+
ParsedUnit(
|
|
77
|
+
name=item.name,
|
|
78
|
+
kind=item.kind,
|
|
79
|
+
line=item.line,
|
|
80
|
+
end_line=item.end_line,
|
|
81
|
+
signature=item.signature,
|
|
82
|
+
return_type=item.return_type,
|
|
83
|
+
bases=item.bases,
|
|
84
|
+
references=item.references,
|
|
85
|
+
)
|
|
86
|
+
for item in units
|
|
87
|
+
]
|
|
88
|
+
return self._parse_structural(spec.name, source)
|
|
89
|
+
|
|
90
|
+
def _parse_python(self, source: str) -> list[ParsedUnit]:
|
|
91
|
+
try:
|
|
92
|
+
tree = ast.parse(source)
|
|
93
|
+
except SyntaxError:
|
|
94
|
+
return []
|
|
95
|
+
units: list[ParsedUnit] = []
|
|
96
|
+
for node in ast.walk(tree):
|
|
97
|
+
if isinstance(node, ast.ClassDef):
|
|
98
|
+
units.append(
|
|
99
|
+
ParsedUnit(
|
|
100
|
+
name=node.name,
|
|
101
|
+
kind="class",
|
|
102
|
+
line=node.lineno,
|
|
103
|
+
end_line=getattr(node, "end_lineno", None),
|
|
104
|
+
bases=tuple(ast.unparse(base) for base in node.bases),
|
|
105
|
+
type_parameters=tuple(
|
|
106
|
+
getattr(item, "name", ast.unparse(item))
|
|
107
|
+
for item in getattr(node, "type_params", [])
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
112
|
+
args = [arg.arg for arg in node.args.posonlyargs + node.args.args]
|
|
113
|
+
args.extend(f"*{arg.arg}" for arg in node.args.kwonlyargs)
|
|
114
|
+
if node.args.vararg:
|
|
115
|
+
args.append(f"*{node.args.vararg.arg}")
|
|
116
|
+
if node.args.kwarg:
|
|
117
|
+
args.append(f"**{node.args.kwarg.arg}")
|
|
118
|
+
refs = tuple(
|
|
119
|
+
child.id
|
|
120
|
+
for child in ast.walk(node)
|
|
121
|
+
if isinstance(child, ast.Name) and isinstance(child.ctx, ast.Load)
|
|
122
|
+
)
|
|
123
|
+
units.append(
|
|
124
|
+
ParsedUnit(
|
|
125
|
+
name=node.name,
|
|
126
|
+
kind=(
|
|
127
|
+
"async_function"
|
|
128
|
+
if isinstance(node, ast.AsyncFunctionDef)
|
|
129
|
+
else "function"
|
|
130
|
+
),
|
|
131
|
+
line=node.lineno,
|
|
132
|
+
end_line=getattr(node, "end_lineno", None),
|
|
133
|
+
signature=f"({', '.join(args)})",
|
|
134
|
+
return_type=ast.unparse(node.returns) if node.returns else None,
|
|
135
|
+
references=refs,
|
|
136
|
+
annotations={
|
|
137
|
+
arg.arg: ast.unparse(arg.annotation)
|
|
138
|
+
for arg in node.args.args
|
|
139
|
+
if arg.annotation is not None
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
return units
|
|
144
|
+
|
|
145
|
+
def _parse_structural(self, language: str, source: str) -> list[ParsedUnit]:
|
|
146
|
+
units: list[ParsedUnit] = []
|
|
147
|
+
patterns = self._patterns(language)
|
|
148
|
+
for line_no, line in enumerate(source.splitlines(), 1):
|
|
149
|
+
stripped = line.strip()
|
|
150
|
+
for kind, pattern in patterns:
|
|
151
|
+
match = pattern.search(stripped)
|
|
152
|
+
if not match:
|
|
153
|
+
continue
|
|
154
|
+
name = match.group("name")
|
|
155
|
+
bases = tuple(
|
|
156
|
+
item.strip()
|
|
157
|
+
for item in (match.groupdict().get("bases") or "").split(",")
|
|
158
|
+
if item.strip()
|
|
159
|
+
)
|
|
160
|
+
units.append(
|
|
161
|
+
ParsedUnit(
|
|
162
|
+
name=name,
|
|
163
|
+
kind=kind,
|
|
164
|
+
line=line_no,
|
|
165
|
+
signature=match.groupdict().get("signature"),
|
|
166
|
+
return_type=match.groupdict().get("return"),
|
|
167
|
+
bases=bases,
|
|
168
|
+
modifiers=tuple(
|
|
169
|
+
item
|
|
170
|
+
for item in (match.groupdict().get("mods") or "").split()
|
|
171
|
+
if item
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
break
|
|
176
|
+
return units
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _patterns(language: str) -> tuple[tuple[str, re.Pattern[str]], ...]:
|
|
180
|
+
common_class = re.compile(
|
|
181
|
+
r"(?P<mods>(?:(?:export|public|private|protected|abstract|final|sealed|static)\s+)*)"
|
|
182
|
+
r"(?:class|interface|trait|struct|enum)\s+(?P<name>[A-Za-z_][\w$]*)"
|
|
183
|
+
r"(?:\s+(?:extends|implements|:)\s+(?P<bases>[^\{]+))?"
|
|
184
|
+
)
|
|
185
|
+
c_like_function = re.compile(
|
|
186
|
+
r"(?P<mods>(?:(?:export|public|private|protected|static|async|virtual|override|final|unsafe)\s+)*)"
|
|
187
|
+
r"(?:(?P<return>[A-Za-z_][\w:<>,\[\]?*& ]*)\s+)?"
|
|
188
|
+
r"(?P<name>[A-Za-z_][\w$]*)\s*(?P<signature>\([^;{}]*\))\s*(?:\{|=>)"
|
|
189
|
+
)
|
|
190
|
+
function_keyword = re.compile(
|
|
191
|
+
r"(?P<mods>(?:(?:export|async|pub|unsafe)\s+)*)"
|
|
192
|
+
r"(?:function|fn|func|def)\s+(?P<name>[A-Za-z_][\w$!?]*)\s*(?P<signature>\([^)]*\))"
|
|
193
|
+
r"(?:\s*(?:->|:)\s*(?P<return>[^\{=]+))?"
|
|
194
|
+
)
|
|
195
|
+
arrow = re.compile(
|
|
196
|
+
r"(?:(?:export|const|let|var)\s+)+(?P<name>[A-Za-z_$][\w$]*)\s*=\s*"
|
|
197
|
+
r"(?:async\s+)?(?P<signature>\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>"
|
|
198
|
+
)
|
|
199
|
+
ruby_class = re.compile(r"(?:class|module)\s+(?P<name>[A-Za-z_][\w:]*)")
|
|
200
|
+
if language == "ruby":
|
|
201
|
+
return (("class", ruby_class), ("function", function_keyword))
|
|
202
|
+
if language in {"typescript", "javascript"}:
|
|
203
|
+
return (
|
|
204
|
+
("class", common_class),
|
|
205
|
+
("function", function_keyword),
|
|
206
|
+
("function", arrow),
|
|
207
|
+
("function", c_like_function),
|
|
208
|
+
)
|
|
209
|
+
return (
|
|
210
|
+
("class", common_class),
|
|
211
|
+
("function", function_keyword),
|
|
212
|
+
("function", c_like_function),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def resolve_types(self, units: list[ParsedUnit]) -> dict[str, set[str]]:
|
|
216
|
+
names = {unit.name for unit in units}
|
|
217
|
+
resolved: dict[str, set[str]] = {}
|
|
218
|
+
for unit in units:
|
|
219
|
+
candidates = set(unit.bases)
|
|
220
|
+
candidates.update(unit.annotations.values())
|
|
221
|
+
if unit.return_type:
|
|
222
|
+
candidates.add(unit.return_type)
|
|
223
|
+
matches = {
|
|
224
|
+
name
|
|
225
|
+
for name in names
|
|
226
|
+
if any(
|
|
227
|
+
re.search(rf"\b{re.escape(name)}\b", candidate)
|
|
228
|
+
for candidate in candidates
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
resolved[unit.name] = matches
|
|
232
|
+
return resolved
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Extended MCP application exposing guarded semantic editing tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from codecortex.editing import EditService
|
|
10
|
+
from codecortex.mcp.server import MCPApplication, MCPServer
|
|
11
|
+
from codecortex.runtime import build_runtime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _schema(properties: dict[str, Any], required: list[str]) -> dict[str, Any]:
|
|
15
|
+
return {
|
|
16
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": properties,
|
|
19
|
+
"required": required,
|
|
20
|
+
"additionalProperties": False,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_EDIT_TOOLS = {
|
|
25
|
+
"cortex_rename_symbol",
|
|
26
|
+
"cortex_replace_symbol_body",
|
|
27
|
+
"cortex_insert_before_symbol",
|
|
28
|
+
"cortex_insert_after_symbol",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ExtendedMCPApplication(MCPApplication):
|
|
33
|
+
def tools(self) -> list[dict[str, Any]]:
|
|
34
|
+
tools = super().tools()
|
|
35
|
+
text = {"type": "string", "minLength": 1}
|
|
36
|
+
tools.extend(
|
|
37
|
+
[
|
|
38
|
+
{
|
|
39
|
+
"name": "cortex_rename_symbol",
|
|
40
|
+
"description": "Rename a symbol across the codebase using language-server refactoring.",
|
|
41
|
+
"inputSchema": _schema(
|
|
42
|
+
{"path": text, "name_path": text, "new_name": text},
|
|
43
|
+
["path", "name_path", "new_name"],
|
|
44
|
+
),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "cortex_replace_symbol_body",
|
|
48
|
+
"description": "Replace one symbol definition after a semantic preflight read.",
|
|
49
|
+
"inputSchema": _schema(
|
|
50
|
+
{"path": text, "name_path": text, "body": text},
|
|
51
|
+
["path", "name_path", "body"],
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "cortex_insert_before_symbol",
|
|
56
|
+
"description": "Insert code immediately before a semantic symbol.",
|
|
57
|
+
"inputSchema": _schema(
|
|
58
|
+
{"path": text, "name_path": text, "body": text},
|
|
59
|
+
["path", "name_path", "body"],
|
|
60
|
+
),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "cortex_insert_after_symbol",
|
|
64
|
+
"description": "Insert code immediately after a semantic symbol.",
|
|
65
|
+
"inputSchema": _schema(
|
|
66
|
+
{"path": text, "name_path": text, "body": text},
|
|
67
|
+
["path", "name_path", "body"],
|
|
68
|
+
),
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
)
|
|
72
|
+
return tools
|
|
73
|
+
|
|
74
|
+
async def call(self, name: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
75
|
+
self.runtime.telemetry.emit("mcp.tool.called", tool=name)
|
|
76
|
+
if name not in _EDIT_TOOLS:
|
|
77
|
+
return await super().call(name, arguments)
|
|
78
|
+
service = EditService(self.runtime)
|
|
79
|
+
path = str(arguments["path"])
|
|
80
|
+
name_path = str(arguments["name_path"])
|
|
81
|
+
if name == "cortex_rename_symbol":
|
|
82
|
+
payload = await asyncio.to_thread(
|
|
83
|
+
service.rename,
|
|
84
|
+
path,
|
|
85
|
+
name_path,
|
|
86
|
+
str(arguments["new_name"]),
|
|
87
|
+
)
|
|
88
|
+
elif name == "cortex_replace_symbol_body":
|
|
89
|
+
payload = await asyncio.to_thread(
|
|
90
|
+
service.replace,
|
|
91
|
+
path,
|
|
92
|
+
name_path,
|
|
93
|
+
str(arguments["body"]),
|
|
94
|
+
)
|
|
95
|
+
elif name == "cortex_insert_before_symbol":
|
|
96
|
+
payload = await asyncio.to_thread(
|
|
97
|
+
service.insert_before,
|
|
98
|
+
path,
|
|
99
|
+
name_path,
|
|
100
|
+
str(arguments["body"]),
|
|
101
|
+
)
|
|
102
|
+
else:
|
|
103
|
+
payload = await asyncio.to_thread(
|
|
104
|
+
service.insert_after,
|
|
105
|
+
path,
|
|
106
|
+
name_path,
|
|
107
|
+
str(arguments["body"]),
|
|
108
|
+
)
|
|
109
|
+
return {"edited": True, "operation": name, "backend_result": payload}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def run_stdio(project_root: Path | None = None) -> None:
|
|
113
|
+
runtime = build_runtime(project_root)
|
|
114
|
+
asyncio.run(MCPServer(ExtendedMCPApplication(runtime)).serve_stdio())
|