code2llm 0.5.155__tar.gz → 0.5.156__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 (204) hide show
  1. {code2llm-0.5.155/code2llm.egg-info → code2llm-0.5.156}/PKG-INFO +6 -6
  2. {code2llm-0.5.155 → code2llm-0.5.156}/README.md +5 -5
  3. code2llm-0.5.156/VERSION +1 -0
  4. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/pipeline_detector.py +3 -0
  6. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_analysis.py +22 -0
  7. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/export_pipeline.py +5 -0
  8. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/php.py +4 -0
  9. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/ts_extractors.py +2 -0
  10. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/large_repo.py +1 -0
  11. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming/cache.py +2 -0
  12. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/article_view.py +8 -0
  13. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/context_exporter.py +4 -0
  14. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/context_view.py +6 -0
  15. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/html_dashboard.py +2 -0
  16. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/planfile_tickets.py +7 -0
  17. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/helpers.py +5 -0
  18. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon_view.py +7 -0
  19. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/analysis.py +5 -0
  20. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/cli.py +2 -0
  21. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/generator.py +2 -0
  22. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_task.py +5 -0
  23. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/nlp/__init__.py +1 -1
  24. {code2llm-0.5.155 → code2llm-0.5.156/code2llm.egg-info}/PKG-INFO +6 -6
  25. {code2llm-0.5.155 → code2llm-0.5.156}/pyproject.toml +1 -1
  26. code2llm-0.5.155/VERSION +0 -1
  27. {code2llm-0.5.155 → code2llm-0.5.156}/LICENSE +0 -0
  28. {code2llm-0.5.155 → code2llm-0.5.156}/MANIFEST.in +0 -0
  29. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/__main__.py +0 -0
  30. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/__init__.py +0 -0
  31. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/call_graph.py +0 -0
  32. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/cfg.py +0 -0
  33. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/coupling.py +0 -0
  34. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/data_analysis.py +0 -0
  35. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/dfg.py +0 -0
  36. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/pipeline_classifier.py +0 -0
  37. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/pipeline_resolver.py +0 -0
  38. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/side_effects.py +0 -0
  39. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/smells.py +0 -0
  40. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/type_inference.py +0 -0
  41. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/utils/__init__.py +0 -0
  42. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/analysis/utils/ast_helpers.py +0 -0
  43. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/api.py +0 -0
  44. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli.py +0 -0
  45. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_commands.py +0 -0
  46. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/__init__.py +0 -0
  47. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/code2logic.py +0 -0
  48. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/formats.py +0 -0
  49. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/orchestrator.py +0 -0
  50. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  51. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  52. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
  53. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_exports/prompt.py +0 -0
  54. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/cli_parser.py +0 -0
  55. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/__init__.py +0 -0
  56. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/analyzer.py +0 -0
  57. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/ast_registry.py +0 -0
  58. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/config.py +0 -0
  59. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/file_analyzer.py +0 -0
  60. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/file_cache.py +0 -0
  61. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/file_filter.py +0 -0
  62. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/gitignore.py +0 -0
  63. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/incremental.py +0 -0
  64. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/__init__.py +0 -0
  65. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/_c_parser.py +0 -0
  66. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/_calls.py +0 -0
  67. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/_complexity.py +0 -0
  68. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/base.py +0 -0
  69. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/cpp.py +0 -0
  70. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/csharp.py +0 -0
  71. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/generic.py +0 -0
  72. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/go_lang.py +0 -0
  73. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/java.py +0 -0
  74. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/ruby.py +0 -0
  75. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/rust.py +0 -0
  76. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/ts_parser.py +0 -0
  77. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/lang/typescript.py +0 -0
  78. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/models.py +0 -0
  79. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/persistent_cache.py +0 -0
  80. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/refactoring.py +0 -0
  81. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/repo_files.py +0 -0
  82. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/source_classifier.py +0 -0
  83. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming/__init__.py +0 -0
  84. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming/incremental.py +0 -0
  85. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming/prioritizer.py +0 -0
  86. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming/scanner.py +0 -0
  87. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming/strategies.py +0 -0
  88. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/streaming_analyzer.py +0 -0
  89. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/core/toon_size_manager.py +0 -0
  90. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/__init__.py +0 -0
  91. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/base.py +0 -0
  92. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/dashboard_data.py +0 -0
  93. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/dashboard_renderer.py +0 -0
  94. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution/__init__.py +0 -0
  95. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution/computation.py +0 -0
  96. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution/constants.py +0 -0
  97. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution/exclusion.py +0 -0
  98. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution/render.py +0 -0
  99. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution/yaml_export.py +0 -0
  100. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/evolution_exporter.py +0 -0
  101. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/flow_constants.py +0 -0
  102. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/flow_exporter.py +0 -0
  103. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/flow_renderer.py +0 -0
  104. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/index_generator/__init__.py +0 -0
  105. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/index_generator/renderer.py +0 -0
  106. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/index_generator/scanner.py +0 -0
  107. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/index_generator.py +0 -0
  108. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/json_exporter.py +0 -0
  109. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/llm_exporter.py +0 -0
  110. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/__init__.py +0 -0
  111. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/alerts.py +0 -0
  112. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/details.py +0 -0
  113. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/header.py +0 -0
  114. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/module_list.py +0 -0
  115. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/utils.py +0 -0
  116. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map/yaml_export.py +0 -0
  117. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/map_exporter.py +0 -0
  118. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/__init__.py +0 -0
  119. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/calls.py +0 -0
  120. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/classic.py +0 -0
  121. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/compact.py +0 -0
  122. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  123. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  124. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/flow_full.py +0 -0
  125. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid/utils.py +0 -0
  126. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid_exporter.py +0 -0
  127. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  128. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/__init__.py +0 -0
  129. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/constants.py +0 -0
  130. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/core.py +0 -0
  131. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/evolution.py +0 -0
  132. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/health.py +0 -0
  133. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  134. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml/modules.py +0 -0
  135. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/project_yaml_exporter.py +0 -0
  136. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/readme/__init__.py +0 -0
  137. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/readme/content.py +0 -0
  138. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/readme/files.py +0 -0
  139. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/readme/insights.py +0 -0
  140. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/readme/sections.py +0 -0
  141. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/readme_exporter.py +0 -0
  142. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/report_generators.py +0 -0
  143. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/__init__.py +0 -0
  144. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/metrics.py +0 -0
  145. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/metrics_core.py +0 -0
  146. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
  147. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/metrics_health.py +0 -0
  148. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/module_detail.py +0 -0
  149. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon/renderer.py +0 -0
  150. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/toon.py +0 -0
  151. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/validate_project.py +0 -0
  152. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/exporters/yaml_exporter.py +0 -0
  153. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/__init__.py +0 -0
  154. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/_utils.py +0 -0
  155. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/__init__.py +0 -0
  156. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/nodes.py +0 -0
  157. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/parsing.py +0 -0
  158. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow/utils.py +0 -0
  159. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/llm_flow.py +0 -0
  160. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/mermaid/__init__.py +0 -0
  161. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/mermaid/fix.py +0 -0
  162. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/mermaid/png.py +0 -0
  163. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/mermaid/validation.py +0 -0
  164. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/generators/mermaid.py +0 -0
  165. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/nlp/config.py +0 -0
  166. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/nlp/entity_resolution.py +0 -0
  167. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/nlp/intent_matching.py +0 -0
  168. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/nlp/normalization.py +0 -0
  169. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/nlp/pipeline.py +0 -0
  170. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/parsers/toon_parser.py +0 -0
  171. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/patterns/__init__.py +0 -0
  172. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/patterns/detector.py +0 -0
  173. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/refactor/__init__.py +0 -0
  174. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm/refactor/prompt_engine.py +0 -0
  175. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm.egg-info/SOURCES.txt +0 -0
  176. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm.egg-info/dependency_links.txt +0 -0
  177. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm.egg-info/entry_points.txt +0 -0
  178. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm.egg-info/requires.txt +0 -0
  179. {code2llm-0.5.155 → code2llm-0.5.156}/code2llm.egg-info/top_level.txt +0 -0
  180. {code2llm-0.5.155 → code2llm-0.5.156}/setup.cfg +0 -0
  181. {code2llm-0.5.155 → code2llm-0.5.156}/setup.py +0 -0
  182. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_advanced_analysis.py +0 -0
  183. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_analyzer.py +0 -0
  184. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_cache_invalidation_e2e.py +0 -0
  185. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_calls_toon_export.py +0 -0
  186. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_declarative_collection.py +0 -0
  187. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_deep_analysis.py +0 -0
  188. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_edge_cases.py +0 -0
  189. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_export_cache_flags.py +0 -0
  190. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_file_analyzer_tagging.py +0 -0
  191. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_flow_exporter.py +0 -0
  192. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_format_quality.py +0 -0
  193. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_multilanguage_e2e.py +0 -0
  194. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_nlp_pipeline.py +0 -0
  195. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_nonpython_cc_calls.py +0 -0
  196. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_orchestrator_cache_mtime.py +0 -0
  197. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_persistent_cache.py +0 -0
  198. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_pipeline_detector.py +0 -0
  199. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_planfile_tickets_exporter.py +0 -0
  200. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_project_toon_export.py +0 -0
  201. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_prompt_engine.py +0 -0
  202. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_prompt_txt.py +0 -0
  203. {code2llm-0.5.155 → code2llm-0.5.156}/tests/test_refactoring_engine.py +0 -0
  204. {code2llm-0.5.155 → code2llm-0.5.156}/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.155
