code2llm 0.5.159__tar.gz → 0.5.161__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.159/code2llm.egg-info → code2llm-0.5.161}/PKG-INFO +10 -16
- {code2llm-0.5.159 → code2llm-0.5.161}/README.md +9 -15
- code2llm-0.5.161/VERSION +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/__init__.py +1 -1
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/_data_impl.py +136 -104
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/call_graph.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/cfg.py +2 -3
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/coupling.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/dfg.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/pipeline_detector.py +18 -26
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/side_effects.py +42 -64
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/smells.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/type_inference.py +6 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_analysis.py +59 -86
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_commands.py +51 -65
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/formats.py +67 -56
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator.py +50 -68
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/prompt.py +47 -55
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/__init__.py +1 -4
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/analyzer.py +69 -40
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/export_pipeline.py +4 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/file_analyzer.py +62 -39
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/file_cache.py +5 -4
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/__init__.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/_c_parser.py +43 -18
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/base.py +3 -17
- code2llm-0.5.161/code2llm/core/lang/generic.py +71 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/ruby.py +65 -79
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/ts_parser.py +2 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/large_repo.py +8 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/persistent_cache.py +4 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/refactoring.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/source_classifier.py +16 -20
- code2llm-0.5.161/code2llm/core/streaming/__init__.py +7 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/streaming/incremental.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/streaming/prioritizer.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/streaming/scanner.py +41 -42
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/streaming_analyzer.py +42 -55
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/context_exporter.py +20 -1
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/context_view.py +12 -10
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/dashboard_data.py +3 -3
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution/yaml_export.py +30 -56
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/flow_exporter.py +6 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/flow_renderer.py +32 -35
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/html_dashboard.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/details.py +16 -24
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/calls.py +15 -13
- code2llm-0.5.161/code2llm/exporters/mermaid/compact.py +52 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid_flow_helpers.py +33 -11
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/planfile_tickets.py +54 -11
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/core.py +15 -32
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/health.py +30 -38
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/hotspots.py +57 -63
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/modules.py +18 -23
- code2llm-0.5.161/code2llm/exporters/readme/insights.py +58 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/report_generators.py +18 -10
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/__init__.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/_render_section_helpers.py +9 -4
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/helpers.py +42 -62
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/metrics.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/metrics_core.py +45 -51
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/metrics_duplicates.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/metrics_health.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/renderer.py +1 -1
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon_view.py +2 -1
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/validate_project.py +14 -21
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/analysis.py +31 -35
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/utils.py +19 -7
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_task.py +79 -46
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/mermaid/fix.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/mermaid/validation.py +19 -12
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/nlp/entity_resolution.py +20 -34
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/patterns/detector.py +1 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/refactor/prompt_engine.py +42 -36
- {code2llm-0.5.159 → code2llm-0.5.161/code2llm.egg-info}/PKG-INFO +10 -16
- {code2llm-0.5.159 → code2llm-0.5.161}/pyproject.toml +1 -1
- code2llm-0.5.159/VERSION +0 -1
- code2llm-0.5.159/code2llm/core/lang/generic.py +0 -88
- code2llm-0.5.159/code2llm/core/streaming/__init__.py +0 -7
- code2llm-0.5.159/code2llm/exporters/mermaid/compact.py +0 -69
- code2llm-0.5.159/code2llm/exporters/readme/insights.py +0 -54
- {code2llm-0.5.159 → code2llm-0.5.161}/LICENSE +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/MANIFEST.in +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/__main__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/api.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator_constants.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/cli_parser.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/config.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/_calls.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/_complexity.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/models.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/dashboard_renderer.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/index_generator/renderer.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/header.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map/yaml_export.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/readme/content.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/readme/sections.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/constants.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/mermaid/png.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/parsers/toon_parser.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/setup.cfg +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/setup.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_export_cache_flags.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_planfile_tickets_exporter.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.159 → code2llm-0.5.161}/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.161
|
|
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
|
|
@@ -66,16 +66,17 @@ Dynamic: requires-python
|
|
|
66
66
|
|
|
67
67
|
## AI Cost Tracking
|
|
68
68
|
|
|
69
|
-
     
