code2llm 0.5.114__tar.gz → 0.5.116__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 (143) hide show
  1. {code2llm-0.5.114 → code2llm-0.5.116}/PKG-INFO +16 -11
  2. {code2llm-0.5.114 → code2llm-0.5.116}/README.md +15 -10
  3. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/call_graph.py +2 -7
  5. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/cfg.py +2 -7
  6. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/dfg.py +2 -7
  7. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/utils/__init__.py +2 -2
  8. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/utils/ast_helpers.py +19 -0
  9. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_analysis.py +5 -1
  10. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_commands.py +67 -1
  11. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/formats.py +23 -11
  12. code2llm-0.5.116/code2llm/cli_exports/orchestrator.py +337 -0
  13. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_parser.py +21 -1
  14. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/analyzer.py +147 -69
  15. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/config.py +2 -1
  16. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/file_filter.py +48 -25
  17. code2llm-0.5.116/code2llm/core/gitignore.py +138 -0
  18. code2llm-0.5.116/code2llm/core/lang/__init__.py +171 -0
  19. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/ruby.py +22 -1
  20. code2llm-0.5.116/code2llm/core/persistent_cache.py +322 -0
  21. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/repo_files.py +2 -1
  22. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/__init__.py +29 -2
  23. code2llm-0.5.116/code2llm/exporters/base.py +158 -0
  24. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/context_exporter.py +9 -7
  25. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/evolution_exporter.py +7 -5
  26. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/flow_exporter.py +7 -5
  27. code2llm-0.5.116/code2llm/exporters/json_exporter.py +27 -0
  28. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/map_exporter.py +8 -5
  29. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/mermaid_exporter.py +55 -43
  30. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/core.py +6 -4
  31. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/hotspots.py +1 -1
  32. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/readme_exporter.py +11 -8
  33. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/__init__.py +8 -5
  34. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/helpers.py +9 -4
  35. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/yaml_exporter.py +17 -8
  36. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/mermaid.py +23 -24
  37. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/__init__.py +1 -1
  38. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/PKG-INFO +16 -11
  39. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/SOURCES.txt +2 -0
  40. {code2llm-0.5.114 → code2llm-0.5.116}/pyproject.toml +1 -1
  41. {code2llm-0.5.114 → code2llm-0.5.116}/setup.py +1 -1
  42. code2llm-0.5.116/tests/test_persistent_cache.py +182 -0
  43. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_project_toon_export.py +1 -5
  44. code2llm-0.5.114/code2llm/cli_exports/orchestrator.py +0 -156
  45. code2llm-0.5.114/code2llm/core/gitignore.py +0 -139
  46. code2llm-0.5.114/code2llm/core/lang/__init__.py +0 -11
  47. code2llm-0.5.114/code2llm/exporters/base.py +0 -13
  48. code2llm-0.5.114/code2llm/exporters/json_exporter.py +0 -17
  49. {code2llm-0.5.114 → code2llm-0.5.116}/LICENSE +0 -0
  50. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/__main__.py +0 -0
  51. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/__init__.py +0 -0
  52. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/coupling.py +0 -0
  53. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/data_analysis.py +0 -0
  54. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/pipeline_detector.py +0 -0
  55. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/side_effects.py +0 -0
  56. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/smells.py +0 -0
  57. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/type_inference.py +0 -0
  58. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/api.py +0 -0
  59. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli.py +0 -0
  60. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/__init__.py +0 -0
  61. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/code2logic.py +0 -0
  62. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/prompt.py +0 -0
  63. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/__init__.py +0 -0
  64. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/ast_registry.py +0 -0
  65. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/export_pipeline.py +0 -0
  66. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/file_analyzer.py +0 -0
  67. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/file_cache.py +0 -0
  68. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/incremental.py +0 -0
  69. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/base.py +0 -0
  70. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/cpp.py +0 -0
  71. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/csharp.py +0 -0
  72. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/generic.py +0 -0
  73. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/go_lang.py +0 -0
  74. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/java.py +0 -0
  75. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/php.py +0 -0
  76. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/rust.py +0 -0
  77. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/ts_extractors.py +0 -0
  78. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/ts_parser.py +0 -0
  79. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/typescript.py +0 -0
  80. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/large_repo.py +0 -0
  81. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/models.py +0 -0
  82. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/refactoring.py +0 -0
  83. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/__init__.py +0 -0
  84. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/cache.py +0 -0
  85. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/incremental.py +0 -0
  86. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/prioritizer.py +0 -0
  87. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/scanner.py +0 -0
  88. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/strategies.py +0 -0
  89. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming_analyzer.py +0 -0
  90. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/toon_size_manager.py +0 -0
  91. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/article_view.py +0 -0
  92. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/context_view.py +0 -0
  93. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/flow_constants.py +0 -0
  94. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/flow_renderer.py +0 -0
  95. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/html_dashboard.py +0 -0
  96. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/index_generator.py +0 -0
  97. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/llm_exporter.py +0 -0
  98. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  99. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/__init__.py +0 -0
  100. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/constants.py +0 -0
  101. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/evolution.py +0 -0
  102. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/health.py +0 -0
  103. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/modules.py +0 -0
  104. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml_exporter.py +0 -0
  105. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/report_generators.py +0 -0
  106. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/metrics.py +0 -0
  107. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/module_detail.py +0 -0
  108. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/renderer.py +0 -0
  109. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon.py +0 -0
  110. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon_view.py +0 -0
  111. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/validate_project.py +0 -0
  112. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/__init__.py +0 -0
  113. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/llm_flow.py +0 -0
  114. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/llm_task.py +0 -0
  115. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/config.py +0 -0
  116. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/entity_resolution.py +0 -0
  117. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/intent_matching.py +0 -0
  118. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/normalization.py +0 -0
  119. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/pipeline.py +0 -0
  120. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/patterns/__init__.py +0 -0
  121. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/patterns/detector.py +0 -0
  122. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/refactor/__init__.py +0 -0
  123. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/refactor/prompt_engine.py +0 -0
  124. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/dependency_links.txt +0 -0
  125. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/entry_points.txt +0 -0
  126. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/requires.txt +0 -0
  127. {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/top_level.txt +0 -0
  128. {code2llm-0.5.114 → code2llm-0.5.116}/setup.cfg +0 -0
  129. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_advanced_analysis.py +0 -0
  130. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_analyzer.py +0 -0
  131. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_calls_toon_export.py +0 -0
  132. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_deep_analysis.py +0 -0
  133. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_edge_cases.py +0 -0
  134. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_flow_exporter.py +0 -0
  135. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_format_quality.py +0 -0
  136. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_multilanguage_e2e.py +0 -0
  137. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_nlp_pipeline.py +0 -0
  138. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_nonpython_cc_calls.py +0 -0
  139. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_pipeline_detector.py +0 -0
  140. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_prompt_engine.py +0 -0
  141. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_prompt_txt.py +0 -0
  142. {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_refactoring_engine.py +0 -0
  143. {code2llm-0.5.114 → code2llm-0.5.116}/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.114
3
+ Version: 0.5.116
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,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.114-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-55.0h-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.116-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-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
72
72
 
73
- - 🤖 **LLM usage:** $7.5000 (162 commits)
74
- - 👤 **Human dev:** ~$5505 (55.0h @ $100/h, 30min dedup)
73
+ - 🤖 **LLM usage:** $7.5000 (166 commits)
74
+ - 👤 **Human dev:** ~$5731 (57.3h @ $100/h, 30min dedup)
75
75
 
76
- Generated on 2026-04-18 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
76
+ Generated on 2026-04-19 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
77
77
 
78
78
  ---
79
79
 
@@ -89,7 +89,7 @@ When you run `code2llm ./ -f all`, the following files are created:
89
89
 
90
90
  | File | Format | Purpose | Key Insights |
91
91
  |------|--------|---------|--------------|
92
- | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Health, LAYERS, COUPLING | 69 critical functions, 0 god modules |
92
+ | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
93
93
 
94
94
  ### 🤖 LLM-Ready Documentation
95
95
 
@@ -97,6 +97,11 @@ When you run `code2llm ./ -f all`, the following files are created:
97
97
  |------|--------|---------|----------|
98
98
  | `context.md` | **Markdown** | **📖 LLM narrative** - Architecture summary | Paste into ChatGPT/Claude for code analysis |
99
99
 
100
+ ### 📊 Visualizations
101
+
102
+ | File | Format | Purpose | Description |
103
+ |------|--------|---------|-------------|
104
+ | `calls.mmd` | **Mermaid** | **📞 Call graph** | Function dependencies (edges only) |
100
105
 
101
106
  ## 🚀 Quick Start Commands
102
107
 
@@ -401,10 +406,10 @@ code2llm ./ -f yaml --separate-orphans
401
406
  ---
402
407
 
403
408
  **Generated by**: `code2llm ./ -f all --readme`
404
- **Analysis Date**: 2026-04-18
405
- **Total Functions**: 1041
406
- **Total Classes**: 111
407
- **Modules**: 135
409
+ **Analysis Date**: 2026-04-19
410
+ **Total Functions**: 1115
411
+ **Total Classes**: 121
412
+ **Modules**: 152
408
413
 
409
414
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
410
415
 
@@ -3,13 +3,13 @@
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.114-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-55.0h-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.116-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-57.3h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $7.5000 (162 commits)
10
- - 👤 **Human dev:** ~$5505 (55.0h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $7.5000 (166 commits)
10
+ - 👤 **Human dev:** ~$5731 (57.3h @ $100/h, 30min dedup)
11
11
 
12
- Generated on 2026-04-18 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
+ Generated on 2026-04-19 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
@@ -25,7 +25,7 @@ When you run `code2llm ./ -f all`, the following files are created:
25
25
 
26
26
  | File | Format | Purpose | Key Insights |
27
27
  |------|--------|---------|--------------|
28
- | `analysis.toon` | **TOON** | **🔥 Health diagnostics** - Health, LAYERS, COUPLING | 69 critical functions, 0 god modules |
28
+ | `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
29
29
 
30
30
  ### 🤖 LLM-Ready Documentation
31
31
 
@@ -33,6 +33,11 @@ When you run `code2llm ./ -f all`, the following files are created:
33
33
  |------|--------|---------|----------|
34
34
  | `context.md` | **Markdown** | **📖 LLM narrative** - Architecture summary | Paste into ChatGPT/Claude for code analysis |
35
35
 
36
+ ### 📊 Visualizations
37
+
38
+ | File | Format | Purpose | Description |
39
+ |------|--------|---------|-------------|
40
+ | `calls.mmd` | **Mermaid** | **📞 Call graph** | Function dependencies (edges only) |
36
41
 
37
42
  ## 🚀 Quick Start Commands
38
43
 
@@ -337,10 +342,10 @@ code2llm ./ -f yaml --separate-orphans
337
342
  ---
338
343
 
339
344
  **Generated by**: `code2llm ./ -f all --readme`
340
- **Analysis Date**: 2026-04-18
341
- **Total Functions**: 1041
342
- **Total Classes**: 111
343
- **Modules**: 135
345
+ **Analysis Date**: 2026-04-19
346
+ **Total Functions**: 1115
347
+ **Total Classes**: 121
348
+ **Modules**: 152
344
349
 
345
350
  For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
346
351
 
@@ -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.114"
11
+ __version__ = "0.5.116"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -5,6 +5,7 @@ from typing import Optional, Set, List, Dict
5
5
 
6
6
  from code2llm.core.config import Config
7
7
  from code2llm.core.models import AnalysisResult, FlowEdge
8
+ from code2llm.analysis.utils import ast_unparse
8
9
 
9
10
 
10
11
  class CallGraphExtractor(ast.NodeVisitor):
@@ -202,10 +203,4 @@ class CallGraphExtractor(ast.NodeVisitor):
202
203
  return None
203
204
 
204
205
  def _expr_to_str(self, node: ast.AST) -> str:
205
- """Convert AST expression to string."""
206
- if node is None:
207
- return ""
208
- try:
209
- return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
210
- except:
211
- return str(node)
206
+ return ast_unparse(node, default_none="")
@@ -6,6 +6,7 @@ from typing import Optional
6
6
 
7
7
  from code2llm.core.config import Config
8
8
  from code2llm.core.models import AnalysisResult, FlowNode, FlowEdge, FunctionInfo
9
+ from code2llm.analysis.utils import ast_unparse
9
10
 
10
11
 
11
12
  class CFGExtractor(ast.NodeVisitor):
@@ -275,13 +276,7 @@ class CFGExtractor(ast.NodeVisitor):
275
276
  return str(node)[:50]
276
277
 
277
278
  def _expr_to_str(self, node: ast.AST) -> str:
278
- """Convert AST expression to string."""
279
- if node is None:
280
- return "None"
281
- try:
282
- return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
283
- except:
284
- return str(node)
279
+ return ast_unparse(node)
285
280
 
286
281
  def _format_except(self, handler: ast.ExceptHandler) -> str:
287
282
  """Format except handler."""
@@ -6,6 +6,7 @@ from typing import Set, Dict, List
6
6
 
7
7
  from code2llm.core.config import Config
8
8
  from code2llm.core.models import AnalysisResult, FlowEdge, DataFlow, Mutation
9
+ from code2llm.analysis.utils import ast_unparse
9
10
 
10
11
 
11
12
  class DFGExtractor(ast.NodeVisitor):
@@ -207,13 +208,7 @@ class DFGExtractor(ast.NodeVisitor):
207
208
  return names
208
209
 
209
210
  def _expr_to_str(self, node: ast.AST) -> str:
210
- """Convert AST expression to string."""
211
- if node is None:
212
- return "None"
213
- try:
214
- return ast.unparse(node) if hasattr(ast, 'unparse') else str(node)
215
- except:
216
- return str(node)
211
+ return ast_unparse(node)
217
212
 
218
213
  def _build_data_flow_edges(self):
219
214
  """Build DFG edges from data flow records."""
@@ -1,5 +1,5 @@
1
1
  """Shared AST utilities for analysis modules."""
2
2
 
3
- from .ast_helpers import get_ast, find_function_node, expr_to_str
3
+ from .ast_helpers import get_ast, find_function_node, expr_to_str, ast_unparse
4
4
 
5
- __all__ = ["get_ast", "find_function_node", "expr_to_str"]
5
+ __all__ = ["get_ast", "find_function_node", "expr_to_str", "ast_unparse"]
@@ -38,6 +38,25 @@ def find_function_node(
38
38
  return None
39
39
 
40
40
 
41
+ def ast_unparse(node: Optional[ast.AST], default_none: str = "None") -> str:
42
+ """Convert an AST node to its source string via ast.unparse (Python 3.9+).
43
+
44
+ Used as a shared replacement for the duplicated *_expr_to_str* methods
45
+ in ``cfg.py``, ``dfg.py``, and ``call_graph.py``.
46
+
47
+ Args:
48
+ node: AST node to convert, or None.
49
+ default_none: value returned when *node* is None (``"None"`` for most
50
+ callers; ``""`` for call_graph which uses empty-string sentinel).
51
+ """
52
+ if node is None:
53
+ return default_none
54
+ try:
55
+ return ast.unparse(node) if hasattr(ast, "unparse") else str(node)
56
+ except Exception:
57
+ return str(node)
58
+
59
+
41
60
  def expr_to_str(node: ast.expr) -> Optional[str]:
42
61
  """Convert an AST expression to a dotted string (for call-name extraction).
43
62
 
@@ -73,7 +73,7 @@ def _build_config(args, output_dir: Path):
73
73
  if hasattr(args, 'no_gitignore') and args.no_gitignore:
74
74
  filter_config.gitignore_enabled = False
75
75
 
76
- return Config(
76
+ config = Config(
77
77
  mode=args.mode,
78
78
  max_depth_enumeration=args.max_depth,
79
79
  detect_state_machines=not args.no_patterns,
@@ -81,6 +81,10 @@ def _build_config(args, output_dir: Path):
81
81
  output_dir=str(output_dir),
82
82
  filters=filter_config
83
83
  )
84
+ # Persistent cache flags (read via getattr with defaults in analyzer.py)
85
+ no_cache = getattr(args, 'no_cache', False) or getattr(args, 'force', False)
86
+ config.no_cache = no_cache
87
+ return config
84
88
 
85
89
 
86
90
  def _print_analysis_summary(result) -> None:
@@ -9,7 +9,7 @@ from .cli_exports import _run_report
9
9
 
10
10
 
11
11
  def handle_special_commands() -> Optional[int]:
12
- """Handle special sub-commands (llm-flow, llm-context, report)."""
12
+ """Handle special sub-commands (llm-flow, llm-context, report, cache)."""
13
13
  if len(sys.argv) > 1 and sys.argv[1] == 'llm-flow':
14
14
  from .generators.llm_flow import main as llm_flow_main
15
15
  return llm_flow_main(sys.argv[2:])
@@ -17,9 +17,75 @@ def handle_special_commands() -> Optional[int]:
17
17
  return generate_llm_context(sys.argv[2:])
18
18
  if len(sys.argv) > 1 and sys.argv[1] == 'report':
19
19
  return handle_report_command(sys.argv[2:])
20
+ if len(sys.argv) > 1 and sys.argv[1] == 'cache':
21
+ return handle_cache_command(sys.argv[2:])
20
22
  return None
21
23
 
22
24
 
25
+ def handle_cache_command(args_list) -> int:
26
+ """Manage persistent cache (~/.code2llm/).
27
+
28
+ Usage:
29
+ code2llm cache status # show size, projects, last used
30
+ code2llm cache clear # clear cache for current directory
31
+ code2llm cache clear --all # clear entire ~/.code2llm/
32
+ code2llm cache gc # manual garbage collection
33
+ """
34
+ import os
35
+ import time
36
+ from .core.persistent_cache import PersistentCache, get_all_projects, clear_all, _DEFAULT_ROOT
37
+
38
+ parser = argparse.ArgumentParser(prog='code2llm cache')
39
+ parser.add_argument('action', choices=['status', 'clear', 'gc'], help='Cache action')
40
+ parser.add_argument('--all', action='store_true', dest='all_projects',
41
+ help='Apply to all cached projects (clear only)')
42
+ parser.add_argument('--max-age', type=int, default=30, metavar='DAYS',
43
+ help='Max age in days for gc (default: 30)')
44
+ args = parser.parse_args(args_list)
45
+
46
+ if args.action == 'status':
47
+ projects = get_all_projects()
48
+ root = _DEFAULT_ROOT
49
+ total_mb = sum(p.get('cache_size_bytes', 0) for p in projects) / (1024 * 1024)
50
+ print(f"Cache: {root}")
51
+ print(f" Projects: {len(projects)} Total: {total_mb:.1f} MB")
52
+ for p in projects:
53
+ size_mb = p.get('cache_size_bytes', 0) / (1024 * 1024)
54
+ updated = p.get('updated_at', 0)
55
+ age_min = int((time.time() - updated) / 60) if updated else 0
56
+ age_str = f"{age_min}m ago" if age_min < 120 else f"{age_min//60}h ago"
57
+ exports = p.get('exports', 0)
58
+ files = p.get('files_cached', 0)
59
+ print(f"\n {p.get('project', '?')}")
60
+ print(f" Files: {files} Exports: {exports} Size: {size_mb:.1f} MB Last: {age_str}")
61
+ return 0
62
+
63
+ if args.action == 'clear':
64
+ if args.all_projects:
65
+ clear_all()
66
+ print("Cleared entire cache.")
67
+ else:
68
+ project_dir = os.path.realpath('.')
69
+ c = PersistentCache(project_dir)
70
+ c.clear()
71
+ print(f"Cleared cache for {project_dir}")
72
+ return 0
73
+
74
+ if args.action == 'gc':
75
+ projects = get_all_projects()
76
+ total_removed = 0
77
+ for p in projects:
78
+ project_dir = p.get('project')
79
+ if project_dir and Path(project_dir).exists():
80
+ c = PersistentCache(project_dir)
81
+ removed = c.gc(max_age_days=args.max_age)
82
+ total_removed += removed
83
+ print(f"GC complete: {total_removed} stale entries removed.")
84
+ return 0
85
+
86
+ return 0
87
+
88
+
23
89
  def handle_report_command(args_list) -> int:
24
90
  """Generate views from an existing project.yaml (legacy).
25
91
 
@@ -215,16 +215,27 @@ def _export_mermaid_pngs(args, output_dir: Path) -> None:
215
215
  print(f" - PNG: Skipped (install with: make install-mermaid)")
216
216
 
217
217
 
218
- def _export_calls(args, result, output_dir: Path):
219
- """Export standalone calls.toon.yaml (call graph in toon format).
220
-
221
- Generates calls.toon.yaml in human-readable toon format with hubs, modules,
222
- and edges sections — useful for programmatic analysis of call graphs.
223
- """
218
+ def _export_calls_format(args, result, output_dir: Path, toon: bool = False) -> None:
219
+ """Shared helper: export call graph in YAML or toon format."""
224
220
  yaml_exporter = YAMLExporter()
225
- yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
226
- if args.verbose:
227
- print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
221
+ if toon:
222
+ yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
223
+ if args.verbose:
224
+ print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
225
+ else:
226
+ yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
227
+ if args.verbose:
228
+ print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
229
+
230
+
231
+ def _export_calls(args, result, output_dir: Path):
232
+ """Export standalone calls.yaml (structured call graph YAML)."""
233
+ _export_calls_format(args, result, output_dir, toon=False)
234
+
235
+
236
+ def _export_calls_toon(args, result, output_dir: Path):
237
+ """Export calls.toon.yaml (call graph in human-readable toon format)."""
238
+ _export_calls_format(args, result, output_dir, toon=True)
228
239
 
229
240
 
230
241
  def _export_mermaid(args, result, output_dir: Path):
@@ -247,8 +258,9 @@ def _export_mermaid(args, result, output_dir: Path):
247
258
  exporter.export_call_graph(result, str(output_dir / 'calls.mmd'))
248
259
  exporter.export_compact(result, str(output_dir / 'compact_flow.mmd'))
249
260
 
250
- # Export calls.toon.yaml (structured call graph data in toon format)
261
+ # Export calls.yaml (structured call graph data) and calls.toon.yaml (human-readable)
251
262
  yaml_exporter = YAMLExporter()
263
+ yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
252
264
  yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
253
265
 
254
266
  if args.verbose:
@@ -257,7 +269,7 @@ def _export_mermaid(args, result, output_dir: Path):
257
269
  files.append('flow_detailed.mmd')
258
270
  if getattr(args, 'flow_full', False):
259
271
  files.append('flow_full.mmd')
260
- files.extend(['calls.mmd', 'compact_flow.mmd', 'calls.toon.yaml'])
272
+ files.extend(['calls.mmd', 'compact_flow.mmd', 'calls.yaml'])
261
273
  print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)})")
262
274
 
263
275
  _export_mermaid_pngs(args, output_dir)