code2llm 0.5.104__tar.gz → 0.5.105__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 (128) hide show
  1. {code2llm-0.5.104 → code2llm-0.5.105}/PKG-INFO +9 -6
  2. {code2llm-0.5.104 → code2llm-0.5.105}/README.md +8 -5
  3. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/__init__.py +1 -1
  4. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_exports/formats.py +17 -1
  5. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_exports/orchestrator.py +4 -0
  6. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_parser.py +1 -1
  7. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/yaml_exporter.py +96 -1
  8. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/nlp/__init__.py +1 -1
  9. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm.egg-info/PKG-INFO +9 -6
  10. {code2llm-0.5.104 → code2llm-0.5.105}/pyproject.toml +1 -1
  11. {code2llm-0.5.104 → code2llm-0.5.105}/setup.py +1 -1
  12. {code2llm-0.5.104 → code2llm-0.5.105}/LICENSE +0 -0
  13. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/__main__.py +0 -0
  14. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/__init__.py +0 -0
  15. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/call_graph.py +0 -0
  16. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/cfg.py +0 -0
  17. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/coupling.py +0 -0
  18. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/data_analysis.py +0 -0
  19. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/dfg.py +0 -0
  20. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/pipeline_detector.py +0 -0
  21. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/side_effects.py +0 -0
  22. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/smells.py +0 -0
  23. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/type_inference.py +0 -0
  24. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/utils/__init__.py +0 -0
  25. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/analysis/utils/ast_helpers.py +0 -0
  26. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/api.py +0 -0
  27. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli.py +0 -0
  28. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_analysis.py +0 -0
  29. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_commands.py +0 -0
  30. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_exports/__init__.py +0 -0
  31. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_exports/code2logic.py +0 -0
  32. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/cli_exports/prompt.py +0 -0
  33. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/__init__.py +0 -0
  34. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/analyzer.py +0 -0
  35. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/ast_registry.py +0 -0
  36. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/config.py +0 -0
  37. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/export_pipeline.py +0 -0
  38. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/file_analyzer.py +0 -0
  39. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/file_cache.py +0 -0
  40. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/file_filter.py +0 -0
  41. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/gitignore.py +0 -0
  42. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/incremental.py +0 -0
  43. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/__init__.py +0 -0
  44. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/base.py +0 -0
  45. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/cpp.py +0 -0
  46. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/csharp.py +0 -0
  47. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/generic.py +0 -0
  48. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/go_lang.py +0 -0
  49. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/java.py +0 -0
  50. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/php.py +0 -0
  51. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/ruby.py +0 -0
  52. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/rust.py +0 -0
  53. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/ts_extractors.py +0 -0
  54. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/ts_parser.py +0 -0
  55. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/lang/typescript.py +0 -0
  56. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/large_repo.py +0 -0
  57. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/models.py +0 -0
  58. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/refactoring.py +0 -0
  59. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/repo_files.py +0 -0
  60. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming/__init__.py +0 -0
  61. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming/cache.py +0 -0
  62. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming/incremental.py +0 -0
  63. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming/prioritizer.py +0 -0
  64. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming/scanner.py +0 -0
  65. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming/strategies.py +0 -0
  66. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/streaming_analyzer.py +0 -0
  67. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/core/toon_size_manager.py +0 -0
  68. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/__init__.py +0 -0
  69. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/article_view.py +0 -0
  70. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/base.py +0 -0
  71. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/context_exporter.py +0 -0
  72. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/context_view.py +0 -0
  73. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/evolution_exporter.py +0 -0
  74. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/flow_constants.py +0 -0
  75. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/flow_exporter.py +0 -0
  76. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/flow_renderer.py +0 -0
  77. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/html_dashboard.py +0 -0
  78. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/index_generator.py +0 -0
  79. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/json_exporter.py +0 -0
  80. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/llm_exporter.py +0 -0
  81. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/map_exporter.py +0 -0
  82. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/mermaid_exporter.py +0 -0
  83. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
  84. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/project_yaml_exporter.py +0 -0
  85. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/readme_exporter.py +0 -0
  86. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/report_generators.py +0 -0
  87. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon/__init__.py +0 -0
  88. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon/helpers.py +0 -0
  89. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon/metrics.py +0 -0
  90. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon/module_detail.py +0 -0
  91. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon/renderer.py +0 -0
  92. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon.py +0 -0
  93. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/toon_view.py +0 -0
  94. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/exporters/validate_project.py +0 -0
  95. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/generators/__init__.py +0 -0
  96. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/generators/llm_flow.py +0 -0
  97. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/generators/llm_task.py +0 -0
  98. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/generators/mermaid.py +0 -0
  99. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/nlp/config.py +0 -0
  100. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/nlp/entity_resolution.py +0 -0
  101. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/nlp/intent_matching.py +0 -0
  102. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/nlp/normalization.py +0 -0
  103. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/nlp/pipeline.py +0 -0
  104. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/patterns/__init__.py +0 -0
  105. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/patterns/detector.py +0 -0
  106. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/refactor/__init__.py +0 -0
  107. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm/refactor/prompt_engine.py +0 -0
  108. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm.egg-info/SOURCES.txt +0 -0
  109. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm.egg-info/dependency_links.txt +0 -0
  110. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm.egg-info/entry_points.txt +0 -0
  111. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm.egg-info/requires.txt +0 -0
  112. {code2llm-0.5.104 → code2llm-0.5.105}/code2llm.egg-info/top_level.txt +0 -0
  113. {code2llm-0.5.104 → code2llm-0.5.105}/setup.cfg +0 -0
  114. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_advanced_analysis.py +0 -0
  115. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_analyzer.py +0 -0
  116. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_deep_analysis.py +0 -0
  117. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_edge_cases.py +0 -0
  118. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_flow_exporter.py +0 -0
  119. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_format_quality.py +0 -0
  120. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_multilanguage_e2e.py +0 -0
  121. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_nlp_pipeline.py +0 -0
  122. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_nonpython_cc_calls.py +0 -0
  123. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_pipeline_detector.py +0 -0
  124. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_project_toon_export.py +0 -0
  125. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_prompt_engine.py +0 -0
  126. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_prompt_txt.py +0 -0
  127. {code2llm-0.5.104 → code2llm-0.5.105}/tests/test_refactoring_engine.py +0 -0
  128. {code2llm-0.5.104 → code2llm-0.5.105}/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.104
