code2llm 0.5.117__tar.gz → 0.5.119__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 (142) hide show
  1. {code2llm-0.5.117 → code2llm-0.5.119}/PKG-INFO +2 -2
  2. {code2llm-0.5.117 → code2llm-0.5.119}/README.md +1 -1
  3. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/call_graph.py +2 -7
  5. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/cfg.py +2 -7
  6. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/data_analysis.py +87 -3
  7. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/utils/__init__.py +2 -2
  8. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/utils/ast_helpers.py +13 -0
  9. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/file_cache.py +7 -3
  10. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/cache.py +3 -4
  11. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/article_view.py +3 -7
  12. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/context_view.py +3 -7
  13. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/flow_constants.py +13 -0
  14. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/flow_exporter.py +2 -10
  15. code2llm-0.5.119/code2llm/exporters/index_generator/__init__.py +73 -0
  16. code2llm-0.5.117/code2llm/exporters/index_generator.py → code2llm-0.5.119/code2llm/exporters/index_generator/renderer.py +168 -321
  17. code2llm-0.5.119/code2llm/exporters/index_generator/scanner.py +116 -0
  18. code2llm-0.5.119/code2llm/exporters/index_generator.py +29 -0
  19. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/map_exporter.py +2 -9
  20. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon_view.py +3 -7
  21. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/__init__.py +3 -0
  22. code2llm-0.5.119/code2llm/generators/_utils.py +15 -0
  23. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/llm_flow.py +2 -9
  24. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/llm_task.py +2 -9
  25. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/__init__.py +1 -1
  26. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/PKG-INFO +2 -2
  27. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/SOURCES.txt +4 -0
  28. {code2llm-0.5.117 → code2llm-0.5.119}/pyproject.toml +1 -1
  29. {code2llm-0.5.117 → code2llm-0.5.119}/setup.py +1 -1
  30. {code2llm-0.5.117 → code2llm-0.5.119}/LICENSE +0 -0
  31. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/__main__.py +0 -0
  32. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/__init__.py +0 -0
  33. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/coupling.py +0 -0
  34. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/dfg.py +0 -0
  35. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/pipeline_detector.py +0 -0
  36. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/side_effects.py +0 -0
  37. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/smells.py +0 -0
  38. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/analysis/type_inference.py +0 -0
  39. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/api.py +0 -0
  40. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli.py +0 -0
  41. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_analysis.py +0 -0
  42. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_commands.py +0 -0
  43. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/__init__.py +0 -0
  44. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/code2logic.py +0 -0
  45. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/formats.py +0 -0
  46. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/orchestrator.py +0 -0
  47. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_exports/prompt.py +0 -0
  48. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/cli_parser.py +0 -0
  49. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/__init__.py +0 -0
  50. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/analyzer.py +0 -0
  51. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/ast_registry.py +0 -0
  52. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/config.py +0 -0
  53. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/export_pipeline.py +0 -0
  54. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/file_analyzer.py +0 -0
  55. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/file_filter.py +0 -0
  56. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/gitignore.py +0 -0
  57. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/incremental.py +0 -0
  58. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/__init__.py +0 -0
  59. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/base.py +0 -0
  60. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/cpp.py +0 -0
  61. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/csharp.py +0 -0
  62. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/generic.py +0 -0
  63. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/go_lang.py +0 -0
  64. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/java.py +0 -0
  65. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/php.py +0 -0
  66. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/ruby.py +0 -0
  67. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/rust.py +0 -0
  68. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/ts_extractors.py +0 -0
  69. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/ts_parser.py +0 -0
  70. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/lang/typescript.py +0 -0
  71. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/large_repo.py +0 -0
  72. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/models.py +0 -0
  73. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/persistent_cache.py +0 -0
  74. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/refactoring.py +0 -0
  75. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/repo_files.py +0 -0
  76. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/__init__.py +0 -0
  77. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/incremental.py +0 -0
  78. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/prioritizer.py +0 -0
  79. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/scanner.py +0 -0
  80. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming/strategies.py +0 -0
  81. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/streaming_analyzer.py +0 -0
  82. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/core/toon_size_manager.py +0 -0
  83. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/__init__.py +0 -0
  84. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/base.py +0 -0
  85. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/context_exporter.py +0 -0
  86. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/evolution_exporter.py +0 -0
  87. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/flow_renderer.py +0 -0
  88. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/html_dashboard.py +0 -0
  89. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/json_exporter.py +0 -0
  90. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/llm_exporter.py +0 -0
  91. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/mermaid_exporter.py +0 -0
  92. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  93. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/__init__.py +0 -0
  94. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/constants.py +0 -0
  95. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/core.py +0 -0
  96. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/evolution.py +0 -0
  97. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/health.py +0 -0
  98. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  99. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml/modules.py +0 -0
  100. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/project_yaml_exporter.py +0 -0
  101. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/readme_exporter.py +0 -0
  102. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/report_generators.py +0 -0
  103. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/__init__.py +0 -0
  104. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/helpers.py +0 -0
  105. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/metrics.py +0 -0
  106. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/module_detail.py +0 -0
  107. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon/renderer.py +0 -0
  108. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/toon.py +0 -0
  109. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/validate_project.py +0 -0
  110. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/exporters/yaml_exporter.py +0 -0
  111. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/generators/mermaid.py +0 -0
  112. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/config.py +0 -0
  113. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/entity_resolution.py +0 -0
  114. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/intent_matching.py +0 -0
  115. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/normalization.py +0 -0
  116. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/nlp/pipeline.py +0 -0
  117. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/patterns/__init__.py +0 -0
  118. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/patterns/detector.py +0 -0
  119. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/refactor/__init__.py +0 -0
  120. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm/refactor/prompt_engine.py +0 -0
  121. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/dependency_links.txt +0 -0
  122. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/entry_points.txt +0 -0
  123. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/requires.txt +0 -0
  124. {code2llm-0.5.117 → code2llm-0.5.119}/code2llm.egg-info/top_level.txt +0 -0
  125. {code2llm-0.5.117 → code2llm-0.5.119}/setup.cfg +0 -0
  126. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_advanced_analysis.py +0 -0
  127. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_analyzer.py +0 -0
  128. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_calls_toon_export.py +0 -0
  129. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_deep_analysis.py +0 -0
  130. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_edge_cases.py +0 -0
  131. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_flow_exporter.py +0 -0
  132. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_format_quality.py +0 -0
  133. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_multilanguage_e2e.py +0 -0
  134. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_nlp_pipeline.py +0 -0
  135. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_nonpython_cc_calls.py +0 -0
  136. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_persistent_cache.py +0 -0
  137. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_pipeline_detector.py +0 -0
  138. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_project_toon_export.py +0 -0
  139. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_prompt_engine.py +0 -0
  140. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_prompt_txt.py +0 -0
  141. {code2llm-0.5.117 → code2llm-0.5.119}/tests/test_refactoring_engine.py +0 -0
  142. {code2llm-0.5.117 → code2llm-0.5.119}/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.117
