code2llm 0.5.183__tar.gz → 0.5.185__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.183/code2llm.egg-info → code2llm-0.5.185}/PKG-INFO +2 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/README.md +1 -0
- code2llm-0.5.185/VERSION +1 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/__init__.py +1 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/smell_engine.py +11 -6
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/formats.py +11 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/orchestrator_constants.py +3 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_parser.py +1 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/__init__.py +8 -0
- code2llm-0.5.185/code2llm/exporters/projections_exporter.py +139 -0
- {code2llm-0.5.183 → code2llm-0.5.185/code2llm.egg-info}/PKG-INFO +2 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm.egg-info/SOURCES.txt +3 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm.egg-info/top_level.txt +0 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/pyproject.toml +1 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/setup.py +1 -1
- code2llm-0.5.185/tests/test_analysis_env.py +46 -0
- code2llm-0.5.185/tests/test_projections_exporter.py +130 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_prompt_engine.py +2 -2
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_smells_data_clumps.py +51 -0
- code2llm-0.5.183/VERSION +0 -1
- {code2llm-0.5.183 → code2llm-0.5.185}/LICENSE +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/MANIFEST.in +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/__main__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/_data_impl.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/call_graph.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/call_graph_engine.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/cfg.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/complexity.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/coupling.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/native_bridge.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/side_effects.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/smells.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/api.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/autoupdate.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_analysis.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_commands.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/orchestrator.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/cli_exports/prompt.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/analyzer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/config.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/file_analyzer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/_c_parser.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/_calls.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/_complexity.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/js_analysis.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/ruby.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/ts_parser.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/models.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/persistent_cache.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/refactoring.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/source_classifier.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming/incremental.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming/prioritizer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/base.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/context_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/dashboard_data.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/dashboard_renderer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution/yaml_export.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/flow_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/index_generator/renderer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/details.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/header.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map/yaml_export.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/calls.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/compact.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/planfile_tickets.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/core.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/health.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/hotspots.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/readme/content.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/readme/insights.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/readme/sections.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/_render_section_helpers.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/constants.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/helpers.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/metrics.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/metrics_core.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/metrics_duplicates.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/metrics_health.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/module_detail.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/analysis.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow/utils.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/mermaid/fix.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/mermaid/png.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/mermaid/validation.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/nlp/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/parsers/toon_parser.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/patterns/detector.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/refactor/prompt_engine.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/templates/extract_method.md +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm/templates/move_method.md +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/setup.cfg +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_analysis_cache_version.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_call_graph_engine.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_complexity.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_dup_groups.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_export_cache_flags.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_export_cache_ownership.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_export_profile_contract.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_js_complexity_boundaries.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_mermaid_png.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_native_rust_bridge.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_perf_exporters.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_planfile_tickets_exporter.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_smell_engine.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_smells_shotgun_surgery.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_toon_v2.py +0 -0
- {code2llm-0.5.183 → code2llm-0.5.185}/tests/test_version_parity.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.185
|
|
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
|
|
@@ -101,6 +101,7 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
101
101
|
|------|--------|---------|--------------|
|
|
102
102
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
103
103
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
104
|
+
| `projections.json` | **JSON** | **🔎 Bounded symbol catalog** (`code2llm.projection/v1`) for intent retrieval / external indexers — deterministic, digested, capped at 2048 records | Reuse contract without analyzer internals |
|
|
104
105
|
|
|
105
106
|
### 🤖 LLM-Ready Documentation
|
|
106
107
|
|
|
@@ -26,6 +26,7 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
26
26
|
|------|--------|---------|--------------|
|
|
27
27
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
28
28
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
29
|
+
| `projections.json` | **JSON** | **🔎 Bounded symbol catalog** (`code2llm.projection/v1`) for intent retrieval / external indexers — deterministic, digested, capped at 2048 records | Reuse contract without analyzer internals |
|
|
29
30
|
|
|
30
31
|
### 🤖 LLM-Ready Documentation
|
|
31
32
|
|
code2llm-0.5.185/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.185
|
|
@@ -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.185"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -212,22 +212,27 @@ def detect_data_clumps(
|
|
|
212
212
|
|
|
213
213
|
for (file, args), funcs in arg_sets.items():
|
|
214
214
|
if len(funcs) >= 2:
|
|
215
|
-
|
|
215
|
+
# Names feed exported ticket identities, so hash/discovery order
|
|
216
|
+
# must not turn the same finding into a new ticket after restart.
|
|
217
|
+
ordered_args = sorted(args)
|
|
218
|
+
ordered_funcs = sorted(funcs)
|
|
219
|
+
arg_names = ", ".join(ordered_args)
|
|
220
|
+
for func_name in ordered_funcs:
|
|
216
221
|
func_info = functions[func_name]
|
|
217
222
|
smells.append(
|
|
218
223
|
CodeSmell(
|
|
219
|
-
name=f"Data Clump: {
|
|
224
|
+
name=f"Data Clump: {arg_names}",
|
|
220
225
|
type="data_clump",
|
|
221
226
|
file=func_info.file,
|
|
222
227
|
line=func_info.line,
|
|
223
228
|
severity=0.6,
|
|
224
229
|
description=(
|
|
225
|
-
f"Arguments ({
|
|
226
|
-
f" in multiple functions: {', '.join(
|
|
230
|
+
f"Arguments ({arg_names}) are used together"
|
|
231
|
+
f" in multiple functions: {', '.join(ordered_funcs)}."
|
|
227
232
|
),
|
|
228
233
|
context={
|
|
229
|
-
"clump":
|
|
230
|
-
"related_functions":
|
|
234
|
+
"clump": ordered_args,
|
|
235
|
+
"related_functions": ordered_funcs,
|
|
231
236
|
},
|
|
232
237
|
)
|
|
233
238
|
)
|
|
@@ -30,6 +30,8 @@ from code2llm.exporters import (
|
|
|
30
30
|
EvolutionExporter,
|
|
31
31
|
READMEExporter,
|
|
32
32
|
ProjectYAMLExporter,
|
|
33
|
+
ProjectionsExporter,
|
|
34
|
+
PROJECTION_FILENAME,
|
|
33
35
|
ToonViewGenerator,
|
|
34
36
|
ContextViewGenerator,
|
|
35
37
|
ArticleViewGenerator,
|
|
@@ -195,6 +197,15 @@ def _export_simple_formats(args, result, output_dir: Path, formats):
|
|
|
195
197
|
if args.verbose:
|
|
196
198
|
print(f" - JSON: {filepath}")
|
|
197
199
|
|
|
200
|
+
if "projections" in formats:
|
|
201
|
+
exporter = ProjectionsExporter()
|
|
202
|
+
filepath = output_dir / PROJECTION_FILENAME
|
|
203
|
+
t0 = time.monotonic()
|
|
204
|
+
exporter.export(result, str(filepath))
|
|
205
|
+
elapsed = time.monotonic() - t0
|
|
206
|
+
if args.verbose:
|
|
207
|
+
print(f" - PROJECTIONS: {filepath} ({elapsed:.2f}s)")
|
|
208
|
+
|
|
198
209
|
|
|
199
210
|
def _export_yaml(args, result, output_dir: Path):
|
|
200
211
|
"""Export YAML with optional split/separated modes."""
|
|
@@ -20,6 +20,7 @@ FORMAT_FILENAMES: Dict[str, str] = {
|
|
|
20
20
|
"planfile": "planfile-tickets.yaml",
|
|
21
21
|
"readme": "README.md",
|
|
22
22
|
"project-yaml": "project.yaml",
|
|
23
|
+
"projections": "projections.json",
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
# Files produced per format in dry-run preview
|
|
@@ -33,6 +34,7 @@ FORMAT_DRY_RUN_FILES: Dict[str, List[str]] = {
|
|
|
33
34
|
"yaml": ["analysis.yaml"],
|
|
34
35
|
"json": ["analysis.json"],
|
|
35
36
|
"readme": ["README.md"],
|
|
37
|
+
"projections": ["projections.json"],
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
# Human-readable labels
|
|
@@ -47,6 +49,7 @@ FORMAT_LABELS: Dict[str, str] = {
|
|
|
47
49
|
"planfile": "PLANFILE (ticket suggestions)",
|
|
48
50
|
"readme": "README (documentation)",
|
|
49
51
|
"project-yaml": "PROJECT-YAML (single source of truth)",
|
|
52
|
+
"projections": "PROJECTIONS (intent retrieval)",
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
# ---------------------------------------------------------------------------
|
|
@@ -97,7 +97,7 @@ Strategy Options (--strategy):
|
|
|
97
97
|
default="toon",
|
|
98
98
|
help=(
|
|
99
99
|
"Output formats: toon,map,flow,context,code2logic,yaml,json,mermaid,"
|
|
100
|
-
"evolution,calls,png,project-yaml,planfile,all (default: toon)"
|
|
100
|
+
"evolution,calls,png,project-yaml,planfile,projections,all (default: toon)"
|
|
101
101
|
),
|
|
102
102
|
)
|
|
103
103
|
|
|
@@ -9,6 +9,7 @@ Available exporters:
|
|
|
9
9
|
- READMEExporter → README.md (documentation of all files)
|
|
10
10
|
- YAMLExporter → analysis.yaml
|
|
11
11
|
- JSONExporter → analysis.json
|
|
12
|
+
- ProjectionsExporter → projections.json (bounded symbol catalog for intent retrieval)
|
|
12
13
|
- MermaidExporter → *.mmd
|
|
13
14
|
|
|
14
15
|
Registry API:
|
|
@@ -33,6 +34,13 @@ from .base import (
|
|
|
33
34
|
|
|
34
35
|
# Standard exporters (auto-registered via @export_format decorator)
|
|
35
36
|
from .json_exporter import JSONExporter
|
|
37
|
+
from .projections_exporter import (
|
|
38
|
+
MAX_RECORDS,
|
|
39
|
+
PROJECTION_FILENAME,
|
|
40
|
+
PROJECTION_SCHEMA,
|
|
41
|
+
ProjectionsExporter,
|
|
42
|
+
build_projection,
|
|
43
|
+
)
|
|
36
44
|
from .yaml_exporter import YAMLExporter
|
|
37
45
|
from .mermaid_exporter import MermaidExporter
|
|
38
46
|
from .context_exporter import ContextExporter
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Bounded, versioned projections of code2llm analysis for intent retrieval.
|
|
2
|
+
|
|
3
|
+
Emits ``projections.json``: a deterministic, capped catalog of source
|
|
4
|
+
symbols with provenance (kind, symbol, file, line) and content digests.
|
|
5
|
+
Downstream consumers (e.g. semcod/search) index it without parsing the
|
|
6
|
+
full analysis or importing analyzer internals — the reuse contract is
|
|
7
|
+
this artifact, not the Python API (PLF-035 / issue #25).
|
|
8
|
+
|
|
9
|
+
Contract invariants:
|
|
10
|
+
- ``schema`` pins the record shape; ``producer.version`` pins the
|
|
11
|
+
analyzer. Bumping either invalidates downstream caches by equality
|
|
12
|
+
of the embedded digests alone.
|
|
13
|
+
- Byte-deterministic for identical analysis results: canonical JSON
|
|
14
|
+
(sorted keys), records sorted by (file, line, kind, symbol), no
|
|
15
|
+
timestamps inside records. ``generated_at`` is optional and never
|
|
16
|
+
contributes to the digest.
|
|
17
|
+
- Bounded: at most ``MAX_RECORDS`` records; symbol and file strings
|
|
18
|
+
truncated at ``FIELD_LIMIT`` characters; ``stats.truncated`` flags
|
|
19
|
+
an overflow so consumers can refuse silently-stale catalogs.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import hashlib
|
|
25
|
+
import json
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Dict, Iterable, List, Optional
|
|
28
|
+
|
|
29
|
+
from code2llm import __version__
|
|
30
|
+
|
|
31
|
+
from .base import BaseExporter, export_format
|
|
32
|
+
|
|
33
|
+
PROJECTION_SCHEMA = "code2llm.projection/v1"
|
|
34
|
+
PROJECTION_FILENAME = "projections.json"
|
|
35
|
+
MAX_RECORDS = 2048
|
|
36
|
+
FIELD_LIMIT = 200
|
|
37
|
+
|
|
38
|
+
_KIND_ORDER = {"module": 0, "class": 1, "function": 2}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _canonical(obj: Any) -> str:
|
|
42
|
+
return json.dumps(obj, sort_keys=True, ensure_ascii=False, separators=(",", ":"))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _digest(obj: Any) -> str:
|
|
46
|
+
return "sha256:" + hashlib.sha256(_canonical(obj).encode("utf-8")).hexdigest()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _bounded(value: str) -> str:
|
|
50
|
+
return value[:FIELD_LIMIT]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _record(kind: str, symbol: str, file: str, line: Optional[int]) -> Dict[str, Any]:
|
|
54
|
+
record: Dict[str, Any] = {"kind": kind, "symbol": _bounded(symbol), "file": _bounded(file)}
|
|
55
|
+
if line is not None:
|
|
56
|
+
record["line"] = int(line)
|
|
57
|
+
record["digest"] = _digest(record)
|
|
58
|
+
return record
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _items(value: Any) -> Iterable[Any]:
|
|
62
|
+
"""Yield info objects from either a Dict[str, Info] or a list."""
|
|
63
|
+
if isinstance(value, dict):
|
|
64
|
+
return value.values()
|
|
65
|
+
return value or []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _iter_records(result: Any) -> Iterable[Dict[str, Any]]:
|
|
69
|
+
modules = _items(getattr(result, "modules", None))
|
|
70
|
+
classes = _items(getattr(result, "classes", None))
|
|
71
|
+
functions = _items(getattr(result, "functions", None))
|
|
72
|
+
for module in modules:
|
|
73
|
+
yield _record("module", module.name, module.file, None)
|
|
74
|
+
for cls in classes:
|
|
75
|
+
yield _record("class", cls.qualified_name or cls.name, cls.file, cls.line)
|
|
76
|
+
for func in functions:
|
|
77
|
+
yield _record("function", func.qualified_name or func.name, func.file, func.line)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_projection(
|
|
81
|
+
result: Any,
|
|
82
|
+
*,
|
|
83
|
+
max_records: int = MAX_RECORDS,
|
|
84
|
+
generated_at: Optional[str] = None,
|
|
85
|
+
) -> Dict[str, Any]:
|
|
86
|
+
"""Build the projection document from an :class:`AnalysisResult`.
|
|
87
|
+
|
|
88
|
+
Duck-typed on purpose: only ``modules``/``classes``/``functions``
|
|
89
|
+
(``name``/``qualified_name``/``file``/``line``) are read, so tests and
|
|
90
|
+
alternative frontends can feed lightweight structures.
|
|
91
|
+
"""
|
|
92
|
+
records: List[Dict[str, Any]] = list(_iter_records(result))
|
|
93
|
+
records.sort(key=lambda r: (r["file"], r.get("line", 0), _KIND_ORDER[r["kind"]], r["symbol"]))
|
|
94
|
+
truncated = len(records) > max_records
|
|
95
|
+
if truncated:
|
|
96
|
+
records = records[:max_records]
|
|
97
|
+
|
|
98
|
+
stats = getattr(result, "stats", None) or {}
|
|
99
|
+
document: Dict[str, Any] = {
|
|
100
|
+
"schema": PROJECTION_SCHEMA,
|
|
101
|
+
"producer": {"tool": "code2llm", "version": __version__},
|
|
102
|
+
"project": {
|
|
103
|
+
"path": _bounded(str(getattr(result, "project_path", "") or "")),
|
|
104
|
+
"analysis_mode": str(getattr(result, "analysis_mode", "") or ""),
|
|
105
|
+
},
|
|
106
|
+
"stats": {
|
|
107
|
+
"files": int(stats.get("files", 0) or 0),
|
|
108
|
+
"lines": int(stats.get("lines", 0) or 0),
|
|
109
|
+
"symbols": len(records),
|
|
110
|
+
"truncated": truncated,
|
|
111
|
+
},
|
|
112
|
+
"records": records,
|
|
113
|
+
"digest": _digest(records),
|
|
114
|
+
}
|
|
115
|
+
if generated_at:
|
|
116
|
+
document["generated_at"] = generated_at
|
|
117
|
+
return document
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@export_format(
|
|
121
|
+
"projections",
|
|
122
|
+
description="Bounded symbol projections for intent retrieval (projections.json)",
|
|
123
|
+
extension=".json",
|
|
124
|
+
)
|
|
125
|
+
class ProjectionsExporter(BaseExporter):
|
|
126
|
+
"""Export the deterministic projections catalog."""
|
|
127
|
+
|
|
128
|
+
def export(
|
|
129
|
+
self,
|
|
130
|
+
result: Any,
|
|
131
|
+
output_path: str,
|
|
132
|
+
generated_at: Optional[str] = None,
|
|
133
|
+
**kwargs: Any,
|
|
134
|
+
) -> Optional[Path]:
|
|
135
|
+
document = build_projection(result, generated_at=generated_at)
|
|
136
|
+
path = self._ensure_dir(output_path)
|
|
137
|
+
payload = json.dumps(document, sort_keys=True, ensure_ascii=False, indent=1) + "\n"
|
|
138
|
+
self._write_text(path, payload)
|
|
139
|
+
return path
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code2llm
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.185
|
|
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
|
|
@@ -101,6 +101,7 @@ When you run `code2llm ./ -f all`, the following files are created:
|
|
|
101
101
|
|------|--------|---------|--------------|
|
|
102
102
|
| `evolution.toon.yaml` | **YAML** | **📋 Refactoring queue** - Prioritized improvements | 0 refactoring actions needed |
|
|
103
103
|
| `map.toon.yaml` | **YAML** | **🗺️ Structural map + project header** - Modules, imports, exports, signatures, stats, alerts, hotspots, trend | Project architecture overview |
|
|
104
|
+
| `projections.json` | **JSON** | **🔎 Bounded symbol catalog** (`code2llm.projection/v1`) for intent retrieval / external indexers — deterministic, digested, capped at 2048 records | Reuse contract without analyzer internals |
|
|
104
105
|
|
|
105
106
|
### 🤖 LLM-Ready Documentation
|
|
106
107
|
|
|
@@ -106,6 +106,7 @@ code2llm/exporters/mermaid_exporter.py
|
|
|
106
106
|
code2llm/exporters/mermaid_flow_helpers.py
|
|
107
107
|
code2llm/exporters/planfile_tickets.py
|
|
108
108
|
code2llm/exporters/project_yaml_exporter.py
|
|
109
|
+
code2llm/exporters/projections_exporter.py
|
|
109
110
|
code2llm/exporters/readme_exporter.py
|
|
110
111
|
code2llm/exporters/report_generators.py
|
|
111
112
|
code2llm/exporters/toon.py
|
|
@@ -190,6 +191,7 @@ code2llm/templates/extract_method.md
|
|
|
190
191
|
code2llm/templates/move_method.md
|
|
191
192
|
tests/test_advanced_analysis.py
|
|
192
193
|
tests/test_analysis_cache_version.py
|
|
194
|
+
tests/test_analysis_env.py
|
|
193
195
|
tests/test_analyzer.py
|
|
194
196
|
tests/test_cache_invalidation_e2e.py
|
|
195
197
|
tests/test_call_graph_engine.py
|
|
@@ -217,6 +219,7 @@ tests/test_persistent_cache.py
|
|
|
217
219
|
tests/test_pipeline_detector.py
|
|
218
220
|
tests/test_planfile_tickets_exporter.py
|
|
219
221
|
tests/test_project_toon_export.py
|
|
222
|
+
tests/test_projections_exporter.py
|
|
220
223
|
tests/test_prompt_engine.py
|
|
221
224
|
tests/test_prompt_txt.py
|
|
222
225
|
tests/test_refactoring_engine.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code2llm"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.185"
|
|
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
10
|
requires-python = ">=3.10"
|
|
@@ -25,7 +25,7 @@ def read_readme():
|
|
|
25
25
|
|
|
26
26
|
setup(
|
|
27
27
|
name="code2llm",
|
|
28
|
-
version="0.5.
|
|
28
|
+
version="0.5.185",
|
|
29
29
|
description="Python code flow analysis tool - CFG, DFG, and call graph extraction",
|
|
30
30
|
long_description=read_readme(),
|
|
31
31
|
long_description_content_type="text/markdown",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Guardrails for the pinned Python 3.12 analysis environment (PLF-036 / #26).
|
|
2
|
+
|
|
3
|
+
The analyzer chain clickmd/code2logic -> pfix -> pipreqs cannot run on the
|
|
4
|
+
Python 3.13 application venv: pipreqs 0.5.0 declares Requires-Python
|
|
5
|
+
>=3.8.1,<3.13 and no newer release exists. These tests keep the analysis
|
|
6
|
+
environment script and its documentation discoverable and pinned.
|
|
7
|
+
"""
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
13
|
+
SCRIPT = ROOT / "scripts" / "analysis-env.sh"
|
|
14
|
+
DOCS = ROOT / "docs" / "dependencies.md"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_analysis_env_script_exists_and_is_executable():
|
|
18
|
+
assert SCRIPT.is_file()
|
|
19
|
+
assert SCRIPT.stat().st_mode & 0o111, "analysis-env.sh must be executable"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_analysis_env_script_pins_python_312_and_locked_sync():
|
|
23
|
+
content = SCRIPT.read_text(encoding="utf-8")
|
|
24
|
+
assert 'ANALYSIS_PYTHON="${ANALYSIS_PYTHON:-3.12}"' in content
|
|
25
|
+
assert "uv sync --locked" in content
|
|
26
|
+
# The application venv must never be the target of the sync.
|
|
27
|
+
assert "UV_PROJECT_ENVIRONMENT" in content
|
|
28
|
+
assert "--all-extras" in content
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize(
|
|
32
|
+
("fragment",),
|
|
33
|
+
[
|
|
34
|
+
("pipreqs",),
|
|
35
|
+
(">=3.8.1,<3.13",),
|
|
36
|
+
("scripts/analysis-env.sh",),
|
|
37
|
+
(".venv-analysis",),
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
def test_dependency_docs_explain_the_analysis_environment(fragment):
|
|
41
|
+
assert fragment in DOCS.read_text(encoding="utf-8")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_gitignore_covers_analysis_environment():
|
|
45
|
+
ignores = (ROOT / ".gitignore").read_text(encoding="utf-8").splitlines()
|
|
46
|
+
assert ".venv-analysis/" in ignores
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Contract tests for the bounded intent-retrieval projection (PLF-035 / #25).
|
|
2
|
+
|
|
3
|
+
The projection is a reuse contract consumed by external indexers
|
|
4
|
+
(semcod/search): determinism, provenance, bounds and version stamping
|
|
5
|
+
are load-bearing, not incidental.
|
|
6
|
+
"""
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import SimpleNamespace
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from code2llm import __version__
|
|
13
|
+
from code2llm.exporters.projections_exporter import (
|
|
14
|
+
FIELD_LIMIT,
|
|
15
|
+
MAX_RECORDS,
|
|
16
|
+
PROJECTION_SCHEMA,
|
|
17
|
+
ProjectionsExporter,
|
|
18
|
+
build_projection,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _fake_result(modules=(), classes=(), functions=(), stats=None, path="proj", mode="standard"):
|
|
23
|
+
def mod(name, file):
|
|
24
|
+
return SimpleNamespace(name=name, file=file)
|
|
25
|
+
|
|
26
|
+
def cls(name, file, line):
|
|
27
|
+
return SimpleNamespace(name=name, qualified_name=f"mod.{name}", file=file, line=line)
|
|
28
|
+
|
|
29
|
+
def fn(name, file, line):
|
|
30
|
+
return SimpleNamespace(name=name, qualified_name=f"mod.{name}", file=file, line=line)
|
|
31
|
+
|
|
32
|
+
return SimpleNamespace(
|
|
33
|
+
modules=[mod(*m) for m in modules],
|
|
34
|
+
classes=[cls(*c) for c in classes],
|
|
35
|
+
functions=[fn(*f) for f in functions],
|
|
36
|
+
stats=stats or {"files": 3, "lines": 100},
|
|
37
|
+
project_path=path,
|
|
38
|
+
analysis_mode=mode,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_schema_and_producer_version_are_pinned():
|
|
43
|
+
doc = build_projection(_fake_result())
|
|
44
|
+
assert doc["schema"] == PROJECTION_SCHEMA
|
|
45
|
+
assert doc["producer"] == {"tool": "code2llm", "version": __version__}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_records_carry_source_and_symbol_provenance():
|
|
49
|
+
doc = build_projection(
|
|
50
|
+
_fake_result(
|
|
51
|
+
modules=[("core", "pkg/core/__init__.py")],
|
|
52
|
+
classes=[("Engine", "pkg/core/engine.py", 12)],
|
|
53
|
+
functions=[("run", "pkg/core/engine.py", 40)],
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
by_kind = {r["kind"]: r for r in doc["records"]}
|
|
57
|
+
assert by_kind["module"] == {
|
|
58
|
+
"kind": "module",
|
|
59
|
+
"symbol": "core",
|
|
60
|
+
"file": "pkg/core/__init__.py",
|
|
61
|
+
"digest": by_kind["module"]["digest"],
|
|
62
|
+
}
|
|
63
|
+
assert by_kind["class"]["symbol"] == "mod.Engine"
|
|
64
|
+
assert by_kind["class"]["file"] == "pkg/core/engine.py"
|
|
65
|
+
assert by_kind["class"]["line"] == 12
|
|
66
|
+
assert by_kind["function"]["line"] == 40
|
|
67
|
+
assert all(r["digest"].startswith("sha256:") for r in doc["records"])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_deterministic_bytes_and_digest():
|
|
71
|
+
result = _fake_result(
|
|
72
|
+
classes=[("B", "b.py", 2), ("A", "a.py", 1)],
|
|
73
|
+
functions=[("z", "a.py", 9), ("a", "b.py", 1)],
|
|
74
|
+
)
|
|
75
|
+
first = build_projection(result)
|
|
76
|
+
second = build_projection(result)
|
|
77
|
+
assert first == second
|
|
78
|
+
assert first["digest"] == second["digest"]
|
|
79
|
+
# Record order is canonical: (file, line, kind, symbol).
|
|
80
|
+
order = [(r["file"], r.get("line", 0), r["kind"], r["symbol"]) for r in first["records"]]
|
|
81
|
+
assert order == sorted(order)
|
|
82
|
+
# generated_at never leaks into the digest.
|
|
83
|
+
stamped = build_projection(result, generated_at="2026-10-23T00:00:00Z")
|
|
84
|
+
assert stamped["digest"] == first["digest"]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_bounds_cap_records_and_flag_truncation():
|
|
88
|
+
functions = [(f"f{i}", "m.py", i + 1) for i in range(MAX_RECORDS + 5)]
|
|
89
|
+
doc = build_projection(_fake_result(functions=functions))
|
|
90
|
+
assert len(doc["records"]) == MAX_RECORDS
|
|
91
|
+
assert doc["stats"]["truncated"] is True
|
|
92
|
+
assert doc["stats"]["symbols"] == MAX_RECORDS
|
|
93
|
+
small = build_projection(_fake_result(functions=functions[:4]))
|
|
94
|
+
assert small["stats"]["truncated"] is False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_long_fields_are_truncated():
|
|
98
|
+
long_name = "x" * (FIELD_LIMIT + 50)
|
|
99
|
+
doc = build_projection(_fake_result(functions=[(long_name, long_name + ".py", 1)]))
|
|
100
|
+
record = doc["records"][0]
|
|
101
|
+
assert len(record["symbol"]) == FIELD_LIMIT
|
|
102
|
+
assert len(record["file"]) == FIELD_LIMIT
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_exporter_writes_canonical_json(tmp_path: Path):
|
|
106
|
+
result = _fake_result(classes=[("E", "e.py", 3)])
|
|
107
|
+
exporter = ProjectionsExporter()
|
|
108
|
+
out = tmp_path / "projections.json"
|
|
109
|
+
path = exporter.export(result, str(out))
|
|
110
|
+
assert path == out and out.exists()
|
|
111
|
+
body = out.read_text(encoding="utf-8")
|
|
112
|
+
again = exporter.export(result, str(tmp_path / "again.json"))
|
|
113
|
+
assert again.read_text(encoding="utf-8") == body
|
|
114
|
+
assert body.endswith("\n")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_registry_lists_projections():
|
|
118
|
+
from code2llm.exporters import EXPORT_REGISTRY, get_exporter
|
|
119
|
+
|
|
120
|
+
assert "projections" in EXPORT_REGISTRY
|
|
121
|
+
assert get_exporter("projections") is ProjectionsExporter
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_cli_help_mentions_projections(capsys):
|
|
125
|
+
from code2llm.cli_parser import create_parser
|
|
126
|
+
|
|
127
|
+
with pytest.raises(SystemExit) as exc:
|
|
128
|
+
create_parser().parse_args(["--help"])
|
|
129
|
+
assert exc.value.code == 0
|
|
130
|
+
assert "projections" in (capsys.readouterr().out or "")
|
|
@@ -17,7 +17,7 @@ def test_tiktoken_truncation():
|
|
|
17
17
|
|
|
18
18
|
engine = PromptEngine(result)
|
|
19
19
|
# Mock source context to be very large
|
|
20
|
-
engine._get_source_context = lambda f, l: "print('Hello world')\n" * 1000
|
|
20
|
+
engine._get_source_context = lambda f, l, *, max_lines: "print('Hello world')\n" * 1000
|
|
21
21
|
|
|
22
22
|
prompts = engine.generate_prompts()
|
|
23
23
|
assert len(prompts) == 1
|
|
@@ -59,7 +59,7 @@ def test_template_rendering_with_metrics():
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
engine = PromptEngine(result)
|
|
62
|
-
engine._get_source_context = lambda f, l: "def complex_func():\n pass"
|
|
62
|
+
engine._get_source_context = lambda f, l, *, max_lines: "def complex_func():\n pass"
|
|
63
63
|
|
|
64
64
|
prompts = engine.generate_prompts()
|
|
65
65
|
prompt_content = list(prompts.values())[0]
|
|
@@ -81,3 +81,54 @@ def test_fewer_than_three_args_never_counted():
|
|
|
81
81
|
smells = SmellDetector(result)._detect_data_clumps()
|
|
82
82
|
|
|
83
83
|
assert smells == []
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_exported_clump_tickets_are_stable_across_processes(tmp_path):
|
|
87
|
+
"""The same finding must not create new ticket keys after a process restart."""
|
|
88
|
+
import json
|
|
89
|
+
import os
|
|
90
|
+
from pathlib import Path
|
|
91
|
+
import subprocess
|
|
92
|
+
import sys
|
|
93
|
+
import textwrap
|
|
94
|
+
|
|
95
|
+
script = textwrap.dedent("""\
|
|
96
|
+
import json
|
|
97
|
+
import sys
|
|
98
|
+
from dataclasses import asdict
|
|
99
|
+
from code2llm.analysis.smells import SmellDetector
|
|
100
|
+
from code2llm.core.models import AnalysisResult, FunctionInfo
|
|
101
|
+
from code2llm.exporters.planfile_tickets import _smell_tickets
|
|
102
|
+
|
|
103
|
+
reverse = sys.argv[1] == "reverse"
|
|
104
|
+
functions = [("create_github_pr", 47), ("create_gitlab_mr", 80)]
|
|
105
|
+
arguments = ["self", "provider", "group", "template"]
|
|
106
|
+
if reverse:
|
|
107
|
+
functions.reverse()
|
|
108
|
+
arguments.reverse()
|
|
109
|
+
result = AnalysisResult(project_path="/fixture")
|
|
110
|
+
for name, line in functions:
|
|
111
|
+
result.functions[name] = FunctionInfo(
|
|
112
|
+
name=name, qualified_name="publish." + name,
|
|
113
|
+
file="/fixture/publish.py", line=line, args=arguments,
|
|
114
|
+
)
|
|
115
|
+
result.smells = SmellDetector(result)._detect_data_clumps()
|
|
116
|
+
tickets = [asdict(ticket) for ticket in _smell_tickets(result)]
|
|
117
|
+
print(json.dumps(sorted(tickets, key=lambda t: t["dedupe_key"]), sort_keys=True))
|
|
118
|
+
""")
|
|
119
|
+
outputs = []
|
|
120
|
+
root = str(Path(__file__).resolve().parents[1])
|
|
121
|
+
for seed, order in [("1", "forward"), ("2", "forward"), ("42", "reverse")]:
|
|
122
|
+
env = os.environ.copy()
|
|
123
|
+
env["PYTHONHASHSEED"] = seed
|
|
124
|
+
env["PYTHONPATH"] = root + os.pathsep + env.get("PYTHONPATH", "")
|
|
125
|
+
child = subprocess.run(
|
|
126
|
+
[sys.executable, "-c", script, order],
|
|
127
|
+
cwd=tmp_path, env=env, check=True, capture_output=True,
|
|
128
|
+
text=True, timeout=30,
|
|
129
|
+
)
|
|
130
|
+
tickets = json.loads(child.stdout)
|
|
131
|
+
assert len(tickets) == 2
|
|
132
|
+
assert len({ticket["dedupe_key"] for ticket in tickets}) == 2
|
|
133
|
+
outputs.append(tickets)
|
|
134
|
+
assert outputs[0] == outputs[1] == outputs[2]
|
code2llm-0.5.183/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.5.183
|
|
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
|