code2llm 0.5.151__tar.gz → 0.5.152__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 (199) hide show
  1. {code2llm-0.5.151/code2llm.egg-info → code2llm-0.5.152}/PKG-INFO +5 -5
  2. {code2llm-0.5.151 → code2llm-0.5.152}/README.md +4 -4
  3. code2llm-0.5.152/VERSION +1 -0
  4. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/orchestrator.py +12 -2
  6. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/config.py +9 -0
  7. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/file_analyzer.py +4 -0
  8. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/file_filter.py +10 -1
  9. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/gitignore.py +21 -17
  10. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/generic.py +4 -1
  11. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/models.py +2 -0
  12. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/repo_files.py +8 -1
  13. code2llm-0.5.152/code2llm/core/source_classifier.py +185 -0
  14. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming/scanner.py +6 -1
  15. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/nlp/__init__.py +1 -1
  16. {code2llm-0.5.151 → code2llm-0.5.152/code2llm.egg-info}/PKG-INFO +5 -5
  17. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm.egg-info/SOURCES.txt +2 -0
  18. {code2llm-0.5.151 → code2llm-0.5.152}/pyproject.toml +1 -1
  19. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_declarative_collection.py +68 -0
  20. code2llm-0.5.152/tests/test_export_cache_flags.py +27 -0
  21. code2llm-0.5.151/VERSION +0 -1
  22. {code2llm-0.5.151 → code2llm-0.5.152}/LICENSE +0 -0
  23. {code2llm-0.5.151 → code2llm-0.5.152}/MANIFEST.in +0 -0
  24. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/__main__.py +0 -0
  25. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/__init__.py +0 -0
  26. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/call_graph.py +0 -0
  27. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/cfg.py +0 -0
  28. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/coupling.py +0 -0
  29. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/data_analysis.py +0 -0
  30. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/dfg.py +0 -0
  31. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/pipeline_classifier.py +0 -0
  32. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/pipeline_detector.py +0 -0
  33. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/pipeline_resolver.py +0 -0
  34. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/side_effects.py +0 -0
  35. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/smells.py +0 -0
  36. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/type_inference.py +0 -0
  37. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/utils/__init__.py +0 -0
  38. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/analysis/utils/ast_helpers.py +0 -0
  39. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/api.py +0 -0
  40. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli.py +0 -0
  41. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_analysis.py +0 -0
  42. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_commands.py +0 -0
  43. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/__init__.py +0 -0
  44. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/code2logic.py +0 -0
  45. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/formats.py +0 -0
  46. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  47. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  48. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
  49. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_exports/prompt.py +0 -0
  50. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/cli_parser.py +0 -0
  51. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/__init__.py +0 -0
  52. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/analyzer.py +0 -0
  53. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/ast_registry.py +0 -0
  54. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/export_pipeline.py +0 -0
  55. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/file_cache.py +0 -0
  56. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/incremental.py +0 -0
  57. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/__init__.py +0 -0
  58. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/base.py +0 -0
  59. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/cpp.py +0 -0
  60. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/csharp.py +0 -0
  61. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/go_lang.py +0 -0
  62. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/java.py +0 -0
  63. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/php.py +0 -0
  64. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/ruby.py +0 -0
  65. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/rust.py +0 -0
  66. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/ts_extractors.py +0 -0
  67. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/ts_parser.py +0 -0
  68. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/lang/typescript.py +0 -0
  69. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/large_repo.py +0 -0
  70. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/persistent_cache.py +0 -0
  71. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/refactoring.py +0 -0
  72. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming/__init__.py +0 -0
  73. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming/cache.py +0 -0
  74. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming/incremental.py +0 -0
  75. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming/prioritizer.py +0 -0
  76. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming/strategies.py +0 -0
  77. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/streaming_analyzer.py +0 -0
  78. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/core/toon_size_manager.py +0 -0
  79. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/__init__.py +0 -0
  80. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/article_view.py +0 -0
  81. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/base.py +0 -0
  82. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/context_exporter.py +0 -0
  83. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/context_view.py +0 -0
  84. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/dashboard_data.py +0 -0
  85. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/dashboard_renderer.py +0 -0
  86. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution/__init__.py +0 -0
  87. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution/computation.py +0 -0
  88. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution/constants.py +0 -0
  89. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution/exclusion.py +0 -0
  90. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution/render.py +0 -0
  91. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution/yaml_export.py +0 -0
  92. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/evolution_exporter.py +0 -0
  93. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/flow_constants.py +0 -0
  94. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/flow_exporter.py +0 -0
  95. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/flow_renderer.py +0 -0
  96. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/html_dashboard.py +0 -0
  97. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/index_generator/__init__.py +0 -0
  98. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/index_generator/renderer.py +0 -0
  99. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/index_generator/scanner.py +0 -0
  100. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/index_generator.py +0 -0
  101. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/json_exporter.py +0 -0
  102. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/llm_exporter.py +0 -0
  103. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/__init__.py +0 -0
  104. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/alerts.py +0 -0
  105. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/details.py +0 -0
  106. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/header.py +0 -0
  107. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/module_list.py +0 -0
  108. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/utils.py +0 -0
  109. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map/yaml_export.py +0 -0
  110. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/map_exporter.py +0 -0
  111. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/__init__.py +0 -0
  112. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/calls.py +0 -0
  113. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/classic.py +0 -0
  114. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/compact.py +0 -0
  115. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  116. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  117. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/flow_full.py +0 -0
  118. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid/utils.py +0 -0
  119. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid_exporter.py +0 -0
  120. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  121. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/__init__.py +0 -0
  122. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/constants.py +0 -0
  123. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/core.py +0 -0
  124. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/evolution.py +0 -0
  125. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/health.py +0 -0
  126. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  127. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml/modules.py +0 -0
  128. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/project_yaml_exporter.py +0 -0
  129. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/readme/__init__.py +0 -0
  130. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/readme/content.py +0 -0
  131. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/readme/files.py +0 -0
  132. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/readme/insights.py +0 -0
  133. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/readme/sections.py +0 -0
  134. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/readme_exporter.py +0 -0
  135. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/report_generators.py +0 -0
  136. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/__init__.py +0 -0
  137. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/helpers.py +0 -0
  138. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/metrics.py +0 -0
  139. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/metrics_core.py +0 -0
  140. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
  141. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/metrics_health.py +0 -0
  142. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/module_detail.py +0 -0
  143. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon/renderer.py +0 -0
  144. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon.py +0 -0
  145. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/toon_view.py +0 -0
  146. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/validate_project.py +0 -0
  147. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/exporters/yaml_exporter.py +0 -0
  148. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/__init__.py +0 -0
  149. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/_utils.py +0 -0
  150. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/__init__.py +0 -0
  151. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/analysis.py +0 -0
  152. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/cli.py +0 -0
  153. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/generator.py +0 -0
  154. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/nodes.py +0 -0
  155. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/parsing.py +0 -0
  156. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow/utils.py +0 -0
  157. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_flow.py +0 -0
  158. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/llm_task.py +0 -0
  159. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/mermaid/__init__.py +0 -0
  160. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/mermaid/fix.py +0 -0
  161. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/mermaid/png.py +0 -0
  162. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/mermaid/validation.py +0 -0
  163. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/generators/mermaid.py +0 -0
  164. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/nlp/config.py +0 -0
  165. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/nlp/entity_resolution.py +0 -0
  166. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/nlp/intent_matching.py +0 -0
  167. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/nlp/normalization.py +0 -0
  168. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/nlp/pipeline.py +0 -0
  169. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/parsers/toon_parser.py +0 -0
  170. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/patterns/__init__.py +0 -0
  171. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/patterns/detector.py +0 -0
  172. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/refactor/__init__.py +0 -0
  173. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm/refactor/prompt_engine.py +0 -0
  174. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm.egg-info/dependency_links.txt +0 -0
  175. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm.egg-info/entry_points.txt +0 -0
  176. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm.egg-info/requires.txt +0 -0
  177. {code2llm-0.5.151 → code2llm-0.5.152}/code2llm.egg-info/top_level.txt +0 -0
  178. {code2llm-0.5.151 → code2llm-0.5.152}/setup.cfg +0 -0
  179. {code2llm-0.5.151 → code2llm-0.5.152}/setup.py +0 -0
  180. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_advanced_analysis.py +0 -0
  181. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_analyzer.py +0 -0
  182. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_cache_invalidation_e2e.py +0 -0
  183. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_calls_toon_export.py +0 -0
  184. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_deep_analysis.py +0 -0
  185. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_edge_cases.py +0 -0
  186. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_file_analyzer_tagging.py +0 -0
  187. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_flow_exporter.py +0 -0
  188. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_format_quality.py +0 -0
  189. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_multilanguage_e2e.py +0 -0
  190. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_nlp_pipeline.py +0 -0
  191. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_nonpython_cc_calls.py +0 -0
  192. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_orchestrator_cache_mtime.py +0 -0
  193. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_persistent_cache.py +0 -0
  194. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_pipeline_detector.py +0 -0
  195. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_project_toon_export.py +0 -0
  196. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_prompt_engine.py +0 -0
  197. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_prompt_txt.py +0 -0
  198. {code2llm-0.5.151 → code2llm-0.5.152}/tests/test_refactoring_engine.py +0 -0
  199. {code2llm-0.5.151 → code2llm-0.5.152}/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.151
