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,26 @@
|
|
|
1
|
+
"""Default local engine assembly."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from codecortex.config import CortexConfig
|
|
6
|
+
from codecortex.core.contracts import MemoryStore
|
|
7
|
+
from codecortex.engines.builtin.memory import MemoryEngine
|
|
8
|
+
from codecortex.engines.builtin.repository import RepositoryEngine
|
|
9
|
+
from codecortex.engines.builtin.symbols import SymbolEngine
|
|
10
|
+
from codecortex.engines.builtin.validation import ValidationEngine
|
|
11
|
+
from codecortex.engines.registry import EngineRegistry
|
|
12
|
+
from codecortex.memory import JsonMemoryStore
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_default_registry(
|
|
16
|
+
config: CortexConfig,
|
|
17
|
+
memory_store: MemoryStore | None = None,
|
|
18
|
+
) -> EngineRegistry:
|
|
19
|
+
config.ensure_directories()
|
|
20
|
+
memory = memory_store or JsonMemoryStore(config.memory_dir)
|
|
21
|
+
registry = EngineRegistry()
|
|
22
|
+
registry.register(RepositoryEngine(config.project_root))
|
|
23
|
+
registry.register(SymbolEngine(config.project_root))
|
|
24
|
+
registry.register(MemoryEngine(memory))
|
|
25
|
+
registry.register(ValidationEngine(config.project_root))
|
|
26
|
+
return registry
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Memory search engine."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from codecortex.core.contracts import Engine, MemoryStore
|
|
6
|
+
from codecortex.core.models import AgentRequest, Capability, ContextChunk, EngineResult
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MemoryEngine(Engine):
|
|
10
|
+
capability = Capability.MEMORY
|
|
11
|
+
|
|
12
|
+
def __init__(self, store: MemoryStore, namespace: str = "project") -> None:
|
|
13
|
+
self.store = store
|
|
14
|
+
self.namespace = namespace
|
|
15
|
+
|
|
16
|
+
async def health(self) -> bool:
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
async def execute(self, request: AgentRequest) -> EngineResult:
|
|
20
|
+
matches = await self.store.search(self.namespace, request.query, limit=10)
|
|
21
|
+
content = "\n\n".join(matches) if matches else "No matching project memory found."
|
|
22
|
+
return EngineResult(
|
|
23
|
+
capability=self.capability,
|
|
24
|
+
content=content,
|
|
25
|
+
chunks=[
|
|
26
|
+
ContextChunk(
|
|
27
|
+
source="project-memory",
|
|
28
|
+
content=content,
|
|
29
|
+
tokens=max(1, len(content) // 4),
|
|
30
|
+
relevance=0.75 if matches else 0.20,
|
|
31
|
+
metadata={"matches": len(matches)},
|
|
32
|
+
)
|
|
33
|
+
],
|
|
34
|
+
metadata={"matches": len(matches)},
|
|
35
|
+
)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Graph-backed local repository intelligence engine."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from codecortex.core.contracts import Engine
|
|
8
|
+
from codecortex.core.models import AgentRequest, Capability, ContextChunk, EngineResult
|
|
9
|
+
from codecortex.indexing import ProjectIndexer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RepositoryEngine(Engine):
|
|
13
|
+
capability = Capability.REPOSITORY
|
|
14
|
+
|
|
15
|
+
def __init__(self, project_root: Path, max_files: int = 5_000) -> None:
|
|
16
|
+
self.project_root = project_root.resolve()
|
|
17
|
+
self.max_files = max_files
|
|
18
|
+
|
|
19
|
+
async def health(self) -> bool:
|
|
20
|
+
return self.project_root.exists() and self.project_root.is_dir()
|
|
21
|
+
|
|
22
|
+
async def execute(self, request: AgentRequest) -> EngineResult:
|
|
23
|
+
graph = ProjectIndexer(self.project_root, max_files=self.max_files).build()
|
|
24
|
+
graph_path = self.project_root / ".codecortex" / "index" / "graph.json"
|
|
25
|
+
graph.save(graph_path)
|
|
26
|
+
|
|
27
|
+
counts = graph.counts()
|
|
28
|
+
matches = graph.search(request.query)
|
|
29
|
+
import_edges = sum(1 for edge in graph.edges if edge.kind == "imports")
|
|
30
|
+
define_edges = sum(1 for edge in graph.edges if edge.kind == "defines")
|
|
31
|
+
|
|
32
|
+
summary = [
|
|
33
|
+
f"Project root: {self.project_root}",
|
|
34
|
+
f"Graph nodes: {len(graph.nodes)}",
|
|
35
|
+
f"Graph edges: {len(graph.edges)}",
|
|
36
|
+
f"Files: {counts.get('file', 0)}",
|
|
37
|
+
f"Symbols: {define_edges}",
|
|
38
|
+
f"Import relationships: {import_edges}",
|
|
39
|
+
]
|
|
40
|
+
if matches:
|
|
41
|
+
summary.append(
|
|
42
|
+
"Relevant graph nodes:\n"
|
|
43
|
+
+ "\n".join(
|
|
44
|
+
f"- {node.kind}: {node.name}"
|
|
45
|
+
+ (f" ({node.path}:{node.line or 1})" if node.path else "")
|
|
46
|
+
for node in matches
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
content = "\n".join(summary)
|
|
51
|
+
return EngineResult(
|
|
52
|
+
capability=self.capability,
|
|
53
|
+
content=content,
|
|
54
|
+
chunks=[
|
|
55
|
+
ContextChunk(
|
|
56
|
+
source="repository-graph",
|
|
57
|
+
content=content,
|
|
58
|
+
tokens=max(1, len(content) // 4),
|
|
59
|
+
relevance=0.85,
|
|
60
|
+
metadata={
|
|
61
|
+
"nodes": len(graph.nodes),
|
|
62
|
+
"edges": len(graph.edges),
|
|
63
|
+
"matches": len(matches),
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
],
|
|
67
|
+
metadata={
|
|
68
|
+
"nodes": len(graph.nodes),
|
|
69
|
+
"edges": len(graph.edges),
|
|
70
|
+
"node_counts": counts,
|
|
71
|
+
"graph_path": str(graph_path),
|
|
72
|
+
},
|
|
73
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Local multi-language symbol intelligence engine."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from codecortex.core.contracts import Engine
|
|
8
|
+
from codecortex.core.models import AgentRequest, Capability, ContextChunk, EngineResult
|
|
9
|
+
from codecortex.symbols import SymbolProviderRegistry, SymbolRecord
|
|
10
|
+
|
|
11
|
+
_EXCLUDED = {".git", ".codecortex", ".venv", "venv", "node_modules", "__pycache__"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SymbolEngine(Engine):
|
|
15
|
+
capability = Capability.SYMBOLS
|
|
16
|
+
|
|
17
|
+
def __init__(self, project_root: Path, max_files: int = 5_000) -> None:
|
|
18
|
+
self.project_root = project_root.resolve()
|
|
19
|
+
self.max_files = max_files
|
|
20
|
+
self.providers = SymbolProviderRegistry()
|
|
21
|
+
|
|
22
|
+
async def health(self) -> bool:
|
|
23
|
+
return self.project_root.exists() and self.project_root.is_dir()
|
|
24
|
+
|
|
25
|
+
def _symbols(self) -> list[SymbolRecord]:
|
|
26
|
+
symbols: list[SymbolRecord] = []
|
|
27
|
+
count = 0
|
|
28
|
+
for path in self.project_root.rglob("*"):
|
|
29
|
+
if count >= self.max_files:
|
|
30
|
+
break
|
|
31
|
+
if not path.is_file() or not self.providers.supports(path):
|
|
32
|
+
continue
|
|
33
|
+
relative = path.relative_to(self.project_root)
|
|
34
|
+
if any(part in _EXCLUDED for part in relative.parts):
|
|
35
|
+
continue
|
|
36
|
+
count += 1
|
|
37
|
+
try:
|
|
38
|
+
source = path.read_text(encoding="utf-8")
|
|
39
|
+
except (OSError, UnicodeDecodeError):
|
|
40
|
+
continue
|
|
41
|
+
symbols.extend(self.providers.extract(path, source))
|
|
42
|
+
return symbols
|
|
43
|
+
|
|
44
|
+
async def execute(self, request: AgentRequest) -> EngineResult:
|
|
45
|
+
symbols = self._symbols()
|
|
46
|
+
terms = {
|
|
47
|
+
term.lower().strip(".,:;()[]{}")
|
|
48
|
+
for term in request.query.split()
|
|
49
|
+
if len(term) > 2
|
|
50
|
+
}
|
|
51
|
+
ranked: list[tuple[int, SymbolRecord]] = []
|
|
52
|
+
for symbol in symbols:
|
|
53
|
+
name = symbol.name.lower()
|
|
54
|
+
path = str(symbol.path.relative_to(self.project_root)).lower()
|
|
55
|
+
score = sum(
|
|
56
|
+
5 if term == name else 3 if term in name else 1 if term in path else 0
|
|
57
|
+
for term in terms
|
|
58
|
+
)
|
|
59
|
+
if score:
|
|
60
|
+
ranked.append((score, symbol))
|
|
61
|
+
ranked.sort(key=lambda item: (-item[0], item[1].line, item[1].name))
|
|
62
|
+
matches = [symbol for _, symbol in ranked[:50]]
|
|
63
|
+
lines = [
|
|
64
|
+
(
|
|
65
|
+
f"{symbol.language}:{symbol.kind} {symbol.name} — "
|
|
66
|
+
f"{symbol.path.relative_to(self.project_root)}:{symbol.line}"
|
|
67
|
+
)
|
|
68
|
+
for symbol in matches
|
|
69
|
+
]
|
|
70
|
+
content = "\n".join(lines) if lines else "No matching symbols found."
|
|
71
|
+
languages = sorted({symbol.language for symbol in symbols})
|
|
72
|
+
return EngineResult(
|
|
73
|
+
capability=self.capability,
|
|
74
|
+
content=content,
|
|
75
|
+
chunks=[
|
|
76
|
+
ContextChunk(
|
|
77
|
+
source="symbol-index",
|
|
78
|
+
content=content,
|
|
79
|
+
tokens=max(1, len(content) // 4),
|
|
80
|
+
relevance=0.90 if matches else 0.30,
|
|
81
|
+
metadata={"matches": len(matches), "languages": languages},
|
|
82
|
+
)
|
|
83
|
+
],
|
|
84
|
+
metadata={
|
|
85
|
+
"symbols_indexed": len(symbols),
|
|
86
|
+
"matches": len(matches),
|
|
87
|
+
"languages": languages,
|
|
88
|
+
},
|
|
89
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Local validation engine."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from codecortex.core.contracts import Engine
|
|
9
|
+
from codecortex.core.models import AgentRequest, Capability, ContextChunk, EngineResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ValidationEngine(Engine):
|
|
13
|
+
capability = Capability.VALIDATION
|
|
14
|
+
|
|
15
|
+
def __init__(self, project_root: Path, max_files: int = 2_000) -> None:
|
|
16
|
+
self.project_root = project_root.resolve()
|
|
17
|
+
self.max_files = max_files
|
|
18
|
+
|
|
19
|
+
async def health(self) -> bool:
|
|
20
|
+
return self.project_root.exists() and self.project_root.is_dir()
|
|
21
|
+
|
|
22
|
+
async def execute(self, request: AgentRequest) -> EngineResult:
|
|
23
|
+
del request
|
|
24
|
+
checked = 0
|
|
25
|
+
issues: list[str] = []
|
|
26
|
+
for path in self.project_root.rglob("*.py"):
|
|
27
|
+
if checked >= self.max_files:
|
|
28
|
+
break
|
|
29
|
+
if any(part in {".git", ".codecortex", ".venv", "venv", "__pycache__"} for part in path.parts):
|
|
30
|
+
continue
|
|
31
|
+
checked += 1
|
|
32
|
+
try:
|
|
33
|
+
ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
|
34
|
+
except SyntaxError as exc:
|
|
35
|
+
relative = path.relative_to(self.project_root)
|
|
36
|
+
issues.append(f"{relative}:{exc.lineno}: {exc.msg}")
|
|
37
|
+
except (OSError, UnicodeDecodeError):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
content = "Python syntax validation passed." if not issues else "\n".join(issues[:100])
|
|
41
|
+
return EngineResult(
|
|
42
|
+
capability=self.capability,
|
|
43
|
+
content=content,
|
|
44
|
+
chunks=[
|
|
45
|
+
ContextChunk(
|
|
46
|
+
source="validation",
|
|
47
|
+
content=content,
|
|
48
|
+
tokens=max(1, len(content) // 4),
|
|
49
|
+
relevance=0.95,
|
|
50
|
+
metadata={"issues": len(issues), "checked": checked},
|
|
51
|
+
)
|
|
52
|
+
],
|
|
53
|
+
metadata={"checked": checked, "issues": len(issues)},
|
|
54
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Capability-based engine registry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from codecortex.core.contracts import Engine
|
|
6
|
+
from codecortex.core.models import Capability
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EngineRegistry:
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
self._engines: dict[Capability, Engine] = {}
|
|
12
|
+
|
|
13
|
+
def register(self, engine: Engine) -> None:
|
|
14
|
+
self._engines[engine.capability] = engine
|
|
15
|
+
|
|
16
|
+
def get(self, capability: Capability) -> Engine | None:
|
|
17
|
+
return self._engines.get(capability)
|
|
18
|
+
|
|
19
|
+
def capabilities(self) -> list[Capability]:
|
|
20
|
+
return list(self._engines)
|
|
21
|
+
|
|
22
|
+
async def health(self) -> dict[Capability, bool]:
|
|
23
|
+
result: dict[Capability, bool] = {}
|
|
24
|
+
for capability, engine in self._engines.items():
|
|
25
|
+
result[capability] = await engine.health()
|
|
26
|
+
return result
|
codecortex/entrypoint.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Extended public CLI surface for packaging, backends, agent setup, and edits."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import asdict
|
|
6
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Annotated
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
import codecortex.cli as cli_module
|
|
15
|
+
from codecortex.backends import (
|
|
16
|
+
BACKENDS,
|
|
17
|
+
BackendManager,
|
|
18
|
+
ContextBackendAdapter,
|
|
19
|
+
GraphBackendAdapter,
|
|
20
|
+
SymbolBackendAdapter,
|
|
21
|
+
)
|
|
22
|
+
from codecortex.cli import app
|
|
23
|
+
from codecortex.editing import EditService
|
|
24
|
+
from codecortex.integrations import AgentConfigurator, AgentTarget
|
|
25
|
+
from codecortex.mcp.extended import run_stdio as extended_run_stdio
|
|
26
|
+
from codecortex.runtime import build_runtime
|
|
27
|
+
from codecortex.setup import ProjectSetup
|
|
28
|
+
|
|
29
|
+
cli_module.run_stdio = extended_run_stdio
|
|
30
|
+
|
|
31
|
+
console = Console()
|
|
32
|
+
backend_app = typer.Typer(help="Install and inspect isolated intelligence backends.")
|
|
33
|
+
agents_app = typer.Typer(help="Detect and configure coding-agent integrations.")
|
|
34
|
+
edit_app = typer.Typer(help="Perform guarded language-server semantic edits.")
|
|
35
|
+
app.add_typer(backend_app, name="backend")
|
|
36
|
+
app.add_typer(agents_app, name="agents")
|
|
37
|
+
app.add_typer(edit_app, name="edit")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _manager() -> BackendManager:
|
|
41
|
+
return BackendManager(timeout_seconds=1200)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _targets(value: str) -> tuple[str, ...]:
|
|
45
|
+
if value == "all":
|
|
46
|
+
return tuple(BACKENDS)
|
|
47
|
+
if value not in BACKENDS:
|
|
48
|
+
raise typer.BadParameter(f"expected one of: all, {', '.join(BACKENDS)}")
|
|
49
|
+
return (value,)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _adapter(key: str, root: Path, manager: BackendManager):
|
|
53
|
+
if key == "graph":
|
|
54
|
+
return GraphBackendAdapter(root, manager)
|
|
55
|
+
if key == "symbols":
|
|
56
|
+
return SymbolBackendAdapter(root, manager)
|
|
57
|
+
if key == "context":
|
|
58
|
+
return ContextBackendAdapter(root, manager)
|
|
59
|
+
raise KeyError(key)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _edit_service(path: Path) -> EditService:
|
|
63
|
+
return EditService(build_runtime(path.expanduser().resolve()))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@app.command("version")
|
|
67
|
+
def version_command() -> None:
|
|
68
|
+
try:
|
|
69
|
+
current = version("codecortex-context-engine")
|
|
70
|
+
except PackageNotFoundError:
|
|
71
|
+
current = "0+unknown"
|
|
72
|
+
console.print(current)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@backend_app.command("list")
|
|
76
|
+
def backend_list() -> None:
|
|
77
|
+
manager = _manager()
|
|
78
|
+
table = Table(title="CodeCortex Backends")
|
|
79
|
+
table.add_column("Backend")
|
|
80
|
+
table.add_column("Installed")
|
|
81
|
+
table.add_column("Revision")
|
|
82
|
+
table.add_column("Capabilities")
|
|
83
|
+
for key, spec in BACKENDS.items():
|
|
84
|
+
table.add_row(
|
|
85
|
+
key,
|
|
86
|
+
"yes" if manager.is_installed(spec) else "no",
|
|
87
|
+
spec.revision[:12],
|
|
88
|
+
", ".join(spec.capabilities),
|
|
89
|
+
)
|
|
90
|
+
console.print(table)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@backend_app.command("install")
|
|
94
|
+
def backend_install(
|
|
95
|
+
target: Annotated[str, typer.Argument(help="graph, symbols, context, or all")] = "all",
|
|
96
|
+
) -> None:
|
|
97
|
+
manager = _manager()
|
|
98
|
+
failures: list[str] = []
|
|
99
|
+
for key in _targets(target):
|
|
100
|
+
spec = BACKENDS[key]
|
|
101
|
+
console.print(f"Installing [bold]{key}[/bold] at {spec.revision[:12]}…")
|
|
102
|
+
try:
|
|
103
|
+
command = manager.ensure(spec)
|
|
104
|
+
console.print(f"[green]✓[/green] {key}: {command}")
|
|
105
|
+
except Exception as exc:
|
|
106
|
+
failures.append(key)
|
|
107
|
+
console.print(f"[red]✗[/red] {key}: {type(exc).__name__}: {exc}")
|
|
108
|
+
if failures:
|
|
109
|
+
raise typer.Exit(code=2)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@backend_app.command("doctor")
|
|
113
|
+
def backend_doctor(
|
|
114
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
115
|
+
) -> None:
|
|
116
|
+
root = path.expanduser().resolve()
|
|
117
|
+
manager = _manager()
|
|
118
|
+
table = Table(title="Backend Health")
|
|
119
|
+
table.add_column("Backend")
|
|
120
|
+
table.add_column("Installed")
|
|
121
|
+
table.add_column("Healthy")
|
|
122
|
+
table.add_column("Contract")
|
|
123
|
+
failed = False
|
|
124
|
+
for key in BACKENDS:
|
|
125
|
+
adapter = _adapter(key, root, manager)
|
|
126
|
+
status = adapter.status()
|
|
127
|
+
table.add_row(
|
|
128
|
+
key,
|
|
129
|
+
"yes" if status.installed else "no",
|
|
130
|
+
"yes" if status.healthy else "no",
|
|
131
|
+
f"v{status.contract_version}",
|
|
132
|
+
)
|
|
133
|
+
if status.installed and not status.healthy:
|
|
134
|
+
failed = True
|
|
135
|
+
console.print(table)
|
|
136
|
+
if failed:
|
|
137
|
+
raise typer.Exit(code=2)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@backend_app.command("remove")
|
|
141
|
+
def backend_remove(
|
|
142
|
+
target: Annotated[str, typer.Argument(help="graph, symbols, context, or all")],
|
|
143
|
+
) -> None:
|
|
144
|
+
manager = _manager()
|
|
145
|
+
for key in _targets(target):
|
|
146
|
+
manager.remove(BACKENDS[key])
|
|
147
|
+
console.print(f"[green]Removed[/green] {key}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@agents_app.command("detect")
|
|
151
|
+
def agents_detect(
|
|
152
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
153
|
+
) -> None:
|
|
154
|
+
found = AgentConfigurator(path).detect()
|
|
155
|
+
if not found:
|
|
156
|
+
console.print("No supported coding agents detected.")
|
|
157
|
+
return
|
|
158
|
+
for target in found:
|
|
159
|
+
console.print(target.value)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@agents_app.command("configure")
|
|
163
|
+
def agents_configure(
|
|
164
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
165
|
+
target: Annotated[list[AgentTarget] | None, typer.Option("--target", "-t")] = None,
|
|
166
|
+
all_supported: Annotated[bool, typer.Option("--all")] = False,
|
|
167
|
+
dry_run: Annotated[bool, typer.Option("--dry-run")] = False,
|
|
168
|
+
) -> None:
|
|
169
|
+
configurator = AgentConfigurator(path)
|
|
170
|
+
selected = tuple(AgentTarget) if all_supported else tuple(target or configurator.detect())
|
|
171
|
+
mutations = configurator.configure(selected, dry_run=dry_run)
|
|
172
|
+
for item in mutations:
|
|
173
|
+
state = (
|
|
174
|
+
"would update"
|
|
175
|
+
if dry_run and item.changed
|
|
176
|
+
else "updated"
|
|
177
|
+
if item.changed
|
|
178
|
+
else "unchanged"
|
|
179
|
+
)
|
|
180
|
+
console.print(f"{item.target.value}: {state} {item.path}")
|
|
181
|
+
if item.backup:
|
|
182
|
+
console.print(f" backup: {item.backup}")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@edit_app.command("rename")
|
|
186
|
+
def edit_rename(
|
|
187
|
+
relative_path: Annotated[str, typer.Argument(help="Repository-relative file")],
|
|
188
|
+
name_path: Annotated[str, typer.Argument(help="Semantic symbol name path")],
|
|
189
|
+
new_name: Annotated[str, typer.Argument(help="New symbol name")],
|
|
190
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
191
|
+
) -> None:
|
|
192
|
+
console.print_json(data=_edit_service(path).rename(relative_path, name_path, new_name))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@edit_app.command("replace")
|
|
196
|
+
def edit_replace(
|
|
197
|
+
relative_path: Annotated[str, typer.Argument()],
|
|
198
|
+
name_path: Annotated[str, typer.Argument()],
|
|
199
|
+
body_file: Annotated[Path, typer.Option("--body-file")],
|
|
200
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
201
|
+
) -> None:
|
|
202
|
+
body = body_file.read_text(encoding="utf-8")
|
|
203
|
+
console.print_json(data=_edit_service(path).replace(relative_path, name_path, body))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@edit_app.command("insert-before")
|
|
207
|
+
def edit_insert_before(
|
|
208
|
+
relative_path: Annotated[str, typer.Argument()],
|
|
209
|
+
name_path: Annotated[str, typer.Argument()],
|
|
210
|
+
body_file: Annotated[Path, typer.Option("--body-file")],
|
|
211
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
212
|
+
) -> None:
|
|
213
|
+
body = body_file.read_text(encoding="utf-8")
|
|
214
|
+
console.print_json(data=_edit_service(path).insert_before(relative_path, name_path, body))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@edit_app.command("insert-after")
|
|
218
|
+
def edit_insert_after(
|
|
219
|
+
relative_path: Annotated[str, typer.Argument()],
|
|
220
|
+
name_path: Annotated[str, typer.Argument()],
|
|
221
|
+
body_file: Annotated[Path, typer.Option("--body-file")],
|
|
222
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
223
|
+
) -> None:
|
|
224
|
+
body = body_file.read_text(encoding="utf-8")
|
|
225
|
+
console.print_json(data=_edit_service(path).insert_after(relative_path, name_path, body))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@app.command("bootstrap")
|
|
229
|
+
def bootstrap(
|
|
230
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
231
|
+
install_backends: Annotated[bool, typer.Option("--backends/--no-backends")] = True,
|
|
232
|
+
configure_agents: Annotated[bool, typer.Option("--agents/--no-agents")] = True,
|
|
233
|
+
strict: Annotated[bool, typer.Option("--strict")] = False,
|
|
234
|
+
) -> None:
|
|
235
|
+
root = path.expanduser().resolve()
|
|
236
|
+
result = ProjectSetup(root).run()
|
|
237
|
+
console.print(
|
|
238
|
+
f"Core ready: {result.index.tracked} files, {result.symbols} symbols, "
|
|
239
|
+
f"{result.graph_nodes} graph nodes."
|
|
240
|
+
)
|
|
241
|
+
failures: list[str] = []
|
|
242
|
+
if install_backends:
|
|
243
|
+
manager = _manager()
|
|
244
|
+
for key, spec in BACKENDS.items():
|
|
245
|
+
try:
|
|
246
|
+
manager.ensure(spec)
|
|
247
|
+
console.print(f"[green]✓[/green] backend {key}")
|
|
248
|
+
except Exception as exc:
|
|
249
|
+
failures.append(key)
|
|
250
|
+
console.print(f"[yellow]![/yellow] backend {key}: {type(exc).__name__}: {exc}")
|
|
251
|
+
if configure_agents:
|
|
252
|
+
configurator = AgentConfigurator(root)
|
|
253
|
+
for mutation in configurator.configure():
|
|
254
|
+
console.print(f"[green]✓[/green] agent {mutation.target.value}: {mutation.path}")
|
|
255
|
+
if strict and failures:
|
|
256
|
+
raise typer.Exit(code=2)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@app.command("backend-status")
|
|
260
|
+
def backend_status(
|
|
261
|
+
path: Annotated[Path, typer.Option("--path", "-p")] = Path("."),
|
|
262
|
+
) -> None:
|
|
263
|
+
root = path.expanduser().resolve()
|
|
264
|
+
manager = _manager()
|
|
265
|
+
payload = {key: asdict(_adapter(key, root, manager).status()) for key in BACKENDS}
|
|
266
|
+
console.print_json(data=payload)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Benchmark and external evaluation infrastructure."""
|
|
2
|
+
|
|
3
|
+
from codecortex.evaluation.external import (
|
|
4
|
+
DeterministicGrader,
|
|
5
|
+
EvaluationCase,
|
|
6
|
+
EvaluationExpectation,
|
|
7
|
+
EvaluationOutput,
|
|
8
|
+
EvaluationReport,
|
|
9
|
+
ExternalEvaluationSuite,
|
|
10
|
+
SubprocessEvaluationTarget,
|
|
11
|
+
)
|
|
12
|
+
from codecortex.evaluation.production import (
|
|
13
|
+
AgentProtocolResult,
|
|
14
|
+
BenchmarkCaseSpec,
|
|
15
|
+
InstrumentedAgentRunner,
|
|
16
|
+
ObservedMetrics,
|
|
17
|
+
ProductionBenchmarkReport,
|
|
18
|
+
ProductionBenchmarkRunner,
|
|
19
|
+
RepositorySpec,
|
|
20
|
+
ScenarioResult,
|
|
21
|
+
SetupMeasurement,
|
|
22
|
+
load_repository_specs,
|
|
23
|
+
)
|
|
24
|
+
from codecortex.evaluation.regression import (
|
|
25
|
+
BenchmarkHistory,
|
|
26
|
+
BenchmarkSnapshot,
|
|
27
|
+
GateReport,
|
|
28
|
+
MetricPolicy,
|
|
29
|
+
RegressionGate,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"AgentProtocolResult",
|
|
34
|
+
"BenchmarkCaseSpec",
|
|
35
|
+
"BenchmarkHistory",
|
|
36
|
+
"BenchmarkSnapshot",
|
|
37
|
+
"DeterministicGrader",
|
|
38
|
+
"EvaluationCase",
|
|
39
|
+
"EvaluationExpectation",
|
|
40
|
+
"EvaluationOutput",
|
|
41
|
+
"EvaluationReport",
|
|
42
|
+
"ExternalEvaluationSuite",
|
|
43
|
+
"GateReport",
|
|
44
|
+
"InstrumentedAgentRunner",
|
|
45
|
+
"MetricPolicy",
|
|
46
|
+
"ObservedMetrics",
|
|
47
|
+
"ProductionBenchmarkReport",
|
|
48
|
+
"ProductionBenchmarkRunner",
|
|
49
|
+
"RegressionGate",
|
|
50
|
+
"RepositorySpec",
|
|
51
|
+
"ScenarioResult",
|
|
52
|
+
"SetupMeasurement",
|
|
53
|
+
"SubprocessEvaluationTarget",
|
|
54
|
+
"load_repository_specs",
|
|
55
|
+
]
|