code2llm 0.5.141__tar.gz → 0.5.143__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 (198) hide show
  1. {code2llm-0.5.141/code2llm.egg-info → code2llm-0.5.143}/PKG-INFO +13 -10
  2. {code2llm-0.5.141 → code2llm-0.5.143}/README.md +12 -9
  3. code2llm-0.5.143/VERSION +1 -0
  4. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/analyzer.py +45 -11
  6. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/config.py +71 -2
  7. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/file_analyzer.py +10 -0
  8. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/persistent_cache.py +127 -14
  9. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/nlp/__init__.py +1 -1
  10. {code2llm-0.5.141 → code2llm-0.5.143/code2llm.egg-info}/PKG-INFO +13 -10
  11. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm.egg-info/SOURCES.txt +3 -0
  12. {code2llm-0.5.141 → code2llm-0.5.143}/pyproject.toml +1 -1
  13. code2llm-0.5.143/tests/test_cache_invalidation_e2e.py +113 -0
  14. code2llm-0.5.143/tests/test_declarative_collection.py +204 -0
  15. code2llm-0.5.143/tests/test_file_analyzer_tagging.py +69 -0
  16. code2llm-0.5.143/tests/test_persistent_cache.py +386 -0
  17. code2llm-0.5.141/VERSION +0 -1
  18. code2llm-0.5.141/tests/test_persistent_cache.py +0 -182
  19. {code2llm-0.5.141 → code2llm-0.5.143}/LICENSE +0 -0
  20. {code2llm-0.5.141 → code2llm-0.5.143}/MANIFEST.in +0 -0
  21. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/__main__.py +0 -0
  22. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/__init__.py +0 -0
  23. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/call_graph.py +0 -0
  24. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/cfg.py +0 -0
  25. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/coupling.py +0 -0
  26. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/data_analysis.py +0 -0
  27. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/dfg.py +0 -0
  28. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/pipeline_classifier.py +0 -0
  29. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/pipeline_detector.py +0 -0
  30. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/pipeline_resolver.py +0 -0
  31. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/side_effects.py +0 -0
  32. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/smells.py +0 -0
  33. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/type_inference.py +0 -0
  34. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/utils/__init__.py +0 -0
  35. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/analysis/utils/ast_helpers.py +0 -0
  36. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/api.py +0 -0
  37. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli.py +0 -0
  38. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_analysis.py +0 -0
  39. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_commands.py +0 -0
  40. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/__init__.py +0 -0
  41. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/code2logic.py +0 -0
  42. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/formats.py +0 -0
  43. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/orchestrator.py +0 -0
  44. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  45. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  46. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
  47. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_exports/prompt.py +0 -0
  48. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/cli_parser.py +0 -0
  49. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/__init__.py +0 -0
  50. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/ast_registry.py +0 -0
  51. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/export_pipeline.py +0 -0
  52. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/file_cache.py +0 -0
  53. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/file_filter.py +0 -0
  54. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/gitignore.py +0 -0
  55. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/incremental.py +0 -0
  56. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/__init__.py +0 -0
  57. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/base.py +0 -0
  58. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/cpp.py +0 -0
  59. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/csharp.py +0 -0
  60. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/generic.py +0 -0
  61. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/go_lang.py +0 -0
  62. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/java.py +0 -0
  63. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/php.py +0 -0
  64. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/ruby.py +0 -0
  65. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/rust.py +0 -0
  66. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/ts_extractors.py +0 -0
  67. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/ts_parser.py +0 -0
  68. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/lang/typescript.py +0 -0
  69. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/large_repo.py +0 -0
  70. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/models.py +0 -0
  71. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/refactoring.py +0 -0
  72. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/repo_files.py +0 -0
  73. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming/__init__.py +0 -0
  74. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming/cache.py +0 -0
  75. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming/incremental.py +0 -0
  76. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming/prioritizer.py +0 -0
  77. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming/scanner.py +0 -0
  78. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming/strategies.py +0 -0
  79. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/streaming_analyzer.py +0 -0
  80. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/core/toon_size_manager.py +0 -0
  81. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/__init__.py +0 -0
  82. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/article_view.py +0 -0
  83. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/base.py +0 -0
  84. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/context_exporter.py +0 -0
  85. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/context_view.py +0 -0
  86. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/dashboard_data.py +0 -0
  87. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/dashboard_renderer.py +0 -0
  88. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution/__init__.py +0 -0
  89. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution/computation.py +0 -0
  90. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution/constants.py +0 -0
  91. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution/exclusion.py +0 -0
  92. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution/render.py +0 -0
  93. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution/yaml_export.py +0 -0
  94. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/evolution_exporter.py +0 -0
  95. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/flow_constants.py +0 -0
  96. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/flow_exporter.py +0 -0
  97. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/flow_renderer.py +0 -0
  98. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/html_dashboard.py +0 -0
  99. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/index_generator/__init__.py +0 -0
  100. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/index_generator/renderer.py +0 -0
  101. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/index_generator/scanner.py +0 -0
  102. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/index_generator.py +0 -0
  103. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/json_exporter.py +0 -0
  104. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/llm_exporter.py +0 -0
  105. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/__init__.py +0 -0
  106. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/alerts.py +0 -0
  107. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/details.py +0 -0
  108. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/header.py +0 -0
  109. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/module_list.py +0 -0
  110. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/utils.py +0 -0
  111. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map/yaml_export.py +0 -0
  112. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/map_exporter.py +0 -0
  113. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/__init__.py +0 -0
  114. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/calls.py +0 -0
  115. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/classic.py +0 -0
  116. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/compact.py +0 -0
  117. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  118. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  119. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/flow_full.py +0 -0
  120. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid/utils.py +0 -0
  121. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid_exporter.py +0 -0
  122. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  123. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/__init__.py +0 -0
  124. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/constants.py +0 -0
  125. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/core.py +0 -0
  126. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/evolution.py +0 -0
  127. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/health.py +0 -0
  128. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  129. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml/modules.py +0 -0
  130. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/project_yaml_exporter.py +0 -0
  131. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/readme/__init__.py +0 -0
  132. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/readme/content.py +0 -0
  133. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/readme/files.py +0 -0
  134. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/readme/insights.py +0 -0
  135. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/readme/sections.py +0 -0
  136. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/readme_exporter.py +0 -0
  137. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/report_generators.py +0 -0
  138. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/__init__.py +0 -0
  139. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/helpers.py +0 -0
  140. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/metrics.py +0 -0
  141. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/metrics_core.py +0 -0
  142. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
  143. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/metrics_health.py +0 -0
  144. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/module_detail.py +0 -0
  145. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon/renderer.py +0 -0
  146. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon.py +0 -0
  147. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/toon_view.py +0 -0
  148. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/validate_project.py +0 -0
  149. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/exporters/yaml_exporter.py +0 -0
  150. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/__init__.py +0 -0
  151. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/_utils.py +0 -0
  152. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/__init__.py +0 -0
  153. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/analysis.py +0 -0
  154. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/cli.py +0 -0
  155. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/generator.py +0 -0
  156. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/nodes.py +0 -0
  157. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/parsing.py +0 -0
  158. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow/utils.py +0 -0
  159. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_flow.py +0 -0
  160. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/llm_task.py +0 -0
  161. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/mermaid/__init__.py +0 -0
  162. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/mermaid/fix.py +0 -0
  163. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/mermaid/png.py +0 -0
  164. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/mermaid/validation.py +0 -0
  165. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/generators/mermaid.py +0 -0
  166. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/nlp/config.py +0 -0
  167. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/nlp/entity_resolution.py +0 -0
  168. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/nlp/intent_matching.py +0 -0
  169. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/nlp/normalization.py +0 -0
  170. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/nlp/pipeline.py +0 -0
  171. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/parsers/toon_parser.py +0 -0
  172. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/patterns/__init__.py +0 -0
  173. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/patterns/detector.py +0 -0
  174. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/refactor/__init__.py +0 -0
  175. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm/refactor/prompt_engine.py +0 -0
  176. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm.egg-info/dependency_links.txt +0 -0
  177. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm.egg-info/entry_points.txt +0 -0
  178. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm.egg-info/requires.txt +0 -0
  179. {code2llm-0.5.141 → code2llm-0.5.143}/code2llm.egg-info/top_level.txt +0 -0
  180. {code2llm-0.5.141 → code2llm-0.5.143}/setup.cfg +0 -0
  181. {code2llm-0.5.141 → code2llm-0.5.143}/setup.py +0 -0
  182. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_advanced_analysis.py +0 -0
  183. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_analyzer.py +0 -0
  184. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_calls_toon_export.py +0 -0
  185. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_deep_analysis.py +0 -0
  186. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_edge_cases.py +0 -0
  187. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_flow_exporter.py +0 -0
  188. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_format_quality.py +0 -0
  189. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_multilanguage_e2e.py +0 -0
  190. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_nlp_pipeline.py +0 -0
  191. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_nonpython_cc_calls.py +0 -0
  192. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_orchestrator_cache_mtime.py +0 -0
  193. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_pipeline_detector.py +0 -0
  194. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_project_toon_export.py +0 -0
  195. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_prompt_engine.py +0 -0
  196. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_prompt_txt.py +0 -0
  197. {code2llm-0.5.141 → code2llm-0.5.143}/tests/test_refactoring_engine.py +0 -0
  198. {code2llm-0.5.141 → code2llm-0.5.143}/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.141
