code2llm 0.5.117__tar.gz → 0.5.118__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 (139) hide show
  1. {code2llm-0.5.117 → code2llm-0.5.118}/PKG-INFO +2 -2
  2. {code2llm-0.5.117 → code2llm-0.5.118}/README.md +1 -1
  3. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/call_graph.py +2 -7
  5. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/cfg.py +2 -7
  6. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/data_analysis.py +87 -3
  7. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/utils/__init__.py +2 -2
  8. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/utils/ast_helpers.py +13 -0
  9. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/file_cache.py +7 -3
  10. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming/cache.py +3 -4
  11. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/article_view.py +3 -7
  12. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/context_view.py +3 -7
  13. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/flow_constants.py +13 -0
  14. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/flow_exporter.py +2 -10
  15. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/map_exporter.py +2 -9
  16. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon_view.py +3 -7
  17. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/generators/__init__.py +3 -0
  18. code2llm-0.5.118/code2llm/generators/_utils.py +15 -0
  19. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/generators/llm_flow.py +2 -9
  20. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/generators/llm_task.py +2 -9
  21. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/nlp/__init__.py +1 -1
  22. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm.egg-info/PKG-INFO +2 -2
  23. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm.egg-info/SOURCES.txt +1 -0
  24. {code2llm-0.5.117 → code2llm-0.5.118}/pyproject.toml +1 -1
  25. {code2llm-0.5.117 → code2llm-0.5.118}/setup.py +1 -1
  26. {code2llm-0.5.117 → code2llm-0.5.118}/LICENSE +0 -0
  27. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/__main__.py +0 -0
  28. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/__init__.py +0 -0
  29. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/coupling.py +0 -0
  30. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/dfg.py +0 -0
  31. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/pipeline_detector.py +0 -0
  32. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/side_effects.py +0 -0
  33. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/smells.py +0 -0
  34. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/analysis/type_inference.py +0 -0
  35. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/api.py +0 -0
  36. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli.py +0 -0
  37. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_analysis.py +0 -0
  38. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_commands.py +0 -0
  39. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_exports/__init__.py +0 -0
  40. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_exports/code2logic.py +0 -0
  41. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_exports/formats.py +0 -0
  42. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_exports/orchestrator.py +0 -0
  43. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_exports/prompt.py +0 -0
  44. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/cli_parser.py +0 -0
  45. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/__init__.py +0 -0
  46. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/analyzer.py +0 -0
  47. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/ast_registry.py +0 -0
  48. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/config.py +0 -0
  49. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/export_pipeline.py +0 -0
  50. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/file_analyzer.py +0 -0
  51. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/file_filter.py +0 -0
  52. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/gitignore.py +0 -0
  53. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/incremental.py +0 -0
  54. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/__init__.py +0 -0
  55. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/base.py +0 -0
  56. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/cpp.py +0 -0
  57. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/csharp.py +0 -0
  58. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/generic.py +0 -0
  59. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/go_lang.py +0 -0
  60. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/java.py +0 -0
  61. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/php.py +0 -0
  62. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/ruby.py +0 -0
  63. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/rust.py +0 -0
  64. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/ts_extractors.py +0 -0
  65. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/ts_parser.py +0 -0
  66. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/lang/typescript.py +0 -0
  67. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/large_repo.py +0 -0
  68. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/models.py +0 -0
  69. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/persistent_cache.py +0 -0
  70. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/refactoring.py +0 -0
  71. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/repo_files.py +0 -0
  72. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming/__init__.py +0 -0
  73. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming/incremental.py +0 -0
  74. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming/prioritizer.py +0 -0
  75. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming/scanner.py +0 -0
  76. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming/strategies.py +0 -0
  77. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/streaming_analyzer.py +0 -0
  78. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/core/toon_size_manager.py +0 -0
  79. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/__init__.py +0 -0
  80. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/base.py +0 -0
  81. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/context_exporter.py +0 -0
  82. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/evolution_exporter.py +0 -0
  83. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/flow_renderer.py +0 -0
  84. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/html_dashboard.py +0 -0
  85. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/index_generator.py +0 -0
  86. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/json_exporter.py +0 -0
  87. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/llm_exporter.py +0 -0
  88. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/mermaid_exporter.py +0 -0
  89. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  90. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/__init__.py +0 -0
  91. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/constants.py +0 -0
  92. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/core.py +0 -0
  93. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/evolution.py +0 -0
  94. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/health.py +0 -0
  95. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  96. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml/modules.py +0 -0
  97. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/project_yaml_exporter.py +0 -0
  98. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/readme_exporter.py +0 -0
  99. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/report_generators.py +0 -0
  100. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon/__init__.py +0 -0
  101. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon/helpers.py +0 -0
  102. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon/metrics.py +0 -0
  103. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon/module_detail.py +0 -0
  104. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon/renderer.py +0 -0
  105. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/toon.py +0 -0
  106. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/validate_project.py +0 -0
  107. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/exporters/yaml_exporter.py +0 -0
  108. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/generators/mermaid.py +0 -0
  109. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/nlp/config.py +0 -0
  110. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/nlp/entity_resolution.py +0 -0
  111. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/nlp/intent_matching.py +0 -0
  112. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/nlp/normalization.py +0 -0
  113. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/nlp/pipeline.py +0 -0
  114. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/patterns/__init__.py +0 -0
  115. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/patterns/detector.py +0 -0
  116. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/refactor/__init__.py +0 -0
  117. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm/refactor/prompt_engine.py +0 -0
  118. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm.egg-info/dependency_links.txt +0 -0
  119. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm.egg-info/entry_points.txt +0 -0
  120. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm.egg-info/requires.txt +0 -0
  121. {code2llm-0.5.117 → code2llm-0.5.118}/code2llm.egg-info/top_level.txt +0 -0
  122. {code2llm-0.5.117 → code2llm-0.5.118}/setup.cfg +0 -0
  123. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_advanced_analysis.py +0 -0
  124. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_analyzer.py +0 -0
  125. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_calls_toon_export.py +0 -0
  126. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_deep_analysis.py +0 -0
  127. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_edge_cases.py +0 -0
  128. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_flow_exporter.py +0 -0
  129. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_format_quality.py +0 -0
  130. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_multilanguage_e2e.py +0 -0
  131. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_nlp_pipeline.py +0 -0
  132. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_nonpython_cc_calls.py +0 -0
  133. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_persistent_cache.py +0 -0
  134. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_pipeline_detector.py +0 -0
  135. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_project_toon_export.py +0 -0
  136. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_prompt_engine.py +0 -0
  137. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_prompt_txt.py +0 -0
  138. {code2llm-0.5.117 → code2llm-0.5.118}/tests/test_refactoring_engine.py +0 -0
  139. {code2llm-0.5.117 → code2llm-0.5.118}/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.118
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.118-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.118-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.118"
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)
@@ -16,6 +16,7 @@ from pathlib import Path
16
16
  from typing import Any, Dict, List, Optional, Set, Tuple
