code2llm 0.5.174__tar.gz → 0.5.175__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 (213) hide show
  1. {code2llm-0.5.174/code2llm.egg-info → code2llm-0.5.175}/PKG-INFO +6 -6
  2. {code2llm-0.5.174 → code2llm-0.5.175}/README.md +5 -5
  3. code2llm-0.5.175/VERSION +1 -0
  4. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/__init__.py +1 -1
  5. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/orchestrator.py +4 -0
  6. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/base.py +26 -2
  7. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/flow_constants.py +28 -0
  8. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/header.py +4 -1
  9. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map_exporter.py +1 -4
  10. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/planfile_tickets.py +6 -1
  11. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/metrics_duplicates.py +4 -0
  12. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/mermaid/png.py +36 -0
  13. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/nlp/__init__.py +1 -1
  14. {code2llm-0.5.174 → code2llm-0.5.175/code2llm.egg-info}/PKG-INFO +6 -6
  15. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm.egg-info/SOURCES.txt +2 -0
  16. {code2llm-0.5.174 → code2llm-0.5.175}/pyproject.toml +1 -1
  17. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_dup_groups.py +61 -1
  18. code2llm-0.5.175/tests/test_export_profile_contract.py +28 -0
  19. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_format_quality.py +3 -0
  20. code2llm-0.5.175/tests/test_mermaid_png.py +29 -0
  21. code2llm-0.5.174/VERSION +0 -1
  22. {code2llm-0.5.174 → code2llm-0.5.175}/LICENSE +0 -0
  23. {code2llm-0.5.174 → code2llm-0.5.175}/MANIFEST.in +0 -0
  24. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/__main__.py +0 -0
  25. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/__init__.py +0 -0
  26. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/_data_impl.py +0 -0
  27. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/call_graph.py +0 -0
  28. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/cfg.py +0 -0
  29. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/coupling.py +0 -0
  30. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/data_analysis.py +0 -0
  31. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/dfg.py +0 -0
  32. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/pipeline_classifier.py +0 -0
  33. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/pipeline_detector.py +0 -0
  34. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/pipeline_resolver.py +0 -0
  35. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/side_effects.py +0 -0
  36. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/smells.py +0 -0
  37. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/type_inference.py +0 -0
  38. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/utils/__init__.py +0 -0
  39. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/analysis/utils/ast_helpers.py +0 -0
  40. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/api.py +0 -0
  41. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli.py +0 -0
  42. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_analysis.py +0 -0
  43. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_commands.py +0 -0
  44. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/__init__.py +0 -0
  45. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/code2logic.py +0 -0
  46. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/formats.py +0 -0
  47. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
  48. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/orchestrator_constants.py +0 -0
  49. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
  50. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_exports/prompt.py +0 -0
  51. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/cli_parser.py +0 -0
  52. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/__init__.py +0 -0
  53. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/analyzer.py +0 -0
  54. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/ast_registry.py +0 -0
  55. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/config.py +0 -0
  56. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/export_pipeline.py +0 -0
  57. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/file_analyzer.py +0 -0
  58. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/file_cache.py +0 -0
  59. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/file_filter.py +0 -0
  60. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/gitignore.py +0 -0
  61. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/incremental.py +0 -0
  62. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/__init__.py +0 -0
  63. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/_c_parser.py +0 -0
  64. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/_calls.py +0 -0
  65. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/_complexity.py +0 -0
  66. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/base.py +0 -0
  67. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/cpp.py +0 -0
  68. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/csharp.py +0 -0
  69. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/generic.py +0 -0
  70. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/go_lang.py +0 -0
  71. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/java.py +0 -0
  72. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/php.py +0 -0
  73. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/ruby.py +0 -0
  74. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/rust.py +0 -0
  75. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/ts_extractors.py +0 -0
  76. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/ts_parser.py +0 -0
  77. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/lang/typescript.py +0 -0
  78. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/large_repo.py +0 -0
  79. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/models.py +0 -0
  80. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/persistent_cache.py +0 -0
  81. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/refactoring.py +0 -0
  82. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/repo_files.py +0 -0
  83. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/source_classifier.py +0 -0
  84. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming/__init__.py +0 -0
  85. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming/cache.py +0 -0
  86. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming/incremental.py +0 -0
  87. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming/prioritizer.py +0 -0
  88. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming/scanner.py +0 -0
  89. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming/strategies.py +0 -0
  90. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/streaming_analyzer.py +0 -0
  91. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/core/toon_size_manager.py +0 -0
  92. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/__init__.py +0 -0
  93. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/article_view.py +0 -0
  94. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/context_exporter.py +0 -0
  95. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/context_view.py +0 -0
  96. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/dashboard_data.py +0 -0
  97. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/dashboard_renderer.py +0 -0
  98. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution/__init__.py +0 -0
  99. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution/computation.py +0 -0
  100. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution/constants.py +0 -0
  101. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution/exclusion.py +0 -0
  102. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution/render.py +0 -0
  103. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution/yaml_export.py +0 -0
  104. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/evolution_exporter.py +0 -0
  105. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/flow_exporter.py +0 -0
  106. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/flow_renderer.py +0 -0
  107. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/html_dashboard.py +0 -0
  108. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/index_generator/__init__.py +0 -0
  109. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/index_generator/renderer.py +0 -0
  110. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/index_generator/scanner.py +0 -0
  111. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/index_generator.py +0 -0
  112. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/json_exporter.py +0 -0
  113. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/llm_exporter.py +0 -0
  114. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/__init__.py +0 -0
  115. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/alerts.py +0 -0
  116. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/details.py +0 -0
  117. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/module_list.py +0 -0
  118. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/utils.py +0 -0
  119. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/map/yaml_export.py +0 -0
  120. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/__init__.py +0 -0
  121. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/calls.py +0 -0
  122. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/classic.py +0 -0
  123. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/compact.py +0 -0
  124. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/flow_compact.py +0 -0
  125. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
  126. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/flow_full.py +0 -0
  127. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid/utils.py +0 -0
  128. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid_exporter.py +0 -0
  129. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  130. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/__init__.py +0 -0
  131. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/constants.py +0 -0
  132. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/core.py +0 -0
  133. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/evolution.py +0 -0
  134. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/health.py +0 -0
  135. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/hotspots.py +0 -0
  136. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml/modules.py +0 -0
  137. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/project_yaml_exporter.py +0 -0
  138. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/readme/__init__.py +0 -0
  139. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/readme/content.py +0 -0
  140. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/readme/files.py +0 -0
  141. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/readme/insights.py +0 -0
  142. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/readme/sections.py +0 -0
  143. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/readme_exporter.py +0 -0
  144. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/report_generators.py +0 -0
  145. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/__init__.py +0 -0
  146. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
  147. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/_render_section_helpers.py +0 -0
  148. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/constants.py +0 -0
  149. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/helpers.py +0 -0
  150. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/metrics.py +0 -0
  151. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/metrics_core.py +0 -0
  152. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/metrics_health.py +0 -0
  153. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/module_detail.py +0 -0
  154. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon/renderer.py +0 -0
  155. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon.py +0 -0
  156. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/toon_view.py +0 -0
  157. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/validate_project.py +0 -0
  158. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/exporters/yaml_exporter.py +0 -0
  159. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/__init__.py +0 -0
  160. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/_utils.py +0 -0
  161. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/__init__.py +0 -0
  162. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/analysis.py +0 -0
  163. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/cli.py +0 -0
  164. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/generator.py +0 -0
  165. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/nodes.py +0 -0
  166. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/parsing.py +0 -0
  167. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow/utils.py +0 -0
  168. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_flow.py +0 -0
  169. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/llm_task.py +0 -0
  170. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/mermaid/__init__.py +0 -0
  171. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/mermaid/fix.py +0 -0
  172. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/mermaid/validation.py +0 -0
  173. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/generators/mermaid.py +0 -0
  174. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/nlp/config.py +0 -0
  175. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/nlp/entity_resolution.py +0 -0
  176. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/nlp/intent_matching.py +0 -0
  177. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/nlp/normalization.py +0 -0
  178. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/nlp/pipeline.py +0 -0
  179. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/parsers/toon_parser.py +0 -0
  180. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/patterns/__init__.py +0 -0
  181. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/patterns/detector.py +0 -0
  182. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/refactor/__init__.py +0 -0
  183. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm/refactor/prompt_engine.py +0 -0
  184. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm.egg-info/dependency_links.txt +0 -0
  185. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm.egg-info/entry_points.txt +0 -0
  186. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm.egg-info/requires.txt +0 -0
  187. {code2llm-0.5.174 → code2llm-0.5.175}/code2llm.egg-info/top_level.txt +0 -0
  188. {code2llm-0.5.174 → code2llm-0.5.175}/setup.cfg +0 -0
  189. {code2llm-0.5.174 → code2llm-0.5.175}/setup.py +0 -0
  190. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_advanced_analysis.py +0 -0
  191. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_analyzer.py +0 -0
  192. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_cache_invalidation_e2e.py +0 -0
  193. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_calls_toon_export.py +0 -0
  194. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_declarative_collection.py +0 -0
  195. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_deep_analysis.py +0 -0
  196. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_edge_cases.py +0 -0
  197. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_export_cache_flags.py +0 -0
  198. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_file_analyzer_tagging.py +0 -0
  199. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_flow_exporter.py +0 -0
  200. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_multilanguage_e2e.py +0 -0
  201. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_nlp_pipeline.py +0 -0
  202. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_nonpython_cc_calls.py +0 -0
  203. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_orchestrator_cache_mtime.py +0 -0
  204. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_persistent_cache.py +0 -0
  205. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_pipeline_detector.py +0 -0
  206. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_planfile_tickets_exporter.py +0 -0
  207. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_project_toon_export.py +0 -0
  208. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_prompt_engine.py +0 -0
  209. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_prompt_txt.py +0 -0
  210. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_refactoring_engine.py +0 -0
  211. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_smells_data_clumps.py +0 -0
  212. {code2llm-0.5.174 → code2llm-0.5.175}/tests/test_smells_shotgun_surgery.py +0 -0
  213. {code2llm-0.5.174 → code2llm-0.5.175}/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.174