3
+ Version: 0.5.143
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
@@ -67,16 +67,18 @@ 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.141-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-66.9h-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.143-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-69.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
- - 🤖 **LLM usage:** $7.5000 (190 commits)
74
- - 👤 **Human dev:** ~$6690 (66.9h @ $100/h, 30min dedup)
73
+ - 🤖 **LLM usage:** $7.5000 (195 commits)
74
+ - 👤 **Human dev:** ~$6974 (69.7h @ $100/h, 30min dedup)
75
75
 
76
- Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
+ Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
77
77
 
78
78
  ---
79
79
 
80
+
81
+
80
82
  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.
81
83
 
82
84
  ## 📁 Generated Files Overview
@@ -88,6 +90,7 @@ When you run `code2llm ./ -f all`, the following files are created:
88
90
  | File | Format | Purpose | Key Insights |
89
91
  |------|--------|---------|--------------|
90
92
  | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
93
+ | `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
91
94
 
92
95
  ### 🤖 LLM-Ready Documentation
93
96
 
@@ -404,10 +407,10 @@ code2llm ./ -f yaml --separate-orphans
404
407
  ---
405
408
 
406
409
  **Generated by**: `code2llm ./ -f all --readme`
407
- **Analysis Date**: 2026-04-20
408
- **Total Functions**: 2124
409
- **Total Classes**: 241
410
- **Modules**: 359
410
+ **Analysis Date**: 2026-04-21
411
+ **Total Functions**: 2150
412
+ **Total Classes**: 245
413
+ **Modules**: 369
411
414
 
412
415
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
413
416
 
@@ -3,16 +3,18 @@
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.141-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-66.9h-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.143-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-69.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $7.5000 (190 commits)
10
- - 👤 **Human dev:** ~$6690 (66.9h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $7.5000 (195 commits)
10
+ - 👤 **Human dev:** ~$6974 (69.7h @ $100/h, 30min dedup)
11
11
 
12
- Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
+ Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
+
17
+
16
18
  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.
17
19
 
18
20
  ## 📁 Generated Files Overview
@@ -24,6 +26,7 @@ When you run `code2llm ./ -f all`, the following files are created:
24
26
  | File | Format | Purpose | Key Insights |
25
27
  |------|--------|---------|--------------|
26
28
  | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
29
+ | `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
27
30
 
