code2llm 0.5.59__tar.gz → 0.5.61__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 (120) hide show
  1. {code2llm-0.5.59 → code2llm-0.5.61}/PKG-INFO +1 -1
  2. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/__init__.py +1 -1
  3. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/index_generator.py +70 -1
  4. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/map_exporter.py +1 -0
  5. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon/__init__.py +6 -0
  6. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon/renderer.py +86 -0
  7. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/nlp/__init__.py +1 -1
  8. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm.egg-info/PKG-INFO +1 -1
  9. {code2llm-0.5.59 → code2llm-0.5.61}/pyproject.toml +1 -1
  10. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_format_quality.py +8 -2
  11. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_toon_v2.py +2 -2
  12. {code2llm-0.5.59 → code2llm-0.5.61}/LICENSE +0 -0
  13. {code2llm-0.5.59 → code2llm-0.5.61}/README.md +0 -0
  14. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/__main__.py +0 -0
  15. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/__init__.py +0 -0
  16. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/call_graph.py +0 -0
  17. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/cfg.py +0 -0
  18. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/coupling.py +0 -0
  19. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/data_analysis.py +0 -0
  20. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/dfg.py +0 -0
  21. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/pipeline_detector.py +0 -0
  22. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/side_effects.py +0 -0
  23. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/smells.py +0 -0
  24. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/analysis/type_inference.py +0 -0
  25. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/api.py +0 -0
  26. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli.py +0 -0
  27. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_analysis.py +0 -0
  28. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_commands.py +0 -0
  29. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_exports/__init__.py +0 -0
  30. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_exports/code2logic.py +0 -0
  31. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_exports/formats.py +0 -0
  32. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_exports/orchestrator.py +0 -0
  33. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_exports/prompt.py +0 -0
  34. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/cli_parser.py +0 -0
  35. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/__init__.py +0 -0
  36. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/analyzer.py +0 -0
  37. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/config.py +0 -0
  38. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/__init__.py +0 -0
  39. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/file_analyzer.py +0 -0
  40. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/file_cache.py +0 -0
  41. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/file_filter.py +0 -0
  42. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/__init__.py +0 -0
  43. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/base.py +0 -0
  44. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/cpp.py +0 -0
  45. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/csharp.py +0 -0
  46. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/generic.py +0 -0
  47. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/go_lang.py +0 -0
  48. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/java.py +0 -0
  49. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/php.py +0 -0
  50. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/ruby.py +0 -0
  51. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/rust.py +0 -0
  52. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/lang/typescript.py +0 -0
  53. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/core/refactoring.py +0 -0
  54. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/gitignore.py +0 -0
  55. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/large_repo.py +0 -0
  56. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/models.py +0 -0
  57. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/repo_files.py +0 -0
  58. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming/__init__.py +0 -0
  59. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming/cache.py +0 -0
  60. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming/incremental.py +0 -0
  61. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming/prioritizer.py +0 -0
  62. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming/scanner.py +0 -0
  63. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming/strategies.py +0 -0
  64. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/streaming_analyzer.py +0 -0
  65. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/core/toon_size_manager.py +0 -0
  66. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/__init__.py +0 -0
  67. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/article_view.py +0 -0
  68. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/base.py +0 -0
  69. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/context_exporter.py +0 -0
  70. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/context_view.py +0 -0
  71. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/evolution_exporter.py +0 -0
  72. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/flow_constants.py +0 -0
  73. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/flow_exporter.py +0 -0
  74. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/flow_renderer.py +0 -0
  75. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/html_dashboard.py +0 -0
  76. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/json_exporter.py +0 -0
  77. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/llm_exporter.py +0 -0
  78. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/mermaid_exporter.py +0 -0
  79. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/project_yaml_exporter.py +0 -0
  80. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/readme_exporter.py +0 -0
  81. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/report_generators.py +0 -0
  82. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon/helpers.py +0 -0
  83. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon/metrics.py +0 -0
  84. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon/module_detail.py +0 -0
  85. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon.py +0 -0
  86. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/toon_view.py +0 -0
  87. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/validate_project.py +0 -0
  88. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/exporters/yaml_exporter.py +0 -0
  89. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/generators/__init__.py +0 -0
  90. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/generators/llm_flow.py +0 -0
  91. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/generators/llm_task.py +0 -0
  92. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/generators/mermaid.py +0 -0
  93. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/nlp/config.py +0 -0
  94. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/nlp/entity_resolution.py +0 -0
  95. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/nlp/intent_matching.py +0 -0
  96. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/nlp/normalization.py +0 -0
  97. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/nlp/pipeline.py +0 -0
  98. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/patterns/__init__.py +0 -0
  99. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/patterns/detector.py +0 -0
  100. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/refactor/__init__.py +0 -0
  101. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm/refactor/prompt_engine.py +0 -0
  102. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm.egg-info/SOURCES.txt +0 -0
  103. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm.egg-info/dependency_links.txt +0 -0
  104. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm.egg-info/entry_points.txt +0 -0
  105. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm.egg-info/requires.txt +0 -0
  106. {code2llm-0.5.59 → code2llm-0.5.61}/code2llm.egg-info/top_level.txt +0 -0
  107. {code2llm-0.5.59 → code2llm-0.5.61}/setup.cfg +0 -0
  108. {code2llm-0.5.59 → code2llm-0.5.61}/setup.py +0 -0
  109. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_advanced_analysis.py +0 -0
  110. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_analyzer.py +0 -0
  111. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_deep_analysis.py +0 -0
  112. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_edge_cases.py +0 -0
  113. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_flow_exporter.py +0 -0
  114. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_multilanguage_e2e.py +0 -0
  115. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_nlp_pipeline.py +0 -0
  116. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_nonpython_cc_calls.py +0 -0
  117. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_pipeline_detector.py +0 -0
  118. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_prompt_engine.py +0 -0
  119. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_prompt_txt.py +0 -0
  120. {code2llm-0.5.59 → code2llm-0.5.61}/tests/test_refactoring_engine.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code2llm
