code2llm 0.5.160__tar.gz → 0.5.162__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.162}/PKG-INFO +9 -16
- {code2llm-0.5.160 → code2llm-0.5.162}/README.md +8 -15
- code2llm-0.5.162/VERSION +1 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/__init__.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/_data_impl.py +121 -89
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/call_graph.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/cfg.py +2 -3
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/dfg.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/pipeline_detector.py +19 -27
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/side_effects.py +21 -20
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/smells.py +32 -7
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/type_inference.py +6 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_analysis.py +42 -67
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_commands.py +51 -65
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/formats.py +48 -34
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator.py +18 -20
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/prompt.py +91 -35
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_parser.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/__init__.py +1 -4
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/analyzer.py +68 -40
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/export_pipeline.py +4 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/file_analyzer.py +61 -39
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/file_cache.py +5 -4
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/__init__.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/_c_parser.py +41 -13
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/base.py +3 -17
- code2llm-0.5.162/code2llm/core/lang/generic.py +71 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/ruby.py +65 -79
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/ts_parser.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/typescript.py +4 -4
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/large_repo.py +8 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/persistent_cache.py +4 -0
- code2llm-0.5.162/code2llm/core/streaming/__init__.py +7 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/scanner.py +41 -42
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming_analyzer.py +42 -55
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/toon_size_manager.py +3 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/context_exporter.py +20 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/context_view.py +12 -10
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/dashboard_data.py +4 -4
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/dashboard_renderer.py +10 -10
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/yaml_export.py +30 -56
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/flow_exporter.py +5 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/flow_renderer.py +32 -35
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/html_dashboard.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator/renderer.py +9 -9
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/header.py +2 -2
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/yaml_export.py +4 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid_flow_helpers.py +33 -11
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/planfile_tickets.py +43 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/hotspots.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/modules.py +18 -23
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/content.py +4 -4
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/sections.py +6 -6
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/report_generators.py +18 -10
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/_render_section_helpers.py +9 -4
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/helpers.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics_core.py +45 -51
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics_health.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/renderer.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon_view.py +2 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/validate_project.py +14 -21
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/yaml_exporter.py +10 -10
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/analysis.py +31 -35
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/utils.py +19 -7
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow.py +0 -2
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_task.py +79 -46
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/fix.py +1 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/png.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/validation.py +19 -12
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/entity_resolution.py +20 -34
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/parsers/toon_parser.py +1 -1
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/refactor/prompt_engine.py +46 -42
- {code2llm-0.5.160 → code2llm-0.5.162/code2llm.egg-info}/PKG-INFO +9 -16
- {code2llm-0.5.160 → code2llm-0.5.162}/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.162}/LICENSE +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/MANIFEST.in +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/__main__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/api.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator_constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/config.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/_calls.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/_complexity.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/models.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/source_classifier.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/details.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/calls.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/compact.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/core.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme/insights.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/constants.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/setup.cfg +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/setup.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_export_cache_flags.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_planfile_tickets_exporter.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.160 → code2llm-0.5.162}/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.162
|
|
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,11 +66,11 @@ 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 (233 commits)
|
|
73
|
+
- 👤 **Human dev:** ~$8506 (85.1h @ $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
|
|
|
@@ -88,7 +88,6 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
88
88
|
|
|
89
89
|
| File | Format | Purpose | Key Insights |
|
|
90
90
|
|------|--------|---------|--------------|
|
|
91
|
-
| `planfile-tickets.yaml` | **YAML** | **🎫 Koru-ready ticket feed** - Actionable planfile suggestions from code2llm findings | Import or apply tickets for autonomous execution |
|
|
92
91
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
93
92
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
94
93
|
|
|
@@ -114,12 +113,6 @@ code2llm ./ -f toon
|
|
|
114
113
|
# Generate all formats (what created these files)
|
|
115
114
|
code2llm ./ -f all
|
|
116
115
|
|
|
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
116
|
# LLM-ready context only
|
|
124
117
|
code2llm ./ -f context
|
|
125
118
|
```
|
|
@@ -413,10 +406,10 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
413
406
|
---
|
|
414
407
|
|
|
415
408
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
416
|
-
**Analysis Date**: 2026-05-
|
|
417
|
-
**Total Functions**:
|
|
418
|
-
**Total Classes**:
|
|
419
|
-
**Modules**:
|
|
409
|
+
**Analysis Date**: 2026-05-25
|
|
410
|
+
**Total Functions**: 1312
|
|
411
|
+
**Total Classes**: 143
|
|
412
|
+
**Modules**: 427
|
|
420
413
|
|
|
421
414
|
For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
|
|
422
415
|
|
|
@@ -2,11 +2,11 @@
|
|
|
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 (233 commits)
|
|
9
|
+
- 👤 **Human dev:** ~$8506 (85.1h @ $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
|
|
|
@@ -24,7 +24,6 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
24
24
|
|
|
25
25
|
| File | Format | Purpose | Key Insights |
|
|
26
26
|
|------|--------|---------|--------------|
|
|
27
|
-
| `planfile-tickets.yaml` | **YAML** | **🎫 Koru-ready ticket feed** - Actionable planfile suggestions from code2llm findings | Import or apply tickets for autonomous execution |
|
|
28
27
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
29
28
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
30
29
|
|
|
@@ -50,12 +49,6 @@ code2llm ./ -f toon
|
|
|
50
49
|
# Generate all formats (what created these files)
|
|
51
50
|
code2llm ./ -f all
|
|
52
51
|
|
|
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
52
|
# LLM-ready context only
|
|
60
53
|
code2llm ./ -f context
|
|
61
54
|
```
|
|
@@ -349,10 +342,10 @@ code2llm ./ -f yaml --separate-orphans
|
|
|
349
342
|
---
|
|
350
343
|
|
|
351
344
|
**Generated by**: `code2llm ./ -f all --readme`
|
|
352
|
-
**Analysis Date**: 2026-05-
|
|
353
|
-
**Total Functions**:
|
|
354
|
-
**Total Classes**:
|
|
355
|
-
**Modules**:
|
|
345
|
+
**Analysis Date**: 2026-05-25
|
|
346
|
+
**Total Functions**: 1312
|
|
347
|
+
**Total Classes**: 143
|
|
348
|
+
**Modules**: 427
|
|
356
349
|
|
|
357
350
|
For more information about code2llm, visit: https://github.com/tom-sapletta/code2llm
|
|
358
351
|
|
code2llm-0.5.162/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.162
|
|
@@ -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.162"
|
|
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:
|
|
@@ -13,7 +13,7 @@ Refactored v0.5.x: Extracted resolver and classifier into separate modules.
|
|
|
13
13
|
|
|
14
14
|
import logging
|
|
15
15
|
from dataclasses import dataclass, field
|
|
16
|
-
from typing import Any, Dict, List, Optional, Set
|
|
16
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
17
17
|
|
|
18
18
|
import networkx as nx
|
|
19
19
|
|
|
@@ -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
|
|
|
@@ -56,7 +56,11 @@ class SmellDetector:
|
|
|
56
56
|
file=func_info.file,
|
|
57
57
|
line=func_info.line,
|
|
58
58
|
severity=severity,
|
|
59
|
-
description=
|
|
59
|
+
description=(
|
|
60
|
+
f"Function '{func_info.name}' is oversized:"
|
|
61
|
+
f" CC={complexity}, fan-out={fan_out},"
|
|
62
|
+
f" mutations={mutation_count}."
|
|
63
|
+
),
|
|
60
64
|
context={
|
|
61
65
|
"fan_out": fan_out,
|
|
62
66
|
"mutations": mutation_count,
|
|
@@ -85,7 +89,11 @@ class SmellDetector:
|
|
|
85
89
|
file=mod.file,
|
|
86
90
|
line=1,
|
|
87
91
|
severity=severity,
|
|
88
|
-
description=
|
|
92
|
+
description=(
|
|
93
|
+
f"Module '{mod_name}' is too large"
|
|
94
|
+
f" ({f_count} functions, {c_count} classes)."
|
|
95
|
+
" Consider splitting into sub-modules."
|
|
96
|
+
),
|
|
89
97
|
context={"functions": f_count, "classes": c_count},
|
|
90
98
|
)
|
|
91
99
|
)
|
|
@@ -113,7 +121,10 @@ class SmellDetector:
|
|
|
113
121
|
file=func_info.file,
|
|
114
122
|
line=func_info.line,
|
|
115
123
|
severity=0.7,
|
|
116
|
-
description=
|
|
124
|
+
description=(
|
|
125
|
+
f"Function '{func_info.name}' mutates multiple variables"
|
|
126
|
+
f" in other modules: {', '.join(set(foreign_mutations))}."
|
|
127
|
+
),
|
|
117
128
|
context={"foreign_mutations": list(set(foreign_mutations))},
|
|
118
129
|
)
|
|
119
130
|
)
|
|
@@ -142,7 +153,10 @@ class SmellDetector:
|
|
|
142
153
|
file=func_info.file,
|
|
143
154
|
line=func_info.line,
|
|
144
155
|
severity=0.6,
|
|
145
|
-
description=
|
|
156
|
+
description=(
|
|
157
|
+
f"Arguments ({', '.join(args)}) are used together"
|
|
158
|
+
f" in multiple functions: {', '.join(funcs)}."
|
|
159
|
+
),
|
|
146
160
|
context={"clump": list(args), "related_functions": funcs},
|
|
147
161
|
)
|
|
148
162
|
)
|
|
@@ -173,7 +187,10 @@ class SmellDetector:
|
|
|
173
187
|
file=func_info.file,
|
|
174
188
|
line=func_info.line,
|
|
175
189
|
severity=0.8,
|
|
176
|
-
description=
|
|
190
|
+
description=(
|
|
191
|
+
f"Mutation of variable '{var}' spans {len(funcs)} functions."
|
|
192
|
+
" Changing this logic requires work in many places."
|
|
193
|
+
),
|
|
177
194
|
context={"variable": var, "affected_functions": list(funcs)},
|
|
178
195
|
)
|
|
179
196
|
)
|
|
@@ -192,7 +209,11 @@ class SmellDetector:
|
|
|
192
209
|
file=func_info.file,
|
|
193
210
|
line=func_info.line,
|
|
194
211
|
severity=min(1.0, func_info.centrality * 5),
|
|
195
|
-
description=
|
|
212
|
+
description=(
|
|
213
|
+
f"Function '{func_info.name}' is a structural bottleneck"
|
|
214
|
+
f" (centrality={round(func_info.centrality, 3)})."
|
|
215
|
+
" Significant logic flows through this function."
|
|
216
|
+
),
|
|
196
217
|
context={"centrality": func_info.centrality},
|
|
197
218
|
)
|
|
198
219
|
)
|
|
@@ -218,7 +239,11 @@ class SmellDetector:
|
|
|
218
239
|
file=func_info.file,
|
|
219
240
|
line=func_info.line,
|
|
220
241
|
severity=0.8,
|
|
221
|
-
description=
|
|
242
|
+
description=(
|
|
243
|
+
f"Circular dependency detected: {' -> '.join(cycle)}."
|
|
244
|
+
" This indicates high coupling and may lead to"
|
|
245
|
+
" infinite recursion or initialization issues."
|
|
246
|
+
),
|
|
222
247
|
context={"cycle": cycle},
|
|
223
248
|
)
|
|
224
249
|
)
|