28
31
  ### 🤖 LLM-Ready Documentation
29
32
 
@@ -340,10 +343,10 @@ code2llm ./ -f yaml --separate-orphans
340
343
  ---
341
344
 
342
345
  **Generated by**: `code2llm ./ -f all --readme`
343
- **Analysis Date**: 2026-04-20
344
- **Total Functions**: 2124
345
- **Total Classes**: 241
346
- **Modules**: 359
346
+ **Analysis Date**: 2026-04-21
347
+ **Total Functions**: 2150
348
+ **Total Classes**: 245
349
+ **Modules**: 369
347
350
 
348
351
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
349
352
 
@@ -0,0 +1 @@
1
+ 0.5.143
@@ -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.141"
11
+ __version__ = "0.5.143"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -16,7 +16,11 @@ except ImportError:
16
16
 
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
- from .config import Config, FAST_CONFIG, ALL_EXTENSIONS, LANGUAGE_EXTENSIONS, DEFAULT_PROGRESS_BAR_THRESHOLD
19
+ from .config import (
20
+ Config, FAST_CONFIG, ALL_EXTENSIONS, ALL_FILENAMES,
21
+ LANGUAGE_EXTENSIONS, LANGUAGE_FILENAME_PREFIXES,
22
+ DEFAULT_PROGRESS_BAR_THRESHOLD,
23
+ )
20
24
  from .models import AnalysisResult, FlowEdge, FlowNode, Pattern
