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.
@@ -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
- visitor = FunctionCallVisitor(file_path)
90
- visitor.visit(tree)
91
-
92
- return visitor.calls
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
- def collect_python_files(root_dir: str):
20
- py_files = []
21
- for root, _, files in os.walk(root_dir):
22
- for file in files:
23
- if file.endswith(".py") and not file.startswith("__"):
24
- py_files.append(os.path.join(root, file))
25
- return py_files
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(repo_dir: Optional[str] = None, output_dir: Optional[str] = None) -> Dict[str, Any]:
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
- repo_docstrings = {"module": None, "classes": {}, "functions": {}, "methods": {}}
121
- repo_signatures = {"functions": {}, "methods": {}}
122
- repo_returns = {"functions": {}, "methods": {}}
123
-
124
- for file_path in python_files:
125
- tree = parse_ast(file_path)
126
- module_path = file_to_module(file_path, repo_dir)
127
-
128
-
129
- # index symbols
130
- symbol_index.index_file(tree, module_path, file_path)
131
-
132
- # extract per-file and merge
133
- merge_maps(repo_docstrings, extract_docstrings(tree))
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
- line = text.strip().splitlines()[0].strip()
18
- return line or None
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 extract_function_calls
12
- from analysis.core.import_extractor import extract_imports
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
- ignore_dirs = {".git", "__pycache__", ".codemap_cache", "node_modules", ".venv", "venv"}
21
- py_files: List[str] = []
22
- for root, dirs, files in os.walk(root_dir):
23
- dirs[:] = [d for d in dirs if d not in ignore_dirs]
24
- for file in files:
25
- if file.endswith(".py") and not file.startswith("__"):
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
- for file_path in python_files:
83
- module_path = file_to_module(file_path, repo_dir)
84
- file_module_map[file_path] = module_path
85
- tree = parse_ast(file_path)
86
- symbol_index.index_file(tree, module_path, file_path)
87
-
88
- import_resolver = ImportResolver(symbol_index)
89
- for file_path in python_files:
90
- module_path = file_module_map[file_path]
91
- imports = extract_imports(file_path)
92
- import_resolver.index_module_imports(module_path, imports)
93
-
94
- all_calls = []
95
- for file_path in python_files:
96
- all_calls.extend(extract_function_calls(file_path))
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 = []
@@ -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 security_utils import redact_secrets
13
-
14
- _LOCK = RLock()
15
- _SENSITIVE_KEYS = ("api_key", "token", "authorization", "bearer", "basic", "secret", "password")
16
- _SKIP_DIRS = {".git", "__pycache__", ".codemap_cache", ".venv", "venv", "node_modules"}
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[:] = [d for d in dirs if d not in _SKIP_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(repo_dir: str, analysis_version: str = "2.2", base_dir: Optional[str] = None) -> bool:
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 webbrowser
7
- from datetime import datetime, timezone
8
- from typing import Dict, Any, List, Optional, Tuple
9
-
10
- from security_utils import redact_payload, redact_secrets
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 cli.py api analyze --path <repo>"
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
- # cli.py is at project root; analysis/ is sibling
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 cli.py api cache list",
271
- "python cli.py api cache info --path <repo>",
272
- "python cli.py api cache info --github <url> --ref <ref> --mode <git|zip>",
273
- "python cli.py api cache clear --path <repo> [--dry-run] [--yes]",
274
- "python cli.py api cache clear --repo-hash <hash> [--dry-run] [--yes]",
275
- "python cli.py api cache clear --all [--dry-run] --yes",
276
- "python cli.py api cache retention --path <repo> --days 7 --yes",
277
- "python cli.py api cache retention --repo-hash <hash> --days 30 --yes",
278
- "python cli.py api cache sweep --dry-run",
279
- "python cli.py api cache sweep --yes",
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 cli.py api cache help"})
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 cli.py api analyze --path <repo>",
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 cli.py api analyze --path <repo>")
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 cli.py api analyze --path <repo>")
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 cli.py api cache help"})
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 cli.py api cache help"})
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 cli.py api cache help"})
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
- ignore_dirs = {".git", ".codemap_cache", "__pycache__", ".venv", "venv", "node_modules"}
677
- root = {
678
- "name": os.path.basename(repo_dir.rstrip("\\/")) or repo_dir,
679
- "type": "directory",
680
- "path": "",
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 d not in ignore_dirs and not d.startswith(".")])
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 cli.py api analyze --path <repo>\n"
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 cli.py search <keyword>")
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 cli.py api analyze --path <repo>",
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
- previous_manifest = load_manifest(repo_dir)
1273
- previous_fingerprints = previous_manifest.get("fingerprints", {})
1274
- current_fingerprints = collect_fingerprints(repo_dir)
1275
- delta = diff_fingerprints(previous_fingerprints, current_fingerprints)
1276
- version_mismatch = previous_manifest.get("analysis_version") != ANALYSIS_VERSION
1277
-
1278
- rebuild_required = bool(force_full_rebuild or should_rebuild(repo_dir, analysis_version=ANALYSIS_VERSION))
1279
- architecture_missing = not os.path.exists(architecture_metrics_path)
1280
- dependency_missing = not os.path.exists(dependency_cycles_path)
1281
- risk_missing = not os.path.exists(risk_radar_path)
1282
- r1 = {}
1283
- r2 = {}
1284
- metrics = {}
1285
-
1286
- # Start progress spinner for large repositories
1287
- spinner = ProgressSpinner("Analyzing repository...")
1288
- spinner.start()
1289
-
1290
- try:
1291
- if rebuild_required:
1292
- r1 = run_phase4(
1293
- repo_dir=repo_dir,
1294
- output_dir=cache_dir,
1295
- force_rebuild=bool(version_mismatch or force_full_rebuild),
1296
- )
1297
- r2 = run_explain(repo_dir=repo_dir, output_dir=cache_dir)
1298
- resolved_calls_path = r1.get("resolved_calls_path", resolved_calls_path)
1299
- metrics = write_hub_metrics_from_resolved_calls(
1300
- resolved_calls_path=resolved_calls_path,
1301
- output_path=analysis_metrics_path,
1302
- )
1303
- save_manifest(
1304
- repo_dir,
1305
- build_manifest(
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
- tree_snapshot = _build_project_tree_snapshot(repo_dir)
1327
- with open(project_tree_path, "w", encoding="utf-8") as f:
1328
- json.dump(tree_snapshot, f, indent=2)
1329
- elif os.path.exists(resolved_calls_path):
1330
- metrics = write_hub_metrics_from_resolved_calls(
1331
- resolved_calls_path=resolved_calls_path,
1332
- output_path=analysis_metrics_path,
1333
- )
1334
- if not os.path.exists(project_tree_path):
1335
- tree_snapshot = _build_project_tree_snapshot(repo_dir)
1336
- with open(project_tree_path, "w", encoding="utf-8") as f:
1337
- json.dump(tree_snapshot, f, indent=2)
1338
-
1339
- # Derived architecture outputs:
1340
- # - regenerate on rebuild
1341
- # - regenerate if architecture/dependency artifacts are missing
1342
- if rebuild_required or architecture_missing or dependency_missing:
1343
- with open(resolved_calls_path, "r", encoding="utf-8") as f:
1344
- resolved_calls = json.load(f)
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
- # Risk radar derived output:
1383
- # - regenerate on rebuild
1384
- # - regenerate when missing (cached analyze run)
1385
- # - regenerate when architecture/dependency were regenerated
1386
- if rebuild_required or risk_missing or architecture_missing or dependency_missing:
1387
- risk_payload = compute_risk_radar(cache_dir=cache_dir, top_k=25)
1388
- with open(risk_radar_path, "w", encoding="utf-8") as f:
1389
- json.dump(risk_payload, f, indent=2)
1390
- repo_hash = compute_repo_hash(repo_dir)
1391
- upsert_metadata(
1392
- repo_hash=repo_hash,
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
- except Exception as e:
1407
- spinner.stop()
1408
- print_json({"ok": False, "error": "ANALYZE_FAILED", "message": redact_secrets(str(e))})
1409
- return 1
1410
-
1411
- spinner.stop()
1412
- print_json({
1413
- "ok": True,
1414
- "source": source,
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
- return 0
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,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from cli import main as _main
3
+ from codemap_app import main as _main
4
4
 
5
5
 
6
6
  def main() -> int:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codemap-python
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Local Python code analysis tool - understand architecture, dependencies, and call graphs
5
5
  Author-email: ADITYA <aditykushwaha69@gmail.com>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
- cli.py,sha256=cJZtfufG23l55sRla2iObgV5ZjjTTEsPPL6vDZV02eQ,73155
2
- codemap_cli.py,sha256=W6GWcVQsIUQwUq5xTe0R-6hSKOwkqgpJxlosdSEyYco,175
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=N30G0SS0bf1S-in2wQmSmnAeOqr86jH-MIFnFe2fo2I,2756
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=e-2HgVkORusvL5pFa5TJL7nW38Ylj8M4ziQMmrt8O40,1419
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=dHggiH4CAdEE1Z9WDqs0YJfiH2-Z2xIhlWSULD2UVNU,5661
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=Km39BqMIBFOljlxTy9-NdyFNnVbx14XQXH2NVbnoWRU,9944
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=9PMf63VAsXDrBWFm4BMDSvp-pW96GnK88h5-6-C36Rw,4966
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=FUuZOO_CSHP_A2cFO4Kk8WQc34AYfqTol2w6n7DmTBE,24579
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=FM-odEwfRQ1TQaNPTzdaoq90eQSRdRyBpFw-fof3FUM,82050
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=E8N2rfoha_4RZQHOpZSiIpVRIlhrB0Qs5FjCog4SUhU,118335
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.4.dist-info/METADATA,sha256=xrlNhbwZeFjtfRwgNE7fQKGglkUpNJBODVYFopI1AcA,11131
57
- codemap_python-0.1.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
58
- codemap_python-0.1.4.dist-info/entry_points.txt,sha256=71TCgwo56CPxSfh-YSmofpNwq4-kSsjmBL_qzJ5kfmk,45
59
- codemap_python-0.1.4.dist-info/top_level.txt,sha256=JOu1LG-DyeBXc4u2Cn7KznNz-bZExhsf1CB7fV4r-t8,43
60
- codemap_python-0.1.4.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  analysis
2
- cli
2
+ codemap_app
3
3
  codemap_cli
4
4
  security_utils
5
5
  ui
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 cli.py api analyze --path <repo>"
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, "cli.py"), "api"] + list(args)
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 cli.py api analyze --github {repo_url} --ref {ref} --mode {mode}"
430
- return f"python cli.py api analyze --path {repo.get('path', '<repo>')}"
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 cli.py api analyze --path <repo>",
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 cli.py api analyze --path <repo>";
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 cli.py api analyze --github ${r.repo_url} --ref ${ref} --mode ${mode}`;
238
+ return `python codemap_app.py api analyze --github ${r.repo_url} --ref ${ref} --mode ${mode}`;
239
239
  }
240
- return `python cli.py api analyze --path ${r.repo_path || "<repo>"}`;
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 cli.py api repo_summary --repo ${repoName || "<repo>"}`;
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 cli.py api risk_radar --repo ${repoName || "<repo>"}`;
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 cli.py api analyze --path &lt;repo&gt;</div>
916
+ <div class="path">Run: python codemap_app.py api analyze --path &lt;repo&gt;</div>
917
917
  </div>
918
918
  `;
919
919
  }