code2llm 0.5.159__tar.gz → 0.5.160__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.159/code2llm.egg-info → code2llm-0.5.160}/PKG-INFO +5 -5
  2. {code2llm-0.5.159 → code2llm-0.5.160}/README.md +4 -4
  3. code2llm-0.5.160/VERSION +1 -0
  4. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/_data_impl.py +15 -15
  6. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/coupling.py +1 -0
  7. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/side_effects.py +21 -44
  8. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/smells.py +1 -0
  9. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_analysis.py +17 -19
  10. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/formats.py +23 -25
  11. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator.py +32 -48
  12. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/prompt.py +30 -55
  13. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/analyzer.py +1 -0
  14. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/file_analyzer.py +1 -0
  15. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/_c_parser.py +14 -17
  16. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/ts_parser.py +2 -0
  17. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/refactoring.py +1 -0
  18. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/source_classifier.py +16 -20
  19. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming/incremental.py +1 -0
  20. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming/prioritizer.py +1 -0
  21. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/flow_exporter.py +1 -0
  22. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/details.py +16 -24
  23. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/calls.py +15 -13
  24. code2llm-0.5.160/code2llm/exporters/mermaid/compact.py +52 -0
  25. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/planfile_tickets.py +12 -11
  26. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/core.py +15 -32
  27. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/health.py +30 -38
  28. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/hotspots.py +57 -63
  29. code2llm-0.5.160/code2llm/exporters/readme/insights.py +58 -0
  30. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/__init__.py +1 -0
  31. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/helpers.py +42 -62
  32. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/metrics.py +1 -0
  33. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/metrics_duplicates.py +1 -0
  34. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/metrics_health.py +1 -0
  35. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/nlp/__init__.py +1 -1
  36. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/patterns/detector.py +1 -0
  37. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/refactor/prompt_engine.py +1 -0
  38. {code2llm-0.5.159 → code2llm-0.5.160/code2llm.egg-info}/PKG-INFO +5 -5
  39. {code2llm-0.5.159 → code2llm-0.5.160}/pyproject.toml +1 -1
  40. code2llm-0.5.159/VERSION +0 -1
  41. code2llm-0.5.159/code2llm/exporters/mermaid/compact.py +0 -69
  42. code2llm-0.5.159/code2llm/exporters/readme/insights.py +0 -54
  43. {code2llm-0.5.159 → code2llm-0.5.160}/LICENSE +0 -0
  44. {code2llm-0.5.159 → code2llm-0.5.160}/MANIFEST.in +0 -0
  45. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/__main__.py +0 -0
  46. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/__init__.py +0 -0
  47. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/call_graph.py +0 -0
  48. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/cfg.py +0 -0
  49. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/data_analysis.py +0 -0
  50. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/dfg.py +0 -0
  51. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/pipeline_classifier.py +0 -0
  52. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/pipeline_detector.py +0 -0
  53. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/pipeline_resolver.py +0 -0
  54. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/type_inference.py +0 -0
  55. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/utils/__init__.py +0 -0
  56. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/analysis/utils/ast_helpers.py +0 -0
  57. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/api.py +0 -0
  58. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli.py +0 -0
  59. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_commands.py +0 -0
  60. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/__init__.py +0 -0
  61. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/code2logic.py +0 -0
  62. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  63. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  64. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
  65. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/cli_parser.py +0 -0
  66. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/__init__.py +0 -0
  67. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/ast_registry.py +0 -0
  68. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/config.py +0 -0
  69. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/export_pipeline.py +0 -0
  70. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/file_cache.py +0 -0
  71. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/file_filter.py +0 -0
  72. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/gitignore.py +0 -0
  73. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/incremental.py +0 -0
  74. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/__init__.py +0 -0
  75. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/_calls.py +0 -0
  76. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/_complexity.py +0 -0
  77. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/base.py +0 -0
  78. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/cpp.py +0 -0
  79. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/csharp.py +0 -0
  80. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/generic.py +0 -0
  81. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/go_lang.py +0 -0
  82. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/java.py +0 -0
  83. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/php.py +0 -0
  84. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/ruby.py +0 -0
  85. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/rust.py +0 -0
  86. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/ts_extractors.py +0 -0
  87. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/lang/typescript.py +0 -0
  88. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/large_repo.py +0 -0
  89. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/models.py +0 -0
  90. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/persistent_cache.py +0 -0
  91. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/repo_files.py +0 -0
  92. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming/__init__.py +0 -0
  93. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming/cache.py +0 -0
  94. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming/scanner.py +0 -0
  95. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming/strategies.py +0 -0
  96. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/streaming_analyzer.py +0 -0
  97. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/core/toon_size_manager.py +0 -0
  98. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/__init__.py +0 -0
  99. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/article_view.py +0 -0
  100. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/base.py +0 -0
  101. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/context_exporter.py +0 -0
  102. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/context_view.py +0 -0
  103. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/dashboard_data.py +0 -0
  104. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/dashboard_renderer.py +0 -0
  105. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution/__init__.py +0 -0
  106. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution/computation.py +0 -0
  107. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution/constants.py +0 -0
  108. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution/exclusion.py +0 -0
  109. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution/render.py +0 -0
  110. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution/yaml_export.py +0 -0
  111. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/evolution_exporter.py +0 -0
  112. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/flow_constants.py +0 -0
  113. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/flow_renderer.py +0 -0
  114. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/html_dashboard.py +0 -0
  115. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/index_generator/__init__.py +0 -0
  116. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/index_generator/renderer.py +0 -0
  117. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/index_generator/scanner.py +0 -0
  118. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/index_generator.py +0 -0
  119. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/json_exporter.py +0 -0
  120. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/llm_exporter.py +0 -0
  121. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/__init__.py +0 -0
  122. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/alerts.py +0 -0
  123. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/header.py +0 -0
  124. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/module_list.py +0 -0
  125. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/utils.py +0 -0
  126. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map/yaml_export.py +0 -0
  127. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/map_exporter.py +0 -0
  128. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/__init__.py +0 -0
  129. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/classic.py +0 -0
  130. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  131. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  132. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/flow_full.py +0 -0
  133. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid/utils.py +0 -0
  134. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid_exporter.py +0 -0
  135. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  136. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/__init__.py +0 -0
  137. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/constants.py +0 -0
  138. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/evolution.py +0 -0
  139. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml/modules.py +0 -0
  140. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/project_yaml_exporter.py +0 -0
  141. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/readme/__init__.py +0 -0
  142. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/readme/content.py +0 -0
  143. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/readme/files.py +0 -0
  144. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/readme/sections.py +0 -0
  145. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/readme_exporter.py +0 -0
  146. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/report_generators.py +0 -0
  147. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
  148. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/_render_section_helpers.py +0 -0
  149. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/constants.py +0 -0
  150. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/metrics_core.py +0 -0
  151. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/module_detail.py +0 -0
  152. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon/renderer.py +0 -0
  153. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon.py +0 -0
  154. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/toon_view.py +0 -0
  155. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/validate_project.py +0 -0
  156. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/exporters/yaml_exporter.py +0 -0
  157. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/__init__.py +0 -0
  158. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/_utils.py +0 -0
  159. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/__init__.py +0 -0
  160. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/analysis.py +0 -0
  161. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/cli.py +0 -0
  162. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/generator.py +0 -0
  163. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/nodes.py +0 -0
  164. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/parsing.py +0 -0
  165. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow/utils.py +0 -0
  166. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_flow.py +0 -0
  167. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/llm_task.py +0 -0
  168. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/mermaid/__init__.py +0 -0
  169. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/mermaid/fix.py +0 -0
  170. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/mermaid/png.py +0 -0
  171. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/mermaid/validation.py +0 -0
  172. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/generators/mermaid.py +0 -0
  173. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/nlp/config.py +0 -0
  174. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/nlp/entity_resolution.py +0 -0
  175. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/nlp/intent_matching.py +0 -0
  176. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/nlp/normalization.py +0 -0
  177. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/nlp/pipeline.py +0 -0
  178. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/parsers/toon_parser.py +0 -0
  179. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/patterns/__init__.py +0 -0
  180. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm/refactor/__init__.py +0 -0
  181. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm.egg-info/SOURCES.txt +0 -0
  182. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm.egg-info/dependency_links.txt +0 -0
  183. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm.egg-info/entry_points.txt +0 -0
  184. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm.egg-info/requires.txt +0 -0
  185. {code2llm-0.5.159 → code2llm-0.5.160}/code2llm.egg-info/top_level.txt +0 -0
  186. {code2llm-0.5.159 → code2llm-0.5.160}/setup.cfg +0 -0
  187. {code2llm-0.5.159 → code2llm-0.5.160}/setup.py +0 -0
  188. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_advanced_analysis.py +0 -0
  189. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_analyzer.py +0 -0
  190. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_cache_invalidation_e2e.py +0 -0
  191. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_calls_toon_export.py +0 -0
  192. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_declarative_collection.py +0 -0
  193. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_deep_analysis.py +0 -0
  194. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_edge_cases.py +0 -0
  195. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_export_cache_flags.py +0 -0
  196. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_file_analyzer_tagging.py +0 -0
  197. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_flow_exporter.py +0 -0
  198. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_format_quality.py +0 -0
  199. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_multilanguage_e2e.py +0 -0
  200. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_nlp_pipeline.py +0 -0
  201. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_nonpython_cc_calls.py +0 -0
  202. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_orchestrator_cache_mtime.py +0 -0
  203. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_persistent_cache.py +0 -0
  204. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_pipeline_detector.py +0 -0
  205. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_planfile_tickets_exporter.py +0 -0
  206. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_project_toon_export.py +0 -0
  207. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_prompt_engine.py +0 -0
  208. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_prompt_txt.py +0 -0
  209. {code2llm-0.5.159 → code2llm-0.5.160}/tests/test_refactoring_engine.py +0 -0
  210. {code2llm-0.5.159 → code2llm-0.5.160}/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.159
