code2llm 0.5.113__tar.gz → 0.5.115__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.113 → code2llm-0.5.115}/PKG-INFO +16 -11
- {code2llm-0.5.113 → code2llm-0.5.115}/README.md +15 -10
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/__init__.py +1 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/utils/__init__.py +2 -2
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/utils/ast_helpers.py +19 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_analysis.py +5 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_commands.py +67 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_exports/formats.py +21 -6
- code2llm-0.5.115/code2llm/cli_exports/orchestrator.py +337 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_parser.py +21 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/analyzer.py +147 -69
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/config.py +2 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/file_filter.py +48 -25
- code2llm-0.5.115/code2llm/core/gitignore.py +138 -0
- code2llm-0.5.115/code2llm/core/persistent_cache.py +322 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/repo_files.py +2 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/__init__.py +29 -2
- code2llm-0.5.115/code2llm/exporters/base.py +158 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/context_exporter.py +9 -7
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/evolution_exporter.py +7 -5
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/flow_exporter.py +7 -5
- code2llm-0.5.115/code2llm/exporters/json_exporter.py +27 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/map_exporter.py +8 -5
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/mermaid_exporter.py +55 -43
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/core.py +6 -4
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/hotspots.py +1 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/readme_exporter.py +11 -8
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon/__init__.py +8 -5
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon/helpers.py +9 -4
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/yaml_exporter.py +17 -8
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/generators/mermaid.py +23 -24
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm.egg-info/PKG-INFO +16 -11
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm.egg-info/SOURCES.txt +2 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/pyproject.toml +1 -1
- {code2llm-0.5.113 → code2llm-0.5.115}/setup.py +1 -1
- code2llm-0.5.115/tests/test_persistent_cache.py +182 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_project_toon_export.py +1 -5
- code2llm-0.5.113/code2llm/cli_exports/orchestrator.py +0 -156
- code2llm-0.5.113/code2llm/core/gitignore.py +0 -139
- code2llm-0.5.113/code2llm/exporters/base.py +0 -13
- code2llm-0.5.113/code2llm/exporters/json_exporter.py +0 -17
- {code2llm-0.5.113 → code2llm-0.5.115}/LICENSE +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/__main__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/call_graph.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/cfg.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/api.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/file_analyzer.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/ruby.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/models.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/setup.cfg +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.113 → code2llm-0.5.115}/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.115
|
|
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.115"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -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
|
|
|
@@ -216,10 +216,24 @@ def _export_mermaid_pngs(args, output_dir: Path) -> None:
|
|
|
216
216
|
|
|
217
217
|
|
|
218
218
|
def _export_calls(args, result, output_dir: Path):
|
|
219
|
-
"""Export standalone calls.
|
|
220
|
-
|
|
221
|
-
Generates calls.
|
|
222
|
-
|
|
219
|
+
"""Export standalone calls.yaml (structured call graph YAML).
|
|
220
|
+
|
|
221
|
+
Generates calls.yaml with structured call graph data:
|
|
222
|
+
- nodes: functions with metadata (CC, calls_in/out)
|
|
223
|
+
- edges: caller -> callee relationships
|
|
224
|
+
- modules: grouping by module
|
|
225
|
+
- stats: summary statistics
|
|
226
|
+
"""
|
|
227
|
+
yaml_exporter = YAMLExporter()
|
|
228
|
+
yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
|
|
229
|
+
if args.verbose:
|
|
230
|
+
print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _export_calls_toon(args, result, output_dir: Path):
|
|
234
|
+
"""Export calls.toon.yaml (call graph in human-readable toon format).
|
|
235
|
+
|
|
236
|
+
Generates calls.toon.yaml with hubs, modules, and edges sections.
|
|
223
237
|
"""
|
|
224
238
|
yaml_exporter = YAMLExporter()
|
|
225
239
|
yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
|
|
@@ -247,8 +261,9 @@ def _export_mermaid(args, result, output_dir: Path):
|
|
|
247
261
|
exporter.export_call_graph(result, str(output_dir / 'calls.mmd'))
|
|
248
262
|
exporter.export_compact(result, str(output_dir / 'compact_flow.mmd'))
|
|
249
263
|
|
|
250
|
-
# Export calls.
|
|
264
|
+
# Export calls.yaml (structured call graph data) and calls.toon.yaml (human-readable)
|
|
251
265
|
yaml_exporter = YAMLExporter()
|
|
266
|
+
yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
|
|
252
267
|
yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
|
|
253
268
|
|
|
254
269
|
if args.verbose:
|
|
@@ -257,7 +272,7 @@ def _export_mermaid(args, result, output_dir: Path):
|
|
|
257
272
|
files.append('flow_detailed.mmd')
|
|
258
273
|
if getattr(args, 'flow_full', False):
|
|
259
274
|
files.append('flow_full.mmd')
|
|
260
|
-
files.extend(['calls.mmd', 'compact_flow.mmd', 'calls.
|
|
275
|
+
files.extend(['calls.mmd', 'compact_flow.mmd', 'calls.yaml'])
|
|
261
276
|
print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)})")
|
|
262
277
|
|
|
263
278
|
_export_mermaid_pngs(args, output_dir)
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Export orchestration — registry-based dispatch for cleaner code.
|
|
2
|
+
|
|
3
|
+
Refactored to use EXPORT_REGISTRY for core format dispatch.
|
|
4
|
+
Maintains backward compatibility with all existing --format values.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional, List, Dict, Any
|
|
10
|
+
|
|
11
|
+
from code2llm.exporters import (
|
|
12
|
+
get_exporter,
|
|
13
|
+
EXPORT_REGISTRY,
|
|
14
|
+
YAMLExporter,
|
|
15
|
+
MermaidExporter,
|
|
16
|
+
ToonViewGenerator,
|
|
17
|
+
IndexHTMLGenerator,
|
|
18
|
+
)
|
|
19
|
+
from code2llm.exporters.project_yaml.evolution import load_previous_evolution
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Format output filenames
|
|
23
|
+
FORMAT_FILENAMES: Dict[str, str] = {
|
|
24
|
+
'toon': 'analysis.toon.yaml',
|
|
25
|
+
'map': 'map.toon.yaml',
|
|
26
|
+
'flow': 'flow.toon.yaml',
|
|
27
|
+
'context': 'context.md',
|
|
28
|
+
'yaml': 'analysis.yaml',
|
|
29
|
+
'json': 'analysis.json',
|
|
30
|
+
'evolution': 'evolution.toon.yaml',
|
|
31
|
+
'readme': 'README.md',
|
|
32
|
+
'project-yaml': 'project.yaml',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Human-readable labels
|
|
36
|
+
FORMAT_LABELS: Dict[str, str] = {
|
|
37
|
+
'toon': 'TOON (diagnostics)',
|
|
38
|
+
'map': 'MAP (structure)',
|
|
39
|
+
'flow': 'FLOW (data-flow)',
|
|
40
|
+
'context': 'CONTEXT (LLM narrative)',
|
|
41
|
+
'yaml': 'YAML',
|
|
42
|
+
'json': 'JSON',
|
|
43
|
+
'evolution': 'EVOLUTION (refactoring queue)',
|
|
44
|
+
'readme': 'README (documentation)',
|
|
45
|
+
'project-yaml': 'PROJECT-YAML (single source of truth)',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _run_exports(args, result, output_dir: Path, source_path: Optional[Path] = None):
|
|
50
|
+
"""Export analysis results in requested formats.
|
|
51
|
+
|
|
52
|
+
Uses EXPORT_REGISTRY for core format dispatch.
|
|
53
|
+
For chunked analysis, exports to subproject subdirectories.
|
|
54
|
+
"""
|
|
55
|
+
requested_formats = [f.strip() for f in args.format.split(',')]
|
|
56
|
+
formats = _expand_all_formats(requested_formats, getattr(args, 'png', False))
|
|
57
|
+
is_chunked = getattr(args, 'chunk', False)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
if is_chunked and source_path:
|
|
61
|
+
_export_chunked(args, result, output_dir, source_path, formats, requested_formats)
|
|
62
|
+
else:
|
|
63
|
+
_export_single(args, result, output_dir, formats, requested_formats, source_path)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f"Error during export: {e}", file=sys.stderr)
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _expand_all_formats(requested: List[str], include_png: bool = False) -> List[str]:
|
|
70
|
+
"""Expand 'all' to concrete format list."""
|
|
71
|
+
if 'all' not in requested:
|
|
72
|
+
return requested[:]
|
|
73
|
+
formats = ['toon', 'map', 'context', 'evolution']
|
|
74
|
+
if include_png:
|
|
75
|
+
formats.append('mermaid')
|
|
76
|
+
return formats
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _export_single(
|
|
80
|
+
args, result, output_dir: Path,
|
|
81
|
+
formats: List[str], requested_formats: List[str],
|
|
82
|
+
source_path: Optional[Path] = None
|
|
83
|
+
):
|
|
84
|
+
"""Export single project results."""
|
|
85
|
+
# Core formats via registry
|
|
86
|
+
_export_registry_formats(args, result, output_dir, formats)
|
|
87
|
+
|
|
88
|
+
# Special/conditional formats
|
|
89
|
+
if 'mermaid' in formats:
|
|
90
|
+
_export_mermaid(args, result, output_dir)
|
|
91
|
+
if 'calls' in formats or 'calls_toon' in formats:
|
|
92
|
+
_export_calls(args, result, output_dir, formats)
|
|
93
|
+
|
|
94
|
+
# Evolution always exported for 'all' or 'evolution' (handled by registry)
|
|
95
|
+
# Context fallback only if not explicitly requested
|
|
96
|
+
if 'context' not in formats and 'all' not in requested_formats:
|
|
97
|
+
_export_context_fallback(args, result, output_dir)
|
|
98
|
+
|
|
99
|
+
# project.toon.yaml for 'all' mode
|
|
100
|
+
if 'all' in requested_formats:
|
|
101
|
+
_export_project_toon(args, result, output_dir)
|
|
102
|
+
|
|
103
|
+
# Optional exports
|
|
104
|
+
if source_path is not None:
|
|
105
|
+
from .code2logic import _export_code2logic
|
|
106
|
+
from .prompt import _export_prompt_txt
|
|
107
|
+
_export_code2logic(args, source_path, output_dir, formats)
|
|
108
|
+
_export_prompt_txt(args, output_dir, requested_formats, source_path)
|
|
109
|
+
|
|
110
|
+
if getattr(args, 'refactor', False):
|
|
111
|
+
from .formats import _export_refactor_prompts
|
|
112
|
+
_export_refactor_prompts(args, result, output_dir)
|
|
113
|
+
|
|
114
|
+
if getattr(args, 'data_structures', False):
|
|
115
|
+
_export_data_structures(args, result, output_dir)
|
|
116
|
+
|
|
117
|
+
# Always export README and index
|
|
118
|
+
_export_readme(args, result, output_dir)
|
|
119
|
+
_export_index_html(args, output_dir)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _export_registry_formats(args, result, output_dir: Path, formats: List[str]):
|
|
123
|
+
"""Export core formats via EXPORT_REGISTRY lookup."""
|
|
124
|
+
for fmt in formats:
|
|
125
|
+
exporter_cls = get_exporter(fmt)
|
|
126
|
+
if exporter_cls is None:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
filename = FORMAT_FILENAMES.get(fmt, f'{fmt}.export')
|
|
130
|
+
label = FORMAT_LABELS.get(fmt, fmt.upper())
|
|
131
|
+
filepath = output_dir / filename
|
|
132
|
+
|
|
133
|
+
exporter = exporter_cls()
|
|
134
|
+
kwargs = _get_format_kwargs(fmt, args)
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
exporter.export(result, str(filepath), **kwargs)
|
|
138
|
+
if args.verbose:
|
|
139
|
+
print(f" - {label}: {filepath}")
|
|
140
|
+
except Exception as e:
|
|
141
|
+
if args.verbose:
|
|
142
|
+
print(f" - {label} export failed: {e}", file=sys.stderr)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _get_format_kwargs(fmt: str, args) -> Dict[str, Any]:
|
|
146
|
+
"""Get format-specific kwargs for export."""
|
|
147
|
+
kwargs: Dict[str, Any] = {}
|
|
148
|
+
if fmt in ('yaml', 'json'):
|
|
149
|
+
kwargs['compact'] = not args.full
|
|
150
|
+
kwargs['include_defaults'] = args.full
|
|
151
|
+
return kwargs
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _export_mermaid(args, result, output_dir: Path):
|
|
155
|
+
"""Export mermaid diagrams."""
|
|
156
|
+
exporter = MermaidExporter()
|
|
157
|
+
include_examples = getattr(args, 'flow_include_examples', False)
|
|
158
|
+
|
|
159
|
+
# Core diagrams
|
|
160
|
+
exporter.export_flow_compact(result, str(output_dir / 'flow.mmd'), include_examples)
|
|
161
|
+
exporter.export_call_graph(result, str(output_dir / 'calls.mmd'))
|
|
162
|
+
exporter.export_compact(result, str(output_dir / 'compact_flow.mmd'))
|
|
163
|
+
|
|
164
|
+
# Optional detailed diagrams
|
|
165
|
+
if getattr(args, 'flow_detail', False):
|
|
166
|
+
exporter.export_flow_detailed(result, str(output_dir / 'flow_detailed.mmd'), include_examples)
|
|
167
|
+
if getattr(args, 'flow_full', False):
|
|
168
|
+
exporter.export_flow_full(result, str(output_dir / 'flow_full.mmd'), include_examples)
|
|
169
|
+
|
|
170
|
+
# Also export calls.yaml/toon
|
|
171
|
+
yaml_exporter = YAMLExporter()
|
|
172
|
+
yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
|
|
173
|
+
yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
|
|
174
|
+
|
|
175
|
+
if args.verbose:
|
|
176
|
+
files = ['flow.mmd', 'calls.mmd', 'compact_flow.mmd', 'calls.yaml']
|
|
177
|
+
if getattr(args, 'flow_detail', False):
|
|
178
|
+
files.append('flow_detailed.mmd')
|
|
179
|
+
if getattr(args, 'flow_full', False):
|
|
180
|
+
files.append('flow_full.mmd')
|
|
181
|
+
print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)})")
|
|
182
|
+
|
|
183
|
+
# PNG generation
|
|
184
|
+
_export_mermaid_pngs(args, output_dir)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _export_mermaid_pngs(args, output_dir: Path):
|
|
188
|
+
"""Generate PNGs from mermaid files."""
|
|
189
|
+
if getattr(args, 'no_png', False):
|
|
190
|
+
return
|
|
191
|
+
try:
|
|
192
|
+
from ..generators.mermaid import generate_pngs
|
|
193
|
+
png_count = generate_pngs(output_dir, output_dir)
|
|
194
|
+
if args.verbose and png_count > 0:
|
|
195
|
+
print(f" - PNG: {png_count} files generated")
|
|
196
|
+
except ImportError:
|
|
197
|
+
if args.verbose:
|
|
198
|
+
print(f" - PNG: Skipped (install with: make install-mermaid)")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _export_calls(args, result, output_dir: Path, formats: List[str]):
|
|
202
|
+
"""Export calls.yaml and calls.toon.yaml."""
|
|
203
|
+
yaml_exporter = YAMLExporter()
|
|
204
|
+
if 'calls' in formats:
|
|
205
|
+
yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
|
|
206
|
+
if args.verbose:
|
|
207
|
+
print(f" - CALLS (call graph YAML): {output_dir / 'calls.yaml'}")
|
|
208
|
+
if 'calls_toon' in formats:
|
|
209
|
+
yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
|
|
210
|
+
if args.verbose:
|
|
211
|
+
print(f" - CALLS (toon format): {output_dir / 'calls.toon.yaml'}")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _export_context_fallback(args, result, output_dir: Path):
|
|
215
|
+
"""Export context.md as fallback."""
|
|
216
|
+
exporter_cls = get_exporter('context')
|
|
217
|
+
if exporter_cls:
|
|
218
|
+
exporter = exporter_cls()
|
|
219
|
+
exporter.export(result, str(output_dir / 'context.md'))
|
|
220
|
+
if args.verbose:
|
|
221
|
+
print(f" - CONTEXT (LLM narrative): {output_dir / 'context.md'}")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _export_data_structures(args, result, output_dir: Path):
|
|
225
|
+
"""Export data_structures.yaml."""
|
|
226
|
+
yaml_exporter = YAMLExporter()
|
|
227
|
+
yaml_exporter.export_data_structures(result, str(output_dir / 'data_structures.yaml'), compact=True)
|
|
228
|
+
if args.verbose:
|
|
229
|
+
print(f" - Data structures: {output_dir / 'data_structures.yaml'}")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _export_project_toon(args, result, output_dir: Path):
|
|
233
|
+
"""Export project.toon.yaml from project.yaml data."""
|
|
234
|
+
from ..exporters.project_yaml_exporter import ProjectYAMLExporter
|
|
235
|
+
|
|
236
|
+
project_yaml_exporter = ProjectYAMLExporter()
|
|
237
|
+
prev_evolution = load_previous_evolution(output_dir / 'project.yaml')
|
|
238
|
+
data = project_yaml_exporter._build_project_yaml(result, prev_evolution)
|
|
239
|
+
|
|
240
|
+
generator = ToonViewGenerator()
|
|
241
|
+
generator.generate(data, str(output_dir / 'project.toon.yaml'))
|
|
242
|
+
|
|
243
|
+
if args.verbose:
|
|
244
|
+
print(f" - PROJECT-TOON (project overview): {output_dir / 'project.toon.yaml'}")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _export_readme(args, result, output_dir: Path):
|
|
248
|
+
"""Export README.md."""
|
|
249
|
+
if getattr(args, 'no_readme', False):
|
|
250
|
+
return
|
|
251
|
+
exporter_cls = get_exporter('readme')
|
|
252
|
+
if exporter_cls:
|
|
253
|
+
exporter = exporter_cls()
|
|
254
|
+
exporter.export(result, str(output_dir / 'README.md'))
|
|
255
|
+
if args.verbose:
|
|
256
|
+
print(f" - README (documentation): {output_dir / 'README.md'}")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _export_index_html(args, output_dir: Path):
|
|
260
|
+
"""Generate index.html for browsing files."""
|
|
261
|
+
if 'all' not in getattr(args, 'format', ''):
|
|
262
|
+
return
|
|
263
|
+
try:
|
|
264
|
+
generator = IndexHTMLGenerator(output_dir)
|
|
265
|
+
index_path = generator.generate()
|
|
266
|
+
if args.verbose:
|
|
267
|
+
print(f" - INDEX (file browser): {index_path}")
|
|
268
|
+
except Exception as e:
|
|
269
|
+
if args.verbose:
|
|
270
|
+
print(f" - INDEX generation failed: {e}", file=sys.stderr)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _export_chunked(
|
|
274
|
+
args, result, output_dir: Path, source_path: Path,
|
|
275
|
+
formats: List[str], requested_formats: List[str]
|
|
276
|
+
):
|
|
277
|
+
"""Export chunked analysis results."""
|
|
278
|
+
subprojects = _get_filtered_subprojects(args, source_path)
|
|
279
|
+
|
|
280
|
+
for sp in subprojects:
|
|
281
|
+
_process_subproject(args, sp, output_dir)
|
|
282
|
+
|
|
283
|
+
# Merged summary
|
|
284
|
+
_export_registry_formats(args, result, output_dir, ['toon', 'context', 'evolution'])
|
|
285
|
+
|
|
286
|
+
if 'calls' in formats or 'calls_toon' in formats:
|
|
287
|
+
_export_calls(args, result, output_dir, formats)
|
|
288
|
+
if 'all' in requested_formats:
|
|
289
|
+
_export_project_toon(args, result, output_dir)
|
|
290
|
+
|
|
291
|
+
if source_path is not None:
|
|
292
|
+
from .code2logic import _export_code2logic
|
|
293
|
+
from .prompt import _export_chunked_prompt_txt
|
|
294
|
+
_export_code2logic(args, source_path, output_dir, formats)
|
|
295
|
+
_export_chunked_prompt_txt(args, output_dir, requested_formats, source_path, subprojects)
|
|
296
|
+
|
|
297
|
+
_export_readme(args, result, output_dir)
|
|
298
|
+
_export_index_html(args, output_dir)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _get_filtered_subprojects(args, source_path: Path):
|
|
302
|
+
"""Get filtered subprojects list."""
|
|
303
|
+
from ..core.large_repo import HierarchicalRepoSplitter
|
|
304
|
+
|
|
305
|
+
splitter = HierarchicalRepoSplitter(size_limit_kb=args.chunk_size)
|
|
306
|
+
subprojects = splitter.get_analysis_plan(source_path)
|
|
307
|
+
|
|
308
|
+
if getattr(args, 'only_subproject', None):
|
|
309
|
+
subprojects = [
|
|
310
|
+
sp for sp in subprojects
|
|
311
|
+
if sp.name == args.only_subproject or sp.name.startswith(args.only_subproject + '.')
|
|
312
|
+
]
|
|
313
|
+
if getattr(args, 'skip_subprojects', None):
|
|
314
|
+
subprojects = [
|
|
315
|
+
sp for sp in subprojects
|
|
316
|
+
if not any(sp.name.startswith(skip) for skip in args.skip_subprojects)
|
|
317
|
+
]
|
|
318
|
+
return subprojects
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _process_subproject(args, sp, output_dir: Path):
|
|
322
|
+
"""Process a single subproject result."""
|
|
323
|
+
sp_output_dir = output_dir / sp.name.replace('.', '_')
|
|
324
|
+
if not sp_output_dir.exists():
|
|
325
|
+
return
|
|
326
|
+
for ext in ['.toon', '.yaml', '.json']:
|
|
327
|
+
result_file = sp_output_dir / f'analysis{ext}'
|
|
328
|
+
if result_file.exists():
|
|
329
|
+
if args.verbose:
|
|
330
|
+
level_name = {0: 'root', 1: 'L1', 2: 'L2'}.get(sp.level, f'L{sp.level}')
|
|
331
|
+
print(f" - Exported [{level_name}] {sp.name}")
|
|
332
|
+
break
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# Backward-compatible aliases
|
|
336
|
+
_export_single_project = _export_single
|
|
337
|
+
_export_chunked_results = _export_chunked
|