3
+ Version: 0.5.175
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: Tom Sapletta
@@ -67,13 +67,13 @@ 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.174-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-$3.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-102.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
70
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.175-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-$3.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-104.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
72
72
 
73
- - 🤖 **LLM usage:** $3.8031 (260 commits)
74
- - 👤 **Human dev:** ~$10235 (102.3h @ $100/h, 30min dedup)
73
+ - 🤖 **LLM usage:** $3.8035 (262 commits)
74
+ - 👤 **Human dev:** ~$10435 (104.3h @ $100/h, 30min dedup)
75
75
 
76
- Generated on 2026-07-13 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
76
+ Generated on 2026-08-01 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
77
77
 
78
78
  ---
79
79
 
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## AI Cost Tracking
4
4
 
5
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.174-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-$3.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-102.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
5
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.175-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-$3.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-104.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
7
7
 
8
- - 🤖 **LLM usage:** $3.8031 (260 commits)
9
- - 👤 **Human dev:** ~$10235 (102.3h @ $100/h, 30min dedup)
8
+ - 🤖 **LLM usage:** $3.8035 (262 commits)
9
+ - 👤 **Human dev:** ~$10435 (104.3h @ $100/h, 30min dedup)
10
10
 
11
- Generated on 2026-07-13 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
11
+ Generated on 2026-08-01 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
12
12
 
