code2llm 0.5.46__tar.gz → 0.5.48__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.
Files changed (103) hide show
  1. {code2llm-0.5.46 → code2llm-0.5.48}/PKG-INFO +6 -6
  2. {code2llm-0.5.46 → code2llm-0.5.48}/README.md +5 -5
  3. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli_exports/formats.py +1 -0
  5. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/context_exporter.py +24 -0
  6. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/html_dashboard.py +177 -24
  7. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/readme_exporter.py +1 -1
  8. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon/renderer.py +29 -1
  9. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon_view.py +38 -6
  10. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/nlp/__init__.py +1 -1
  11. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm.egg-info/PKG-INFO +6 -6
  12. {code2llm-0.5.46 → code2llm-0.5.48}/pyproject.toml +1 -1
  13. {code2llm-0.5.46 → code2llm-0.5.48}/LICENSE +0 -0
  14. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/__main__.py +0 -0
  15. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/__init__.py +0 -0
  16. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/call_graph.py +0 -0
  17. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/cfg.py +0 -0
  18. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/coupling.py +0 -0
  19. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/data_analysis.py +0 -0
  20. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/dfg.py +0 -0
  21. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/pipeline_detector.py +0 -0
  22. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/side_effects.py +0 -0
  23. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/smells.py +0 -0
  24. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/analysis/type_inference.py +0 -0
  25. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/api.py +0 -0
  26. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli.py +0 -0
  27. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli_analysis.py +0 -0
  28. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli_exports/__init__.py +0 -0
  29. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli_exports/code2logic.py +0 -0
  30. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli_exports/orchestrator.py +0 -0
  31. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/cli_exports/prompt.py +0 -0
  32. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/__init__.py +0 -0
  33. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/analyzer.py +0 -0
  34. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/config.py +0 -0
  35. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/core/__init__.py +0 -0
  36. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/core/file_analyzer.py +0 -0
  37. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/core/file_cache.py +0 -0
  38. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/core/file_filter.py +0 -0
  39. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/core/refactoring.py +0 -0
  40. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/large_repo.py +0 -0
  41. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/models.py +0 -0
  42. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming/__init__.py +0 -0
  43. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming/cache.py +0 -0
  44. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming/incremental.py +0 -0
  45. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming/prioritizer.py +0 -0
  46. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming/scanner.py +0 -0
  47. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming/strategies.py +0 -0
  48. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/streaming_analyzer.py +0 -0
  49. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/core/toon_size_manager.py +0 -0
  50. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/__init__.py +0 -0
  51. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/article_view.py +0 -0
  52. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/base.py +0 -0
  53. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/context_view.py +0 -0
  54. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/evolution_exporter.py +0 -0
  55. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/flow_constants.py +0 -0
  56. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/flow_exporter.py +0 -0
  57. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/flow_renderer.py +0 -0
  58. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/json_exporter.py +0 -0
  59. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/llm_exporter.py +0 -0
  60. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/map_exporter.py +0 -0
  61. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/mermaid_exporter.py +0 -0
  62. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/project_yaml_exporter.py +0 -0
  63. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/report_generators.py +0 -0
  64. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon/__init__.py +0 -0
  65. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon/helpers.py +0 -0
  66. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon/metrics.py +0 -0
  67. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon/module_detail.py +0 -0
  68. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/toon.py +0 -0
  69. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/validate_project.py +0 -0
  70. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/exporters/yaml_exporter.py +0 -0
  71. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/generators/__init__.py +0 -0
  72. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/generators/llm_flow.py +0 -0
  73. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/generators/llm_task.py +0 -0
  74. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/generators/mermaid.py +0 -0
  75. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/nlp/config.py +0 -0
  76. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/nlp/entity_resolution.py +0 -0
  77. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/nlp/intent_matching.py +0 -0
  78. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/nlp/normalization.py +0 -0
  79. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/nlp/pipeline.py +0 -0
  80. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/patterns/__init__.py +0 -0
  81. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/patterns/detector.py +0 -0
  82. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/refactor/__init__.py +0 -0
  83. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm/refactor/prompt_engine.py +0 -0
  84. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm.egg-info/SOURCES.txt +0 -0
  85. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm.egg-info/dependency_links.txt +0 -0
  86. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm.egg-info/entry_points.txt +0 -0
  87. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm.egg-info/requires.txt +0 -0
  88. {code2llm-0.5.46 → code2llm-0.5.48}/code2llm.egg-info/top_level.txt +0 -0
  89. {code2llm-0.5.46 → code2llm-0.5.48}/setup.cfg +0 -0
  90. {code2llm-0.5.46 → code2llm-0.5.48}/setup.py +0 -0
  91. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_advanced_analysis.py +0 -0
  92. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_analyzer.py +0 -0
  93. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_deep_analysis.py +0 -0
  94. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_edge_cases.py +0 -0
  95. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_flow_exporter.py +0 -0
  96. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_format_quality.py +0 -0
  97. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_multilanguage_e2e.py +0 -0
  98. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_nlp_pipeline.py +0 -0
  99. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_pipeline_detector.py +0 -0
  100. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_prompt_engine.py +0 -0
  101. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_prompt_txt.py +0 -0
  102. {code2llm-0.5.46 → code2llm-0.5.48}/tests/test_refactoring_engine.py +0 -0
  103. {code2llm-0.5.46 → code2llm-0.5.48}/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.46
