codemap-python 0.1.4__py3-none-any.whl → 0.1.5__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.
- analysis/call_graph/call_extractor.py +8 -5
- analysis/core/import_extractor.py +40 -36
- analysis/explain/explain_runner.py +42 -34
- analysis/explain/summary_generator.py +8 -5
- analysis/runners/phase4_runner.py +35 -33
- analysis/utils/cache_manager.py +26 -21
- analysis/utils/repo_walk.py +27 -0
- cli.py → codemap_app.py +202 -138
- codemap_cli.py +1 -1
- {codemap_python-0.1.4.dist-info → codemap_python-0.1.5.dist-info}/METADATA +1 -1
- {codemap_python-0.1.4.dist-info → codemap_python-0.1.5.dist-info}/RECORD +16 -15
- {codemap_python-0.1.4.dist-info → codemap_python-0.1.5.dist-info}/top_level.txt +1 -1
- ui/app.py +5 -5
- ui/static/app.js +6 -6
- {codemap_python-0.1.4.dist-info → codemap_python-0.1.5.dist-info}/WHEEL +0 -0
- {codemap_python-0.1.4.dist-info → codemap_python-0.1.5.dist-info}/entry_points.txt +0 -0
|
@@ -85,8 +85,11 @@ class FunctionCallVisitor(ast.NodeVisitor):
|
|
|
85
85
|
def extract_function_calls(file_path):
|
|
86
86
|
source = read_source_file(file_path)
|
|
87
87
|
tree = parse_source_to_ast(source, file_path=file_path)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
return extract_function_calls_from_tree(tree, file_path)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def extract_function_calls_from_tree(tree, file_path):
|
|
92
|
+
visitor = FunctionCallVisitor(file_path)
|
|
93
|
+
visitor.visit(tree)
|
|
94
|
+
|
|
95
|
+
return visitor.calls
|
|
@@ -3,43 +3,47 @@
|
|
|
3
3
|
|
|
4
4
|
import ast
|
|
5
5
|
from analysis.utils.bom_handler import read_source_file, parse_source_to_ast
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def extract_imports_from_tree(tree, file_path):
|
|
9
|
+
"""Extract imports from an already-parsed AST tree."""
|
|
10
|
+
imports = []
|
|
11
|
+
|
|
12
|
+
for node in ast.walk(tree):
|
|
13
|
+
|
|
14
|
+
# import module
|
|
15
|
+
if isinstance(node, ast.Import):
|
|
16
|
+
for alias in node.names:
|
|
17
|
+
imports.append({
|
|
18
|
+
"type": "import",
|
|
19
|
+
"module": alias.name,
|
|
20
|
+
"name": None,
|
|
21
|
+
"alias": alias.asname,
|
|
22
|
+
"line": node.lineno,
|
|
23
|
+
"file": file_path
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
# from module import name
|
|
27
|
+
elif isinstance(node, ast.ImportFrom):
|
|
28
|
+
module = node.module
|
|
29
|
+
level = node.level # 0 = absolute, >0 = relative
|
|
30
|
+
|
|
31
|
+
for alias in node.names:
|
|
32
|
+
imports.append({
|
|
33
|
+
"type": "from_import",
|
|
34
|
+
"module": module,
|
|
35
|
+
"name": alias.name,
|
|
36
|
+
"alias": alias.asname,
|
|
37
|
+
"level": level,
|
|
38
|
+
"line": node.lineno,
|
|
39
|
+
"file": file_path
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return imports
|
|
43
|
+
|
|
44
|
+
|
|
8
45
|
def extract_imports(file_path):
|
|
9
46
|
"""Extract imports from a Python file with automatic encoding and BOM handling."""
|
|
10
47
|
source = read_source_file(file_path)
|
|
11
48
|
tree = parse_source_to_ast(source, file_path=file_path)
|
|
12
|
-
|
|
13
|
-
imports = []
|
|
14
|
-
|
|
15
|
-
for node in ast.walk(tree):
|
|
16
|
-
|
|
17
|
-
# import module
|
|
18
|
-
if isinstance(node, ast.Import):
|
|
19
|
-
for alias in node.names:
|
|
20
|
-
imports.append({
|
|
21
|
-
"type": "import",
|
|
22
|
-
"module": alias.name,
|
|
23
|
-
"name": None,
|
|
24
|
-
"alias": alias.asname,
|
|
25
|
-
"line": node.lineno,
|
|
26
|
-
"file": file_path
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
# from module import name
|
|
30
|
-
elif isinstance(node, ast.ImportFrom):
|
|
31
|
-
module = node.module
|
|
32
|
-
level = node.level # 0 = absolute, >0 = relative
|
|
33
|
-
|
|
34
|
-
for alias in node.names:
|
|
35
|
-
imports.append({
|
|
36
|
-
"type": "from_import",
|
|
37
|
-
"module": module,
|
|
38
|
-
"name": alias.name,
|
|
39
|
-
"alias": alias.asname,
|
|
40
|
-
"level": level,
|
|
41
|
-
"line": node.lineno,
|
|
42
|
-
"file": file_path
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
return imports
|
|
49
|
+
return extract_imports_from_tree(tree, file_path)
|
|
@@ -7,22 +7,24 @@ from typing import Optional, Dict, Any
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
-
|
|
11
|
-
from analysis.indexing.symbol_index import SymbolIndex, SymbolInfo
|
|
12
|
-
from analysis.graph.callgraph_index import CallGraphIndex, CallSite
|
|
13
|
-
from analysis.explain.docstring_extractor import extract_docstrings
|
|
14
|
-
from analysis.explain.signature_extractor import extract_signatures
|
|
15
|
-
from analysis.explain.return_analyzer import analyze_returns
|
|
16
|
-
from analysis.explain.summary_generator import generate_symbol_summary
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
|
|
11
|
+
from analysis.indexing.symbol_index import SymbolIndex, SymbolInfo
|
|
12
|
+
from analysis.graph.callgraph_index import CallGraphIndex, CallSite
|
|
13
|
+
from analysis.explain.docstring_extractor import extract_docstrings
|
|
14
|
+
from analysis.explain.signature_extractor import extract_signatures
|
|
15
|
+
from analysis.explain.return_analyzer import analyze_returns
|
|
16
|
+
from analysis.explain.summary_generator import generate_symbol_summary
|
|
17
|
+
from analysis.utils.repo_walk import filter_skipped_dirs
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def collect_python_files(root_dir: str):
|
|
21
|
+
py_files = []
|
|
22
|
+
for root, dirs, files in os.walk(root_dir):
|
|
23
|
+
dirs[:] = filter_skipped_dirs(dirs)
|
|
24
|
+
for file in files:
|
|
25
|
+
if file.endswith(".py") and not file.startswith("__"):
|
|
26
|
+
py_files.append(os.path.join(root, file))
|
|
27
|
+
return py_files
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
def parse_ast(file_path: str):
|
|
@@ -78,7 +80,11 @@ def merge_maps(dst: dict, src: dict):
|
|
|
78
80
|
dst[k].update(src.get(k, {}))
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
def run(
|
|
83
|
+
def run(
|
|
84
|
+
repo_dir: Optional[str] = None,
|
|
85
|
+
output_dir: Optional[str] = None,
|
|
86
|
+
symbol_snapshot: Optional[list] = None,
|
|
87
|
+
) -> Dict[str, Any]:
|
|
82
88
|
"""
|
|
83
89
|
Callable explain pipeline (Phase-5/6), suitable for CLI/VS Code.
|
|
84
90
|
|
|
@@ -114,23 +120,25 @@ def run(repo_dir: Optional[str] = None, output_dir: Optional[str] = None) -> Dic
|
|
|
114
120
|
# 2) Collect repo python files
|
|
115
121
|
python_files = collect_python_files(repo_dir)
|
|
116
122
|
|
|
117
|
-
# 3) Build symbol index + extractors across repo
|
|
118
|
-
symbol_index = SymbolIndex()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
# 3) Build symbol index + extractors across repo
|
|
124
|
+
symbol_index = SymbolIndex()
|
|
125
|
+
loaded_snapshot = False
|
|
126
|
+
if isinstance(symbol_snapshot, list) and symbol_snapshot:
|
|
127
|
+
symbol_index.load_snapshot(symbol_snapshot)
|
|
128
|
+
loaded_snapshot = True
|
|
129
|
+
|
|
130
|
+
repo_docstrings = {"module": None, "classes": {}, "functions": {}, "methods": {}}
|
|
131
|
+
repo_signatures = {"functions": {}, "methods": {}}
|
|
132
|
+
repo_returns = {"functions": {}, "methods": {}}
|
|
133
|
+
|
|
134
|
+
for file_path in python_files:
|
|
135
|
+
tree = parse_ast(file_path)
|
|
136
|
+
if not loaded_snapshot:
|
|
137
|
+
module_path = file_to_module(file_path, repo_dir)
|
|
138
|
+
symbol_index.index_file(tree, module_path, file_path)
|
|
139
|
+
|
|
140
|
+
# extract per-file and merge
|
|
141
|
+
merge_maps(repo_docstrings, extract_docstrings(tree))
|
|
134
142
|
|
|
135
143
|
sigs = extract_signatures(tree)
|
|
136
144
|
repo_signatures["functions"].update(sigs.get("functions", {}))
|
|
@@ -11,11 +11,14 @@ from analysis.indexing.symbol_index import SymbolInfo
|
|
|
11
11
|
from analysis.graph.callgraph_index import CallGraphIndex
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def _first_line(text: Optional[str]) -> Optional[str]:
|
|
15
|
-
if not text:
|
|
16
|
-
return None
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
def _first_line(text: Optional[str]) -> Optional[str]:
|
|
15
|
+
if not text:
|
|
16
|
+
return None
|
|
17
|
+
stripped = text.strip()
|
|
18
|
+
if not stripped:
|
|
19
|
+
return None
|
|
20
|
+
line = stripped.splitlines()[0].strip()
|
|
21
|
+
return line or None
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
def _humanize_name(name: str) -> str:
|
|
@@ -5,25 +5,25 @@ from typing import Optional, Dict, Any, List
|
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
7
|
import json
|
|
8
|
-
from analysis.indexing.symbol_index import SymbolIndex
|
|
9
|
-
from analysis.indexing.import_resolver import ImportResolver
|
|
10
|
-
from analysis.call_graph.cross_file_resolver import CrossFileResolver
|
|
11
|
-
from analysis.call_graph.call_extractor import
|
|
12
|
-
from analysis.core.import_extractor import
|
|
13
|
-
from analysis.graph.callgraph_index import build_caller_fqn
|
|
8
|
+
from analysis.indexing.symbol_index import SymbolIndex
|
|
9
|
+
from analysis.indexing.import_resolver import ImportResolver
|
|
10
|
+
from analysis.call_graph.cross_file_resolver import CrossFileResolver
|
|
11
|
+
from analysis.call_graph.call_extractor import extract_function_calls_from_tree
|
|
12
|
+
from analysis.core.import_extractor import extract_imports_from_tree
|
|
13
|
+
from analysis.graph.callgraph_index import build_caller_fqn
|
|
14
|
+
from analysis.utils.repo_walk import filter_skipped_dirs
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def collect_python_files(root_dir: str) -> List[str]:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
py_files.append(os.path.join(root, file))
|
|
20
|
+
def collect_python_files(root_dir: str) -> List[str]:
|
|
21
|
+
py_files: List[str] = []
|
|
22
|
+
for root, dirs, files in os.walk(root_dir):
|
|
23
|
+
dirs[:] = filter_skipped_dirs(dirs)
|
|
24
|
+
for file in files:
|
|
25
|
+
if file.endswith(".py") and not file.startswith("__"):
|
|
26
|
+
py_files.append(os.path.join(root, file))
|
|
27
27
|
return py_files
|
|
28
28
|
|
|
29
29
|
|
|
@@ -75,25 +75,27 @@ def run(repo_dir: Optional[str] = None, output_dir: Optional[str] = None, force_
|
|
|
75
75
|
|
|
76
76
|
os.makedirs(output_dir, exist_ok=True)
|
|
77
77
|
|
|
78
|
-
python_files = collect_python_files(repo_dir)
|
|
79
|
-
symbol_index = SymbolIndex()
|
|
80
|
-
file_module_map: Dict[str, str] = {}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
78
|
+
python_files = collect_python_files(repo_dir)
|
|
79
|
+
symbol_index = SymbolIndex()
|
|
80
|
+
file_module_map: Dict[str, str] = {}
|
|
81
|
+
parsed_trees: Dict[str, Any] = {}
|
|
82
|
+
|
|
83
|
+
for file_path in python_files:
|
|
84
|
+
module_path = file_to_module(file_path, repo_dir)
|
|
85
|
+
file_module_map[file_path] = module_path
|
|
86
|
+
tree = parse_ast(file_path)
|
|
87
|
+
parsed_trees[file_path] = tree
|
|
88
|
+
symbol_index.index_file(tree, module_path, file_path)
|
|
89
|
+
|
|
90
|
+
import_resolver = ImportResolver(symbol_index)
|
|
91
|
+
for file_path in python_files:
|
|
92
|
+
module_path = file_module_map[file_path]
|
|
93
|
+
imports = extract_imports_from_tree(parsed_trees[file_path], file_path)
|
|
94
|
+
import_resolver.index_module_imports(module_path, imports)
|
|
95
|
+
|
|
96
|
+
all_calls = []
|
|
97
|
+
for file_path in python_files:
|
|
98
|
+
all_calls.extend(extract_function_calls_from_tree(parsed_trees[file_path], file_path))
|
|
97
99
|
|
|
98
100
|
cross_resolver = CrossFileResolver(symbol_index, import_resolver)
|
|
99
101
|
resolved_calls = []
|
analysis/utils/cache_manager.py
CHANGED
|
@@ -5,15 +5,15 @@ import json
|
|
|
5
5
|
import os
|
|
6
6
|
import shutil
|
|
7
7
|
import tempfile
|
|
8
|
-
from datetime import datetime, timezone
|
|
9
|
-
from threading import RLock
|
|
10
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
11
|
-
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from threading import RLock
|
|
10
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
11
|
+
|
|
12
|
+
from analysis.utils.repo_walk import filter_skipped_dirs
|
|
13
|
+
from security_utils import redact_secrets
|
|
14
|
+
|
|
15
|
+
_LOCK = RLock()
|
|
16
|
+
_SENSITIVE_KEYS = ("api_key", "token", "authorization", "bearer", "basic", "secret", "password")
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _project_root() -> str:
|
|
@@ -195,14 +195,14 @@ def save_policy(policy: Dict[str, Any], base_dir: Optional[str] = None) -> Dict[
|
|
|
195
195
|
|
|
196
196
|
def collect_fingerprints(repo_dir: str) -> Dict[str, Dict[str, int]]:
|
|
197
197
|
repo_root = os.path.abspath(repo_dir)
|
|
198
|
-
out: Dict[str, Dict[str, int]] = {}
|
|
199
|
-
if not os.path.isdir(repo_root):
|
|
200
|
-
return out
|
|
201
|
-
for root, dirs, files in os.walk(repo_root):
|
|
202
|
-
dirs[:] =
|
|
203
|
-
for name in files:
|
|
204
|
-
if not name.endswith(".py"):
|
|
205
|
-
continue
|
|
198
|
+
out: Dict[str, Dict[str, int]] = {}
|
|
199
|
+
if not os.path.isdir(repo_root):
|
|
200
|
+
return out
|
|
201
|
+
for root, dirs, files in os.walk(repo_root):
|
|
202
|
+
dirs[:] = filter_skipped_dirs(dirs)
|
|
203
|
+
for name in files:
|
|
204
|
+
if not name.endswith(".py"):
|
|
205
|
+
continue
|
|
206
206
|
fp = os.path.join(root, name)
|
|
207
207
|
try:
|
|
208
208
|
st = os.stat(fp)
|
|
@@ -250,16 +250,21 @@ def save_manifest(repo_dir: str, manifest: Dict[str, Any], base_dir: Optional[st
|
|
|
250
250
|
_atomic_json_write(_manifest_path(repo_dir, base_dir), _scrub_payload(payload))
|
|
251
251
|
|
|
252
252
|
|
|
253
|
-
def should_rebuild(
|
|
253
|
+
def should_rebuild(
|
|
254
|
+
repo_dir: str,
|
|
255
|
+
analysis_version: str = "2.2",
|
|
256
|
+
base_dir: Optional[str] = None,
|
|
257
|
+
current_fingerprints: Optional[Dict[str, Any]] = None,
|
|
258
|
+
) -> bool:
|
|
254
259
|
manifest = load_manifest(repo_dir, base_dir=base_dir)
|
|
255
260
|
if not manifest:
|
|
256
261
|
return True
|
|
257
262
|
if str(manifest.get("analysis_version", "") or "") != str(analysis_version or ""):
|
|
258
263
|
return True
|
|
259
264
|
previous = manifest.get("fingerprints", {}) if isinstance(manifest.get("fingerprints"), dict) else {}
|
|
260
|
-
current = collect_fingerprints(repo_dir)
|
|
261
|
-
delta = diff_fingerprints(previous, current)
|
|
262
|
-
return bool(delta.get("changed_count", 0))
|
|
265
|
+
current = current_fingerprints if isinstance(current_fingerprints, dict) else collect_fingerprints(repo_dir)
|
|
266
|
+
delta = diff_fingerprints(previous, current)
|
|
267
|
+
return bool(delta.get("changed_count", 0))
|
|
263
268
|
|
|
264
269
|
|
|
265
270
|
def _default_metadata(repo_hash: str) -> Dict[str, Any]:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Shared repository walking rules for source analysis."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
SKIP_DIR_NAMES = {
|
|
9
|
+
".git",
|
|
10
|
+
"__pycache__",
|
|
11
|
+
".codemap_cache",
|
|
12
|
+
".venv",
|
|
13
|
+
"venv",
|
|
14
|
+
"env",
|
|
15
|
+
"ENV",
|
|
16
|
+
".env",
|
|
17
|
+
"node_modules",
|
|
18
|
+
"site-packages",
|
|
19
|
+
"dist-packages",
|
|
20
|
+
".tox",
|
|
21
|
+
".nox",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def filter_skipped_dirs(dir_names: Iterable[str]) -> list[str]:
|
|
26
|
+
"""Return directory names that should still be traversed."""
|
|
27
|
+
return [name for name in dir_names if name not in SKIP_DIR_NAMES]
|
cli.py → codemap_app.py
RENAMED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
import shutil
|
|
5
|
-
import sys
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import webbrowser
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from typing import Dict, Any, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from analysis.utils.repo_walk import filter_skipped_dirs
|
|
12
|
+
from security_utils import redact_payload, redact_secrets
|
|
11
13
|
|
|
12
14
|
def print_json(obj) -> None:
|
|
13
15
|
safe_obj = redact_payload(obj)
|
|
14
16
|
sys.stdout.write(json.dumps(safe_obj, indent=2))
|
|
15
17
|
sys.stdout.write("\n")
|
|
16
18
|
|
|
17
|
-
MISSING_ANALYSIS_MESSAGE = "Run: python
|
|
19
|
+
MISSING_ANALYSIS_MESSAGE = "Run: python codemap_app.py api analyze --path <repo>"
|
|
18
20
|
ANALYSIS_VERSION = "2.2"
|
|
19
21
|
|
|
20
22
|
|
|
@@ -35,8 +37,8 @@ def _print_public_help() -> int:
|
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
|
|
38
|
-
def _analysis_root() -> str:
|
|
39
|
-
#
|
|
40
|
+
def _analysis_root() -> str:
|
|
41
|
+
# codemap_app.py is at project root; analysis/ is sibling
|
|
40
42
|
return os.path.join(os.path.dirname(__file__), "analysis")
|
|
41
43
|
|
|
42
44
|
|
|
@@ -267,16 +269,16 @@ def api_cache_help(_args) -> int:
|
|
|
267
269
|
print_json({
|
|
268
270
|
"ok": True,
|
|
269
271
|
"commands": [
|
|
270
|
-
"python
|
|
271
|
-
"python
|
|
272
|
-
"python
|
|
273
|
-
"python
|
|
274
|
-
"python
|
|
275
|
-
"python
|
|
276
|
-
"python
|
|
277
|
-
"python
|
|
278
|
-
"python
|
|
279
|
-
"python
|
|
272
|
+
"python codemap_app.py api cache list",
|
|
273
|
+
"python codemap_app.py api cache info --path <repo>",
|
|
274
|
+
"python codemap_app.py api cache info --github <url> --ref <ref> --mode <git|zip>",
|
|
275
|
+
"python codemap_app.py api cache clear --path <repo> [--dry-run] [--yes]",
|
|
276
|
+
"python codemap_app.py api cache clear --repo-hash <hash> [--dry-run] [--yes]",
|
|
277
|
+
"python codemap_app.py api cache clear --all [--dry-run] --yes",
|
|
278
|
+
"python codemap_app.py api cache retention --path <repo> --days 7 --yes",
|
|
279
|
+
"python codemap_app.py api cache retention --repo-hash <hash> --days 30 --yes",
|
|
280
|
+
"python codemap_app.py api cache sweep --dry-run",
|
|
281
|
+
"python codemap_app.py api cache sweep --yes",
|
|
280
282
|
],
|
|
281
283
|
})
|
|
282
284
|
return 0
|
|
@@ -354,7 +356,7 @@ def api_cache_info(args) -> int:
|
|
|
354
356
|
|
|
355
357
|
target = _resolve_cache_target(args)
|
|
356
358
|
if not target.get("ok"):
|
|
357
|
-
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python
|
|
359
|
+
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python codemap_app.py api cache help"})
|
|
358
360
|
return 1
|
|
359
361
|
|
|
360
362
|
cache_item = next((c for c in list_caches() if str(c.get("repo_hash")) == str(target["repo_hash"])), None)
|
|
@@ -363,7 +365,7 @@ def api_cache_info(args) -> int:
|
|
|
363
365
|
"ok": False,
|
|
364
366
|
"error": "CACHE_NOT_FOUND",
|
|
365
367
|
"message": f"Cache directory not found: {target['cache_dir']}",
|
|
366
|
-
"hint": "Run: python
|
|
368
|
+
"hint": "Run: python codemap_app.py api analyze --path <repo>",
|
|
367
369
|
})
|
|
368
370
|
return 1
|
|
369
371
|
|
|
@@ -385,9 +387,9 @@ def api_cache_info(args) -> int:
|
|
|
385
387
|
}
|
|
386
388
|
notes = []
|
|
387
389
|
if files["explain_path"] is None:
|
|
388
|
-
notes.append("missing explain.json; run: python
|
|
390
|
+
notes.append("missing explain.json; run: python codemap_app.py api analyze --path <repo>")
|
|
389
391
|
if files["resolved_calls_path"] is None:
|
|
390
|
-
notes.append("missing resolved_calls.json; run: python
|
|
392
|
+
notes.append("missing resolved_calls.json; run: python codemap_app.py api analyze --path <repo>")
|
|
391
393
|
|
|
392
394
|
print_json({
|
|
393
395
|
"ok": True,
|
|
@@ -446,7 +448,7 @@ def api_cache_clear(args) -> int:
|
|
|
446
448
|
if not repo_hash_arg:
|
|
447
449
|
target = _resolve_cache_target(args)
|
|
448
450
|
if not target.get("ok"):
|
|
449
|
-
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python
|
|
451
|
+
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python codemap_app.py api cache help"})
|
|
450
452
|
return 1
|
|
451
453
|
repo_hash_arg = str(target["repo_hash"])
|
|
452
454
|
|
|
@@ -476,7 +478,7 @@ def api_cache_retention(args) -> int:
|
|
|
476
478
|
if not repo_hash_arg:
|
|
477
479
|
target = _resolve_cache_target(args)
|
|
478
480
|
if not target.get("ok"):
|
|
479
|
-
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python
|
|
481
|
+
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python codemap_app.py api cache help"})
|
|
480
482
|
return 1
|
|
481
483
|
repo_hash_arg = str(target["repo_hash"])
|
|
482
484
|
|
|
@@ -508,7 +510,7 @@ def api_cache_delete(args) -> int:
|
|
|
508
510
|
|
|
509
511
|
target = _resolve_cache_target(args)
|
|
510
512
|
if not target.get("ok"):
|
|
511
|
-
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python
|
|
513
|
+
print_json({"ok": False, "error": target.get("error"), "message": target.get("message"), "hint": "Use: python codemap_app.py api cache help"})
|
|
512
514
|
return 1
|
|
513
515
|
|
|
514
516
|
dry_run = bool(getattr(args, "dry_run", False))
|
|
@@ -671,20 +673,19 @@ def api_cache_sweep(args) -> int:
|
|
|
671
673
|
return api_cache_cleanup(args)
|
|
672
674
|
|
|
673
675
|
|
|
674
|
-
def _build_project_tree_snapshot(repo_dir: str) -> Dict[str, Any]:
|
|
675
|
-
repo_dir = os.path.abspath(repo_dir)
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
"
|
|
679
|
-
"
|
|
680
|
-
"
|
|
681
|
-
"children": [],
|
|
676
|
+
def _build_project_tree_snapshot(repo_dir: str) -> Dict[str, Any]:
|
|
677
|
+
repo_dir = os.path.abspath(repo_dir)
|
|
678
|
+
root = {
|
|
679
|
+
"name": os.path.basename(repo_dir.rstrip("\\/")) or repo_dir,
|
|
680
|
+
"type": "directory",
|
|
681
|
+
"path": "",
|
|
682
|
+
"children": [],
|
|
682
683
|
}
|
|
683
684
|
nodes: Dict[str, Dict[str, Any]] = {"": root}
|
|
684
|
-
|
|
685
|
-
for current_root, dirs, files in os.walk(repo_dir):
|
|
686
|
-
dirs[:] = sorted([d for d in dirs if
|
|
687
|
-
files = sorted([f for f in files if not f.startswith(".")])
|
|
685
|
+
|
|
686
|
+
for current_root, dirs, files in os.walk(repo_dir):
|
|
687
|
+
dirs[:] = sorted([d for d in filter_skipped_dirs(dirs) if not d.startswith(".")])
|
|
688
|
+
files = sorted([f for f in files if not f.startswith(".")])
|
|
688
689
|
|
|
689
690
|
rel_root = os.path.relpath(current_root, repo_dir)
|
|
690
691
|
rel_root = "" if rel_root == "." else rel_root.replace("\\", "/")
|
|
@@ -736,7 +737,7 @@ def load_explain_db(repo: Optional[str] = None) -> Dict[str, Any]:
|
|
|
736
737
|
path = paths["explain_path"]
|
|
737
738
|
if not os.path.exists(path):
|
|
738
739
|
hint = (
|
|
739
|
-
"Run:\n python
|
|
740
|
+
"Run:\n python codemap_app.py api analyze --path <repo>\n"
|
|
740
741
|
"to build repo-scoped cache before querying with --repo."
|
|
741
742
|
if repo else
|
|
742
743
|
"Run:\n python -m analysis.explain.explain_runner\n"
|
|
@@ -802,7 +803,7 @@ def cmd_explain(args) -> int:
|
|
|
802
803
|
for s in suggestions:
|
|
803
804
|
print(f" - {s}")
|
|
804
805
|
else:
|
|
805
|
-
print("No similar symbols found. Try: python
|
|
806
|
+
print("No similar symbols found. Try: python codemap_app.py search <keyword>")
|
|
806
807
|
print()
|
|
807
808
|
return 1
|
|
808
809
|
|
|
@@ -996,7 +997,7 @@ def api_repo_summary(args) -> int:
|
|
|
996
997
|
"repo_hash": compute_repo_hash(repo_dir),
|
|
997
998
|
"cached": False,
|
|
998
999
|
"summary": {},
|
|
999
|
-
"error": "Missing architecture cache. Run: python
|
|
1000
|
+
"error": "Missing architecture cache. Run: python codemap_app.py api analyze --path <repo>",
|
|
1000
1001
|
})
|
|
1001
1002
|
return 1
|
|
1002
1003
|
|
|
@@ -1269,40 +1270,77 @@ def api_analyze(args) -> int:
|
|
|
1269
1270
|
llm_cache_path = os.path.join(cache_dir, "llm_cache.json")
|
|
1270
1271
|
project_tree_path = os.path.join(cache_dir, "project_tree.json")
|
|
1271
1272
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
spinner = ProgressSpinner("
|
|
1288
|
-
spinner.start()
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1273
|
+
overall_started = time.perf_counter()
|
|
1274
|
+
stage_timings: Dict[str, float] = {}
|
|
1275
|
+
current_stage = "initializing"
|
|
1276
|
+
stage_started = overall_started
|
|
1277
|
+
|
|
1278
|
+
def _start_stage(stage_name: str, spinner_message: Optional[str] = None) -> None:
|
|
1279
|
+
nonlocal current_stage, stage_started
|
|
1280
|
+
current_stage = stage_name
|
|
1281
|
+
stage_started = time.perf_counter()
|
|
1282
|
+
if spinner_message:
|
|
1283
|
+
spinner.update(spinner_message)
|
|
1284
|
+
|
|
1285
|
+
def _finish_stage(stage_name: str) -> None:
|
|
1286
|
+
stage_timings[stage_name] = round(time.perf_counter() - stage_started, 3)
|
|
1287
|
+
|
|
1288
|
+
spinner = ProgressSpinner("Scanning repository...")
|
|
1289
|
+
spinner.start()
|
|
1290
|
+
|
|
1291
|
+
_start_stage("fingerprint_scan", "Scanning repository...")
|
|
1292
|
+
previous_manifest = load_manifest(repo_dir)
|
|
1293
|
+
previous_fingerprints = previous_manifest.get("fingerprints", {})
|
|
1294
|
+
current_fingerprints = collect_fingerprints(repo_dir)
|
|
1295
|
+
delta = diff_fingerprints(previous_fingerprints, current_fingerprints)
|
|
1296
|
+
version_mismatch = previous_manifest.get("analysis_version") != ANALYSIS_VERSION
|
|
1297
|
+
|
|
1298
|
+
rebuild_required = bool(
|
|
1299
|
+
force_full_rebuild
|
|
1300
|
+
or should_rebuild(
|
|
1301
|
+
repo_dir,
|
|
1302
|
+
analysis_version=ANALYSIS_VERSION,
|
|
1303
|
+
current_fingerprints=current_fingerprints,
|
|
1304
|
+
)
|
|
1305
|
+
)
|
|
1306
|
+
_finish_stage("fingerprint_scan")
|
|
1307
|
+
architecture_missing = not os.path.exists(architecture_metrics_path)
|
|
1308
|
+
dependency_missing = not os.path.exists(dependency_cycles_path)
|
|
1309
|
+
risk_missing = not os.path.exists(risk_radar_path)
|
|
1310
|
+
r1 = {}
|
|
1311
|
+
r2 = {}
|
|
1312
|
+
metrics = {}
|
|
1313
|
+
|
|
1314
|
+
try:
|
|
1315
|
+
if rebuild_required:
|
|
1316
|
+
_start_stage("phase4_analysis", "Building call graph...")
|
|
1317
|
+
r1 = run_phase4(
|
|
1318
|
+
repo_dir=repo_dir,
|
|
1319
|
+
output_dir=cache_dir,
|
|
1320
|
+
force_rebuild=bool(version_mismatch or force_full_rebuild),
|
|
1321
|
+
)
|
|
1322
|
+
_finish_stage("phase4_analysis")
|
|
1323
|
+
|
|
1324
|
+
_start_stage("explain_generation", "Generating symbol explanations...")
|
|
1325
|
+
r2 = run_explain(
|
|
1326
|
+
repo_dir=repo_dir,
|
|
1327
|
+
output_dir=cache_dir,
|
|
1328
|
+
symbol_snapshot=r1.get("symbol_snapshot", []),
|
|
1329
|
+
)
|
|
1330
|
+
_finish_stage("explain_generation")
|
|
1331
|
+
resolved_calls_path = r1.get("resolved_calls_path", resolved_calls_path)
|
|
1332
|
+
|
|
1333
|
+
_start_stage("analysis_metrics", "Computing analysis metrics...")
|
|
1334
|
+
metrics = write_hub_metrics_from_resolved_calls(
|
|
1335
|
+
resolved_calls_path=resolved_calls_path,
|
|
1336
|
+
output_path=analysis_metrics_path,
|
|
1337
|
+
)
|
|
1338
|
+
_finish_stage("analysis_metrics")
|
|
1339
|
+
|
|
1340
|
+
_start_stage("manifest_write", "Saving analysis manifest...")
|
|
1341
|
+
save_manifest(
|
|
1342
|
+
repo_dir,
|
|
1343
|
+
build_manifest(
|
|
1306
1344
|
repo_dir,
|
|
1307
1345
|
current_fingerprints,
|
|
1308
1346
|
metadata={
|
|
@@ -1320,28 +1358,37 @@ def api_analyze(args) -> int:
|
|
|
1320
1358
|
"critical_apis": len(metrics.get("critical_apis", [])),
|
|
1321
1359
|
"orchestrators": len(metrics.get("orchestrators", [])),
|
|
1322
1360
|
},
|
|
1323
|
-
},
|
|
1324
|
-
),
|
|
1325
|
-
)
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1361
|
+
},
|
|
1362
|
+
),
|
|
1363
|
+
)
|
|
1364
|
+
_finish_stage("manifest_write")
|
|
1365
|
+
|
|
1366
|
+
_start_stage("project_tree", "Building project tree...")
|
|
1367
|
+
tree_snapshot = _build_project_tree_snapshot(repo_dir)
|
|
1368
|
+
with open(project_tree_path, "w", encoding="utf-8") as f:
|
|
1369
|
+
json.dump(tree_snapshot, f, indent=2)
|
|
1370
|
+
_finish_stage("project_tree")
|
|
1371
|
+
elif os.path.exists(resolved_calls_path):
|
|
1372
|
+
_start_stage("analysis_metrics", "Computing analysis metrics...")
|
|
1373
|
+
metrics = write_hub_metrics_from_resolved_calls(
|
|
1374
|
+
resolved_calls_path=resolved_calls_path,
|
|
1375
|
+
output_path=analysis_metrics_path,
|
|
1376
|
+
)
|
|
1377
|
+
_finish_stage("analysis_metrics")
|
|
1378
|
+
if not os.path.exists(project_tree_path):
|
|
1379
|
+
_start_stage("project_tree", "Building project tree...")
|
|
1380
|
+
tree_snapshot = _build_project_tree_snapshot(repo_dir)
|
|
1381
|
+
with open(project_tree_path, "w", encoding="utf-8") as f:
|
|
1382
|
+
json.dump(tree_snapshot, f, indent=2)
|
|
1383
|
+
_finish_stage("project_tree")
|
|
1384
|
+
|
|
1385
|
+
# Derived architecture outputs:
|
|
1386
|
+
# - regenerate on rebuild
|
|
1387
|
+
# - regenerate if architecture/dependency artifacts are missing
|
|
1388
|
+
if rebuild_required or architecture_missing or dependency_missing:
|
|
1389
|
+
_start_stage("architecture_metrics", "Computing architecture metrics...")
|
|
1390
|
+
with open(resolved_calls_path, "r", encoding="utf-8") as f:
|
|
1391
|
+
resolved_calls = json.load(f)
|
|
1345
1392
|
|
|
1346
1393
|
callgraph = CallGraphIndex()
|
|
1347
1394
|
for c in resolved_calls:
|
|
@@ -1374,22 +1421,27 @@ def api_analyze(args) -> int:
|
|
|
1374
1421
|
repo_prefix=repo_prefix,
|
|
1375
1422
|
)
|
|
1376
1423
|
|
|
1377
|
-
with open(architecture_metrics_path, "w", encoding="utf-8") as f:
|
|
1378
|
-
json.dump(arch_payload, f, indent=2)
|
|
1379
|
-
with open(dependency_cycles_path, "w", encoding="utf-8") as f:
|
|
1380
|
-
json.dump(dep_payload, f, indent=2)
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
#
|
|
1384
|
-
# - regenerate
|
|
1385
|
-
# - regenerate when
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1424
|
+
with open(architecture_metrics_path, "w", encoding="utf-8") as f:
|
|
1425
|
+
json.dump(arch_payload, f, indent=2)
|
|
1426
|
+
with open(dependency_cycles_path, "w", encoding="utf-8") as f:
|
|
1427
|
+
json.dump(dep_payload, f, indent=2)
|
|
1428
|
+
_finish_stage("architecture_metrics")
|
|
1429
|
+
|
|
1430
|
+
# Risk radar derived output:
|
|
1431
|
+
# - regenerate on rebuild
|
|
1432
|
+
# - regenerate when missing (cached analyze run)
|
|
1433
|
+
# - regenerate when architecture/dependency were regenerated
|
|
1434
|
+
if rebuild_required or risk_missing or architecture_missing or dependency_missing:
|
|
1435
|
+
_start_stage("risk_radar", "Computing risk radar...")
|
|
1436
|
+
risk_payload = compute_risk_radar(cache_dir=cache_dir, top_k=25)
|
|
1437
|
+
with open(risk_radar_path, "w", encoding="utf-8") as f:
|
|
1438
|
+
json.dump(risk_payload, f, indent=2)
|
|
1439
|
+
_finish_stage("risk_radar")
|
|
1440
|
+
|
|
1441
|
+
_start_stage("cache_metadata", "Updating cache metadata...")
|
|
1442
|
+
repo_hash = compute_repo_hash(repo_dir)
|
|
1443
|
+
upsert_metadata(
|
|
1444
|
+
repo_hash=repo_hash,
|
|
1393
1445
|
source=source,
|
|
1394
1446
|
repo_path=os.path.abspath(repo_dir),
|
|
1395
1447
|
repo_url=repo_url or "",
|
|
@@ -1398,20 +1450,31 @@ def api_analyze(args) -> int:
|
|
|
1398
1450
|
analysis_version=ANALYSIS_VERSION,
|
|
1399
1451
|
private_mode=bool(private_repo_mode),
|
|
1400
1452
|
)
|
|
1401
|
-
set_retention(
|
|
1402
|
-
repo_hash=repo_hash,
|
|
1403
|
-
days=int(retention_days_effective),
|
|
1404
|
-
)
|
|
1405
|
-
touch_last_accessed(repo_hash)
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1453
|
+
set_retention(
|
|
1454
|
+
repo_hash=repo_hash,
|
|
1455
|
+
days=int(retention_days_effective),
|
|
1456
|
+
)
|
|
1457
|
+
touch_last_accessed(repo_hash)
|
|
1458
|
+
_finish_stage("cache_metadata")
|
|
1459
|
+
except Exception as e:
|
|
1460
|
+
spinner.stop()
|
|
1461
|
+
if current_stage and current_stage not in stage_timings:
|
|
1462
|
+
stage_timings[current_stage] = round(time.perf_counter() - stage_started, 3)
|
|
1463
|
+
stage_timings["total"] = round(time.perf_counter() - overall_started, 3)
|
|
1464
|
+
print_json({
|
|
1465
|
+
"ok": False,
|
|
1466
|
+
"error": "ANALYZE_FAILED",
|
|
1467
|
+
"message": redact_secrets(str(e)),
|
|
1468
|
+
"failed_stage": current_stage,
|
|
1469
|
+
"timings": stage_timings,
|
|
1470
|
+
})
|
|
1471
|
+
return 1
|
|
1472
|
+
|
|
1473
|
+
spinner.stop()
|
|
1474
|
+
stage_timings["total"] = round(time.perf_counter() - overall_started, 3)
|
|
1475
|
+
print_json({
|
|
1476
|
+
"ok": True,
|
|
1477
|
+
"source": source,
|
|
1415
1478
|
"mode": mode,
|
|
1416
1479
|
"auth": auth,
|
|
1417
1480
|
"private_repo_mode": private_repo_mode,
|
|
@@ -1442,13 +1505,14 @@ def api_analyze(args) -> int:
|
|
|
1442
1505
|
"fetched": fetched,
|
|
1443
1506
|
"downloaded": downloaded,
|
|
1444
1507
|
"zip_url": zip_url,
|
|
1445
|
-
"repo_dir": repo_dir,
|
|
1446
|
-
"retention": {
|
|
1447
|
-
"mode": retention_mode,
|
|
1448
|
-
"ttl_days": int(retention_days_effective),
|
|
1449
|
-
},
|
|
1450
|
-
|
|
1451
|
-
|
|
1508
|
+
"repo_dir": repo_dir,
|
|
1509
|
+
"retention": {
|
|
1510
|
+
"mode": retention_mode,
|
|
1511
|
+
"ttl_days": int(retention_days_effective),
|
|
1512
|
+
},
|
|
1513
|
+
"timings": stage_timings,
|
|
1514
|
+
})
|
|
1515
|
+
return 0
|
|
1452
1516
|
|
|
1453
1517
|
|
|
1454
1518
|
def cmd_ui(args) -> int:
|
codemap_cli.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
codemap_cli.py,sha256=
|
|
1
|
+
codemap_app.py,sha256=CSPT7lVdHVXrutscUVHCNdfSlGtoZKz85oQAdLcs1ps,75678
|
|
2
|
+
codemap_cli.py,sha256=BzQpgP7ZEKvMwjWlrAXR0dr_WcZFIDk3QVwH5GHNflE,182
|
|
3
3
|
security_utils.py,sha256=6wmKx3qeYQmyIhc69ENocdclzKjIskhGhbbYzyrOfSs,1958
|
|
4
4
|
analysis/__init__.py,sha256=gk7XcsmXhT6ewXV3CFezT6R5Q8E2RJ_W1lAPYWmloUQ,20
|
|
5
5
|
analysis/architecture/__init__.py,sha256=8Bo3TpyB49uJs6QpQMTWpUR2hJhqEpbkK_E_GW7tYpU,5
|
|
@@ -7,7 +7,7 @@ analysis/architecture/architecture_engine.py,sha256=8aWqfvdGHP9gIf03VdLrkn1vF670
|
|
|
7
7
|
analysis/architecture/dependency_cycles.py,sha256=fu1kQY1N4jmAdCdh2V2g9e8aZRoC17WtGEy6rPStDbI,3342
|
|
8
8
|
analysis/architecture/risk_radar.py,sha256=3nCxjH96cicb_UQ67T4Hk63cIWWgG5syQhlaoFsoWEA,8681
|
|
9
9
|
analysis/call_graph/__init__.py,sha256=w0foWNOGbFJTxtZUs08tnR3MM5mRTfA0xz6DtGRNGOk,22
|
|
10
|
-
analysis/call_graph/call_extractor.py,sha256=
|
|
10
|
+
analysis/call_graph/call_extractor.py,sha256=EBeXK-Dbj3HNossNFWzEwnzgx-TFfbLTa5JZxrNtOVU,2868
|
|
11
11
|
analysis/call_graph/call_graph_builder.py,sha256=h3D2SzvNmyPtVEBvwGTHM2LVsUwxq9MBLEMhkMi8gGc,29
|
|
12
12
|
analysis/call_graph/call_resolver.py,sha256=gdSAy_042xxXby65FfSIMGBYI3yU4-jT-zMvpajHdF8,1600
|
|
13
13
|
analysis/call_graph/context_models.py,sha256=pYCvdJOEdR35p-BO42BtXoJIFdieF6CEp-f_rSFbd0Q,29
|
|
@@ -20,15 +20,15 @@ analysis/core/ast_context.py,sha256=kQZB3jM4Mnqy8VtnlT5Towdo5Rfg6sr2hF-eVxjQY6Q,
|
|
|
20
20
|
analysis/core/ast_parser.py,sha256=gFNicvvMFP8wR6afFKBdDFUJWJZaAZu4ZX4DlNXgxxM,759
|
|
21
21
|
analysis/core/class_extractor.py,sha256=YJctW3TigWa7gra02qgZJXoVboDDTikkIyPzEDfNiI8,1039
|
|
22
22
|
analysis/core/function_extractor.py,sha256=5y0teAJ8vzGTZ8Te80m9i0ihP8g2jgR2FEcddxIvhwc,436
|
|
23
|
-
analysis/core/import_extractor.py,sha256=
|
|
23
|
+
analysis/core/import_extractor.py,sha256=lxlhzTy4-3t06JUNKYrRl6UAGgjUSgG-fpgVmSqB7AY,1541
|
|
24
24
|
analysis/explain/__init__.py,sha256=2FsKrvLP_OgFFTuoFToaXRs6lM3lmz6lpEqjy5Agko8,55
|
|
25
25
|
analysis/explain/docstring_extractor.py,sha256=9AO1ITf48KMg5ezZm_GYJzTlFi8sOBYih3uXlyectWM,1449
|
|
26
|
-
analysis/explain/explain_runner.py,sha256=
|
|
26
|
+
analysis/explain/explain_runner.py,sha256=dsZLz_KwtW5ATc28EHF9aAIkkTB75kNneMlsPJxGI7M,5975
|
|
27
27
|
analysis/explain/repo_summary_generator.py,sha256=eCNve4-eHG0d2XLJttFA-Bh5suZIvKack_jBRaMHrHc,6474
|
|
28
28
|
analysis/explain/return_analyzer.py,sha256=KjHj7SHWTUMQAzLUi0A8BL5cv5tCxr5XeAw2VG-uyYg,3100
|
|
29
29
|
analysis/explain/risk_flags.py,sha256=QNV101q4PMMIJ_LKv80EwX6uaAysiZC-aGyYTSbwPb8,39
|
|
30
30
|
analysis/explain/signature_extractor.py,sha256=Wx-8tvCArg8B9LtMsvw0O5kd48PsGf0KjiEDF7uzTmc,3430
|
|
31
|
-
analysis/explain/summary_generator.py,sha256=
|
|
31
|
+
analysis/explain/summary_generator.py,sha256=s1mZePpU2KHvNW82a4ZS0xrdbysXhzxp231hDwYlH_o,10004
|
|
32
32
|
analysis/graph/__init__.py,sha256=JcO7GEPE5fMocTUC1ou9AKGCnv3RwpTKt1CrNKQ-puM,52
|
|
33
33
|
analysis/graph/callgraph_index.py,sha256=34LE8Vgv-z6kVOIoCdKJ19ej550ENEYI-78xuhF6QQE,3770
|
|
34
34
|
analysis/graph/entrypoint_detector.py,sha256=4NDD4z-tAIcryTVcHkV7dMLDOQp3J-QKDFPUcIESRsg,40
|
|
@@ -37,24 +37,25 @@ analysis/indexing/__init__.py,sha256=eiE_smnnd-0xjpMBKDQilIypx5oiLA9N_40xBxDQSe4
|
|
|
37
37
|
analysis/indexing/import_resolver.py,sha256=ZkTyx3RdzrEwdHMVax0uM5D4AjJNEo1FoXAD1rsx5QY,4697
|
|
38
38
|
analysis/indexing/symbol_index.py,sha256=RoBsehau9u0tYB8hUEvN5K4dINgtiGH07qV0vznKAhA,5491
|
|
39
39
|
analysis/runners/__init__.py,sha256=4caStudxQFG1nCXBGpoyV7DvxN5ySfwVm4ENQc46w6s,21
|
|
40
|
-
analysis/runners/phase4_runner.py,sha256=
|
|
40
|
+
analysis/runners/phase4_runner.py,sha256=Pta7tF-JU4EWFRE5dgnRKhqTVgq8NbMPM7xwR2MO0WI,5048
|
|
41
41
|
analysis/utils/__init__.py,sha256=sJCGmNWVjSiFK21dDvTzQFMUPyQYNP3b4-Lj42DNA-s,17
|
|
42
42
|
analysis/utils/ast_helpers.py,sha256=qMny-x6kvMt7F1b9Q8HtR2o0nen6bR-MyQsClBzLNpI,20
|
|
43
43
|
analysis/utils/bom_handler.py,sha256=WkDvE1jMGEJgxW-aS5gPfHmFKv7NNDT35j6dbPBFQ3c,4059
|
|
44
|
-
analysis/utils/cache_manager.py,sha256=
|
|
44
|
+
analysis/utils/cache_manager.py,sha256=wOVDm-udThUV0mxEA3CLfL2d8SOPwW_BvvUwIayY_tI,24654
|
|
45
45
|
analysis/utils/path_resolver.py,sha256=NprapbBX8E1c_5jp2R1OLIYMeqrFeNfaxnmIlwqs9hI,27
|
|
46
46
|
analysis/utils/progress_spinner.py,sha256=l_2YeddB3xSp46H8pF3vc3h_XxbCu03lfLBJQndO4sI,2792
|
|
47
47
|
analysis/utils/repo_fetcher.py,sha256=bCUBG0qUypcXXgQPAAdJAaRoQSc75Nd53B7HD72Euno,15105
|
|
48
|
+
analysis/utils/repo_walk.py,sha256=pMVX9pMLtOV-5ln6mRbJ6vVApkjo1vnkJl7YPvXiGNk,546
|
|
48
49
|
ui/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
49
|
-
ui/app.py,sha256=
|
|
50
|
+
ui/app.py,sha256=LJct_PfF10eHRTsgD-9IJcZlVCSXQBqvw6Mdk38gWJA,82085
|
|
50
51
|
ui/device_id.py,sha256=gAan8gMnSY_lDpDSllafK27CSskFT-_OdFw2m8y2QUA,708
|
|
51
|
-
ui/static/app.js,sha256=
|
|
52
|
+
ui/static/app.js,sha256=WzTs9ZywGoCXHrqeuqFzCbzXdtMShAMfXUVwDaqAm6Y,118377
|
|
52
53
|
ui/static/styles.css,sha256=B_QkNK8zziUDl-m8HzcpyO1FFujYf-DdO05Ly7JZbB8,23639
|
|
53
54
|
ui/templates/index.html,sha256=x8isBJyxzt8UdkLJK4NDqBlEjn_FbXdFc1O_llti-R0,11945
|
|
54
55
|
ui/utils/__init__.py,sha256=8Bo3TpyB49uJs6QpQMTWpUR2hJhqEpbkK_E_GW7tYpU,5
|
|
55
56
|
ui/utils/registry_manager.py,sha256=jEhWMQJ3H1ZtyUP2ObneqFOVDTdDqiBvUh-46-rGFDw,6730
|
|
56
|
-
codemap_python-0.1.
|
|
57
|
-
codemap_python-0.1.
|
|
58
|
-
codemap_python-0.1.
|
|
59
|
-
codemap_python-0.1.
|
|
60
|
-
codemap_python-0.1.
|
|
57
|
+
codemap_python-0.1.5.dist-info/METADATA,sha256=KnQdf8myIYhINPfL_UnaFe3aIvt865bTSFIOD8bYknY,11131
|
|
58
|
+
codemap_python-0.1.5.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
59
|
+
codemap_python-0.1.5.dist-info/entry_points.txt,sha256=71TCgwo56CPxSfh-YSmofpNwq4-kSsjmBL_qzJ5kfmk,45
|
|
60
|
+
codemap_python-0.1.5.dist-info/top_level.txt,sha256=ut06MYFLgR7psHfy_wVlrKDzJO4NX0LWIj_dgkaOXSE,51
|
|
61
|
+
codemap_python-0.1.5.dist-info/RECORD,,
|
ui/app.py
CHANGED
|
@@ -61,7 +61,7 @@ ANALYSIS_ROOT = os.path.join(PROJECT_ROOT, "analysis")
|
|
|
61
61
|
DEFAULT_REPO = os.getenv("CODEMAP_UI_REPO", "testing_repo")
|
|
62
62
|
GLOBAL_CACHE_DIR = os.path.join(PROJECT_ROOT, ".codemap_cache")
|
|
63
63
|
|
|
64
|
-
MISSING_CACHE_MESSAGE = "Not analyzed yet. Run: python
|
|
64
|
+
MISSING_CACHE_MESSAGE = "Not analyzed yet. Run: python codemap_app.py api analyze --path <repo>"
|
|
65
65
|
|
|
66
66
|
_SENSITIVE_FIELD_RE = re.compile(r"(?i)(api[_-]?key|token|authorization|bearer|basic|secret|password)")
|
|
67
67
|
|
|
@@ -372,7 +372,7 @@ def _cli_json_with_input(
|
|
|
372
372
|
stdin_text: Optional[str] = None,
|
|
373
373
|
extra_env: Optional[Dict[str, str]] = None,
|
|
374
374
|
) -> Dict[str, Any]:
|
|
375
|
-
cmd = [sys.executable, os.path.join(PROJECT_ROOT, "
|
|
375
|
+
cmd = [sys.executable, os.path.join(PROJECT_ROOT, "codemap_app.py"), "api"] + list(args)
|
|
376
376
|
env = os.environ.copy()
|
|
377
377
|
if isinstance(extra_env, dict):
|
|
378
378
|
for k, v in extra_env.items():
|
|
@@ -426,8 +426,8 @@ def _repo_analyze_command(repo: Dict[str, Any]) -> str:
|
|
|
426
426
|
repo_url = str(repo.get("repo_url", "") or "").strip()
|
|
427
427
|
ref = str(repo.get("ref", "") or "").strip() or "main"
|
|
428
428
|
mode = str(repo.get("mode", "") or "zip")
|
|
429
|
-
return f"python
|
|
430
|
-
return f"python
|
|
429
|
+
return f"python codemap_app.py api analyze --github {repo_url} --ref {ref} --mode {mode}"
|
|
430
|
+
return f"python codemap_app.py api analyze --path {repo.get('path', '<repo>')}"
|
|
431
431
|
|
|
432
432
|
|
|
433
433
|
def _get_active_repo_entry() -> Optional[Dict[str, str]]:
|
|
@@ -1670,7 +1670,7 @@ def api_architecture(repo: Optional[str] = Query(default=None)):
|
|
|
1670
1670
|
content={
|
|
1671
1671
|
"ok": False,
|
|
1672
1672
|
"error": "MISSING_ARCHITECTURE_CACHE",
|
|
1673
|
-
"message": "Run: python
|
|
1673
|
+
"message": "Run: python codemap_app.py api analyze --path <repo>",
|
|
1674
1674
|
"missing_files": missing,
|
|
1675
1675
|
},
|
|
1676
1676
|
)
|
ui/static/app.js
CHANGED
|
@@ -231,13 +231,13 @@
|
|
|
231
231
|
|
|
232
232
|
function analyzeCommandForRepo(repo) {
|
|
233
233
|
const r = repo || currentRepoEntry();
|
|
234
|
-
if (!r) return "python
|
|
234
|
+
if (!r) return "python codemap_app.py api analyze --path <repo>";
|
|
235
235
|
if (String(r.source || "filesystem") === "github" && r.repo_url) {
|
|
236
236
|
const ref = r.ref || "main";
|
|
237
237
|
const mode = r.mode || "zip";
|
|
238
|
-
return `python
|
|
238
|
+
return `python codemap_app.py api analyze --github ${r.repo_url} --ref ${ref} --mode ${mode}`;
|
|
239
239
|
}
|
|
240
|
-
return `python
|
|
240
|
+
return `python codemap_app.py api analyze --path ${r.repo_path || "<repo>"}`;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
function updateAiModeUi() {
|
|
@@ -612,7 +612,7 @@
|
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
function repoSummarySection() {
|
|
615
|
-
const cmd = `python
|
|
615
|
+
const cmd = `python codemap_app.py api repo_summary --repo ${repoName || "<repo>"}`;
|
|
616
616
|
const controls = `
|
|
617
617
|
<div class="repo-row-actions">
|
|
618
618
|
<button id='repo-summary-view' class='repo-refresh-btn' type='button'>View summary</button>
|
|
@@ -730,7 +730,7 @@
|
|
|
730
730
|
}
|
|
731
731
|
|
|
732
732
|
function riskRadarSection() {
|
|
733
|
-
const cmd = `python
|
|
733
|
+
const cmd = `python codemap_app.py api risk_radar --repo ${repoName || "<repo>"}`;
|
|
734
734
|
if (riskRadarStatus === "loading") {
|
|
735
735
|
return `<div class="card"><div class="section-title">Risk Radar</div><div class="path">Loading risk radar...</div></div>`;
|
|
736
736
|
}
|
|
@@ -913,7 +913,7 @@
|
|
|
913
913
|
<div class="card arch-missing">
|
|
914
914
|
<div class="section-title">Architecture Insights Unavailable</div>
|
|
915
915
|
<div>${esc((e && (e.message || e.error)) || "Missing architecture cache artifacts.")}</div>
|
|
916
|
-
<div class="path">Run: python
|
|
916
|
+
<div class="path">Run: python codemap_app.py api analyze --path <repo></div>
|
|
917
917
|
</div>
|
|
918
918
|
`;
|
|
919
919
|
}
|
|
File without changes
|
|
File without changes
|