3
+ Version: 0.5.119
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
@@ -67,7 +67,7 @@ Dynamic: requires-python
67
67
 
68
68
  ## AI Cost Tracking
69
69
 
70
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.117-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.119-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
71
71
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
73
  - 🤖 **LLM usage:** $7.5000 (166 commits)
@@ -3,7 +3,7 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.117-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.119-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
7
  ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
9
  - 🤖 **LLM usage:** $7.5000 (166 commits)
@@ -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.117"
11
+ __version__ = "0.5.119"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -5,7 +5,7 @@ from typing import Optional, Set, List, Dict
5
5
 
6
6
  from code2llm.core.config import Config
7
7
  from code2llm.core.models import AnalysisResult, FlowEdge
8
- from code2llm.analysis.utils import ast_unparse
8
+ from code2llm.analysis.utils import ast_unparse, qualified_name
9
9
 
10
10
 
11
11
  class CallGraphExtractor(ast.NodeVisitor):
@@ -140,12 +140,7 @@ class CallGraphExtractor(ast.NodeVisitor):
140
140
  self.generic_visit(node)
141
141
 
142
142
  def _qualified_name(self, name: str) -> str:
143
- """Get fully qualified name."""
144
- parts = [self.module_name]
145
- if self.class_stack:
146
- parts.append(self.class_stack[-1])
147
- parts.append(name)
148
- return '.'.join(parts)
143
+ return qualified_name(self.module_name, self.class_stack, name)
149
144
 