3
+ Version: 0.5.48
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
@@ -50,7 +50,7 @@ Dynamic: requires-python
50
50
 
51
51
  # code2llm - Generated Analysis Files
52
52
 
53
- This directory contains the complete analysis of your Python project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
53
+ This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
54
54
 
55
55
  ## 📁 Generated Files Overview
56
56
 
@@ -60,7 +60,7 @@ When you run `code2llm ./ -f all`, the following files are created:
60
60
 
61
61
  | File | Format | Purpose | Key Insights |
62
62
  |------|--------|---------|--------------|
63
- | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Complexity, god modules, coupling | 54 critical functions, 0 god modules |
63
+ | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Complexity, god modules, coupling | 59 critical functions, 0 god modules |
64
64
  | `project.toon` | **TOON** | **🧠 Project logic** - Compact module view from code2logic | Generated via code2logic integration |
65
65
 
66
66
  ### 🤖 LLM-Ready Documentation
@@ -374,10 +374,10 @@ code2llm ./ -f yaml --separate-orphans
374
374
  ---
375
375
 
376
376
  **Generated by**: `code2llm ./ -f all --readme`
377
- **Analysis Date**: 2026-03-07
378
- **Total Functions**: 826
377
+ **Analysis Date**: 2026-03-09
378
+ **Total Functions**: 850
379
379
  **Total Classes**: 104
380
- **Modules**: 103
380
+ **Modules**: 105
381
381
 
382
382
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
383
383
 
@@ -1,6 +1,6 @@
1
1
  # code2llm - Generated Analysis Files
2
2
 
3
- This directory contains the complete analysis of your Python project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
3
+ This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
4
4
 
5
5
  ## 📁 Generated Files Overview
6
6
 
@@ -10,7 +10,7 @@ When you run `code2llm ./ -f all`, the following files are created:
10
10
 
11
11
  | File | Format | Purpose | Key Insights |
12
12
  |------|--------|---------|--------------|
13
- | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Complexity, god modules, coupling | 54 critical functions, 0 god modules |
13
+ | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Complexity, god modules, coupling | 59 critical functions, 0 god modules |
14
14
  | `project.toon` | **TOON** | **🧠 Project logic** - Compact module view from code2logic | Generated via code2logic integration |
15
15
 
16
16
  ### 🤖 LLM-Ready Documentation
@@ -324,10 +324,10 @@ code2llm ./ -f yaml --separate-orphans
324
324
  ---
325
325
 
326
326
  **Generated by**: `code2llm ./ -f all --readme`
327
- **Analysis Date**: 2026-03-07
328
- **Total Functions**: 826
327
+ **Analysis Date**: 2026-03-09
328
+ **Total Functions**: 850
329
329
  **Total Classes**: 104
330
- **Modules**: 103
330
+ **Modules**: 105
331
331
 
332
332
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
333
333
 