3
- Version: 0.5.59
3
+ Version: 0.5.61
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
@@ -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.59"
11
+ __version__ = "0.5.61"
12
12
  __author__ = "STTS Project"
13
13
 
14
14
  # Core analysis components (lightweight, always needed)
@@ -28,6 +28,12 @@ class IndexHTMLGenerator:
28
28
  '.go': {'icon': '🐹', 'type': 'code', 'name': 'Go'},
29
29
  '.rs': {'icon': '🦀', 'type': 'code', 'name': 'Rust'},
30
30
  '.java': {'icon': '☕', 'type': 'code', 'name': 'Java'},
31
+ '.png': {'icon': '🖼️', 'type': 'image', 'name': 'Image'},
32
+ '.jpg': {'icon': '🖼️', 'type': 'image', 'name': 'Image'},
33
+ '.jpeg': {'icon': '🖼️', 'type': 'image', 'name': 'Image'},
34
+ '.gif': {'icon': '🖼️', 'type': 'image', 'name': 'Image'},
35
+ '.svg': {'icon': '🖼️', 'type': 'image', 'name': 'Image'},
36
+ '.webp': {'icon': '🖼️', 'type': 'image', 'name': 'Image'},
31
37
  }
32
38
 
33
39
  def __init__(self, output_dir: Path):
@@ -120,6 +126,11 @@ class IndexHTMLGenerator:
120
126
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
121
127
  <title>code2llm Analysis Results</title>
122
128
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
129
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
130
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
131
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
132
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js"></script>
133
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/yaml.min.js"></script>
123
134
  <style>
