code2llm 0.5.171__tar.gz → 0.5.173__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.171/code2llm.egg-info → code2llm-0.5.173}/PKG-INFO +7 -6
- {code2llm-0.5.171 → code2llm-0.5.173}/README.md +5 -5
- code2llm-0.5.173/VERSION +1 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/__init__.py +1 -1
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/smells.py +49 -15
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.171 → code2llm-0.5.173/code2llm.egg-info}/PKG-INFO +7 -6
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm.egg-info/SOURCES.txt +2 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm.egg-info/requires.txt +1 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/pyproject.toml +3 -2
- code2llm-0.5.173/tests/test_smells_data_clumps.py +83 -0
- code2llm-0.5.173/tests/test_smells_shotgun_surgery.py +97 -0
- code2llm-0.5.171/VERSION +0 -1
- {code2llm-0.5.171 → code2llm-0.5.173}/LICENSE +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/MANIFEST.in +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/__main__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/_data_impl.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/call_graph.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/cfg.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/api.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_analysis.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_commands.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/formats.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/orchestrator.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/orchestrator_constants.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/cli_parser.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/analyzer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/config.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/file_analyzer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/_c_parser.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/_calls.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/_complexity.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/ruby.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/models.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/persistent_cache.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/source_classifier.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/context_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/dashboard_data.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/dashboard_renderer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution/yaml_export.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/flow_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/index_generator/renderer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/details.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/header.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map/yaml_export.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/calls.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/compact.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/planfile_tickets.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/core.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/hotspots.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/readme/content.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/readme/insights.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/readme/sections.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/_render_section_helpers.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/constants.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/helpers.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/metrics_core.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/metrics_health.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/analysis.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow/utils.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/mermaid/fix.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/mermaid/png.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/mermaid/validation.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/parsers/toon_parser.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/setup.cfg +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/setup.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_export_cache_flags.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_planfile_tickets_exporter.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.171 → code2llm-0.5.173}/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.173
|
|
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: Tom Sapletta
|
|
@@ -32,6 +32,7 @@ Requires-Dist: numpy>=1.20
|
|
|
32
32
|
Requires-Dist: jinja2>=3.0
|
|
33
33
|
Requires-Dist: radon>=5.1
|
|
34
34
|
Requires-Dist: astroid>=3.0
|
|
35
|
+
Requires-Dist: psutil>=5.9
|
|
35
36
|
Requires-Dist: code2logic
|
|
36
37
|
Requires-Dist: vulture>=2.10
|
|
37
38
|
Requires-Dist: tiktoken>=0.5
|
|
@@ -66,13 +67,13 @@ Dynamic: requires-python
|
|
|
66
67
|
|
|
67
68
|
## AI Cost Tracking
|
|
68
69
|
|
|
69
|
-
    
|
|
71
|
+
  
|
|
71
72
|
|
|
72
|
-
- 🤖 **LLM usage:** $17.
|
|
73
|
-
- 👤 **Human dev:** ~$
|
|
73
|
+
- 🤖 **LLM usage:** $17.7289 (247 commits)
|
|
74
|
+
- 👤 **Human dev:** ~$9380 (93.8h @ $100/h, 30min dedup)
|
|
74
75
|
|
|
75
|
-
Generated on 2026-07-
|
|
76
|
+
Generated on 2026-07-06 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
|
|
76
77
|
|
|
77
78
|
---
|
|
78
79
|
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## AI Cost Tracking
|
|
4
4
|
|
|
5
|
-
    
|
|
6
|
+
  