@@ -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.46"
11
+ __version__ = "0.5.48"
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, evolution chart, module CC bar chart,
5
- alerts table, hotspots table, refactoring priorities.
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
- mod_chart = self._build_module_chart_data(modules)
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, mod_chart=mod_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 _build_module_chart_data(modules: List[Dict]) -> Dict[str, Any]:
75
- top = sorted(modules, key=lambda m: m.get("cc_max", 0), reverse=True)[:15]
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
- "cc": [m.get("cc_max", 0) for m in top],
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", [])[:10], 1):
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
- mod = ctx["mod_chart"]
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(180px,1fr)); gap:1rem; margin:1rem 0; }}
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
- @media (max-width:768px) {{ .two-col {{ grid-template-columns:1fr; }} }}
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
+ &nbsp;·&nbsp; Primary language: <strong>{proj.get('language', 'unknown')}</strong>
293
+ &nbsp;·&nbsp; {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 class="chart-container">
219
- <h2 style="border:none;margin:0 0 .5rem;">Module CC (top 15)</h2>
220
- <canvas id="modChart" height="200"></canvas>
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
- const modCtx = document.getElementById('modChart').getContext('2d');
260
- new Chart(modCtx, {{
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: {mod["names"]},
264
- datasets: [{{ label: 'Max CC', data: {mod["cc"]},
265
- backgroundColor: {mod["cc"]}.map(v => v >= 15 ? '#ef4444' : v >= 10 ? '#eab308' : '#22c55e')
416
+ labels: {mod_funcs['names']},
417
+ datasets: [{{ label: 'Functions', data: {mod_funcs['funcs']},
418
+ backgroundColor: '#22c55e'
266
419
  }}]
267
420
  }},
268
421
  options: {{
@@ -101,7 +101,7 @@ class READMEExporter(BaseExporter):
101
101
 
102
102
  content = f"""# code2llm - Generated Analysis Files
103
103
 
104
- This directory contains the complete analysis of your Python project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
104
+ This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
105
105
 
106
106
  ## 📁 Generated Files Overview
107
107
 
@@ -1,8 +1,11 @@
1
1
  """Rendering functions for TOON exporter."""
2
2
 
3
+ from collections import defaultdict
4
+ from pathlib import Path
3
5
  from typing import Any, Dict, List
4
6
 
5
7
  from ...core.models import AnalysisResult, FunctionInfo
8
+ from ...core.config import LANGUAGE_EXTENSIONS
6
9
 
7
10
  from .helpers import _dup_file_set, _package_of
8
11
 
@@ -32,12 +35,37 @@ class ToonRenderer:
32
35
  ndups = len(ctx["duplicates"])
33
36
  ncycles = len(ctx["cycles"])
34
37
 
38
+ lang_label = self._detect_language_label(result)
39
+
35
40
  lines = [
36
- f"# code2llm | {nfiles}f {total_lines}L | py:{nfiles} | {ctx['timestamp']}",
41
+ f"# code2llm | {nfiles}f {total_lines}L | {lang_label} | {ctx['timestamp']}",
37
42
  f"# CC̄={avg_cc} | critical:{critical}/{nfuncs} | dups:{ndups} | cycles:{ncycles}",
38
43
  ]
39
44
  return lines
40
45
 
46
+ @staticmethod
47
+ def _detect_language_label(result: AnalysisResult) -> str:
48
+ """Build language breakdown label like 'typescript:463,javascript:10,rust:1'."""
49
+ from .helpers import _is_excluded
50
+ langs: Dict[str, int] = defaultdict(int)
51
+ for mi in result.modules.values():
52
+ if _is_excluded(mi.file):
53
+ continue
54
+ detected = False
55
+ for lang, extensions in LANGUAGE_EXTENSIONS.items():
56
+ if any(mi.file.endswith(ext) for ext in extensions):
57
+ langs[lang] += 1
58
+ detected = True
59
+ break
60
+ if not detected:
61
+ ext = Path(mi.file).suffix.lower()
62
+ if ext:
63
+ langs[ext.lstrip('.')] += 1
64
+ if not langs:
65
+ return "unknown"
66
+ sorted_langs = sorted(langs.items(), key=lambda x: -x[1])
67
+ return ",".join(f"{lang}:{count}" for lang, count in sorted_langs)
68
+
41
69
  def render_health(self, ctx: Dict[str, Any]) -> List[str]:
42
70
  """Render health section."""
43
71
  issues = ctx["health"]
@@ -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
- top_mods = [m for m in modules if m.get("cc_max", 0) > 0][:10]
77
- lines = ["", f"MODULES[{len(modules)}] (top by CC):"]
78
- for m in top_mods:
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[{m.get('path', '?')}] "
100
+ f" M[{path}] "
81
101
  f"{m.get('lines', 0)}L "
82
102
  f"C:{m.get('classes', 0)} "
83
- f"M:{m.get('methods', 0)} "
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
@@ -4,7 +4,7 @@ Provides query normalization, intent matching, and entity resolution
4
4
  with multilingual support and fuzzy matching.
5
5
  """
6
6
 
7
- __version__ = "0.5.46"
7
+ __version__ = "0.5.48"
8
8
 
9
9
  from .pipeline import NLPPipeline
10
10
  from .normalization import QueryNormalizer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2llm
3
- Version: 0.5.46
3
+ Version: 0.5.48
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
@@ -50,7 +50,7 @@ Dynamic: requires-python
50
50
 
51
51
  # code2llm - Generated Analysis Files
52
52
 
53
- This directory contains the complete analysis of your Python project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
53
+ This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
54
54
 
55
55
  ## 📁 Generated Files Overview
56
56
 
@@ -60,7 +60,7 @@ When you run `code2llm ./ -f all`, the following files are created:
60
60
 
61
61
  | File | Format | Purpose | Key Insights |
62
62
  |------|--------|---------|--------------|
63
- | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Complexity, god modules, coupling | 54 critical functions, 0 god modules |
63
+ | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Complexity, god modules, coupling | 59 critical functions, 0 god modules |
64
64
  | `project.toon` | **TOON** | **🧠 Project logic** - Compact module view from code2logic | Generated via code2logic integration |
65
65
 
66
66
  ### 🤖 LLM-Ready Documentation
@@ -374,10 +374,10 @@ code2llm ./ -f yaml --separate-orphans
374
374
  ---
375
375
 
376
376
  **Generated by**: `code2llm ./ -f all --readme`
377
- **Analysis Date**: 2026-03-07
378
- **Total Functions**: 826
377
+ **Analysis Date**: 2026-03-09
378
+ **Total Functions**: 850
379
379
  **Total Classes**: 104
380
- **Modules**: 103
380
+ **Modules**: 105
381
381
 
382
382
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
383
383
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.46"
7
+ version = "0.5.48"
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