3
+ Version: 0.5.160
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.159-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-80.7h-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.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)
71
71
 
72
- - 🤖 **LLM usage:** $7.5000 (216 commits)
73
- - 👤 **Human dev:** ~$8072 (80.7h @ $100/h, 30min dedup)
72
+ - 🤖 **LLM usage:** $7.5000 (221 commits)
73
+ - 👤 **Human dev:** ~$8118 (81.2h @ $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
 
@@ -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.159-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-80.7h-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.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)
7
7
 
8
- - 🤖 **LLM usage:** $7.5000 (216 commits)
9
- - 👤 **Human dev:** ~$8072 (80.7h @ $100/h, 30min dedup)
8
+ - 🤖 **LLM usage:** $7.5000 (221 commits)
9
+ - 👤 **Human dev:** ~$8118 (81.2h @ $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
 
@@ -0,0 +1 @@
1
+ 0.5.160
@@ -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.159"
11
+ __version__ = "0.5.160"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -222,26 +222,26 @@ def _analyze_data_types(result: AnalysisResult) -> list:
222
222
  # ---------------------------------------------------------------------------
223
223
 
224
224
 
225
+ _NAME_TYPE_KEYWORDS: List[tuple] = [
226
+ ("list", ("list", "items")),
227
+ ("dict", ("dict", "map")),
228
+ ("str", ("text", "string")),
229
+ ("int", ("count", "index")),
230
+ ]
231
+ _DOC_TYPE_KEYWORDS: List[tuple] = [
232
+ ("list", ("list",)),
233
+ ("dict", ("dict",)),
234
+ ("str", ("string", "text")),
235
+ ]
236
+
237
+
225
238
  def _get_function_data_types(func) -> list:
226
239
  """Derive data type labels for a function from its name and docstring."""
227
- types: List[str] = []
228
240
  name = func.name.lower()
229
- if "list" in name or "items" in name:
230
- types.append("list")
231
- if "dict" in name or "map" in name:
232
- types.append("dict")
233
- if "text" in name or "string" in name:
234
- types.append("str")
235
- if "count" in name or "index" in name:
236
- types.append("int")
241
+ types = [dt for dt, kws in _NAME_TYPE_KEYWORDS if any(kw in name for kw in kws)]
237
242
  if func.docstring:
238
243
  doc = func.docstring.lower()
239
- if "list" in doc:
240
- types.append("list")
241
- if "dict" in doc:
242
- types.append("dict")
243
- if "string" in doc or "text" in doc:
244
- types.append("str")
244
+ types += [dt for dt, kws in _DOC_TYPE_KEYWORDS if any(kw in doc for kw in kws)]
245
245
  return list(set(types))
246
246
 
247
247
 
@@ -8,6 +8,7 @@ class CouplingAnalyzer:
8
8
  """Analyze coupling between modules."""
9
9
 
10
10
  def __init__(self, result: AnalysisResult):
11
+ """Initialise with the analysis result to analyse for coupling."""
11
12
  self.result = result
12
13
 
13
14
  def analyze(self) -> Dict[str, Any]:
@@ -323,54 +323,31 @@ class SideEffectDetector:
323
323
  else:
324
324
  info.classification = "pure"
325
325
 
326
+ # word-sets and their target attribute names, in priority order
327
+ _HEURISTIC_CATEGORIES: List[tuple] = [
328
+ (
329
+ "IO",
330
+ {"write", "read", "open", "save", "load", "export", "dump", "print", "mkdir", "rmdir", "remove"},
331
+ "io_operations",
332
+ ),
333
+ ("cache", {"cache", "memoize", "lru_cache", "store", "fetch"}, "cache_operations"),
334
+ ("mutation", {"set_", "update", "modify", "mutate", "append", "insert", "delete", "fix", "patch"}, "mutations"),
335
+ ]
336
+
326
337
  def _heuristic_classify(self, fi: FunctionInfo, info: SideEffectInfo) -> None:
327
338
  """Classify based on function name and calls (fallback)."""
328
339
  name_lower = fi.name.lower()
329
340
  calls_lower = {c.lower() for c in fi.calls}
330
-
331
- io_words = {
332
- "write",
333
- "read",
334
- "open",
335
- "save",
336
- "load",
337
- "export",
338
- "dump",
339
- "print",
340
- "mkdir",
341
- "rmdir",
342
- "remove",
343
- }
344
- cache_words = {"cache", "memoize", "lru_cache", "store", "fetch"}
345
- mutation_words = {
346
- "set_",
347
- "update",
348
- "modify",
349
- "mutate",
350
- "append",
351
- "insert",
352
- "delete",
353
- "fix",
354
- "patch",
355
- }
356
-
357
- if any(w in name_lower for w in io_words):
358
- info.classification = "IO"
359
- info.io_operations.append(f"name:{fi.name}")
360
- elif any(any(w in c for w in io_words) for c in calls_lower):
361
- info.classification = "IO"
362
- info.io_operations.append("calls:IO")
363
- elif any(w in name_lower for w in cache_words):
364
- info.classification = "cache"
365
- info.cache_operations.append(f"name:{fi.name}")
366
- elif any(any(w in c for w in cache_words) for c in calls_lower):
367
- info.classification = "cache"
368
- info.cache_operations.append("calls:cache")
369
- elif any(w in name_lower for w in mutation_words):
370
- info.classification = "mutation"
371
- info.mutations.append(f"name:{fi.name}")
372
- else:
373
- info.classification = "pure"
341
+ for classification, words, attr in self._HEURISTIC_CATEGORIES:
342
+ if any(w in name_lower for w in words):
343
+ info.classification = classification
344
+ getattr(info, attr).append(f"name:{fi.name}")
345
+ return
346
+ if any(any(w in c for w in words) for c in calls_lower):
347
+ info.classification = classification
348
+ getattr(info, attr).append(f"calls:{classification}")
349
+ return
350
+ info.classification = "pure"
374
351
 
375
352
  # ------------------------------------------------------------------
376
353
  # AST helpers
@@ -9,6 +9,7 @@ class SmellDetector:
9
9
  """Detect code smells from analysis results."""
10
10
 
11
11
  def __init__(self, result: AnalysisResult):
12
+ """Initialise with the analysis result to detect code smells."""
12
13
  self.result = result
13
14
  # Pre-index mutations by scope — avoids O(n×m) full scans
14
15
  self._mutations_by_scope: Dict[str, list] = defaultdict(list)
@@ -218,30 +218,28 @@ def _analyze_all_subprojects(args, subprojects, output_dir: Path) -> list:
218
218
  return all_results
219
219
 
220
220
 
221
+ def _build_filter_config(args):
222
+ """Build a FilterConfig from CLI args (exclude patterns, gitignore flag)."""
223
+ from .core.config import FilterConfig
224
+ fc = FilterConfig()
225
+ if getattr(args, "exclude", None):
226
+ custom = [
227
+ f"*{p}*" if not p.startswith("*") and not p.endswith("*") else p
228
+ for p in args.exclude
229
+ ]
230
+ fc.exclude_patterns = list(set(fc.exclude_patterns + custom))
231
+ if getattr(args, "no_gitignore", False):
232
+ fc.gitignore_enabled = False
233
+ return fc
234
+
235
+
221
236
  def _analyze_subproject(args, subproject, output_dir: Path):
222
237
  """Analyze and export a single subproject."""
223
238
  from .core.analyzer import ProjectAnalyzer
224
- from .core.config import Config, FilterConfig
239
+ from .core.config import Config
225
240
  from .cli_exports import _export_simple_formats, _export_evolution
226
241
 
227
- # Start with default filter config
228
- filter_config = FilterConfig()
229
-
230
- # Apply custom exclude patterns if provided
231
- if hasattr(args, "exclude") and args.exclude:
232
- default_patterns = filter_config.exclude_patterns
233
- custom_patterns = [
234
- f"*{pattern}*"
235
- if not pattern.startswith("*") and not pattern.endswith("*")
236
- else pattern
237
- for pattern in args.exclude
238
- ]
239
- filter_config.exclude_patterns = list(set(default_patterns + custom_patterns))
240
-
241
- # Apply gitignore setting
242
- if hasattr(args, "no_gitignore") and args.no_gitignore:
243
- filter_config.gitignore_enabled = False
244
-
242
+ filter_config = _build_filter_config(args)
245
243
  config = Config(
246
244
  mode=args.mode,
247
245
  max_depth_enumeration=args.max_depth,
@@ -163,32 +163,8 @@ def _export_simple_formats(args, result, output_dir: Path, formats):
163
163
  if args.verbose:
164
164
  print(f" - {label}: {filepath} ({elapsed:.2f}s)")
165
165
 
166
- # Unified project.yaml (single source of truth)
167
166
  if "project-yaml" in formats:
168
- yaml_path = _export_project_yaml(args, result, output_dir)
169
- # Auto-generate all views from project.yaml
170
- data = load_project_yaml(str(yaml_path))
171
- view_map = {
172
- "project.toon.yaml": ToonViewGenerator(),
173
- "context.md": ContextViewGenerator(),
174
- "dashboard.html": HTMLDashboardGenerator(),
175
- }
176
- for filename, generator in view_map.items():
177
- filepath = output_dir / filename
178
- generator.generate(data, str(filepath))
179
- if args.verbose:
180
- print(f" - Generated view: {filepath}")
181
-
182
- # Auto-validate consistency
183
- from ..exporters.validate_project import validate_project_yaml
184
-
185
- is_valid, issues = validate_project_yaml(output_dir, verbose=args.verbose)
186
- if not is_valid and not args.verbose:
187
- print(
188
- f" ⚠ project.yaml validation: {len(issues)} issue(s)", file=sys.stderr
189
- )
190
- for issue in issues:
191
- print(f" - {issue}", file=sys.stderr)
167
+ _export_project_yaml_bundle(args, result, output_dir)
192
168
 
193
169
  if "yaml" in formats:
194
170
  _export_yaml(args, result, output_dir)
@@ -221,6 +197,28 @@ def _export_yaml(args, result, output_dir: Path):
221
197
  print(f" - YAML: {filepath}")
222
198
 
223
199
 
200
+ def _export_project_yaml_bundle(args, result, output_dir: Path) -> None:
201
+ """Export project.yaml and auto-generate all derived views, then validate."""
202
+ yaml_path = _export_project_yaml(args, result, output_dir)
203
+ data = load_project_yaml(str(yaml_path))
204
+ view_map = {
205
+ "project.toon.yaml": ToonViewGenerator(),
206
+ "context.md": ContextViewGenerator(),
207
+ "dashboard.html": HTMLDashboardGenerator(),
208
+ }
209
+ for filename, generator in view_map.items():
210
+ filepath = output_dir / filename
211
+ generator.generate(data, str(filepath))
212
+ if args.verbose:
213
+ print(f" - Generated view: {filepath}")
214
+ from ..exporters.validate_project import validate_project_yaml
215
+ is_valid, issues = validate_project_yaml(output_dir, verbose=args.verbose)
216
+ if not is_valid and not args.verbose:
217
+ print(f" \u26a0 project.yaml validation: {len(issues)} issue(s)", file=sys.stderr)
218
+ for issue in issues:
219
+ print(f" - {issue}", file=sys.stderr)
220
+
221
+
224
222
  def _export_mermaid_pngs(args, output_dir: Path) -> None:
225
223
  """Attempt PNG generation from .mmd files, with graceful fallback."""
226
224
  if args.no_png:
@@ -354,6 +354,26 @@ def _export_chunked(
354
354
  _chunked_impl(args, result, output_dir, source_path, formats, requested_formats)
355
355
 
356
356
 
357
+ # Maps suffix(es) → (prefix, suffix_str) for inserting after line 1.
358
+ # None = skip (no comment possible). "html" handled separately.
359
+ _COMMENT_PREFIXES = {
360
+ ".mmd": ("%%", ""),
361
+ ".export": ("%%", ""),
362
+ ".yaml": ("#", ""),
363
+ ".yml": ("#", ""),
364
+ ".txt": ("#", ""),
365
+ ".md": ("<!--", " -->"),
366
+ }
367
+ _SKIP_SUFFIXES = {".json"}
368
+
369
+
370
+ def _insert_after_first_line(content: str, tag: str, pre: str, suf: str) -> str:
371
+ """Return content with a comment line inserted after the first newline."""
372
+ parts = content.split("\n", 1)
373
+ rest = parts[1] if len(parts) == 2 else ""
374
+ return f"{parts[0]}\n{pre} {tag}{suf}\n{rest}"
375
+
376
+
357
377
  def _inject_generation_time(filepath: Path, elapsed: float) -> None:
358
378
  """Inject generation time comment into the second line of an exported file."""
359
379
  try:
@@ -361,58 +381,22 @@ def _inject_generation_time(filepath: Path, elapsed: float) -> None:
361
381
  if not path.exists():
362
382
  return
363
383
  suffix = path.suffix.lower()
364
- name = path.name.lower()
365
-
366
- # Only inject into text-based files
367
- if suffix not in (
368
- ".yaml",
369
- ".yml",
370
- ".md",
371
- ".txt",
372
- ".mmd",
373
- ".html",
374
- ".json",
375
- ".export",
376
- ):
377
- return
378
-
379
- content = path.read_text(encoding="utf-8")
380
- if not content:
381
- return
382
-
383
384
  tag = f"generated in {elapsed:.2f}s"
384
385
 
385
- if suffix in (".mmd", ".export"):
386
- # Mermaid uses %% for comments
387
- lines = content.split("\n", 1)
388
- if len(lines) == 2:
389
- content = f"{lines[0]}\n%% {tag}\n{lines[1]}"
390
- else:
391
- content = f"{lines[0]}\n%% {tag}\n"
392
- elif suffix in (".yaml", ".yml", ".txt") or name.endswith(".toon.yaml"):
393
- # YAML/text: insert '# generated in X.XXs' after first line
394
- lines = content.split("\n", 1)
395
- if len(lines) == 2:
396
- content = f"{lines[0]}\n# {tag}\n{lines[1]}"
397
- else:
398
- content = f"{lines[0]}\n# {tag}\n"
399
- elif suffix == ".md":
400
- # Markdown: insert HTML comment after first line
401
- lines = content.split("\n", 1)
402
- if len(lines) == 2:
403
- content = f"{lines[0]}\n<!-- {tag} -->\n{lines[1]}"
404
- else:
405
- content = f"{lines[0]}\n<!-- {tag} -->\n"
406
- elif suffix == ".html":
407
- # HTML: insert comment after <!DOCTYPE or <html>
408
- content = content.replace("\n", f"\n<!-- {tag} -->\n", 1)
409
- elif suffix == ".json":
410
- # JSON doesn't support comments — skip
386
+ if suffix in _SKIP_SUFFIXES:
411
387
  return
412
- else:
388
+ if suffix == ".html":
389
+ path.write_text(
390
+ path.read_text(encoding="utf-8").replace("\n", f"\n<!-- {tag} -->\n", 1),
391
+ encoding="utf-8",
392
+ )
413
393
  return
414
-
415
- path.write_text(content, encoding="utf-8")
394
+ pre, suf_str = _COMMENT_PREFIXES.get(suffix, (None, None))
395
+ if pre is None:
396
+ return
397
+ content = path.read_text(encoding="utf-8")
398
+ if content:
399
+ path.write_text(_insert_after_first_line(content, tag, pre, suf_str), encoding="utf-8")
416
400
  except Exception:
417
401
  pass # Never fail the export pipeline for a comment
418
402
 
@@ -282,63 +282,38 @@ def _build_missing_files_section(output_dir: Path, output_rel_path: str) -> List
282
282
  return lines
283
283
 
284
284
 
285
- def _analyze_generated_files(output_dir: Path, subprojects: list = None) -> dict:
286
- """Analyze which files were generated and determine appropriate focus areas."""
287
- analysis_file = _find_existing_prompt_file(
288
- output_dir, ("analysis.toon", "analysis.toon.yaml")
289
- )
290
- map_file = _find_existing_prompt_file(output_dir, ("map.toon.yaml",))
291
- evolution_file = _find_existing_prompt_file(output_dir, ("evolution.toon.yaml",))
292
- project_toon_file = _find_existing_prompt_file(output_dir, ("project.toon.yaml",))
293
- context_file = _find_existing_prompt_file(output_dir, ("context.md",))
294
- readme_file = _find_existing_prompt_file(output_dir, ("README.md",))
295
- project_logic_file = _find_existing_prompt_file(
296
- output_dir, ("project.toon", "project/project.toon", "project.toon.txt")
297
- )
298
- validation_file = _find_existing_prompt_file(
299
- output_dir, ("project/validation.toon.yaml",)
300
- )
301
- duplication_file = _find_existing_prompt_file(
302
- output_dir, ("project/duplication.toon.yaml",)
303
- )
285
+ # (has_key, file_key, candidate_filenames, default_path)
286
+ _OUTPUT_FILE_SPECS = [
287
+ ("has_analysis_toon", "analysis_file", ("analysis.toon", "analysis.toon.yaml"), "analysis.toon"),
288
+ ("has_map_toon", "map_file", ("map.toon.yaml",), "map.toon.yaml"),
289
+ ("has_evolution_toon", "evolution_file", ("evolution.toon.yaml",), "evolution.toon.yaml"),
290
+ ("has_project_toon_yaml","project_toon_file", ("project.toon.yaml",), "project.toon.yaml"),
291
+ ("has_context_md", "context_file", ("context.md",), "context.md"),
292
+ ("has_readme_md", "readme_file", ("README.md",), "README.md"),
293
+ ("has_project_logic", "project_logic_file", ("project.toon", "project/project.toon", "project.toon.txt"), "project.toon"),
294
+ ("has_validation_toon", "validation_file", ("project/validation.toon.yaml",), "project/validation.toon.yaml"),
295
+ ("has_duplication_toon", "duplication_file", ("project/duplication.toon.yaml",), "project/duplication.toon.yaml"),
296
+ ]
297
+ _ACTIONABLE_KEYS = {"has_analysis_toon", "has_map_toon", "has_evolution_toon",
298
+ "has_project_toon_yaml", "has_project_logic",
299
+ "has_validation_toon", "has_duplication_toon"}
300
+
301
+
302
+ def _probe_output_files(output_dir: Path) -> dict:
303
+ """Probe output_dir for known generated files, returning presence flags and paths."""
304
+ result: dict = {}
305
+ for has_key, file_key, candidates, default in _OUTPUT_FILE_SPECS:
306
+ found = _find_existing_prompt_file(output_dir, candidates)
307
+ result[has_key] = found is not None
308
+ result[file_key] = found or default
309
+ return result
304
310
 
305
- analysis = {
306
- "has_analysis_toon": analysis_file is not None,
307
- "analysis_file": analysis_file or "analysis.toon",
308
- "has_map_toon": map_file is not None,
309
- "map_file": map_file or "map.toon.yaml",
310
- "has_evolution_toon": evolution_file is not None,
311
- "evolution_file": evolution_file or "evolution.toon.yaml",
312
- "has_project_toon_yaml": project_toon_file is not None,
313
- "project_toon_file": project_toon_file or "project.toon.yaml",
314
- "has_context_md": context_file is not None,
315
- "context_file": context_file or "context.md",
316
- "has_readme_md": readme_file is not None,
317
- "readme_file": readme_file or "README.md",
318
- "has_project_logic": project_logic_file is not None,
319
- "project_logic_file": project_logic_file or "project.toon",
320
- "has_validation_toon": validation_file is not None,
321
- "validation_file": validation_file or "project/validation.toon.yaml",
322
- "has_duplication_toon": duplication_file is not None,
323
- "duplication_file": duplication_file or "project/duplication.toon.yaml",
324
- "is_chunked": subprojects is not None and len(subprojects) > 0,
325
- "file_count": 0,
326
- }
327
-
328
- # Count total files
329
- actionable_keys = {
330
- "has_analysis_toon",
331
- "has_map_toon",
332
- "has_evolution_toon",
333
- "has_project_toon_yaml",
334
- "has_project_logic",
335
- "has_validation_toon",
336
- "has_duplication_toon",
337
- }
338
- for key, exists in analysis.items():
339
- if key in actionable_keys and exists:
340
- analysis["file_count"] += 1
341
311
 
312
+ def _analyze_generated_files(output_dir: Path, subprojects: list = None) -> dict:
313
+ """Analyze which files were generated and determine appropriate focus areas."""
314
+ analysis = _probe_output_files(output_dir)
315
+ analysis["is_chunked"] = bool(subprojects)
316
+ analysis["file_count"] = sum(1 for k in _ACTIONABLE_KEYS if analysis.get(k))
342
317
  return analysis
343
318
 
344
319
 
@@ -40,6 +40,7 @@ class ProjectAnalyzer:
40
40
  def __init__(
41
41
  self, config: Optional[Config] = None, project_path: Optional[Path] = None
42
42
  ):
43
+ """Initialise the analyser with optional config and project path."""
43
44
  self.config = config or FAST_CONFIG
44
45
  self.project_path = project_path
45
46
  self.cache = (
@@ -29,6 +29,7 @@ class FileAnalyzer:
29
29
  """Analyzes a single file."""
30
30
 
31
31
  def __init__(self, config: Config, cache=None):
32
+ """Initialise with project config and optional file cache."""
32
33
  self.config = config
33
34
  self.cache = cache
34
35
  self._file_filter = FastFileFilter(config.filters)
@@ -149,23 +149,20 @@ def _process_standalone_function(
149
149
  def _match_method_name(arrow_prop_re, method_re, func_re, line, reserved):
150
150
  """Return matched method name from any of the three patterns, or None."""
151
151
  if arrow_prop_re:
152
- apm = arrow_prop_re.match(line)
153
- if apm:
154
- mname = apm.group(1)
155
- if mname not in reserved and mname != "constructor":
156
- return mname
157
- if method_re:
158
- mm = method_re.match(line)
159
- if mm:
160
- mname = mm.group(1)
161
- if mname not in reserved:
162
- return mname
163
- if func_re:
164
- fm = func_re.match(line)
165
- if fm:
166
- fn = fm.group(1) or (fm.group(2) if len(fm.groups()) > 1 else None)
167
- if fn and fn not in reserved:
168
- return fn
152
+ m = arrow_prop_re.match(line)
153
+ if m:
154
+ name = m.group(1)
155
+ if name not in reserved and name != "constructor":
156
+ return name
157
+ for regex in (method_re, func_re):
158
+ if not regex:
159
+ continue
160
+ m = regex.match(line)
161
+ if not m:
162
+ continue
163
+ name = m.group(1) or (m.group(2) if len(m.groups()) > 1 else None)
164
+ if name and name not in reserved:
165
+ return name
169
166
  return None
170
167
 
171
168
 
@@ -117,10 +117,12 @@ class TreeSitterParser:
117
117
  """
118
118
 
119
119
  def __init__(self):
120
+ """Initialise the parser pool, loading tree-sitter language modules."""
120
121
  self._initialized = _init_tree_sitter()
121
122
 
122
123
  @property
123
124
  def available(self) -> bool:
125
+ """Return True when tree-sitter is available for parsing."""
124
126
  return self._initialized
125
127
 
126
128
  def parse(self, source: bytes, ext: str) -> Optional[Any]:
@@ -11,6 +11,7 @@ class RefactoringAnalyzer:
11
11
  """Performs refactoring analysis on code."""
12
12
 
13
13
  def __init__(self, config: Config, file_filter: FastFileFilter):
14
+ """Initialise with project config and file filter."""
14
15
  self.config = config
15
16
  self.file_filter = file_filter
16
17
 
@@ -194,6 +194,17 @@ def _has_code2llm_output_manifest(path: Path) -> bool:
194
194
  )
195
195
 
196
196
 
197
+ def _is_generated_by_name(name: str, p: Path) -> bool:
198
+ """Return True when the file name alone marks it as a generated artefact."""
199
+ if name in GENERATED_EXACT_FILENAMES:
200
+ return True
201
+ if any(name.startswith(prefix) for prefix in GENERATED_PREFIXES):
202
+ return True
203
+ if name in GENERATED_SNIFF_FILENAMES and _looks_like_generated_content(p):
204
+ return True
205
+ return False
206
+
207
+
197
208
  def is_generated_artifact(
198
209
  path: str | Path, project_root: Optional[Path] = None
199
210
  ) -> bool:
@@ -204,29 +215,14 @@ def is_generated_artifact(
204
215
  return True
205
216
 
206
217
  if project_root:
207
- rel_parts = parts[:-1]
208
- for idx, part in enumerate(rel_parts):
209
- if part != "project":
210
- continue
211
- candidate = Path(project_root).joinpath(*parts[: idx + 1])
212
- if _has_code2llm_output_manifest(candidate):
218
+ for idx, part in enumerate(parts[:-1]):
219
+ if part == "project" and _has_code2llm_output_manifest(
220
+ Path(project_root).joinpath(*parts[: idx + 1])
221
+ ):
213
222
  return True
214
223
 
215
224
  name = p.name.lower()
216
- if name in GENERATED_EXACT_FILENAMES:
217
- return True
218
- if any(name.startswith(prefix) for prefix in GENERATED_PREFIXES):
219
- return True
220
- if name in GENERATED_SNIFF_FILENAMES and _looks_like_generated_content(p):
221
- return True
222
-
223
- # The default code2llm output folder is commonly named "project". Avoid
224
- # treating arbitrary project/ directories as generated unless the filename
225
- # itself is a known code2llm artefact.
226
- if "project" in parts[:-1] and name in GENERATED_EXACT_FILENAMES:
227
- return True
228
-
229
- return False
225
+ return _is_generated_by_name(name, p)
230
226
 
231
227
 
232
228
  def classify_source_path(path: str | Path, project_root: Optional[Path] = None) -> str:
@@ -13,6 +13,7 @@ class StreamingIncrementalAnalyzer:
13
13
  """Incremental analysis with change detection for streaming analyzer."""
14
14
 
15
15
  def __init__(self, config: Optional[Config] = None):
16
+ """Initialise incremental analyser with optional config."""
16
17
  self.config = config or FAST_CONFIG
17
18
  self.state_file = Path(".code2llm_state.json")
18
19
  self.previous_state: Dict[str, str] = {}
@@ -30,6 +30,7 @@ class SmartPrioritizer:
30
30
  """Smart file prioritization for optimal analysis order."""
31
31
 
32
32
  def __init__(self, strategy: ScanStrategy):
33
+ """Initialise the file prioritiser with a scan strategy."""
33
34
  self.strategy = strategy
34
35
 
35
36
  def prioritize_files(