150
145
  def _resolve_call(self, node: ast.AST) -> Optional[str]:
151
146
  """Resolve a call to its full name."""
@@ -6,7 +6,7 @@ from typing import Optional
6
6
 
7
7
  from code2llm.core.config import Config
8
8
  from code2llm.core.models import AnalysisResult, FlowNode, FlowEdge, FunctionInfo
9
- from code2llm.analysis.utils import ast_unparse
9
+ from code2llm.analysis.utils import ast_unparse, qualified_name
10
10
 
11
11
 
12
12
  class CFGExtractor(ast.NodeVisitor):
@@ -261,12 +261,7 @@ class CFGExtractor(ast.NodeVisitor):
261
261
  self.generic_visit(node)
262
262
 
263
263
  def _qualified_name(self, name: str) -> str:
264
- """Get fully qualified name."""
265
- parts = [self.module_name]
266
- if self.class_stack:
267
- parts.append(self.class_stack[-1])
268
- parts.append(name)
269
- return '.'.join(parts)
264
+ return qualified_name(self.module_name, self.class_stack, name)
270
265
 
271
266
  def _extract_condition(self, node: ast.AST) -> str:
272
267
  """Extract condition as string."""
@@ -1,6 +1,12 @@
1
- """Data Analysis logic for code2llm - extracted from YAMLExporter."""
1
+ """Data Analysis logic for code2llm - split into focused analyzers.
2
2
 
3
- from typing import Any, Dict, List
3
+ This module provides three analyzers:
4
+ - DataFlowAnalyzer: data pipelines, state patterns, dependencies, event flows
5
+ - OptimizationAdvisor: data types, optimization opportunities, process patterns
6
+ - DataAnalyzer: facade combining both analyzers (backward compatibility)
7
+ """
8
+
9
+ from typing import Any, Dict, List, Tuple
4
10
  from code2llm.core.models import AnalysisResult
5
11
 
6
12
 