21
25
  from code2llm.analysis.call_graph import CallGraphExtractor
22
26
 
@@ -89,6 +93,11 @@ class ProjectAnalyzer:
89
93
  pcache = PersistentCache(str(project_path))
90
94
  file_paths = [fp for fp, _ in files]
91
95
  changed_paths, cached_paths = pcache.get_changed_files(file_paths)
96
+ # Drop manifest entries for files that vanished since the last
97
+ # run. Without this the export-level cache key (derived from the
98
+ # manifest) wouldn't shrink on delete and stale exports could
99
+ # be re-used.
100
+ removed = pcache.prune_missing(file_paths)
92
101
  path_to_module = dict(files)
93
102
  cached_results: List[Dict] = []
94
103
  for fp in cached_paths:
@@ -100,6 +109,12 @@ class ProjectAnalyzer:
100
109
  files_to_analyze = [(fp, path_to_module[fp]) for fp in changed_paths]
101
110
  if self.config.verbose:
102
111
  print(f" - Persistent cache: {len(cached_results)} hits, {len(files_to_analyze)} to analyze")
112
+ if removed:
113
+ print(f" - Persistent cache: {len(removed)} stale entries pruned (deleted files)")
114
+ for rel in removed[:10]:
115
+ print(f" • {rel}")
116
+ if len(removed) > 10:
117
+ print(f" ... and {len(removed) - 10} more")
103
118
  return pcache, cached_results, files_to_analyze
104
119
  except Exception as exc:
105
120
  logger.debug("PersistentCache init failed, falling back: %s", exc)
@@ -119,16 +134,23 @@ class ProjectAnalyzer:
119
134
  files_to_analyze: List[Tuple[str, str]],
120
135
  fresh_results: List[Dict],
121
136
  ) -> None:
122
- """Persist fresh analysis results to cache; no-op when pcache is None."""
123
- if pcache is None or not fresh_results:
137
+ """Persist fresh analysis results to cache; no-op when pcache is None.
138
+
139
+ Note: we always attempt to save — even without fresh_results — so
140
+ that manifest pruning of deleted files (done in
141
+ _load_from_persistent_cache) is persisted. save() is a cheap no-op
142
+ when nothing is dirty.
143
+ """
144
+ if pcache is None:
124
145
  return
125
- path_to_result = {r.get('file', ''): r for r in fresh_results if r}
126
- for fp, _ in files_to_analyze:
127
- if fp in path_to_result:
128
- try:
129
- pcache.put_file_result(fp, path_to_result[fp])
130
- except Exception as exc:
131
- logger.debug("put_file_result failed: %s", exc)
146
+ if fresh_results:
147
+ path_to_result = {r.get('file', ''): r for r in fresh_results if r}
148
+ for fp, _ in files_to_analyze:
149
+ if fp in path_to_result:
150
+ try:
151
+ pcache.put_file_result(fp, path_to_result[fp])
152
+ except Exception as exc:
153
+ logger.debug("put_file_result failed: %s", exc)
132
154
  try:
133
155
  pcache.save()
134
156
  except Exception as exc:
