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.
- {code2llm-0.5.114 → code2llm-0.5.116}/PKG-INFO +16 -11
- {code2llm-0.5.114 → code2llm-0.5.116}/README.md +15 -10
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/__init__.py +1 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/call_graph.py +2 -7
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/cfg.py +2 -7
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/dfg.py +2 -7
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/utils/__init__.py +2 -2
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/utils/ast_helpers.py +19 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_analysis.py +5 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_commands.py +67 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/formats.py +23 -11
- code2llm-0.5.116/code2llm/cli_exports/orchestrator.py +337 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_parser.py +21 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/analyzer.py +147 -69
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/config.py +2 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/file_filter.py +48 -25
- code2llm-0.5.116/code2llm/core/gitignore.py +138 -0
- code2llm-0.5.116/code2llm/core/lang/__init__.py +171 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/ruby.py +22 -1
- code2llm-0.5.116/code2llm/core/persistent_cache.py +322 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/repo_files.py +2 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/__init__.py +29 -2
- code2llm-0.5.116/code2llm/exporters/base.py +158 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/context_exporter.py +9 -7
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/evolution_exporter.py +7 -5
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/flow_exporter.py +7 -5
- code2llm-0.5.116/code2llm/exporters/json_exporter.py +27 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/map_exporter.py +8 -5
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/mermaid_exporter.py +55 -43
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/core.py +6 -4
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/hotspots.py +1 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/readme_exporter.py +11 -8
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/__init__.py +8 -5
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/helpers.py +9 -4
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/yaml_exporter.py +17 -8
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/mermaid.py +23 -24
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/PKG-INFO +16 -11
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/SOURCES.txt +2 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/pyproject.toml +1 -1
- {code2llm-0.5.114 → code2llm-0.5.116}/setup.py +1 -1
- code2llm-0.5.116/tests/test_persistent_cache.py +182 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_project_toon_export.py +1 -5
- code2llm-0.5.114/code2llm/cli_exports/orchestrator.py +0 -156
- code2llm-0.5.114/code2llm/core/gitignore.py +0 -139
- code2llm-0.5.114/code2llm/core/lang/__init__.py +0 -11
- code2llm-0.5.114/code2llm/exporters/base.py +0 -13
- code2llm-0.5.114/code2llm/exporters/json_exporter.py +0 -17
- {code2llm-0.5.114 → code2llm-0.5.116}/LICENSE +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/__main__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/api.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/file_analyzer.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/models.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/setup.cfg +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.114 → code2llm-0.5.116}/tests/test_refactoring_engine.py +0 -0
- {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.
|
|
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
|
-
     
|
|
71
|
+
  
|
|
72
72
|
|
|
73
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
74
|
-
- 👤 **Human dev:** ~$
|
|
73
|
+
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
74
|
+
- 👤 **Human dev:** ~$5731 (57.3h @ $100/h, 30min dedup)
|
|
75
75
|
|
|
76
|
-
Generated on 2026-04-
|
|
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
|
-
| `
|
|
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-
|
|
405
|
-
**Total Functions**:
|
|
406
|
-
**Total Classes**:
|
|
407
|
-
**Modules**:
|
|
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
|
-
     
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $7.5000 (166 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$5731 (57.3h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
|
-
Generated on 2026-04-
|
|
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
|
-
| `
|
|
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-
|
|
341
|
-
**Total Functions**:
|
|
342
|
-
**Total Classes**:
|
|
343
|
-
**Modules**:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
219
|
-
"""
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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.
|
|
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.
|
|
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)
|