code2llm 0.5.144__tar.gz → 0.5.146__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.144/code2llm.egg-info → code2llm-0.5.146}/PKG-INFO +12 -14
- {code2llm-0.5.144 → code2llm-0.5.146}/README.md +9 -11
- code2llm-0.5.146/VERSION +1 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/__init__.py +1 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_analysis.py +14 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/formats.py +26 -6
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator.py +52 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator_handlers.py +36 -11
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/prompt.py +6 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_parser.py +6 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/__init__.py +2 -2
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/analyzer.py +47 -28
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/config.py +14 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/file_analyzer.py +11 -4
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/persistent_cache.py +50 -32
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/refactoring.py +21 -7
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/__init__.py +1 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/incremental.py +11 -11
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming_analyzer.py +2 -2
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/core.py +1 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/helpers.py +40 -9
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics.py +1 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics_core.py +49 -57
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_task.py +44 -34
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/pipeline.py +8 -8
- {code2llm-0.5.144 → code2llm-0.5.146/code2llm.egg-info}/PKG-INFO +12 -14
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/requires.txt +1 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/pyproject.toml +5 -3
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_deep_analysis.py +1 -0
- code2llm-0.5.144/VERSION +0 -1
- {code2llm-0.5.144 → code2llm-0.5.146}/LICENSE +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/MANIFEST.in +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/__main__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/call_graph.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/cfg.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/api.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_commands.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/cli_exports/orchestrator_constants.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/ruby.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/models.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/context_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/dashboard_data.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/dashboard_renderer.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution/yaml_export.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/flow_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator/renderer.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/details.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/header.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map/yaml_export.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/calls.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/compact.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/hotspots.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/content.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/insights.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme/sections.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/metrics_health.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/analysis.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow/utils.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/fix.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/png.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid/validation.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/parsers/toon_parser.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/setup.cfg +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/setup.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.144 → code2llm-0.5.146}/tests/test_toon_v2.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.146
|
|
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
|
|
7
7
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
8
|
-
License
|
|
8
|
+
License: Apache-2.0
|
|
9
9
|
Project-URL: Homepage, https://github.com/wronai/stts
|
|
10
10
|
Project-URL: Repository, https://github.com/wronai/stts
|
|
11
11
|
Project-URL: Issues, https://github.com/wronai/stts/issues
|
|
@@ -54,7 +54,7 @@ Requires-Dist: pytest-xdist>=3.0; extra == "dev"
|
|
|
54
54
|
Requires-Dist: black>=21.0; extra == "dev"
|
|
55
55
|
Requires-Dist: flake8>=3.9; extra == "dev"
|
|
56
56
|
Requires-Dist: mypy>=0.910; extra == "dev"
|
|
57
|
-
Requires-Dist: goal>=2.1.
|
|
57
|
+
Requires-Dist: goal>=2.1.218; extra == "dev"
|
|
58
58
|
Requires-Dist: costs>=0.1.20; extra == "dev"
|
|
59
59
|
Requires-Dist: pfix>=0.1.60; extra == "dev"
|
|
60
60
|
Dynamic: author
|
|
@@ -67,18 +67,16 @@ 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 (198 commits)
|
|
74
|
+
- 👤 **Human dev:** ~$7224 (72.2h @ $100/h, 30min dedup)
|
|
75
75
|
|
|
76
|
-
Generated on 2026-
|
|
76
|
+
Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
77
77
|
|
|
78
78
|
---
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
80
|
This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
|
|
83
81
|
|
|
84
82
|
## 📁 Generated Files Overview
|
|
@@ -407,10 +405,10 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
407
405
|
---
|
|
408
406
|
|
|
409
407
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
410
|
-
**Analysis Date**: 2026-
|
|
411
|
-
**Total Functions**:
|
|
412
|
-
**Total Classes**:
|
|
413
|
-
**Modules**:
|
|
408
|
+
**Analysis Date**: 2026-05-06
|
|
409
|
+
**Total Functions**: 3625
|
|
410
|
+
**Total Classes**: 250
|
|
411
|
+
**Modules**: 492
|
|
414
412
|
|
|
415
413
|
For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
|
|
416
414
|
|
|
@@ -3,18 +3,16 @@
|
|
|
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 (198 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$7224 (72.2h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
|
-
Generated on 2026-
|
|
12
|
+
Generated on 2026-05-06 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
16
|
This directory contains the complete analysis of your project generated by `code2llm`. Each file serves a specific purpose for understanding, refactoring, and documenting your codebase.
|
|
19
17
|
|
|
20
18
|
## 📁 Generated Files Overview
|
|
@@ -343,10 +341,10 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
343
341
|
---
|
|
344
342
|
|
|
345
343
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
346
|
-
**Analysis Date**: 2026-
|
|
347
|
-
**Total Functions**:
|
|
348
|
-
**Total Classes**:
|
|
349
|
-
**Modules**:
|
|
344
|
+
**Analysis Date**: 2026-05-06
|
|
345
|
+
**Total Functions**: 3625
|
|
346
|
+
**Total Classes**: 250
|
|
347
|
+
**Modules**: 492
|
|
350
348
|
|
|
351
349
|
For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
|
|
352
350
|
|
code2llm-0.5.146/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.146
|
|
@@ -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.146"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -88,6 +88,20 @@ def _build_config(args, output_dir: Path):
|
|
|
88
88
|
config.watch = getattr(args, 'watch', False)
|
|
89
89
|
# Dry-run mode (handled in orchestrator, but stored for reference)
|
|
90
90
|
config.dry_run = getattr(args, 'dry_run', False)
|
|
91
|
+
|
|
92
|
+
# --fast: skip expensive analyses (vulture, centrality, DFG, communities)
|
|
93
|
+
if getattr(args, 'fast', False):
|
|
94
|
+
config.performance.fast_mode = True
|
|
95
|
+
config.performance.apply_fast_mode()
|
|
96
|
+
|
|
97
|
+
# Strategy-based performance tuning
|
|
98
|
+
strategy = getattr(args, 'strategy', 'standard')
|
|
99
|
+
if strategy == 'quick':
|
|
100
|
+
config.performance.skip_data_flow = True
|
|
101
|
+
config.performance.skip_dead_code_detection = True
|
|
102
|
+
config.performance.skip_centrality = True
|
|
103
|
+
config.performance.skip_community_detection = True
|
|
104
|
+
|
|
91
105
|
return config
|
|
92
106
|
|
|
93
107
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
+
import time
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import Optional
|
|
7
8
|
|
|
@@ -13,6 +14,7 @@ from code2llm.exporters import (
|
|
|
13
14
|
ArticleViewGenerator, HTMLDashboardGenerator,
|
|
14
15
|
load_project_yaml, IndexHTMLGenerator,
|
|
15
16
|
)
|
|
17
|
+
from .orchestrator import _inject_generation_time as _inject_time
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
def _export_evolution(args, result, output_dir: Path):
|
|
@@ -21,9 +23,12 @@ def _export_evolution(args, result, output_dir: Path):
|
|
|
21
23
|
return
|
|
22
24
|
exporter = EvolutionExporter()
|
|
23
25
|
filepath = output_dir / 'evolution.toon.yaml'
|
|
26
|
+
t0 = time.monotonic()
|
|
24
27
|
exporter.export(result, str(filepath))
|
|
28
|
+
elapsed = time.monotonic() - t0
|
|
29
|
+
_inject_time(filepath, elapsed)
|
|
25
30
|
if args.verbose:
|
|
26
|
-
print(f" - EVOLUTION (refactoring queue): {filepath}")
|
|
31
|
+
print(f" - EVOLUTION (refactoring queue): {filepath} ({elapsed:.2f}s)")
|
|
27
32
|
|
|
28
33
|
|
|
29
34
|
def _export_data_structures(args, result, output_dir: Path):
|
|
@@ -43,9 +48,12 @@ def _export_context_fallback(args, result, output_dir: Path, formats: list):
|
|
|
43
48
|
return
|
|
44
49
|
exporter = ContextExporter()
|
|
45
50
|
filepath = output_dir / 'context.md'
|
|
51
|
+
t0 = time.monotonic()
|
|
46
52
|
exporter.export(result, str(filepath))
|
|
53
|
+
elapsed = time.monotonic() - t0
|
|
54
|
+
_inject_time(filepath, elapsed)
|
|
47
55
|
if args.verbose:
|
|
48
|
-
print(f" - CONTEXT (LLM narrative): {filepath}")
|
|
56
|
+
print(f" - CONTEXT (LLM narrative): {filepath} ({elapsed:.2f}s)")
|
|
49
57
|
|
|
50
58
|
|
|
51
59
|
def _export_readme(args, result, output_dir: Path):
|
|
@@ -54,18 +62,24 @@ def _export_readme(args, result, output_dir: Path):
|
|
|
54
62
|
return
|
|
55
63
|
exporter = READMEExporter()
|
|
56
64
|
filepath = output_dir / 'README.md'
|
|
65
|
+
t0 = time.monotonic()
|
|
57
66
|
exporter.export(result, str(filepath))
|
|
67
|
+
elapsed = time.monotonic() - t0
|
|
68
|
+
_inject_time(filepath, elapsed)
|
|
58
69
|
if args.verbose:
|
|
59
|
-
print(f" - README (documentation): {filepath}")
|
|
70
|
+
print(f" - README (documentation): {filepath} ({elapsed:.2f}s)")
|
|
60
71
|
|
|
61
72
|
|
|
62
73
|
def _export_project_yaml(args, result, output_dir: Path):
|
|
63
74
|
"""Export unified project.yaml — single source of truth."""
|
|
64
75
|
exporter = ProjectYAMLExporter()
|
|
65
76
|
filepath = output_dir / 'project.yaml'
|
|
77
|
+
t0 = time.monotonic()
|
|
66
78
|
exporter.export(result, str(filepath))
|
|
79
|
+
elapsed = time.monotonic() - t0
|
|
80
|
+
_inject_time(filepath, elapsed)
|
|
67
81
|
if getattr(args, 'verbose', False):
|
|
68
|
-
print(f" - PROJECT-YAML (single source of truth): {filepath}")
|
|
82
|
+
print(f" - PROJECT-YAML (single source of truth): {filepath} ({elapsed:.2f}s)")
|
|
69
83
|
return filepath
|
|
70
84
|
|
|
71
85
|
|
|
@@ -79,10 +93,13 @@ def _export_project_toon(args, result, output_dir: Path):
|
|
|
79
93
|
|
|
80
94
|
exporter = ToonViewGenerator()
|
|
81
95
|
filepath = output_dir / 'project.toon.yaml'
|
|
96
|
+
t0 = time.monotonic()
|
|
82
97
|
exporter.generate(data, str(filepath))
|
|
98
|
+
elapsed = time.monotonic() - t0
|
|
99
|
+
_inject_time(filepath, elapsed)
|
|
83
100
|
|
|
84
101
|
if getattr(args, 'verbose', False):
|
|
85
|
-
print(f" - PROJECT-TOON (project overview): {filepath}")
|
|
102
|
+
print(f" - PROJECT-TOON (project overview): {filepath} ({elapsed:.2f}s)")
|
|
86
103
|
|
|
87
104
|
return filepath
|
|
88
105
|
|
|
@@ -129,9 +146,12 @@ def _export_simple_formats(args, result, output_dir: Path, formats):
|
|
|
129
146
|
exporter = exporter_cls()
|
|
130
147
|
# Export as plain text TOON format with .toon.yaml extension
|
|
131
148
|
filepath = output_dir / filename
|
|
149
|
+
t0 = time.monotonic()
|
|
132
150
|
exporter.export(result, str(filepath))
|
|
151
|
+
elapsed = time.monotonic() - t0
|
|
152
|
+
_inject_time(filepath, elapsed)
|
|
133
153
|
if args.verbose:
|
|
134
|
-
print(f" - {label}: {filepath}")
|
|
154
|
+
print(f" - {label}: {filepath} ({elapsed:.2f}s)")
|
|
135
155
|
|
|
136
156
|
# Unified project.yaml (single source of truth)
|
|
137
157
|
if 'project-yaml' in formats:
|
|
@@ -7,6 +7,7 @@ Maintains backward compatibility with all existing --format values.
|
|
|
7
7
|
import os
|
|
8
8
|
import shutil
|
|
9
9
|
import sys
|
|
10
|
+
import time
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Optional, List, Dict, Any
|
|
12
13
|
|
|
@@ -280,9 +281,12 @@ def _export_registry_formats(args, result, output_dir: Path, formats: List[str])
|
|
|
280
281
|
kwargs = _get_format_kwargs(fmt, args)
|
|
281
282
|
|
|
282
283
|
try:
|
|
284
|
+
t0 = time.monotonic()
|
|
283
285
|
exporter.export(result, str(filepath), **kwargs)
|
|
286
|
+
elapsed = time.monotonic() - t0
|
|
287
|
+
_inject_generation_time(filepath, elapsed)
|
|
284
288
|
if args.verbose:
|
|
285
|
-
print(f" - {label}: {filepath}")
|
|
289
|
+
print(f" - {label}: {filepath} ({elapsed:.2f}s)")
|
|
286
290
|
except Exception as e:
|
|
287
291
|
if args.verbose:
|
|
288
292
|
print(f" - {label} export failed: {e}", file=sys.stderr)
|
|
@@ -306,6 +310,53 @@ def _export_chunked(
|
|
|
306
310
|
_chunked_impl(args, result, output_dir, source_path, formats, requested_formats)
|
|
307
311
|
|
|
308
312
|
|
|
313
|
+
def _inject_generation_time(filepath: Path, elapsed: float) -> None:
|
|
314
|
+
"""Inject generation time comment into the second line of an exported file."""
|
|
315
|
+
try:
|
|
316
|
+
path = Path(filepath)
|
|
317
|
+
if not path.exists():
|
|
318
|
+
return
|
|
319
|
+
suffix = path.suffix.lower()
|
|
320
|
+
name = path.name.lower()
|
|
321
|
+
|
|
322
|
+
# Only inject into text-based files
|
|
323
|
+
if suffix not in ('.yaml', '.yml', '.md', '.txt', '.mmd', '.html', '.json', '.export'):
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
content = path.read_text(encoding='utf-8')
|
|
327
|
+
if not content:
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
tag = f"generated in {elapsed:.2f}s"
|
|
331
|
+
|
|
332
|
+
if suffix in ('.yaml', '.yml', '.mmd', '.export', '.txt') or name.endswith('.toon.yaml'):
|
|
333
|
+
# YAML/Mermaid/text: insert '# generated in X.XXs' after first line
|
|
334
|
+
lines = content.split('\n', 1)
|
|
335
|
+
if len(lines) == 2:
|
|
336
|
+
content = f"{lines[0]}\n# {tag}\n{lines[1]}"
|
|
337
|
+
else:
|
|
338
|
+
content = f"{lines[0]}\n# {tag}\n"
|
|
339
|
+
elif suffix == '.md':
|
|
340
|
+
# Markdown: insert HTML comment after first line
|
|
341
|
+
lines = content.split('\n', 1)
|
|
342
|
+
if len(lines) == 2:
|
|
343
|
+
content = f"{lines[0]}\n<!-- {tag} -->\n{lines[1]}"
|
|
344
|
+
else:
|
|
345
|
+
content = f"{lines[0]}\n<!-- {tag} -->\n"
|
|
346
|
+
elif suffix == '.html':
|
|
347
|
+
# HTML: insert comment after <!DOCTYPE or <html>
|
|
348
|
+
content = content.replace('\n', f'\n<!-- {tag} -->\n', 1)
|
|
349
|
+
elif suffix == '.json':
|
|
350
|
+
# JSON doesn't support comments — skip
|
|
351
|
+
return
|
|
352
|
+
else:
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
path.write_text(content, encoding='utf-8')
|
|
356
|
+
except Exception:
|
|
357
|
+
pass # Never fail the export pipeline for a comment
|
|
358
|
+
|
|
359
|
+
|
|
309
360
|
# Backward-compatible aliases
|
|
310
361
|
_export_single_project = _export_single
|
|
311
362
|
_export_chunked_results = _export_chunked
|
|
@@ -4,6 +4,7 @@ This module contains all the individual export handler functions
|
|
|
4
4
|
that were extracted from orchestrator.py to reduce its size.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import time
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Any, Dict, List
|
|
9
10
|
|
|
@@ -20,10 +21,14 @@ from .orchestrator_constants import FORMAT_LABELS
|
|
|
20
21
|
|
|
21
22
|
def _export_mermaid(args, result, output_dir: Path):
|
|
22
23
|
"""Export mermaid diagrams."""
|
|
24
|
+
from .orchestrator import _inject_generation_time
|
|
25
|
+
|
|
23
26
|
exporter = MermaidExporter()
|
|
24
27
|
include_examples = getattr(args, 'flow_include_examples', False)
|
|
25
28
|
|
|
26
29
|
# Core diagrams
|
|
30
|
+
mmd_files = ['flow.mmd', 'calls.mmd', 'compact_flow.mmd']
|
|
31
|
+
t0 = time.monotonic()
|
|
27
32
|
exporter.export_flow_compact(result, str(output_dir / 'flow.mmd'), include_examples)
|
|
28
33
|
exporter.export_call_graph(result, str(output_dir / 'calls.mmd'))
|
|
29
34
|
exporter.export_compact(result, str(output_dir / 'compact_flow.mmd'))
|
|
@@ -31,21 +36,27 @@ def _export_mermaid(args, result, output_dir: Path):
|
|
|
31
36
|
# Optional detailed diagrams
|
|
32
37
|
if getattr(args, 'flow_detail', False):
|
|
33
38
|
exporter.export_flow_detailed(result, str(output_dir / 'flow_detailed.mmd'), include_examples)
|
|
39
|
+
mmd_files.append('flow_detailed.mmd')
|
|
34
40
|
if getattr(args, 'flow_full', False):
|
|
35
41
|
exporter.export_flow_full(result, str(output_dir / 'flow_full.mmd'), include_examples)
|
|
42
|
+
mmd_files.append('flow_full.mmd')
|
|
43
|
+
elapsed_mmd = time.monotonic() - t0
|
|
44
|
+
|
|
45
|
+
# Inject timing into each .mmd file
|
|
46
|
+
for mf in mmd_files:
|
|
47
|
+
_inject_generation_time(output_dir / mf, elapsed_mmd)
|
|
36
48
|
|
|
37
49
|
# Also export calls.yaml/toon
|
|
38
50
|
yaml_exporter = YAMLExporter()
|
|
51
|
+
t0 = time.monotonic()
|
|
39
52
|
yaml_exporter.export_calls(result, str(output_dir / 'calls.yaml'))
|
|
40
53
|
yaml_exporter.export_calls_toon(result, str(output_dir / 'calls.toon.yaml'))
|
|
54
|
+
elapsed_calls = time.monotonic() - t0
|
|
55
|
+
_inject_generation_time(output_dir / 'calls.toon.yaml', elapsed_calls)
|
|
41
56
|
|
|
42
57
|
if args.verbose:
|
|
43
|
-
files =
|
|
44
|
-
|
|
45
|
-
files.append('flow_detailed.mmd')
|
|
46
|
-
if getattr(args, 'flow_full', False):
|
|
47
|
-
files.append('flow_full.mmd')
|
|
48
|
-
print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)})")
|
|
58
|
+
files = mmd_files + ['calls.yaml']
|
|
59
|
+
print(f" - Mermaid: {output_dir}/*.mmd ({', '.join(files)}) ({elapsed_mmd:.2f}s)")
|
|
49
60
|
|
|
50
61
|
# PNG generation
|
|
51
62
|
_export_mermaid_pngs(args, output_dir)
|
|
@@ -99,28 +110,38 @@ def _export_data_structures(args, result, output_dir: Path):
|
|
|
99
110
|
def _export_project_toon(args, result, output_dir: Path):
|
|
100
111
|
"""Export project.toon.yaml from project.yaml data."""
|
|
101
112
|
from ..exporters.project_yaml_exporter import ProjectYAMLExporter
|
|
113
|
+
from .orchestrator import _inject_generation_time
|
|
102
114
|
|
|
103
115
|
project_yaml_exporter = ProjectYAMLExporter()
|
|
104
116
|
prev_evolution = load_previous_evolution(output_dir / 'project.yaml')
|
|
105
117
|
data = project_yaml_exporter._build_project_yaml(result, prev_evolution)
|
|
106
118
|
|
|
107
119
|
generator = ToonViewGenerator()
|
|
108
|
-
|
|
120
|
+
filepath = output_dir / 'project.toon.yaml'
|
|
121
|
+
t0 = time.monotonic()
|
|
122
|
+
generator.generate(data, str(filepath))
|
|
123
|
+
elapsed = time.monotonic() - t0
|
|
124
|
+
_inject_generation_time(filepath, elapsed)
|
|
109
125
|
|
|
110
126
|
if args.verbose:
|
|
111
|
-
print(f" - PROJECT-TOON (project overview): {
|
|
127
|
+
print(f" - PROJECT-TOON (project overview): {filepath} ({elapsed:.2f}s)")
|
|
112
128
|
|
|
113
129
|
|
|
114
130
|
def _export_readme(args, result, output_dir: Path):
|
|
115
131
|
"""Export README.md."""
|
|
116
132
|
if getattr(args, 'no_readme', False):
|
|
117
133
|
return
|
|
134
|
+
from .orchestrator import _inject_generation_time
|
|
118
135
|
exporter_cls = get_exporter('readme')
|
|
119
136
|
if exporter_cls:
|
|
120
137
|
exporter = exporter_cls()
|
|
121
|
-
|
|
138
|
+
filepath = output_dir / 'README.md'
|
|
139
|
+
t0 = time.monotonic()
|
|
140
|
+
exporter.export(result, str(filepath))
|
|
141
|
+
elapsed = time.monotonic() - t0
|
|
142
|
+
_inject_generation_time(filepath, elapsed)
|
|
122
143
|
if args.verbose:
|
|
123
|
-
print(f" - README (documentation): {
|
|
144
|
+
print(f" - README (documentation): {filepath} ({elapsed:.2f}s)")
|
|
124
145
|
|
|
125
146
|
|
|
126
147
|
def _export_index_html(args, output_dir: Path):
|
|
@@ -128,10 +149,14 @@ def _export_index_html(args, output_dir: Path):
|
|
|
128
149
|
if 'all' not in getattr(args, 'format', ''):
|
|
129
150
|
return
|
|
130
151
|
try:
|
|
152
|
+
from .orchestrator import _inject_generation_time
|
|
131
153
|
generator = IndexHTMLGenerator(output_dir)
|
|
154
|
+
t0 = time.monotonic()
|
|
132
155
|
index_path = generator.generate()
|
|
156
|
+
elapsed = time.monotonic() - t0
|
|
157
|
+
_inject_generation_time(index_path, elapsed)
|
|
133
158
|
if args.verbose:
|
|
134
|
-
print(f" - INDEX (file browser): {index_path}")
|
|
159
|
+
print(f" - INDEX (file browser): {index_path} ({elapsed:.2f}s)")
|
|
135
160
|
except Exception as e:
|
|
136
161
|
if args.verbose:
|
|
137
162
|
print(f" - INDEX generation failed: {e}", file=__import__('sys').stderr)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Prompt generation — prompt.txt for LLM consumption (regular and chunked)."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
import time
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import List, Optional, Tuple
|
|
6
7
|
|
|
@@ -30,9 +31,13 @@ def _export_prompt_txt(args, output_dir: Path, formats: list[str], source_path:
|
|
|
30
31
|
lines.extend(_build_prompt_footer(chunked=False, file_analysis=file_analysis))
|
|
31
32
|
|
|
32
33
|
prompt_path = output_dir / 'prompt.txt'
|
|
34
|
+
t0 = time.monotonic()
|
|
33
35
|
prompt_path.write_text("\n".join(lines) + "\n", encoding='utf-8')
|
|
36
|
+
elapsed = time.monotonic() - t0
|
|
37
|
+
from .orchestrator import _inject_generation_time
|
|
38
|
+
_inject_generation_time(prompt_path, elapsed)
|
|
34
39
|
if args.verbose:
|
|
35
|
-
print(f" - PROMPT: {prompt_path}")
|
|
40
|
+
print(f" - PROMPT: {prompt_path} ({elapsed:.2f}s)")
|
|
36
41
|
|
|
37
42
|
|
|
38
43
|
def _export_chunked_prompt_txt(args, output_dir: Path, formats: list[str], source_path: Optional[Path] = None, subprojects: list = None) -> None:
|
|
@@ -159,6 +159,12 @@ Strategy Options (--strategy):
|
|
|
159
159
|
help='Auto-detect changed files and only re-analyze those (faster subsequent runs)'
|
|
160
160
|
)
|
|
161
161
|
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
'--fast',
|
|
164
|
+
action='store_true',
|
|
165
|
+
help='Skip expensive analyses (vulture, centrality, DFG, communities) for faster runs'
|
|
166
|
+
)
|
|
167
|
+
|
|
162
168
|
parser.add_argument(
|
|
163
169
|
'--strategy',
|
|
164
170
|
choices=['quick', 'standard', 'deep'],
|
|
@@ -9,7 +9,7 @@ from .models import (
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
'ProjectAnalyzer',
|
|
11
11
|
'StreamingAnalyzer',
|
|
12
|
-
'
|
|
12
|
+
'StreamingIncrementalAnalyzer',
|
|
13
13
|
'ScanStrategy',
|
|
14
14
|
'SmartPrioritizer',
|
|
15
15
|
'STRATEGY_QUICK',
|
|
@@ -43,7 +43,7 @@ def __getattr__(name):
|
|
|
43
43
|
return locals()[name]
|
|
44
44
|
|
|
45
45
|
_streaming_names = {
|
|
46
|
-
'StreamingAnalyzer', '
|
|
46
|
+
'StreamingAnalyzer', 'StreamingIncrementalAnalyzer', 'ScanStrategy',
|
|
47
47
|
'SmartPrioritizer', 'STRATEGY_QUICK', 'STRATEGY_STANDARD', 'STRATEGY_DEEP',
|
|
48
48
|
}
|
|
49
49
|
if name in _streaming_names:
|
|
@@ -121,10 +121,16 @@ class ProjectAnalyzer:
|
|
|
121
121
|
return None, [], files
|
|
122
122
|
|
|
123
123
|
def _run_analysis(self, files_to_analyze: List[Tuple[str, str]]) -> List[Dict]:
|
|
124
|
-
"""Analyze files in parallel or sequentially depending on config.
|
|
124
|
+
"""Analyze files in parallel or sequentially depending on config.
|
|
125
|
+
|
|
126
|
+
Parallel mode has significant startup/pickle overhead. It is only
|
|
127
|
+
beneficial for larger file sets (threshold: 30 files).
|
|
128
|
+
"""
|
|
125
129
|
if not files_to_analyze:
|
|
126
130
|
return []
|
|
127
|
-
|
|
131
|
+
_PARALLEL_THRESHOLD = 30
|
|
132
|
+
if (self.config.performance.parallel_enabled
|
|
133
|
+
and len(files_to_analyze) > _PARALLEL_THRESHOLD):
|
|
128
134
|
return self._analyze_parallel(files_to_analyze)
|
|
129
135
|
return self._analyze_sequential(files_to_analyze)
|
|
130
136
|
|
|
@@ -197,39 +203,61 @@ class ProjectAnalyzer:
|
|
|
197
203
|
if self.config.verbose:
|
|
198
204
|
self._print_summary(merged)
|
|
199
205
|
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _should_collect_file(
|
|
208
|
+
filename: str,
|
|
209
|
+
ext_set: set,
|
|
210
|
+
filename_set_lower: frozenset,
|
|
211
|
+
filename_prefixes_lower: tuple,
|
|
212
|
+
) -> bool:
|
|
213
|
+
"""Return True if *filename* matches a known language extension,
|
|
214
|
+
well-known filename (Dockerfile, Makefile, ...), or filename prefix
|
|
215
|
+
(Dockerfile.dev, Makefile.am).
|
|
216
|
+
"""
|
|
217
|
+
filename_lower = filename.lower()
|
|
218
|
+
suffix = os.path.splitext(filename)[1].lower()
|
|
219
|
+
return (
|
|
220
|
+
suffix in ext_set
|
|
221
|
+
or filename_lower in filename_set_lower
|
|
222
|
+
or filename_lower.startswith(filename_prefixes_lower)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def _compute_module_name(
|
|
227
|
+
rel: str, filename: str, project_name: str
|
|
228
|
+
) -> str:
|
|
229
|
+
"""Derive a Pythonic module name from a relative file path."""
|
|
230
|
+
parts = rel.replace('\\', '/').split('/')
|
|
231
|
+
dir_parts = parts[:-1]
|
|
232
|
+
init_names = frozenset({'__init__.py', 'index.js', 'index.ts', 'mod.rs', 'lib.rs'})
|
|
233
|
+
if filename in init_names:
|
|
234
|
+
return '.'.join(dir_parts) if dir_parts else project_name
|
|
235
|
+
stem = os.path.splitext(filename)[0]
|
|
236
|
+
return '.'.join(dir_parts + [stem]) if dir_parts else stem
|
|
237
|
+
|
|
200
238
|
def _collect_files(self, project_path: Path) -> List[Tuple[str, str]]:
|
|
201
239
|
"""Collect all source files with their module names for all supported languages.
|
|
202
|
-
|
|
240
|
+
|
|
203
241
|
Uses a single os.walk traversal with early directory pruning instead of
|
|
204
242
|
separate rglob calls per extension (~40x speedup on large repos).
|
|
205
243
|
"""
|
|
206
244
|
files = []
|
|
207
|
-
ext_set = set(ALL_EXTENSIONS)
|
|
208
|
-
# Filename lookup uses a case-insensitive set. We lowercase once
|
|
209
|
-
# here so the hot loop only does a single lower() per file.
|
|
245
|
+
ext_set = set(ALL_EXTENSIONS)
|
|
210
246
|
filename_set_lower = frozenset(n.lower() for n in ALL_FILENAMES)
|
|
211
247
|
filename_prefixes_lower = tuple(p.lower() for p in LANGUAGE_FILENAME_PREFIXES)
|
|
212
|
-
|
|
213
|
-
seen = set() # guard against duplicate paths (e.g. .h in both c and cpp lists)
|
|
248
|
+
seen = set()
|
|
214
249
|
project_str = str(project_path)
|
|
250
|
+
project_name = project_path.name
|
|
215
251
|
|
|
216
252
|
for dirpath, dirnames, filenames in os.walk(project_str, topdown=True):
|
|
217
|
-
# Prune skipped directories in-place so os.walk won't descend into them
|
|
218
253
|
dirnames[:] = [
|
|
219
254
|
d for d in dirnames
|
|
220
255
|
if not self.file_filter.should_skip_dir(d)
|
|
221
256
|
]
|
|
222
257
|
|
|
223
258
|
for filename in filenames:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
# Accept by extension, well-known filename
|
|
227
|
-
# (Dockerfile, Makefile, Jenkinsfile, ...), or known
|
|
228
|
-
# prefix (Dockerfile.dev, Dockerfile.prod, Makefile.am).
|
|
229
|
-
if (
|
|
230
|
-
suffix not in ext_set
|
|
231
|
-
and filename_lower not in filename_set_lower
|
|
232
|
-
and not filename_lower.startswith(filename_prefixes_lower)
|
|
259
|
+
if not self._should_collect_file(
|
|
260
|
+
filename, ext_set, filename_set_lower, filename_prefixes_lower
|
|
233
261
|
):
|
|
234
262
|
continue
|
|
235
263
|
|
|
@@ -241,17 +269,8 @@ class ProjectAnalyzer:
|
|
|
241
269
|
if not self.file_filter.should_process(file_str):
|
|
242
270
|
continue
|
|
243
271
|
|
|
244
|
-
# Calculate module name from relative path
|
|
245
272
|
rel = os.path.relpath(file_str, project_str)
|
|
246
|
-
|
|
247
|
-
dir_parts = parts[:-1] # everything before filename
|
|
248
|
-
|
|
249
|
-
if filename in init_names:
|
|
250
|
-
module_name = '.'.join(dir_parts) if dir_parts else project_path.name
|
|
251
|
-
else:
|
|
252
|
-
stem = os.path.splitext(filename)[0]
|
|
253
|
-
module_name = '.'.join(dir_parts + [stem]) if dir_parts else stem
|
|
254
|
-
|
|
273
|
+
module_name = self._compute_module_name(rel, filename, project_name)
|
|
255
274
|
files.append((file_str, module_name))
|
|
256
275
|
|
|
257
276
|
return files
|
|
@@ -81,6 +81,10 @@ class PerformanceConfig:
|
|
|
81
81
|
fast_mode: bool = False
|
|
82
82
|
skip_data_flow: bool = False
|
|
83
83
|
skip_pattern_detection: bool = False
|
|
84
|
+
skip_refactoring_analysis: bool = False
|
|
85
|
+
skip_dead_code_detection: bool = True
|
|
86
|
+
skip_centrality: bool = False
|
|
87
|
+
skip_community_detection: bool = False
|
|
84
88
|
|
|
85
89
|
def get_workers(self) -> int:
|
|
86
90
|
"""Get effective worker count (auto-detect if set to 0)."""
|
|
@@ -88,6 +92,16 @@ class PerformanceConfig:
|
|
|
88
92
|
return _get_optimal_workers(default=4)
|
|
89
93
|
return self.parallel_workers
|
|
90
94
|
|
|
95
|
+
def apply_fast_mode(self) -> None:
|
|
96
|
+
"""Apply fast_mode overrides — skip expensive analyses."""
|
|
97
|
+
if not self.fast_mode:
|
|
98
|
+
return
|
|
99
|
+
self.skip_data_flow = True
|
|
100
|
+
self.skip_pattern_detection = True
|
|
101
|
+
self.skip_dead_code_detection = True
|
|
102
|
+
self.skip_centrality = True
|
|
103
|
+
self.skip_community_detection = True
|
|
104
|
+
|
|
91
105
|
|
|
92
106
|
@dataclass
|
|
93
107
|
class FilterConfig:
|
|
@@ -124,8 +124,9 @@ class FileAnalyzer:
|
|
|
124
124
|
# Calculate complexity with radon
|
|
125
125
|
self._calculate_complexity(content, file_path, result)
|
|
126
126
|
|
|
127
|
-
# Deep Analysis for refactoring
|
|
128
|
-
self.
|
|
127
|
+
# Deep Analysis for refactoring (skip when data flow not needed)
|
|
128
|
+
if not self.config.performance.skip_data_flow:
|
|
129
|
+
self._perform_deep_analysis(tree, module_name, file_path, result)
|
|
129
130
|
|
|
130
131
|
self.stats['files_processed'] += 1
|
|
131
132
|
return result
|
|
@@ -164,12 +165,18 @@ class FileAnalyzer:
|
|
|
164
165
|
result['mutations'] = dfg_res.mutations
|
|
165
166
|
result['data_flows'] = dfg_res.data_flows
|
|
166
167
|
|
|
167
|
-
# Update function calls from CG extractor which is more robust
|
|
168
|
+
# Update function calls from CG extractor which is more robust.
|
|
169
|
+
# Use set-based merge to avoid duplicate call entries (ast.walk
|
|
170
|
+
# in _process_function already extracts calls).
|
|
168
171
|
cg_ext = CallGraphExtractor(self.config)
|
|
169
172
|
cg_res = cg_ext.extract(tree, module_name, file_path)
|
|
170
173
|
for func_name, cg_func in cg_res.functions.items():
|
|
171
174
|
if func_name in result['functions']:
|
|
172
|
-
result['functions'][func_name].calls
|
|
175
|
+
existing = set(result['functions'][func_name].calls)
|
|
176
|
+
for c in cg_func.calls:
|
|
177
|
+
if c not in existing:
|
|
178
|
+
result['functions'][func_name].calls.append(c)
|
|
179
|
+
existing.add(c)
|
|
173
180
|
except Exception as e:
|
|
174
181
|
if self.config.verbose:
|
|
175
182
|
print(f"Error in deep analysis for {file_path}: {e}")
|