@@ -183,6 +205,10 @@ class ProjectAnalyzer:
183
205
  """
184
206
  files = []
185
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.
210
+ filename_set_lower = frozenset(n.lower() for n in ALL_FILENAMES)
211
+ filename_prefixes_lower = tuple(p.lower() for p in LANGUAGE_FILENAME_PREFIXES)
186
212
  init_names = frozenset({'__init__.py', 'index.js', 'index.ts', 'mod.rs', 'lib.rs'})
187
213
  seen = set() # guard against duplicate paths (e.g. .h in both c and cpp lists)
188
214
  project_str = str(project_path)
@@ -195,8 +221,16 @@ class ProjectAnalyzer:
195
221
  ]
196
222
 
197
223
  for filename in filenames:
224
+ filename_lower = filename.lower()
198
225
  suffix = os.path.splitext(filename)[1].lower()
199
- if suffix not in ext_set:
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)
233
+ ):
200
234
  continue
201
235
 
202
236
  file_str = os.path.join(dirpath, filename)
@@ -99,6 +99,12 @@ class FilterConfig:
99
99
  "*_test.py", "test_*.py", "conftest.py",
100
100
  "*/tests/*", "*/test/*",
101
101
  "*demo_langs/invalid*",
102
+ # Lockfiles and generated artefacts — declarative but noisy and
103
+ # auto-generated, keep them out of the manifest by default.
104
+ "*package-lock.json", "*yarn.lock", "*pnpm-lock.yaml",
105
+ "*poetry.lock", "*Pipfile.lock", "*Cargo.lock", "*composer.lock",
106
+ "*.terraform.lock.hcl", "*.tfstate", "*.tfstate.backup",
107
+ "*.min.js", "*.min.css", "*.map",
102
108
  ])
103
109
  include_patterns: List[str] = field(default_factory=list)
104
110
  min_function_lines: int = 1
@@ -238,8 +244,71 @@ LANGUAGE_EXTENSIONS = {
238
244
  'raku': ['.raku', '.rakumod'],
239
245
  }
240
246
 
241
- # All supported extensions flat list
242
- ALL_EXTENSIONS = [ext for exts in LANGUAGE_EXTENSIONS.values() for ext in exts]
247
+ # Declarative / IaC / config / docs extensions.
248
+ # These are NOT analyzed for call graphs or CC they're routed to the
249
+ # generic analyzer. The point is to include them in the content-addressed
250
+ # manifest so the export cache is invalidated when they change (e.g.
251
+ # editing Dockerfile, terraform .tf, k8s manifests, pyproject.toml,
252
+ # README.md, openapi.yaml, ...).
253
+ DECLARATIVE_EXTENSIONS = {
254
+ # Infrastructure as Code
255
+ 'terraform': ['.tf', '.tfvars', '.hcl'],
256
+ 'bicep': ['.bicep'],
257
+ 'nix': ['.nix'],
258
+ # (K8s/Helm/Ansible/Pulumi/CloudFormation/docker-compose all use
259
+ # yaml/json, matched below.)
260
+ # Config / serialization
261
+ 'yaml': ['.yaml', '.yml'],
262
+ 'toml': ['.toml'],
263
+ 'ini': ['.ini', '.cfg', '.conf', '.properties'],
264
+ 'xml': ['.xml'],
265
+ 'json_config': ['.json', '.json5', '.jsonc'],
266
+ 'env': ['.env'],
267
+ # Schema / IDL
268
+ 'proto': ['.proto'],
269
+ 'graphql': ['.graphql', '.gql'],
270
+ 'avro': ['.avsc'],
271
+ 'prisma': ['.prisma'],
272
+ # Documentation
273
+ 'markdown': ['.md', '.mdx', '.markdown'],
274
+ 'rst': ['.rst'],
275
+ 'asciidoc': ['.adoc', '.asciidoc'],
276
+ 'text': ['.txt'],
277
+ # Project DSLs (user-specific, low-friction to include)
278
+ 'dsl': ['.doql', '.dsl'],
279
+ }
280
+
281
+ # Declarative files matched by FILENAME (no extension, or compound names).
282
+ # Case-insensitive matched against os.path.basename().
283
+ LANGUAGE_FILENAMES = {
284
+ 'dockerfile': ['Dockerfile', 'Containerfile'],
285
+ 'makefile': ['Makefile', 'GNUmakefile', 'BSDmakefile'],
286
+ 'jenkins': ['Jenkinsfile'],
287
+ 'vagrant': ['Vagrantfile'],
288
+ 'rakefile': ['Rakefile'],
289
+ 'gemfile': ['Gemfile'],
290
+ 'procfile': ['Procfile'],
291
+ 'caddyfile': ['Caddyfile'],
292
+ 'pipfile': ['Pipfile'],
293
+ 'brewfile': ['Brewfile'],
294
+ }
295
+
296
+ # Filename PREFIXES that indicate a declarative file even with arbitrary
297
+ # suffix — e.g. `Dockerfile.dev`, `Dockerfile.prod`, `Makefile.am`.
298
+ # The match is case-insensitive against the basename; a '.' suffix is
299
+ # required so we don't accidentally match e.g. `Dockerfiles/`.
300
+ LANGUAGE_FILENAME_PREFIXES = ('Dockerfile.', 'Containerfile.', 'Makefile.')
301
+
302
+ # All supported extensions flat list (programming languages + declarative)
303
+ ALL_EXTENSIONS = (
304
+ [ext for exts in LANGUAGE_EXTENSIONS.values() for ext in exts]
305
+ + [ext for exts in DECLARATIVE_EXTENSIONS.values() for ext in exts]
306
+ )
307
+
308
+ # Flat set of recognised filenames (case-insensitive compare at collection time).
309
+ ALL_FILENAMES = frozenset(
310
+ name for names in LANGUAGE_FILENAMES.values() for name in names
311
+ )
243
312
 
244
313
  # Node types
245
314
  NODE_TYPES = {
@@ -65,6 +65,8 @@ class FileAnalyzer:
65
65
  cached = self.cache.get_fast(file_path)
66
66
  if cached:
67
67
  self.stats['cache_hits'] += 1
68
+ if 'file' not in cached:
69
+ cached['file'] = file_path
68
70
  return cached
69
71
 
70
72
  ext = path.suffix.lower()
@@ -75,6 +77,14 @@ class FileAnalyzer:
75
77
 
76
78
  result = self._route_to_language_analyzer(content, file_path, module_name, ext)
77
79
 
80
+ # Tag result with its source file so downstream callers
81
+ # (e.g. PersistentCache in ProjectAnalyzer._store_to_persistent_cache)
82
+ # can match results back to file paths. Without this, the persistent
83
+ # manifest never gets populated and the export-level cache key
84
+ # collapses to md5("{}")[:12], causing stale exports to be reused.
85
+ if result:
86
+ result['file'] = file_path
87
+
78
88
  if self.cache and self.config.performance.enable_cache and result:
79
89
  self.cache.put_fast(file_path, result)
80
90
 
@@ -36,27 +36,26 @@ VERSION = 2
36
36
  _DEFAULT_ROOT = os.path.expanduser("~/.code2llm")
37
37
  _MAX_CACHE_MB = 500
38
38
  _GC_THRESHOLD = 0.8
39
-
40
- try:
41
- import msgpack as _msgpack
42
- _HAS_MSGPACK = True
43
- except ImportError:
44
- _HAS_MSGPACK = False
45
-
39
+ _DEFAULT_TTL_DAYS = 1.0
40
+ # Opt-out via env var: CODE2LLM_AUTO_CLEANUP=0 disables automatic TTL-based
41
+ # cleanup on PersistentCache initialisation. Override TTL via
42
+ # CODE2LLM_CACHE_TTL_DAYS (accepts fractional days, e.g. "0.5").
43
+ _ENV_AUTO_CLEANUP = "CODE2LLM_AUTO_CLEANUP"
44
+ _ENV_TTL_DAYS = "CODE2LLM_CACHE_TTL_DAYS"
46
45
 
47
46
  def _pack(obj: Any) -> bytes:
48
- if _HAS_MSGPACK:
49
- return _msgpack.packb(obj, use_bin_type=True)
47
+ # Always pickle: analysis results contain dataclasses (ModuleInfo,
48
+ # FunctionInfo, ...) and graph objects that msgpack cannot serialize
49
+ # natively. Falling back to pickle unconditionally keeps the cache
50
+ # correct; the speed/size delta is negligible for our payloads.
50
51
  return pickle.dumps(obj, protocol=4)
51
52
 
52
53
 
53
54
  def _unpack(data: bytes) -> Any:
54
- if _HAS_MSGPACK:
55
- return _msgpack.unpackb(data, raw=False)
56
55
  return pickle.loads(data)
57
56
 
58
57
 
59
- _EXT = "msgpack" if _HAS_MSGPACK else "pkl"
58
+ _EXT = "pkl"
60
59
 
61
60
 
62
61
  class PersistentCache:
@@ -67,7 +66,13 @@ class PersistentCache:
67
66
  safe because entries are immutable once written (content-addressed).
68
67
  """
