code2llm 0.5.160__tar.gz → 0.5.162__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 (210) hide show
  1. {code2llm-0.5.160/code2llm.egg-info → code2llm-0.5.162}/PKG-INFO +9 -16
  2. {code2llm-0.5.160 → code2llm-0.5.162}/README.md +8 -15
  3. code2llm-0.5.162/VERSION +1 -0
  4. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/_data_impl.py +121 -89
  6. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/call_graph.py +1 -0
  7. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/cfg.py +2 -3
  8. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/dfg.py +1 -0
  9. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/pipeline_detector.py +19 -27
  10. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/side_effects.py +21 -20
  11. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/smells.py +32 -7
  12. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/type_inference.py +6 -0
  13. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_analysis.py +42 -67
  14. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_commands.py +51 -65
  15. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/formats.py +48 -34
  16. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator.py +18 -20
  17. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/prompt.py +91 -35
  18. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_parser.py +1 -1
  19. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/__init__.py +1 -4
  20. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/analyzer.py +68 -40
  21. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/export_pipeline.py +4 -0
  22. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/file_analyzer.py +61 -39
  23. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/file_cache.py +5 -4
  24. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/__init__.py +1 -0
  25. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/_c_parser.py +41 -13
  26. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/base.py +3 -17
  27. code2llm-0.5.162/code2llm/core/lang/generic.py +71 -0
  28. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/ruby.py +65 -79
  29. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/ts_parser.py +1 -1
  30. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/typescript.py +4 -4
  31. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/large_repo.py +8 -0
  32. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/persistent_cache.py +4 -0
  33. code2llm-0.5.162/code2llm/core/streaming/__init__.py +7 -0
  34. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/scanner.py +41 -42
  35. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming_analyzer.py +42 -55
  36. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/toon_size_manager.py +3 -1
  37. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/context_exporter.py +20 -1
  38. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/context_view.py +12 -10
  39. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/dashboard_data.py +4 -4
  40. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/dashboard_renderer.py +10 -10
  41. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/yaml_export.py +30 -56
  42. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/flow_exporter.py +5 -0
  43. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/flow_renderer.py +32 -35
  44. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/html_dashboard.py +1 -0
  45. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator/renderer.py +9 -9
  46. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/header.py +2 -2
  47. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/yaml_export.py +4 -1
  48. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid_flow_helpers.py +33 -11
  49. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/planfile_tickets.py +43 -1
  50. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/hotspots.py +1 -1
  51. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/modules.py +18 -23
  52. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/content.py +4 -4
  53. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/sections.py +6 -6
  54. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/report_generators.py +18 -10
  55. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/_render_section_helpers.py +9 -4
  56. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/helpers.py +1 -1
  57. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics_core.py +45 -51
  58. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics_health.py +1 -1
  59. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/renderer.py +1 -1
  60. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon_view.py +2 -1
  61. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/validate_project.py +14 -21
  62. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/yaml_exporter.py +10 -10
  63. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/analysis.py +31 -35
  64. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/utils.py +19 -7
  65. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow.py +0 -2
  66. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_task.py +79 -46
  67. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/fix.py +1 -0
  68. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/png.py +1 -1
  69. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/validation.py +19 -12
  70. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/__init__.py +1 -1
  71. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/entity_resolution.py +20 -34
  72. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/parsers/toon_parser.py +1 -1
  73. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/refactor/prompt_engine.py +46 -42
  74. {code2llm-0.5.160 → code2llm-0.5.162/code2llm.egg-info}/PKG-INFO +9 -16
  75. {code2llm-0.5.160 → code2llm-0.5.162}/pyproject.toml +1 -1
  76. code2llm-0.5.160/VERSION +0 -1
  77. code2llm-0.5.160/code2llm/core/lang/generic.py +0 -88
  78. code2llm-0.5.160/code2llm/core/streaming/__init__.py +0 -7
  79. {code2llm-0.5.160 → code2llm-0.5.162}/LICENSE +0 -0
  80. {code2llm-0.5.160 → code2llm-0.5.162}/MANIFEST.in +0 -0
  81. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/__main__.py +0 -0
  82. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/__init__.py +0 -0
  83. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/coupling.py +0 -0
  84. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/data_analysis.py +0 -0
  85. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/pipeline_classifier.py +0 -0
  86. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/pipeline_resolver.py +0 -0
  87. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/utils/__init__.py +0 -0
  88. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/utils/ast_helpers.py +0 -0
  89. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/api.py +0 -0
  90. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli.py +0 -0
  91. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/__init__.py +0 -0
  92. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/code2logic.py +0 -0
  93. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  94. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  95. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
  96. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/ast_registry.py +0 -0
  97. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/config.py +0 -0
  98. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/file_filter.py +0 -0
  99. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/gitignore.py +0 -0
  100. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/incremental.py +0 -0
  101. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/_calls.py +0 -0
  102. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/_complexity.py +0 -0
  103. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/cpp.py +0 -0
  104. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/csharp.py +0 -0
  105. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/go_lang.py +0 -0
  106. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/java.py +0 -0
  107. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/php.py +0 -0
  108. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/rust.py +0 -0
  109. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/ts_extractors.py +0 -0
  110. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/models.py +0 -0
  111. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/refactoring.py +0 -0
  112. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/repo_files.py +0 -0
  113. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/source_classifier.py +0 -0
  114. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/cache.py +0 -0
  115. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/incremental.py +0 -0
  116. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/prioritizer.py +0 -0
  117. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/strategies.py +0 -0
  118. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/__init__.py +0 -0
  119. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/article_view.py +0 -0
  120. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/base.py +0 -0
  121. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/__init__.py +0 -0
  122. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/computation.py +0 -0
  123. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/constants.py +0 -0
  124. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/exclusion.py +0 -0
  125. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/render.py +0 -0
  126. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution_exporter.py +0 -0
  127. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/flow_constants.py +0 -0
  128. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator/__init__.py +0 -0
  129. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator/scanner.py +0 -0
  130. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator.py +0 -0
  131. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/json_exporter.py +0 -0
  132. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/llm_exporter.py +0 -0
  133. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/__init__.py +0 -0
  134. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/alerts.py +0 -0
  135. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/details.py +0 -0
  136. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/module_list.py +0 -0
  137. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/utils.py +0 -0
  138. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map_exporter.py +0 -0
  139. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/__init__.py +0 -0
  140. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/calls.py +0 -0
  141. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/classic.py +0 -0
  142. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/compact.py +0 -0
  143. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  144. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  145. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/flow_full.py +0 -0
  146. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/utils.py +0 -0
  147. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid_exporter.py +0 -0
  148. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/__init__.py +0 -0
  149. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/constants.py +0 -0
  150. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/core.py +0 -0
  151. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/evolution.py +0 -0
  152. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/health.py +0 -0
  153. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml_exporter.py +0 -0
  154. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/__init__.py +0 -0
  155. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/files.py +0 -0
  156. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/insights.py +0 -0
  157. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme_exporter.py +0 -0
  158. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/__init__.py +0 -0
  159. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
  160. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/constants.py +0 -0
  161. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics.py +0 -0
  162. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
  163. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/module_detail.py +0 -0
  164. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon.py +0 -0
  165. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/__init__.py +0 -0
  166. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/_utils.py +0 -0
  167. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/__init__.py +0 -0
  168. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/cli.py +0 -0
  169. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/generator.py +0 -0
  170. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/nodes.py +0 -0
  171. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/parsing.py +0 -0
  172. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/__init__.py +0 -0
  173. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid.py +0 -0
  174. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/config.py +0 -0
  175. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/intent_matching.py +0 -0
  176. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/normalization.py +0 -0
  177. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/pipeline.py +0 -0
  178. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/patterns/__init__.py +0 -0
  179. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/patterns/detector.py +0 -0
  180. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/refactor/__init__.py +0 -0
  181. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/SOURCES.txt +0 -0
  182. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/dependency_links.txt +0 -0
  183. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/entry_points.txt +0 -0
  184. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/requires.txt +0 -0
  185. {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/top_level.txt +0 -0
  186. {code2llm-0.5.160 → code2llm-0.5.162}/setup.cfg +0 -0
  187. {code2llm-0.5.160 → code2llm-0.5.162}/setup.py +0 -0
  188. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_advanced_analysis.py +0 -0
  189. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_analyzer.py +0 -0
  190. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_cache_invalidation_e2e.py +0 -0
  191. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_calls_toon_export.py +0 -0
  192. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_declarative_collection.py +0 -0
  193. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_deep_analysis.py +0 -0
  194. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_edge_cases.py +0 -0
  195. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_export_cache_flags.py +0 -0
  196. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_file_analyzer_tagging.py +0 -0
  197. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_flow_exporter.py +0 -0
  198. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_format_quality.py +0 -0
  199. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_multilanguage_e2e.py +0 -0
  200. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_nlp_pipeline.py +0 -0
  201. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_nonpython_cc_calls.py +0 -0
  202. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_orchestrator_cache_mtime.py +0 -0
  203. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_persistent_cache.py +0 -0
  204. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_pipeline_detector.py +0 -0
  205. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_planfile_tickets_exporter.py +0 -0
  206. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_project_toon_export.py +0 -0
  207. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_prompt_engine.py +0 -0
  208. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_prompt_txt.py +0 -0
  209. {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_refactoring_engine.py +0 -0
  210. {code2llm-0.5.160 → code2llm-0.5.162}/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.160
3
+ Version: 0.5.162
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
@@ -66,11 +66,11 @@ Dynamic: requires-python
66
66
 
67
67
  ## AI Cost Tracking
68
68
 
69
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.160-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-81.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
69
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.162-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-85.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
71
71
 
72
- - 🤖 **LLM usage:** $7.5000 (221 commits)
73
- - 👤 **Human dev:** ~$8118 (81.2h @ $100/h, 30min dedup)
72
+ - 🤖 **LLM usage:** $7.5000 (233 commits)
73
+ - 👤 **Human dev:** ~$8506 (85.1h @ $100/h, 30min dedup)
74
74
 
75
75
  Generated on 2026-05-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
76
 
@@ -88,7 +88,6 @@ When you run `code2llm ./ -f all`, the following files are created:
88
88
 
89
89
  | File | Format | Purpose | Key Insights |
90
90
  |------|--------|---------|--------------|
91
- | `planfile-tickets.yaml` | **YAML** | **🎫 Koru-ready ticket feed** - Actionable planfile suggestions from code2llm findings | Import or apply tickets for autonomous execution |
92
91
  | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
93
92
  | `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
94
93
 
@@ -114,12 +113,6 @@ code2llm ./ -f toon
114
113
  # Generate all formats (what created these files)
115
114
  code2llm ./ -f all
116
115
 
117
- # Generate planfile suggestions and create executable tickets for Koru
118
- code2llm ./ -f all -o ./project --no-chunk --planfile-apply
119
-
120
- # Or import the generated manifest later
121
- planfile ticket import --from ./project/planfile-tickets.yaml --source code2llm
122
-
123
116
  # LLM-ready context only
124
117
  code2llm ./ -f context
125
118
  ```
@@ -413,10 +406,10 @@ code2llm ./ -f yaml --separate-orphans
413
406
  ---
414
407
 
415
408
  **Generated by**: `code2llm ./ -f all --readme`
416
- **Analysis Date**: 2026-05-06
417
- **Total Functions**: 3800
418
- **Total Classes**: 259
419
- **Modules**: 497
409
+ **Analysis Date**: 2026-05-25
410
+ **Total Functions**: 1312
411
+ **Total Classes**: 143
412
+ **Modules**: 427
420
413
 
421
414
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
422
415
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## AI Cost Tracking
4
4
 
5
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.160-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
6
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-81.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
5
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.162-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
6
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-85.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
7
7
 
8
- - 🤖 **LLM usage:** $7.5000 (221 commits)
9
- - 👤 **Human dev:** ~$8118 (81.2h @ $100/h, 30min dedup)
8
+ - 🤖 **LLM usage:** $7.5000 (233 commits)
9
+ - 👤 **Human dev:** ~$8506 (85.1h @ $100/h, 30min dedup)
10
10
 
11
11
  Generated on 2026-05-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
12
 
@@ -24,7 +24,6 @@ When you run `code2llm ./ -f all`, the following files are created:
24
24
 
25
25
  | File | Format | Purpose | Key Insights |
26
26
  |------|--------|---------|--------------|
27
- | `planfile-tickets.yaml` | **YAML** | **🎫 Koru-ready ticket feed** - Actionable planfile suggestions from code2llm findings | Import or apply tickets for autonomous execution |
28
27
  | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
29
28
  | `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
30
29
 
@@ -50,12 +49,6 @@ code2llm ./ -f toon
50
49
  # Generate all formats (what created these files)
51
50
  code2llm ./ -f all
52
51
 
53
- # Generate planfile suggestions and create executable tickets for Koru
54
- code2llm ./ -f all -o ./project --no-chunk --planfile-apply
55
-
56
- # Or import the generated manifest later
57
- planfile ticket import --from ./project/planfile-tickets.yaml --source code2llm
58
-
59
52
  # LLM-ready context only
60
53
  code2llm ./ -f context
61
54
  ```
@@ -349,10 +342,10 @@ code2llm ./ -f yaml --separate-orphans
349
342
  ---
350
343
 
351
344
  **Generated by**: `code2llm ./ -f all --readme`
352
- **Analysis Date**: 2026-05-06
353
- **Total Functions**: 3800
354
- **Total Classes**: 259
355
- **Modules**: 497
345
+ **Analysis Date**: 2026-05-25
346
+ **Total Functions**: 1312
347
+ **Total Classes**: 143
348
+ **Modules**: 427
356
349
 
357
350
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
358
351
 
@@ -0,0 +1 @@
1
+ 0.5.162
@@ -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.160"
11
+ __version__ = "0.5.162"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -4,6 +4,26 @@ Extracted to keep data_analysis.py under the MI hard-gate threshold.
4
4
  All functions here are module-private (leading underscore) and called
5
5
  exclusively from DataAnalyzer in data_analysis.py.
6
6
  """
7
+ # ── Data pipeline helpers ─────────────────────────────────────────────────────
8
+ # _find_data_pipelines() : detect sequential transform chains from call graph.
9
+ # ── State-machine helpers ─────────────────────────────────────────────────────
10
+ # _is_state_func() : heuristic; True if name contains state keyword.
11
+ # _state_affected_by() : callers that mutate the given function's state.
12
+ # _find_state_patterns() : aggregate state-machine detections per module.
13
+ # ── Data-dependency helpers ───────────────────────────────────────────────────
14
+ # _find_data_dependencies() : build producer→consumer edges from shared types.
15
+ # ── Event-flow helpers ────────────────────────────────────────────────────────
16
+ # _is_event_func() : heuristic; True if name contains event keyword.
17
+ # _event_handlers() : functions that consume outputs of an event source.
18
+ # _find_event_flows() : aggregate event-flow detections across the project.
19
+ # ── Type-inference helpers ────────────────────────────────────────────────────
20
+ # _detect_types_from_name(): keyword-match on function name + docstring.
21
+ # _create_type_entry() : initialise a type-usage dict entry.
22
+ # _update_type_stats() : accumulate consumed/produced counts per type.
23
+ # _infer_parameter_types() : extract type hints from function parameters.
24
+ # _infer_return_types() : extract type hints from function return annotation.
25
+ # _analyze_data_types() : top-level driver; returns list of type-usage entries.
26
+ # _get_function_data_types(): merge name-based + annotation-based type signals.
7
27
 
8
28
  from typing import Any, Dict, List
9
29
  from code2llm.core.models import AnalysisResult
@@ -42,32 +62,38 @@ def _find_data_pipelines(result: AnalysisResult, categorize_fn, make_stage_fn, m
42
62
  return pipelines
43
63
 
44
64
 
65
+ _STATE_INDICATORS = ["state", "status", "mode", "phase", "lifecycle", "session", "context"]
66
+ _TRANSITION_INDICATORS = ["transition", "change", "update", "set_state", "switch"]
67
+
68
+
69
+ def _is_state_func(name_lower: str) -> bool:
70
+ """Return True if the function name suggests state management."""
71
+ return any(ind in name_lower for ind in _STATE_INDICATORS + _TRANSITION_INDICATORS)
72
+
73
+
74
+ def _state_affected_by(func, functions: dict) -> list:
75
+ """Return calls that look like state-managing functions."""
76
+ return [
77
+ call for call in list(func.calls)[:10]
78
+ if (cf := functions.get(call)) and any(ind in cf.name.lower() for ind in _STATE_INDICATORS)
79
+ ]
80
+
81
+
45
82
  def _find_state_patterns(result: AnalysisResult) -> list:
46
83
  """Find state management patterns."""
47
84
  patterns: list = []
48
- state_indicators = ["state", "status", "mode", "phase", "lifecycle", "session", "context"]
49
- transition_indicators = ["transition", "change", "update", "set_state", "switch"]
50
-
51
85
  for func_name, func in result.functions.items():
52
86
  name_lower = func.name.lower()
53
- if any(ind in name_lower for ind in state_indicators + transition_indicators):
54
- affected_states = []
55
- for call in list(func.calls)[:10]:
56
- call_func = result.functions.get(call)
57
- if call_func and any(ind in call_func.name.lower() for ind in state_indicators):
58
- affected_states.append(call)
59
- patterns.append(
60
- {
61
- "function": func_name,
62
- "type": "state_manager"
63
- if "set" in name_lower or "update" in name_lower
64
- else "state_reader",
65
- "affects_states": affected_states[:5],
66
- "description": func.docstring[:150] if func.docstring else "N/A",
67
- }
68
- )
69
- if len(patterns) >= 20:
70
- break
87
+ if not _is_state_func(name_lower):
88
+ continue
89
+ patterns.append({
90
+ "function": func_name,
91
+ "type": "state_manager" if ("set" in name_lower or "update" in name_lower) else "state_reader",
92
+ "affects_states": _state_affected_by(func, result.functions)[:5],
93
+ "description": func.docstring[:150] if func.docstring else "N/A",
94
+ })
95
+ if len(patterns) >= 20:
96
+ break
71
97
  return patterns
72
98
 
73
99
 
@@ -95,33 +121,43 @@ def _find_data_dependencies(result: AnalysisResult) -> list:
95
121
  return deps[:15]
96
122
 
97
123
 
124
+ _EVENT_INDICATORS = ["event", "emit", "trigger", "notify", "callback", "handler", "listen", "subscribe"]
125
+ _HOOK_INDICATORS = ["hook", "on_", "before_", "after_", "pre_", "post_"]
126
+ _EVENT_HANDLER_WORDS = _EVENT_INDICATORS + ["handle", "process"]
127
+
128
+
129
+ def _is_event_func(name_lower: str) -> bool:
130
+ """Return True if the function name suggests event handling or emission."""
131
+ return any(ind in name_lower for ind in _EVENT_INDICATORS) or any(
132
+ name_lower.startswith(ind) for ind in _HOOK_INDICATORS
133
+ )
134
+
135
+
136
+ def _event_handlers(func, functions: dict) -> list:
137
+ """Return list of called functions that look like event handlers."""
138
+ result = []
139
+ for called in list(func.calls)[:10]:
140
+ cf = functions.get(called)
141
+ if cf and any(ind in cf.name.lower() for ind in _EVENT_HANDLER_WORDS):
142
+ result.append(called)
143
+ return result
144
+
145
+
98
146
  def _find_event_flows(result: AnalysisResult) -> list:
99
147
  """Find event-driven patterns."""
100
148
  flows: list = []
101
- event_indicators = ["event", "emit", "trigger", "notify", "callback", "handler", "listen", "subscribe"]
102
- hook_indicators = ["hook", "on_", "before_", "after_", "pre_", "post_"]
103
149
  for func_name, func in result.functions.items():
104
150
  name_lower = func.name.lower()
105
- if any(ind in name_lower for ind in event_indicators) or any(
106
- name_lower.startswith(ind) for ind in hook_indicators
107
- ):
108
- handlers = []
109
- for called in list(func.calls)[:10]:
110
- called_func = result.functions.get(called)
111
- if called_func and any(
112
- ind in called_func.name.lower() for ind in event_indicators + ["handle", "process"]
113
- ):
114
- handlers.append(called)
115
- flows.append(
116
- {
117
- "event_source": func_name,
118
- "type": "emitter" if "emit" in name_lower or "trigger" in name_lower else "handler",
119
- "handlers": handlers[:5],
120
- "description": func.docstring[:150] if func.docstring else "N/A",
121
- }
122
- )
123
- if len(flows) >= 20:
124
- break
151
+ if not _is_event_func(name_lower):
152
+ continue
153
+ flows.append({
154
+ "event_source": func_name,
155
+ "type": "emitter" if ("emit" in name_lower or "trigger" in name_lower) else "handler",
156
+ "handlers": _event_handlers(func, result.functions)[:5],
157
+ "description": func.docstring[:150] if func.docstring else "N/A",
158
+ })
159
+ if len(flows) >= 20:
160
+ break
125
161
  return flows
126
162
 
127
163
 
@@ -306,55 +342,51 @@ def _identify_process_patterns(result: AnalysisResult) -> list:
306
342
  return sorted(res, key=lambda x: x["count"], reverse=True)
307
343
 
308
344
 
309
- def _analyze_optimization_opportunities(
310
- result: AnalysisResult, data_types: list, dfg: dict
311
- ) -> dict:
312
- """Analyze optimization opportunities in data handling."""
313
- opt: Dict[str, Any] = {
314
- "potential_score": 0.0,
315
- "type_consolidation": [],
316
- "process_consolidation": [],
317
- "hub_optimization": [],
318
- "recommendations": [],
319
- }
345
+ def _type_consolidations(data_types: list) -> list:
346
+ """Find groups of similar data types that could be consolidated."""
320
347
  similar: Dict[str, list] = {}
321
348
  for dt in data_types:
322
349
  sig = ",".join(sorted(dt["detected_types"]))
323
- if sig not in similar:
324
- similar[sig] = []
325
- similar[sig].append(dt)
326
- for _sig, sims in similar.items():
350
+ similar.setdefault(sig, []).append(dt)
351
+ result = []
352
+ for sig, sims in similar.items():
327
353
  if len(sims) > 1:
328
354
  usage = sum(s["usage_count"] for s in sims)
329
355
  if usage > 10:
330
- opt["type_consolidation"].append(
331
- {
332
- "type_signature": _sig,
333
- "similar_types": [s["type_name"] for s in sims],
334
- "total_usage": usage,
335
- "potential_reduction": len(sims) - 1,
336
- }
337
- )
338
- for p in _identify_process_patterns(result):
339
- if p["count"] > 5:
340
- opt["process_consolidation"].append(
341
- {
342
- "pattern_type": p["pattern_type"],
343
- "function_count": p["count"],
344
- "potential_reduction": p["count"] // 3,
345
- }
346
- )
347
- for hub in [n for n in dfg["nodes"].values() if n["is_hub"]][:10]:
348
- opt["hub_optimization"].append(
349
- {
350
- "function": hub["id"],
351
- "connections": hub["in_degree"] + hub["out_degree"],
352
- "optimization_type": "split" if hub["out_degree"] > 10 else "cache",
353
- }
354
- )
355
- opt["potential_score"] = (
356
- len(opt["type_consolidation"]) * 10
357
- + len(opt["process_consolidation"]) * 15
358
- + len(opt["hub_optimization"]) * 5
359
- ) / 100.0
360
- return opt
356
+ result.append({"type_signature": sig, "similar_types": [s["type_name"] for s in sims],
357
+ "total_usage": usage, "potential_reduction": len(sims) - 1})
358
+ return result
359
+
360
+
361
+ def _process_consolidations(result: AnalysisResult) -> list:
362
+ """Find repeated process patterns that could be consolidated."""
363
+ return [
364
+ {"pattern_type": p["pattern_type"], "function_count": p["count"],
365
+ "potential_reduction": p["count"] // 3}
366
+ for p in _identify_process_patterns(result) if p["count"] > 5
367
+ ]
368
+
369
+
370
+ def _hub_optimizations(dfg: dict) -> list:
371
+ """Identify hub nodes that are candidates for splitting or caching."""
372
+ return [
373
+ {"function": hub["id"], "connections": hub["in_degree"] + hub["out_degree"],
374
+ "optimization_type": "split" if hub["out_degree"] > 10 else "cache"}
375
+ for hub in list(n for n in dfg["nodes"].values() if n["is_hub"])[:10]
376
+ ]
377
+
378
+
379
+ def _analyze_optimization_opportunities(
380
+ result: AnalysisResult, data_types: list, dfg: dict
381
+ ) -> dict:
382
+ """Analyze optimization opportunities in data handling."""
383
+ type_cons = _type_consolidations(data_types)
384
+ proc_cons = _process_consolidations(result)
385
+ hub_opts = _hub_optimizations(dfg)
386
+ return {
387
+ "potential_score": (len(type_cons) * 10 + len(proc_cons) * 15 + len(hub_opts) * 5) / 100.0,
388
+ "type_consolidation": type_cons,
389
+ "process_consolidation": proc_cons,
390
+ "hub_optimization": hub_opts,
391
+ "recommendations": [],
392
+ }
@@ -204,4 +204,5 @@ class CallGraphExtractor(ast.NodeVisitor):
204
204
  return None
205
205
 
206
206
  def _expr_to_str(self, node: ast.AST) -> str:
207
+ """Unparse an AST expression node to a source string (empty string on failure)."""
207
208
  return ast_unparse(node, default_none="")
@@ -125,7 +125,6 @@ class CFGExtractor(ast.NodeVisitor):
125
125
  # Visit then branch
126
126
  then_last = []
127
127
  for stmt in node.body:
128
- prev = self.current_node
129
128
  self.current_node = branch_entry
130
129
  self.visit(stmt)
131
130
  then_last.append(self.current_node)
@@ -136,7 +135,6 @@ class CFGExtractor(ast.NodeVisitor):
136
135
  if node.orelse:
137
136
  branch_entry = cond_node
138
137
  for stmt in node.orelse:
139
- prev = self.current_node
140
138
  self.current_node = branch_entry
141
139
  self.visit(stmt)
142
140
  else_last.append(self.current_node)
@@ -270,10 +268,11 @@ class CFGExtractor(ast.NodeVisitor):
270
268
  """Extract condition as string."""
271
269
  try:
272
270
  return ast.unparse(node) if hasattr(ast, "unparse") else str(node)[:50]
273
- except:
271
+ except Exception:
274
272
  return str(node)[:50]
275
273
 
276
274
  def _expr_to_str(self, node: ast.AST) -> str:
275
+ """Unparse an AST expression node to a source string."""
277
276
  return ast_unparse(node)
278
277
 
279
278
  def _format_except(self, handler: ast.ExceptHandler) -> str:
@@ -230,6 +230,7 @@ class DFGExtractor(ast.NodeVisitor):
230
230
  return names
231
231
 
232
232
  def _expr_to_str(self, node: ast.AST) -> str:
233
+ """Unparse an AST expression node to a source string."""
233
234
  return ast_unparse(node)
234
235
 
235
236
  def _build_data_flow_edges(self):
@@ -13,7 +13,7 @@ Refactored v0.5.x: Extracted resolver and classifier into separate modules.
13
13
 
14
14
  import logging
15
15
  from dataclasses import dataclass, field
16
- from typing import Any, Dict, List, Optional, Set
16
+ from typing import Any, Dict, List, Optional, Set, Tuple
17
17
 
18
18
  import networkx as nx
19
19
 
@@ -191,6 +191,22 @@ class PipelineDetector:
191
191
  # ------------------------------------------------------------------
192
192
  # path finding
193
193
  # ------------------------------------------------------------------
194
+ def _process_components(
195
+ self, graph: nx.DiGraph, paths: List[List[str]], used_nodes: Set[str]
196
+ ) -> None:
197
+ """Extend paths with longest paths from each weakly connected component."""
198
+ for component in nx.weakly_connected_components(graph):
199
+ if len(component) < MIN_PIPELINE_LENGTH:
200
+ continue
201
+ if len(component & used_nodes) > len(component) * 0.5:
202
+ continue
203
+ path = self._longest_path_in_dag(graph.subgraph(component))
204
+ if len(path) >= MIN_PIPELINE_LENGTH:
205
+ new_overlap = sum(1 for n in path if n in used_nodes)
206
+ if new_overlap <= len(path) * 0.5:
207
+ paths.append(path)
208
+ used_nodes.update(path)
209
+
194
210
  def _find_pipeline_paths(self, graph: nx.DiGraph) -> List[List[str]]:
195
211
  """Find longest paths in the call graph as pipeline candidates.
196
212
 
@@ -200,41 +216,17 @@ class PipelineDetector:
200
216
  3. For each source, find longest simple path to any sink
201
217
  4. Also consider longest paths in each weakly connected component
202
218
  """
203
- paths: List[List[str]] = []
204
-
205
- # Get source nodes (in-degree 0) as potential pipeline entry points
206
219
  sources = [n for n in graph.nodes() if graph.in_degree(n) == 0]
207
-
208
- # If no natural sources, use nodes with low in-degree
209
220
  if not sources:
210
221
  sources = sorted(graph.nodes(), key=lambda n: graph.in_degree(n))[:5]
211
-
212
- # Try to find longest paths from each source
222
+ paths: List[List[str]] = []
213
223
  used_nodes: Set[str] = set()
214
224
  for source in sources:
215
225
  best_path = self._longest_path_from(graph, source, used_nodes)
216
226
  if len(best_path) >= MIN_PIPELINE_LENGTH:
217
227
  paths.append(best_path)
218
228
  used_nodes.update(best_path)
219
-
220
- # Also try: for each weakly connected component, find the longest path
221
- for component in nx.weakly_connected_components(graph):
222
- if len(component) < MIN_PIPELINE_LENGTH:
223
- continue
224
- # Skip if heavily overlapping with existing paths
225
- overlap = len(component & used_nodes)
226
- if overlap > len(component) * 0.5:
227
- continue
228
-
229
- subgraph = graph.subgraph(component)
230
- path = self._longest_path_in_dag(subgraph)
231
- if len(path) >= MIN_PIPELINE_LENGTH:
232
- # Check overlap with existing paths
233
- new_overlap = sum(1 for n in path if n in used_nodes)
234
- if new_overlap <= len(path) * 0.5:
235
- paths.append(path)
236
- used_nodes.update(path)
237
-
229
+ self._process_components(graph, paths, used_nodes)
238
230
  return paths
239
231
 
240
232
  def _longest_path_from(
@@ -241,36 +241,37 @@ class SideEffectDetector:
241
241
  self._check_yield(node, info)
242
242
  self._check_delete(node, info)
243
243
 
244
- def _check_calls(self, node: ast.AST, info: SideEffectInfo) -> None:
245
- """Detect IO and cache calls."""
246
- if not isinstance(node, ast.Call):
247
- return
248
-
249
- call_name = self._get_call_name(node.func)
250
- if not call_name:
251
- return
252
-
253
- parts = call_name.split(".")
254
- base_name = parts[-1]
255
-
256
- # IO detection
244
+ @staticmethod
245
+ def _check_io_call(
246
+ base_name: str, parts: list, call_name: str, info: "SideEffectInfo"
247
+ ) -> None:
248
+ """Classify call as IO if it matches known IO patterns."""
257
249
  if base_name in IO_CALLS:
258
250
  info.io_operations.append(base_name)
259
- elif base_name in HTTP_METHODS and len(parts) >= 2:
260
- # Only classify as IO if caller looks like HTTP client
261
- caller = parts[-2].lower()
262
- if caller in HTTP_CALLERS:
263
- info.io_operations.append(call_name)
251
+ elif base_name in HTTP_METHODS and len(parts) >= 2 and parts[-2].lower() in HTTP_CALLERS:
252
+ info.io_operations.append(call_name)
264
253
  elif base_name in IO_ATTRIBUTES:
265
254
  info.io_operations.append(call_name)
266
255
 
267
- # Cache detection
256
+ @staticmethod
257
+ def _check_cache_call(base_name: str, call_name: str, info: "SideEffectInfo") -> None:
258
+ """Classify call as cache operation if it matches known cache patterns."""
268
259
  if base_name in CACHE_CALLS:
269
260
  info.cache_operations.append(base_name)
270
261
  elif any(ci in call_name for ci in CACHE_INDICATORS):
271
262
  info.cache_operations.append(call_name)
272
263
 
273
- # Mutation via method calls (e.g., list.append)
264
+ def _check_calls(self, node: ast.AST, info: SideEffectInfo) -> None:
265
+ """Detect IO and cache calls."""
266
+ if not isinstance(node, ast.Call):
267
+ return
268
+ call_name = self._get_call_name(node.func)
269
+ if not call_name:
270
+ return
271
+ parts = call_name.split(".")
272
+ base_name = parts[-1]
273
+ self._check_io_call(base_name, parts, call_name, info)
274
+ self._check_cache_call(base_name, call_name, info)
274
275
  if base_name in MUTATION_CALLS and len(parts) >= 2:
275
276
  info.mutations.append(call_name)
276
277
 
@@ -56,7 +56,11 @@ class SmellDetector:
56
56
  file=func_info.file,
57
57
  line=func_info.line,
58
58
  severity=severity,
59
- description=f"Function '{func_info.name}' is oversized: CC={complexity}, fan-out={fan_out}, mutations={mutation_count}.",
59
+ description=(
60
+ f"Function '{func_info.name}' is oversized:"
61
+ f" CC={complexity}, fan-out={fan_out},"
62
+ f" mutations={mutation_count}."
63
+ ),
60
64
  context={
61
65
  "fan_out": fan_out,
62
66
  "mutations": mutation_count,
@@ -85,7 +89,11 @@ class SmellDetector:
85
89
  file=mod.file,
86
90
  line=1,
87
91
  severity=severity,
88
- description=f"Module '{mod_name}' is too large ({f_count} functions, {c_count} classes). Consider splitting into sub-modules.",
92
+ description=(
93
+ f"Module '{mod_name}' is too large"
94
+ f" ({f_count} functions, {c_count} classes)."
95
+ " Consider splitting into sub-modules."
96
+ ),
89
97
  context={"functions": f_count, "classes": c_count},
90
98
  )
91
99
  )
@@ -113,7 +121,10 @@ class SmellDetector:
113
121
  file=func_info.file,
114
122
  line=func_info.line,
115
123
  severity=0.7,
116
- description=f"Function '{func_info.name}' mutates multiple variables in other modules: {', '.join(set(foreign_mutations))}.",
124
+ description=(
125
+ f"Function '{func_info.name}' mutates multiple variables"
126
+ f" in other modules: {', '.join(set(foreign_mutations))}."
127
+ ),
117
128
  context={"foreign_mutations": list(set(foreign_mutations))},
118
129
  )
119
130
  )
@@ -142,7 +153,10 @@ class SmellDetector:
142
153
  file=func_info.file,
143
154
  line=func_info.line,
144
155
  severity=0.6,
145
- description=f"Arguments ({', '.join(args)}) are used together in multiple functions: {', '.join(funcs)}.",
156
+ description=(
157
+ f"Arguments ({', '.join(args)}) are used together"
158
+ f" in multiple functions: {', '.join(funcs)}."
159
+ ),
146
160
  context={"clump": list(args), "related_functions": funcs},
147
161
  )
148
162
  )
@@ -173,7 +187,10 @@ class SmellDetector:
173
187
  file=func_info.file,
174
188
  line=func_info.line,
175
189
  severity=0.8,
176
- description=f"Mutation of variable '{var}' spans {len(funcs)} functions. Changing this logic requires work in many places.",
190
+ description=(
191
+ f"Mutation of variable '{var}' spans {len(funcs)} functions."
192
+ " Changing this logic requires work in many places."
193
+ ),
177
194
  context={"variable": var, "affected_functions": list(funcs)},
178
195
  )
179
196
  )
@@ -192,7 +209,11 @@ class SmellDetector:
192
209
  file=func_info.file,
193
210
  line=func_info.line,
194
211
  severity=min(1.0, func_info.centrality * 5),
195
- description=f"Function '{func_info.name}' is a structural bottleneck (centrality={round(func_info.centrality, 3)}). Significant logic flows through this function.",
212
+ description=(
213
+ f"Function '{func_info.name}' is a structural bottleneck"
214
+ f" (centrality={round(func_info.centrality, 3)})."
215
+ " Significant logic flows through this function."
216
+ ),
196
217
  context={"centrality": func_info.centrality},
197
218
  )
198
219
  )
@@ -218,7 +239,11 @@ class SmellDetector:
218
239
  file=func_info.file,
219
240
  line=func_info.line,
220
241
  severity=0.8,
221
- description=f"Circular dependency detected: {' -> '.join(cycle)}. This indicates high coupling and may lead to infinite recursion or initialization issues.",
242
+ description=(
243
+ f"Circular dependency detected: {' -> '.join(cycle)}."
244
+ " This indicates high coupling and may lead to"
245
+ " infinite recursion or initialization issues."
246
+ ),
222
247
  context={"cycle": cycle},
223
248
  )
224
249
  )