3
+ Version: 0.5.156
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,13 +66,13 @@ 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.155-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-79.1h-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.156-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-79.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
71
71
 
72
- - 🤖 **LLM usage:** $7.5000 (208 commits)
73
- - 👤 **Human dev:** ~$7906 (79.1h @ $100/h, 30min dedup)
72
+ - 🤖 **LLM usage:** $7.5000 (211 commits)
73
+ - 👤 **Human dev:** ~$7972 (79.7h @ $100/h, 30min dedup)
74
74
 
75
- Generated on 2026-05-24 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
75
+ Generated on 2026-05-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
76
 
77
77
  ---
78
78
 
@@ -2,13 +2,13 @@
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.155-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-79.1h-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.156-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-79.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
7
7
 
8
- - 🤖 **LLM usage:** $7.5000 (208 commits)
9
- - 👤 **Human dev:** ~$7906 (79.1h @ $100/h, 30min dedup)
8
+ - 🤖 **LLM usage:** $7.5000 (211 commits)
9
+ - 👤 **Human dev:** ~$7972 (79.7h @ $100/h, 30min dedup)
10
10
 
11
- Generated on 2026-05-24 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
11
+ Generated on 2026-05-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
12
 
13
13
  ---
14
14
 
@@ -0,0 +1 @@
1
+ 0.5.156
@@ -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.155"
11
+ __version__ = "0.5.156"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -63,9 +63,11 @@ class Pipeline:
63
63
 