17
17
 
18
18
  from .base import BaseExporter, export_format
19
+ from .flow_constants import is_excluded_path
19
20
  from code2llm.core.models import AnalysisResult, FunctionInfo, ClassInfo, ModuleInfo
20
21
  from code2llm.core.config import LANGUAGE_EXTENSIONS
21
22
  from typing import Optional
@@ -315,15 +316,7 @@ class MapExporter(BaseExporter):
315
316
  return f"{fi.name}({args_str}){ret}"
316
317
 
317
318
  def _is_excluded(self, path: str) -> bool:
318
- if not path:
319
- return False
320
- path_lower = path.lower().replace('\\', '/')
321
- for pattern in EXCLUDE_PATTERNS:
322
- if f'/{pattern}/' in path_lower or path_lower.startswith(f'{pattern}/'):
323
- return True
324
- if pattern in path_lower.split('/'):
325
- return True
326
- return False
319
+ return is_excluded_path(path)
327
320
 
328
321
  def _rel_path(self, fpath: str, project_path: str) -> str:
329
322
  if not project_path or not fpath:
@@ -7,6 +7,8 @@ from collections import defaultdict
7
7
  from pathlib import Path
8
8
  from typing import Any, Dict, List
9
9
 
10
+ from code2llm.exporters.base import ViewGeneratorMixin
11
+
10
12
  # Language detection from file extensions