|
|
70
|
+
  
|
|
71
71
|
|
|
72
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
73
|
-
- 👤 **Human dev:** ~$
|
|
72
|
+
- 🤖 **LLM usage:** $7.5000 (230 commits)
|
|
73
|
+
- 👤 **Human dev:** ~$8470 (84.7h @ $100/h, 30min dedup)
|
|
74
74
|
|
|
75
75
|
Generated on 2026-05-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
76
76
|
|
|
77
77
|
---
|
|
78
78
|
|
|
79
|
+
|
|
79
80
|
<!-- generated in 0.00s -->
|
|
80
81
|
|
|
81
82
|
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.
|
|
@@ -88,7 +89,6 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
88
89
|
|
|
89
90
|
| File | Format | Purpose | Key Insights |
|
|
90
91
|
|------|--------|---------|--------------|
|
|
91
|
-
| `planfile-tickets.yaml` | **YAML** | **🎫 Koru-ready ticket feed** - Actionable planfile suggestions from code2llm findings | Import or apply tickets for autonomous execution |
|
|
92
92
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
93
93
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
94
94
|
|
|
@@ -114,12 +114,6 @@ code2llm ./ -f toon
|
|
|
114
114
|
# Generate all formats (what created these files)
|
|
115
115
|
code2llm ./ -f all
|
|
116
116
|
|
|
117
|
-
# Generate planfile suggestions and create executable tickets for Koru
|
|
118
|
-
code2llm ./ -f all -o ./project --no-chunk --planfile-apply
|
|
119
|
-
|
|
120
|
-
# Or import the generated manifest later
|
|
121
|
-
planfile ticket import --from ./project/planfile-tickets.yaml --source code2llm
|
|
122
|
-
|
|
123
117
|
# LLM-ready context only
|
|
124
118
|
code2llm ./ -f context
|
|
125
119
|
```
|
|
@@ -413,10 +407,10 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
413
407
|
---
|
|
414
408
|
|
|
415
409
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
416
|
-
**Analysis Date**: 2026-05-
|
|
417
|
-
**Total Functions**:
|
|
418
|
-
**Total Classes**:
|
|
419
|
-
**Modules**:
|
|
410
|
+
**Analysis Date**: 2026-05-25
|
|
411
|
+
**Total Functions**: 1312
|
|
412
|
+
**Total Classes**: 143
|
|
413
|
+
**Modules**: 427
|
|
420
414
|
|
|
421
415
|
For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
|
|
422
416
|
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## AI Cost Tracking
|
|
4
4
|
|
|
5
|
-
     
|
|
6
|
+
  
|
|
7
7
|
|
|
8
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
9
|
-
- 👤 **Human dev:** ~$
|
|
8
|
+
- 🤖 **LLM usage:** $7.5000 (230 commits)
|
|
9
|
+
- 👤 **Human dev:** ~$8470 (84.7h @ $100/h, 30min dedup)
|
|
10
10
|
|
|
11
11
|
Generated on 2026-05-25 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
|
|
15
16
|
<!-- generated in 0.00s -->
|
|
16
17
|
|
|
17
18
|
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.
|
|
@@ -24,7 +25,6 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
24
25
|
|
|
25
26
|
| File | Format | Purpose | Key Insights |
|
|
26
27
|
|------|--------|---------|--------------|
|
|
27
|
-
| `planfile-tickets.yaml` | **YAML** | **🎫 Koru-ready ticket feed** - Actionable planfile suggestions from code2llm findings | Import or apply tickets for autonomous execution |
|
|
28
28
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
29
29
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
30
30
|
|
|
@@ -50,12 +50,6 @@ code2llm ./ -f toon
|
|
|
50
50
|
# Generate all formats (what created these files)
|
|
51
51
|
code2llm ./ -f all
|
|
52
52
|
|
|
53
|
-
# Generate planfile suggestions and create executable tickets for Koru
|
|
54
|
-
code2llm ./ -f all -o ./project --no-chunk --planfile-apply
|
|
55
|
-
|
|
56
|
-
# Or import the generated manifest later
|
|
57
|
-
planfile ticket import --from ./project/planfile-tickets.yaml --source code2llm
|
|
58
|
-
|
|
59
53
|
# LLM-ready context only
|
|
60
54
|
code2llm ./ -f context
|
|
61
55
|
```
|
|
@@ -349,10 +343,10 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
349
343
|
---
|
|
350
344
|
|
|
351
345
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
352
|
-
**Analysis Date**: 2026-05-
|
|
353
|
-
**Total Functions**:
|
|
354
|
-
**Total Classes**:
|
|
355
|
-
**Modules**:
|
|
346
|
+
**Analysis Date**: 2026-05-25
|
|
347
|
+
**Total Functions**: 1312
|
|
348
|
+
**Total Classes**: 143
|
|
349
|
+
**Modules**: 427
|
|
356
350
|
|
|
357
351
|
For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
|
|
358
352
|
|
code2llm-0.5.161/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.161
|
|
@@ -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.161"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -4,6 +4,26 @@ Extracted to keep data_analysis.py under the MI hard-gate threshold.
|
|
|
4
4
|
All functions here are module-private (leading underscore) and called
|
|
5
5
|
exclusively from DataAnalyzer in data_analysis.py.
|
|
6
6
|
"""
|
|
7
|
+
# ── Data pipeline helpers ─────────────────────────────────────────────────────
|
|
8
|
+
# _find_data_pipelines() : detect sequential transform chains from call graph.
|
|
9
|
+
# ── State-machine helpers ─────────────────────────────────────────────────────
|
|
10
|
+
# _is_state_func() : heuristic; True if name contains state keyword.
|
|
11
|
+
# _state_affected_by() : callers that mutate the given function's state.
|
|
12
|
+
# _find_state_patterns() : aggregate state-machine detections per module.
|
|
13
|
+
# ── Data-dependency helpers ───────────────────────────────────────────────────
|
|
14
|
+
# _find_data_dependencies() : build producer→consumer edges from shared types.
|
|
15
|
+
# ── Event-flow helpers ────────────────────────────────────────────────────────
|
|
16
|
+
# _is_event_func() : heuristic; True if name contains event keyword.
|
|
17
|
+
# _event_handlers() : functions that consume outputs of an event source.
|
|
18
|
+
# _find_event_flows() : aggregate event-flow detections across the project.
|
|
19
|
+
# ── Type-inference helpers ────────────────────────────────────────────────────
|
|
20
|
+
# _detect_types_from_name(): keyword-match on function name + docstring.
|
|
21
|
+
# _create_type_entry() : initialise a type-usage dict entry.
|
|
22
|
+
# _update_type_stats() : accumulate consumed/produced counts per type.
|
|
23
|
+
# _infer_parameter_types() : extract type hints from function parameters.
|
|
24
|
+
# _infer_return_types() : extract type hints from function return annotation.
|
|
25
|
+
# _analyze_data_types() : top-level driver; returns list of type-usage entries.
|
|
26
|
+
# _get_function_data_types(): merge name-based + annotation-based type signals.
|
|
7
27
|
|
|
8
28
|
from typing import Any, Dict, List
|
|
9
29
|
from code2llm.core.models import AnalysisResult
|
|
@@ -42,32 +62,38 @@ def _find_data_pipelines(result: AnalysisResult, categorize_fn, make_stage_fn, m
|
|
|
42
62
|
return pipelines
|
|
43
63
|
|
|
44
64
|
|
|
65
|
+
_STATE_INDICATORS = ["state", "status", "mode", "phase", "lifecycle", "session", "context"]
|
|
66
|
+
_TRANSITION_INDICATORS = ["transition", "change", "update", "set_state", "switch"]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _is_state_func(name_lower: str) -> bool:
|
|
70
|
+
"""Return True if the function name suggests state management."""
|
|
71
|
+
return any(ind in name_lower for ind in _STATE_INDICATORS + _TRANSITION_INDICATORS)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _state_affected_by(func, functions: dict) -> list:
|
|
75
|
+
"""Return calls that look like state-managing functions."""
|
|
76
|
+
return [
|
|
77
|
+
call for call in list(func.calls)[:10]
|
|
78
|
+
if (cf := functions.get(call)) and any(ind in cf.name.lower() for ind in _STATE_INDICATORS)
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
45
82
|
def _find_state_patterns(result: AnalysisResult) -> list:
|
|
46
83
|
"""Find state management patterns."""
|
|
47
84
|
patterns: list = []
|
|
48
|
-
state_indicators = ["state", "status", "mode", "phase", "lifecycle", "session", "context"]
|
|
49
|
-
transition_indicators = ["transition", "change", "update", "set_state", "switch"]
|
|
50
|
-
|
|
51
85
|
for func_name, func in result.functions.items():
|
|
52
86
|
name_lower = func.name.lower()
|
|
53
|
-
if
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if "set" in name_lower or "update" in name_lower
|
|
64
|
-
else "state_reader",
|
|
65
|
-
"affects_states": affected_states[:5],
|
|
66
|
-
"description": func.docstring[:150] if func.docstring else "N/A",
|
|
67
|
-
}
|
|
68
|
-
)
|
|
69
|
-
if len(patterns) >= 20:
|
|
70
|
-
break
|
|
87
|
+
if not _is_state_func(name_lower):
|
|
88
|
+
continue
|
|
89
|
+
patterns.append({
|
|
90
|
+
"function": func_name,
|
|
91
|
+
"type": "state_manager" if ("set" in name_lower or "update" in name_lower) else "state_reader",
|
|
92
|
+
"affects_states": _state_affected_by(func, result.functions)[:5],
|
|
93
|
+
"description": func.docstring[:150] if func.docstring else "N/A",
|
|
94
|
+
})
|
|
95
|
+
if len(patterns) >= 20:
|
|
96
|
+
break
|
|
71
97
|
return patterns
|
|
72
98
|
|
|
73
99
|
|
|
@@ -95,33 +121,43 @@ def _find_data_dependencies(result: AnalysisResult) -> list:
|
|
|
95
121
|
return deps[:15]
|
|
96
122
|
|
|
97
123
|
|
|
124
|
+
_EVENT_INDICATORS = ["event", "emit", "trigger", "notify", "callback", "handler", "listen", "subscribe"]
|
|
125
|
+
_HOOK_INDICATORS = ["hook", "on_", "before_", "after_", "pre_", "post_"]
|
|
126
|
+
_EVENT_HANDLER_WORDS = _EVENT_INDICATORS + ["handle", "process"]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _is_event_func(name_lower: str) -> bool:
|
|
130
|
+
"""Return True if the function name suggests event handling or emission."""
|
|
131
|
+
return any(ind in name_lower for ind in _EVENT_INDICATORS) or any(
|
|
132
|
+
name_lower.startswith(ind) for ind in _HOOK_INDICATORS
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _event_handlers(func, functions: dict) -> list:
|
|
137
|
+
"""Return list of called functions that look like event handlers."""
|
|
138
|
+
result = []
|
|
139
|
+
for called in list(func.calls)[:10]:
|
|
140
|
+
cf = functions.get(called)
|
|
141
|
+
if cf and any(ind in cf.name.lower() for ind in _EVENT_HANDLER_WORDS):
|
|
142
|
+
result.append(called)
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
98
146
|
def _find_event_flows(result: AnalysisResult) -> list:
|
|
99
147
|
"""Find event-driven patterns."""
|
|
100
148
|
flows: list = []
|
|
101
|
-
event_indicators = ["event", "emit", "trigger", "notify", "callback", "handler", "listen", "subscribe"]
|
|
102
|
-
hook_indicators = ["hook", "on_", "before_", "after_", "pre_", "post_"]
|
|
103
149
|
for func_name, func in result.functions.items():
|
|
104
150
|
name_lower = func.name.lower()
|
|
105
|
-
if
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
flows.append(
|
|
116
|
-
{
|
|
117
|
-
"event_source": func_name,
|
|
118
|
-
"type": "emitter" if "emit" in name_lower or "trigger" in name_lower else "handler",
|
|
119
|
-
"handlers": handlers[:5],
|
|
120
|
-
"description": func.docstring[:150] if func.docstring else "N/A",
|
|
121
|
-
}
|
|
122
|
-
)
|
|
123
|
-
if len(flows) >= 20:
|
|
124
|
-
break
|
|
151
|
+
if not _is_event_func(name_lower):
|
|
152
|
+
continue
|
|
153
|
+
flows.append({
|
|
154
|
+
"event_source": func_name,
|
|
155
|
+
"type": "emitter" if ("emit" in name_lower or "trigger" in name_lower) else "handler",
|
|
156
|
+
"handlers": _event_handlers(func, result.functions)[:5],
|
|
157
|
+
"description": func.docstring[:150] if func.docstring else "N/A",
|
|
158
|
+
})
|
|
159
|
+
if len(flows) >= 20:
|
|
160
|
+
break
|
|
125
161
|
return flows
|
|
126
162
|
|
|
127
163
|
|
|
@@ -222,26 +258,26 @@ def _analyze_data_types(result: AnalysisResult) -> list:
|
|
|
222
258
|
# ---------------------------------------------------------------------------
|
|
223
259
|
|
|
224
260
|
|
|
261
|
+
_NAME_TYPE_KEYWORDS: List[tuple] = [
|
|
262
|
+
("list", ("list", "items")),
|
|
263
|
+
("dict", ("dict", "map")),
|
|
264
|
+
("str", ("text", "string")),
|
|
265
|
+
("int", ("count", "index")),
|
|
266
|
+
]
|
|
267
|
+
_DOC_TYPE_KEYWORDS: List[tuple] = [
|
|
268
|
+
("list", ("list",)),
|
|
269
|
+
("dict", ("dict",)),
|
|
270
|
+
("str", ("string", "text")),
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
|
|
225
274
|
def _get_function_data_types(func) -> list:
|
|
226
275
|
"""Derive data type labels for a function from its name and docstring."""
|
|
227
|
-
types: List[str] = []
|
|
228
276
|
name = func.name.lower()
|
|
229
|
-
if
|
|
230
|
-
types.append("list")
|
|
231
|
-
if "dict" in name or "map" in name:
|
|
232
|
-
types.append("dict")
|
|
233
|
-
if "text" in name or "string" in name:
|
|
234
|
-
types.append("str")
|
|
235
|
-
if "count" in name or "index" in name:
|
|
236
|
-
types.append("int")
|
|
277
|
+
types = [dt for dt, kws in _NAME_TYPE_KEYWORDS if any(kw in name for kw in kws)]
|
|
237
278
|
if func.docstring:
|
|
238
279
|
doc = func.docstring.lower()
|
|
239
|
-
if
|
|
240
|
-
types.append("list")
|
|
241
|
-
if "dict" in doc:
|
|
242
|
-
types.append("dict")
|
|
243
|
-
if "string" in doc or "text" in doc:
|
|
244
|
-
types.append("str")
|
|
280
|
+
types += [dt for dt, kws in _DOC_TYPE_KEYWORDS if any(kw in doc for kw in kws)]
|
|
245
281
|
return list(set(types))
|
|
246
282
|
|
|
247
283
|
|
|
@@ -306,55 +342,51 @@ def _identify_process_patterns(result: AnalysisResult) -> list:
|
|
|
306
342
|
return sorted(res, key=lambda x: x["count"], reverse=True)
|
|
307
343
|
|
|
308
344
|
|
|
309
|
-
def
|
|
310
|
-
|
|
311
|
-
) -> dict:
|
|
312
|
-
"""Analyze optimization opportunities in data handling."""
|
|
313
|
-
opt: Dict[str, Any] = {
|
|
314
|
-
"potential_score": 0.0,
|
|
315
|
-
"type_consolidation": [],
|
|
316
|
-
"process_consolidation": [],
|
|
317
|
-
"hub_optimization": [],
|
|
318
|
-
"recommendations": [],
|
|
319
|
-
}
|
|
345
|
+
def _type_consolidations(data_types: list) -> list:
|
|
346
|
+
"""Find groups of similar data types that could be consolidated."""
|
|
320
347
|
similar: Dict[str, list] = {}
|
|
321
348
|
for dt in data_types:
|
|
322
349
|
sig = ",".join(sorted(dt["detected_types"]))
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
for _sig, sims in similar.items():
|
|
350
|
+
similar.setdefault(sig, []).append(dt)
|
|
351
|
+
result = []
|
|
352
|
+
for sig, sims in similar.items():
|
|
327
353
|
if len(sims) > 1:
|
|
328
354
|
usage = sum(s["usage_count"] for s in sims)
|
|
329
355
|
if usage > 10:
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return
|
|
356
|
+
result.append({"type_signature": sig, "similar_types": [s["type_name"] for s in sims],
|
|
357
|
+
"total_usage": usage, "potential_reduction": len(sims) - 1})
|
|
358
|
+
return result
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _process_consolidations(result: AnalysisResult) -> list:
|
|
362
|
+
"""Find repeated process patterns that could be consolidated."""
|
|
363
|
+
return [
|
|
364
|
+
{"pattern_type": p["pattern_type"], "function_count": p["count"],
|
|
365
|
+
"potential_reduction": p["count"] // 3}
|
|
366
|
+
for p in _identify_process_patterns(result) if p["count"] > 5
|
|
367
|
+
]
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _hub_optimizations(dfg: dict) -> list:
|
|
371
|
+
"""Identify hub nodes that are candidates for splitting or caching."""
|
|
372
|
+
return [
|
|
373
|
+
{"function": hub["id"], "connections": hub["in_degree"] + hub["out_degree"],
|
|
374
|
+
"optimization_type": "split" if hub["out_degree"] > 10 else "cache"}
|
|
375
|
+
for hub in list(n for n in dfg["nodes"].values() if n["is_hub"])[:10]
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _analyze_optimization_opportunities(
|
|
380
|
+
result: AnalysisResult, data_types: list, dfg: dict
|
|
381
|
+
) -> dict:
|
|
382
|
+
"""Analyze optimization opportunities in data handling."""
|
|
383
|
+
type_cons = _type_consolidations(data_types)
|
|
384
|
+
proc_cons = _process_consolidations(result)
|
|
385
|
+
hub_opts = _hub_optimizations(dfg)
|
|
386
|
+
return {
|
|
387
|
+
"potential_score": (len(type_cons) * 10 + len(proc_cons) * 15 + len(hub_opts) * 5) / 100.0,
|
|
388
|
+
"type_consolidation": type_cons,
|
|
389
|
+
"process_consolidation": proc_cons,
|
|
390
|
+
"hub_optimization": hub_opts,
|
|
391
|
+
"recommendations": [],
|
|
392
|
+
}
|
|
@@ -125,7 +125,6 @@ class CFGExtractor(ast.NodeVisitor):
|
|
|
125
125
|
# Visit then branch
|
|
126
126
|
then_last = []
|
|
127
127
|
for stmt in node.body:
|
|
128
|
-
prev = self.current_node
|
|
129
128
|
self.current_node = branch_entry
|
|
130
129
|
self.visit(stmt)
|
|
131
130
|
then_last.append(self.current_node)
|
|
@@ -136,7 +135,6 @@ class CFGExtractor(ast.NodeVisitor):
|
|
|
136
135
|
if node.orelse:
|
|
137
136
|
branch_entry = cond_node
|
|
138
137
|
for stmt in node.orelse:
|
|
139
|
-
prev = self.current_node
|
|
140
138
|
self.current_node = branch_entry
|
|
141
139
|
self.visit(stmt)
|
|
142
140
|
else_last.append(self.current_node)
|
|
@@ -270,10 +268,11 @@ class CFGExtractor(ast.NodeVisitor):
|
|
|
270
268
|
"""Extract condition as string."""
|
|
271
269
|
try:
|
|
272
270
|
return ast.unparse(node) if hasattr(ast, "unparse") else str(node)[:50]
|
|
273
|
-
except:
|
|
271
|
+
except Exception:
|
|
274
272
|
return str(node)[:50]
|
|
275
273
|
|
|
276
274
|
def _expr_to_str(self, node: ast.AST) -> str:
|
|
275
|
+
"""Unparse an AST expression node to a source string."""
|
|
277
276
|
return ast_unparse(node)
|
|
278
277
|
|
|
279
278
|
def _format_except(self, handler: ast.ExceptHandler) -> str:
|
|
@@ -191,6 +191,22 @@ class PipelineDetector:
|
|
|
191
191
|
# ------------------------------------------------------------------
|
|
192
192
|
# path finding
|
|
193
193
|
# ------------------------------------------------------------------
|
|
194
|
+
def _process_components(
|
|
195
|
+
self, graph: nx.DiGraph, paths: List[List[str]], used_nodes: Set[str]
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Extend paths with longest paths from each weakly connected component."""
|
|
198
|
+
for component in nx.weakly_connected_components(graph):
|
|
199
|
+
if len(component) < MIN_PIPELINE_LENGTH:
|
|
200
|
+
continue
|
|
201
|
+
if len(component & used_nodes) > len(component) * 0.5:
|
|
202
|
+
continue
|
|
203
|
+
path = self._longest_path_in_dag(graph.subgraph(component))
|
|
204
|
+
if len(path) >= MIN_PIPELINE_LENGTH:
|
|
205
|
+
new_overlap = sum(1 for n in path if n in used_nodes)
|
|
206
|
+
if new_overlap <= len(path) * 0.5:
|
|
207
|
+
paths.append(path)
|
|
208
|
+
used_nodes.update(path)
|
|
209
|
+
|
|
194
210
|
def _find_pipeline_paths(self, graph: nx.DiGraph) -> List[List[str]]:
|
|
195
211
|
"""Find longest paths in the call graph as pipeline candidates.
|
|
196
212
|
|
|
@@ -200,41 +216,17 @@ class PipelineDetector:
|
|
|
200
216
|
3. For each source, find longest simple path to any sink
|
|
201
217
|
4. Also consider longest paths in each weakly connected component
|
|
202
218
|
"""
|
|
203
|
-
paths: List[List[str]] = []
|
|
204
|
-
|
|
205
|
-
# Get source nodes (in-degree 0) as potential pipeline entry points
|
|
206
219
|
sources = [n for n in graph.nodes() if graph.in_degree(n) == 0]
|
|
207
|
-
|
|
208
|
-
# If no natural sources, use nodes with low in-degree
|
|
209
220
|
if not sources:
|
|
210
221
|
sources = sorted(graph.nodes(), key=lambda n: graph.in_degree(n))[:5]
|
|
211
|
-
|
|
212
|
-
# Try to find longest paths from each source
|
|
222
|
+
paths: List[List[str]] = []
|
|
213
223
|
used_nodes: Set[str] = set()
|
|
214
224
|
for source in sources:
|
|
215
225
|
best_path = self._longest_path_from(graph, source, used_nodes)
|
|
216
226
|
if len(best_path) >= MIN_PIPELINE_LENGTH:
|
|
217
227
|
paths.append(best_path)
|
|
218
228
|
used_nodes.update(best_path)
|
|
219
|
-
|
|
220
|
-
# Also try: for each weakly connected component, find the longest path
|
|
221
|
-
for component in nx.weakly_connected_components(graph):
|
|
222
|
-
if len(component) < MIN_PIPELINE_LENGTH:
|
|
223
|
-
continue
|
|
224
|
-
# Skip if heavily overlapping with existing paths
|
|
225
|
-
overlap = len(component & used_nodes)
|
|
226
|
-
if overlap > len(component) * 0.5:
|
|
227
|
-
continue
|
|
228
|
-
|
|
229
|
-
subgraph = graph.subgraph(component)
|
|
230
|
-
path = self._longest_path_in_dag(subgraph)
|
|
231
|
-
if len(path) >= MIN_PIPELINE_LENGTH:
|
|
232
|
-
# Check overlap with existing paths
|
|
233
|
-
new_overlap = sum(1 for n in path if n in used_nodes)
|
|
234
|
-
if new_overlap <= len(path) * 0.5:
|
|
235
|
-
paths.append(path)
|
|
236
|
-
used_nodes.update(path)
|
|
237
|
-
|
|
229
|
+
self._process_components(graph, paths, used_nodes)
|
|
238
230
|
return paths
|
|
239
231
|
|
|
240
232
|
def _longest_path_from(
|