64
64
  @property
65
65
  def purity_ratio(self) -> float:
66
+ """Fraction of stages that are free of side effects."""
66
67
  return self.pure_count / self.total_stages if self.total_stages else 0.0
67
68
 
68
69
  def to_dict(self) -> Dict[str, Any]:
70
+ """Serialise the Pipeline to a plain dict."""
69
71
  return {
70
72
  "name": self.name,
71
73
  "domain": self.domain,
@@ -125,6 +127,7 @@ class PipelineDetector:
125
127
  type_engine: Optional[TypeInferenceEngine] = None,
126
128
  side_effect_detector: Optional[SideEffectDetector] = None,
127
129
  ):
130
+ """Initialise with optional shared type engine and side-effect detector."""
128
131
  self._type_engine = type_engine or TypeInferenceEngine()
129
132
  self._se_detector = side_effect_detector or SideEffectDetector()
130
133
  self._resolver = PipelineResolver()
@@ -283,6 +283,15 @@ def _merge_chunked_results(all_results, source_path: Path):
283
283
 
284
284
  merged = AnalysisResult(project_path=str(source_path))
285
285
 
286
+ # Track seen (file, simple_name) pairs to deduplicate overlapping chunk scopes.
287
+ # When the splitter assigns the same directory to multiple chunks (e.g. root and
288
+ # batch_1 both point at the project root), the same class/function would otherwise
289
+ # appear multiple times in the merged result and trigger false-positive duplicate
290
+ # detection in the TOON exporter.
291
+ _seen_funcs: set = set()
292
+ _seen_classes: set = set()
293
+ _seen_modules: set = set()
294
+
286
295
  for name, result, output_dir in all_results:
287
296
  if not result:
288
297
  continue
@@ -290,14 +299,26 @@ def _merge_chunked_results(all_results, source_path: Path):
290
299
  prefix = f"{name}."
291
300
 
292
301
  for func_name, func_info in result.functions.items():
302
+ dedup_key = (func_info.file, func_info.name)
303
+ if dedup_key in _seen_funcs:
304
+ continue
305
+ _seen_funcs.add(dedup_key)
293
306
  new_name = f"{prefix}{func_name}" if "." not in func_name else func_name
294
307
  merged.functions[new_name] = func_info
295
308
 
296
309
  for class_name, class_info in result.classes.items():
310
+ dedup_key = (class_info.file, class_info.name)
311
+ if dedup_key in _seen_classes:
312
+ continue
313
+ _seen_classes.add(dedup_key)
297
314
  new_name = f"{prefix}{class_name}" if "." not in class_name else class_name
298
315
  merged.classes[new_name] = class_info
299
316
 
300
317
  for mod_name, mod_info in result.modules.items():
318
+ dedup_key = (mod_info.file, mod_info.name)
319
+ if dedup_key in _seen_modules:
320
+ continue
321
+ _seen_modules.add(dedup_key)
301
322
  new_name = f"{prefix}{mod_name}" if "." not in mod_name else mod_name
302
323
  merged.modules[new_name] = mod_info
303
324
 
@@ -336,6 +357,7 @@ def _run_streaming_analysis(args, config, source_path: Path):
336
357
  if args.verbose:
337
358
 
338
359
  def on_progress(update):
360
+ """Print a progress percentage line in-place."""
339
361
  pct = update.get("percentage", 0)
340
362
  print(f"\r[{pct:.0f}%] {update.get('message', '')}", end="", flush=True)
341
363
 
@@ -19,23 +19,28 @@ class SharedExportContext:
19
19
  __slots__ = ("_result", "_computed")
20
20
 
21
21
  def __init__(self, result: AnalysisResult):
22
+ """Wrap an AnalysisResult for lazy computed-metric access."""
22
23
  self._result = result
23
24
  self._computed: Dict[str, Any] = {}