3
+ Version: 0.5.105
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
@@ -63,13 +63,16 @@ Dynamic: requires-python
63
63
 
64
64
  # code2llm - Generated Analysis Files
65
65
 
66
+
66
67
  ## AI Cost Tracking
67
68
 
68
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-yellow) ![AI Model](https://img.shields.io/badge/AI%20Model-openrouter%2Fqwen%2Fqwen3-coder-next-lightgrey)
69
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.105-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-53.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
69
71
 
70
- This project uses AI-generated code. Total cost: **$7.5000** with **153** AI commits.
72
+ - 🤖 **LLM usage:** $7.5000 (154 commits)
73
+ - 👤 **Human dev:** ~$5373 (53.7h @ $100/h, 30min dedup)
71
74
 
72
- Generated on 2026-04-09 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/models/openrouter/qwen/qwen3-coder-next)
75
+ Generated on 2026-04-18 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
73
76
 
74
77
  ---
75
78
 
@@ -397,8 +400,8 @@ code2llm ./ -f yaml --separate-orphans
397
400
  ---
398
401
 
399
402
  **Generated by**: `code2llm ./ -f all --readme`
400
- **Analysis Date**: 2026-04-09
401
- **Total Functions**: 1011
403
+ **Analysis Date**: 2026-04-18
404
+ **Total Functions**: 1014
402
405
  **Total Classes**: 111
403
406
  **Modules**: 131
404
407
 
@@ -1,12 +1,15 @@
1
1
  # code2llm - Generated Analysis Files
2
2
 
3
+
3
4
  ## AI Cost Tracking
4
5
 
5
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-yellow) ![AI Model](https://img.shields.io/badge/AI%20Model-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.105-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-53.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
8
 
7
- This project uses AI-generated code. Total cost: **$7.5000** with **153** AI commits.
9
+ - 🤖 **LLM usage:** $7.5000 (154 commits)
10
+ - 👤 **Human dev:** ~$5373 (53.7h @ $100/h, 30min dedup)
8
11
 
9
- Generated on 2026-04-09 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/models/openrouter/qwen/qwen3-coder-next)
12
+ Generated on 2026-04-18 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
10
13
 
11
14
  ---
12
15
 
@@ -334,8 +337,8 @@ code2llm ./ -f yaml --separate-orphans
334
337
  ---
335
338
 
336
339
  **Generated by**: `code2llm ./ -f all --readme`
337
- **Analysis Date**: 2026-04-09
338
- **Total Functions**: 1011
340
+ **Analysis Date**: 2026-04-18
341
+ **Total Functions**: 1014
339
342
  **Total Classes**: 111
340
343
  **Modules**: 131
341
344
 
@@ -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.104"
11
+ __version__ = "0.5.105"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -213,6 +213,18 @@ def _export_mermaid_pngs(args, output_dir: Path) -> None:
213
213
  print(f" - PNG: Skipped (install with: make install-mermaid)")
214
214
 
215
215
 