13
13
  ---
14
14
 
@@ -0,0 +1 @@
1
+ 0.5.175
@@ -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.174"
11
+ __version__ = "0.5.175"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -42,6 +42,10 @@ def _build_export_config(args, formats: List[str]) -> Dict[str, Any]:
42
42
  """Build config dict for export caching."""
43
43
  return {
44
44
  "formats": sorted(formats),
45
+ "mode": getattr(args, "mode", None),
46
+ "strategy": getattr(args, "strategy", None),
47
+ "exclude": sorted(str(item) for item in (getattr(args, "exclude", None) or [])),
48
+ "gitignore": getattr(args, "gitignore", None),
45
49
  "png": getattr(args, "png", False),
46
50
  "no_png": getattr(args, "no_png", False),
47
51
  "flow_include_examples": getattr(args, "flow_include_examples", False),
@@ -7,6 +7,8 @@ Provides:
7
7
  """
8
8
 
9
9
  from abc import ABC, abstractmethod
10
+ import os
11
+ import tempfile
10
12
  from pathlib import Path
11
13
  from typing import Any, Dict, Optional, Type, ClassVar
12
14
  from code2llm.core.models import AnalysisResult
@@ -80,9 +82,31 @@ class BaseExporter(ABC):
80
82
  return path
81
83
 
82
84
  def _write_text(self, output_path: str, content: str) -> Path:
83
- """Write text content to file, ensuring directory exists."""
85
+ """Atomically write text content, ensuring the directory exists.
86
+
87
+ Analysis artifacts are consumed by background tools immediately after
88
+ generation. A same-directory temporary file plus ``os.replace`` keeps
89
+ readers from observing a truncated map while an export is in progress.
90
+ """
84
91
  path = self._ensure_dir(output_path)
85
- path.write_text(content, encoding="utf-8")
92
+ temporary = None
93
+ try:
94
+ with tempfile.NamedTemporaryFile(
95
+ mode="w",
96
+ encoding="utf-8",
97
+ dir=path.parent,
98
+ prefix=f".{path.name}.",
99
+ suffix=".tmp",
100
+ delete=False,
101
+ ) as handle:
102
+ handle.write(content)
103
+ handle.flush()
104
+ os.fsync(handle.fileno())
105
+ temporary = Path(handle.name)
106
+ os.replace(temporary, path)
107
+ finally:
108
+ if temporary is not None:
109
+ temporary.unlink(missing_ok=True)
86
110
  return path
87
111
 
88
112
 
@@ -46,6 +46,15 @@ INTENTIONAL_DUP_COPY_MARKERS: tuple[str, ...] = (
46
46
  "pc2/",
47
47
  "examples/",
48
48
  "-contract-",
49
+ "work/repos/",
50
+ )
51
+
52
+ # These repositories are deliberately separate execution planes. Classes with
53
+ # similar infrastructure-shaped APIs across two different agents are not
54
+ # consolidation candidates: sharing their implementations would couple the
55
+ # independent diagnostic, repair, and validation boundaries.
56
+ INDEPENDENT_AGENT_REPOSITORIES = frozenset(
57
+ {"doctor-agent", "repair-agent", "validator-agent"}
49
58
  )
50
59
 
51
60
 
@@ -58,6 +67,25 @@ def is_intentional_duplicate_copy(path: str) -> bool:
58
67
  return any(marker in norm for marker in INTENTIONAL_DUP_COPY_MARKERS)
59
68
 
60
69
 
70
+ @lru_cache(maxsize=4096)
71
+ def _independent_agent_repository(path: str) -> str | None:
72
+ """Return the independent agent repository segment present in *path*."""
73
+ if not path:
74
+ return None
75
+ parts = path.replace("\\", "/").lower().split("/")
76
+ return next((part for part in parts if part in INDEPENDENT_AGENT_REPOSITORIES), None)
77
+
78
+
79
+ @lru_cache(maxsize=4096)
80
+ def is_intentional_duplicate_pair(path_a: str, path_b: str) -> bool:
81
+ """True when two similar classes belong to intentionally parallel copies."""
82
+ if is_intentional_duplicate_copy(path_a) or is_intentional_duplicate_copy(path_b):
83
+ return True
84
+ repo_a = _independent_agent_repository(path_a)
85
+ repo_b = _independent_agent_repository(path_b)
86
+ return repo_a is not None and repo_b is not None and repo_a != repo_b
87
+
88
+
61
89
  @lru_cache(maxsize=4096)
62
90
  def is_excluded_path(path: str) -> bool:
63
91
  """Return True if *path* matches any standard exclusion pattern (venv, cache, etc.)."""
@@ -8,6 +8,8 @@ from code2llm.core.models import AnalysisResult, FunctionInfo, ModuleInfo
8
8
  from .utils import count_total_lines, detect_languages
9
9
  from .alerts import build_alerts, build_hotspots, load_evolution_trend
10
10
 
11
+ MAP_PRODUCER_HEADER = "# producer: code2llm | artifact: map.toon.yaml | schema: 1"
12
+
11
13
 
12
14
  def render_header(
13
15
  result: AnalysisResult, output_path: str, is_excluded_path
@@ -38,6 +40,7 @@ def render_header(
38
40
 
39
41
  lines = [
40
42
  f"# {project_name} | {stats_line['files']}f {stats_line['lines']}L | {lang_str} | {datetime.now().strftime('%Y-%m-%d')}", # noqa: E501
43
+ MAP_PRODUCER_HEADER,
41
44
  f"# stats: {stats_line['funcs']} func | {stats_line['classes']} cls | {stats_line['files']} mod | CC̄={stats_line['avg_cc']} | critical:{stats_line['critical']} | cycles:{stats_line['cycles']}", # noqa: E501
42
45
  alerts_line,
43
46
  hotspots_line,
@@ -78,4 +81,4 @@ def _render_hotspots_line(funcs: List[FunctionInfo]) -> str:
78
81
  return f"# hotspots[{len(hotspots)}]: {'; '.join(hotspots) if hotspots else 'none'}"
79
82
 
80
83
 
81
- __all__ = ["render_header"]
84
+ __all__ = ["MAP_PRODUCER_HEADER", "render_header"]
@@ -41,10 +41,7 @@ class MapExporter(BaseExporter):
41
41
  lines.extend(render_module_list(result, is_excluded_path))
42
42
  lines.extend(render_details(result, is_excluded_path))
43
43
 
44
- path = self._ensure_dir(output_path)
45
- with open(path, "w", encoding="utf-8") as f:
46
- f.write("\n".join(lines) + "\n")
47
- return path
44
+ return self._write_text(output_path, "\n".join(lines) + "\n")
48
45
 
49
46
  def export_to_yaml(
50
47
  self, result: AnalysisResult, output_path: str, **kwargs
@@ -293,7 +293,10 @@ def _duplicate_class_tickets(result: AnalysisResult) -> list[PlanfileTicketSugge
293
293
 
294
294
  def _duplicate_class_pairs(result: AnalysisResult) -> list[tuple[str, str, str]]:
295
295
  """Return (class_name, file_a, file_b) for each similar class pair."""
296
- from code2llm.exporters.flow_constants import is_intentional_duplicate_copy
296
+ from code2llm.exporters.flow_constants import (
297
+ is_intentional_duplicate_copy,
298
+ is_intentional_duplicate_pair,
299
+ )
297
300
 
298
301
  pairs: list[tuple[str, str, str]] = []
299
302
  classes = [
@@ -308,6 +311,8 @@ def _duplicate_class_pairs(result: AnalysisResult) -> list[tuple[str, str, str]]
308
311
  for right in classes[index + 1 :]:
309
312
  if left.name != right.name:
310
313
  continue
314
+ if is_intentional_duplicate_pair(left.file, right.file):
315
+ continue
311
316
  right_methods = _method_names(right)
312
317
  if len(right_methods) < 3:
313
318
  continue
@@ -7,6 +7,8 @@ from typing import Any, Dict, List, Optional, Set
7
7
 
8
8
  from code2llm.core.models import AnalysisResult, ClassInfo
9
9
 
10
+ from code2llm.exporters.flow_constants import is_intentional_duplicate_pair
11
+
10
12
  from .helpers import _is_excluded, _rel_path, is_intentional_duplicate_copy
11
13
 
12
14
 
@@ -47,6 +49,8 @@ class DuplicatesMetricsComputer:
47
49
  qb, cb = class_list[j]
48
50
  if ca.name != cb.name:
49
51
  continue
52
+ if is_intentional_duplicate_pair(ca.file, cb.file):
53
+ continue
50
54
 
51
55
  methods_b = {m.split(".")[-1] for m in cb.methods}
52
56
  if len(methods_b) < 3:
@@ -4,6 +4,7 @@ import os
4
4
  import subprocess
5
5
  import tempfile
6
6
  import json
7
+ import shutil
7
8
  from concurrent.futures import ThreadPoolExecutor, as_completed
8
9
  from pathlib import Path
9
10
  from typing import List, Optional
@@ -17,6 +18,38 @@ from .validation import validate_mermaid_file
17
18
  from .fix import fix_mermaid_file
18
19
 
19
20
 
21
+ _BROWSER_ENV_VARS = (
22
+ "PUPPETEER_EXECUTABLE_PATH",
23
+ "CHROME_PATH",
24
+ "GOOGLE_CHROME_BIN",
25
+ )
26
+ _BROWSER_COMMANDS = (
27
+ "google-chrome",
28
+ "google-chrome-stable",
29
+ "chromium",
30
+ "chromium-browser",
31
+ )
32
+
33
+
34
+ def _find_browser_executable() -> Optional[str]:
35
+ """Return an explicit Chrome/Chromium path suitable for Puppeteer.
36
+
37
+ Mermaid CLI bundles puppeteer-core, which does not download Chromium on
38
+ installation. Prefer an explicitly configured browser and otherwise use
39
+ the system browser already available on the PATH.
40
+ """
41
+ for variable in _BROWSER_ENV_VARS:
42
+ candidate = os.getenv(variable)
43
+ if candidate and Path(candidate).is_file() and os.access(candidate, os.X_OK):
44
+ return candidate
45
+
46
+ for command in _BROWSER_COMMANDS:
47
+ candidate = shutil.which(command)
48
+ if candidate:
49
+ return candidate
50
+ return None
51
+
52
+
20
53
  def _is_png_fresh(mmd_file: Path, output_dir: Path) -> bool:
21
54
  """Check if PNG exists and is newer than MMD source."""
22
55
  png_file = output_dir / f"{mmd_file.stem}.png"
@@ -115,6 +148,9 @@ def _setup_puppeteer_config() -> tuple[int, int, Optional[str], Optional[str]]:
115
148
  puppeteer_cfg = {
116
149
  "args": ["--no-sandbox", "--disable-setuid-sandbox"],
117
150
  }
151
+ browser_executable = _find_browser_executable()
152
+ if browser_executable:
153
+ puppeteer_cfg["executablePath"] = browser_executable
118
154
  with tempfile.NamedTemporaryFile(
119
155
  mode="w", suffix=".json", delete=False
120
156
  ) as tmp_pcfg:
@@ -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.174"
7
+ __version__ = "0.5.175"
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.174
3
+ Version: 0.5.175
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: Tom Sapletta
@@ -67,13 +67,13 @@ 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.174-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-$3.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-102.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
70
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.175-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-$3.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-104.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
72
72
 
73
- - 🤖 **LLM usage:** $3.8031 (260 commits)
74
- - 👤 **Human dev:** ~$10235 (102.3h @ $100/h, 30min dedup)
73
+ - 🤖 **LLM usage:** $3.8035 (262 commits)
74
+ - 👤 **Human dev:** ~$10435 (104.3h @ $100/h, 30min dedup)
75
75
 
76
- Generated on 2026-07-13 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
76
+ Generated on 2026-08-01 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
77
77
 
78
78
  ---
79
79
 
@@ -189,9 +189,11 @@ tests/test_deep_analysis.py
189
189
  tests/test_dup_groups.py
190
190
  tests/test_edge_cases.py
191
191
  tests/test_export_cache_flags.py
192
+ tests/test_export_profile_contract.py
192
193
  tests/test_file_analyzer_tagging.py
193
194
  tests/test_flow_exporter.py
194
195
  tests/test_format_quality.py
196
+ tests/test_mermaid_png.py
195
197
  tests/test_multilanguage_e2e.py
196
198
  tests/test_nlp_pipeline.py
197
199
  tests/test_nonpython_cc_calls.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.174"
7
+ version = "0.5.175"
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.10"
@@ -5,7 +5,10 @@ from __future__ import annotations
5
5
  from pathlib import Path
6
6
 
7
7
  from code2llm.core.models import AnalysisResult, ClassInfo
8
- from code2llm.exporters.flow_constants import is_intentional_duplicate_copy
8
+ from code2llm.exporters.flow_constants import (
9
+ is_intentional_duplicate_copy,
10
+ is_intentional_duplicate_pair,
11
+ )
9
12
  from code2llm.exporters.planfile_tickets import collect_planfile_tickets
10
13
  from code2llm.exporters.toon.metrics_duplicates import DuplicatesMetricsComputer
11
14
  from code2llm.exporters.toon.metrics_health import HealthMetricsComputer
@@ -26,9 +29,23 @@ def test_intentional_copy_paths_are_detected() -> None:
26
29
  assert is_intentional_duplicate_copy("/repo/pc1/net-user-pl/handler.py")
27
30
  assert is_intentional_duplicate_copy("/repo/examples/demo/foo.py")
28
31
  assert is_intentional_duplicate_copy("/repo/urirun-contract-windowpair/x.py")
32
+ assert is_intentional_duplicate_copy(
33
+ "/repo/doctor-agent/work/repos/runtime/src/executor.py"
34
+ )
29
35
  assert not is_intentional_duplicate_copy("/repo/app/src/handler.py")
30
36
 
31
37
 
38
+ def test_independent_agent_repositories_are_an_intentional_pair() -> None:
39
+ doctor = "/repo/doctor-agent/src/ifuri_doctor/command.py"
40
+ repair = "/repo/repair-agent/src/repair_agent/command.py"
41
+ validator = r"C:\repo\validator-agent\src\validator_agent\command.py"
42
+
43
+ assert is_intentional_duplicate_pair(doctor, repair)
44
+ assert is_intentional_duplicate_pair(repair, validator)
45
+ assert not is_intentional_duplicate_pair(repair, repair)
46
+ assert not is_intentional_duplicate_pair(repair, "/repo/app/command.py")
47
+
48
+
32
49
  def test_duplicate_groups_not_pair_count(tmp_path: Path) -> None:
33
50
  project = tmp_path
34
51
  result = AnalysisResult(project_path=str(project))
@@ -78,3 +95,46 @@ def test_examples_dir_excluded_from_duplicate_pairs(tmp_path: Path) -> None:
78
95
 
79
96
  dupes = DuplicatesMetricsComputer(str(project)).detect_duplicates(result)
80
97
  assert dupes == []
98
+
99
+
100
+ def test_cross_agent_duplicates_are_excluded_but_same_agent_duplicates_remain(
101
+ tmp_path: Path,
102
+ ) -> None:
103
+ result = AnalysisResult(project_path=str(tmp_path))
104
+ paths = {
105
+ "doctor.main": tmp_path / "doctor-agent/src/main.py",
106
+ "doctor.legacy": tmp_path / "doctor-agent/src/legacy.py",
107
+ "repair.main": tmp_path / "repair-agent/src/main.py",
108
+ "validator.main": tmp_path / "validator-agent/src/main.py",
109
+ }
110
+ for pkg, path in paths.items():
111
+ path.parent.mkdir(parents=True, exist_ok=True)
112
+ path.write_text("# stub\n", encoding="utf-8")
113
+ result.classes[f"{pkg}.Worker"] = _worker(path, pkg)
114
+
115
+ dupes = DuplicatesMetricsComputer(str(tmp_path)).detect_duplicates(result)
116
+
117
+ assert len(dupes) == 1
118
+ assert dupes[0]["fileA"] == "doctor-agent/src/main.py"
119
+ assert dupes[0]["fileB"] == "doctor-agent/src/legacy.py"
120
+
121
+
122
+ def test_cross_agent_duplicates_do_not_create_planfile_ticket(tmp_path: Path) -> None:
123
+ result = AnalysisResult(project_path=str(tmp_path))
124
+ for repo, pkg in (
125
+ ("doctor-agent", "doctor"),
126
+ ("repair-agent", "repair"),
127
+ ("validator-agent", "validator"),
128
+ ):
129
+ path = tmp_path / repo / "src/command.py"
130
+ path.parent.mkdir(parents=True, exist_ok=True)
131
+ path.write_text("# stub\n", encoding="utf-8")
132
+ result.classes[f"{pkg}.Worker"] = _worker(path, pkg)
133
+
134
+ tickets = [
135
+ ticket
136
+ for ticket in collect_planfile_tickets(result)
137
+ if ticket.signal == "code2llm_dup"
138
+ ]
139
+
140
+ assert tickets == []
@@ -0,0 +1,28 @@
1
+ from types import SimpleNamespace
2
+
3
+ from code2llm.cli_exports.orchestrator import _build_export_config
4
+
5
+
6
+ def _args(**overrides):
7
+ values = {
8
+ "mode": "hybrid",
9
+ "strategy": None,
10
+ "exclude": [],
11
+ "gitignore": True,
12
+ }
13
+ values.update(overrides)
14
+ return SimpleNamespace(**values)
15
+
16
+
17
+ def test_export_cache_key_inputs_include_scan_profile():
18
+ full = _build_export_config(_args(), ["map"])
19
+ scoped = _build_export_config(
20
+ _args(mode="static", strategy="quick", exclude=["plugins", "*.md"]),
21
+ ["map"],
22
+ )
23
+
24
+ assert full["mode"] == "hybrid"
25
+ assert scoped["mode"] == "static"
26
+ assert scoped["strategy"] == "quick"
27
+ assert scoped["exclude"] == ["*.md", "plugins"]
28
+ assert full != scoped
@@ -280,6 +280,9 @@ class TestProjectMap:
280
280
  assert "etl.py" in map_content
281
281
  assert "utils.py" in map_content
282
282
 
283
+ def test_declares_code2llm_as_canonical_producer(self, map_content):
284
+ assert "# producer: code2llm | artifact: map.toon.yaml | schema: 1" in map_content
285
+
283
286
  def test_has_import_info(self, map_content):
284
287
  assert "i:" in map_content or "import" in map_content.lower()
285
288
 
@@ -0,0 +1,29 @@
1
+ import json
2
+
3
+ from code2llm.generators.mermaid import png
4
+
5
+
6
+ def test_find_browser_executable_prefers_puppeteer_environment(monkeypatch, tmp_path):
7
+ browser = tmp_path / "chrome"
8
+ browser.write_text("#!/bin/sh\n", encoding="utf-8")
9
+ browser.chmod(0o755)
10
+ monkeypatch.setenv("PUPPETEER_EXECUTABLE_PATH", str(browser))
11
+ monkeypatch.setattr(png.shutil, "which", lambda _command: None)
12
+
13
+ assert png._find_browser_executable() == str(browser)
14
+
15
+
16
+ def test_puppeteer_config_uses_detected_system_browser(monkeypatch):
17
+ monkeypatch.setattr(png, "_find_browser_executable", lambda: "/usr/bin/google-chrome")
18
+
19
+ _max_text_size, _max_edges, cfg_path, puppeteer_cfg_path = png._setup_puppeteer_config()
20
+ try:
21
+ assert cfg_path is not None
22
+ assert puppeteer_cfg_path is not None
23
+ config = json.loads(open(puppeteer_cfg_path, encoding="utf-8").read())
24
+ assert config["executablePath"] == "/usr/bin/google-chrome"
25
+ assert "--no-sandbox" in config["args"]
26
+ finally:
27
+ for path in (cfg_path, puppeteer_cfg_path):
28
+ if path:
29
+ png.os.unlink(path)
code2llm-0.5.174/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.5.174
File without changes
File without changes
File without changes
File without changes