24
25
 
25
26
  @property
26
27
  def result(self) -> AnalysisResult:
28
+ """Return the underlying AnalysisResult."""
27
29
  return self._result
28
30
 
29
31
  @property
30
32
  def functions(self) -> Dict:
33
+ """Return the functions dict from the underlying result."""
31
34
  return self._result.functions
32
35
 
33
36
  @property
34
37
  def classes(self) -> Dict:
38
+ """Return the classes dict from the underlying result."""
35
39
  return self._result.classes
36
40
 
37
41
  @property
38
42
  def modules(self) -> Dict:
43
+ """Return the modules dict from the underlying result."""
39
44
  return self._result.modules
40
45
 
41
46
  @property
@@ -11,6 +11,7 @@ from code2llm.core.lang.base import (
11
11
  def _parse_php_metadata(
12
12
  content: str, module_name: str, result: Dict
13
13
  ) -> Tuple[Optional[str], bool]:
14
+ """Extract PHP namespace and import declarations from source content."""
14
15
  lines = content.split("\n")
15
16
  current_namespace = None
16
17
  in_php = False
@@ -35,6 +36,7 @@ def _parse_php_metadata(
35
36
 
36
37
 
37
38
  def _adjust_qualified_names(result: Dict, module_name: str, namespace: str) -> None:
39
+ """Prefix all class/function qualified names with the PHP namespace."""
38
40
  ns_prefix = f".{namespace}"
39
41
  for key in ["classes", "functions"]:
40
42
  new_items = {}
@@ -56,6 +58,7 @@ def _extract_php_traits(
56
58
  result: Dict,
57
59
  stats: Dict,
58
60
  ) -> None:
61
+ """Detect PHP trait declarations and add them as ClassInfo entries."""
59
62
  trait_pattern = re.compile(r"^\s*trait\s+(\w+)")
60
63
  for line_no, line in enumerate(content.split("\n"), 1):
61
64
  tm = trait_pattern.match(line.strip())
@@ -79,6 +82,7 @@ def _extract_php_traits(
79
82
  def analyze_php(
80
83
  content: str, file_path: str, module_name: str, ext: str, stats: Dict
81
84
  ) -> Dict:
85
+ """Analyze a PHP source file and return declarations, complexity, and call info."""
82
86
  patterns = {
83
87
  "import": re.compile(
84
88
  r'^(?:include|require|include_once|require_once)\s*["\']([^"\']+)["\']'
@@ -108,6 +108,7 @@ def _extract_functions_ts(
108
108
  func_types = FUNCTION_TYPES.get(lang, ())
109
109
 
110
110
  def visit(node, class_context: Optional[str] = None):
111
+ """Recursively visit a tree-sitter node, extracting function declarations."""
111
112
  if node.type in func_types:
112
113
  name_node = _find_name_node(node)
113
114
  if name_node:
@@ -156,6 +157,7 @@ def _extract_classes_ts(
156
157
  class_types = CLASS_TYPES.get(lang, ())
157
158
 
158
159
  def visit(node):
160
+ """Recursively visit a tree-sitter node, extracting class declarations."""
159
161
  if node.type in class_types:
160
162
  name_node = _find_name_node(node)
161
163
  if name_node:
@@ -44,6 +44,7 @@ class SubProject:
44
44
 
45
45
  @property
46
46
  def file_count(self) -> int:
47
+ """Return the number of files in this sub-project."""
47
48
  return len(self.files)
48
49
 
49
50
 
@@ -11,6 +11,7 @@ class StreamingFileCache:
11
11
  """Memory-efficient cache with LRU eviction."""
12
12
 
13
13
  def __init__(self, max_size: int = 100, cache_dir: str = ".code2llm_cache"):
14
+ """Initialise the hybrid (memory + disk) LRU cache."""
14
15
  self.max_size = max_size
15
16
  self.cache_dir = Path(cache_dir)
16
17
  self.cache_dir.mkdir(parents=True, exist_ok=True)
@@ -18,6 +19,7 @@ class StreamingFileCache:
18
19
  self._access_order: List[str] = []
19
20
 
20
21
  def _get_cache_key(self, file_path: str, content: str) -> str:
22
+ """Derive the cache key for a given file path and content."""
21
23
  return make_cache_key(file_path, content)
22
24
 
23
25
  def _evict_if_needed(self) -> None:
@@ -13,6 +13,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
13
13
  """Generate status.md — publishable project health article."""
14
14
 
15
15
  def _render(self, data: Dict[str, Any]) -> List[str]:
16
+ """Render the full article from project.yaml data, returning Markdown lines."""
16
17
  proj = data.get("project", {})
17
18
  health = data.get("health", {})
18
19
  hotspots = data.get("hotspots", [])
@@ -31,6 +32,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
31
32
 
32
33
  @staticmethod
33
34
  def _render_frontmatter(proj: Dict) -> List[str]:
35
+ """Render YAML frontmatter and H1 heading."""
34
36
  stats = proj.get("stats", {})
35
37
  return [
36
38
  "---",
@@ -49,6 +51,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
49
51
 
50
52
  @staticmethod
51
53
  def _render_health_summary(proj: Dict, health: Dict) -> List[str]:
54
+ """Render the health summary table with overall verdict."""
52
55
  stats = proj.get("stats", {})
53
56
  cc_avg = health.get("cc_avg", 0)
54
57
  crit = health.get("critical_count", 0)
@@ -78,6 +81,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
78
81
 
79
82
  @staticmethod
80
83
  def _render_alerts(health: Dict) -> List[str]:
84
+ """Render the alert list (health warnings/errors)."""
81
85
  alerts = health.get("alerts", [])
82
86
  if not alerts:
83
87
  return []
@@ -95,6 +99,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
95
99
 
96
100
  @staticmethod
97
101
  def _render_hotspots(hotspots: List[Dict]) -> List[str]:
102
+ """Render the top hotspot functions by fan-out."""
98
103
  if not hotspots:
99
104
  return []
100
105
  lines = [
@@ -112,6 +117,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
112
117
 
113
118
  @staticmethod
114
119
  def _render_roadmap(refactoring: Dict) -> List[str]:
120
+ """Render the refactoring roadmap priority list."""
115
121
  priorities = refactoring.get("priorities", [])
116
122
  if not priorities:
117
123
  return []
@@ -128,6 +134,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
128
134
 
129
135
  @staticmethod
130
136
  def _render_evolution(evolution: List[Dict]) -> List[str]:
137
+ """Render the Evolution table with CC trend delta."""
131
138
  if len(evolution) <= 1:
132
139
  return []
133
140
  lines = [
@@ -161,6 +168,7 @@ class ArticleViewGenerator(ViewGeneratorMixin):
161
168
 
162
169
  @staticmethod
163
170
  def _render_footer() -> List[str]:
171
+ """Render the article footer with generation timestamp."""
164
172
  return [
165
173
  "---",
166
174
  f"*Generated by code2llm on {datetime.now().strftime('%Y-%m-%d %H:%M')}*",
@@ -64,6 +64,7 @@ class ContextExporter(BaseExporter):
64
64
  return path
65
65
 
66
66
  def _get_overview(self, result: AnalysisResult) -> List[str]:
67
+ """Build the Overview section listing language, file counts, and top classes."""
67
68
  lang_info = self._detect_languages(result)
68
69
  primary = lang_info[0][0] if lang_info else "unknown"
69
70
  lang_summary = ", ".join(f"{l}: {c}" for l, c in lang_info[:5])
@@ -99,6 +100,7 @@ class ContextExporter(BaseExporter):
99
100
  return sorted(lang_counts.items(), key=lambda x: -x[1])
100
101
 
101
102
  def _get_architecture_by_module(self, result: AnalysisResult) -> List[str]:
103
+ """Build the Architecture section grouped by module with stats."""
102
104
  lines = ["## Architecture by Module", ""]
103
105
  module_stats = []
104
106
  for mod_name, mod in result.modules.items():
@@ -121,6 +123,7 @@ class ContextExporter(BaseExporter):
121
123
  def _get_important_entries(
122
124
  self, result: AnalysisResult
123
125
  ) -> List[Tuple[str, int, Any]]:
126
+ """Return (name, fan_out, func_info) triples for notable entry-point functions."""
124
127
  entries = []
125
128
  for ep in result.entry_points:
126
129
  func = result.functions.get(ep)
@@ -133,6 +136,7 @@ class ContextExporter(BaseExporter):
133
136
  def _get_key_entry_points(
134
137
  self, important_entries: List[Tuple[str, int, Any]]
135
138
  ) -> List[str]:
139
+ """Build the Key Entry Points section from pre-ranked entry list."""
136
140
  lines = ["## Key Entry Points", "", "Main execution flows into the system:", ""]
137
141
  for ep, _, func in important_entries[:30]:
138
142
  lines.append(f"### {ep}")
@@ -13,6 +13,7 @@ class ContextViewGenerator(ViewGeneratorMixin):
13
13
  """Generate context.md from project.yaml data."""
14
14
 
15
15
  def _render(self, data: Dict[str, Any]) -> List[str]:
16
+ """Render the full context narrative from project.yaml data."""
16
17
  proj = data.get("project", {})
17
18
  health = data.get("health", {})
18
19
  modules = data.get("modules", [])
@@ -30,6 +31,7 @@ class ContextViewGenerator(ViewGeneratorMixin):
30
31
 
31
32
  @staticmethod
32
33
  def _render_overview(proj: Dict, health: Dict) -> List[str]:
34
+ """Render the Overview section with key project stats."""
33
35
  stats = proj.get("stats", {})
34
36
  return [
35
37
  "# System Architecture Analysis",
@@ -49,6 +51,7 @@ class ContextViewGenerator(ViewGeneratorMixin):
49
51
 
50
52
  @staticmethod
51
53
  def _render_architecture(modules: List[Dict]) -> List[str]:
54
+ """Render the Architecture section grouped by directory."""
52
55
  lines = ["## Architecture", ""]
53
56
  dir_groups: Dict[str, List[Dict]] = {}
54
57
  for m in modules:
@@ -81,6 +84,7 @@ class ContextViewGenerator(ViewGeneratorMixin):
81
84
 
82
85
  @staticmethod
83
86
  def _render_exports(modules: List[Dict]) -> List[str]:
87
+ """Render the Key Exports section (flagged classes and functions)."""
84
88
  lines = ["## Key Exports", ""]
85
89
  for m in modules:
86
90
  for exp in m.get("exports", []):
@@ -104,6 +108,7 @@ class ContextViewGenerator(ViewGeneratorMixin):
104
108
 
105
109
  @staticmethod
106
110
  def _render_hotspots(hotspots: List[Dict]) -> List[str]:
111
+ """Render the Hotspots section (top 7 by fan-out)."""
107
112
  if not hotspots:
108
113
  return []
109
114
  lines = ["## Hotspots (High Fan-Out)", ""]
@@ -116,6 +121,7 @@ class ContextViewGenerator(ViewGeneratorMixin):
116
121
 
117
122
  @staticmethod
118
123
  def _render_refactoring(refactoring: Dict) -> List[str]:
124
+ """Render the Refactoring Priorities table."""
119
125
  priorities = refactoring.get("priorities", [])
120
126
  if not priorities:
121
127
  return []
@@ -23,10 +23,12 @@ class HTMLDashboardGenerator:
23
23
  """
24
24
 
25
25
  def __init__(self):
26
+ """Initialise with a DashboardDataBuilder and DashboardRenderer."""
26
27
  self._data_builder = DashboardDataBuilder()
27
28
  self._renderer = DashboardRenderer()
28
29
 
29
30
  def generate(self, data: Dict[str, Any], output_path: str) -> None:
31
+ """Render dashboard HTML from data and write it to output_path."""
30
32
  html = self._render(data)
31
33
  Path(output_path).parent.mkdir(parents=True, exist_ok=True)
32
34
  with open(output_path, "w", encoding="utf-8") as f:
@@ -159,6 +159,7 @@ class PlanfileTicketsExporter(BaseExporter):
159
159
 
160
160
 
161
161
  def _high_cc_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion]:
162
+ """Yield refactor tickets for functions whose cyclomatic complexity exceeds the limit."""
162
163
  tickets: list[PlanfileTicketSuggestion] = []
163
164
  for func in result.functions.values():
164
165
  cc = _function_cc(func)
@@ -187,6 +188,7 @@ def _high_cc_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion]:
187
188
 
188
189
 
189
190
  def _god_module_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion]:
191
+ """Yield split tickets for modules that are too large (god-module smell)."""
190
192
  tickets: list[PlanfileTicketSuggestion] = []
191
193
  for module in result.modules.values():
192
194
  class_count = len(module.classes)
@@ -213,6 +215,7 @@ def _god_module_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion
213
215
 
214
216
 
215
217
  def _duplicate_class_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion]:
218
+ """Yield deduplication tickets for class pairs with ≥60% method-name overlap."""
216
219
  tickets: list[PlanfileTicketSuggestion] = []
217
220
  classes = list(result.classes.values())
218
221
  for index, left in enumerate(classes):
@@ -251,6 +254,7 @@ def _duplicate_class_tickets(result: AnalysisResult) -> list[PlanfileTicketSugge
251
254
 
252
255
 
253
256
  def _smell_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion]:
257
+ """Yield tickets for structural smells not already covered by other ticket generators."""
254
258
  tickets: list[PlanfileTicketSuggestion] = []
255
259
  for smell in result.smells:
256
260
  if smell.type == "god_function" and smell.context.get("complexity", 0) >= _CC_LIMIT:
@@ -277,6 +281,7 @@ def _smell_tickets(result: AnalysisResult) -> list[PlanfileTicketSuggestion]:
277
281
 
278
282
 
279
283
  def _function_cc(func: FunctionInfo) -> int:
284
+ """Extract the cyclomatic complexity integer from a FunctionInfo, defaulting to 0."""
280
285
  raw = func.complexity.get("cyclomatic_complexity", func.complexity.get("cc", 0))
281
286
  try:
282
287
  return int(raw)
@@ -285,10 +290,12 @@ def _function_cc(func: FunctionInfo) -> int:
285
290
 
286
291
 
287
292
  def _method_names(cls: ClassInfo) -> set[str]:
293
+ """Return the set of unqualified method names for a class."""
288
294
  return {method.split(".")[-1] for method in cls.methods}
289
295
 
290
296
 
291
297
  def _rel_path(path: str, project_path: str) -> str:
298
+ """Return path relative to project_path, falling back to the original if resolution fails."""
292
299
  if not path:
293
300
  return ""
294
301
  raw = Path(path)
@@ -11,11 +11,13 @@ from code2llm.exporters.flow_constants import is_excluded_path
11
11
 
12
12
 
13
13
  def _is_excluded(path: str) -> bool:
14
+ """Return True if the given file path should be excluded from duplicate analysis."""
14
15
  return is_excluded_path(path)
15
16
 
16
17
 
17
18
  @lru_cache(maxsize=4096)
18
19
  def _rel_path(fpath: str, project_path: str) -> str:
20
+ """Return fpath relative to project_path, or fpath unchanged if resolution fails."""
19
21
  if not project_path or not fpath:
20
22
  return fpath or ""
21
23
  try:
@@ -51,6 +53,7 @@ def _package_of_module(module_name: str) -> str:
51
53
 
52
54
 
53
55
  def _traits_from_cfg(fi: FunctionInfo, result: AnalysisResult) -> list:
56
+ """Derive structural trait labels (loops/cond/ret) from a function's CFG nodes."""
54
57
  traits = []
55
58
  node_types = set()
56
59
  for nid in fi.cfg_nodes or []:
@@ -67,6 +70,7 @@ def _traits_from_cfg(fi: FunctionInfo, result: AnalysisResult) -> list:
67
70
 
68
71
 
69
72
  def _dup_file_set(ctx: Dict[str, Any]) -> Set[str]:
73
+ """Return the set of file paths that appear in at least one duplicate pair."""
70
74
  s: Set[str] = set()
71
75
  for d in ctx["duplicates"]:
72
76
  s.add(d["fileA"])
@@ -75,6 +79,7 @@ def _dup_file_set(ctx: Dict[str, Any]) -> Set[str]:
75
79
 
76
80
 
77
81
  def _hotspot_description(fi: FunctionInfo, fan_out: int) -> str:
82
+ """Generate a short human-readable description for a hotspot function."""
78
83
  if fi.name == "to_dict":
79
84
  return f"{fan_out} conditional field serializations"
80
85
  if "format" in fi.name.lower() or "dispatch" in fi.name.lower():
@@ -52,6 +52,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
52
52
  """Generate project.toon.yaml from project.yaml data."""
53
53
 
54
54
  def _render(self, data: Dict[str, Any]) -> List[str]:
55
+ """Render the full TOON view from project.yaml data."""
55
56
  proj = data.get("project", {})
56
57
  health = data.get("health", {})
57
58
  modules = data.get("modules", [])
@@ -71,6 +72,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
71
72
 
72
73
  @staticmethod
73
74
  def _render_header(proj: Dict) -> List[str]:
75
+ """Render single-line project header with key stats."""
74
76
  stats = proj.get("stats", {})
75
77
  lang = proj.get("language", "unknown")
76
78
  return [
@@ -85,6 +87,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
85
87
 
86
88
  @staticmethod
87
89
  def _render_health(health: Dict) -> List[str]:
90
+ """Render one-line HEALTH summary."""
88
91
  return [
89
92
  "HEALTH:",
90
93
  f" CC̄={health.get('cc_avg', 0)} "
@@ -95,6 +98,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
95
98
 
96
99
  @staticmethod
97
100
  def _render_alerts(health: Dict) -> List[str]:
101
+ """Render ALERTS block (returns empty list if no alerts)."""
98
102
  alerts = health.get("alerts", [])
99
103
  if not alerts:
100
104
  return []
@@ -116,6 +120,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
116
120
 
117
121
  @staticmethod
118
122
  def _render_modules(modules: List[Dict]) -> List[str]:
123
+ """Render MODULES block with top-15 by size and language breakdown."""
119
124
  # Show top modules by size (lines) - works for all languages
120
125
  top_by_lines = sorted(modules, key=lambda m: m.get("lines", 0), reverse=True)[
121
126
  :15
@@ -149,6 +154,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
149
154
 
150
155
  @staticmethod
151
156
  def _render_hotspots(hotspots: List[Dict]) -> List[str]:
157
+ """Render HOTSPOTS block (top 5 by fan-out)."""
152
158
  if not hotspots:
153
159
  return []
154
160
  lines = ["", f"HOTSPOTS[{len(hotspots)}]:"]
@@ -161,6 +167,7 @@ class ToonViewGenerator(ViewGeneratorMixin):
161
167
 
162
168
  @staticmethod
163
169
  def _render_refactoring(refactoring: Dict) -> List[str]:
170
+ """Render REFACTOR block with top 5 refactoring actions."""
164
171
  priorities = refactoring.get("priorities", [])
165
172
  if not priorities:
166
173
  return []
@@ -11,6 +11,8 @@ from .parsing import _parse_call_label
11
11
 
12
12
  @dataclass(frozen=True)
13
13
  class FuncSummary:
14
+ """Immutable summary of a single function's decisions and outgoing calls."""
15
+
14
16
  name: str
15
17
  file: Optional[str]
16
18
  line: Optional[int]
@@ -67,6 +69,7 @@ def _pick_relevant_functions(
67
69
  ]
68
70
 
69
71
  def score(fn: str) -> int:
72
+ """Rank function by node count, calls, and entry-point status for flow selection."""
70
73
  s = 0
71
74
  s += min(500, counts.get(fn, 0)) # node count baseline
72
75
  for needle, boost in keyword_boosts:
@@ -147,6 +150,7 @@ def _summarize_functions(
147
150
  def _build_call_graph(
148
151
  func_summaries: Dict[str, FuncSummary], known_functions: Set[str]
149
152
  ) -> Dict[str, Set[str]]:
153
+ """Build adjacency list of internal calls between known functions."""
150
154
  g: Dict[str, Set[str]] = defaultdict(set)
151
155
  for fn, s in func_summaries.items():
152
156
  for callee in s.calls:
@@ -158,6 +162,7 @@ def _build_call_graph(
158
162
  def _reachable(
159
163
  g: Dict[str, Set[str]], roots: Iterable[str], max_nodes: int
160
164
  ) -> List[str]:
165
+ """BFS from roots through call graph; return up to max_nodes reachable functions."""
161
166
  seen: Set[str] = set()
162
167
  q: deque[str] = deque([r for r in roots if r])
163
168
 
@@ -11,6 +11,7 @@ from .generator import generate_llm_flow, render_llm_flow_md
11
11
 
12
12
 
13
13
  def create_parser() -> argparse.ArgumentParser:
14
+ """Build and return the argument parser for the llm-flow-generator CLI."""
14
15
  p = argparse.ArgumentParser(
15
16
  prog="llm-flow-generator",
16
17
  description="Generate compact LLM-friendly app flow summary from code2llm analysis.yaml",
@@ -39,6 +40,7 @@ def create_parser() -> argparse.ArgumentParser:
39
40
 
40
41
 
41
42
  def main(argv: Optional[List[str]] = None) -> int:
43
+ """Entry point: parse args, run flow generation, write output files."""
42
44
  args = create_parser().parse_args(argv)
43
45
 
44
46
  input_path = Path(args.input)
@@ -16,6 +16,7 @@ def generate_llm_flow(
16
16
  limit_decisions: int,
17
17
  limit_calls: int,
18
18
  ) -> Dict[str, Any]:
19
+ """Build a compact LLM-friendly flow dict from a code2llm analysis result."""
19
20
  nodes = _collect_nodes(analysis)
20
21
  entrypoints = _collect_entrypoints(nodes)
21
22
 
@@ -62,6 +63,7 @@ def generate_llm_flow(
62
63
 
63
64
 
64
65
  def render_llm_flow_md(flow: Dict[str, Any]) -> str:
66
+ """Render a flow dict (from generate_llm_flow) as a Markdown summary string."""
65
67
  app = _as_dict(flow.get("app"))
66
68
  entrypoints = _as_list(app.get("entrypoints"))
67
69
  selected = _as_list(_as_dict(flow.get("flow")).get("selected_functions"))
@@ -9,10 +9,12 @@ from ._utils import dump_yaml
9
9
 
10
10
 
11
11
  def _strip_bom(text: str) -> str:
12
+ """Strip UTF-8 BOM from the start of text if present."""
12
13
  return text[1:] if text.startswith("\ufeff") else text
13
14
 
14
15
 
15
16
  def _ensure_list(value: Any) -> List[Any]:
17
+ """Wrap scalar value in a list; return value unchanged if already a list."""
16
18
  if value is None:
17
19
  return []
18
20
  if isinstance(value, list):
@@ -30,6 +32,7 @@ def _deep_get(d: Dict[str, Any], path: Tuple[str, ...]) -> Any:
30
32
 
31
33
 
32
34
  def normalize_llm_task(data: Dict[str, Any]) -> Dict[str, Any]:
35
+ """Normalise a raw LLM task dict into a canonical structure with all required keys."""
33
36
  task = data.get("task") or {}
34
37
  context = data.get("context") or {}
35
38
  deliverables = data.get("deliverables") or {}
@@ -308,6 +311,7 @@ def load_input(path: Path) -> Dict[str, Any]:
308
311
 
309
312
 
310
313
  def create_parser() -> argparse.ArgumentParser:
314
+ """Build and return the CLI argument parser for the LLM task generator."""
311
315
  p = argparse.ArgumentParser(
312
316
  prog="llm-task-generator",
313
317
  description="Generate normalized llm_task.yaml from simplified task spec (text/YAML/JSON).",
@@ -325,6 +329,7 @@ def create_parser() -> argparse.ArgumentParser:
325
329
 
326
330
 
327
331
  def main(argv: Optional[List[str]] = None) -> int:
332
+ """Entry point for the LLM task generator CLI."""
328
333
  args = create_parser().parse_args(argv)
329
334
 
330
335
  input_path = Path(args.input)
@@ -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.155"
7
+ __version__ = "0.5.156"
8
8
 
9
9
  from .pipeline import NLPPipeline
10
10
  from .normalization import QueryNormalizer