69
68
 
70
- def __init__(self, project_dir: str, cache_root: Optional[str] = None):
69
+ def __init__(
70
+ self,
71
+ project_dir: str,
72
+ cache_root: Optional[str] = None,
73
+ auto_cleanup: Optional[bool] = None,
74
+ ttl_days: Optional[float] = None,
75
+ ):
71
76
  self._project_dir = os.path.realpath(project_dir)
72
77
  self._root = Path(cache_root or _DEFAULT_ROOT)
73
78
  self._project_hash = hashlib.md5(
@@ -85,6 +90,21 @@ class PersistentCache:
85
90
  self._manifest: Dict[str, Any] = self._load_manifest()
86
91
  self._dirty = False
87
92
 
93
+ # Resolve cleanup policy: explicit arg > env var > default.
94
+ if auto_cleanup is None:
95
+ auto_cleanup = os.environ.get(_ENV_AUTO_CLEANUP, "1") != "0"
96
+ if ttl_days is None:
97
+ try:
98
+ ttl_days = float(os.environ.get(_ENV_TTL_DAYS, _DEFAULT_TTL_DAYS))
99
+ except ValueError:
100
+ ttl_days = _DEFAULT_TTL_DAYS
101
+ if auto_cleanup:
102
+ try:
103
+ self.auto_cleanup(ttl_days=ttl_days)
104
+ except Exception as exc:
105
+ # Never let cleanup errors break analysis.
106
+ logger.debug("auto_cleanup failed: %s", exc)
107
+
88
108
  # ------------------------------------------------------------------
89
109
  # File-level cache
90
110
  # ------------------------------------------------------------------
@@ -167,12 +187,43 @@ class PersistentCache:
167
187
 
168
188
  return changed, cached
169
189
 
190
+ def prune_missing(self, current_filepaths: List[str]) -> List[str]:
191
+ """Remove manifest entries for files not present in *current_filepaths*.
192
+
193
+ Keeps the manifest in sync with the real project state so that a
194
+ deleted source file invalidates the export-level cache (the
195
+ per-file cache key is content-addressed, but the export-level cache
196
+ key is derived from the manifest and therefore must shrink when
197
+ files disappear).
198
+
199
+ Returns the list of relative paths that were removed.
200
+ """
201
+ current_rel = {
202
+ os.path.relpath(fp, self._project_dir) for fp in current_filepaths
203
+ }
204
+ files_index = self._manifest["files"]
205
+ stale = [rel for rel in files_index if rel not in current_rel]
206
+ if not stale:
207
+ return []
208
+ for rel in stale:
209
+ files_index.pop(rel, None)
210
+ self._dirty = True
211
+ return stale
212
+
170
213
  # ------------------------------------------------------------------
171
214
  # Export-level cache
172
215
  # ------------------------------------------------------------------
173
216
 
174
217
  def get_export_cache_dir(self, config_dict: Dict) -> Optional[Path]:
175
- """Return path to a complete cached export, or None if stale/absent."""
218
+ """Return path to a complete cached export, or None if stale/absent.
219
+
220
+ Safety: when the per-file manifest is empty we cannot reliably key
221
+ the run (hash collapses to md5("{}")), so we refuse to return a
222
+ cached export. This avoids propagating stale content from an earlier
223
+ run after the per-file cache was cleared or never populated.
224
+ """
225
+ if not self._manifest.get("files"):
226
+ return None
176
227
  run_hash = self._compute_run_hash(config_dict)
177
228
  export_dir = self._exports_dir / run_hash
178
229
  if export_dir.exists() and (export_dir / "_complete").exists():
@@ -231,6 +282,68 @@ class PersistentCache:
231
282
  except OSError:
232
283
  return 0.0
233
284
 
285
+ def auto_cleanup(self, ttl_days: float = _DEFAULT_TTL_DAYS) -> Dict[str, int]:
286
+ """Remove stale cache artefacts older than *ttl_days*.
287
+
288
+ Runs automatically on `__init__` (opt-out via env var
289
+ `CODE2LLM_AUTO_CLEANUP=0`). Prevents unbounded disk growth while
290
+ keeping content-addressed file cache entries that are still
291
+ referenced by the manifest.
292
+
293
+ What is removed:
294
+ - Export directories with a `_complete` stamp older than TTL,
295
+ or export dirs never marked complete whose mtime is older
296
+ than TTL (dead/abandoned runs).
297
+ - File-level cache entries (`files/*.pkl`) whose mtime is older
298
+ than TTL AND whose hash is no longer referenced by the
299
+ manifest (orphaned). Referenced entries are kept regardless
300
+ of age — they represent content still present in the project.
301
+
302
+ Returns a dict summary `{"exports": n, "files": n}` for logging.
303
+ """
304
+ cutoff = time.time() - (ttl_days * 86400)
305
+ removed = {"exports": 0, "files": 0}
306
+
307
+ # 1) Stale exports (complete or abandoned)
308
+ if self._exports_dir.exists():
309
+ for export_dir in list(self._exports_dir.iterdir()):
310
+ if not export_dir.is_dir():
311
+ continue
312
+ complete = export_dir / "_complete"
313
+ try:
314
+ if complete.exists():
315
+ ts = float(complete.read_text())
316
+ else:
317
+ ts = export_dir.stat().st_mtime
318
+ if ts < cutoff:
319
+ shutil.rmtree(export_dir, ignore_errors=True)
320
+ removed["exports"] += 1
321
+ except (ValueError, OSError):
322
+ pass
323
+
324
+ # 2) Orphaned file-level entries older than TTL
325
+ known = {v.get("hash") for v in self._manifest.get("files", {}).values()}
326
+ if self._files_dir.exists():
327
+ for f in list(self._files_dir.iterdir()):
328
+ if not f.is_file():
329
+ continue
330
+ if f.stem in known:
331
+ continue # still referenced
332
+ try:
333
+ if f.stat().st_mtime < cutoff:
334
+ f.unlink()
335
+ removed["files"] += 1
336
+ except OSError:
337
+ pass
338
+
339
+ if removed["exports"] or removed["files"]:
340
+ logger.debug(
341
+ "auto_cleanup: removed %d exports, %d orphan file entries "
342
+ "(ttl=%.1fd, project=%s)",
343
+ removed["exports"], removed["files"], ttl_days, self._project_dir,
344
+ )
345
+ return removed
346
+
234
347
  def gc(self, max_age_days: int = 30, max_size_mb: int = _MAX_CACHE_MB) -> int:
235
348
  """Remove stale exports and orphaned file entries.
236
349
 
@@ -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.141"
7
+ __version__ = "0.5.143"
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.141
3
+ Version: 0.5.143
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
@@ -67,16 +67,18 @@ 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.141-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-66.9h-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.143-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-69.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
- - 🤖 **LLM usage:** $7.5000 (190 commits)
74
- - 👤 **Human dev:** ~$6690 (66.9h @ $100/h, 30min dedup)
73
+ - 🤖 **LLM usage:** $7.5000 (195 commits)
74
+ - 👤 **Human dev:** ~$6974 (69.7h @ $100/h, 30min dedup)
75
75
 
76
- Generated on 2026-04-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
+ Generated on 2026-04-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
77
77
 
78
78
  ---
79
79
 
80
+
81
+
80
82
  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.
81
83
 
82
84
  ## 📁 Generated Files Overview
@@ -88,6 +90,7 @@ When you run `code2llm ./ -f all`, the following files are created:
88
90
  | File | Format | Purpose | Key Insights |
89
91
  |------|--------|---------|--------------|
90
92
  | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
93
+ | `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
91
94
 
92
95
  ### 🤖 LLM-Ready Documentation
93
96
 
@@ -404,10 +407,10 @@ code2llm ./ -f yaml --separate-orphans
404
407
  ---
405
408
 
406
409
  **Generated by**: `code2llm ./ -f all --readme`
407
- **Analysis Date**: 2026-04-20
408
- **Total Functions**: 2124
409
- **Total Classes**: 241
410
- **Modules**: 359
410
+ **Analysis Date**: 2026-04-21
411
+ **Total Functions**: 2150
412
+ **Total Classes**: 245
413
+ **Modules**: 369
411
414
 
412
415
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
413
416
 
@@ -173,9 +173,12 @@ code2llm/refactor/__init__.py
173
173
  code2llm/refactor/prompt_engine.py
174
174
  tests/test_advanced_analysis.py
175
175
  tests/test_analyzer.py
176
+ tests/test_cache_invalidation_e2e.py
176
177
  tests/test_calls_toon_export.py
178
+ tests/test_declarative_collection.py
177
179
  tests/test_deep_analysis.py
178
180
  tests/test_edge_cases.py
181
+ tests/test_file_analyzer_tagging.py
179
182
  tests/test_flow_exporter.py
180
183
  tests/test_format_quality.py
181
184
  tests/test_multilanguage_e2e.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.141"
7
+ version = "0.5.143"
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"