|
|
7
7
|
|
|
8
|
-
- 🤖 **LLM usage:** $17.
|
|
9
|
-
- 👤 **Human dev:** ~$
|
|
8
|
+
- 🤖 **LLM usage:** $17.7289 (247 commits)
|
|
9
|
+
- 👤 **Human dev:** ~$9380 (93.8h @ $100/h, 30min dedup)
|
|
10
10
|
|
|
11
|
-
Generated on 2026-07-
|
|
11
|
+
Generated on 2026-07-06 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
code2llm-0.5.173/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.173
|
|
@@ -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.173"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -131,18 +131,34 @@ class SmellDetector:
|
|
|
131
131
|
return smells
|
|
132
132
|
|
|
133
133
|
def _detect_data_clumps(self) -> List[CodeSmell]:
|
|
134
|
-
"""Detect 3+ variables frequently passed together.
|
|
134
|
+
"""Detect 3+ variables frequently passed together.
|
|
135
|
+
|
|
136
|
+
Grouped by (file, argument-set), not argument-set alone: a common
|
|
137
|
+
constructor shape like `(self, project_root, config)` is a
|
|
138
|
+
conventional pattern reused independently by unrelated classes
|
|
139
|
+
across unrelated files (any two classes that each take a project
|
|
140
|
+
root and a config object) -- that's coincidence, not evidence the
|
|
141
|
+
two classes should share a bundled parameter object. Grouping by
|
|
142
|
+
bare argument-set alone falsely flagged such coincidences as data
|
|
143
|
+
clumps (reproduced: `wup.anomaly_detector.AnomalyDetector.__init__`
|
|
144
|
+
and `wup.testql_monitor.TestQLMonitor.__init__` -- two unrelated
|
|
145
|
+
classes in unrelated files -- flagged as clumped on `project_root,
|
|
146
|
+
self, config`). A genuine data clump -- args that should become one
|
|
147
|
+
object -- is a same-file (usually same-class) phenomenon; scoping
|
|
148
|
+
by file preserves that signal while dropping cross-file
|
|
149
|
+
coincidental-signature noise.
|
|
150
|
+
"""
|
|
135
151
|
smells = []
|
|
136
152
|
# Simplified: find functions with same 3+ arguments
|
|
137
|
-
arg_sets = {} # frozenset(args) -> List[func_names]
|
|
153
|
+
arg_sets = {} # (file, frozenset(args)) -> List[func_names]
|
|
138
154
|
for func_name, func_info in self.result.functions.items():
|
|
139
155
|
if len(func_info.args) >= 3:
|
|
140
|
-
|
|
141
|
-
if
|
|
142
|
-
arg_sets[
|
|
143
|
-
arg_sets[
|
|
156
|
+
key = (func_info.file, frozenset(func_info.args))
|
|
157
|
+
if key not in arg_sets:
|
|
158
|
+
arg_sets[key] = []
|
|
159
|
+
arg_sets[key].append(func_name)
|
|
144
160
|
|
|
145
|
-
for args, funcs in arg_sets.items():
|
|
161
|
+
for (file, args), funcs in arg_sets.items():
|
|
146
162
|
if len(funcs) >= 2:
|
|
147
163
|
for func_name in funcs:
|
|
148
164
|
func_info = self.result.functions[func_name]
|
|
@@ -163,16 +179,33 @@ class SmellDetector:
|
|
|
163
179
|
return smells
|
|
164
180
|
|
|
165
181
|
def _detect_shotgun_surgery(self) -> List[CodeSmell]:
|
|
166
|
-
"""Detect variables whose mutation requires changes across many functions.
|
|
182
|
+
"""Detect variables whose mutation requires changes across many functions.
|
|
183
|
+
|
|
184
|
+
Grouped by (file, variable), not variable name alone: a generic local
|
|
185
|
+
name like `args`, `data`, or `client` is conventionally reused as an
|
|
186
|
+
independent local in unrelated functions across unrelated files/
|
|
187
|
+
packages (every argparse `main()` has an `args`) -- that's naming
|
|
188
|
+
coincidence, not a variable whose logic is genuinely coupled across
|
|
189
|
+
call sites. Grouping by bare name alone falsely flagged every such
|
|
190
|
+
coincidence as "shotgun surgery" (reproduced: 40+ false-positive
|
|
191
|
+
tickets in a single monorepo, e.g. `args` in a 40-line, one-function
|
|
192
|
+
CLI file reported as "spans 8 functions" -- the other 7 were
|
|
193
|
+
unrelated `args` locals in unrelated files elsewhere in the tree).
|
|
194
|
+
Real shotgun surgery -- one variable/attribute whose change genuinely
|
|
195
|
+
ripples across many functions -- is a same-file (or same-class)
|
|
196
|
+
phenomenon; scoping by file preserves that signal while dropping the
|
|
197
|
+
cross-file name-collision noise.
|
|
198
|
+
"""
|
|
167
199
|
smells = []
|
|
168
|
-
var_mutators = {} # variable -> set(functions)
|
|
200
|
+
var_mutators = {} # (file, variable) -> set(functions)
|
|
169
201
|
|
|
170
202
|
for mutation in self.result.mutations:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
203
|
+
key = (mutation.file, mutation.variable)
|
|
204
|
+
if key not in var_mutators:
|
|
205
|
+
var_mutators[key] = set()
|
|
206
|
+
var_mutators[key].add(mutation.scope)
|
|
174
207
|
|
|
175
|
-
for var, funcs in var_mutators.items():
|
|
208
|
+
for (file, var), funcs in var_mutators.items():
|
|
176
209
|
if len(funcs) >= 5:
|
|
177
210
|
# Find a representative function to report the smell
|
|
178
211
|
func_name = list(funcs)[0]
|
|
@@ -188,10 +221,11 @@ class SmellDetector:
|
|
|
188
221
|
line=func_info.line,
|
|
189
222
|
severity=0.8,
|
|
190
223
|
description=(
|
|
191
|
-
f"Mutation of variable '{var}' spans {len(funcs)} functions
|
|
224
|
+
f"Mutation of variable '{var}' spans {len(funcs)} functions"
|
|
225
|
+
f" in {file}."
|
|
192
226
|
" Changing this logic requires work in many places."
|
|
193
227
|
),
|
|
194
|
-
context={"variable": var, "affected_functions": list(funcs)},
|
|
228
|
+
context={"variable": var, "file": file, "affected_functions": list(funcs)},
|
|
195
229
|
)
|
|
196
230
|
)
|
|
197
231
|
return smells
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.173
|
|
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: Tom Sapletta
|
|
@@ -32,6 +32,7 @@ Requires-Dist: numpy>=1.20
|
|
|
32
32
|
Requires-Dist: jinja2>=3.0
|
|
33
33
|
Requires-Dist: radon>=5.1
|
|
34
34
|
Requires-Dist: astroid>=3.0
|
|
35
|
+
Requires-Dist: psutil>=5.9
|
|
35
36
|
Requires-Dist: code2logic
|
|
36
37
|
Requires-Dist: vulture>=2.10
|
|
37
38
|
Requires-Dist: tiktoken>=0.5
|
|
@@ -66,13 +67,13 @@ Dynamic: requires-python
|
|
|
66
67
|
|
|
67
68
|
## AI Cost Tracking
|
|
68
69
|
|
|
69
|
-
    
