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,120 @@
|
|
|
1
|
+
"""CLI command: context — generate structured context for external 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_info,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.context")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("context")
|
|
21
|
+
@click.argument("mode", type=click.Choice(["query", "symbol", "file", "repo"]))
|
|
22
|
+
@click.argument("target", required=False, default=None)
|
|
23
|
+
@click.option(
|
|
24
|
+
"--top-k",
|
|
25
|
+
"-k",
|
|
26
|
+
default=5,
|
|
27
|
+
type=int,
|
|
28
|
+
help="Number of results for query mode.",
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--file-path",
|
|
32
|
+
"-f",
|
|
33
|
+
default=None,
|
|
34
|
+
type=str,
|
|
35
|
+
help="File path hint (for symbol mode).",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--json-output",
|
|
39
|
+
"--json",
|
|
40
|
+
"json_mode",
|
|
41
|
+
is_flag=True,
|
|
42
|
+
default=False,
|
|
43
|
+
help="Output raw JSON (default is pretty-printed).",
|
|
44
|
+
)
|
|
45
|
+
@click.option(
|
|
46
|
+
"--path",
|
|
47
|
+
"-p",
|
|
48
|
+
default=".",
|
|
49
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
50
|
+
help="Project root path.",
|
|
51
|
+
)
|
|
52
|
+
@click.pass_context
|
|
53
|
+
def context_cmd(
|
|
54
|
+
ctx: click.Context,
|
|
55
|
+
mode: str,
|
|
56
|
+
target: str | None,
|
|
57
|
+
top_k: int,
|
|
58
|
+
file_path: str | None,
|
|
59
|
+
json_mode: bool,
|
|
60
|
+
path: str,
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Generate structured context for external AI pipelines.
|
|
63
|
+
|
|
64
|
+
\b
|
|
65
|
+
Modes:
|
|
66
|
+
query — semantic search (TARGET = search query)
|
|
67
|
+
symbol — symbol context (TARGET = symbol name)
|
|
68
|
+
file — file context (TARGET = file path)
|
|
69
|
+
repo — repo summary (no TARGET needed)
|
|
70
|
+
"""
|
|
71
|
+
from semantic_code_intelligence.bridge.context_provider import ContextProvider
|
|
72
|
+
|
|
73
|
+
project_root = Path(path)
|
|
74
|
+
provider = ContextProvider(project_root)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
if mode == "query":
|
|
78
|
+
if not target:
|
|
79
|
+
print_error("Query mode requires a TARGET argument (the search query).")
|
|
80
|
+
ctx.exit(1)
|
|
81
|
+
return
|
|
82
|
+
data = provider.context_for_query(query=target, top_k=top_k)
|
|
83
|
+
|
|
84
|
+
elif mode == "symbol":
|
|
85
|
+
if not target:
|
|
86
|
+
print_error("Symbol mode requires a TARGET argument (symbol name).")
|
|
87
|
+
ctx.exit(1)
|
|
88
|
+
return
|
|
89
|
+
data = provider.context_for_symbol(
|
|
90
|
+
symbol_name=target, file_path=file_path,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
elif mode == "file":
|
|
94
|
+
if not target:
|
|
95
|
+
print_error("File mode requires a TARGET argument (file path).")
|
|
96
|
+
ctx.exit(1)
|
|
97
|
+
return
|
|
98
|
+
data = provider.context_for_file(file_path=target)
|
|
99
|
+
|
|
100
|
+
elif mode == "repo":
|
|
101
|
+
data = provider.context_for_repo()
|
|
102
|
+
|
|
103
|
+
else:
|
|
104
|
+
print_error(f"Unknown mode: {mode}")
|
|
105
|
+
ctx.exit(1)
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
except Exception as exc:
|
|
109
|
+
logger.exception("Context generation failed")
|
|
110
|
+
print_error(f"Error: {exc}")
|
|
111
|
+
ctx.exit(1)
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
if json_mode:
|
|
115
|
+
click.echo(json_mod.dumps(data, indent=2))
|
|
116
|
+
else:
|
|
117
|
+
from rich.syntax import Syntax
|
|
118
|
+
|
|
119
|
+
formatted = json_mod.dumps(data, indent=2)
|
|
120
|
+
console.print(Syntax(formatted, "json", theme="monokai"))
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""CLI command: cross-refactor — cross-repository refactoring suggestions."""
|
|
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_info,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger("cli.cross_refactor")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command("cross-refactor")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--json-output", "--json", "json_mode",
|
|
23
|
+
is_flag=True,
|
|
24
|
+
default=False,
|
|
25
|
+
help="Output in JSON format.",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--threshold", "-t",
|
|
29
|
+
default=0.70,
|
|
30
|
+
type=float,
|
|
31
|
+
help="Similarity threshold for duplicate detection (0.0-1.0).",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--path", "-p",
|
|
35
|
+
default=".",
|
|
36
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
37
|
+
help="Workspace root path.",
|
|
38
|
+
)
|
|
39
|
+
@click.option("--pipe", is_flag=True, default=False, hidden=True)
|
|
40
|
+
@click.pass_context
|
|
41
|
+
def cross_refactor_cmd(
|
|
42
|
+
ctx: click.Context,
|
|
43
|
+
json_mode: bool,
|
|
44
|
+
threshold: float,
|
|
45
|
+
path: str,
|
|
46
|
+
pipe: bool,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Analyse workspace repos for cross-repo refactoring opportunities.
|
|
49
|
+
|
|
50
|
+
Scans all registered repositories in the workspace for duplicate logic,
|
|
51
|
+
inconsistent patterns, and symbols that could be extracted into a shared
|
|
52
|
+
library. When an LLM is configured, generates actionable suggestions.
|
|
53
|
+
"""
|
|
54
|
+
from semantic_code_intelligence.llm.cross_refactor import analyze_cross_repo
|
|
55
|
+
|
|
56
|
+
root = Path(path).resolve()
|
|
57
|
+
pipe = pipe or ctx.obj.get("pipe", False)
|
|
58
|
+
|
|
59
|
+
# Optionally get LLM provider for suggestions
|
|
60
|
+
provider = None
|
|
61
|
+
try:
|
|
62
|
+
from semantic_code_intelligence.config.settings import load_config
|
|
63
|
+
|
|
64
|
+
config = load_config(root)
|
|
65
|
+
if config.llm.provider != "none":
|
|
66
|
+
from semantic_code_intelligence.cli.commands.chat_cmd import _get_provider
|
|
67
|
+
|
|
68
|
+
provider = _get_provider(config)
|
|
69
|
+
except Exception:
|
|
70
|
+
logger.debug("LLM provider not available; running without AI suggestions")
|
|
71
|
+
|
|
72
|
+
result = analyze_cross_repo(root, provider=provider, threshold=threshold)
|
|
73
|
+
|
|
74
|
+
if json_mode:
|
|
75
|
+
click.echo(json_mod.dumps(result.to_dict(), indent=2))
|
|
76
|
+
elif pipe:
|
|
77
|
+
click.echo(f"Repos: {', '.join(result.repos_analyzed)}")
|
|
78
|
+
click.echo(f"Symbols: {result.total_symbols}")
|
|
79
|
+
click.echo(f"Matches: {len(result.matches)}")
|
|
80
|
+
for m in result.matches:
|
|
81
|
+
click.echo(f" {m.repo_a}/{m.symbol_a} <-> {m.repo_b}/{m.symbol_b} ({m.similarity_note})")
|
|
82
|
+
for s in result.suggestions:
|
|
83
|
+
click.echo(f" Suggestion: {s.get('title', 'N/A')}")
|
|
84
|
+
else:
|
|
85
|
+
from rich.table import Table
|
|
86
|
+
from rich.panel import Panel
|
|
87
|
+
|
|
88
|
+
if not result.repos_analyzed:
|
|
89
|
+
print_info("No workspace found. Use 'codexa workspace init' first.")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
console.print(f"[bold]Cross-repo analysis[/] — {result.total_symbols} symbols across {len(result.repos_analyzed)} repos")
|
|
93
|
+
|
|
94
|
+
if result.matches:
|
|
95
|
+
table = Table(title="Cross-Repo Duplicates")
|
|
96
|
+
table.add_column("Repo A")
|
|
97
|
+
table.add_column("Symbol A")
|
|
98
|
+
table.add_column("Repo B")
|
|
99
|
+
table.add_column("Symbol B")
|
|
100
|
+
table.add_column("Similarity")
|
|
101
|
+
for m in result.matches[:20]:
|
|
102
|
+
table.add_row(m.repo_a, m.symbol_a, m.repo_b, m.symbol_b, m.similarity_note)
|
|
103
|
+
console.print(table)
|
|
104
|
+
else:
|
|
105
|
+
print_info("No cross-repo duplicates detected.")
|
|
106
|
+
|
|
107
|
+
if result.suggestions:
|
|
108
|
+
console.print()
|
|
109
|
+
for s in result.suggestions:
|
|
110
|
+
console.print(Panel(
|
|
111
|
+
f"{s.get('description', '')}",
|
|
112
|
+
title=f"[bold]{s.get('title', 'Suggestion')}[/] [{s.get('priority', 'medium')}]",
|
|
113
|
+
))
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""CLI command: deps — show file/project dependency map."""
|
|
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 load_config
|
|
10
|
+
from semantic_code_intelligence.context.engine import DependencyMap
|
|
11
|
+
from semantic_code_intelligence.indexing.scanner import scan_repository
|
|
12
|
+
from semantic_code_intelligence.utils.logging import (
|
|
13
|
+
console,
|
|
14
|
+
get_logger,
|
|
15
|
+
print_error,
|
|
16
|
+
print_info,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = get_logger("cli.deps")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.command("deps")
|
|
23
|
+
@click.argument("target", type=str, default=".")
|
|
24
|
+
@click.option(
|
|
25
|
+
"--path",
|
|
26
|
+
"-p",
|
|
27
|
+
default=".",
|
|
28
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
29
|
+
help="Project root path.",
|
|
30
|
+
)
|
|
31
|
+
@click.option(
|
|
32
|
+
"--json-output",
|
|
33
|
+
"--json",
|
|
34
|
+
"json_mode",
|
|
35
|
+
is_flag=True,
|
|
36
|
+
default=False,
|
|
37
|
+
help="Output in JSON format.",
|
|
38
|
+
)
|
|
39
|
+
@click.pass_context
|
|
40
|
+
def deps_cmd(ctx: click.Context, target: str, path: str, json_mode: bool) -> None:
|
|
41
|
+
"""Show the dependency/import map for a file or the whole project.
|
|
42
|
+
|
|
43
|
+
TARGET can be a specific file path or '.' for the whole project.
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
|
|
47
|
+
codexa deps src/main.py
|
|
48
|
+
|
|
49
|
+
codexa deps .
|
|
50
|
+
|
|
51
|
+
codexa deps . --json
|
|
52
|
+
"""
|
|
53
|
+
import json as json_mod
|
|
54
|
+
|
|
55
|
+
root = Path(path).resolve()
|
|
56
|
+
dep_map = DependencyMap()
|
|
57
|
+
|
|
58
|
+
if target != ".":
|
|
59
|
+
# Single file
|
|
60
|
+
fpath = (root / target).resolve()
|
|
61
|
+
if not fpath.exists():
|
|
62
|
+
print_error(f"File not found: {target}")
|
|
63
|
+
return
|
|
64
|
+
content = fpath.read_text(encoding="utf-8", errors="replace")
|
|
65
|
+
dep_map.add_file(str(fpath), content)
|
|
66
|
+
else:
|
|
67
|
+
# Whole project
|
|
68
|
+
config = load_config(root)
|
|
69
|
+
scanned = scan_repository(root, config.index)
|
|
70
|
+
if not json_mode:
|
|
71
|
+
print_info(f"Analyzing dependencies across {len(scanned)} files...")
|
|
72
|
+
for sf in scanned:
|
|
73
|
+
full_path = root / sf.relative_path
|
|
74
|
+
try:
|
|
75
|
+
content = full_path.read_text(encoding="utf-8", errors="replace")
|
|
76
|
+
dep_map.add_file(str(full_path), content)
|
|
77
|
+
except Exception:
|
|
78
|
+
logger.debug("Could not read %s", sf.relative_path)
|
|
79
|
+
|
|
80
|
+
data = dep_map.to_dict()
|
|
81
|
+
|
|
82
|
+
if json_mode:
|
|
83
|
+
click.echo(json_mod.dumps(data, indent=2))
|
|
84
|
+
else:
|
|
85
|
+
for file_path, deps_list in data.items():
|
|
86
|
+
console.print(f"\n[bold]{file_path}[/bold]")
|
|
87
|
+
if deps_list:
|
|
88
|
+
for dep in deps_list:
|
|
89
|
+
console.print(f" → {dep.get('import_text', dep)}")
|
|
90
|
+
else:
|
|
91
|
+
console.print(" (no imports)")
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""CLI command: docs — generate project documentation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from semantic_code_intelligence.utils.logging import (
|
|
10
|
+
get_logger,
|
|
11
|
+
print_error,
|
|
12
|
+
print_info,
|
|
13
|
+
print_success,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = get_logger("cli.docs")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.command("docs")
|
|
20
|
+
@click.option(
|
|
21
|
+
"--output",
|
|
22
|
+
"-o",
|
|
23
|
+
default="docs",
|
|
24
|
+
type=click.Path(file_okay=False),
|
|
25
|
+
help="Output directory for generated docs.",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--section",
|
|
29
|
+
"-s",
|
|
30
|
+
type=click.Choice(["cli", "plugins", "bridge", "tools", "all"], case_sensitive=False),
|
|
31
|
+
default="all",
|
|
32
|
+
help="Which documentation section to generate.",
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--json-output",
|
|
36
|
+
"--json",
|
|
37
|
+
"json_mode",
|
|
38
|
+
is_flag=True,
|
|
39
|
+
default=False,
|
|
40
|
+
help="Output file list as JSON.",
|
|
41
|
+
)
|
|
42
|
+
def docs_cmd(output: str, section: str, json_mode: bool) -> None:
|
|
43
|
+
"""Generate Markdown documentation for CodexA components.
|
|
44
|
+
|
|
45
|
+
Produces auto-generated reference docs for CLI commands, plugin hooks,
|
|
46
|
+
bridge protocol, and tool registry.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
|
|
50
|
+
codexa docs
|
|
51
|
+
|
|
52
|
+
codexa docs --section plugins -o reference/
|
|
53
|
+
|
|
54
|
+
codexa docs --json
|
|
55
|
+
"""
|
|
56
|
+
import json
|
|
57
|
+
|
|
58
|
+
from semantic_code_intelligence.docs import (
|
|
59
|
+
generate_all_docs,
|
|
60
|
+
generate_bridge_reference,
|
|
61
|
+
generate_cli_reference,
|
|
62
|
+
generate_plugin_reference,
|
|
63
|
+
generate_tool_reference,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
out_dir = Path(output).resolve()
|
|
67
|
+
|
|
68
|
+
if section == "all":
|
|
69
|
+
generated = generate_all_docs(out_dir)
|
|
70
|
+
else:
|
|
71
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
generated = []
|
|
73
|
+
|
|
74
|
+
if section == "cli":
|
|
75
|
+
from semantic_code_intelligence.cli.main import cli
|
|
76
|
+
|
|
77
|
+
md = generate_cli_reference(cli)
|
|
78
|
+
(out_dir / "CLI.md").write_text(md, encoding="utf-8")
|
|
79
|
+
generated.append("CLI.md")
|
|
80
|
+
elif section == "plugins":
|
|
81
|
+
md = generate_plugin_reference()
|
|
82
|
+
(out_dir / "PLUGINS.md").write_text(md, encoding="utf-8")
|
|
83
|
+
generated.append("PLUGINS.md")
|
|
84
|
+
elif section == "bridge":
|
|
85
|
+
md = generate_bridge_reference()
|
|
86
|
+
(out_dir / "BRIDGE.md").write_text(md, encoding="utf-8")
|
|
87
|
+
generated.append("BRIDGE.md")
|
|
88
|
+
elif section == "tools":
|
|
89
|
+
md = generate_tool_reference()
|
|
90
|
+
(out_dir / "TOOLS.md").write_text(md, encoding="utf-8")
|
|
91
|
+
generated.append("TOOLS.md")
|
|
92
|
+
|
|
93
|
+
if json_mode:
|
|
94
|
+
click.echo(json.dumps({"output_dir": str(out_dir), "files": generated}))
|
|
95
|
+
else:
|
|
96
|
+
if generated:
|
|
97
|
+
for f in generated:
|
|
98
|
+
print_success(f"Generated {out_dir / f}")
|
|
99
|
+
print_info(f"{len(generated)} doc(s) written to {out_dir}/")
|
|
100
|
+
else:
|
|
101
|
+
print_error("No documentation generated.")
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""CLI command: doctor — check environment health and dependencies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import json as json_mod
|
|
7
|
+
import platform
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from semantic_code_intelligence import __version__
|
|
15
|
+
from semantic_code_intelligence.utils.logging import (
|
|
16
|
+
console,
|
|
17
|
+
get_logger,
|
|
18
|
+
print_error,
|
|
19
|
+
print_info,
|
|
20
|
+
print_success,
|
|
21
|
+
print_warning,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = get_logger("cli.doctor")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _check_python() -> dict[str, Any]:
|
|
28
|
+
"""Check Python version."""
|
|
29
|
+
ver = platform.python_version()
|
|
30
|
+
ok = sys.version_info >= (3, 11)
|
|
31
|
+
return {
|
|
32
|
+
"name": "Python",
|
|
33
|
+
"version": ver,
|
|
34
|
+
"ok": ok,
|
|
35
|
+
"detail": f"Python {ver}" + ("" if ok else " (3.11+ required)"),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _check_package(pkg_name: str, import_name: str | None = None) -> dict[str, Any]:
|
|
40
|
+
"""Check if a Python package is importable."""
|
|
41
|
+
mod_name = import_name or pkg_name
|
|
42
|
+
try:
|
|
43
|
+
importlib.import_module(mod_name)
|
|
44
|
+
# Prefer importlib.metadata to avoid deprecated __version__ attributes
|
|
45
|
+
try:
|
|
46
|
+
from importlib.metadata import version as meta_version
|
|
47
|
+
ver = meta_version(pkg_name)
|
|
48
|
+
except Exception:
|
|
49
|
+
ver = "installed"
|
|
50
|
+
return {"name": pkg_name, "version": str(ver), "ok": True, "detail": f"{pkg_name} {ver}"}
|
|
51
|
+
except ImportError:
|
|
52
|
+
return {"name": pkg_name, "version": None, "ok": False, "detail": f"{pkg_name} not installed"}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _check_project(path: Path) -> dict[str, Any]:
|
|
56
|
+
"""Check if a CodexA project is initialized at the given path."""
|
|
57
|
+
config_dir = path / ".codexa"
|
|
58
|
+
if config_dir.is_dir():
|
|
59
|
+
index_dir = config_dir / "index"
|
|
60
|
+
has_index = index_dir.is_dir() and any(index_dir.iterdir()) if index_dir.is_dir() else False
|
|
61
|
+
return {
|
|
62
|
+
"name": "Project",
|
|
63
|
+
"version": None,
|
|
64
|
+
"ok": True,
|
|
65
|
+
"detail": f"Initialized at {path}" + (" (indexed)" if has_index else " (not indexed)"),
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
"name": "Project",
|
|
69
|
+
"version": None,
|
|
70
|
+
"ok": False,
|
|
71
|
+
"detail": f"Not initialized at {path} — run 'codexa init'",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def run_checks(path: Path) -> list[dict[str, Any]]:
|
|
76
|
+
"""Run all health checks and return results."""
|
|
77
|
+
checks = [
|
|
78
|
+
_check_python(),
|
|
79
|
+
{"name": "CodexA", "version": __version__, "ok": True, "detail": f"CodexA {__version__}"},
|
|
80
|
+
_check_package("click"),
|
|
81
|
+
_check_package("pydantic"),
|
|
82
|
+
_check_package("rich"),
|
|
83
|
+
_check_package("sentence_transformers", "sentence_transformers"),
|
|
84
|
+
_check_package("faiss", "faiss"),
|
|
85
|
+
_check_package("tree_sitter", "tree_sitter"),
|
|
86
|
+
_check_project(path),
|
|
87
|
+
]
|
|
88
|
+
return checks
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@click.command("doctor")
|
|
92
|
+
@click.option(
|
|
93
|
+
"--path",
|
|
94
|
+
"-p",
|
|
95
|
+
default=".",
|
|
96
|
+
type=click.Path(exists=True, file_okay=False, resolve_path=True),
|
|
97
|
+
help="Project root path.",
|
|
98
|
+
)
|
|
99
|
+
@click.option(
|
|
100
|
+
"--json-output",
|
|
101
|
+
"--json",
|
|
102
|
+
"json_mode",
|
|
103
|
+
is_flag=True,
|
|
104
|
+
default=False,
|
|
105
|
+
help="Output results in JSON format.",
|
|
106
|
+
)
|
|
107
|
+
def doctor_cmd(path: str, json_mode: bool) -> None:
|
|
108
|
+
"""Check environment health, dependencies, and project status.
|
|
109
|
+
|
|
110
|
+
Useful for debugging installation issues or verifying that all
|
|
111
|
+
required packages are available.
|
|
112
|
+
|
|
113
|
+
Examples:
|
|
114
|
+
|
|
115
|
+
codexa doctor
|
|
116
|
+
|
|
117
|
+
codexa doctor --json
|
|
118
|
+
|
|
119
|
+
codexa doctor -p /path/to/project
|
|
120
|
+
"""
|
|
121
|
+
checks = run_checks(Path(path))
|
|
122
|
+
|
|
123
|
+
if json_mode:
|
|
124
|
+
click.echo(json_mod.dumps({"checks": checks}, indent=2))
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
from rich.table import Table
|
|
128
|
+
|
|
129
|
+
table = Table(title="CodexA Health Check", show_lines=False)
|
|
130
|
+
table.add_column("Component", style="bold")
|
|
131
|
+
table.add_column("Status")
|
|
132
|
+
table.add_column("Detail")
|
|
133
|
+
|
|
134
|
+
all_ok = True
|
|
135
|
+
for c in checks:
|
|
136
|
+
status = "[green]OK[/green]" if c["ok"] else "[red]FAIL[/red]"
|
|
137
|
+
if not c["ok"]:
|
|
138
|
+
all_ok = False
|
|
139
|
+
table.add_row(c["name"], status, c["detail"])
|
|
140
|
+
|
|
141
|
+
console.print(table)
|
|
142
|
+
console.print()
|
|
143
|
+
|
|
144
|
+
if all_ok:
|
|
145
|
+
print_success("All checks passed.")
|
|
146
|
+
else:
|
|
147
|
+
print_warning("Some checks failed. See details above.")
|