code2llm 0.5.160__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.160/code2llm.egg-info → code2llm-0.5.161}/PKG-INFO +10 -16
- {code2llm-0.5.160 → code2llm-0.5.161}/README.md +9 -15
- code2llm-0.5.161/VERSION +1 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/__init__.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/_data_impl.py +121 -89
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/call_graph.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/cfg.py +2 -3
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/dfg.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/pipeline_detector.py +18 -26
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/side_effects.py +21 -20
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/type_inference.py +6 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_analysis.py +42 -67
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_commands.py +51 -65
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/formats.py +46 -33
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator.py +18 -20
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/prompt.py +17 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/__init__.py +1 -4
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/analyzer.py +68 -40
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/export_pipeline.py +4 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/file_analyzer.py +61 -39
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/file_cache.py +5 -4
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/__init__.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/_c_parser.py +41 -13
- {code2llm-0.5.160 → 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.160 → code2llm-0.5.161}/code2llm/core/lang/ruby.py +65 -79
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/large_repo.py +8 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/persistent_cache.py +4 -0
- code2llm-0.5.161/code2llm/core/streaming/__init__.py +7 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/streaming/scanner.py +41 -42
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/streaming_analyzer.py +42 -55
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/context_exporter.py +20 -1
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/context_view.py +12 -10
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/dashboard_data.py +3 -3
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution/yaml_export.py +30 -56
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/flow_exporter.py +5 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/flow_renderer.py +32 -35
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/html_dashboard.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid_flow_helpers.py +33 -11
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/planfile_tickets.py +43 -1
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/modules.py +18 -23
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/report_generators.py +18 -10
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/_render_section_helpers.py +9 -4
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/metrics_core.py +45 -51
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/renderer.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon_view.py +2 -1
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/validate_project.py +14 -21
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/analysis.py +31 -35
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/utils.py +19 -7
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_task.py +79 -46
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/mermaid/fix.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/mermaid/validation.py +19 -12
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/nlp/entity_resolution.py +20 -34
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/refactor/prompt_engine.py +41 -36
- {code2llm-0.5.160 → code2llm-0.5.161/code2llm.egg-info}/PKG-INFO +10 -16
- {code2llm-0.5.160 → code2llm-0.5.161}/pyproject.toml +1 -1
- code2llm-0.5.160/VERSION +0 -1
- code2llm-0.5.160/code2llm/core/lang/generic.py +0 -88
- code2llm-0.5.160/code2llm/core/streaming/__init__.py +0 -7
- {code2llm-0.5.160 → code2llm-0.5.161}/LICENSE +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/MANIFEST.in +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/__main__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/api.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator_constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/cli_parser.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/config.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/_calls.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/_complexity.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/models.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/source_classifier.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/dashboard_renderer.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/index_generator/renderer.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/details.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/header.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map/yaml_export.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/calls.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/compact.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/core.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml/hotspots.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/readme/content.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/readme/insights.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/readme/sections.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/helpers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/metrics_health.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/mermaid/png.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/parsers/toon_parser.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/setup.cfg +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/setup.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_export_cache_flags.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_planfile_tickets_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.161}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.160 → 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
|
|
|
@@ -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(
|
|
@@ -241,36 +241,37 @@ class SideEffectDetector:
|
|
|
241
241
|
self._check_yield(node, info)
|
|
242
242
|
self._check_delete(node, info)
|
|
243
243
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
call_name = self._get_call_name(node.func)
|
|
250
|
-
if not call_name:
|
|
251
|
-
return
|
|
252
|
-
|
|
253
|
-
parts = call_name.split(".")
|
|
254
|
-
base_name = parts[-1]
|
|
255
|
-
|
|
256
|
-
# IO detection
|
|
244
|
+
@staticmethod
|
|
245
|
+
def _check_io_call(
|
|
246
|
+
base_name: str, parts: list, call_name: str, info: "SideEffectInfo"
|
|
247
|
+
) -> None:
|
|
248
|
+
"""Classify call as IO if it matches known IO patterns."""
|
|
257
249
|
if base_name in IO_CALLS:
|
|
258
250
|
info.io_operations.append(base_name)
|
|
259
|
-
elif base_name in HTTP_METHODS and len(parts) >= 2:
|
|
260
|
-
|
|
261
|
-
caller = parts[-2].lower()
|
|
262
|
-
if caller in HTTP_CALLERS:
|
|
263
|
-
info.io_operations.append(call_name)
|
|
251
|
+
elif base_name in HTTP_METHODS and len(parts) >= 2 and parts[-2].lower() in HTTP_CALLERS:
|
|
252
|
+
info.io_operations.append(call_name)
|
|
264
253
|
elif base_name in IO_ATTRIBUTES:
|
|
265
254
|
info.io_operations.append(call_name)
|
|
266
255
|
|
|
267
|
-
|
|
256
|
+
@staticmethod
|
|
257
|
+
def _check_cache_call(base_name: str, call_name: str, info: "SideEffectInfo") -> None:
|
|
258
|
+
"""Classify call as cache operation if it matches known cache patterns."""
|
|
268
259
|
if base_name in CACHE_CALLS:
|
|
269
260
|
info.cache_operations.append(base_name)
|
|
270
261
|
elif any(ci in call_name for ci in CACHE_INDICATORS):
|
|
271
262
|
info.cache_operations.append(call_name)
|
|
272
263
|
|
|
273
|
-
|
|
264
|
+
def _check_calls(self, node: ast.AST, info: SideEffectInfo) -> None:
|
|
265
|
+
"""Detect IO and cache calls."""
|
|
266
|
+
if not isinstance(node, ast.Call):
|
|
267
|
+
return
|
|
268
|
+
call_name = self._get_call_name(node.func)
|
|
269
|
+
if not call_name:
|
|
270
|
+
return
|
|
271
|
+
parts = call_name.split(".")
|
|
272
|
+
base_name = parts[-1]
|
|
273
|
+
self._check_io_call(base_name, parts, call_name, info)
|
|
274
|
+
self._check_cache_call(base_name, call_name, info)
|
|
274
275
|
if base_name in MUTATION_CALLS and len(parts) >= 2:
|
|
275
276
|
info.mutations.append(call_name)
|
|
276
277
|
|
|
@@ -221,25 +221,31 @@ class TypeInferenceEngine:
|
|
|
221
221
|
return None
|
|
222
222
|
|
|
223
223
|
def _ann_constant(self, node: ast.Constant) -> str:
|
|
224
|
+
"""Convert a Constant annotation node to its string representation."""
|
|
224
225
|
return str(node.value)
|
|
225
226
|
|
|
226
227
|
def _ann_name(self, node: ast.Name) -> str:
|
|
228
|
+
"""Return the identifier string from a Name annotation node."""
|
|
227
229
|
return node.id
|
|
228
230
|
|
|
229
231
|
def _ann_attribute(self, node: ast.Attribute) -> str:
|
|
232
|
+
"""Convert an Attribute annotation node (e.g. typing.Optional) to dotted string."""
|
|
230
233
|
value = self._annotation_to_str(node.value)
|
|
231
234
|
return f"{value}.{node.attr}" if value else node.attr
|
|
232
235
|
|
|
233
236
|
def _ann_subscript(self, node: ast.Subscript) -> Optional[str]:
|
|
237
|
+
"""Convert a Subscript annotation node (e.g. List[str]) to string."""
|
|
234
238
|
base = self._annotation_to_str(node.value)
|
|
235
239
|
slice_str = self._annotation_to_str(node.slice)
|
|
236
240
|
return f"{base}[{slice_str}]" if (base and slice_str) else base
|
|
237
241
|
|
|
238
242
|
def _ann_tuple(self, node: ast.Tuple) -> str:
|
|
243
|
+
"""Convert a Tuple annotation node (e.g. Tuple[int, str]) to comma-separated string."""
|
|
239
244
|
parts = [self._annotation_to_str(e) for e in node.elts]
|
|
240
245
|
return ", ".join(p for p in parts if p)
|
|
241
246
|
|
|
242
247
|
def _ann_binop(self, node: ast.BinOp) -> Optional[str]:
|
|
248
|
+
"""Convert a BinOp annotation node (X | Y union syntax) to string."""
|
|
243
249
|
left = self._annotation_to_str(node.left)
|
|
244
250
|
right = self._annotation_to_str(node.right)
|
|
245
251
|
return f"{left} | {right}" if (left and right) else None
|