11
13
  _LANG_EXT_MAP = {
12
14
  '.py': 'python', '.ts': 'typescript', '.tsx': 'typescript',
@@ -21,15 +23,9 @@ _LANG_EXT_MAP = {
21
23
  }
22
24
 
23
25
 
24
- class ToonViewGenerator:
26
+ class ToonViewGenerator(ViewGeneratorMixin):
25
27
  """Generate project.toon.yaml from project.yaml data."""
26
28
 
27
- def generate(self, data: Dict[str, Any], output_path: str) -> None:
28
- lines = self._render(data)
29
- Path(output_path).parent.mkdir(parents=True, exist_ok=True)
30
- with open(output_path, "w", encoding="utf-8") as f:
31
- f.write("\n".join(lines) + "\n")
32
-
33
29
  def _render(self, data: Dict[str, Any]) -> List[str]:
34
30
  proj = data.get("project", {})
35
31
  health = data.get("health", {})
@@ -5,8 +5,11 @@ Generators for LLM flow summaries, task breakdowns, and Mermaid diagrams.
5
5
 
6
6
  from .llm_flow import main as llm_flow_main
7
7
  from .mermaid import generate_pngs
8
+ from ._utils import dump_yaml
9
+
8
10
 
9
11
  __all__ = [
10
12
  'llm_flow_main',
11
13
  'generate_pngs',
14
+ 'dump_yaml',
12
15
  ]
@@ -0,0 +1,15 @@
1
+ """Shared utilities for generators — avoids circular imports."""
2
+
3
+ import yaml
4
+ from typing import Any, Dict
5
+
6
+
7
+ def dump_yaml(data: Dict[str, Any]) -> str:
8
+ """Shared YAML serialiser (sort_keys=False, unicode, width=100)."""
9
+ return yaml.safe_dump(
10
+ data,
11
+ sort_keys=False,
12
+ allow_unicode=True,
13
+ width=100,
14
+ default_flow_style=False,
15
+ )
@@ -8,6 +8,8 @@ from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
8
8
 
9
9
  import yaml
10
10
 
11
+ from ._utils import dump_yaml
12
+
11
13
 
12
14
  _FUNC_LABEL_PREFIX = "FUNC:"
13
15
  _CALL_LABEL_PREFIX = "CALL "
@@ -402,15 +404,6 @@ def render_llm_flow_md(flow: Dict[str, Any]) -> str:
402
404
  return "\n".join(lines).rstrip() + "\n"
403
405
 
404
406
 
405
- def dump_yaml(data: Dict[str, Any]) -> str:
406
- return yaml.safe_dump(
407
- data,
408
- sort_keys=False,
409
- allow_unicode=True,
410
- width=100,
411
- default_flow_style=False,
412
- )
413
-
414
407
 
415
408
  def create_parser() -> argparse.ArgumentParser:
416
409
  p = argparse.ArgumentParser(
@@ -5,6 +5,8 @@ from typing import Any, Dict, List, Optional, Tuple
5
5
 
6
6
  import yaml
7
7
 
8
+ from ._utils import dump_yaml
9
+
8
10
 
9
11
  def _strip_bom(text: str) -> str:
10
12
  return text[1:] if text.startswith("\ufeff") else text
@@ -230,15 +232,6 @@ def load_input(path: Path) -> Dict[str, Any]:
230
232
  return parse_llm_task_text(raw)
231
233
 
232
234
 
233
- def dump_yaml(data: Dict[str, Any]) -> str:
234
- return yaml.safe_dump(
235
- data,
236
- sort_keys=False,
237
- allow_unicode=True,
238
- width=100,
239
- default_flow_style=False,
240
- )
241
-
242
235
 
243
236
  def create_parser() -> argparse.ArgumentParser:
244
237
  p = argparse.ArgumentParser(
@@ -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.117"
7
+ __version__ = "0.5.118"
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.117
3
+ Version: 0.5.118
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.118-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)
@@ -104,6 +104,7 @@ code2llm/exporters/toon/metrics.py
104
104
  code2llm/exporters/toon/module_detail.py
105
105
  code2llm/exporters/toon/renderer.py
106
106
  code2llm/generators/__init__.py
107
+ code2llm/generators/_utils.py
107
108
  code2llm/generators/llm_flow.py
108
109
  code2llm/generators/llm_task.py
109
110
  code2llm/generators/mermaid.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.117"
7
+ version = "0.5.118"
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"
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
  import os
5
5
 
6
6
  # Read version
7
- version = "0.5.116"
7
+ version = "0.5.117"
8
8
 
9
9
  # Read long description
10
10
  def read_readme():
File without changes
File without changes
File without changes
File without changes