|
|
71
|
+
  
|
|
71
72
|
|
|
72
|
-
- 🤖 **LLM usage:** $17.
|
|
73
|
-
- 👤 **Human dev:** ~$
|
|
73
|
+
- 🤖 **LLM usage:** $17.7289 (247 commits)
|
|
74
|
+
- 👤 **Human dev:** ~$9380 (93.8h @ $100/h, 30min dedup)
|
|
74
75
|
|
|
75
|
-
Generated on 2026-07-
|
|
76
|
+
Generated on 2026-07-06 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
|
|
76
77
|
|
|
77
78
|
---
|
|
78
79
|
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code2llm"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.173"
|
|
8
8
|
description = "High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
authors = [
|
|
12
12
|
{name = "Tom Sapletta", email = "tom@sapletta.com"},
|
|
13
13
|
]
|
|
@@ -45,6 +45,7 @@ dependencies = [
|
|
|
45
45
|
"jinja2>=3.0",
|
|
46
46
|
"radon>=5.1",
|
|
47
47
|
"astroid>=3.0",
|
|
48
|
+
"psutil>=5.9",
|
|
48
49
|
"code2logic",
|
|
49
50
|
"vulture>=2.10",
|
|
50
51
|
"tiktoken>=0.5",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Tests for SmellDetector._detect_data_clumps.
|
|
2
|
+
|
|
3
|
+
2026-07-06 incident (same root cause as the Shotgun Surgery fix): grouping
|
|
4
|
+
functions by their argument-name set alone, ignoring file, meant a common
|
|
5
|
+
constructor shape like `(self, project_root, config)` -- conventionally
|
|
6
|
+
reused independently by unrelated classes across unrelated files -- falsely
|
|
7
|
+
triggered "Data Clump". Reproduced live:
|
|
8
|
+
`wup.anomaly_detector.AnomalyDetector.__init__` and
|
|
9
|
+
`wup.testql_monitor.TestQLMonitor.__init__` are two unrelated classes in
|
|
10
|
+
unrelated files, flagged as clumped on `project_root, self, config` purely
|
|
11
|
+
because both happen to take a project root and a config object.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from code2llm.analysis.smells import SmellDetector
|
|
17
|
+
from code2llm.core.models import AnalysisResult, FunctionInfo
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _function(name: str, file: str, args: list[str], line: int = 1) -> FunctionInfo:
|
|
21
|
+
return FunctionInfo(
|
|
22
|
+
name=name, qualified_name=f"{file}::{name}", file=file, line=line, args=args
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_same_args_across_unrelated_files_is_not_a_data_clump():
|
|
27
|
+
"""The core regression: two unrelated classes in unrelated files each
|
|
28
|
+
taking (self, project_root, config) must NOT be flagged."""
|
|
29
|
+
result = AnalysisResult()
|
|
30
|
+
result.functions["a::AnomalyDetector.__init__"] = _function(
|
|
31
|
+
"AnomalyDetector.__init__", "wup/anomaly_detector.py", ["self", "project_root", "config"]
|
|
32
|
+
)
|
|
33
|
+
result.functions["b::TestQLMonitor.__init__"] = _function(
|
|
34
|
+
"TestQLMonitor.__init__", "wup/testql_monitor.py", ["self", "project_root", "config"]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
smells = SmellDetector(result)._detect_data_clumps()
|
|
38
|
+
|
|
39
|
+
assert smells == []
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_genuine_same_file_data_clump_still_detected():
|
|
43
|
+
"""Two methods in the SAME file sharing a 3+ arg signature is the real
|
|
44
|
+
smell and must still be caught."""
|
|
45
|
+
result = AnalysisResult()
|
|
46
|
+
file = "core/session.py"
|
|
47
|
+
result.functions["core.session::start"] = _function(
|
|
48
|
+
"start", file, ["self", "user_id", "token", "scope"]
|
|
49
|
+
)
|
|
50
|
+
result.functions["core.session::refresh"] = _function(
|
|
51
|
+
"refresh", file, ["self", "user_id", "token", "scope"]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
smells = SmellDetector(result)._detect_data_clumps()
|
|
55
|
+
|
|
56
|
+
assert len(smells) == 2
|
|
57
|
+
assert all(s.name == "Data Clump: self, user_id, token, scope"
|
|
58
|
+
or set(s.context["clump"]) == {"self", "user_id", "token", "scope"}
|
|
59
|
+
for s in smells)
|
|
60
|
+
assert all(s.file == file for s in smells)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_single_function_with_matching_args_in_other_file_not_flagged():
|
|
64
|
+
"""Only one function per file matches the arg-set -- no clump within
|
|
65
|
+
either file, and cross-file coincidence must not count."""
|
|
66
|
+
result = AnalysisResult()
|
|
67
|
+
result.functions["a::f"] = _function("f", "a.py", ["self", "x", "y", "z"])
|
|
68
|
+
result.functions["b::g"] = _function("g", "b.py", ["self", "x", "y", "z"])
|
|
69
|
+
|
|
70
|
+
smells = SmellDetector(result)._detect_data_clumps()
|
|
71
|
+
|
|
72
|
+
assert smells == []
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_fewer_than_three_args_never_counted():
|
|
76
|
+
result = AnalysisResult()
|
|
77
|
+
file = "core/session.py"
|
|
78
|
+
result.functions["core.session::start"] = _function("start", file, ["self", "user_id"])
|
|
79
|
+
result.functions["core.session::refresh"] = _function("refresh", file, ["self", "user_id"])
|
|
80
|
+
|
|
81
|
+
smells = SmellDetector(result)._detect_data_clumps()
|
|
82
|
+
|
|
83
|
+
assert smells == []
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Tests for SmellDetector._detect_shotgun_surgery.
|
|
2
|
+
|
|
3
|
+
2026-07-06 incident: grouping mutations by variable *name* alone (ignoring
|
|
4
|
+
file) meant a generic local name like `args`/`data`/`client` -- conventionally
|
|
5
|
+
reused as an independent local in unrelated functions across unrelated
|
|
6
|
+
files -- falsely triggered "Shotgun Surgery" whenever 5+ unrelated functions
|
|
7
|
+
anywhere in the analyzed tree happened to share that name. Reproduced live:
|
|
8
|
+
40+ false-positive tickets in one monorepo, including `args` in a 40-line,
|
|
9
|
+
one-function CLI file reported as "spans 8 functions" (the other 7 were
|
|
10
|
+
unrelated `args` locals in unrelated files elsewhere in the tree).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from code2llm.analysis.smells import SmellDetector
|
|
16
|
+
from code2llm.core.models import AnalysisResult, FunctionInfo, Mutation
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _function(name: str, file: str, line: int = 1) -> FunctionInfo:
|
|
20
|
+
return FunctionInfo(name=name, qualified_name=f"{file}::{name}", file=file, line=line)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _mutation(variable: str, file: str, scope: str, line: int = 1) -> Mutation:
|
|
24
|
+
return Mutation(
|
|
25
|
+
variable=variable, file=file, line=line, type="assign", scope=scope, context=""
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_same_name_across_unrelated_files_is_not_shotgun_surgery():
|
|
30
|
+
"""The core regression: `args` reused independently in 6 unrelated
|
|
31
|
+
files/functions must NOT be flagged -- no single file has more than one
|
|
32
|
+
mutating function."""
|
|
33
|
+
result = AnalysisResult()
|
|
34
|
+
for i in range(6):
|
|
35
|
+
file = f"pkg{i}/cli.py"
|
|
36
|
+
scope = f"pkg{i}.cli::main"
|
|
37
|
+
result.functions[scope] = _function("main", file)
|
|
38
|
+
result.mutations.append(_mutation("args", file, scope))
|
|
39
|
+
|
|
40
|
+
smells = SmellDetector(result)._detect_shotgun_surgery()
|
|
41
|
+
|
|
42
|
+
assert smells == []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_genuine_same_file_shotgun_surgery_still_detected():
|
|
46
|
+
"""5+ functions *within the same file* mutating the same variable is the
|
|
47
|
+
real smell and must still be caught."""
|
|
48
|
+
result = AnalysisResult()
|
|
49
|
+
file = "core/session.py"
|
|
50
|
+
for i in range(5):
|
|
51
|
+
scope = f"core.session::step_{i}"
|
|
52
|
+
result.functions[scope] = _function(f"step_{i}", file, line=10 * i)
|
|
53
|
+
result.mutations.append(_mutation("state", file, scope))
|
|
54
|
+
|
|
55
|
+
smells = SmellDetector(result)._detect_shotgun_surgery()
|
|
56
|
+
|
|
57
|
+
assert len(smells) == 1
|
|
58
|
+
assert smells[0].name == "Shotgun Surgery: state"
|
|
59
|
+
assert set(smells[0].context["affected_functions"]) == {
|
|
60
|
+
f"core.session::step_{i}" for i in range(5)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_four_functions_in_one_file_below_threshold():
|
|
65
|
+
result = AnalysisResult()
|
|
66
|
+
file = "core/session.py"
|
|
67
|
+
for i in range(4):
|
|
68
|
+
scope = f"core.session::step_{i}"
|
|
69
|
+
result.functions[scope] = _function(f"step_{i}", file)
|
|
70
|
+
result.mutations.append(_mutation("state", file, scope))
|
|
71
|
+
|
|
72
|
+
smells = SmellDetector(result)._detect_shotgun_surgery()
|
|
73
|
+
|
|
74
|
+
assert smells == []
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_same_variable_name_in_two_files_counts_separately_per_file():
|
|
78
|
+
"""5 mutators of 'data' in file A (real smell) plus unrelated mutators of
|
|
79
|
+
'data' in file B must not combine into a bigger, less accurate count,
|
|
80
|
+
and file B (below threshold) must not itself be flagged."""
|
|
81
|
+
result = AnalysisResult()
|
|
82
|
+
file_a = "a.py"
|
|
83
|
+
for i in range(5):
|
|
84
|
+
scope = f"a::fn_{i}"
|
|
85
|
+
result.functions[scope] = _function(f"fn_{i}", file_a)
|
|
86
|
+
result.mutations.append(_mutation("data", file_a, scope))
|
|
87
|
+
|
|
88
|
+
file_b = "b.py"
|
|
89
|
+
for i in range(3):
|
|
90
|
+
scope = f"b::fn_{i}"
|
|
91
|
+
result.functions[scope] = _function(f"fn_{i}", file_b)
|
|
92
|
+
result.mutations.append(_mutation("data", file_b, scope))
|
|
93
|
+
|
|
94
|
+
smells = SmellDetector(result)._detect_shotgun_surgery()
|
|
95
|
+
|
|
96
|
+
assert len(smells) == 1
|
|
97
|
+
assert smells[0].context["file"] == file_a
|
code2llm-0.5.171/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.5.171
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|