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,103 @@
|
|
|
1
|
+
"""CLI command: refactor — AI-powered refactoring suggestions for a file."""
|
|
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.config.settings import AppConfig, load_config
|
|
11
|
+
from semantic_code_intelligence.utils.logging import (
|
|
12
|
+
console,
|
|
13
|
+
get_logger,
|
|
14
|
+
print_error,
|
|
15
|
+
print_info,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
logger = get_logger("cli.refactor")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.command("refactor")
|
|
22
|
+
@click.argument(
|
|
23
|
+
"file",
|
|
24
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--instruction",
|
|
28
|
+
"-i",
|
|
29
|
+
default="Improve code quality, readability, and performance.",
|
|
30
|
+
help="Refactoring instruction for the AI.",
|
|
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.option(
|
|
41
|
+
"--path",
|
|
42
|
+
"-p",
|
|
43
|
+
default=".",
|
|
44
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
45
|
+
help="Project root path.",
|
|
46
|
+
)
|
|
47
|
+
@click.pass_context
|
|
48
|
+
def refactor_cmd(
|
|
49
|
+
ctx: click.Context,
|
|
50
|
+
file: str,
|
|
51
|
+
instruction: str,
|
|
52
|
+
json_mode: bool,
|
|
53
|
+
path: str,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Suggest refactored code for a source file.
|
|
56
|
+
|
|
57
|
+
Uses structural analysis + LLM to propose improved code with explanations.
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
|
|
61
|
+
codexa refactor src/main.py
|
|
62
|
+
|
|
63
|
+
codexa refactor src/utils.py -i "Extract duplicated logic into helpers"
|
|
64
|
+
"""
|
|
65
|
+
root = Path(path).resolve()
|
|
66
|
+
config_dir = AppConfig.config_dir(root)
|
|
67
|
+
|
|
68
|
+
if not config_dir.exists():
|
|
69
|
+
print_error(f"Project not initialized at {root}. Run 'codexa init' first.")
|
|
70
|
+
ctx.exit(1)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
config = load_config(root)
|
|
74
|
+
|
|
75
|
+
from semantic_code_intelligence.cli.commands.ask_cmd import _get_provider
|
|
76
|
+
from semantic_code_intelligence.llm.reasoning import ReasoningEngine
|
|
77
|
+
from semantic_code_intelligence.llm.safety import SafetyValidator
|
|
78
|
+
|
|
79
|
+
provider = _get_provider(config)
|
|
80
|
+
engine = ReasoningEngine(provider, root)
|
|
81
|
+
result = engine.refactor(file, instruction)
|
|
82
|
+
|
|
83
|
+
# Safety check on refactored code
|
|
84
|
+
if result.refactored_code:
|
|
85
|
+
validator = SafetyValidator()
|
|
86
|
+
report = validator.validate(result.refactored_code)
|
|
87
|
+
if not report.safe:
|
|
88
|
+
from semantic_code_intelligence.utils.logging import _ICON_WARNING
|
|
89
|
+
console.print(f"[bold red]{_ICON_WARNING} Safety issues detected in refactored code:[/bold red]")
|
|
90
|
+
for issue in report.issues:
|
|
91
|
+
console.print(f" L{issue.line_number}: {issue.description}")
|
|
92
|
+
console.print()
|
|
93
|
+
|
|
94
|
+
if json_mode:
|
|
95
|
+
click.echo(json_mod.dumps(result.to_dict(), indent=2))
|
|
96
|
+
else:
|
|
97
|
+
console.print(f"\n[bold cyan]Refactor:[/bold cyan] {result.file_path}\n")
|
|
98
|
+
console.print(f"[bold green]Explanation:[/bold green]\n{result.explanation}\n")
|
|
99
|
+
if result.refactored_code:
|
|
100
|
+
console.print("[bold]Refactored code:[/bold]")
|
|
101
|
+
console.print(result.refactored_code)
|
|
102
|
+
else:
|
|
103
|
+
print_info("No refactored code produced (check LLM configuration).")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""CLI command: review — AI-powered code review of a file."""
|
|
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.config.settings import AppConfig, load_config
|
|
11
|
+
from semantic_code_intelligence.utils.logging import (
|
|
12
|
+
console,
|
|
13
|
+
get_logger,
|
|
14
|
+
print_error,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.review")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("review")
|
|
21
|
+
@click.argument(
|
|
22
|
+
"file",
|
|
23
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--json-output",
|
|
27
|
+
"--json",
|
|
28
|
+
"json_mode",
|
|
29
|
+
is_flag=True,
|
|
30
|
+
default=False,
|
|
31
|
+
help="Output in JSON format.",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--path",
|
|
35
|
+
"-p",
|
|
36
|
+
default=".",
|
|
37
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
38
|
+
help="Project root path.",
|
|
39
|
+
)
|
|
40
|
+
@click.pass_context
|
|
41
|
+
def review_cmd(
|
|
42
|
+
ctx: click.Context,
|
|
43
|
+
file: str,
|
|
44
|
+
json_mode: bool,
|
|
45
|
+
path: str,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""Review a source file for issues, bugs, and improvements.
|
|
48
|
+
|
|
49
|
+
Uses structural analysis + LLM to perform an AI-assisted code review.
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
|
|
53
|
+
codexa review src/main.py
|
|
54
|
+
|
|
55
|
+
codexa review src/utils.py --json
|
|
56
|
+
"""
|
|
57
|
+
root = Path(path).resolve()
|
|
58
|
+
config_dir = AppConfig.config_dir(root)
|
|
59
|
+
|
|
60
|
+
if not config_dir.exists():
|
|
61
|
+
print_error(f"Project not initialized at {root}. Run 'codexa init' first.")
|
|
62
|
+
ctx.exit(1)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
config = load_config(root)
|
|
66
|
+
|
|
67
|
+
from semantic_code_intelligence.cli.commands.ask_cmd import _get_provider
|
|
68
|
+
from semantic_code_intelligence.llm.reasoning import ReasoningEngine
|
|
69
|
+
|
|
70
|
+
provider = _get_provider(config)
|
|
71
|
+
engine = ReasoningEngine(provider, root)
|
|
72
|
+
result = engine.review(file)
|
|
73
|
+
|
|
74
|
+
if json_mode:
|
|
75
|
+
click.echo(json_mod.dumps(result.to_dict(), indent=2))
|
|
76
|
+
else:
|
|
77
|
+
console.print(f"\n[bold cyan]Code Review:[/bold cyan] {result.file_path}\n")
|
|
78
|
+
if result.issues:
|
|
79
|
+
for issue in result.issues:
|
|
80
|
+
sev = issue.get("severity", "info")
|
|
81
|
+
line = issue.get("line", "?")
|
|
82
|
+
msg = issue.get("message", str(issue))
|
|
83
|
+
color = {"error": "red", "warning": "yellow"}.get(sev, "blue")
|
|
84
|
+
console.print(f" [{color}]{sev.upper()}[/{color}] L{line}: {msg}")
|
|
85
|
+
if issue.get("suggestion"):
|
|
86
|
+
console.print(f" [dim]Suggestion: {issue['suggestion']}[/dim]")
|
|
87
|
+
console.print()
|
|
88
|
+
console.print(f"[bold green]Summary:[/bold green]\n{result.summary}")
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""CLI command: search - Perform semantic, keyword, regex, or hybrid search."""
|
|
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.config.settings import AppConfig, load_config
|
|
11
|
+
from semantic_code_intelligence.services.search_service import SearchMode, search_codebase
|
|
12
|
+
from semantic_code_intelligence.search.formatter import (
|
|
13
|
+
format_results_json,
|
|
14
|
+
format_results_jsonl,
|
|
15
|
+
format_results_rich,
|
|
16
|
+
)
|
|
17
|
+
from semantic_code_intelligence.utils.logging import (
|
|
18
|
+
get_logger,
|
|
19
|
+
print_error,
|
|
20
|
+
print_info,
|
|
21
|
+
print_warning,
|
|
22
|
+
console,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
logger = get_logger("cli.search")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.command("search")
|
|
29
|
+
@click.argument("query", type=str)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--top-k",
|
|
32
|
+
"-k",
|
|
33
|
+
default=None,
|
|
34
|
+
type=int,
|
|
35
|
+
help="Number of results to return (overrides config).",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--json-output",
|
|
39
|
+
"--json",
|
|
40
|
+
"json_mode",
|
|
41
|
+
is_flag=True,
|
|
42
|
+
default=False,
|
|
43
|
+
help="Output results in JSON format for AI integration.",
|
|
44
|
+
)
|
|
45
|
+
@click.option(
|
|
46
|
+
"--jsonl",
|
|
47
|
+
"jsonl_mode",
|
|
48
|
+
is_flag=True,
|
|
49
|
+
default=False,
|
|
50
|
+
help="Output one JSON object per line (JSONL), for piping into jq/fzf.",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"--path",
|
|
54
|
+
"-p",
|
|
55
|
+
default=".",
|
|
56
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
57
|
+
help="Project root path.",
|
|
58
|
+
)
|
|
59
|
+
@click.option(
|
|
60
|
+
"--mode",
|
|
61
|
+
"-m",
|
|
62
|
+
type=click.Choice(["semantic", "keyword", "regex", "hybrid"], case_sensitive=False),
|
|
63
|
+
default="semantic",
|
|
64
|
+
help="Search mode: semantic (default), keyword (BM25), regex, or hybrid (RRF).",
|
|
65
|
+
)
|
|
66
|
+
@click.option(
|
|
67
|
+
"--full-section",
|
|
68
|
+
"--full",
|
|
69
|
+
is_flag=True,
|
|
70
|
+
default=False,
|
|
71
|
+
help="Expand results to show the full enclosing function/class.",
|
|
72
|
+
)
|
|
73
|
+
@click.option(
|
|
74
|
+
"--no-auto-index",
|
|
75
|
+
is_flag=True,
|
|
76
|
+
default=False,
|
|
77
|
+
help="Disable automatic indexing on first search.",
|
|
78
|
+
)
|
|
79
|
+
@click.option(
|
|
80
|
+
"--case-sensitive",
|
|
81
|
+
"-s",
|
|
82
|
+
is_flag=True,
|
|
83
|
+
default=False,
|
|
84
|
+
help="Case-sensitive matching (regex mode only).",
|
|
85
|
+
)
|
|
86
|
+
@click.option(
|
|
87
|
+
"--context-lines",
|
|
88
|
+
"-C",
|
|
89
|
+
default=0,
|
|
90
|
+
type=int,
|
|
91
|
+
help="Show N context lines before/after each match (grep-style).",
|
|
92
|
+
)
|
|
93
|
+
@click.option(
|
|
94
|
+
"--files-only",
|
|
95
|
+
"-l",
|
|
96
|
+
is_flag=True,
|
|
97
|
+
default=False,
|
|
98
|
+
help="Print only file paths with matches (like grep -l).",
|
|
99
|
+
)
|
|
100
|
+
@click.option(
|
|
101
|
+
"--files-without-match",
|
|
102
|
+
"-L",
|
|
103
|
+
is_flag=True,
|
|
104
|
+
default=False,
|
|
105
|
+
help="Print file paths without any matches (like grep -L).",
|
|
106
|
+
)
|
|
107
|
+
@click.option(
|
|
108
|
+
"--line-numbers",
|
|
109
|
+
"-n",
|
|
110
|
+
is_flag=True,
|
|
111
|
+
default=False,
|
|
112
|
+
help="Prefix each output line with its line number (like grep -n).",
|
|
113
|
+
)
|
|
114
|
+
@click.pass_context
|
|
115
|
+
def search_cmd(
|
|
116
|
+
ctx: click.Context,
|
|
117
|
+
query: str,
|
|
118
|
+
top_k: int | None,
|
|
119
|
+
json_mode: bool,
|
|
120
|
+
jsonl_mode: bool,
|
|
121
|
+
path: str,
|
|
122
|
+
mode: str,
|
|
123
|
+
full_section: bool,
|
|
124
|
+
no_auto_index: bool,
|
|
125
|
+
case_sensitive: bool,
|
|
126
|
+
context_lines: int,
|
|
127
|
+
files_only: bool,
|
|
128
|
+
files_without_match: bool,
|
|
129
|
+
line_numbers: bool,
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Search the indexed codebase using a natural language query.
|
|
132
|
+
|
|
133
|
+
Supports four search modes:
|
|
134
|
+
|
|
135
|
+
\b
|
|
136
|
+
semantic — vector similarity (default)
|
|
137
|
+
keyword — BM25 ranked keyword search
|
|
138
|
+
regex — grep-compatible regex pattern matching
|
|
139
|
+
hybrid — fused semantic + BM25 via Reciprocal Rank Fusion
|
|
140
|
+
|
|
141
|
+
Grep-compatible flags:
|
|
142
|
+
|
|
143
|
+
\b
|
|
144
|
+
-l show only file paths with matches
|
|
145
|
+
-L show only file paths without matches
|
|
146
|
+
-n prefix lines with line numbers
|
|
147
|
+
-C N show N context lines before/after each match
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
|
|
151
|
+
\b
|
|
152
|
+
codexa search "jwt verification"
|
|
153
|
+
codexa search "database connection" --mode hybrid
|
|
154
|
+
codexa search "def\\s+authenticate" --mode regex -n
|
|
155
|
+
codexa search "error handling" --mode keyword --full-section
|
|
156
|
+
codexa search "error handling" -k 5 --json
|
|
157
|
+
codexa search "TODO" --mode regex -l
|
|
158
|
+
codexa search "pattern" --jsonl | jq .file_path
|
|
159
|
+
"""
|
|
160
|
+
root = Path(path).resolve()
|
|
161
|
+
config_dir = AppConfig.config_dir(root)
|
|
162
|
+
|
|
163
|
+
if not config_dir.exists():
|
|
164
|
+
print_error(
|
|
165
|
+
f"Project not initialized at {root}. Run 'codexa init' first."
|
|
166
|
+
)
|
|
167
|
+
ctx.exit(1)
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
config = load_config(root)
|
|
171
|
+
result_count = top_k or config.search.top_k
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
results = search_codebase(
|
|
175
|
+
query=query,
|
|
176
|
+
project_root=root,
|
|
177
|
+
top_k=result_count,
|
|
178
|
+
mode=mode, # type: ignore[arg-type]
|
|
179
|
+
full_section=full_section,
|
|
180
|
+
auto_index=not no_auto_index,
|
|
181
|
+
case_insensitive=not case_sensitive,
|
|
182
|
+
)
|
|
183
|
+
except FileNotFoundError:
|
|
184
|
+
if json_mode:
|
|
185
|
+
click.echo(format_results_json(query, [], result_count))
|
|
186
|
+
elif jsonl_mode:
|
|
187
|
+
pass # no output for JSONL with no results
|
|
188
|
+
else:
|
|
189
|
+
print_warning(
|
|
190
|
+
"Search index is empty. Run 'codexa index' to build the index."
|
|
191
|
+
)
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
# --- Grep-style output modes ---
|
|
195
|
+
|
|
196
|
+
if files_without_match:
|
|
197
|
+
# Show all indexed files NOT in the results
|
|
198
|
+
matched_files = {r.file_path for r in results}
|
|
199
|
+
from semantic_code_intelligence.storage.vector_store import VectorStore
|
|
200
|
+
index_dir = AppConfig.index_dir(root)
|
|
201
|
+
try:
|
|
202
|
+
store = VectorStore.load(index_dir)
|
|
203
|
+
all_files = sorted({m.file_path for m in store.metadata})
|
|
204
|
+
except FileNotFoundError:
|
|
205
|
+
all_files = []
|
|
206
|
+
for fp in all_files:
|
|
207
|
+
if fp not in matched_files:
|
|
208
|
+
click.echo(fp)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
if files_only:
|
|
212
|
+
seen: set[str] = set()
|
|
213
|
+
for r in results:
|
|
214
|
+
if r.file_path not in seen:
|
|
215
|
+
seen.add(r.file_path)
|
|
216
|
+
click.echo(r.file_path)
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
# --- Machine-readable output ---
|
|
220
|
+
|
|
221
|
+
if jsonl_mode:
|
|
222
|
+
click.echo(format_results_jsonl(results))
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
if json_mode:
|
|
226
|
+
click.echo(format_results_json(query, results, result_count))
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
# --- Rich / grep-style human output ---
|
|
230
|
+
|
|
231
|
+
format_results_rich(
|
|
232
|
+
query,
|
|
233
|
+
results,
|
|
234
|
+
line_numbers=line_numbers,
|
|
235
|
+
context_lines=context_lines,
|
|
236
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""CLI command: serve — start the CodexA bridge server.
|
|
2
|
+
|
|
3
|
+
Supports MCP-over-SSE via ``--mcp`` flag for AI agent integration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from semantic_code_intelligence.utils.logging import (
|
|
13
|
+
get_logger,
|
|
14
|
+
print_info,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.serve")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("serve")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--host",
|
|
23
|
+
"-h",
|
|
24
|
+
default="127.0.0.1",
|
|
25
|
+
type=str,
|
|
26
|
+
help="Host to bind the bridge server to.",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--port",
|
|
30
|
+
"-p",
|
|
31
|
+
default=24842,
|
|
32
|
+
type=int,
|
|
33
|
+
help="Port to bind the bridge server to.",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--path",
|
|
37
|
+
default=".",
|
|
38
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
39
|
+
help="Project root path.",
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"--mcp",
|
|
43
|
+
"mcp_mode",
|
|
44
|
+
is_flag=True,
|
|
45
|
+
default=False,
|
|
46
|
+
help="Expose MCP tools over HTTP+SSE (for AI agent integration).",
|
|
47
|
+
)
|
|
48
|
+
@click.pass_context
|
|
49
|
+
def serve_cmd(
|
|
50
|
+
ctx: click.Context,
|
|
51
|
+
host: str,
|
|
52
|
+
port: int,
|
|
53
|
+
path: str,
|
|
54
|
+
mcp_mode: bool,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Start the CodexA bridge server for external AI integration.
|
|
57
|
+
|
|
58
|
+
Use --mcp to expose MCP-compliant tools over HTTP with SSE streaming.
|
|
59
|
+
|
|
60
|
+
\b
|
|
61
|
+
Examples:
|
|
62
|
+
codexa serve
|
|
63
|
+
codexa serve --port 8080
|
|
64
|
+
codexa serve --mcp
|
|
65
|
+
"""
|
|
66
|
+
project_root = Path(path)
|
|
67
|
+
|
|
68
|
+
if mcp_mode:
|
|
69
|
+
_run_mcp_http(project_root, host, port)
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
from semantic_code_intelligence.bridge.server import BridgeServer
|
|
73
|
+
|
|
74
|
+
server = BridgeServer(project_root, host=host, port=port)
|
|
75
|
+
|
|
76
|
+
print_info(f"Starting CodexA bridge server on {server.url}")
|
|
77
|
+
print_info("Press Ctrl+C to stop.")
|
|
78
|
+
|
|
79
|
+
server.start()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _run_mcp_http(project_root: Path, host: str, port: int) -> None:
|
|
83
|
+
"""Run MCP tools over HTTP+SSE using the streamable-http transport."""
|
|
84
|
+
try:
|
|
85
|
+
from mcp.server.sse import SseServerTransport
|
|
86
|
+
from starlette.applications import Starlette
|
|
87
|
+
from starlette.routing import Route, Mount
|
|
88
|
+
import uvicorn
|
|
89
|
+
except ImportError:
|
|
90
|
+
# Fallback: just run MCP stdio server
|
|
91
|
+
print_info("SSE/Starlette not installed — falling back to stdio MCP.")
|
|
92
|
+
from semantic_code_intelligence.mcp import run_mcp_server
|
|
93
|
+
run_mcp_server(project_root)
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
from semantic_code_intelligence.mcp import _create_server
|
|
97
|
+
|
|
98
|
+
server = _create_server(project_root)
|
|
99
|
+
sse = SseServerTransport("/messages/")
|
|
100
|
+
|
|
101
|
+
async def handle_sse(request):
|
|
102
|
+
async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
|
|
103
|
+
await server.run(
|
|
104
|
+
streams[0], streams[1],
|
|
105
|
+
server.create_initialization_options(),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
app = Starlette(
|
|
109
|
+
routes=[
|
|
110
|
+
Route("/sse", endpoint=handle_sse),
|
|
111
|
+
Mount("/messages/", app=sse.handle_post_message),
|
|
112
|
+
],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
print_info(f"MCP-over-SSE server on http://{host}:{port}/sse")
|
|
116
|
+
print_info("Press Ctrl+C to stop.")
|
|
117
|
+
uvicorn.run(app, host=host, port=port, log_level="info")
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""CLI command: suggest — AI-powered intelligent suggestions for a symbol/file."""
|
|
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.config.settings import AppConfig, load_config
|
|
11
|
+
from semantic_code_intelligence.utils.logging import (
|
|
12
|
+
console,
|
|
13
|
+
get_logger,
|
|
14
|
+
print_error,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.suggest")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("suggest")
|
|
21
|
+
@click.argument("target", type=str)
|
|
22
|
+
@click.option(
|
|
23
|
+
"--top-k",
|
|
24
|
+
"-k",
|
|
25
|
+
default=5,
|
|
26
|
+
type=int,
|
|
27
|
+
help="Number of context snippets to consider.",
|
|
28
|
+
)
|
|
29
|
+
@click.option(
|
|
30
|
+
"--json-output",
|
|
31
|
+
"--json",
|
|
32
|
+
"json_mode",
|
|
33
|
+
is_flag=True,
|
|
34
|
+
default=False,
|
|
35
|
+
help="Output in JSON format.",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--path",
|
|
39
|
+
"-p",
|
|
40
|
+
default=".",
|
|
41
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
42
|
+
help="Project root path.",
|
|
43
|
+
)
|
|
44
|
+
@click.pass_context
|
|
45
|
+
def suggest_cmd(
|
|
46
|
+
ctx: click.Context,
|
|
47
|
+
target: str,
|
|
48
|
+
top_k: int,
|
|
49
|
+
json_mode: bool,
|
|
50
|
+
path: str,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Get intelligent suggestions for a symbol, file, or topic.
|
|
53
|
+
|
|
54
|
+
Combines call-graph, dependency, and semantic data with LLM reasoning
|
|
55
|
+
to produce actionable suggestions with "why" reasoning.
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
|
|
59
|
+
codexa suggest search_codebase
|
|
60
|
+
|
|
61
|
+
codexa suggest "error handling patterns" --json
|
|
62
|
+
"""
|
|
63
|
+
root = Path(path).resolve()
|
|
64
|
+
config_dir = AppConfig.config_dir(root)
|
|
65
|
+
|
|
66
|
+
if not config_dir.exists():
|
|
67
|
+
print_error(f"Project not initialized at {root}. Run 'codexa init' first.")
|
|
68
|
+
ctx.exit(1)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
config = load_config(root)
|
|
72
|
+
|
|
73
|
+
from semantic_code_intelligence.cli.commands.ask_cmd import _get_provider
|
|
74
|
+
from semantic_code_intelligence.llm.reasoning import ReasoningEngine
|
|
75
|
+
|
|
76
|
+
provider = _get_provider(config)
|
|
77
|
+
engine = ReasoningEngine(provider, root)
|
|
78
|
+
result = engine.suggest(target, top_k=top_k)
|
|
79
|
+
|
|
80
|
+
if json_mode:
|
|
81
|
+
click.echo(json_mod.dumps(result.to_dict(), indent=2))
|
|
82
|
+
else:
|
|
83
|
+
console.print(f"\n[bold cyan]Suggestions for:[/bold cyan] {result.target}\n")
|
|
84
|
+
if result.suggestions:
|
|
85
|
+
for i, sug in enumerate(result.suggestions, 1):
|
|
86
|
+
title = sug.get("title", f"Suggestion {i}")
|
|
87
|
+
desc = sug.get("description", "")
|
|
88
|
+
reason = sug.get("reason", "")
|
|
89
|
+
priority = sug.get("priority", "medium")
|
|
90
|
+
color = {"high": "red", "medium": "yellow", "low": "green"}.get(
|
|
91
|
+
priority, "blue"
|
|
92
|
+
)
|
|
93
|
+
console.print(f" [{color}]{priority.upper()}[/{color}] {title}")
|
|
94
|
+
if desc:
|
|
95
|
+
console.print(f" {desc}")
|
|
96
|
+
if reason:
|
|
97
|
+
console.print(f" [dim]Why: {reason}[/dim]")
|
|
98
|
+
console.print()
|
|
99
|
+
else:
|
|
100
|
+
console.print("[dim]No suggestions generated.[/dim]")
|