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,78 @@
|
|
|
1
|
+
"""CLI command: summary — generate structured repository summary."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from semantic_code_intelligence.analysis.ai_features import summarize_repository
|
|
10
|
+
from semantic_code_intelligence.config.settings import load_config
|
|
11
|
+
from semantic_code_intelligence.context.engine import ContextBuilder
|
|
12
|
+
from semantic_code_intelligence.indexing.scanner import scan_repository
|
|
13
|
+
from semantic_code_intelligence.utils.logging import (
|
|
14
|
+
console,
|
|
15
|
+
get_logger,
|
|
16
|
+
print_error,
|
|
17
|
+
print_info,
|
|
18
|
+
print_success,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
logger = get_logger("cli.summary")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.command("summary")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--path",
|
|
27
|
+
"-p",
|
|
28
|
+
default=".",
|
|
29
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
30
|
+
help="Project root path.",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--json-output",
|
|
34
|
+
"--json",
|
|
35
|
+
"json_mode",
|
|
36
|
+
is_flag=True,
|
|
37
|
+
default=False,
|
|
38
|
+
help="Output in JSON format.",
|
|
39
|
+
)
|
|
40
|
+
@click.pass_context
|
|
41
|
+
def summary_cmd(ctx: click.Context, path: str, json_mode: bool) -> None:
|
|
42
|
+
"""Generate a structured summary of the repository.
|
|
43
|
+
|
|
44
|
+
Shows language breakdown, symbol counts, and top functions/classes.
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
|
|
48
|
+
codexa summary
|
|
49
|
+
|
|
50
|
+
codexa summary --json
|
|
51
|
+
|
|
52
|
+
codexa summary -p /path/to/project
|
|
53
|
+
"""
|
|
54
|
+
root = Path(path).resolve()
|
|
55
|
+
config = load_config(root)
|
|
56
|
+
scanned = scan_repository(root, config.index)
|
|
57
|
+
|
|
58
|
+
if not scanned:
|
|
59
|
+
print_error("No source files found in the project.")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
if not json_mode:
|
|
63
|
+
print_info(f"Analyzing {len(scanned)} files...")
|
|
64
|
+
builder = ContextBuilder()
|
|
65
|
+
for sf in scanned:
|
|
66
|
+
full_path = str(root / sf.relative_path)
|
|
67
|
+
try:
|
|
68
|
+
builder.index_file(full_path)
|
|
69
|
+
except Exception:
|
|
70
|
+
logger.debug("Could not parse %s", sf.relative_path)
|
|
71
|
+
|
|
72
|
+
summary = summarize_repository(builder)
|
|
73
|
+
|
|
74
|
+
if json_mode:
|
|
75
|
+
click.echo(summary.to_json())
|
|
76
|
+
else:
|
|
77
|
+
console.print(summary.render(), markup=False)
|
|
78
|
+
print_success(f"Summary generated for {summary.total_files} files.")
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""CLI command: tool — invoke and manage AI agent tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json as json_mod
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from semantic_code_intelligence.utils.logging import (
|
|
11
|
+
console,
|
|
12
|
+
get_logger,
|
|
13
|
+
print_error,
|
|
14
|
+
print_header,
|
|
15
|
+
print_separator,
|
|
16
|
+
print_success,
|
|
17
|
+
print_warning,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = get_logger("cli.tool")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.group("tool")
|
|
24
|
+
def tool_cmd() -> None:
|
|
25
|
+
"""AI Agent Tooling Protocol — invoke and inspect tools.
|
|
26
|
+
|
|
27
|
+
Provides a CLI interface to the same tool execution engine that
|
|
28
|
+
AI coding agents use over the Bridge HTTP API.
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
|
|
32
|
+
codexa tool list
|
|
33
|
+
codexa tool run semantic_search --arg query="parse file"
|
|
34
|
+
codexa tool schema semantic_search
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@tool_cmd.command("list")
|
|
39
|
+
@click.option(
|
|
40
|
+
"--json-output", "--json", "json_mode",
|
|
41
|
+
is_flag=True, default=False,
|
|
42
|
+
help="Output in JSON format.",
|
|
43
|
+
)
|
|
44
|
+
def tool_list(json_mode: bool) -> None:
|
|
45
|
+
"""List all available tools with their descriptions."""
|
|
46
|
+
from semantic_code_intelligence.tools import TOOL_DEFINITIONS
|
|
47
|
+
from semantic_code_intelligence.tools.executor import ToolExecutor
|
|
48
|
+
|
|
49
|
+
from rich.table import Table
|
|
50
|
+
|
|
51
|
+
executor = ToolExecutor(Path(".").resolve())
|
|
52
|
+
tools = executor.available_tools
|
|
53
|
+
|
|
54
|
+
if json_mode:
|
|
55
|
+
click.echo(json_mod.dumps({"tools": tools, "count": len(tools)}, indent=2))
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
print_header("Available Tools", f"{len(tools)} registered")
|
|
59
|
+
table = Table(show_header=True, header_style="bold cyan", box=None, padding=(0, 2))
|
|
60
|
+
table.add_column("Tool", style="bold")
|
|
61
|
+
table.add_column("Source", style="dim")
|
|
62
|
+
table.add_column("Parameters", style="yellow")
|
|
63
|
+
table.add_column("Description")
|
|
64
|
+
|
|
65
|
+
for t in tools:
|
|
66
|
+
source = t.get("source", "built-in")
|
|
67
|
+
params = t.get("parameters", {})
|
|
68
|
+
param_names = ", ".join(params.keys()) if params else "-"
|
|
69
|
+
desc = t.get("description", "No description")
|
|
70
|
+
if len(desc) > 60:
|
|
71
|
+
desc = desc[:57] + "..."
|
|
72
|
+
table.add_row(t["name"], source, param_names, desc)
|
|
73
|
+
|
|
74
|
+
console.print(table)
|
|
75
|
+
print_separator()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@tool_cmd.command("run")
|
|
79
|
+
@click.argument("tool_name")
|
|
80
|
+
@click.option(
|
|
81
|
+
"--arg", "-a",
|
|
82
|
+
multiple=True,
|
|
83
|
+
help="Tool argument as key=value (repeatable).",
|
|
84
|
+
)
|
|
85
|
+
@click.option(
|
|
86
|
+
"--path", "-p",
|
|
87
|
+
default=".",
|
|
88
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
89
|
+
help="Project root path.",
|
|
90
|
+
)
|
|
91
|
+
@click.option(
|
|
92
|
+
"--json-output", "--json", "json_mode",
|
|
93
|
+
is_flag=True, default=False,
|
|
94
|
+
help="Output in JSON format.",
|
|
95
|
+
)
|
|
96
|
+
@click.option(
|
|
97
|
+
"--pipe",
|
|
98
|
+
is_flag=True, default=False,
|
|
99
|
+
help="Plain text output for piping / CI.",
|
|
100
|
+
)
|
|
101
|
+
@click.pass_context
|
|
102
|
+
def tool_run(
|
|
103
|
+
ctx: click.Context,
|
|
104
|
+
tool_name: str,
|
|
105
|
+
arg: tuple[str, ...],
|
|
106
|
+
path: str,
|
|
107
|
+
json_mode: bool,
|
|
108
|
+
pipe: bool,
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Run TOOL_NAME with the given arguments.
|
|
111
|
+
|
|
112
|
+
Arguments are passed as --arg key=value pairs.
|
|
113
|
+
|
|
114
|
+
Examples:
|
|
115
|
+
|
|
116
|
+
codexa tool run semantic_search --arg query="parse file"
|
|
117
|
+
codexa tool run explain_symbol --arg symbol_name=ToolRegistry
|
|
118
|
+
codexa tool run summarize_repo --json
|
|
119
|
+
"""
|
|
120
|
+
from semantic_code_intelligence.tools.executor import ToolExecutor
|
|
121
|
+
from semantic_code_intelligence.tools.protocol import ToolInvocation
|
|
122
|
+
|
|
123
|
+
project_root = Path(path).resolve()
|
|
124
|
+
|
|
125
|
+
# Parse arguments
|
|
126
|
+
arguments: dict[str, str] = {}
|
|
127
|
+
for a in arg:
|
|
128
|
+
if "=" not in a:
|
|
129
|
+
print_error(f"Invalid argument format: {a!r} (expected key=value)")
|
|
130
|
+
ctx.exit(1)
|
|
131
|
+
return
|
|
132
|
+
key, value = a.split("=", 1)
|
|
133
|
+
arguments[key.strip()] = value.strip()
|
|
134
|
+
|
|
135
|
+
executor = ToolExecutor(project_root)
|
|
136
|
+
invocation = ToolInvocation(tool_name=tool_name, arguments=arguments)
|
|
137
|
+
|
|
138
|
+
if not (json_mode or pipe):
|
|
139
|
+
print_separator(f"Running: {tool_name}")
|
|
140
|
+
|
|
141
|
+
result = executor.execute(invocation)
|
|
142
|
+
|
|
143
|
+
if json_mode or pipe:
|
|
144
|
+
click.echo(result.to_json(indent=2 if json_mode else None))
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
if result.success:
|
|
148
|
+
print_success(f"Tool '{tool_name}' completed in {result.execution_time_ms:.1f}ms")
|
|
149
|
+
print_separator()
|
|
150
|
+
payload = result.result_payload
|
|
151
|
+
if payload:
|
|
152
|
+
_render_tool_result(tool_name, payload)
|
|
153
|
+
else:
|
|
154
|
+
print_error(f"Tool '{tool_name}' failed")
|
|
155
|
+
if result.error:
|
|
156
|
+
console.print(f" [red]Error code:[/red] {result.error.error_code}")
|
|
157
|
+
console.print(f" [red]Message:[/red] {result.error.error_message}")
|
|
158
|
+
print_separator()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _render_tool_result(tool_name: str, payload: dict) -> None:
|
|
162
|
+
"""Render a tool result payload with rich formatting."""
|
|
163
|
+
from rich.panel import Panel
|
|
164
|
+
from rich.syntax import Syntax
|
|
165
|
+
from rich.table import Table
|
|
166
|
+
|
|
167
|
+
# explanation results (explain_symbol, explain_file, get_context)
|
|
168
|
+
if "explanation" in payload:
|
|
169
|
+
console.print(Panel(payload["explanation"], title="Explanation", border_style="cyan"))
|
|
170
|
+
if payload.get("code_snippet"):
|
|
171
|
+
lang = payload.get("language", "python")
|
|
172
|
+
console.print(Syntax(payload["code_snippet"], lang, theme="monokai", line_numbers=True))
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
# search results
|
|
176
|
+
if "results" in payload and isinstance(payload["results"], list):
|
|
177
|
+
results = payload["results"]
|
|
178
|
+
console.print(f" [bold]{len(results)} result(s)[/bold]\n")
|
|
179
|
+
for i, r in enumerate(results[:20], 1):
|
|
180
|
+
score = r.get("score", r.get("similarity", 0))
|
|
181
|
+
path = r.get("file_path", r.get("path", "?"))
|
|
182
|
+
console.print(f" [cyan]{i}.[/cyan] [bold]{path}[/bold] [dim](score: {score:.3f})[/dim]")
|
|
183
|
+
snippet = r.get("content", r.get("snippet", ""))
|
|
184
|
+
if snippet:
|
|
185
|
+
preview = snippet.strip()[:120].replace("\n", " ")
|
|
186
|
+
console.print(f" [dim]{preview}[/dim]")
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
# call graph
|
|
190
|
+
if "call_graph" in payload or "callers" in payload or "callees" in payload:
|
|
191
|
+
if payload.get("callers"):
|
|
192
|
+
console.print(" [bold]Callers:[/bold]")
|
|
193
|
+
for c in payload["callers"]:
|
|
194
|
+
console.print(f" [green]←[/green] {c}")
|
|
195
|
+
if payload.get("callees"):
|
|
196
|
+
console.print(" [bold]Callees:[/bold]")
|
|
197
|
+
for c in payload["callees"]:
|
|
198
|
+
console.print(f" [magenta]→[/magenta] {c}")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# references
|
|
202
|
+
if "references" in payload:
|
|
203
|
+
refs = payload["references"]
|
|
204
|
+
console.print(f" [bold]{len(refs)} reference(s)[/bold]\n")
|
|
205
|
+
for ref in refs[:30]:
|
|
206
|
+
path = ref.get("file_path", ref.get("path", "?"))
|
|
207
|
+
line = ref.get("line", "?")
|
|
208
|
+
console.print(f" [cyan]{path}[/cyan]:[yellow]{line}[/yellow]")
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
# dependencies
|
|
212
|
+
if "dependencies" in payload or "imports" in payload:
|
|
213
|
+
deps = payload.get("dependencies", payload.get("imports", []))
|
|
214
|
+
console.print(f" [bold]{len(deps)} dependenc(ies)[/bold]\n")
|
|
215
|
+
for d in deps:
|
|
216
|
+
if isinstance(d, str):
|
|
217
|
+
console.print(f" [dim]→[/dim] {d}")
|
|
218
|
+
elif isinstance(d, dict):
|
|
219
|
+
console.print(f" [dim]→[/dim] {d.get('name', d.get('module', str(d)))}")
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
# summary
|
|
223
|
+
if "summary" in payload:
|
|
224
|
+
console.print(Panel(payload["summary"], title="Summary", border_style="green"))
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
# fallback: pretty-print JSON
|
|
228
|
+
console.print(Syntax(json_mod.dumps(payload, indent=2), "json", theme="monokai"))
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@tool_cmd.command("schema")
|
|
232
|
+
@click.argument("tool_name")
|
|
233
|
+
@click.option(
|
|
234
|
+
"--json-output", "--json", "json_mode",
|
|
235
|
+
is_flag=True, default=False,
|
|
236
|
+
help="Output in JSON format.",
|
|
237
|
+
)
|
|
238
|
+
def tool_schema(tool_name: str, json_mode: bool) -> None:
|
|
239
|
+
"""Show the schema definition for TOOL_NAME.
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
|
|
243
|
+
codexa tool schema semantic_search
|
|
244
|
+
codexa tool schema explain_symbol --json
|
|
245
|
+
"""
|
|
246
|
+
from semantic_code_intelligence.tools.executor import ToolExecutor
|
|
247
|
+
|
|
248
|
+
executor = ToolExecutor(Path(".").resolve())
|
|
249
|
+
schema = executor.get_tool_schema(tool_name)
|
|
250
|
+
|
|
251
|
+
if schema is None:
|
|
252
|
+
print_error(f"Unknown tool: {tool_name}")
|
|
253
|
+
available = [t["name"] for t in ToolExecutor(Path(".").resolve()).available_tools]
|
|
254
|
+
if available:
|
|
255
|
+
print_warning(f"Available tools: {', '.join(available)}")
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
if json_mode:
|
|
259
|
+
click.echo(json_mod.dumps(schema, indent=2))
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
from rich.table import Table
|
|
263
|
+
|
|
264
|
+
print_header(schema["name"], schema.get("description", ""))
|
|
265
|
+
params = schema.get("parameters", {})
|
|
266
|
+
if params:
|
|
267
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
268
|
+
table.add_column("Parameter", style="cyan")
|
|
269
|
+
table.add_column("Type", style="yellow")
|
|
270
|
+
table.add_column("Required", style="red")
|
|
271
|
+
table.add_column("Default", style="dim")
|
|
272
|
+
table.add_column("Description")
|
|
273
|
+
for pname, pdef in params.items():
|
|
274
|
+
req = "yes" if pdef.get("required") else ""
|
|
275
|
+
ptype = pdef.get("type", "any")
|
|
276
|
+
desc = pdef.get("description", "")
|
|
277
|
+
default = str(pdef["default"]) if "default" in pdef and pdef["default"] is not None else ""
|
|
278
|
+
table.add_row(pname, ptype, req, default, desc)
|
|
279
|
+
console.print(table)
|
|
280
|
+
else:
|
|
281
|
+
console.print(" [dim]No parameters[/dim]")
|
|
282
|
+
print_separator()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""CLI command: trace — trace execution relationships of a symbol."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json as json_mod
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from semantic_code_intelligence.utils.logging import (
|
|
11
|
+
console,
|
|
12
|
+
get_logger,
|
|
13
|
+
print_error,
|
|
14
|
+
print_success,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.trace")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("trace")
|
|
21
|
+
@click.argument("symbol")
|
|
22
|
+
@click.option(
|
|
23
|
+
"--path", "-p",
|
|
24
|
+
default=".",
|
|
25
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
26
|
+
help="Project root path.",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--json-output", "--json", "json_mode",
|
|
30
|
+
is_flag=True, default=False,
|
|
31
|
+
help="Output in JSON format.",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--pipe",
|
|
35
|
+
is_flag=True, default=False,
|
|
36
|
+
help="Plain text output for piping / CI.",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--max-depth", "-d",
|
|
40
|
+
type=int, default=5,
|
|
41
|
+
help="Maximum traversal depth (default: 5).",
|
|
42
|
+
)
|
|
43
|
+
@click.pass_context
|
|
44
|
+
def trace_cmd(
|
|
45
|
+
ctx: click.Context,
|
|
46
|
+
symbol: str,
|
|
47
|
+
path: str,
|
|
48
|
+
json_mode: bool,
|
|
49
|
+
pipe: bool,
|
|
50
|
+
max_depth: int,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Trace execution relationships for SYMBOL.
|
|
53
|
+
|
|
54
|
+
Shows upstream callers and downstream callees to map the flow of
|
|
55
|
+
execution through the codebase.
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
|
|
59
|
+
codexa trace parse_file
|
|
60
|
+
|
|
61
|
+
codexa trace MyClass.process --json
|
|
62
|
+
|
|
63
|
+
codexa trace build_context --max-depth 3 --pipe
|
|
64
|
+
"""
|
|
65
|
+
from semantic_code_intelligence.ci.trace import trace_symbol
|
|
66
|
+
from semantic_code_intelligence.context.engine import CallGraph, ContextBuilder
|
|
67
|
+
|
|
68
|
+
root = Path(path).resolve()
|
|
69
|
+
builder = ContextBuilder()
|
|
70
|
+
|
|
71
|
+
py_files = sorted(root.rglob("*.py"))
|
|
72
|
+
py_files = [f for f in py_files if ".venv" not in f.parts and "__pycache__" not in f.parts]
|
|
73
|
+
|
|
74
|
+
for fp in py_files:
|
|
75
|
+
try:
|
|
76
|
+
content = fp.read_text(encoding="utf-8", errors="replace")
|
|
77
|
+
builder.index_file(str(fp), content)
|
|
78
|
+
except Exception:
|
|
79
|
+
logger.debug("Failed to index %s", fp)
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
symbols = builder.get_all_symbols()
|
|
83
|
+
call_graph = CallGraph()
|
|
84
|
+
call_graph.build(symbols)
|
|
85
|
+
call_graph.build(symbols)
|
|
86
|
+
|
|
87
|
+
result = trace_symbol(symbol, symbols, call_graph, max_depth=max_depth)
|
|
88
|
+
|
|
89
|
+
if not result.target_file:
|
|
90
|
+
if json_mode:
|
|
91
|
+
click.echo(json_mod.dumps({"error": f"Symbol '{symbol}' not found"}, indent=2))
|
|
92
|
+
elif pipe:
|
|
93
|
+
click.echo(f"ERROR symbol_not_found {symbol}")
|
|
94
|
+
else:
|
|
95
|
+
print_error(f"Symbol '{symbol}' not found in the project.")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
if json_mode:
|
|
99
|
+
click.echo(json_mod.dumps(result.to_dict(), indent=2))
|
|
100
|
+
elif pipe:
|
|
101
|
+
click.echo(f"target={result.target} file={result.target_file} up={len(result.upstream)} down={len(result.downstream)}")
|
|
102
|
+
for n in result.upstream:
|
|
103
|
+
click.echo(f" UP depth={n.depth} {n.kind:<10} {n.file_path}:{n.name}")
|
|
104
|
+
for n in result.downstream:
|
|
105
|
+
click.echo(f" DOWN depth={n.depth} {n.kind:<10} {n.file_path}:{n.name}")
|
|
106
|
+
else:
|
|
107
|
+
console.print(f"\n[bold]Symbol Trace[/bold] — [cyan]{result.target}[/cyan] ({result.target_file})\n")
|
|
108
|
+
if not result.upstream and not result.downstream:
|
|
109
|
+
print_success("No execution relationships found.")
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
if result.upstream:
|
|
113
|
+
console.print(f"[bold]Upstream callers[/bold] (max depth {result.max_upstream_depth}):")
|
|
114
|
+
for n in result.upstream:
|
|
115
|
+
console.print(f" [yellow]{n.name}[/yellow] depth={n.depth} ({n.kind}) [dim]{n.file_path}[/dim]")
|
|
116
|
+
|
|
117
|
+
if result.downstream:
|
|
118
|
+
console.print(f"\n[bold]Downstream callees[/bold] (max depth {result.max_downstream_depth}):")
|
|
119
|
+
for n in result.downstream:
|
|
120
|
+
console.print(f" [green]{n.name}[/green] depth={n.depth} ({n.kind}) [dim]{n.file_path}[/dim]")
|
|
121
|
+
|
|
122
|
+
console.print(f"\n[bold]Edges:[/bold] {len(result.edges)} | [bold]Total nodes:[/bold] {result.total_nodes}")
|
|
123
|
+
console.print()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""CLI command: tui - Launch interactive terminal search interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from semantic_code_intelligence.config.settings import AppConfig
|
|
10
|
+
from semantic_code_intelligence.utils.logging import get_logger, print_error
|
|
11
|
+
|
|
12
|
+
logger = get_logger("cli.tui")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command("tui")
|
|
16
|
+
@click.option(
|
|
17
|
+
"--path",
|
|
18
|
+
"-p",
|
|
19
|
+
default=".",
|
|
20
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
21
|
+
help="Project root path.",
|
|
22
|
+
)
|
|
23
|
+
@click.option(
|
|
24
|
+
"--mode",
|
|
25
|
+
"-m",
|
|
26
|
+
type=click.Choice(["semantic", "keyword", "regex", "hybrid"], case_sensitive=False),
|
|
27
|
+
default="hybrid",
|
|
28
|
+
help="Default search mode.",
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--top-k",
|
|
32
|
+
"-k",
|
|
33
|
+
default=10,
|
|
34
|
+
type=int,
|
|
35
|
+
help="Results per query.",
|
|
36
|
+
)
|
|
37
|
+
@click.pass_context
|
|
38
|
+
def tui_cmd(ctx: click.Context, path: str, mode: str, top_k: int) -> None:
|
|
39
|
+
"""Launch the interactive terminal search interface.
|
|
40
|
+
|
|
41
|
+
Provides a live search REPL with mode switching and result preview.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
|
|
45
|
+
\b
|
|
46
|
+
codexa tui
|
|
47
|
+
codexa tui --mode hybrid -k 20
|
|
48
|
+
"""
|
|
49
|
+
root = Path(path).resolve()
|
|
50
|
+
config_dir = AppConfig.config_dir(root)
|
|
51
|
+
|
|
52
|
+
if not config_dir.exists():
|
|
53
|
+
print_error(f"Project not initialized at {root}. Run 'codexa init' first.")
|
|
54
|
+
ctx.exit(1)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
from semantic_code_intelligence.tui import run_tui
|
|
58
|
+
run_tui(root, mode=mode, top_k=top_k) # type: ignore[arg-type]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""CLI command: viz — generate Mermaid-compatible visualizations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from semantic_code_intelligence.utils.logging import (
|
|
11
|
+
get_logger,
|
|
12
|
+
print_error,
|
|
13
|
+
print_info,
|
|
14
|
+
console,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.viz")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("viz")
|
|
21
|
+
@click.argument(
|
|
22
|
+
"kind",
|
|
23
|
+
type=click.Choice(["callgraph", "deps", "symbols", "workspace"], case_sensitive=False),
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--target",
|
|
27
|
+
"-t",
|
|
28
|
+
default="",
|
|
29
|
+
type=str,
|
|
30
|
+
help="Symbol name or file path to visualize.",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--output",
|
|
34
|
+
"-o",
|
|
35
|
+
default=None,
|
|
36
|
+
type=click.Path(dir_okay=False, resolve_path=True),
|
|
37
|
+
help="Write Mermaid output to a file instead of stdout.",
|
|
38
|
+
)
|
|
39
|
+
@click.option(
|
|
40
|
+
"--json-output",
|
|
41
|
+
"--json",
|
|
42
|
+
"json_mode",
|
|
43
|
+
is_flag=True,
|
|
44
|
+
default=False,
|
|
45
|
+
help="Output as JSON with a 'mermaid' field.",
|
|
46
|
+
)
|
|
47
|
+
@click.option(
|
|
48
|
+
"--path",
|
|
49
|
+
"-p",
|
|
50
|
+
default=".",
|
|
51
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
52
|
+
help="Project root path.",
|
|
53
|
+
)
|
|
54
|
+
@click.pass_context
|
|
55
|
+
def viz_cmd(
|
|
56
|
+
ctx: click.Context,
|
|
57
|
+
kind: str,
|
|
58
|
+
target: str,
|
|
59
|
+
output: str | None,
|
|
60
|
+
json_mode: bool,
|
|
61
|
+
path: str,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Generate Mermaid-compatible diagrams from codebase analysis.
|
|
64
|
+
|
|
65
|
+
KIND is one of: callgraph, deps, symbols, workspace.
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
|
|
69
|
+
codexa viz callgraph
|
|
70
|
+
|
|
71
|
+
codexa viz deps --target src/main.py
|
|
72
|
+
|
|
73
|
+
codexa viz symbols --target auth.py -o symbols.mmd
|
|
74
|
+
|
|
75
|
+
codexa viz callgraph --json
|
|
76
|
+
"""
|
|
77
|
+
from semantic_code_intelligence.bridge.context_provider import ContextProvider
|
|
78
|
+
from semantic_code_intelligence.web.visualize import (
|
|
79
|
+
render_call_graph,
|
|
80
|
+
render_dependency_graph,
|
|
81
|
+
render_symbol_map,
|
|
82
|
+
render_workspace_graph,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
project_root = Path(path)
|
|
86
|
+
provider = ContextProvider(project_root)
|
|
87
|
+
|
|
88
|
+
kind_lower = kind.lower()
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
if kind_lower == "callgraph":
|
|
92
|
+
data = provider.get_call_graph(symbol_name=target)
|
|
93
|
+
mermaid = render_call_graph(data.get("edges", []))
|
|
94
|
+
elif kind_lower == "deps":
|
|
95
|
+
data = provider.get_dependencies(file_path=target)
|
|
96
|
+
mermaid = render_dependency_graph(data)
|
|
97
|
+
elif kind_lower == "symbols":
|
|
98
|
+
builder = provider._ensure_indexed()
|
|
99
|
+
symbols = builder.get_all_symbols()
|
|
100
|
+
if target:
|
|
101
|
+
symbols = [s for s in symbols if target in s.file_path]
|
|
102
|
+
sym_dicts = [s.to_dict() for s in symbols[:100]]
|
|
103
|
+
mermaid = render_symbol_map(sym_dicts, file_path=target)
|
|
104
|
+
elif kind_lower == "workspace":
|
|
105
|
+
# Try to load workspace repos
|
|
106
|
+
try:
|
|
107
|
+
from semantic_code_intelligence.workspace import Workspace
|
|
108
|
+
ws = Workspace.load(project_root)
|
|
109
|
+
repos = [r.to_dict() for r in ws.repos] if ws.repos else []
|
|
110
|
+
except Exception:
|
|
111
|
+
repos = []
|
|
112
|
+
mermaid = render_workspace_graph(repos)
|
|
113
|
+
else:
|
|
114
|
+
print_error(f"Unknown visualization kind: {kind}")
|
|
115
|
+
return
|
|
116
|
+
except Exception as exc:
|
|
117
|
+
print_error(f"Visualization error: {exc}")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
# Output
|
|
121
|
+
if json_mode:
|
|
122
|
+
click.echo(json.dumps({"kind": kind_lower, "mermaid": mermaid}, indent=2))
|
|
123
|
+
elif output:
|
|
124
|
+
Path(output).write_text(mermaid, encoding="utf-8")
|
|
125
|
+
print_info(f"Written to {output}")
|
|
126
|
+
else:
|
|
127
|
+
console.print(mermaid)
|