216
+ def _export_calls(args, result, output_dir: Path):
217
+ """Export standalone calls.yaml (call graph as structured YAML).
218
+
219
+ Generates calls.yaml without any Mermaid files — useful for programmatic
220
+ analysis of call graphs without visualization overhead.
221
+ """
222
+ yaml_exporter = YAMLExporter()
223
+ yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
224
+ if args.verbose:
225
+ print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
226
+
227
+
216
228
  def _export_mermaid(args, result, output_dir: Path):
217
229
  """Export Mermaid diagrams + optional PNG generation.
218
230
 
@@ -233,13 +245,17 @@ def _export_mermaid(args, result, output_dir: Path):
233
245
  exporter.export_call_graph(result, str(output_dir / 'calls.mmd'))
234
246
  exporter.export_compact(result, str(output_dir / 'compact_flow.mmd'))
235
247
 
248
+ # Export calls.yaml (structured call graph data)
249
+ yaml_exporter = YAMLExporter()
250
+ yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
251
+
236
252
  if args.verbose:
237
253
  files = ['flow.mmd']
238
254
  if getattr(args, 'flow_detail', False):
239
255
  files.append('flow_detailed.mmd')
240
256
  if getattr(args, 'flow_full', False):
241
257
  files.append('flow_full.mmd')
242
- files.extend(['calls.mmd', 'compact_flow.mmd'])
258
+ files.extend(['calls.mmd', 'compact_flow.mmd', 'calls.yaml'])
243
259
  print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)})")
244
260
 
245
261
  _export_mermaid_pngs(args, output_dir)
@@ -7,6 +7,7 @@ from typing import Optional
7
7
  from .formats import (
8
8
  _export_simple_formats,
9
9
  _export_mermaid,
10
+ _export_calls,
10
11
  _export_evolution,
11
12
  _export_data_structures,
12
13
  _export_context_fallback,
@@ -58,6 +59,9 @@ def _export_single_project(
58
59
  if 'mermaid' in formats:
59
60
  _export_mermaid(args, result, output_dir)
60
61
 
62
+ if 'calls' in formats:
63
+ _export_calls(args, result, output_dir)
64
+
61
65
  _export_evolution(args, result, output_dir)
62
66
  _export_data_structures(args, result, output_dir)
63
67
  _export_context_fallback(args, result, output_dir, formats)
@@ -75,7 +75,7 @@ Strategy Options (--strategy):
75
75
  parser.add_argument(
76
76
  '-f', '--format',
77
77
  default='toon',
78
- help='Output formats: toon,map,flow,context,code2logic,yaml,json,mermaid,evolution,png,project-yaml,all (default: toon)'
78
+ help='Output formats: toon,map,flow,context,code2logic,yaml,json,mermaid,evolution,calls,png,project-yaml,all (default: toon)'
79
79
  )
80
80
 
81
81
  parser.add_argument(
@@ -3,8 +3,9 @@
3
3
  import yaml
4
4
  from collections import defaultdict
5
5
  from pathlib import Path
6
+ from typing import Dict, List, Set, Tuple, Optional
6
7
  from .base import Exporter
7
- from code2llm.core.models import AnalysisResult
8
+ from code2llm.core.models import AnalysisResult, FunctionInfo
8
9
  from code2llm.analysis.data_analysis import DataAnalyzer
9
10
 
10
11
 
@@ -106,3 +107,97 @@ class YAMLExporter(Exporter):
106
107
  safe_name = mod_name.replace('.', '_') or 'root'
107
108
  with open(output_path / f'{safe_name}.yaml', 'w', encoding='utf-8') as f:
108
109
  yaml.dump(content, f, default_flow_style=False, allow_unicode=True)
110
+
111
+ def export_calls(self, result: AnalysisResult, output_path: str, max_calls_per_func: int = 10, max_edges: int = 500) -> None:
112
+ """Export call graph as structured YAML (calls.yaml).
113
+
114
+ Generates a structured representation of the call graph with:
115
+ - nodes: all functions that participate in calls (with metadata)
116
+ - edges: caller -> callee relationships
117
+ - modules: grouping of functions by module
118
+ - stats: summary statistics
119
+ """
120
+ # Collect connected nodes and edges
121
+ connected: Set[str] = set()
122
+ edges: List[Dict] = []
123
+ seen_pairs: Set[Tuple[str, str]] = set()
124
+
125
+ for func_name, fi in result.functions.items():
126
+ for callee in fi.calls[:max_calls_per_func]:
127
+ resolved = self._resolve_callee(callee, result.functions)
128
+ if resolved and resolved != func_name:
129
+ connected.add(func_name)
130
+ connected.add(resolved)
131
+ pair = (func_name, resolved)
132
+ if pair not in seen_pairs:
133
+ seen_pairs.add(pair)
134
+ edges.append({
135
+ 'caller': func_name,
136
+ 'callee': resolved,
137
+ 'call_type': 'direct' if callee == resolved.split('.')[-1] else 'resolved'
138
+ })
139
+ if len(edges) >= max_edges:
140
+ break
141
+ if len(edges) >= max_edges:
142
+ break
143
+
144
+ # Build nodes data
145
+ nodes: Dict[str, Dict] = {}
146
+ for fn in connected:
147
+ fi = result.functions.get(fn)
148
+ if fi:
149
+ cc = self._get_cc(fi)
150
+ nodes[fn] = {
151
+ 'name': fi.name,
152
+ 'module': fi.module,
153
+ 'line': fi.line,
154
+ 'cyclomatic_complexity': cc,
155
+ 'calls_out': len(fi.calls),
156
+ 'calls_in': sum(1 for f in result.functions.values() if fn in [self._resolve_callee(c, result.functions) for c in f.calls]),
157
+ }
158
+
159
+ # Group by module
160
+ modules: Dict[str, List[str]] = defaultdict(list)
161
+ for fn in connected:
162
+ fi = result.functions.get(fn)
163
+ if fi:
164
+ modules[fi.module].append(fn)
165
+
166
+ # Build output structure
167
+ calls_data = {
168
+ 'project': result.project_path,
169
+ 'generated_from': 'code2llm call graph analysis',
170
+ 'stats': {
171
+ 'total_nodes': len(nodes),
172
+ 'total_edges': len(edges),
173
+ 'modules_count': len(modules),
174
+ },
175
+ 'nodes': nodes,
176
+ 'edges': edges,
177
+ 'modules': {mod: sorted(funcs) for mod, funcs in sorted(modules.items())},
178
+ }
179
+
180
+ # Add entry points if available
181
+ if result.entry_points:
182
+ calls_data['entry_points'] = sorted(result.entry_points)
183
+
184
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
185
+ with open(output_path, 'w', encoding='utf-8') as f:
186
+ yaml.dump(calls_data, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
187
+
188
+ @staticmethod
189
+ def _resolve_callee(callee: str, funcs: Dict[str, FunctionInfo]) -> Optional[str]:
190
+ """Resolve callee to a known qualified name."""
191
+ if callee in funcs:
192
+ return callee
193
+ candidates = [qn for qn in funcs if qn.endswith(f".{callee}")]
194
+ if len(candidates) == 1:
195
+ return candidates[0]
196
+ return None
197
+
198
+ @staticmethod
199
+ def _get_cc(fi: FunctionInfo) -> int:
200
+ """Extract cyclomatic complexity from FunctionInfo."""
201
+ if isinstance(fi.complexity, dict):
202
+ return fi.complexity.get('cyclomatic_complexity', 0)
203
+ return fi.complexity or 0
@@ -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.104"
7
+ __version__ = "0.5.105"
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.104
3
+ Version: 0.5.105
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
@@ -63,13 +63,16 @@ Dynamic: requires-python
63
63
 
64
64
  # code2llm - Generated Analysis Files
65
65
 
66
+
66
67
  ## AI Cost Tracking
67
68
 
68
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-yellow) ![AI Model](https://img.shields.io/badge/AI%20Model-openrouter%2Fqwen%2Fqwen3-coder-next-lightgrey)
69
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.5.105-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
70
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-53.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
69
71
 
70
- This project uses AI-generated code. Total cost: **$7.5000** with **153** AI commits.
72
+ - 🤖 **LLM usage:** $7.5000 (154 commits)
73
+ - 👤 **Human dev:** ~$5373 (53.7h @ $100/h, 30min dedup)
71
74
 
72
- Generated on 2026-04-09 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/models/openrouter/qwen/qwen3-coder-next)
75
+ Generated on 2026-04-18 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
73
76
 
74
77
  ---
75
78
 
@@ -397,8 +400,8 @@ code2llm ./ -f yaml --separate-orphans
397
400
  ---
398
401
 
399
402
  **Generated by**: `code2llm ./ -f all --readme`
400
- **Analysis Date**: 2026-04-09
401
- **Total Functions**: 1011
403
+ **Analysis Date**: 2026-04-18
404
+ **Total Functions**: 1014
402
405
  **Total Classes**: 111
403
406
  **Modules**: 131
404
407
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.104"
7
+ version = "0.5.105"
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"
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
  import os
5
5
 
6
6
  # Read version
7
- version = "0.5.103"
7
+ version = "0.5.104"
8
8
 
9
9
  # Read long description
10
10
  def read_readme():
File without changes
File without changes
File without changes
File without changes