124
135
  :root {{
125
136
  --bg: #0f172a;
@@ -517,6 +528,36 @@ class IndexHTMLGenerator:
517
528
  margin: 1.5rem 0;
518
529
  }}
519
530
 
531
+ /* Mermaid diagram styles */
532
+ .mermaid-content {{
533
+ display: flex;
534
+ justify-content: center;
535
+ padding: 1rem;
536
+ background: var(--surface);
537
+ border-radius: 0.5rem;
538
+ border: 1px solid var(--border);
539
+ }}
540
+
541
+ .mermaid-content .mermaid {{
542
+ background: transparent;
543
+ }}
544
+
545
+ /* Image preview styles */
546
+ .image-preview {{
547
+ display: flex;
548
+ justify-content: center;
549
+ align-items: center;
550
+ height: 100%;
551
+ padding: 1rem;
552
+ background: var(--surface);
553
+ border-radius: 0.5rem;
554
+ border: 1px solid var(--border);
555
+ }}
556
+
557
+ .image-preview img {{
558
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
559
+ }}
560
+
520
561
  /* Mobile responsive */
521
562
  @media (max-width: 768px) {{
522
563
  .container {{
@@ -603,6 +644,9 @@ class IndexHTMLGenerator:
603
644
  </div>
604
645
 
605
646
  <script>
647
+ // Initialize mermaid
648
+ mermaid.initialize({{ startOnLoad: false, theme: 'dark' }});
649
+
606
650
  const files = {files_json};
607
651
  let currentFile = null;
608
652
 
@@ -673,13 +717,38 @@ class IndexHTMLGenerator:
673
717
  }} else if (file.type === 'html') {{
674
718
  // For HTML files, show in iframe for safety
675
719
  body.innerHTML = `<iframe src="${{file.rel_path}}" style="width:100%;height:100%;border:none;border-radius:0.5rem;"></iframe>`;
720
+ }} else if (file.type === 'image') {{
721
+ // For images, display the actual image
722
+ body.innerHTML = `<div class="image-preview"><img src="${{file.rel_path}}" alt="${{file.name}}" style="max-width:100%;max-height:100%;object-fit:contain;border-radius:0.5rem;"></div>`;
723
+ }} else if (file.type === 'mermaid') {{
724
+ // Render mermaid diagram
725
+ const diagramId = 'mermaid-diagram-' + Date.now();
726
+ body.innerHTML = `<div class="mermaid-content"><pre class="mermaid" id="${{diagramId}}">${{file.content}}</pre></div>`;
727
+ // Initialize mermaid on the new element
728
+ setTimeout(() => {{
729
+ mermaid.init(undefined, document.getElementById(diagramId));
730
+ }}, 0);
676
731
  }} else if (file.type === 'json') {{
677
732
  try {{
678
733
  const json = JSON.parse(file.content.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&'));
679
- body.innerHTML = `<pre>${{JSON.stringify(json, null, 2)}}</pre>`;
734
+ const formatted = JSON.stringify(json, null, 2);
735
+ body.innerHTML = `<pre><code class="language-json">${{formatted.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}}</code></pre>`;
736
+ hljs.highlightElement(body.querySelector('code'));
680
737
  }} catch {{
681
738
  body.innerHTML = `<pre>${{file.content}}</pre>`;
682
739
  }}
740
+ }} else if (file.type === 'yaml') {{
741
+ // YAML with syntax highlighting
742
+ body.innerHTML = `<pre><code class="language-yaml">${{file.content.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}}</code></pre>`;
743
+ hljs.highlightElement(body.querySelector('code'));
744
+ }} else if (file.type === 'toon') {{
745
+ // TOON with simple highlighting (use ini as closest match for key: value format)
746
+ body.innerHTML = `<pre><code class="language-ini">${{file.content.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}}</code></pre>`;
747
+ hljs.highlightElement(body.querySelector('code'));
748
+ }} else if (file.type === 'code') {{
749
+ // Code files with auto-highlighting
750
+ body.innerHTML = `<pre><code>${{file.content.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')}}</code></pre>`;
751
+ hljs.highlightElement(body.querySelector('code'));
683
752
  }} else {{
684
753
  body.innerHTML = `<pre>${{file.content}}</pre>`;
685
754
  }}
@@ -23,6 +23,7 @@ EXCLUDE_PATTERNS = {
23
23
  'venv', '.venv', 'env', '.env', 'publish-env', 'test-env',
24
24
  'site-packages', 'node_modules', '__pycache__', '.git',
25
25
  'dist', 'build', 'egg-info', '.tox', '.mypy_cache',
26
+ 'TODO', 'examples',
26
27
  }
27
28
 
28
29
 
@@ -38,9 +38,15 @@ class ToonExporter:
38
38
  sections.append("")
39
39
  sections.extend(self.renderer.render_health(ctx))
40
40
  sections.append("")
41
+ sections.extend(self.renderer.render_refactor(ctx))
42
+ sections.append("")
43
+ sections.extend(self.renderer.render_pipelines(ctx))
44
+ sections.append("")
41
45
  sections.extend(self.renderer.render_layers(ctx))
42
46
  sections.append("")
43
47
  sections.extend(self.renderer.render_coupling(ctx))
48
+ sections.append("")
49
+ sections.extend(self.renderer.render_external(ctx))
44
50
 
45
51
  Path(output_path).parent.mkdir(parents=True, exist_ok=True)
46
52
  with open(output_path, "w", encoding="utf-8") as f:
@@ -383,3 +383,89 @@ class ToonRenderer:
383
383
  )
384
384
 
385
385
  return lines
386
+
387
+ def render_pipelines(self, ctx: Dict[str, Any]) -> List[str]:
388
+ """Render PIPELINES section - data flow pipelines from entry points."""
389
+ result: AnalysisResult = ctx["result"]
390
+
391
+ # Find entry points and their downstream pipelines
392
+ pipelines = []
393
+ for func_name, func_info in result.functions.items():
394
+ # Entry points: functions with no callers but have calls
395
+ if not func_info.called_by and func_info.calls:
396
+ chain = self._trace_pipeline(func_name, result, depth=0)
397
+ if chain:
398
+ pipelines.append({
399
+ "entry": func_name.split(".")[-1],
400
+ "chain": chain,
401
+ "purity": self._calculate_purity(chain, result),
402
+ })
403
+
404
+ if not pipelines:
405
+ return ["PIPELINES[0]: none detected"]
406
+
407
+ lines = [f"PIPELINES[{len(pipelines)}]:"]
408
+ for i, pipe in enumerate(pipelines[:5], 1): # Max 5 pipelines
409
+ purity_pct = int(pipe["purity"] * 100)
410
+ chain_str = " → ".join(pipe["chain"][:4]) # Show first 4 steps
411
+ if len(pipe["chain"]) > 4:
412
+ chain_str += f" → ...({len(pipe['chain']) - 4} more)"
413
+ lines.append(f" [{i}] Src [{pipe['entry']}]: {chain_str}")
414
+ lines.append(f" PURITY: {purity_pct}% pure")
415
+
416
+ return lines
417
+
418
+ def _trace_pipeline(self, start_func: str, result: AnalysisResult, depth: int) -> List[str]:
419
+ """Trace a pipeline starting from an entry point."""
420
+ if depth > 10: # Prevent infinite recursion
421
+ return []
422
+
423
+ chain = []
424
+ current = start_func
425
+ visited = set()
426
+
427
+ while current and current not in visited and len(chain) < 20:
428
+ visited.add(current)
429
+ func_info = result.functions.get(current)
430
+ if not func_info:
431
+ break
432
+
433
+ chain.append(current.split(".")[-1])
434
+
435
+ # Follow the first call that's not a builtin
436
+ next_func = None
437
+ for callee in func_info.calls:
438
+ if callee in result.functions:
439
+ next_func = callee
440
+ break
441
+
442
+ current = next_func
443
+
444
+ return chain
445
+
446
+ def _calculate_purity(self, chain: List[str], result: AnalysisResult) -> float:
447
+ """Calculate purity ratio (functions without side effects)."""
448
+ if not chain:
449
+ return 0.0
450
+
451
+ pure_count = 0
452
+ for func_name in chain:
453
+ full_name = None
454
+ for qname, fi in result.functions.items():
455
+ if fi.name == func_name:
456
+ full_name = qname
457
+ break
458
+
459
+ if full_name:
460
+ func_info = result.functions.get(full_name)
461
+ if func_info and not getattr(func_info, 'has_side_effects', False):
462
+ pure_count += 1
463
+
464
+ return pure_count / len(chain)
465
+
466
+ def render_external(self, ctx: Dict[str, Any]) -> List[str]:
467
+ """Render EXTERNAL section - cross-references to other tools."""
468
+ lines = ["EXTERNAL:"]
469
+ lines.append(" validation: run `vallm batch .` → validation.toon")
470
+ lines.append(" duplication: run `redup scan .` → duplication.toon")
471
+ return lines
@@ -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.59"
7
+ __version__ = "0.5.61"
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.59
3
+ Version: 0.5.61
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "code2llm"
7
- version = "0.5.59"
7
+ version = "0.5.61"
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"
@@ -53,11 +53,15 @@ def ground_truth_project(tmp_path_factory) -> Path:
53
53
  return Result(errors=["empty"])
54
54
  if not isinstance(data, list):
55
55
  return Result(errors=["not list"])
56
+ if len(data) > config.max_items:
57
+ return Result(errors=["too many items"])
56
58
  filtered = []
57
59
  for item in data:
58
60
  if isinstance(item, dict):
59
61
  if "id" in item:
60
62
  if item.get("active", True):
63
+ if config.debug:
64
+ print(f"Adding active: {item['id']}")
61
65
  filtered.append(item)
62
66
  elif config.debug:
63
67
  filtered.append(item)
@@ -184,7 +188,7 @@ class TestAnalysisToon:
184
188
  assert "HEALTH[" in toon_content
185
189
 
186
190
  def test_has_refactor_section(self, toon_content):
187
- assert "REFACTOR[" not in toon_content
191
+ assert "REFACTOR[" in toon_content
188
192
 
189
193
  def test_has_coupling_section(self, toon_content):
190
194
  assert "COUPLING" in toon_content
@@ -336,7 +340,9 @@ class TestCrossFormat:
336
340
  assert "HEALTH[" in toon
337
341
  assert "LAYERS" in toon
338
342
  assert "COUPLING" in toon
339
- assert "REFACTOR[" not in toon
343
+ assert "REFACTOR[" in toon
344
+ assert "PIPELINES" in toon
345
+ assert "EXTERNAL" in toon
340
346
 
341
347
  def test_project_map_has_unique_structure_info(self, all_formats):
342
348
  """map.toon should have the structural import/signature map."""
@@ -105,13 +105,13 @@ class TestToonExporterV2:
105
105
  assert "HEALTH[" in content
106
106
 
107
107
  def test_refactor_section(self, sample_result, tmp_path):
108
- """REFACTOR section should no longer be present in analysis.toon."""
108
+ """REFACTOR section should be present in analysis.toon with actionable steps."""
109
109
  exporter = ToonExporter()
110
110
  output_file = tmp_path / "test.toon"
111
111
  exporter.export(sample_result, str(output_file))
112
112
 
113
113
  content = output_file.read_text()
114
- assert "REFACTOR[" not in content
114
+ assert "REFACTOR[" in content
115
115
 
116
116
  def test_layers_section(self, sample_result, tmp_path):
117
117
  """Test LAYERS section shows package structure."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes