codexa 0.4.0__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.
- codexa-0.4.0.dist-info/METADATA +650 -0
- codexa-0.4.0.dist-info/RECORD +189 -0
- codexa-0.4.0.dist-info/WHEEL +5 -0
- codexa-0.4.0.dist-info/entry_points.txt +2 -0
- codexa-0.4.0.dist-info/licenses/LICENSE +21 -0
- codexa-0.4.0.dist-info/top_level.txt +1 -0
- semantic_code_intelligence/__init__.py +5 -0
- semantic_code_intelligence/analysis/__init__.py +21 -0
- semantic_code_intelligence/analysis/ai_features.py +351 -0
- semantic_code_intelligence/bridge/__init__.py +28 -0
- semantic_code_intelligence/bridge/context_provider.py +245 -0
- semantic_code_intelligence/bridge/protocol.py +167 -0
- semantic_code_intelligence/bridge/server.py +348 -0
- semantic_code_intelligence/bridge/vscode.py +271 -0
- semantic_code_intelligence/ci/__init__.py +13 -0
- semantic_code_intelligence/ci/hooks.py +98 -0
- semantic_code_intelligence/ci/hotspots.py +272 -0
- semantic_code_intelligence/ci/impact.py +246 -0
- semantic_code_intelligence/ci/metrics.py +591 -0
- semantic_code_intelligence/ci/pr.py +412 -0
- semantic_code_intelligence/ci/quality.py +557 -0
- semantic_code_intelligence/ci/templates.py +164 -0
- semantic_code_intelligence/ci/trace.py +224 -0
- semantic_code_intelligence/cli/__init__.py +0 -0
- semantic_code_intelligence/cli/commands/__init__.py +0 -0
- semantic_code_intelligence/cli/commands/ask_cmd.py +153 -0
- semantic_code_intelligence/cli/commands/benchmark_cmd.py +303 -0
- semantic_code_intelligence/cli/commands/chat_cmd.py +252 -0
- semantic_code_intelligence/cli/commands/ci_gen_cmd.py +74 -0
- semantic_code_intelligence/cli/commands/context_cmd.py +120 -0
- semantic_code_intelligence/cli/commands/cross_refactor_cmd.py +113 -0
- semantic_code_intelligence/cli/commands/deps_cmd.py +91 -0
- semantic_code_intelligence/cli/commands/docs_cmd.py +101 -0
- semantic_code_intelligence/cli/commands/doctor_cmd.py +147 -0
- semantic_code_intelligence/cli/commands/evolve_cmd.py +171 -0
- semantic_code_intelligence/cli/commands/explain_cmd.py +112 -0
- semantic_code_intelligence/cli/commands/gate_cmd.py +135 -0
- semantic_code_intelligence/cli/commands/grep_cmd.py +234 -0
- semantic_code_intelligence/cli/commands/hotspots_cmd.py +119 -0
- semantic_code_intelligence/cli/commands/impact_cmd.py +131 -0
- semantic_code_intelligence/cli/commands/index_cmd.py +138 -0
- semantic_code_intelligence/cli/commands/init_cmd.py +152 -0
- semantic_code_intelligence/cli/commands/investigate_cmd.py +163 -0
- semantic_code_intelligence/cli/commands/languages_cmd.py +101 -0
- semantic_code_intelligence/cli/commands/lsp_cmd.py +49 -0
- semantic_code_intelligence/cli/commands/mcp_cmd.py +50 -0
- semantic_code_intelligence/cli/commands/metrics_cmd.py +264 -0
- semantic_code_intelligence/cli/commands/models_cmd.py +157 -0
- semantic_code_intelligence/cli/commands/plugin_cmd.py +275 -0
- semantic_code_intelligence/cli/commands/pr_summary_cmd.py +178 -0
- semantic_code_intelligence/cli/commands/quality_cmd.py +208 -0
- semantic_code_intelligence/cli/commands/refactor_cmd.py +103 -0
- semantic_code_intelligence/cli/commands/review_cmd.py +88 -0
- semantic_code_intelligence/cli/commands/search_cmd.py +236 -0
- semantic_code_intelligence/cli/commands/serve_cmd.py +117 -0
- semantic_code_intelligence/cli/commands/suggest_cmd.py +100 -0
- semantic_code_intelligence/cli/commands/summary_cmd.py +78 -0
- semantic_code_intelligence/cli/commands/tool_cmd.py +282 -0
- semantic_code_intelligence/cli/commands/trace_cmd.py +123 -0
- semantic_code_intelligence/cli/commands/tui_cmd.py +58 -0
- semantic_code_intelligence/cli/commands/viz_cmd.py +127 -0
- semantic_code_intelligence/cli/commands/watch_cmd.py +72 -0
- semantic_code_intelligence/cli/commands/web_cmd.py +61 -0
- semantic_code_intelligence/cli/commands/workspace_cmd.py +250 -0
- semantic_code_intelligence/cli/main.py +65 -0
- semantic_code_intelligence/cli/router.py +92 -0
- semantic_code_intelligence/config/__init__.py +0 -0
- semantic_code_intelligence/config/settings.py +260 -0
- semantic_code_intelligence/context/__init__.py +19 -0
- semantic_code_intelligence/context/engine.py +429 -0
- semantic_code_intelligence/context/memory.py +253 -0
- semantic_code_intelligence/daemon/__init__.py +1 -0
- semantic_code_intelligence/daemon/watcher.py +515 -0
- semantic_code_intelligence/docs/__init__.py +1080 -0
- semantic_code_intelligence/embeddings/__init__.py +0 -0
- semantic_code_intelligence/embeddings/enhanced.py +131 -0
- semantic_code_intelligence/embeddings/generator.py +149 -0
- semantic_code_intelligence/embeddings/model_registry.py +100 -0
- semantic_code_intelligence/evolution/__init__.py +1 -0
- semantic_code_intelligence/evolution/budget_guard.py +111 -0
- semantic_code_intelligence/evolution/commit_manager.py +88 -0
- semantic_code_intelligence/evolution/context_builder.py +131 -0
- semantic_code_intelligence/evolution/engine.py +249 -0
- semantic_code_intelligence/evolution/patch_generator.py +229 -0
- semantic_code_intelligence/evolution/task_selector.py +214 -0
- semantic_code_intelligence/evolution/test_runner.py +111 -0
- semantic_code_intelligence/indexing/__init__.py +0 -0
- semantic_code_intelligence/indexing/chunker.py +174 -0
- semantic_code_intelligence/indexing/parallel.py +86 -0
- semantic_code_intelligence/indexing/scanner.py +146 -0
- semantic_code_intelligence/indexing/semantic_chunker.py +337 -0
- semantic_code_intelligence/llm/__init__.py +62 -0
- semantic_code_intelligence/llm/cache.py +219 -0
- semantic_code_intelligence/llm/cached_provider.py +145 -0
- semantic_code_intelligence/llm/conversation.py +190 -0
- semantic_code_intelligence/llm/cross_refactor.py +272 -0
- semantic_code_intelligence/llm/investigation.py +274 -0
- semantic_code_intelligence/llm/mock_provider.py +77 -0
- semantic_code_intelligence/llm/ollama_provider.py +122 -0
- semantic_code_intelligence/llm/openai_provider.py +100 -0
- semantic_code_intelligence/llm/provider.py +92 -0
- semantic_code_intelligence/llm/rate_limiter.py +164 -0
- semantic_code_intelligence/llm/reasoning.py +438 -0
- semantic_code_intelligence/llm/safety.py +110 -0
- semantic_code_intelligence/llm/streaming.py +251 -0
- semantic_code_intelligence/lsp/__init__.py +609 -0
- semantic_code_intelligence/mcp/__init__.py +393 -0
- semantic_code_intelligence/parsing/__init__.py +19 -0
- semantic_code_intelligence/parsing/parser.py +375 -0
- semantic_code_intelligence/plugins/__init__.py +255 -0
- semantic_code_intelligence/plugins/examples/__init__.py +1 -0
- semantic_code_intelligence/plugins/examples/code_quality.py +73 -0
- semantic_code_intelligence/plugins/examples/search_annotator.py +56 -0
- semantic_code_intelligence/scalability/__init__.py +205 -0
- semantic_code_intelligence/search/__init__.py +0 -0
- semantic_code_intelligence/search/formatter.py +123 -0
- semantic_code_intelligence/search/grep.py +361 -0
- semantic_code_intelligence/search/hybrid_search.py +170 -0
- semantic_code_intelligence/search/keyword_search.py +311 -0
- semantic_code_intelligence/search/section_expander.py +103 -0
- semantic_code_intelligence/services/__init__.py +0 -0
- semantic_code_intelligence/services/indexing_service.py +630 -0
- semantic_code_intelligence/services/search_service.py +269 -0
- semantic_code_intelligence/storage/__init__.py +0 -0
- semantic_code_intelligence/storage/chunk_hash_store.py +86 -0
- semantic_code_intelligence/storage/hash_store.py +66 -0
- semantic_code_intelligence/storage/index_manifest.py +85 -0
- semantic_code_intelligence/storage/index_stats.py +138 -0
- semantic_code_intelligence/storage/query_history.py +160 -0
- semantic_code_intelligence/storage/symbol_registry.py +209 -0
- semantic_code_intelligence/storage/vector_store.py +297 -0
- semantic_code_intelligence/tests/__init__.py +0 -0
- semantic_code_intelligence/tests/test_ai_features.py +351 -0
- semantic_code_intelligence/tests/test_chunker.py +119 -0
- semantic_code_intelligence/tests/test_cli.py +188 -0
- semantic_code_intelligence/tests/test_config.py +154 -0
- semantic_code_intelligence/tests/test_context.py +381 -0
- semantic_code_intelligence/tests/test_embeddings.py +73 -0
- semantic_code_intelligence/tests/test_endtoend.py +1142 -0
- semantic_code_intelligence/tests/test_enhanced_embeddings.py +92 -0
- semantic_code_intelligence/tests/test_hash_store.py +79 -0
- semantic_code_intelligence/tests/test_logging.py +55 -0
- semantic_code_intelligence/tests/test_new_cli.py +138 -0
- semantic_code_intelligence/tests/test_parser.py +495 -0
- semantic_code_intelligence/tests/test_phase10.py +355 -0
- semantic_code_intelligence/tests/test_phase11.py +593 -0
- semantic_code_intelligence/tests/test_phase12.py +375 -0
- semantic_code_intelligence/tests/test_phase13.py +663 -0
- semantic_code_intelligence/tests/test_phase14.py +568 -0
- semantic_code_intelligence/tests/test_phase15.py +814 -0
- semantic_code_intelligence/tests/test_phase16.py +792 -0
- semantic_code_intelligence/tests/test_phase17.py +815 -0
- semantic_code_intelligence/tests/test_phase18.py +934 -0
- semantic_code_intelligence/tests/test_phase19.py +986 -0
- semantic_code_intelligence/tests/test_phase20.py +2753 -0
- semantic_code_intelligence/tests/test_phase20b.py +2058 -0
- semantic_code_intelligence/tests/test_phase20c.py +962 -0
- semantic_code_intelligence/tests/test_phase21.py +428 -0
- semantic_code_intelligence/tests/test_phase22.py +799 -0
- semantic_code_intelligence/tests/test_phase23.py +783 -0
- semantic_code_intelligence/tests/test_phase24.py +715 -0
- semantic_code_intelligence/tests/test_phase25.py +496 -0
- semantic_code_intelligence/tests/test_phase26.py +251 -0
- semantic_code_intelligence/tests/test_phase27.py +531 -0
- semantic_code_intelligence/tests/test_phase8.py +592 -0
- semantic_code_intelligence/tests/test_phase9.py +643 -0
- semantic_code_intelligence/tests/test_plugins.py +293 -0
- semantic_code_intelligence/tests/test_priority_features.py +727 -0
- semantic_code_intelligence/tests/test_router.py +41 -0
- semantic_code_intelligence/tests/test_scalability.py +138 -0
- semantic_code_intelligence/tests/test_scanner.py +125 -0
- semantic_code_intelligence/tests/test_search.py +160 -0
- semantic_code_intelligence/tests/test_semantic_chunker.py +255 -0
- semantic_code_intelligence/tests/test_tools.py +182 -0
- semantic_code_intelligence/tests/test_vector_store.py +151 -0
- semantic_code_intelligence/tests/test_watcher.py +211 -0
- semantic_code_intelligence/tools/__init__.py +442 -0
- semantic_code_intelligence/tools/executor.py +232 -0
- semantic_code_intelligence/tools/protocol.py +200 -0
- semantic_code_intelligence/tui/__init__.py +454 -0
- semantic_code_intelligence/utils/__init__.py +0 -0
- semantic_code_intelligence/utils/logging.py +112 -0
- semantic_code_intelligence/version.py +3 -0
- semantic_code_intelligence/web/__init__.py +11 -0
- semantic_code_intelligence/web/api.py +289 -0
- semantic_code_intelligence/web/server.py +397 -0
- semantic_code_intelligence/web/ui.py +659 -0
- semantic_code_intelligence/web/visualize.py +226 -0
- semantic_code_intelligence/workspace/__init__.py +427 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Context provider — structured context generation for external AI pipelines.
|
|
2
|
+
|
|
3
|
+
The ``ContextProvider`` is the heart of CodexA's cooperation model. It
|
|
4
|
+
wraps the existing semantic search, symbol analysis, and dependency tools
|
|
5
|
+
into a single entry point that external AI assistants can call to enrich
|
|
6
|
+
their prompts with deep repository knowledge.
|
|
7
|
+
|
|
8
|
+
All public methods return plain ``dict`` objects ready for JSON
|
|
9
|
+
serialisation, so they can be consumed by any agent that speaks JSON —
|
|
10
|
+
no CodexA-specific types leak across the boundary.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import time
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from semantic_code_intelligence.analysis.ai_features import (
|
|
20
|
+
explain_file,
|
|
21
|
+
explain_symbol,
|
|
22
|
+
generate_ai_context,
|
|
23
|
+
summarize_repository,
|
|
24
|
+
)
|
|
25
|
+
from semantic_code_intelligence.context.engine import (
|
|
26
|
+
CallGraph,
|
|
27
|
+
ContextBuilder,
|
|
28
|
+
DependencyMap,
|
|
29
|
+
)
|
|
30
|
+
from semantic_code_intelligence.llm.safety import SafetyValidator
|
|
31
|
+
from semantic_code_intelligence.services.search_service import search_codebase
|
|
32
|
+
from semantic_code_intelligence.utils.logging import get_logger
|
|
33
|
+
|
|
34
|
+
logger = get_logger("bridge.context")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ContextProvider:
|
|
38
|
+
"""Generates structured context for consumption by external AI agents.
|
|
39
|
+
|
|
40
|
+
Usage::
|
|
41
|
+
|
|
42
|
+
provider = ContextProvider(Path("/my/project"))
|
|
43
|
+
ctx = provider.context_for_symbol("authenticate")
|
|
44
|
+
# ctx is a plain dict ready to be injected into an LLM prompt
|
|
45
|
+
|
|
46
|
+
All expensive work (scanning, parsing) is done lazily on first access.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, project_root: Path) -> None:
|
|
50
|
+
self._root = project_root.resolve()
|
|
51
|
+
self._builder: ContextBuilder | None = None
|
|
52
|
+
self._indexed = False
|
|
53
|
+
self._validator = SafetyValidator()
|
|
54
|
+
|
|
55
|
+
# --- lazy indexing -------------------------------------------------
|
|
56
|
+
|
|
57
|
+
def _ensure_indexed(self) -> ContextBuilder:
|
|
58
|
+
if self._builder is not None and self._indexed:
|
|
59
|
+
return self._builder
|
|
60
|
+
|
|
61
|
+
from semantic_code_intelligence.config.settings import load_config
|
|
62
|
+
from semantic_code_intelligence.indexing.scanner import scan_repository
|
|
63
|
+
|
|
64
|
+
self._builder = ContextBuilder()
|
|
65
|
+
config = load_config(self._root)
|
|
66
|
+
scanned = scan_repository(self._root, config.index)
|
|
67
|
+
for sf in scanned:
|
|
68
|
+
full = str(self._root / sf.relative_path)
|
|
69
|
+
try:
|
|
70
|
+
self._builder.index_file(full)
|
|
71
|
+
except Exception:
|
|
72
|
+
logger.debug("Skip %s", full)
|
|
73
|
+
self._indexed = True
|
|
74
|
+
return self._builder
|
|
75
|
+
|
|
76
|
+
# --- public context generators ----------------------------------
|
|
77
|
+
|
|
78
|
+
def context_for_query(
|
|
79
|
+
self,
|
|
80
|
+
query: str,
|
|
81
|
+
*,
|
|
82
|
+
top_k: int = 5,
|
|
83
|
+
threshold: float = 0.2,
|
|
84
|
+
include_repo_summary: bool = False,
|
|
85
|
+
) -> dict[str, Any]:
|
|
86
|
+
"""Return semantic-search context for a natural-language query.
|
|
87
|
+
|
|
88
|
+
Designed to be injected as additional context into an LLM prompt
|
|
89
|
+
by an external AI assistant.
|
|
90
|
+
"""
|
|
91
|
+
snippets: list[dict[str, Any]] = []
|
|
92
|
+
try:
|
|
93
|
+
results = search_codebase(query, self._root, top_k=top_k, threshold=threshold)
|
|
94
|
+
snippets = [r.to_dict() for r in results]
|
|
95
|
+
except Exception:
|
|
96
|
+
logger.debug("Search unavailable — returning empty snippets.")
|
|
97
|
+
|
|
98
|
+
resp: dict[str, Any] = {
|
|
99
|
+
"query": query,
|
|
100
|
+
"snippet_count": len(snippets),
|
|
101
|
+
"snippets": snippets,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if include_repo_summary:
|
|
105
|
+
builder = self._ensure_indexed()
|
|
106
|
+
resp["repo_summary"] = summarize_repository(builder).to_dict()
|
|
107
|
+
|
|
108
|
+
return resp
|
|
109
|
+
|
|
110
|
+
def context_for_symbol(
|
|
111
|
+
self,
|
|
112
|
+
symbol_name: str,
|
|
113
|
+
*,
|
|
114
|
+
file_path: str | None = None,
|
|
115
|
+
include_call_graph: bool = True,
|
|
116
|
+
include_dependencies: bool = True,
|
|
117
|
+
) -> dict[str, Any]:
|
|
118
|
+
"""Return rich context around a symbol for external AI consumption.
|
|
119
|
+
|
|
120
|
+
Includes the symbol's definition, related symbols, callers/callees,
|
|
121
|
+
and dependency information.
|
|
122
|
+
"""
|
|
123
|
+
builder = self._ensure_indexed()
|
|
124
|
+
|
|
125
|
+
if file_path:
|
|
126
|
+
builder.index_file(file_path)
|
|
127
|
+
|
|
128
|
+
matches = builder.find_symbol(symbol_name)
|
|
129
|
+
if not matches:
|
|
130
|
+
return {"symbol_name": symbol_name, "found": False}
|
|
131
|
+
|
|
132
|
+
explanations = [explain_symbol(s, builder).to_dict() for s in matches[:5]]
|
|
133
|
+
context_windows = [
|
|
134
|
+
builder.build_context(s).to_dict() for s in matches[:5]
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
result: dict[str, Any] = {
|
|
138
|
+
"symbol_name": symbol_name,
|
|
139
|
+
"found": True,
|
|
140
|
+
"match_count": len(matches),
|
|
141
|
+
"explanations": explanations,
|
|
142
|
+
"context_windows": context_windows,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if include_call_graph:
|
|
146
|
+
all_syms = builder.get_all_symbols()
|
|
147
|
+
graph = CallGraph()
|
|
148
|
+
graph.build(all_syms)
|
|
149
|
+
callers = [e.to_dict() for e in graph.callers_of(symbol_name)]
|
|
150
|
+
callees: list[dict[str, Any]] = []
|
|
151
|
+
for edge in graph.edges:
|
|
152
|
+
if edge.caller.endswith(f":{symbol_name}"):
|
|
153
|
+
callees.append(edge.to_dict())
|
|
154
|
+
result["call_graph"] = {"callers": callers, "callees": callees}
|
|
155
|
+
|
|
156
|
+
if include_dependencies:
|
|
157
|
+
for s in matches[:1]:
|
|
158
|
+
dep_map = DependencyMap()
|
|
159
|
+
if s.file_path in builder._file_contents:
|
|
160
|
+
dep_map.add_file(s.file_path, builder._file_contents[s.file_path])
|
|
161
|
+
result["dependencies"] = dep_map.to_dict()
|
|
162
|
+
|
|
163
|
+
return result
|
|
164
|
+
|
|
165
|
+
def context_for_file(self, file_path: str) -> dict[str, Any]:
|
|
166
|
+
"""Return structured context for an entire file."""
|
|
167
|
+
builder = self._ensure_indexed()
|
|
168
|
+
builder.index_file(file_path)
|
|
169
|
+
|
|
170
|
+
symbols = builder.get_symbols(file_path)
|
|
171
|
+
explanations = explain_file(file_path)
|
|
172
|
+
|
|
173
|
+
dep_map = DependencyMap()
|
|
174
|
+
if file_path in builder._file_contents:
|
|
175
|
+
dep_map.add_file(file_path, builder._file_contents[file_path])
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
"file_path": file_path,
|
|
179
|
+
"symbol_count": len(symbols),
|
|
180
|
+
"symbols": [s.to_dict() for s in symbols],
|
|
181
|
+
"explanations": [e.to_dict() for e in explanations],
|
|
182
|
+
"dependencies": dep_map.to_dict(),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
def context_for_repo(self) -> dict[str, Any]:
|
|
186
|
+
"""Return a repository-level summary."""
|
|
187
|
+
builder = self._ensure_indexed()
|
|
188
|
+
summary = summarize_repository(builder)
|
|
189
|
+
return summary.to_dict()
|
|
190
|
+
|
|
191
|
+
def validate_code(self, code: str) -> dict[str, Any]:
|
|
192
|
+
"""Validate code (e.g. AI-generated) for safety issues."""
|
|
193
|
+
report = self._validator.validate(code)
|
|
194
|
+
return report.to_dict()
|
|
195
|
+
|
|
196
|
+
def get_dependencies(self, file_path: str) -> dict[str, Any]:
|
|
197
|
+
"""Return file-level dependency map."""
|
|
198
|
+
builder = self._ensure_indexed()
|
|
199
|
+
# Resolve relative paths against project root
|
|
200
|
+
resolved = Path(file_path)
|
|
201
|
+
if not resolved.is_absolute():
|
|
202
|
+
resolved = self._root / resolved
|
|
203
|
+
resolved_str = str(resolved)
|
|
204
|
+
builder.index_file(resolved_str)
|
|
205
|
+
dep_map = DependencyMap()
|
|
206
|
+
if resolved_str in builder._file_contents:
|
|
207
|
+
dep_map.add_file(resolved_str, builder._file_contents[resolved_str])
|
|
208
|
+
return {"file_path": file_path, "dependencies": dep_map.to_dict()}
|
|
209
|
+
|
|
210
|
+
def get_call_graph(self, symbol_name: str) -> dict[str, Any]:
|
|
211
|
+
"""Return callers and callees for a symbol."""
|
|
212
|
+
builder = self._ensure_indexed()
|
|
213
|
+
all_syms = builder.get_all_symbols()
|
|
214
|
+
graph = CallGraph()
|
|
215
|
+
graph.build(all_syms)
|
|
216
|
+
callers = [e.to_dict() for e in graph.callers_of(symbol_name)]
|
|
217
|
+
callees: list[dict[str, Any]] = []
|
|
218
|
+
for edge in graph.edges:
|
|
219
|
+
if edge.caller.endswith(f":{symbol_name}"):
|
|
220
|
+
callees.append(edge.to_dict())
|
|
221
|
+
edges = callers + callees
|
|
222
|
+
return {
|
|
223
|
+
"symbol_name": symbol_name,
|
|
224
|
+
"callers": callers,
|
|
225
|
+
"callees": callees,
|
|
226
|
+
"edges": edges,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
def find_references(self, symbol_name: str) -> dict[str, Any]:
|
|
230
|
+
"""Find all references to a symbol."""
|
|
231
|
+
builder = self._ensure_indexed()
|
|
232
|
+
all_syms = builder.get_all_symbols()
|
|
233
|
+
refs: list[dict[str, Any]] = []
|
|
234
|
+
for sym in all_syms:
|
|
235
|
+
if sym.name == symbol_name:
|
|
236
|
+
refs.append(sym.to_dict())
|
|
237
|
+
elif symbol_name in sym.body:
|
|
238
|
+
refs.append({
|
|
239
|
+
"referencing_symbol": sym.name,
|
|
240
|
+
"kind": sym.kind,
|
|
241
|
+
"file_path": sym.file_path,
|
|
242
|
+
"start_line": sym.start_line,
|
|
243
|
+
"end_line": sym.end_line,
|
|
244
|
+
})
|
|
245
|
+
return {"symbol_name": symbol_name, "reference_count": len(refs), "references": refs}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Agent cooperation protocol — request/response types for external AI systems.
|
|
2
|
+
|
|
3
|
+
Defines a model-neutral JSON protocol that any IDE AI assistant (Copilot,
|
|
4
|
+
Cursor, Continue, etc.) can use to request context from CodexA and receive
|
|
5
|
+
structured responses.
|
|
6
|
+
|
|
7
|
+
The protocol is intentionally simple and stateless — every request carries
|
|
8
|
+
the information needed to produce a response. This keeps the bridge
|
|
9
|
+
lightweight and easy to integrate with any tool that speaks JSON.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# Enums
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
class RequestKind(str, Enum):
|
|
26
|
+
"""Types of requests the bridge can handle."""
|
|
27
|
+
|
|
28
|
+
SEMANTIC_SEARCH = "semantic_search"
|
|
29
|
+
EXPLAIN_SYMBOL = "explain_symbol"
|
|
30
|
+
EXPLAIN_FILE = "explain_file"
|
|
31
|
+
GET_CONTEXT = "get_context"
|
|
32
|
+
GET_DEPENDENCIES = "get_dependencies"
|
|
33
|
+
GET_CALL_GRAPH = "get_call_graph"
|
|
34
|
+
SUMMARIZE_REPO = "summarize_repo"
|
|
35
|
+
FIND_REFERENCES = "find_references"
|
|
36
|
+
VALIDATE_CODE = "validate_code"
|
|
37
|
+
LIST_CAPABILITIES = "list_capabilities"
|
|
38
|
+
|
|
39
|
+
# Phase 19 — AI Agent Tooling Protocol
|
|
40
|
+
INVOKE_TOOL = "invoke_tool"
|
|
41
|
+
LIST_TOOLS = "list_tools"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# Request / Response
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class AgentRequest:
|
|
50
|
+
"""Incoming request from an external AI agent or IDE extension.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
kind: The type of operation requested.
|
|
54
|
+
params: Operation-specific parameters (key-value).
|
|
55
|
+
request_id: Optional caller-assigned ID for correlation.
|
|
56
|
+
source: Optional identifier for the calling agent (e.g. "copilot").
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
kind: str
|
|
60
|
+
params: dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
request_id: str = ""
|
|
62
|
+
source: str = ""
|
|
63
|
+
|
|
64
|
+
def to_dict(self) -> dict[str, Any]:
|
|
65
|
+
"""Serialize the request to a plain dictionary."""
|
|
66
|
+
return {
|
|
67
|
+
"kind": self.kind,
|
|
68
|
+
"params": self.params,
|
|
69
|
+
"request_id": self.request_id,
|
|
70
|
+
"source": self.source,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
def to_json(self) -> str:
|
|
74
|
+
"""Serialize the request to a JSON string."""
|
|
75
|
+
return json.dumps(self.to_dict())
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_dict(cls, data: dict[str, Any]) -> "AgentRequest":
|
|
79
|
+
"""Create an AgentRequest from a dictionary."""
|
|
80
|
+
return cls(
|
|
81
|
+
kind=data.get("kind", ""),
|
|
82
|
+
params=data.get("params", {}),
|
|
83
|
+
request_id=data.get("request_id", ""),
|
|
84
|
+
source=data.get("source", ""),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def from_json(cls, text: str) -> "AgentRequest":
|
|
89
|
+
"""Create an AgentRequest by parsing a JSON string."""
|
|
90
|
+
return cls.from_dict(json.loads(text))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class AgentResponse:
|
|
95
|
+
"""Outgoing response to an external AI agent or IDE extension.
|
|
96
|
+
|
|
97
|
+
Attributes:
|
|
98
|
+
success: Whether the request was handled without error.
|
|
99
|
+
data: The structured response payload.
|
|
100
|
+
error: Human-readable error message if success is False.
|
|
101
|
+
request_id: Echoes the caller's request_id for correlation.
|
|
102
|
+
elapsed_ms: Time taken to process the request.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
success: bool
|
|
106
|
+
data: dict[str, Any] = field(default_factory=dict)
|
|
107
|
+
error: str = ""
|
|
108
|
+
request_id: str = ""
|
|
109
|
+
elapsed_ms: float = 0.0
|
|
110
|
+
|
|
111
|
+
def to_dict(self) -> dict[str, Any]:
|
|
112
|
+
"""Serialize the response to a plain dictionary."""
|
|
113
|
+
result: dict[str, Any] = {
|
|
114
|
+
"success": self.success,
|
|
115
|
+
"request_id": self.request_id,
|
|
116
|
+
"elapsed_ms": round(self.elapsed_ms, 2),
|
|
117
|
+
}
|
|
118
|
+
if self.success:
|
|
119
|
+
result["data"] = self.data
|
|
120
|
+
else:
|
|
121
|
+
result["error"] = self.error
|
|
122
|
+
return result
|
|
123
|
+
|
|
124
|
+
def to_json(self, indent: int | None = None) -> str:
|
|
125
|
+
"""Serialize the response to a JSON string."""
|
|
126
|
+
return json.dumps(self.to_dict(), indent=indent)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
# Capabilities manifest
|
|
131
|
+
# ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class BridgeCapabilities:
|
|
135
|
+
"""Advertises what CodexA can do to external consumers.
|
|
136
|
+
|
|
137
|
+
Returned in response to ``list_capabilities`` requests and also
|
|
138
|
+
served at ``GET /`` by the bridge server.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
version: str = "0.9.0"
|
|
142
|
+
name: str = "CodexA Bridge"
|
|
143
|
+
description: str = (
|
|
144
|
+
"Semantic code intelligence provider — context, search, "
|
|
145
|
+
"explanation, dependency analysis, and safety validation "
|
|
146
|
+
"for external AI coding assistants."
|
|
147
|
+
)
|
|
148
|
+
supported_requests: list[str] = field(
|
|
149
|
+
default_factory=lambda: [k.value for k in RequestKind]
|
|
150
|
+
)
|
|
151
|
+
tools: list[dict[str, Any]] = field(default_factory=list)
|
|
152
|
+
|
|
153
|
+
def to_dict(self) -> dict[str, Any]:
|
|
154
|
+
"""Serialize the capabilities manifest to a plain dictionary."""
|
|
155
|
+
result: dict[str, Any] = {
|
|
156
|
+
"version": self.version,
|
|
157
|
+
"name": self.name,
|
|
158
|
+
"description": self.description,
|
|
159
|
+
"supported_requests": self.supported_requests,
|
|
160
|
+
}
|
|
161
|
+
if self.tools:
|
|
162
|
+
result["tools"] = self.tools
|
|
163
|
+
return result
|
|
164
|
+
|
|
165
|
+
def to_json(self, indent: int = 2) -> str:
|
|
166
|
+
"""Serialize the capabilities manifest to a JSON string."""
|
|
167
|
+
return json.dumps(self.to_dict(), indent=indent)
|