code2llm 0.5.144__tar.gz → 0.5.146__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 (197) hide show
  1. {code2llm-0.5.144/code2llm.egg-info → code2llm-0.5.146}/PKG-INFO +12 -14
  2. {code2llm-0.5.144 → code2llm-0.5.146}/README.md +9 -11
  3. code2llm-0.5.146/VERSION +1 -0
  4. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_analysis.py +14 -0
  6. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/formats.py +26 -6
  7. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator.py +52 -1
  8. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator_handlers.py +36 -11
  9. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/prompt.py +6 -1
  10. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_parser.py +6 -0
  11. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/__init__.py +2 -2
  12. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/analyzer.py +47 -28
  13. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/config.py +14 -0
  14. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/file_analyzer.py +11 -4
  15. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/persistent_cache.py +50 -32
  16. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/refactoring.py +21 -7
  17. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/__init__.py +1 -1
  18. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/incremental.py +11 -11
  19. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming_analyzer.py +2 -2
  20. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/core.py +1 -1
  21. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/helpers.py +40 -9
  22. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics.py +1 -1
  23. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics_core.py +49 -57
  24. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_task.py +44 -34
  25. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/__init__.py +1 -1
  26. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/pipeline.py +8 -8
  27. {code2llm-0.5.144 → code2llm-0.5.146/code2llm.egg-info}/PKG-INFO +12 -14
  28. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/requires.txt +1 -1
  29. {code2llm-0.5.144 → code2llm-0.5.146}/pyproject.toml +5 -3
  30. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_deep_analysis.py +1 -0
  31. code2llm-0.5.144/VERSION +0 -1
  32. {code2llm-0.5.144 → code2llm-0.5.146}/LICENSE +0 -0
  33. {code2llm-0.5.144 → code2llm-0.5.146}/MANIFEST.in +0 -0
  34. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/__main__.py +0 -0
  35. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/__init__.py +0 -0
  36. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/call_graph.py +0 -0
  37. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/cfg.py +0 -0
  38. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/coupling.py +0 -0
  39. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/data_analysis.py +0 -0
  40. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/dfg.py +0 -0
  41. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/pipeline_classifier.py +0 -0
  42. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/pipeline_detector.py +0 -0
  43. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/pipeline_resolver.py +0 -0
  44. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/side_effects.py +0 -0
  45. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/smells.py +0 -0
  46. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/type_inference.py +0 -0
  47. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/utils/__init__.py +0 -0
  48. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/utils/ast_helpers.py +0 -0
  49. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/api.py +0 -0
  50. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli.py +0 -0
  51. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_commands.py +0 -0
  52. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/__init__.py +0 -0
  53. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/code2logic.py +0 -0
  54. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  55. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  56. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/ast_registry.py +0 -0
  57. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/export_pipeline.py +0 -0
  58. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/file_cache.py +0 -0
  59. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/file_filter.py +0 -0
  60. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/gitignore.py +0 -0
  61. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/incremental.py +0 -0
  62. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/__init__.py +0 -0
  63. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/base.py +0 -0
  64. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/cpp.py +0 -0
  65. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/csharp.py +0 -0
  66. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/generic.py +0 -0
  67. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/go_lang.py +0 -0
  68. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/java.py +0 -0
  69. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/php.py +0 -0
  70. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/ruby.py +0 -0
  71. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/rust.py +0 -0
  72. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/ts_extractors.py +0 -0
  73. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/ts_parser.py +0 -0
  74. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/typescript.py +0 -0
  75. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/large_repo.py +0 -0
  76. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/models.py +0 -0
  77. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/repo_files.py +0 -0
  78. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/cache.py +0 -0
  79. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/prioritizer.py +0 -0
  80. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/scanner.py +0 -0
  81. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/strategies.py +0 -0
  82. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/toon_size_manager.py +0 -0
  83. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/__init__.py +0 -0
  84. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/article_view.py +0 -0
  85. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/base.py +0 -0
  86. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/context_exporter.py +0 -0
  87. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/context_view.py +0 -0
  88. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/dashboard_data.py +0 -0
  89. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/dashboard_renderer.py +0 -0
  90. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/__init__.py +0 -0
  91. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/computation.py +0 -0
  92. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/constants.py +0 -0
  93. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/exclusion.py +0 -0
  94. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/render.py +0 -0
  95. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/yaml_export.py +0 -0
  96. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution_exporter.py +0 -0
  97. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/flow_constants.py +0 -0
  98. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/flow_exporter.py +0 -0
  99. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/flow_renderer.py +0 -0
  100. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/html_dashboard.py +0 -0
  101. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator/__init__.py +0 -0
  102. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator/renderer.py +0 -0
  103. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator/scanner.py +0 -0
  104. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator.py +0 -0
  105. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/json_exporter.py +0 -0
  106. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/llm_exporter.py +0 -0
  107. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/__init__.py +0 -0
  108. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/alerts.py +0 -0
  109. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/details.py +0 -0
  110. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/header.py +0 -0
  111. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/module_list.py +0 -0
  112. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/utils.py +0 -0
  113. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/yaml_export.py +0 -0
  114. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map_exporter.py +0 -0
  115. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/__init__.py +0 -0
  116. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/calls.py +0 -0
  117. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/classic.py +0 -0
  118. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/compact.py +0 -0
  119. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  120. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  121. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/flow_full.py +0 -0
  122. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/utils.py +0 -0
  123. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid_exporter.py +0 -0
  124. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  125. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/__init__.py +0 -0
  126. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/constants.py +0 -0
  127. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/evolution.py +0 -0
  128. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/health.py +0 -0
  129. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  130. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/modules.py +0 -0
  131. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml_exporter.py +0 -0
  132. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/__init__.py +0 -0
  133. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/content.py +0 -0
  134. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/files.py +0 -0
  135. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/insights.py +0 -0
  136. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/sections.py +0 -0
  137. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme_exporter.py +0 -0
  138. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/report_generators.py +0 -0
  139. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/__init__.py +0 -0
  140. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
  141. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics_health.py +0 -0
  142. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/module_detail.py +0 -0
  143. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/renderer.py +0 -0
  144. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon.py +0 -0
  145. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon_view.py +0 -0
  146. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/validate_project.py +0 -0
  147. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/yaml_exporter.py +0 -0
  148. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/__init__.py +0 -0
  149. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/_utils.py +0 -0
  150. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/__init__.py +0 -0
  151. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/analysis.py +0 -0
  152. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/cli.py +0 -0
  153. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/generator.py +0 -0
  154. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/nodes.py +0 -0
  155. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/parsing.py +0 -0
  156. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/utils.py +0 -0
  157. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow.py +0 -0
  158. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/__init__.py +0 -0
  159. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/fix.py +0 -0
  160. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/png.py +0 -0
  161. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/validation.py +0 -0
  162. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid.py +0 -0
  163. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/config.py +0 -0
  164. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/entity_resolution.py +0 -0
  165. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/intent_matching.py +0 -0
  166. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/normalization.py +0 -0
  167. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/parsers/toon_parser.py +0 -0
  168. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/patterns/__init__.py +0 -0
  169. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/patterns/detector.py +0 -0
  170. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/refactor/__init__.py +0 -0
  171. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/refactor/prompt_engine.py +0 -0
  172. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/SOURCES.txt +0 -0
  173. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/dependency_links.txt +0 -0
  174. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/entry_points.txt +0 -0
  175. {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/top_level.txt +0 -0
  176. {code2llm-0.5.144 → code2llm-0.5.146}/setup.cfg +0 -0
  177. {code2llm-0.5.144 → code2llm-0.5.146}/setup.py +0 -0
  178. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_advanced_analysis.py +0 -0
  179. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_analyzer.py +0 -0
  180. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_cache_invalidation_e2e.py +0 -0
  181. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_calls_toon_export.py +0 -0
  182. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_declarative_collection.py +0 -0
  183. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_edge_cases.py +0 -0
  184. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_file_analyzer_tagging.py +0 -0
  185. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_flow_exporter.py +0 -0
  186. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_format_quality.py +0 -0
  187. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_multilanguage_e2e.py +0 -0
  188. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_nlp_pipeline.py +0 -0
  189. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_nonpython_cc_calls.py +0 -0
  190. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_orchestrator_cache_mtime.py +0 -0
  191. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_persistent_cache.py +0 -0
  192. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_pipeline_detector.py +0 -0
  193. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_project_toon_export.py +0 -0
  194. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_prompt_engine.py +0 -0
  195. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_prompt_txt.py +0 -0
  196. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_refactoring_engine.py +0 -0
  197. {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_toon_v2.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2llm
3
- Version: 0.5.144
3
+ Version: 0.5.146
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
7
7
  Author-email: Tom Sapletta <tom@sapletta.com>
8
- License-Expression: Apache-2.0
8
+ License: Apache-2.0
9
9
  Project-URL: Homepage, https://github.com/wronai/stts
10
10
  Project-URL: Repository, https://github.com/wronai/stts
11
11
  Project-URL: Issues, https://github.com/wronai/stts/issues
@@ -54,7 +54,7 @@ Requires-Dist: pytest-xdist>=3.0; extra == "dev"
54
54
  Requires-Dist: black>=21.0; extra == "dev"
55
55
  Requires-Dist: flake8>=3.9; extra == "dev"
56
56
  Requires-Dist: mypy>=0.910; extra == "dev"
57
- Requires-Dist: goal>=2.1.0; extra == "dev"
57
+ Requires-Dist: goal>=2.1.218; extra == "dev"
58
58
  Requires-Dist: costs>=0.1.20; extra == "dev"
59
59
  Requires-Dist: pfix>=0.1.60; extra == "dev"
60
60
  Dynamic: author
@@ -67,18 +67,16 @@ Dynamic: requires-python
67
67
 
68
68
  ## AI Cost Tracking
69
69
 
70
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.144-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
71
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-70.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
70
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.146-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
71
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-72.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
- - 🤖 **LLM usage:** $7.5000 (196 commits)
74
- - 👤 **Human dev:** ~$7024 (70.2h @ $100/h, 30min dedup)
73
+ - 🤖 **LLM usage:** $7.5000 (198 commits)
74
+ - 👤 **Human dev:** ~$7224 (72.2h @ $100/h, 30min dedup)
75
75
 
76
- Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
+ Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
77
77
 
78
78
  ---
79
79
 
80
-
81
-
82
80
  This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
83
81
 
84
82
  ## 📁 Generated Files Overview
@@ -407,10 +405,10 @@ code2llm ./ -f yaml --separate-orphans
407
405
  ---
408
406
 
409
407
  **Generated by**: `code2llm ./ -f all --readme`
410
- **Analysis Date**: 2026-04-25
411
- **Total Functions**: 3085
412
- **Total Classes**: 259
413
- **Modules**: 498
408
+ **Analysis Date**: 2026-05-06
409
+ **Total Functions**: 3625
410
+ **Total Classes**: 250
411
+ **Modules**: 492
414
412
 
415
413
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
416
414
 
@@ -3,18 +3,16 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.144-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-70.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.146-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-72.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $7.5000 (196 commits)
10
- - 👤 **Human dev:** ~$7024 (70.2h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $7.5000 (198 commits)
10
+ - 👤 **Human dev:** ~$7224 (72.2h @ $100/h, 30min dedup)
11
11
 
12
- Generated on 2026-04-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
+ Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
-
17
-
18
16
  This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
19
17
 
20
18
  ## 📁 Generated Files Overview
@@ -343,10 +341,10 @@ code2llm ./ -f yaml --separate-orphans
343
341
  ---
344
342
 
345
343
  **Generated by**: `code2llm ./ -f all --readme`
346
- **Analysis Date**: 2026-04-25
347
- **Total Functions**: 3085
348
- **Total Classes**: 259
349
- **Modules**: 498
344
+ **Analysis Date**: 2026-05-06
345
+ **Total Functions**: 3625
346
+ **Total Classes**: 250
347
+ **Modules**: 492
350
348
 
351
349
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
352
350
 
@@ -0,0 +1 @@
1
+ 0.5.146
@@ -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.144"
11
+ __version__ = "0.5.146"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -88,6 +88,20 @@ def _build_config(args, output_dir: Path):
88
88
  config.watch = getattr(args, 'watch', False)
89
89
  # Dry-run mode (handled in orchestrator, but stored for reference)
90
90
  config.dry_run = getattr(args, 'dry_run', False)
91
+
92
+ # --fast: skip expensive analyses (vulture, centrality, DFG, communities)
93
+ if getattr(args, 'fast', False):
94
+ config.performance.fast_mode = True
95
+ config.performance.apply_fast_mode()
96
+
97
+ # Strategy-based performance tuning
98
+ strategy = getattr(args, 'strategy', 'standard')
99
+ if strategy == 'quick':
100
+ config.performance.skip_data_flow = True
101
+ config.performance.skip_dead_code_detection = True
102
+ config.performance.skip_centrality = True
103
+ config.performance.skip_community_detection = True
104
+
91
105
  return config
92
106
 
93
107
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import os
4
4
  import sys
5
+ import time
5
6
  from pathlib import Path
6
7
  from typing import Optional
7
8
 
@@ -13,6 +14,7 @@ from code2llm.exporters import (
13
14
  ArticleViewGenerator, HTMLDashboardGenerator,
14
15
  load_project_yaml, IndexHTMLGenerator,
15
16
  )
17
+ from .orchestrator import _inject_generation_time as _inject_time
16
18
 
17
19
 
18
20
  def _export_evolution(args, result, output_dir: Path):
@@ -21,9 +23,12 @@ def _export_evolution(args, result, output_dir: Path):
21
23
  return
22
24
  exporter = EvolutionExporter()
23
25
  filepath = output_dir / 'evolution.toon.yaml'
26
+ t0 = time.monotonic()
24
27
  exporter.export(result, str(filepath))
28
+ elapsed = time.monotonic() - t0
29
+ _inject_time(filepath, elapsed)
25
30
  if args.verbose:
26
- print(f" - EVOLUTION (refactoring queue): {filepath}")
31
+ print(f" - EVOLUTION (refactoring queue): {filepath} ({elapsed:.2f}s)")
27
32
 
28
33
 
29
34
  def _export_data_structures(args, result, output_dir: Path):
@@ -43,9 +48,12 @@ def _export_context_fallback(args, result, output_dir: Path, formats: list):
43
48
  return
44
49
  exporter = ContextExporter()
45
50
  filepath = output_dir / 'context.md'
51
+ t0 = time.monotonic()
46
52
  exporter.export(result, str(filepath))
53
+ elapsed = time.monotonic() - t0
54
+ _inject_time(filepath, elapsed)
47
55
  if args.verbose:
48
- print(f" - CONTEXT (LLM narrative): {filepath}")
56
+ print(f" - CONTEXT (LLM narrative): {filepath} ({elapsed:.2f}s)")
49
57
 
50
58
 
51
59
  def _export_readme(args, result, output_dir: Path):
@@ -54,18 +62,24 @@ def _export_readme(args, result, output_dir: Path):
54
62
  return
55
63
  exporter = READMEExporter()
56
64
  filepath = output_dir / 'README.md'
65
+ t0 = time.monotonic()
57
66
  exporter.export(result, str(filepath))
67
+ elapsed = time.monotonic() - t0
68
+ _inject_time(filepath, elapsed)
58
69
  if args.verbose:
59
- print(f" - README (documentation): {filepath}")
70
+ print(f" - README (documentation): {filepath} ({elapsed:.2f}s)")
60
71
 
61
72
 
62
73
  def _export_project_yaml(args, result, output_dir: Path):
63
74
  """Export unified project.yaml — single source of truth."""
64
75
  exporter = ProjectYAMLExporter()
65
76
  filepath = output_dir / 'project.yaml'
77
+ t0 = time.monotonic()
66
78
  exporter.export(result, str(filepath))
79
+ elapsed = time.monotonic() - t0
80
+ _inject_time(filepath, elapsed)
67
81
  if getattr(args, 'verbose', False):
68
- print(f" - PROJECT-YAML (single source of truth): {filepath}")
82
+ print(f" - PROJECT-YAML (single source of truth): {filepath} ({elapsed:.2f}s)")
69
83
  return filepath
70
84
 
71
85
 
@@ -79,10 +93,13 @@ def _export_project_toon(args, result, output_dir: Path):
79
93
 
80
94
  exporter = ToonViewGenerator()
81
95
  filepath = output_dir / 'project.toon.yaml'
96
+ t0 = time.monotonic()
82
97
  exporter.generate(data, str(filepath))
98
+ elapsed = time.monotonic() - t0
99
+ _inject_time(filepath, elapsed)
83
100
 
84
101
  if getattr(args, 'verbose', False):
85
- print(f" - PROJECT-TOON (project overview): {filepath}")
102
+ print(f" - PROJECT-TOON (project overview): {filepath} ({elapsed:.2f}s)")
86
103
 
87
104
  return filepath
88
105
 
@@ -129,9 +146,12 @@ def _export_simple_formats(args, result, output_dir: Path, formats):
129
146
  exporter = exporter_cls()
130
147
  # Export as plain text TOON format with .toon.yaml extension
131
148
  filepath = output_dir / filename
149
+ t0 = time.monotonic()
132
150
  exporter.export(result, str(filepath))
151
+ elapsed = time.monotonic() - t0
152
+ _inject_time(filepath, elapsed)
133
153
  if args.verbose:
134
- print(f" - {label}: {filepath}")
154
+ print(f" - {label}: {filepath} ({elapsed:.2f}s)")
135
155
 
136
156
  # Unified project.yaml (single source of truth)
137
157
  if 'project-yaml' in formats:
@@ -7,6 +7,7 @@ Maintains backward compatibility with all existing --format values.
7
7
  import os
8
8
  import shutil
9
9
  import sys
10
+ import time
10
11
  from pathlib import Path
11
12
  from typing import Optional, List, Dict, Any
12
13
 
@@ -280,9 +281,12 @@ def _export_registry_formats(args, result, output_dir: Path, formats: List[str])
280
281
  kwargs = _get_format_kwargs(fmt, args)
281
282
 
282
283
  try:
284
+ t0 = time.monotonic()
283
285
  exporter.export(result, str(filepath), **kwargs)
286
+ elapsed = time.monotonic() - t0
287
+ _inject_generation_time(filepath, elapsed)
284
288
  if args.verbose:
285
- print(f" - {label}: {filepath}")
289
+ print(f" - {label}: {filepath} ({elapsed:.2f}s)")
286
290
  except Exception as e:
287
291
  if args.verbose:
288
292
  print(f" - {label} export failed: {e}", file=sys.stderr)
@@ -306,6 +310,53 @@ def _export_chunked(
306
310
  _chunked_impl(args, result, output_dir, source_path, formats, requested_formats)
307
311
 
308
312
 
313
+ def _inject_generation_time(filepath: Path, elapsed: float) -> None:
314
+ """Inject generation time comment into the second line of an exported file."""
315
+ try:
316
+ path = Path(filepath)
317
+ if not path.exists():
318
+ return
319
+ suffix = path.suffix.lower()
320
+ name = path.name.lower()
321
+
322
+ # Only inject into text-based files
323
+ if suffix not in ('.yaml', '.yml', '.md', '.txt', '.mmd', '.html', '.json', '.export'):
324
+ return
325
+
326
+ content = path.read_text(encoding='utf-8')
327
+ if not content:
328
+ return
329
+
330
+ tag = f"generated in {elapsed:.2f}s"
331
+
332
+ if suffix in ('.yaml', '.yml', '.mmd', '.export', '.txt') or name.endswith('.toon.yaml'):
333
+ # YAML/Mermaid/text: insert '# generated in X.XXs' after first line
334
+ lines = content.split('\n', 1)
335
+ if len(lines) == 2:
336
+ content = f"{lines[0]}\n# {tag}\n{lines[1]}"
337
+ else:
338
+ content = f"{lines[0]}\n# {tag}\n"
339
+ elif suffix == '.md':
340
+ # Markdown: insert HTML comment after first line
341
+ lines = content.split('\n', 1)
342
+ if len(lines) == 2:
343
+ content = f"{lines[0]}\n<!-- {tag} -->\n{lines[1]}"
344
+ else:
345
+ content = f"{lines[0]}\n<!-- {tag} -->\n"
346
+ elif suffix == '.html':
347
+ # HTML: insert comment after <!DOCTYPE or <html>
348
+ content = content.replace('\n', f'\n<!-- {tag} -->\n', 1)
349
+ elif suffix == '.json':
350
+ # JSON doesn't support comments — skip
351
+ return
352
+ else:
353
+ return
354
+
355
+ path.write_text(content, encoding='utf-8')
356
+ except Exception:
357
+ pass # Never fail the export pipeline for a comment
358
+
359
+
309
360
  # Backward-compatible aliases
310
361
  _export_single_project = _export_single
311
362
  _export_chunked_results = _export_chunked
@@ -4,6 +4,7 @@ This module contains all the individual export handler functions
4
4
  that were extracted from orchestrator.py to reduce its size.
5
5
  """
6
6
 
7
+ import time
7
8
  from pathlib import Path
8
9
  from typing import Any, Dict, List
9
10
 
@@ -20,10 +21,14 @@ from .orchestrator_constants import FORMAT_LABELS
20
21
 
21
22
  def _export_mermaid(args, result, output_dir: Path):
22
23
  """Export mermaid diagrams."""
24
+ from .orchestrator import _inject_generation_time
25
+
23
26
  exporter = MermaidExporter()
24
27
  include_examples = getattr(args, 'flow_include_examples', False)
25
28
 
26
29
  # Core diagrams
30
+ mmd_files = ['flow.mmd', 'calls.mmd', 'compact_flow.mmd']
31
+ t0 = time.monotonic()
27
32
  exporter.export_flow_compact(result, str(output_dir / 'flow.mmd'), include_examples)
28
33
  exporter.export_call_graph(result, str(output_dir / 'calls.mmd'))
29
34
  exporter.export_compact(result, str(output_dir / 'compact_flow.mmd'))
@@ -31,21 +36,27 @@ def _export_mermaid(args, result, output_dir: Path):
31
36
  # Optional detailed diagrams
32
37
  if getattr(args, 'flow_detail', False):
33
38
  exporter.export_flow_detailed(result, str(output_dir / 'flow_detailed.mmd'), include_examples)
39
+ mmd_files.append('flow_detailed.mmd')
34
40
  if getattr(args, 'flow_full', False):
35
41
  exporter.export_flow_full(result, str(output_dir / 'flow_full.mmd'), include_examples)
42
+ mmd_files.append('flow_full.mmd')
43
+ elapsed_mmd = time.monotonic() - t0
44
+
45
+ # Inject timing into each .mmd file
46
+ for mf in mmd_files:
47
+ _inject_generation_time(output_dir / mf, elapsed_mmd)
36
48
 
37
49
  # Also export calls.yaml/toon
38
50
  yaml_exporter = YAMLExporter()
51
+ t0 = time.monotonic()
39
52
  yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
40
53
  yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
54
+ elapsed_calls = time.monotonic() - t0
55
+ _inject_generation_time(output_dir / 'calls.toon.yaml', elapsed_calls)
41
56
 
42
57
  if args.verbose:
43
- files = ['flow.mmd', 'calls.mmd', 'compact_flow.mmd', 'calls.yaml']
44
- if getattr(args, 'flow_detail', False):
45
- files.append('flow_detailed.mmd')
46
- if getattr(args, 'flow_full', False):
47
- files.append('flow_full.mmd')
48
- print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)})")
58
+ files = mmd_files + ['calls.yaml']
59
+ print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)}) ({elapsed_mmd:.2f}s)")
49
60
 
50
61
  # PNG generation
51
62
  _export_mermaid_pngs(args, output_dir)
@@ -99,28 +110,38 @@ def _export_data_structures(args, result, output_dir: Path):
99
110
  def _export_project_toon(args, result, output_dir: Path):
100
111
  """Export project.toon.yaml from project.yaml data."""
101
112
  from ..exporters.project_yaml_exporter import ProjectYAMLExporter
113
+ from .orchestrator import _inject_generation_time
102
114
 
103
115
  project_yaml_exporter = ProjectYAMLExporter()
104
116
  prev_evolution = load_previous_evolution(output_dir / 'project.yaml')
105
117
  data = project_yaml_exporter._build_project_yaml(result, prev_evolution)
106
118
 
107
119
  generator = ToonViewGenerator()
108
- generator.generate(data, str(output_dir / 'project.toon.yaml'))
120
+ filepath = output_dir / 'project.toon.yaml'
121
+ t0 = time.monotonic()
122
+ generator.generate(data, str(filepath))
123
+ elapsed = time.monotonic() - t0
124
+ _inject_generation_time(filepath, elapsed)
109
125
 
110
126
  if args.verbose:
111
- print(f" - PROJECT-TOON (project overview): {output_dir / 'project.toon.yaml'}")
127
+ print(f" - PROJECT-TOON (project overview): {filepath} ({elapsed:.2f}s)")
112
128
 
113
129
 
114
130
  def _export_readme(args, result, output_dir: Path):
115
131
  """Export README.md."""
116
132
  if getattr(args, 'no_readme', False):
117
133
  return
134
+ from .orchestrator import _inject_generation_time
118
135
  exporter_cls = get_exporter('readme')
119
136
  if exporter_cls:
120
137
  exporter = exporter_cls()
121
- exporter.export(result, str(output_dir / 'README.md'))
138
+ filepath = output_dir / 'README.md'
139
+ t0 = time.monotonic()
140
+ exporter.export(result, str(filepath))
141
+ elapsed = time.monotonic() - t0
142
+ _inject_generation_time(filepath, elapsed)
122
143
  if args.verbose:
123
- print(f" - README (documentation): {output_dir / 'README.md'}")
144
+ print(f" - README (documentation): {filepath} ({elapsed:.2f}s)")
124
145
 
125
146
 
126
147
  def _export_index_html(args, output_dir: Path):
@@ -128,10 +149,14 @@ def _export_index_html(args, output_dir: Path):
128
149
  if 'all' not in getattr(args, 'format', ''):
129
150
  return
130
151
  try:
152
+ from .orchestrator import _inject_generation_time
131
153
  generator = IndexHTMLGenerator(output_dir)
154
+ t0 = time.monotonic()
132
155
  index_path = generator.generate()
156
+ elapsed = time.monotonic() - t0
157
+ _inject_generation_time(index_path, elapsed)
133
158
  if args.verbose:
134
- print(f" - INDEX (file browser): {index_path}")
159
+ print(f" - INDEX (file browser): {index_path} ({elapsed:.2f}s)")
135
160
  except Exception as e:
136
161
  if args.verbose:
137
162
  print(f" - INDEX generation failed: {e}", file=__import__('sys').stderr)
@@ -1,6 +1,7 @@
1
1
  """Prompt generation — prompt.txt for LLM consumption (regular and chunked)."""
2
2
 
3
3
  import sys
4
+ import time
4
5
  from pathlib import Path
5
6
  from typing import List, Optional, Tuple
6
7
 
@@ -30,9 +31,13 @@ def _export_prompt_txt(args, output_dir: Path, formats: list[str], source_path:
30
31
  lines.extend(_build_prompt_footer(chunked=False, file_analysis=file_analysis))
31
32
 
32
33
  prompt_path = output_dir / 'prompt.txt'
34
+ t0 = time.monotonic()
33
35
  prompt_path.write_text("\n".join(lines) + "\n", encoding='utf-8')
36
+ elapsed = time.monotonic() - t0
37
+ from .orchestrator import _inject_generation_time
38
+ _inject_generation_time(prompt_path, elapsed)
34
39
  if args.verbose:
35
- print(f" - PROMPT: {prompt_path}")
40
+ print(f" - PROMPT: {prompt_path} ({elapsed:.2f}s)")
36
41
 
37
42
 
38
43
  def _export_chunked_prompt_txt(args, output_dir: Path, formats: list[str], source_path: Optional[Path] = None, subprojects: list = None) -> None:
@@ -159,6 +159,12 @@ Strategy Options (--strategy):
159
159
  help='Auto-detect changed files and only re-analyze those (faster subsequent runs)'
160
160
  )
161
161
 
162
+ parser.add_argument(
163
+ '--fast',
164
+ action='store_true',
165
+ help='Skip expensive analyses (vulture, centrality, DFG, communities) for faster runs'
166
+ )
167
+
162
168
  parser.add_argument(
163
169
  '--strategy',
164
170
  choices=['quick', 'standard', 'deep'],
@@ -9,7 +9,7 @@ from .models import (
9
9
  __all__ = [
10
10
  'ProjectAnalyzer',
11
11
  'StreamingAnalyzer',
12
- 'IncrementalAnalyzer',
12
+ 'StreamingIncrementalAnalyzer',
13
13
  'ScanStrategy',
14
14
  'SmartPrioritizer',
15
15
  'STRATEGY_QUICK',
@@ -43,7 +43,7 @@ def __getattr__(name):
43
43
  return locals()[name]
44
44
 
45
45
  _streaming_names = {
46
- 'StreamingAnalyzer', 'IncrementalAnalyzer', 'ScanStrategy',
46
+ 'StreamingAnalyzer', 'StreamingIncrementalAnalyzer', 'ScanStrategy',
47
47
  'SmartPrioritizer', 'STRATEGY_QUICK', 'STRATEGY_STANDARD', 'STRATEGY_DEEP',
48
48
  }
49
49
  if name in _streaming_names:
@@ -121,10 +121,16 @@ class ProjectAnalyzer:
121
121
  return None, [], files
122
122
 
123
123
  def _run_analysis(self, files_to_analyze: List[Tuple[str, str]]) -> List[Dict]:
124
- """Analyze files in parallel or sequentially depending on config."""
124
+ """Analyze files in parallel or sequentially depending on config.
125
+
126
+ Parallel mode has significant startup/pickle overhead. It is only
127
+ beneficial for larger file sets (threshold: 30 files).
128
+ """
125
129
  if not files_to_analyze:
126
130
  return []
127
- if self.config.performance.parallel_enabled and len(files_to_analyze) > 1:
131
+ _PARALLEL_THRESHOLD = 30
132
+ if (self.config.performance.parallel_enabled
133
+ and len(files_to_analyze) > _PARALLEL_THRESHOLD):
128
134
  return self._analyze_parallel(files_to_analyze)
129
135
  return self._analyze_sequential(files_to_analyze)
130
136
 
@@ -197,39 +203,61 @@ class ProjectAnalyzer:
197
203
  if self.config.verbose:
198
204
  self._print_summary(merged)
199
205
 
206
+ @staticmethod
207
+ def _should_collect_file(
208
+ filename: str,
209
+ ext_set: set,
210
+ filename_set_lower: frozenset,
211
+ filename_prefixes_lower: tuple,
212
+ ) -> bool:
213
+ """Return True if *filename* matches a known language extension,
214
+ well-known filename (Dockerfile, Makefile, ...), or filename prefix
215
+ (Dockerfile.dev, Makefile.am).
216
+ """
217
+ filename_lower = filename.lower()
218
+ suffix = os.path.splitext(filename)[1].lower()
219
+ return (
220
+ suffix in ext_set
221
+ or filename_lower in filename_set_lower
222
+ or filename_lower.startswith(filename_prefixes_lower)
223
+ )
224
+
225
+ @staticmethod
226
+ def _compute_module_name(
227
+ rel: str, filename: str, project_name: str
228
+ ) -> str:
229
+ """Derive a Pythonic module name from a relative file path."""
230
+ parts = rel.replace('\\', '/').split('/')
231
+ dir_parts = parts[:-1]
232
+ init_names = frozenset({'__init__.py', 'index.js', 'index.ts', 'mod.rs', 'lib.rs'})
233
+ if filename in init_names:
234
+ return '.'.join(dir_parts) if dir_parts else project_name
235
+ stem = os.path.splitext(filename)[0]
236
+ return '.'.join(dir_parts + [stem]) if dir_parts else stem
237
+
200
238
  def _collect_files(self, project_path: Path) -> List[Tuple[str, str]]:
201
239
  """Collect all source files with their module names for all supported languages.
202
-
240
+
203
241
  Uses a single os.walk traversal with early directory pruning instead of
204
242
  separate rglob calls per extension (~40x speedup on large repos).
205
243
  """
206
244
  files = []
207
- ext_set = set(ALL_EXTENSIONS) # O(1) lookup
208
- # Filename lookup uses a case-insensitive set. We lowercase once
209
- # here so the hot loop only does a single lower() per file.
245
+ ext_set = set(ALL_EXTENSIONS)
210
246
  filename_set_lower = frozenset(n.lower() for n in ALL_FILENAMES)
211
247
  filename_prefixes_lower = tuple(p.lower() for p in LANGUAGE_FILENAME_PREFIXES)
212
- init_names = frozenset({'__init__.py', 'index.js', 'index.ts', 'mod.rs', 'lib.rs'})
213
- seen = set() # guard against duplicate paths (e.g. .h in both c and cpp lists)
248
+ seen = set()
214
249
  project_str = str(project_path)
250
+ project_name = project_path.name
215
251
 
216
252
  for dirpath, dirnames, filenames in os.walk(project_str, topdown=True):
217
- # Prune skipped directories in-place so os.walk won't descend into them
218
253
  dirnames[:] = [
219
254
  d for d in dirnames
220
255
  if not self.file_filter.should_skip_dir(d)
221
256
  ]
222
257
 
223
258
  for filename in filenames:
224
- filename_lower = filename.lower()
225
- suffix = os.path.splitext(filename)[1].lower()
226
- # Accept by extension, well-known filename
227
- # (Dockerfile, Makefile, Jenkinsfile, ...), or known
228
- # prefix (Dockerfile.dev, Dockerfile.prod, Makefile.am).
229
- if (
230
- suffix not in ext_set
231
- and filename_lower not in filename_set_lower
232
- and not filename_lower.startswith(filename_prefixes_lower)
259
+ if not self._should_collect_file(
260
+ filename, ext_set, filename_set_lower, filename_prefixes_lower
233
261
  ):
234
262
  continue
235
263
 
@@ -241,17 +269,8 @@ class ProjectAnalyzer:
241
269
  if not self.file_filter.should_process(file_str):
242
270
  continue
243
271
 
244
- # Calculate module name from relative path
245
272
  rel = os.path.relpath(file_str, project_str)
246
- parts = rel.replace('\\', '/').split('/')
247
- dir_parts = parts[:-1] # everything before filename
248
-
249
- if filename in init_names:
250
- module_name = '.'.join(dir_parts) if dir_parts else project_path.name
251
- else:
252
- stem = os.path.splitext(filename)[0]
253
- module_name = '.'.join(dir_parts + [stem]) if dir_parts else stem
254
-
273
+ module_name = self._compute_module_name(rel, filename, project_name)
255
274
  files.append((file_str, module_name))
256
275
 
257
276
  return files
@@ -81,6 +81,10 @@ class PerformanceConfig:
81
81
  fast_mode: bool = False
82
82
  skip_data_flow: bool = False
83
83
  skip_pattern_detection: bool = False
84
+ skip_refactoring_analysis: bool = False
85
+ skip_dead_code_detection: bool = True
86
+ skip_centrality: bool = False
87
+ skip_community_detection: bool = False
84
88
 
85
89
  def get_workers(self) -> int:
86
90
  """Get effective worker count (auto-detect if set to 0)."""
@@ -88,6 +92,16 @@ class PerformanceConfig:
88
92
  return _get_optimal_workers(default=4)
89
93
  return self.parallel_workers
90
94
 
95
+ def apply_fast_mode(self) -> None:
96
+ """Apply fast_mode overrides — skip expensive analyses."""
97
+ if not self.fast_mode:
98
+ return
99
+ self.skip_data_flow = True
100
+ self.skip_pattern_detection = True
101
+ self.skip_dead_code_detection = True
102
+ self.skip_centrality = True
103
+ self.skip_community_detection = True
104
+
91
105
 
92
106
  @dataclass
93
107
  class FilterConfig:
@@ -124,8 +124,9 @@ class FileAnalyzer:
124
124
  # Calculate complexity with radon
125
125
  self._calculate_complexity(content, file_path, result)
126
126
 
127
- # Deep Analysis for refactoring
128
- self._perform_deep_analysis(tree, module_name, file_path, result)
127
+ # Deep Analysis for refactoring (skip when data flow not needed)
128
+ if not self.config.performance.skip_data_flow:
129
+ self._perform_deep_analysis(tree, module_name, file_path, result)
129
130
 
130
131
  self.stats['files_processed'] += 1
131
132
  return result
@@ -164,12 +165,18 @@ class FileAnalyzer:
164
165
  result['mutations'] = dfg_res.mutations
165
166
  result['data_flows'] = dfg_res.data_flows
166
167
 
167
- # Update function calls from CG extractor which is more robust
168
+ # Update function calls from CG extractor which is more robust.
169
+ # Use set-based merge to avoid duplicate call entries (ast.walk
170
+ # in _process_function already extracts calls).
168
171
  cg_ext = CallGraphExtractor(self.config)
169
172
  cg_res = cg_ext.extract(tree, module_name, file_path)
170
173
  for func_name, cg_func in cg_res.functions.items():
171
174
  if func_name in result['functions']:
172
- result['functions'][func_name].calls.extend(list(cg_func.calls))
175
+ existing = set(result['functions'][func_name].calls)
176
+ for c in cg_func.calls:
177
+ if c not in existing:
178
+ result['functions'][func_name].calls.append(c)
179
+ existing.add(c)
173
180
  except Exception as e:
174
181
  if self.config.verbose:
175
182
  print(f"Error in deep analysis for {file_path}: {e}")