code2llm 0.5.47__tar.gz → 0.5.49__tar.gz
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.
- {code2llm-0.5.47 → code2llm-0.5.49}/PKG-INFO +2 -2
- {code2llm-0.5.47 → code2llm-0.5.49}/README.md +1 -1
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/__init__.py +1 -1
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli_exports/formats.py +1 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/context_exporter.py +24 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/html_dashboard.py +177 -24
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon_view.py +38 -6
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm.egg-info/PKG-INFO +2 -2
- {code2llm-0.5.47 → code2llm-0.5.49}/pyproject.toml +1 -1
- {code2llm-0.5.47 → code2llm-0.5.49}/LICENSE +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/__main__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/call_graph.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/cfg.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/api.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli_analysis.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli_exports/orchestrator.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/analyzer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/config.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/core/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/core/file_analyzer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/core/file_cache.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/core/file_filter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/core/refactoring.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/models.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/flow_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon/helpers.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/setup.cfg +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/setup.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.47 → code2llm-0.5.49}/tests/test_toon_v2.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.49
|
|
4
4
|
Summary: High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries
|
|
5
5
|
Home-page: https://github.com/wronai/stts
|
|
6
6
|
Author: STTS Project
|
|
@@ -375,7 +375,7 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
375
375
|
|
|
376
376
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
377
377
|
**Analysis Date**: 2026-03-09
|
|
378
|
-
**Total Functions**:
|
|
378
|
+
**Total Functions**: 854
|
|
379
379
|
**Total Classes**: 104
|
|
380
380
|
**Modules**: 105
|
|
381
381
|
|
|
@@ -8,7 +8,7 @@ Includes NLP Processing Pipeline for query normalization, intent matching,
|
|
|
8
8
|
and entity resolution with multilingual support.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
__version__ = "0.5.
|
|
11
|
+
__version__ = "0.5.49"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -121,6 +121,7 @@ def _export_simple_formats(args, result, output_dir: Path, formats):
|
|
|
121
121
|
data = load_project_yaml(str(yaml_path))
|
|
122
122
|
view_map = {
|
|
123
123
|
'project.toon': ToonViewGenerator(),
|
|
124
|
+
'context.md': ContextViewGenerator(),
|
|
124
125
|
'dashboard.html': HTMLDashboardGenerator(),
|
|
125
126
|
}
|
|
126
127
|
for filename, generator in view_map.items():
|
|
@@ -4,10 +4,12 @@ Rename from llm_exporter.py → context_exporter.py (Sprint 4, v0.3.3).
|
|
|
4
4
|
Produces LLM-ready architecture summary with flows, patterns, and API surface.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from collections import defaultdict
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Any, Dict, List, Tuple
|
|
9
10
|
from .base import Exporter
|
|
10
11
|
from ..core.models import AnalysisResult, FunctionInfo
|
|
12
|
+
from ..core.config import LANGUAGE_EXTENSIONS
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class ContextExporter(Exporter):
|
|
@@ -54,10 +56,15 @@ class ContextExporter(Exporter):
|
|
|
54
56
|
f.write('\n'.join(lines))
|
|
55
57
|
|
|
56
58
|
def _get_overview(self, result: AnalysisResult) -> List[str]:
|
|
59
|
+
lang_info = self._detect_languages(result)
|
|
60
|
+
primary = lang_info[0][0] if lang_info else 'unknown'
|
|
61
|
+
lang_summary = ', '.join(f'{l}: {c}' for l, c in lang_info[:5])
|
|
57
62
|
return [
|
|
58
63
|
"## Overview",
|
|
59
64
|
"",
|
|
60
65
|
f"- **Project**: {result.project_path}",
|
|
66
|
+
f"- **Primary Language**: {primary}",
|
|
67
|
+
f"- **Languages**: {lang_summary}",
|
|
61
68
|
f"- **Analysis Mode**: {result.analysis_mode}",
|
|
62
69
|
f"- **Total Functions**: {len(result.functions)}",
|
|
63
70
|
f"- **Total Classes**: {len(result.classes)}",
|
|
@@ -66,6 +73,23 @@ class ContextExporter(Exporter):
|
|
|
66
73
|
"",
|
|
67
74
|
]
|
|
68
75
|
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _detect_languages(result: AnalysisResult) -> List[tuple]:
|
|
78
|
+
"""Detect languages from module file extensions."""
|
|
79
|
+
lang_counts: Dict[str, int] = defaultdict(int)
|
|
80
|
+
for mi in result.modules.values():
|
|
81
|
+
detected = False
|
|
82
|
+
for lang, extensions in LANGUAGE_EXTENSIONS.items():
|
|
83
|
+
if any(mi.file.endswith(ext) for ext in extensions):
|
|
84
|
+
lang_counts[lang] += 1
|
|
85
|
+
detected = True
|
|
86
|
+
break
|
|
87
|
+
if not detected:
|
|
88
|
+
ext = Path(mi.file).suffix.lower()
|
|
89
|
+
if ext:
|
|
90
|
+
lang_counts[ext.lstrip('.')] += 1
|
|
91
|
+
return sorted(lang_counts.items(), key=lambda x: -x[1])
|
|
92
|
+
|
|
69
93
|
def _get_architecture_by_module(self, result: AnalysisResult) -> List[str]:
|
|
70
94
|
lines = ["## Architecture by Module", ""]
|
|
71
95
|
module_stats = []
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
"""HTML Dashboard Generator — web visualization with trend charts.
|
|
2
2
|
|
|
3
3
|
Generates dashboard.html from project.yaml data.
|
|
4
|
-
Includes: metric cards,
|
|
5
|
-
|
|
4
|
+
Includes: metric cards, language breakdown, evolution chart,
|
|
5
|
+
module size/function charts, alerts, hotspots, refactoring priorities.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from collections import defaultdict
|
|
8
9
|
from datetime import datetime
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import Any, Dict, List
|
|
11
12
|
|
|
13
|
+
# Language detection from file extensions
|
|
14
|
+
_LANG_EXT_MAP = {
|
|
15
|
+
'.py': 'Python', '.ts': 'TypeScript', '.tsx': 'TypeScript',
|
|
16
|
+
'.js': 'JavaScript', '.jsx': 'JavaScript', '.mjs': 'JavaScript', '.cjs': 'JavaScript',
|
|
17
|
+
'.go': 'Go', '.rs': 'Rust', '.java': 'Java',
|
|
18
|
+
'.cpp': 'C++', '.cc': 'C++', '.cxx': 'C++', '.hpp': 'C++', '.h': 'C/C++',
|
|
19
|
+
'.c': 'C', '.cs': 'C#', '.rb': 'Ruby', '.php': 'PHP',
|
|
20
|
+
'.swift': 'Swift', '.kt': 'Kotlin', '.kts': 'Kotlin',
|
|
21
|
+
'.scala': 'Scala', '.sh': 'Shell', '.bash': 'Shell', '.zsh': 'Shell',
|
|
22
|
+
'.dart': 'Dart', '.ex': 'Elixir', '.exs': 'Elixir',
|
|
23
|
+
'.hs': 'Haskell', '.lua': 'Lua', '.pl': 'Perl', '.r': 'R', '.R': 'R',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_LANG_COLORS = {
|
|
27
|
+
'TypeScript': '#3178c6', 'JavaScript': '#f7df1e', 'Python': '#3572A5',
|
|
28
|
+
'Go': '#00ADD8', 'Rust': '#dea584', 'Java': '#b07219',
|
|
29
|
+
'C++': '#f34b7d', 'C': '#555555', 'C/C++': '#555555', 'C#': '#178600',
|
|
30
|
+
'Ruby': '#701516', 'PHP': '#4F5D95', 'Swift': '#F05138',
|
|
31
|
+
'Kotlin': '#A97BFF', 'Scala': '#c22d40', 'Shell': '#89e051',
|
|
32
|
+
'Dart': '#00B4AB', 'Elixir': '#6e4a7e', 'Haskell': '#5e5086',
|
|
33
|
+
'Lua': '#000080', 'Perl': '#0298c3', 'R': '#198CE7',
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
|
|
13
37
|
class HTMLDashboardGenerator:
|
|
14
38
|
"""Generate dashboard.html from project.yaml data."""
|
|
@@ -30,20 +54,25 @@ class HTMLDashboardGenerator:
|
|
|
30
54
|
|
|
31
55
|
health_color, health_label = self._health_verdict(health)
|
|
32
56
|
evo_chart = self._build_evolution_section(evolution)
|
|
33
|
-
|
|
57
|
+
lang_data = self._build_language_breakdown(modules)
|
|
58
|
+
mod_lines_chart = self._build_module_lines_chart(modules)
|
|
59
|
+
mod_funcs_chart = self._build_module_funcs_chart(modules)
|
|
34
60
|
alerts_html = self._build_alerts_html(health)
|
|
35
61
|
hotspots_html = self._build_hotspots_html(hotspots)
|
|
36
62
|
refactor_html = self._build_refactoring_html(refactoring)
|
|
63
|
+
top_modules_html = self._build_top_modules_html(modules)
|
|
37
64
|
|
|
38
65
|
cc_avg = health.get("cc_avg", 0)
|
|
39
66
|
|
|
40
67
|
return self._assemble_html(
|
|
41
68
|
proj=proj, stats=stats, health=health,
|
|
42
69
|
cc_avg=cc_avg, health_color=health_color, health_label=health_label,
|
|
43
|
-
evo_chart=evo_chart,
|
|
70
|
+
evo_chart=evo_chart, lang_data=lang_data,
|
|
71
|
+
mod_lines_chart=mod_lines_chart, mod_funcs_chart=mod_funcs_chart,
|
|
44
72
|
alerts_html=alerts_html, hotspots_html=hotspots_html,
|
|
45
73
|
hotspots=hotspots, refactor_html=refactor_html,
|
|
46
|
-
refactoring=refactoring,
|
|
74
|
+
refactoring=refactoring, top_modules_html=top_modules_html,
|
|
75
|
+
modules=modules,
|
|
47
76
|
)
|
|
48
77
|
|
|
49
78
|
# ------------------------------------------------------------------
|
|
@@ -71,13 +100,66 @@ class HTMLDashboardGenerator:
|
|
|
71
100
|
}
|
|
72
101
|
|
|
73
102
|
@staticmethod
|
|
74
|
-
def
|
|
75
|
-
|
|
103
|
+
def _build_language_breakdown(modules: List[Dict]) -> Dict[str, Any]:
|
|
104
|
+
"""Detect languages from module paths and build pie chart data."""
|
|
105
|
+
lang_files: Dict[str, int] = defaultdict(int)
|
|
106
|
+
lang_lines: Dict[str, int] = defaultdict(int)
|
|
107
|
+
for m in modules:
|
|
108
|
+
ext = Path(m.get("path", "")).suffix.lower()
|
|
109
|
+
lang = _LANG_EXT_MAP.get(ext, ext.lstrip('.').capitalize() if ext else "Other")
|
|
110
|
+
lang_files[lang] += 1
|
|
111
|
+
lang_lines[lang] += m.get("lines", 0)
|
|
112
|
+
|
|
113
|
+
sorted_langs = sorted(lang_files.items(), key=lambda x: -x[1])
|
|
114
|
+
names = [l[0] for l in sorted_langs]
|
|
115
|
+
files = [l[1] for l in sorted_langs]
|
|
116
|
+
lines = [lang_lines[l[0]] for l in sorted_langs]
|
|
117
|
+
colors = [_LANG_COLORS.get(n, '#6b7280') for n in names]
|
|
118
|
+
return {"names": names, "files": files, "lines": lines, "colors": colors}
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def _build_module_lines_chart(modules: List[Dict]) -> Dict[str, Any]:
|
|
122
|
+
"""Top 15 modules by line count."""
|
|
123
|
+
top = sorted(modules, key=lambda m: m.get("lines", 0), reverse=True)[:15]
|
|
124
|
+
return {
|
|
125
|
+
"names": [Path(m.get("path", "")).name for m in top],
|
|
126
|
+
"lines": [m.get("lines", 0) for m in top],
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def _build_module_funcs_chart(modules: List[Dict]) -> Dict[str, Any]:
|
|
131
|
+
"""Top 15 modules by function/method count."""
|
|
132
|
+
top = sorted(modules, key=lambda m: m.get("methods", 0), reverse=True)[:15]
|
|
76
133
|
return {
|
|
77
134
|
"names": [Path(m.get("path", "")).name for m in top],
|
|
78
|
-
"
|
|
135
|
+
"funcs": [m.get("methods", 0) for m in top],
|
|
79
136
|
}
|
|
80
137
|
|
|
138
|
+
@staticmethod
|
|
139
|
+
def _build_top_modules_html(modules: List[Dict]) -> str:
|
|
140
|
+
"""Build top modules table sorted by lines."""
|
|
141
|
+
top = sorted(modules, key=lambda m: m.get("lines", 0), reverse=True)[:20]
|
|
142
|
+
html = ""
|
|
143
|
+
for m in top:
|
|
144
|
+
path = m.get("path", "?")
|
|
145
|
+
lines = m.get("lines", 0)
|
|
146
|
+
methods = m.get("methods", 0)
|
|
147
|
+
classes = m.get("classes", 0)
|
|
148
|
+
cc_max = m.get("cc_max", 0)
|
|
149
|
+
ext = Path(path).suffix.lower()
|
|
150
|
+
lang = _LANG_EXT_MAP.get(ext, ext.lstrip('.'))
|
|
151
|
+
color = _LANG_COLORS.get(lang, '#6b7280')
|
|
152
|
+
html += f"""
|
|
153
|
+
<tr>
|
|
154
|
+
<td><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:{color};margin-right:6px"></span>{Path(path).name}</td>
|
|
155
|
+
<td style="color:var(--muted);font-size:.75rem">{'/'.join(Path(path).parts[:-1]) or '.'}</td>
|
|
156
|
+
<td style="text-align:right">{lines:,}</td>
|
|
157
|
+
<td style="text-align:right">{methods}</td>
|
|
158
|
+
<td style="text-align:right">{classes}</td>
|
|
159
|
+
<td style="text-align:right">{cc_max}</td>
|
|
160
|
+
</tr>"""
|
|
161
|
+
return html
|
|
162
|
+
|
|
81
163
|
@staticmethod
|
|
82
164
|
def _build_alerts_html(health: Dict) -> str:
|
|
83
165
|
html = ""
|
|
@@ -109,7 +191,7 @@ class HTMLDashboardGenerator:
|
|
|
109
191
|
@staticmethod
|
|
110
192
|
def _build_refactoring_html(refactoring: Dict) -> str:
|
|
111
193
|
html = ""
|
|
112
|
-
for i, p in enumerate(refactoring.get("priorities", [])[:
|
|
194
|
+
for i, p in enumerate(refactoring.get("priorities", [])[:15], 1):
|
|
113
195
|
impact_class = p.get("impact", "low")
|
|
114
196
|
html += f"""
|
|
115
197
|
<tr>
|
|
@@ -131,15 +213,20 @@ class HTMLDashboardGenerator:
|
|
|
131
213
|
health_color = ctx["health_color"]
|
|
132
214
|
health_label = ctx["health_label"]
|
|
133
215
|
evo = ctx["evo_chart"]
|
|
134
|
-
|
|
216
|
+
lang = ctx["lang_data"]
|
|
217
|
+
mod_lines = ctx["mod_lines_chart"]
|
|
218
|
+
mod_funcs = ctx["mod_funcs_chart"]
|
|
135
219
|
alerts_html = ctx["alerts_html"]
|
|
136
220
|
hotspots_html = ctx["hotspots_html"]
|
|
137
221
|
hotspots = ctx["hotspots"]
|
|
138
222
|
refactor_html = ctx["refactor_html"]
|
|
139
223
|
refactoring = ctx["refactoring"]
|
|
224
|
+
top_modules_html = ctx["top_modules_html"]
|
|
225
|
+
modules = ctx["modules"]
|
|
140
226
|
|
|
141
227
|
evo_section = self._render_evolution_section(evo)
|
|
142
228
|
evo_script = self._render_evolution_script(evo)
|
|
229
|
+
lang_summary = ', '.join(f'{n}: {c}' for n, c in zip(lang['names'], lang['files']))
|
|
143
230
|
|
|
144
231
|
return f"""<!DOCTYPE html>
|
|
145
232
|
<html lang="en">
|
|
@@ -165,7 +252,7 @@ class HTMLDashboardGenerator:
|
|
|
165
252
|
body {{ font-family: 'Segoe UI',system-ui,sans-serif; background:var(--bg); color:var(--text); padding:2rem; }}
|
|
166
253
|
h1 {{ font-size:1.5rem; margin-bottom:.5rem; }}
|
|
167
254
|
h2 {{ font-size:1.1rem; color:var(--muted); margin:1.5rem 0 .75rem; border-bottom:1px solid var(--border); padding-bottom:.25rem; }}
|
|
168
|
-
.grid {{ display:grid; grid-template-columns:repeat(auto-fit,minmax(
|
|
255
|
+
.grid {{ display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:1rem; margin:1rem 0; }}
|
|
169
256
|
.card {{ background:var(--surface); border:1px solid var(--border); border-radius:.5rem; padding:1rem; }}
|
|
170
257
|
.card .value {{ font-size:1.8rem; font-weight:700; }}
|
|
171
258
|
.card .label {{ color:var(--muted); font-size:.8rem; text-transform:uppercase; }}
|
|
@@ -186,10 +273,12 @@ class HTMLDashboardGenerator:
|
|
|
186
273
|
tr.warning td {{ background:rgba(234,179,8,.05); }}
|
|
187
274
|
.health-indicator {{ display:inline-block; width:12px; height:12px; border-radius:50%; margin-right:.5rem; }}
|
|
188
275
|
.two-col {{ display:grid; grid-template-columns:1fr 1fr; gap:1rem; }}
|
|
276
|
+
.three-col {{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:1rem; }}
|
|
189
277
|
.evo-cards {{ display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:.75rem; }}
|
|
190
278
|
.evo-cards .card {{ text-align:center; }}
|
|
191
279
|
.trend {{ font-size:.75rem; color:var(--muted); }}
|
|
192
|
-
|
|
280
|
+
.lang-tag {{ display:inline-block; padding:.1rem .4rem; border-radius:.2rem; font-size:.7rem; font-weight:600; margin-right:.25rem; color:#fff; }}
|
|
281
|
+
@media (max-width:768px) {{ .two-col,.three-col {{ grid-template-columns:1fr; }} }}
|
|
193
282
|
footer {{ margin-top:2rem; color:var(--muted); font-size:.75rem; text-align:center; }}
|
|
194
283
|
</style>
|
|
195
284
|
</head>
|
|
@@ -200,24 +289,47 @@ class HTMLDashboardGenerator:
|
|
|
200
289
|
</h1>
|
|
201
290
|
<p style="color:var(--muted);font-size:.85rem;">
|
|
202
291
|
Analyzed {proj.get('analyzed_at', '?')[:10]} by code2llm
|
|
292
|
+
· Primary language: <strong>{proj.get('language', 'unknown')}</strong>
|
|
293
|
+
· {lang_summary}
|
|
203
294
|
</p>
|
|
204
295
|
|
|
205
296
|
<div class="grid">
|
|
297
|
+
<div class="card"><div class="value">{stats.get('functions', 0):,}</div><div class="label">Functions</div></div>
|
|
298
|
+
<div class="card"><div class="value">{stats.get('classes', 0):,}</div><div class="label">Classes</div></div>
|
|
299
|
+
<div class="card"><div class="value">{stats.get('files', 0):,}</div><div class="label">Files</div></div>
|
|
300
|
+
<div class="card"><div class="value">{stats.get('lines', 0):,}</div><div class="label">Lines</div></div>
|
|
301
|
+
<div class="card"><div class="value">{len(lang['names'])}</div><div class="label">Languages</div></div>
|
|
206
302
|
<div class="card"><div class="value">{cc_avg}</div><div class="label">Avg CC</div></div>
|
|
207
303
|
<div class="card"><div class="value">{health.get('critical_count', 0)}</div><div class="label">Critical (CC≥{health.get('critical_limit', 10)})</div></div>
|
|
208
|
-
<div class="card"><div class="value">{stats.get('functions', 0)}</div><div class="label">Functions</div></div>
|
|
209
|
-
<div class="card"><div class="value">{stats.get('classes', 0)}</div><div class="label">Classes</div></div>
|
|
210
|
-
<div class="card"><div class="value">{stats.get('files', 0)}</div><div class="label">Files</div></div>
|
|
211
|
-
<div class="card"><div class="value">{stats.get('lines', 0)}</div><div class="label">Lines</div></div>
|
|
212
304
|
<div class="card"><div class="value">{health.get('duplicates', 0)}</div><div class="label">Duplicates</div></div>
|
|
213
305
|
<div class="card"><div class="value">{health.get('cycles', 0)}</div><div class="label">Cycles</div></div>
|
|
214
306
|
</div>
|
|
215
307
|
|
|
308
|
+
<div class="three-col">
|
|
309
|
+
<div class="chart-container">
|
|
310
|
+
<h2 style="border:none;margin:0 0 .5rem;">Language Distribution</h2>
|
|
311
|
+
<canvas id="langChart" height="200"></canvas>
|
|
312
|
+
</div>
|
|
313
|
+
<div class="chart-container">
|
|
314
|
+
<h2 style="border:none;margin:0 0 .5rem;">Largest Modules (lines)</h2>
|
|
315
|
+
<canvas id="modLinesChart" height="200"></canvas>
|
|
316
|
+
</div>
|
|
317
|
+
<div class="chart-container">
|
|
318
|
+
<h2 style="border:none;margin:0 0 .5rem;">Most Complex Modules (functions)</h2>
|
|
319
|
+
<canvas id="modFuncsChart" height="200"></canvas>
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
216
323
|
<div class="two-col">
|
|
217
324
|
{evo_section}
|
|
218
|
-
<div
|
|
219
|
-
<h2
|
|
220
|
-
<
|
|
325
|
+
<div>
|
|
326
|
+
<h2>Top Modules ({len(modules)})</h2>
|
|
327
|
+
<div class="card"><div class="table-wrap">
|
|
328
|
+
<table>
|
|
329
|
+
<thead><tr><th>Module</th><th>Path</th><th style="text-align:right">Lines</th><th style="text-align:right">Funcs</th><th style="text-align:right">Classes</th><th style="text-align:right">CC max</th></tr></thead>
|
|
330
|
+
<tbody>{top_modules_html if top_modules_html else '<tr><td colspan="6" style="color:var(--muted)">No modules</td></tr>'}</tbody>
|
|
331
|
+
</table>
|
|
332
|
+
</div></div>
|
|
221
333
|
</div>
|
|
222
334
|
</div>
|
|
223
335
|
|
|
@@ -256,13 +368,54 @@ class HTMLDashboardGenerator:
|
|
|
256
368
|
<script>
|
|
257
369
|
{evo_script}
|
|
258
370
|
|
|
259
|
-
|
|
260
|
-
|
|
371
|
+
// Language distribution pie chart
|
|
372
|
+
const langCtx = document.getElementById('langChart').getContext('2d');
|
|
373
|
+
new Chart(langCtx, {{
|
|
374
|
+
type: 'doughnut',
|
|
375
|
+
data: {{
|
|
376
|
+
labels: {lang['names']},
|
|
377
|
+
datasets: [{{
|
|
378
|
+
data: {lang['files']},
|
|
379
|
+
backgroundColor: {lang['colors']},
|
|
380
|
+
borderColor: 'var(--border)', borderWidth: 1
|
|
381
|
+
}}]
|
|
382
|
+
}},
|
|
383
|
+
options: {{
|
|
384
|
+
responsive: true,
|
|
385
|
+
plugins: {{
|
|
386
|
+
legend: {{ position: 'right', labels: {{ color: '#e2e8f0', font: {{size: 11}} }} }}
|
|
387
|
+
}}
|
|
388
|
+
}}
|
|
389
|
+
}});
|
|
390
|
+
|
|
391
|
+
// Module lines bar chart
|
|
392
|
+
const modLinesCtx = document.getElementById('modLinesChart').getContext('2d');
|
|
393
|
+
new Chart(modLinesCtx, {{
|
|
394
|
+
type: 'bar',
|
|
395
|
+
data: {{
|
|
396
|
+
labels: {mod_lines['names']},
|
|
397
|
+
datasets: [{{ label: 'Lines', data: {mod_lines['lines']},
|
|
398
|
+
backgroundColor: '#3b82f6'
|
|
399
|
+
}}]
|
|
400
|
+
}},
|
|
401
|
+
options: {{
|
|
402
|
+
responsive: true, indexAxis: 'y',
|
|
403
|
+
scales: {{
|
|
404
|
+
x: {{ grid:{{color:'#334155'}}, ticks:{{color:'#94a3b8'}} }},
|
|
405
|
+
y: {{ grid:{{color:'#334155'}}, ticks:{{color:'#94a3b8',font:{{size:10}}}} }}
|
|
406
|
+
}},
|
|
407
|
+
plugins: {{ legend: {{ display:false }} }}
|
|
408
|
+
}}
|
|
409
|
+
}});
|
|
410
|
+
|
|
411
|
+
// Module functions bar chart
|
|
412
|
+
const modFuncsCtx = document.getElementById('modFuncsChart').getContext('2d');
|
|
413
|
+
new Chart(modFuncsCtx, {{
|
|
261
414
|
type: 'bar',
|
|
262
415
|
data: {{
|
|
263
|
-
labels: {
|
|
264
|
-
datasets: [{{ label: '
|
|
265
|
-
backgroundColor:
|
|
416
|
+
labels: {mod_funcs['names']},
|
|
417
|
+
datasets: [{{ label: 'Functions', data: {mod_funcs['funcs']},
|
|
418
|
+
backgroundColor: '#22c55e'
|
|
266
419
|
}}]
|
|
267
420
|
}},
|
|
268
421
|
options: {{
|
|
@@ -3,9 +3,23 @@
|
|
|
3
3
|
Generates project.toon from project.yaml data.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from collections import defaultdict
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any, Dict, List
|
|
8
9
|
|
|
10
|
+
# Language detection from file extensions
|
|
11
|
+
_LANG_EXT_MAP = {
|
|
12
|
+
'.py': 'python', '.ts': 'typescript', '.tsx': 'typescript',
|
|
13
|
+
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
14
|
+
'.go': 'go', '.rs': 'rust', '.java': 'java',
|
|
15
|
+
'.cpp': 'cpp', '.cc': 'cpp', '.cxx': 'cpp', '.hpp': 'cpp', '.h': 'c',
|
|
16
|
+
'.c': 'c', '.cs': 'csharp', '.rb': 'ruby', '.php': 'php',
|
|
17
|
+
'.swift': 'swift', '.kt': 'kotlin', '.kts': 'kotlin',
|
|
18
|
+
'.scala': 'scala', '.sh': 'shell', '.bash': 'shell', '.zsh': 'shell',
|
|
19
|
+
'.dart': 'dart', '.ex': 'elixir', '.exs': 'elixir',
|
|
20
|
+
'.hs': 'haskell', '.lua': 'lua', '.pl': 'perl', '.r': 'r', '.R': 'r',
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
|
|
10
24
|
class ToonViewGenerator:
|
|
11
25
|
"""Generate project.toon from project.yaml data."""
|
|
@@ -37,11 +51,13 @@ class ToonViewGenerator:
|
|
|
37
51
|
@staticmethod
|
|
38
52
|
def _render_header(proj: Dict) -> List[str]:
|
|
39
53
|
stats = proj.get("stats", {})
|
|
54
|
+
lang = proj.get('language', 'unknown')
|
|
40
55
|
return [
|
|
41
56
|
f"# {proj.get('name', '?')} | "
|
|
42
57
|
f"{stats.get('functions', 0)} func | "
|
|
43
58
|
f"{stats.get('files', 0)}f | "
|
|
44
59
|
f"{stats.get('lines', 0)}L | "
|
|
60
|
+
f"{lang} | "
|
|
45
61
|
f"{proj.get('analyzed_at', '?')[:10]}",
|
|
46
62
|
"",
|
|
47
63
|
]
|
|
@@ -73,17 +89,33 @@ class ToonViewGenerator:
|
|
|
73
89
|
|
|
74
90
|
@staticmethod
|
|
75
91
|
def _render_modules(modules: List[Dict]) -> List[str]:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
# Show top modules by size (lines) - works for all languages
|
|
93
|
+
top_by_lines = sorted(modules, key=lambda m: m.get("lines", 0), reverse=True)[:15]
|
|
94
|
+
lines = ["", f"MODULES[{len(modules)}] (top by size):"]
|
|
95
|
+
for m in top_by_lines:
|
|
96
|
+
path = m.get('path', '?')
|
|
97
|
+
ext = Path(path).suffix.lower()
|
|
98
|
+
lang = _LANG_EXT_MAP.get(ext, ext.lstrip('.'))
|
|
79
99
|
lines.append(
|
|
80
|
-
f" M[{
|
|
100
|
+
f" M[{path}] "
|
|
81
101
|
f"{m.get('lines', 0)}L "
|
|
82
102
|
f"C:{m.get('classes', 0)} "
|
|
83
|
-
f"
|
|
103
|
+
f"F:{m.get('methods', 0)} "
|
|
84
104
|
f"CC↑{m.get('cc_max', 0)} "
|
|
85
|
-
f"D:{m.get('inbound_deps', 0)}"
|
|
105
|
+
f"D:{m.get('inbound_deps', 0)} "
|
|
106
|
+
f"({lang})"
|
|
86
107
|
)
|
|
108
|
+
|
|
109
|
+
# Language breakdown
|
|
110
|
+
lang_counts: Dict[str, int] = defaultdict(int)
|
|
111
|
+
for m in modules:
|
|
112
|
+
ext = Path(m.get('path', '')).suffix.lower()
|
|
113
|
+
lang = _LANG_EXT_MAP.get(ext, ext.lstrip('.') if ext else 'other')
|
|
114
|
+
lang_counts[lang] += 1
|
|
115
|
+
if lang_counts:
|
|
116
|
+
sorted_langs = sorted(lang_counts.items(), key=lambda x: -x[1])
|
|
117
|
+
lang_str = '/'.join(f"{l}:{c}" for l, c in sorted_langs)
|
|
118
|
+
lines.append(f" LANGS: {lang_str}")
|
|
87
119
|
return lines
|
|
88
120
|
|
|
89
121
|
@staticmethod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.49
|
|
4
4
|
Summary: High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries
|
|
5
5
|
Home-page: https://github.com/wronai/stts
|
|
6
6
|
Author: STTS Project
|
|
@@ -375,7 +375,7 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
375
375
|
|
|
376
376
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
377
377
|
**Analysis Date**: 2026-03-09
|
|
378
|
-
**Total Functions**:
|
|
378
|
+
**Total Functions**: 854
|
|
379
379
|
**Total Classes**: 104
|
|
380
380
|
**Modules**: 105
|
|
381
381
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code2llm"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.49"
|
|
8
8
|
description = "High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|