@@ -10,7 +16,7 @@ _OUTPUT_INDICATORS = ['serialize', 'format', 'write', 'save', 'send', 'output',
10
16
  _MAX_PIPELINES = 15
11
17
 
12
18
 
13
- def _categorize_functions(result: 'AnalysisResult'):
19
+ def _categorize_functions(result: 'AnalysisResult') -> Tuple[list, list, list]:
14
20
  """Categorize functions into input/transform/output based on name patterns."""
15
21
  input_funcs, transform_funcs, output_funcs = [], [], []
16
22
  for func_name, func in result.functions.items():
@@ -284,3 +290,81 @@ class DataAnalyzer:
284
290
  opt['hub_optimization'].append({'function': hub['id'], 'connections': hub['in_degree'] + hub['out_degree'], 'optimization_type': 'split' if hub['out_degree'] > 10 else 'cache'})
285
291
  opt['potential_score'] = (len(opt['type_consolidation']) * 10 + len(opt['process_consolidation']) * 15 + len(opt['hub_optimization']) * 5) / 100.0
286
292
  return opt
293
+
294
+
295
+ # ---------------------------------------------------------------------------
296
+ # New focused analyzer classes (Step 5 refactoring)
297
+ # ---------------------------------------------------------------------------
298
+
299
+ class DataFlowAnalyzer:
300
+ """Analyze data flows: pipelines, state patterns, dependencies, and event flows.
301
+
302
+ Extracted from DataAnalyzer to provide focused data flow analysis.
303
+ """
304
+
305
+ def analyze(self, result: AnalysisResult) -> Dict[str, Any]:
306
+ """Perform complete data flow analysis."""
307
+ return {
308
+ 'data_pipelines': self.find_data_pipelines(result),
309
+ 'state_patterns': self.find_state_patterns(result),
310
+ 'data_dependencies': self.find_data_dependencies(result),
311
+ 'event_flows': self.find_event_flows(result),
312
+ }
313
+
314
+ def find_data_pipelines(self, result: AnalysisResult) -> list:
315
+ """Find data transformation pipelines (wrapper for backward compat)."""
316
+ # Delegate to the module-level helper via DataAnalyzer instance
317
+ return DataAnalyzer()._find_data_pipelines(result)
318
+
319
+ def find_state_patterns(self, result: AnalysisResult) -> list:
320
+ """Find state management patterns."""
321
+ return DataAnalyzer()._find_state_patterns(result)
322
+
323
+ def find_data_dependencies(self, result: AnalysisResult) -> list:
324
+ """Find cross-module data dependencies."""
325
+ return DataAnalyzer()._find_data_dependencies(result)
326
+
327
+ def find_event_flows(self, result: AnalysisResult) -> list:
328
+ """Find event-driven patterns."""
329
+ return DataAnalyzer()._find_event_flows(result)
330
+
331
+
332
+ class OptimizationAdvisor:
333
+ """Analyze optimization opportunities: data types and process patterns.
334
+
335
+ Extracted from DataAnalyzer to provide focused optimization analysis.
336
+ """
337
+
338
+ def analyze(self, result: AnalysisResult) -> Dict[str, Any]:
339
+ """Perform complete optimization analysis."""
340
+ data_types = self.analyze_data_types(result)
341
+ data_flow_graph = self.build_data_flow_graph(result)
342
+ process_patterns = self.identify_process_patterns(result)
343
+ optimization_analysis = self.analyze_optimization_opportunities(
344
+ result, data_types, data_flow_graph
345
+ )
346
+
347
+ return {
348
+ 'data_types': data_types,
349
+ 'data_flow_graph': data_flow_graph,
350
+ 'process_patterns': process_patterns,
351
+ 'optimization_analysis': optimization_analysis,
352
+ }
353
+
354
+ def analyze_data_types(self, result: AnalysisResult) -> list:
355
+ """Analyze data types and usage."""
356
+ return DataAnalyzer()._analyze_data_types(result)
357
+
358
+ def build_data_flow_graph(self, result: AnalysisResult) -> dict:
359
+ """Build data flow graph from function relationships."""
360
+ return DataAnalyzer()._build_data_flow_graph(result)
361
+
362
+ def identify_process_patterns(self, result: AnalysisResult) -> list:
363
+ """Identify common data processing patterns."""
364
+ return DataAnalyzer()._identify_process_patterns(result)
365
+
366
+ def analyze_optimization_opportunities(
367
+ self, result: AnalysisResult, data_types: list, dfg: dict
368
+ ) -> dict:
369
+ """Analyze optimization opportunities in data handling."""
370
+ return DataAnalyzer()._analyze_optimization_opportunities(result, data_types, dfg)
@@ -1,5 +1,5 @@
1
1
  """Shared AST utilities for analysis modules."""
2
2
 
3
- from .ast_helpers import get_ast, find_function_node, expr_to_str, ast_unparse
3
+ from .ast_helpers import get_ast, find_function_node, expr_to_str, ast_unparse, qualified_name
4
4
 
5
- __all__ = ["get_ast", "find_function_node", "expr_to_str", "ast_unparse"]
5
+ __all__ = ["get_ast", "find_function_node", "expr_to_str", "ast_unparse", "qualified_name"]
@@ -57,6 +57,19 @@ def ast_unparse(node: Optional[ast.AST], default_none: str = "None") -> str:
57
57
  return str(node)
58
58
 
59
59
 
60
+ def qualified_name(module_name: str, class_stack: list, name: str) -> str:
61
+ """Build a fully-qualified dotted name from module, optional class scope, and name.
62
+
63
+ Shared replacement for the identical ``_qualified_name`` methods in
64
+ ``CallGraphExtractor`` and ``CFGExtractor``.
65
+ """
66
+ parts = [module_name]
67
+ if class_stack:
68
+ parts.append(class_stack[-1])
69
+ parts.append(name)
70
+ return '.'.join(parts)
71
+
72
+
60
73
  def expr_to_str(node: ast.expr) -> Optional[str]:
61
74
  """Convert an AST expression to a dotted string (for call-name extraction).
62
75
 
@@ -9,6 +9,12 @@ from typing import Any, Optional, Tuple
9
9
  import ast
10
10
 
11
11
 
12
+ def make_cache_key(file_path: str, content: str) -> str:
13
+ """Generate a cache key from file stem and MD5 of content."""
14
+ content_hash = hashlib.md5(content.encode()).hexdigest()[:16]
15
+ return f"{Path(file_path).stem}_{content_hash}"
16
+
17
+
12
18
  class FileCache:
13
19
  """Cache for parsed AST files."""
14
20
 
@@ -26,9 +32,7 @@ class FileCache:
26
32
  return f"{Path(file_path).stem}_unknown"
27
33
 
28
34
  def _get_cache_key(self, file_path: str, content: str) -> str:
29
- """Generate cache key from file path and content hash (legacy)."""
30
- content_hash = hashlib.md5(content.encode()).hexdigest()[:16]
31
- return f"{Path(file_path).stem}_{content_hash}"
35
+ return make_cache_key(file_path, content)
32
36
 
33
37
  def _get_cache_path(self, cache_key: str) -> Path:
34
38
  """Get cache file path."""
@@ -1,10 +1,11 @@
1
1
  """Memory-efficient streaming cache with LRU eviction."""
2
2
 
3
3
  import ast
4
- import hashlib
5
4
  from pathlib import Path
6
5
  from typing import Dict, List, Optional, Tuple
7
6
 
7
+ from code2llm.core.file_cache import make_cache_key
8
+
8
9
 
9
10
  class StreamingFileCache:
10
11
  """Memory-efficient cache with LRU eviction."""
@@ -17,9 +18,7 @@ class StreamingFileCache:
17
18
  self._access_order: List[str] = []
18
19
 
19
20
  def _get_cache_key(self, file_path: str, content: str) -> str:
20
- """Generate cache key."""
21
- content_hash = hashlib.md5(content.encode()).hexdigest()[:16]
22
- return f"{Path(file_path).stem}_{content_hash}"
21
+ return make_cache_key(file_path, content)
23
22
 
24
23
  def _evict_if_needed(self) -> None:
25
24
  """Evict oldest entries if cache is full."""
@@ -7,15 +7,11 @@ from datetime import datetime
7
7
  from pathlib import Path
8
8
  from typing import Any, Dict, List
9
9
 
10
+ from code2llm.exporters.base import ViewGeneratorMixin
10
11
 
11
- class ArticleViewGenerator:
12
- """Generate status.md — publishable project health article."""
13
12
 
14
- def generate(self, data: Dict[str, Any], output_path: str) -> None:
15
- lines = self._render(data)
16
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
17
- with open(output_path, "w", encoding="utf-8") as f:
18
- f.write("\n".join(lines) + "\n")
13
+ class ArticleViewGenerator(ViewGeneratorMixin):
14
+ """Generate status.md — publishable project health article."""
19
15
 
20
16
  def _render(self, data: Dict[str, Any]) -> List[str]:
21
17
  proj = data.get("project", {})
@@ -6,15 +6,11 @@ Generates context.md from project.yaml data.
6
6
  from pathlib import Path
7
7
  from typing import Any, Dict, List
8
8
 
9
+ from code2llm.exporters.base import ViewGeneratorMixin
9
10
 
10
- class ContextViewGenerator:
11
- """Generate context.md from project.yaml data."""
12
11
 
13
- def generate(self, data: Dict[str, Any], output_path: str) -> None:
14
- lines = self._render(data)
15
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
16
- with open(output_path, "w", encoding="utf-8") as f:
17
- f.write("\n".join(lines) + "\n")
12
+ class ContextViewGenerator(ViewGeneratorMixin):
13
+ """Generate context.md from project.yaml data."""
18
14
 
19
15
  def _render(self, data: Dict[str, Any]) -> List[str]:
20
16
  proj = data.get("project", {})
@@ -15,6 +15,19 @@ EXCLUDE_PATTERNS = {
15
15
  'dist', 'build', 'egg-info', '.tox', '.mypy_cache',
16
16
  }
17
17
 
18
+ def is_excluded_path(path: str) -> bool:
19
+ """Return True if *path* matches any standard exclusion pattern (venv, cache, etc.)."""
20
+ if not path:
21
+ return False
22
+ path_lower = path.lower().replace('\\', '/')
23
+ for pattern in EXCLUDE_PATTERNS:
24
+ if f'/{pattern}/' in path_lower or path_lower.startswith(f'{pattern}/'):
25
+ return True
26
+ if pattern in path_lower.split('/'):
27
+ return True
28
+ return False
29
+
30
+
18
31
  # Rekomendacje podziału typów hub: typ -> sugerowane pod-interfejsy
19
32
  HUB_SPLIT_RECOMMENDATIONS = {
20
33
  "AnalysisResult": [
@@ -17,7 +17,7 @@ from pathlib import Path
17
17
  from typing import Any, Dict, List, Optional, Set, Tuple
18
18
 
19
19
  from .base import BaseExporter, export_format
20
- from .flow_constants import CC_HIGH, FAN_OUT_THRESHOLD, EXCLUDE_PATTERNS, HUB_SPLIT_RECOMMENDATIONS, HUB_TYPE_THRESHOLD
20
+ from .flow_constants import CC_HIGH, FAN_OUT_THRESHOLD, EXCLUDE_PATTERNS, HUB_SPLIT_RECOMMENDATIONS, HUB_TYPE_THRESHOLD, is_excluded_path
21
21
  from .flow_renderer import FlowRenderer
22
22
  from code2llm.core.models import (
23
23
  AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo, FlowNode
@@ -382,12 +382,4 @@ class FlowExporter(BaseExporter):
382
382
  # utility helpers
383
383
  # ------------------------------------------------------------------
384
384
  def _is_excluded(self, path: str) -> bool:
385
- if not path:
386
- return False
387
- path_lower = path.lower().replace('\\', '/')
388
- for pattern in EXCLUDE_PATTERNS:
389
- if f'/{pattern}/' in path_lower or path_lower.startswith(f'{pattern}/'):
390
- return True
391
- if pattern in path_lower.split('/'):
392
- return True
393
- return False
385
+ return is_excluded_path(path)
@@ -0,0 +1,73 @@
1
+ """Index HTML Generator — web-based file browser for all generated outputs.
2
+
3
+ Generates index.html that provides a GitHub Pages-ready interface
4
+ for browsing all generated analysis files (toon, md, yaml, json, etc.)
5
+
6
+ This package provides three components:
7
+ - FileScanner: scans output directory and collects file metadata
8
+ - HTMLRenderer: generates HTML with CSS and JavaScript
9
+ - IndexHTMLGenerator: facade combining scanner and renderer
10
+ """
11
+
12
+ from pathlib import Path
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ from .scanner import FileScanner, get_file_types, get_default_file_info, FILE_TYPES
16
+ from .renderer import HTMLRenderer
17
+
18
+
19
+ class IndexHTMLGenerator:
20
+ """Generate index.html for browsing all generated files.
21
+
22
+ This is a facade class that combines FileScanner and HTMLRenderer
23
+ to provide a simple interface for generating the index file.
24
+ """
25
+
26
+ # Backward compatibility: expose FILE_TYPES at class level
27
+ FILE_TYPES = FILE_TYPES
28
+
29
+ def __init__(self, output_dir: Path):
30
+ self.output_dir = Path(output_dir).resolve()
31
+ self._scanner = FileScanner(self.output_dir)
32
+ self._renderer = HTMLRenderer()
33
+
34
+ def generate(self) -> Path:
35
+ """Generate index.html in the output directory."""
36
+ files = self._scanner.scan()
37
+ html = self._renderer.render(files)
38
+ index_path = self.output_dir / 'index.html'
39
+ with open(index_path, 'w', encoding='utf-8') as f:
40
+ f.write(html)
41
+ return index_path
42
+
43
+ def scan_files(self) -> List[Dict[str, Any]]:
44
+ """Scan and return file metadata without generating HTML."""
45
+ return self._scanner.scan()
46
+
47
+ def render_html(self, files: List[Dict[str, Any]]) -> str:
48
+ """Render HTML from pre-scanned file list."""
49
+ return self._renderer.render(files)
50
+
51
+
52
+ # Convenience function for direct usage
53
+ def generate_index_html(output_dir: Path) -> Path:
54
+ """Generate index.html in the specified directory.
55
+
56
+ Args:
57
+ output_dir: Directory containing generated analysis files
58
+
59
+ Returns:
60
+ Path to the generated index.html file
61
+ """
62
+ return IndexHTMLGenerator(output_dir).generate()
63
+
64
+
65
+ __all__ = [
66
+ 'IndexHTMLGenerator',
67
+ 'FileScanner',
68
+ 'HTMLRenderer',
69
+ 'generate_index_html',
70
+ 'get_file_types',
71
+ 'get_default_file_info',
72
+ 'FILE_TYPES',
73
+ ]