3
+ Version: 0.5.152
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.151-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-74.3h-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.152-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-75.8h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
71
71
 
72
- - 🤖 **LLM usage:** $7.5000 (203 commits)
73
- - 👤 **Human dev:** ~$7429 (74.3h @ $100/h, 30min dedup)
72
+ - 🤖 **LLM usage:** $7.5000 (204 commits)
73
+ - 👤 **Human dev:** ~$7584 (75.8h @ $100/h, 30min dedup)
74
74
 
75
75
  Generated on 2026-05-06 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.151-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-74.3h-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.152-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-75.8h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
7
7
 
8
- - 🤖 **LLM usage:** $7.5000 (203 commits)
9
- - 👤 **Human dev:** ~$7429 (74.3h @ $100/h, 30min dedup)
8
+ - 🤖 **LLM usage:** $7.5000 (204 commits)
9
+ - 👤 **Human dev:** ~$7584 (75.8h @ $100/h, 30min dedup)
10
10
 
11
11
  Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
12
 
@@ -0,0 +1 @@
1
+ 0.5.152
@@ -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.151"
11
+ __version__ = "0.5.152"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -85,6 +85,15 @@ def _show_dry_run_plan(formats: List[str], output_dir: Path, is_chunked: bool, r
85
85
  print(f"\n✅ Dry-run complete. Use without --dry-run to export.\n")
86
86
 
87
87
 
88
+ def _should_skip_export_cache(args, is_chunked: bool) -> bool:
89
+ """Return True when this run must not read or write export cache."""
90
+ return (
91
+ is_chunked
92
+ or getattr(args, 'no_cache', False)
93
+ or getattr(args, 'force', False)
94
+ )
95
+
96
+
88
97
  def _run_exports(args, result, output_dir: Path, source_path: Optional[Path] = None):
89
98
  """Export analysis results in requested formats.
90
99
 
@@ -102,8 +111,9 @@ def _run_exports(args, result, output_dir: Path, source_path: Optional[Path] = N
102
111
  _show_dry_run_plan(formats, output_dir, is_chunked, result)
103
112
  return
104
113
 
105
- # Skip cache for chunked or when explicitly disabled
106
- skip_cache = is_chunked or getattr(args, 'no_cache', False)
114
+ # Skip cache for chunked runs or explicit cache bypass flags. --force is
115
+ # documented as a --no-cache alias, so it must bypass export cache too.
116
+ skip_cache = _should_skip_export_cache(args, is_chunked)
107
117
 
108
118
  if not skip_cache and source_path:
109
119
  cache = PersistentCache(str(source_path))
@@ -119,6 +119,15 @@ class FilterConfig:
119
119
  "*poetry.lock", "*Pipfile.lock", "*Cargo.lock", "*composer.lock",
120
120
  "*.terraform.lock.hcl", "*.tfstate", "*.tfstate.backup",
121
121
  "*.min.js", "*.min.css", "*.map",
122
+ # code2llm / analysis artefacts. These are useful outputs, but feeding
123
+ # them back into the next run makes reports rank generated files above
124
+ # real source files.
125
+ "*analysis.toon*", "*map.toon.yaml", "*evolution.toon*",
126
+ "*validation.toon.yaml", "*duplication.toon.yaml",
127
+ "*project.toon*", "*calls.yaml", "*calls.toon.yaml", "*calls.mmd",
128
+ "*compact_flow.mmd", "*flow.mmd", "*flow_detailed.mmd",
129
+ "*flow_full.mmd", "*mermaid.export", "*defscan-*.md",
130
+ "*SUMD.md", "*SUMR.md",
122
131
  ])
123
132
  include_patterns: List[str] = field(default_factory=list)
124
133
  min_function_lines: int = 1
@@ -11,6 +11,7 @@ from .models import ClassInfo, FlowEdge, FlowNode, FunctionInfo, ModuleInfo
11
11
  from code2llm.analysis.dfg import DFGExtractor
12
12
  from code2llm.analysis.call_graph import CallGraphExtractor
13
13
  from .file_filter import FastFileFilter
14
+ from .source_classifier import classify_source_path
14
15
  from .lang import (
15
16
  analyze_typescript_js, analyze_go, analyze_rust, analyze_java,
16
17
  analyze_cpp, analyze_csharp, analyze_php, analyze_ruby, analyze_generic,
@@ -76,6 +77,9 @@ class FileAnalyzer:
76
77
  return {}
77
78
 
78
79
  result = self._route_to_language_analyzer(content, file_path, module_name, ext)
80
+ if result and result.get('module'):
81
+ result['module'].line_count = len(content.splitlines())
82
+ result['module'].source_kind = classify_source_path(file_path)
79
83
 
80
84
  # Tag result with its source file so downstream callers
81
85
  # (e.g. PersistentCache in ProjectAnalyzer._store_to_persistent_cache)
@@ -5,6 +5,11 @@ import re
5
5
  from pathlib import Path
6
6
  from .config import FilterConfig
7
7
  from .gitignore import load_gitignore_patterns
8
+ from .source_classifier import (
9
+ ARCHIVE_DIR_NAMES,
10
+ GENERATED_OUTPUT_DIR_NAMES,
11
+ is_generated_artifact,
12
+ )
8
13
 
9
14
 
10
15
  _SKIP_DIR_NAMES = frozenset({
@@ -19,13 +24,14 @@ _SKIP_DIR_NAMES = frozenset({
19
24
  'lib', 'lib64', 'site-packages', 'include', 'bin', 'share',
20
25
  '.code2llm_cache',
21
26
  'tests', 'test',
27
+ 'coverage', '.nyc_output',
22
28
  # Backup and auto-generated directories that often contain venvs
23
29
  '.algitex', '.backup', 'backups', '.bak', 'bak',
24
30
  # Additional venv patterns
25
31
  'virtualenv', '.virtualenv', 'envs', '.envs', 'venv-', '.venv-',
26
32
  # CI/CD and deployment artifacts
27
33
  '.terraform', '.serverless', '.netlify', '.vercel',
28
- })
34
+ }) | ARCHIVE_DIR_NAMES | GENERATED_OUTPUT_DIR_NAMES
29
35
 
30
36
 
31
37
  class FastFileFilter:
@@ -99,6 +105,9 @@ class FastFileFilter:
99
105
  path_lower = file_path.lower()
100
106
  basename_lower = Path(file_path).name.lower()
101
107
 
108
+ if is_generated_artifact(file_path, self.project_path):
109
+ return False
110
+
102
111
  return (
103
112
  self._passes_gitignore(file_path) and
104
113
  self._passes_excludes(path_lower, basename_lower) and
@@ -1,7 +1,7 @@
1
1
  """Gitignore support for code2llm file filtering."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import List
4
+ from typing import Iterable, List
5
5
  import re
6
6
 
7
7
 
@@ -118,21 +118,25 @@ class GitIgnoreParser:
118
118
  return ignored
119
119
 
120
120
 
121
- def load_gitignore_patterns(project_path: Path) -> GitIgnoreParser:
122
- """Load gitignore patterns from project directory.
123
-
124
- Searches up the directory tree from project_path until it finds a .gitignore file
125
- or reaches the filesystem root. This ensures that gitignore rules are properly applied
126
- even when analyzing subdirectories of a larger project.
127
- """
121
+ def _nearest_ignore_files(project_path: Path, names: Iterable[str]) -> List[Path]:
122
+ """Find nearest ignore files while walking upward from project_path."""
128
123
  current_path = project_path.resolve()
129
-
130
- while current_path != current_path.parent: # Stop at filesystem root
131
- gitignore_path = current_path / '.gitignore'
132
- if gitignore_path.exists():
133
- return GitIgnoreParser(gitignore_path)
124
+ while current_path != current_path.parent:
125
+ found = [current_path / name for name in names if (current_path / name).exists()]
126
+ if found:
127
+ return found
134
128
  current_path = current_path.parent
135
-
136
- # Check filesystem root as last resort
137
- gitignore_path = current_path / '.gitignore'
138
- return GitIgnoreParser(gitignore_path)
129
+ return [current_path / name for name in names if (current_path / name).exists()]
130
+
131
+
132
+ def load_gitignore_patterns(project_path: Path) -> GitIgnoreParser:
133
+ """Load .gitignore and .code2llmignore patterns from the nearest parent.
134
+
135
+ .code2llmignore is loaded after .gitignore so project-specific analysis
136
+ rules can add exclusions or negations without changing version-control
137
+ ignores.
138
+ """
139
+ parser = GitIgnoreParser()
140
+ for ignore_path in _nearest_ignore_files(project_path, ('.gitignore', '.code2llmignore')):
141
+ parser._load_gitignore(ignore_path)
142
+ return parser
@@ -4,6 +4,7 @@ import re
4
4
  from typing import Dict
5
5
 
6
6
  from code2llm.core.models import ClassInfo, FunctionInfo, ModuleInfo
7
+ from code2llm.core.source_classifier import is_structural_only_file
7
8
 
8
9
 
9
10
  def analyze_generic(content: str, file_path: str, module_name: str,
@@ -19,7 +20,9 @@ def analyze_generic(content: str, file_path: str, module_name: str,
19
20
 
20
21
  # Count lines as basic metric
21
22
  lines = content.split('\n')
22
- non_empty = len([l for l in lines if l.strip()])
23
+ if is_structural_only_file(file_path):
24
+ stats['files_processed'] += 1
25
+ return result
23
26
 
24
27
  # Try to detect function-like patterns
25
28
  func_patterns = [
@@ -100,6 +100,8 @@ class ModuleInfo(BaseModel):
100
100
  name: str
101
101
  file: str
102
102
  is_package: bool = False
103
+ source_kind: str = "source"
104
+ line_count: int = 0
103
105
  imports: List[str] = field(default_factory=list)
104
106
  functions: List[str] = field(default_factory=list)
105
107
  classes: List[str] = field(default_factory=list)
@@ -5,6 +5,11 @@ from typing import List, Tuple, Optional
5
5
 
6
6
  from functools import lru_cache
7
7
  from .gitignore import load_gitignore_patterns, GitIgnoreParser
8
+ from .source_classifier import (
9
+ ARCHIVE_DIR_NAMES,
10
+ GENERATED_OUTPUT_DIR_NAMES,
11
+ is_generated_artifact,
12
+ )
8
13
 
9
14
  # Directories to skip during analysis
10
15
  SKIP_DIRS = {
@@ -14,7 +19,7 @@ SKIP_DIRS = {
14
19
  'build', 'dist', 'egg-info', '.eggs',
15
20
  'htmlcov', '.coverage', '.cache',
16
21
  'lib', 'lib64', 'site-packages', 'include', 'bin', 'share', # venv internals
17
- }
22
+ } | ARCHIVE_DIR_NAMES | GENERATED_OUTPUT_DIR_NAMES
18
23
 
19
24
  # Patterns that indicate a file should be skipped
20
25
  SKIP_PATTERNS = [
@@ -42,6 +47,8 @@ def should_skip_file(file_str: str, project_path: Optional[Path] = None,
42
47
  lower_path = file_str.lower()
43
48
  if any(pattern in lower_path for pattern in SKIP_PATTERNS):
44
49
  return True
50
+ if is_generated_artifact(file_str, project_path):
51
+ return True
45
52
 
46
53
  # Check gitignore if parser provided
47
54
  if gitignore_parser and project_path:
@@ -0,0 +1,185 @@
1
+ """Source path classification and default noise filtering.
2
+
3
+ The analyzer intentionally tracks some non-code files so export caches are
4
+ invalidated when project manifests or docs change. Those files should not be
5
+ treated the same as production source, and generated code2llm artefacts should
6
+ not be fed back into the next analysis run.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+
15
+ CODE_EXTENSIONS = {
16
+ ".py", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".go", ".rs",
17
+ ".java", ".cpp", ".cc", ".cxx", ".hpp", ".hh", ".h", ".c", ".cs",
18
+ ".rb", ".rbw", ".php", ".swift", ".kt", ".kts", ".scala", ".r",
19
+ ".m", ".sh", ".bash", ".zsh", ".dart", ".ex", ".exs", ".clj",
20
+ ".cljs", ".hs", ".lua", ".pl", ".pm", ".raku", ".rakumod",
21
+ }
22
+
23
+ DOC_EXTENSIONS = {".md", ".mdx", ".markdown", ".rst", ".adoc", ".asciidoc", ".txt"}
24
+
25
+ STRUCTURAL_ONLY_EXTENSIONS = {
26
+ ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf", ".properties",
27
+ ".xml", ".json", ".json5", ".jsonc", ".env", ".proto", ".graphql",
28
+ ".gql", ".avsc", ".prisma", ".tf", ".tfvars", ".hcl", ".bicep",
29
+ ".nix",
30
+ *DOC_EXTENSIONS,
31
+ }
32
+
33
+ ARCHIVE_DIR_NAMES = frozenset({
34
+ "_archive", "archive", "archives", "_archives", "legacy", "old",
35
+ })
36
+
37
+ GENERATED_OUTPUT_DIR_NAMES = frozenset({
38
+ ".code2llm_cache",
39
+ "code2llm_output",
40
+ "output_toon",
41
+ "output_all",
42
+ "output_comparison",
43
+ "output_modes",
44
+ "analysis_all",
45
+ "compare",
46
+ "calls_output",
47
+ "refactor_output",
48
+ "playwright-report",
49
+ "test-results",
50
+ "html-report",
51
+ })
52
+
53
+ GENERATED_EXACT_FILENAMES = frozenset({
54
+ "analysis.toon",
55
+ "analysis.toon.yaml",
56
+ "analysis_toon.yaml",
57
+ ".code2llm_incremental.json",
58
+ "map.toon",
59
+ "map.toon.yaml",
60
+ "evolution.toon",
61
+ "evolution.toon.yaml",
62
+ "validation.toon.yaml",
63
+ "duplication.toon.yaml",
64
+ "calls.toon.yaml",
65
+ "calls.yaml",
66
+ "calls.mmd",
67
+ "compact_flow.mmd",
68
+ "flow.mmd",
69
+ "flow_detailed.mmd",
70
+ "flow_full.mmd",
71
+ "mermaid.export",
72
+ "project.toon",
73
+ "project.toon.yaml",
74
+ "project.toon.txt",
75
+ "defscan-classes-py.md",
76
+ "defscan-classes-ts.md",
77
+ "sumd.md",
78
+ "sumr.md",
79
+ })
80
+
81
+ GENERATED_PREFIXES = ("defscan-",)
82
+
83
+ GENERATED_SNIFF_FILENAMES = frozenset({
84
+ "context.md",
85
+ "readme.md",
86
+ "index.html",
87
+ "prompt.txt",
88
+ })
89
+
90
+ GENERATED_MARKERS = (
91
+ "generated by: `code2llm`",
92
+ "generated by code2llm",
93
+ "code2llm - generated analysis files",
94
+ "<title>code2llm analysis results</title>",
95
+ "# system architecture analysis\n<!-- generated",
96
+ "use the attached/generated files as the authoritative context.",
97
+ )
98
+
99
+
100
+ def _relative_parts(path: Path, project_root: Optional[Path]) -> tuple[str, ...]:
101
+ """Return normalized relative path parts for classification."""
102
+ try:
103
+ if project_root:
104
+ rel = path.resolve().relative_to(project_root.resolve())
105
+ else:
106
+ rel = path
107
+ except (OSError, ValueError, RuntimeError):
108
+ rel = path
109
+ return tuple(part.lower() for part in rel.parts)
110
+
111
+
112
+ def _looks_like_generated_content(path: Path) -> bool:
113
+ """Cheap content sniff for generic generated filenames such as README.md."""
114
+ try:
115
+ text = path.read_text(encoding="utf-8", errors="ignore")[:4096].lower()
116
+ except (OSError, UnicodeDecodeError):
117
+ return False
118
+ return any(marker in text for marker in GENERATED_MARKERS)
119
+
120
+
121
+ def _has_code2llm_output_manifest(path: Path) -> bool:
122
+ """Return True if a directory looks like a code2llm output folder."""
123
+ return (
124
+ (path / "analysis.toon.yaml").exists()
125
+ or (path / "analysis.toon").exists()
126
+ or (path / "map.toon.yaml").exists()
127
+ )
128
+
129
+
130
+ def is_generated_artifact(path: str | Path, project_root: Optional[Path] = None) -> bool:
131
+ """Return True when *path* is a generated analysis/report artefact."""
132
+ p = Path(path)
133
+ parts = _relative_parts(p, project_root)
134
+ if any(part in GENERATED_OUTPUT_DIR_NAMES for part in parts[:-1]):
135
+ return True
136
+
137
+ if project_root:
138
+ rel_parts = parts[:-1]
139
+ for idx, part in enumerate(rel_parts):
140
+ if part != "project":
141
+ continue
142
+ candidate = Path(project_root).joinpath(*parts[: idx + 1])
143
+ if _has_code2llm_output_manifest(candidate):
144
+ return True
145
+
146
+ name = p.name.lower()
147
+ if name in GENERATED_EXACT_FILENAMES:
148
+ return True
149
+ if any(name.startswith(prefix) for prefix in GENERATED_PREFIXES):
150
+ return True
151
+ if name in GENERATED_SNIFF_FILENAMES and _looks_like_generated_content(p):
152
+ return True
153
+
154
+ # The default code2llm output folder is commonly named "project". Avoid
155
+ # treating arbitrary project/ directories as generated unless the filename
156
+ # itself is a known code2llm artefact.
157
+ if "project" in parts[:-1] and name in GENERATED_EXACT_FILENAMES:
158
+ return True
159
+
160
+ return False
161
+
162
+
163
+ def classify_source_path(path: str | Path, project_root: Optional[Path] = None) -> str:
164
+ """Classify a path for reporting and filtering."""
165
+ p = Path(path)
166
+ parts = _relative_parts(p, project_root)
167
+ if is_generated_artifact(p, project_root):
168
+ return "generated"
169
+ if any(part in ARCHIVE_DIR_NAMES for part in parts[:-1]):
170
+ return "archive"
171
+ if any(part in {"tests", "test", "testing", "__tests__"} for part in parts[:-1]):
172
+ return "test"
173
+ ext = p.suffix.lower()
174
+ if ext in DOC_EXTENSIONS:
175
+ return "docs"
176
+ if ext in STRUCTURAL_ONLY_EXTENSIONS:
177
+ return "config"
178
+ if ext in CODE_EXTENSIONS:
179
+ return "source"
180
+ return "other"
181
+
182
+
183
+ def is_structural_only_file(path: str | Path) -> bool:
184
+ """Return True for tracked non-code text files that should emit no symbols."""
185
+ return Path(path).suffix.lower() in STRUCTURAL_ONLY_EXTENSIONS
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from typing import Dict, List, Optional, Tuple
6
6
 
7
7
  from code2llm.core.models import ClassInfo, FlowNode, FunctionInfo, ModuleInfo
8
+ from code2llm.core.source_classifier import classify_source_path, is_generated_artifact
8
9
 
9
10
  from .cache import StreamingFileCache
10
11
  from .prioritizer import FilePriority
@@ -45,7 +46,9 @@ class StreamingScanner:
45
46
  result = {
46
47
  'module': ModuleInfo(
47
48
  name=priority.module_name,
48
- file=priority.file_path
49
+ file=priority.file_path,
50
+ source_kind=classify_source_path(priority.file_path),
51
+ line_count=len(content.splitlines()),
49
52
  ),
50
53
  'functions': {},
51
54
  'classes': {},
@@ -187,6 +190,8 @@ class StreamingScanner:
187
190
 
188
191
  if any(x in file_str.lower() for x in ['__pycache__', '.venv', 'venv']):
189
192
  continue
193
+ if is_generated_artifact(py_file, project_path):
194
+ continue
190
195
 
191
196
  # Calculate module name
192
197
  rel_path = py_file.relative_to(project_path)
@@ -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.151"
7
+ __version__ = "0.5.152"
8
8
 
9
9
  from .pipeline import NLPPipeline
10
10
  from .normalization import QueryNormalizer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2llm
3
- Version: 0.5.151
3
+ Version: 0.5.152
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.151-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-74.3h-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.152-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-75.8h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
71
71
 
72
- - 🤖 **LLM usage:** $7.5000 (203 commits)
73
- - 👤 **Human dev:** ~$7429 (74.3h @ $100/h, 30min dedup)
72
+ - 🤖 **LLM usage:** $7.5000 (204 commits)
73
+ - 👤 **Human dev:** ~$7584 (75.8h @ $100/h, 30min dedup)
74
74
 
75
75
  Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
76
 
@@ -54,6 +54,7 @@ code2llm/core/models.py
54
54
  code2llm/core/persistent_cache.py
55
55
  code2llm/core/refactoring.py
56
56
  code2llm/core/repo_files.py
57
+ code2llm/core/source_classifier.py
57
58
  code2llm/core/streaming_analyzer.py
58
59
  code2llm/core/toon_size_manager.py
59
60
  code2llm/core/lang/__init__.py
@@ -178,6 +179,7 @@ tests/test_calls_toon_export.py
178
179
  tests/test_declarative_collection.py
179
180
  tests/test_deep_analysis.py
180
181
  tests/test_edge_cases.py
182
+ tests/test_export_cache_flags.py
181
183
  tests/test_file_analyzer_tagging.py
182
184
  tests/test_flow_exporter.py
183
185
  tests/test_format_quality.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.151"
7
+ version = "0.5.152"
8
8
  description = "High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -20,6 +20,7 @@ from code2llm.core.config import (
20
20
  ALL_EXTENSIONS, ALL_FILENAMES, DECLARATIVE_EXTENSIONS, Config,
21
21
  LANGUAGE_FILENAMES,
22
22
  )
23
+ from code2llm.core.file_analyzer import FileAnalyzer
23
24
  from code2llm.core.persistent_cache import PersistentCache
24
25
 
25
26
 
@@ -202,3 +203,70 @@ def test_lockfiles_excluded_by_default(iac_project, tmp_path):
202
203
  assert "Cargo.lock" not in names
203
204
  # But the non-lock package.json stays.
204
205
  assert "package.json" in names
206
+
207
+
208
+ def test_generated_analysis_artifacts_are_excluded_by_default(tmp_path):
209
+ """code2llm outputs must not be fed back into the next analysis run."""
210
+ p = tmp_path / "proj"
211
+ p.mkdir()
212
+ (p / "app.py").write_text("def run():\n return 1\n")
213
+ out = p / "project"
214
+ out.mkdir()
215
+ (out / "analysis.toon.yaml").write_text("# code2llm\nHEALTH[0]: ok\n")
216
+ (out / "map.toon.yaml").write_text("# generated map\n")
217
+ (out / "index.html").write_text("<title>code2llm Analysis Results</title>\n")
218
+ batch = out / "batch_1"
219
+ batch.mkdir()
220
+ (batch / "context.md").write_text("# System Architecture Analysis\n<!-- generated in 0.01s -->\n")
221
+ (p / ".code2llm_incremental.json").write_text("{}\n")
222
+ (p / "SUMD.md").write_text("# generated summary\n")
223
+ (p / "defscan-classes-py.md").write_text("class Noise:\n pass\n")
224
+
225
+ cfg = Config()
226
+ cfg.performance.parallel_enabled = False
227
+ files = ProjectAnalyzer(cfg, p)._collect_files(p)
228
+ names = {str(Path(fp).relative_to(p)) for fp, _ in files}
229
+
230
+ assert "app.py" in names
231
+ assert "project/analysis.toon.yaml" not in names
232
+ assert "project/map.toon.yaml" not in names
233
+ assert "project/index.html" not in names
234
+ assert "project/batch_1/context.md" not in names
235
+ assert ".code2llm_incremental.json" not in names
236
+ assert "SUMD.md" not in names
237
+ assert "defscan-classes-py.md" not in names
238
+
239
+
240
+ def test_code2llmignore_is_applied(tmp_path):
241
+ """Project-local .code2llmignore should refine analysis scope."""
242
+ p = tmp_path / "proj"
243
+ p.mkdir()
244
+ (p / ".code2llmignore").write_text("docs/\n")
245
+ (p / "app.py").write_text("def run():\n return 1\n")
246
+ docs = p / "docs"
247
+ docs.mkdir()
248
+ (docs / "README.md").write_text("# Docs\n")
249
+
250
+ cfg = Config()
251
+ cfg.performance.parallel_enabled = False
252
+ files = ProjectAnalyzer(cfg, p)._collect_files(p)
253
+ names = {Path(fp).name for fp, _ in files}
254
+
255
+ assert "app.py" in names
256
+ assert "README.md" not in names
257
+
258
+
259
+ def test_markdown_and_config_do_not_emit_fake_symbols(iac_project):
260
+ """Tracked docs/config invalidate cache but should not create classes/functions."""
261
+ cfg = Config()
262
+ analyzer = FileAnalyzer(cfg)
263
+
264
+ markdown = analyzer.analyze_file(str(iac_project / "README.md"), "README")
265
+ yaml_result = analyzer.analyze_file(str(iac_project / "k8s.yaml"), "k8s")
266
+
267
+ assert markdown["functions"] == {}
268
+ assert markdown["classes"] == {}
269
+ assert yaml_result["functions"] == {}
270
+ assert yaml_result["classes"] == {}
271
+ assert markdown["module"].source_kind == "docs"
272
+ assert yaml_result["module"].source_kind == "config"
@@ -0,0 +1,27 @@
1
+ from types import SimpleNamespace
2
+
3
+ from code2llm.cli_exports.orchestrator import _should_skip_export_cache
4
+
5
+
6
+ def test_force_skips_export_cache():
7
+ args = SimpleNamespace(no_cache=False, force=True)
8
+
9
+ assert _should_skip_export_cache(args, is_chunked=False) is True
10
+
11
+
12
+ def test_no_cache_skips_export_cache():
13
+ args = SimpleNamespace(no_cache=True, force=False)
14
+
15
+ assert _should_skip_export_cache(args, is_chunked=False) is True
16
+
17
+
18
+ def test_chunked_skips_export_cache():
19
+ args = SimpleNamespace(no_cache=False, force=False)
20
+
21
+ assert _should_skip_export_cache(args, is_chunked=True) is True
22
+
23
+
24
+ def test_standard_run_can_use_export_cache():
25
+ args = SimpleNamespace(no_cache=False, force=False)
26
+
27
+ assert _should_skip_export_cache(args, is_chunked=False) is False
code2llm-0.5.151/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.5.151
File without changes
File without changes