code2llm 0.5.158__tar.gz → 0.5.160__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.158/code2llm.egg-info → code2llm-0.5.160}/PKG-INFO +5 -5
- {code2llm-0.5.158 → code2llm-0.5.160}/README.md +4 -4
- code2llm-0.5.160/VERSION +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/__init__.py +1 -1
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/_data_impl.py +15 -15
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/coupling.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/side_effects.py +23 -44
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/smells.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_analysis.py +17 -19
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/formats.py +23 -25
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator.py +32 -48
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/prompt.py +30 -55
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/analyzer.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/file_analyzer.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/_c_parser.py +14 -17
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/ts_parser.py +2 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/refactoring.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/source_classifier.py +16 -20
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming/incremental.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming/prioritizer.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/base.py +2 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/dashboard_renderer.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/flow_exporter.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/details.py +16 -24
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/calls.py +15 -13
- code2llm-0.5.160/code2llm/exporters/mermaid/compact.py +52 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/planfile_tickets.py +14 -11
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/core.py +15 -32
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/health.py +30 -38
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/hotspots.py +57 -63
- code2llm-0.5.160/code2llm/exporters/readme/insights.py +58 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/__init__.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/helpers.py +42 -62
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/metrics.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/metrics_duplicates.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/metrics_health.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/module_detail.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/nlp/__init__.py +1 -1
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/patterns/detector.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/refactor/prompt_engine.py +1 -0
- {code2llm-0.5.158 → code2llm-0.5.160/code2llm.egg-info}/PKG-INFO +5 -5
- {code2llm-0.5.158 → code2llm-0.5.160}/pyproject.toml +1 -1
- code2llm-0.5.158/VERSION +0 -1
- code2llm-0.5.158/code2llm/exporters/mermaid/compact.py +0 -69
- code2llm-0.5.158/code2llm/exporters/readme/insights.py +0 -54
- {code2llm-0.5.158 → code2llm-0.5.160}/LICENSE +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/MANIFEST.in +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/__main__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/call_graph.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/cfg.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/data_analysis.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/dfg.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/pipeline_classifier.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/pipeline_detector.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/pipeline_resolver.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/type_inference.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/utils/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/analysis/utils/ast_helpers.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/api.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_commands.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/code2logic.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator_chunked.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator_constants.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_exports/orchestrator_handlers.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/cli_parser.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/ast_registry.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/config.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/export_pipeline.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/file_cache.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/file_filter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/gitignore.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/incremental.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/_calls.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/_complexity.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/base.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/cpp.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/csharp.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/generic.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/go_lang.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/java.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/php.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/ruby.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/rust.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/ts_extractors.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/lang/typescript.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/large_repo.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/models.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/persistent_cache.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/repo_files.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming/cache.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming/scanner.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming/strategies.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/streaming_analyzer.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/core/toon_size_manager.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/article_view.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/context_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/context_view.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/dashboard_data.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution/computation.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution/constants.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution/exclusion.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution/render.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution/yaml_export.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/evolution_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/flow_constants.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/flow_renderer.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/html_dashboard.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/index_generator/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/index_generator/renderer.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/index_generator/scanner.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/index_generator.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/json_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/llm_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/alerts.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/header.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/module_list.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/utils.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map/yaml_export.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/map_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/classic.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/flow_compact.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/flow_detailed.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/flow_full.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid/utils.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/mermaid_flow_helpers.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/constants.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/evolution.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml/modules.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/project_yaml_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/readme/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/readme/content.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/readme/files.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/readme/sections.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/readme_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/report_generators.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/_render_coupling_helpers.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/_render_section_helpers.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/constants.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/metrics_core.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon/renderer.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/toon_view.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/validate_project.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/exporters/yaml_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/_utils.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/analysis.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/cli.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/generator.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/nodes.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/parsing.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow/utils.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_flow.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/llm_task.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/mermaid/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/mermaid/fix.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/mermaid/png.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/mermaid/validation.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/generators/mermaid.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/nlp/config.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/nlp/entity_resolution.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/nlp/intent_matching.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/nlp/normalization.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/nlp/pipeline.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/parsers/toon_parser.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/patterns/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm/refactor/__init__.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm.egg-info/SOURCES.txt +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm.egg-info/dependency_links.txt +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm.egg-info/entry_points.txt +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm.egg-info/requires.txt +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/code2llm.egg-info/top_level.txt +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/setup.cfg +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/setup.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_advanced_analysis.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_analyzer.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_cache_invalidation_e2e.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_calls_toon_export.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_declarative_collection.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_deep_analysis.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_edge_cases.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_export_cache_flags.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_file_analyzer_tagging.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_flow_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_format_quality.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_multilanguage_e2e.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_nlp_pipeline.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_nonpython_cc_calls.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_orchestrator_cache_mtime.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_persistent_cache.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_pipeline_detector.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_planfile_tickets_exporter.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_project_toon_export.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_prompt_engine.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_prompt_txt.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/tests/test_refactoring_engine.py +0 -0
- {code2llm-0.5.158 → code2llm-0.5.160}/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.160
|
|
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 (221 commits)
|
|
73
|
+
- 👤 **Human dev:** ~$8118 (81.2h @ $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
|
|
|
@@ -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 (221 commits)
|
|
9
|
+
- 👤 **Human dev:** ~$8118 (81.2h @ $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
|
|
code2llm-0.5.160/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.160
|
|
@@ -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.160"
|
|
12
12
|
__author__ = "STTS Project"
|
|
13
13
|
|
|
14
14
|
# Core analysis components (lightweight, always needed)
|
|
@@ -222,26 +222,26 @@ def _analyze_data_types(result: AnalysisResult) -> list:
|
|
|
222
222
|
# ---------------------------------------------------------------------------
|
|
223
223
|
|
|
224
224
|
|
|
225
|
+
_NAME_TYPE_KEYWORDS: List[tuple] = [
|
|
226
|
+
("list", ("list", "items")),
|
|
227
|
+
("dict", ("dict", "map")),
|
|
228
|
+
("str", ("text", "string")),
|
|
229
|
+
("int", ("count", "index")),
|
|
230
|
+
]
|
|
231
|
+
_DOC_TYPE_KEYWORDS: List[tuple] = [
|
|
232
|
+
("list", ("list",)),
|
|
233
|
+
("dict", ("dict",)),
|
|
234
|
+
("str", ("string", "text")),
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
|
|
225
238
|
def _get_function_data_types(func) -> list:
|
|
226
239
|
"""Derive data type labels for a function from its name and docstring."""
|
|
227
|
-
types: List[str] = []
|
|
228
240
|
name = func.name.lower()
|
|
229
|
-
if
|
|
230
|
-
types.append("list")
|
|
231
|
-
if "dict" in name or "map" in name:
|
|
232
|
-
types.append("dict")
|
|
233
|
-
if "text" in name or "string" in name:
|
|
234
|
-
types.append("str")
|
|
235
|
-
if "count" in name or "index" in name:
|
|
236
|
-
types.append("int")
|
|
241
|
+
types = [dt for dt, kws in _NAME_TYPE_KEYWORDS if any(kw in name for kw in kws)]
|
|
237
242
|
if func.docstring:
|
|
238
243
|
doc = func.docstring.lower()
|
|
239
|
-
if
|
|
240
|
-
types.append("list")
|
|
241
|
-
if "dict" in doc:
|
|
242
|
-
types.append("dict")
|
|
243
|
-
if "string" in doc or "text" in doc:
|
|
244
|
-
types.append("str")
|
|
244
|
+
types += [dt for dt, kws in _DOC_TYPE_KEYWORDS if any(kw in doc for kw in kws)]
|
|
245
245
|
return list(set(types))
|
|
246
246
|
|
|
247
247
|
|
|
@@ -157,6 +157,7 @@ class SideEffectInfo:
|
|
|
157
157
|
|
|
158
158
|
@property
|
|
159
159
|
def is_pure(self) -> bool:
|
|
160
|
+
"""Return True when the function has no side effects."""
|
|
160
161
|
return self.classification == "pure"
|
|
161
162
|
|
|
162
163
|
@property
|
|
@@ -176,6 +177,7 @@ class SideEffectInfo:
|
|
|
176
177
|
return "; ".join(parts) if parts else "pure"
|
|
177
178
|
|
|
178
179
|
def to_dict(self) -> Dict[str, Any]:
|
|
180
|
+
"""Serialise the side-effect analysis result to a plain dict."""
|
|
179
181
|
return {
|
|
180
182
|
"function": self.function_name,
|
|
181
183
|
"qualified_name": self.qualified_name,
|
|
@@ -321,54 +323,31 @@ class SideEffectDetector:
|
|
|
321
323
|
else:
|
|
322
324
|
info.classification = "pure"
|
|
323
325
|
|
|
326
|
+
# word-sets and their target attribute names, in priority order
|
|
327
|
+
_HEURISTIC_CATEGORIES: List[tuple] = [
|
|
328
|
+
(
|
|
329
|
+
"IO",
|
|
330
|
+
{"write", "read", "open", "save", "load", "export", "dump", "print", "mkdir", "rmdir", "remove"},
|
|
331
|
+
"io_operations",
|
|
332
|
+
),
|
|
333
|
+
("cache", {"cache", "memoize", "lru_cache", "store", "fetch"}, "cache_operations"),
|
|
334
|
+
("mutation", {"set_", "update", "modify", "mutate", "append", "insert", "delete", "fix", "patch"}, "mutations"),
|
|
335
|
+
]
|
|
336
|
+
|
|
324
337
|
def _heuristic_classify(self, fi: FunctionInfo, info: SideEffectInfo) -> None:
|
|
325
338
|
"""Classify based on function name and calls (fallback)."""
|
|
326
339
|
name_lower = fi.name.lower()
|
|
327
340
|
calls_lower = {c.lower() for c in fi.calls}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
"mkdir",
|
|
339
|
-
"rmdir",
|
|
340
|
-
"remove",
|
|
341
|
-
}
|
|
342
|
-
cache_words = {"cache", "memoize", "lru_cache", "store", "fetch"}
|
|
343
|
-
mutation_words = {
|
|
344
|
-
"set_",
|
|
345
|
-
"update",
|
|
346
|
-
"modify",
|
|
347
|
-
"mutate",
|
|
348
|
-
"append",
|
|
349
|
-
"insert",
|
|
350
|
-
"delete",
|
|
351
|
-
"fix",
|
|
352
|
-
"patch",
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if any(w in name_lower for w in io_words):
|
|
356
|
-
info.classification = "IO"
|
|
357
|
-
info.io_operations.append(f"name:{fi.name}")
|
|
358
|
-
elif any(any(w in c for w in io_words) for c in calls_lower):
|
|
359
|
-
info.classification = "IO"
|
|
360
|
-
info.io_operations.append("calls:IO")
|
|
361
|
-
elif any(w in name_lower for w in cache_words):
|
|
362
|
-
info.classification = "cache"
|
|
363
|
-
info.cache_operations.append(f"name:{fi.name}")
|
|
364
|
-
elif any(any(w in c for w in cache_words) for c in calls_lower):
|
|
365
|
-
info.classification = "cache"
|
|
366
|
-
info.cache_operations.append("calls:cache")
|
|
367
|
-
elif any(w in name_lower for w in mutation_words):
|
|
368
|
-
info.classification = "mutation"
|
|
369
|
-
info.mutations.append(f"name:{fi.name}")
|
|
370
|
-
else:
|
|
371
|
-
info.classification = "pure"
|
|
341
|
+
for classification, words, attr in self._HEURISTIC_CATEGORIES:
|
|
342
|
+
if any(w in name_lower for w in words):
|
|
343
|
+
info.classification = classification
|
|
344
|
+
getattr(info, attr).append(f"name:{fi.name}")
|
|
345
|
+
return
|
|
346
|
+
if any(any(w in c for w in words) for c in calls_lower):
|
|
347
|
+
info.classification = classification
|
|
348
|
+
getattr(info, attr).append(f"calls:{classification}")
|
|
349
|
+
return
|
|
350
|
+
info.classification = "pure"
|
|
372
351
|
|
|
373
352
|
# ------------------------------------------------------------------
|
|
374
353
|
# AST helpers
|
|
@@ -9,6 +9,7 @@ class SmellDetector:
|
|
|
9
9
|
"""Detect code smells from analysis results."""
|
|
10
10
|
|
|
11
11
|
def __init__(self, result: AnalysisResult):
|
|
12
|
+
"""Initialise with the analysis result to detect code smells."""
|
|
12
13
|
self.result = result
|
|
13
14
|
# Pre-index mutations by scope — avoids O(n×m) full scans
|
|
14
15
|
self._mutations_by_scope: Dict[str, list] = defaultdict(list)
|
|
@@ -218,30 +218,28 @@ def _analyze_all_subprojects(args, subprojects, output_dir: Path) -> list:
|
|
|
218
218
|
return all_results
|
|
219
219
|
|
|
220
220
|
|
|
221
|
+
def _build_filter_config(args):
|
|
222
|
+
"""Build a FilterConfig from CLI args (exclude patterns, gitignore flag)."""
|
|
223
|
+
from .core.config import FilterConfig
|
|
224
|
+
fc = FilterConfig()
|
|
225
|
+
if getattr(args, "exclude", None):
|
|
226
|
+
custom = [
|
|
227
|
+
f"*{p}*" if not p.startswith("*") and not p.endswith("*") else p
|
|
228
|
+
for p in args.exclude
|
|
229
|
+
]
|
|
230
|
+
fc.exclude_patterns = list(set(fc.exclude_patterns + custom))
|
|
231
|
+
if getattr(args, "no_gitignore", False):
|
|
232
|
+
fc.gitignore_enabled = False
|
|
233
|
+
return fc
|
|
234
|
+
|
|
235
|
+
|
|
221
236
|
def _analyze_subproject(args, subproject, output_dir: Path):
|
|
222
237
|
"""Analyze and export a single subproject."""
|
|
223
238
|
from .core.analyzer import ProjectAnalyzer
|
|
224
|
-
from .core.config import Config
|
|
239
|
+
from .core.config import Config
|
|
225
240
|
from .cli_exports import _export_simple_formats, _export_evolution
|
|
226
241
|
|
|
227
|
-
|
|
228
|
-
filter_config = FilterConfig()
|
|
229
|
-
|
|
230
|
-
# Apply custom exclude patterns if provided
|
|
231
|
-
if hasattr(args, "exclude") and args.exclude:
|
|
232
|
-
default_patterns = filter_config.exclude_patterns
|
|
233
|
-
custom_patterns = [
|
|
234
|
-
f"*{pattern}*"
|
|
235
|
-
if not pattern.startswith("*") and not pattern.endswith("*")
|
|
236
|
-
else pattern
|
|
237
|
-
for pattern in args.exclude
|
|
238
|
-
]
|
|
239
|
-
filter_config.exclude_patterns = list(set(default_patterns + custom_patterns))
|
|
240
|
-
|
|
241
|
-
# Apply gitignore setting
|
|
242
|
-
if hasattr(args, "no_gitignore") and args.no_gitignore:
|
|
243
|
-
filter_config.gitignore_enabled = False
|
|
244
|
-
|
|
242
|
+
filter_config = _build_filter_config(args)
|
|
245
243
|
config = Config(
|
|
246
244
|
mode=args.mode,
|
|
247
245
|
max_depth_enumeration=args.max_depth,
|
|
@@ -163,32 +163,8 @@ def _export_simple_formats(args, result, output_dir: Path, formats):
|
|
|
163
163
|
if args.verbose:
|
|
164
164
|
print(f" - {label}: {filepath} ({elapsed:.2f}s)")
|
|
165
165
|
|
|
166
|
-
# Unified project.yaml (single source of truth)
|
|
167
166
|
if "project-yaml" in formats:
|
|
168
|
-
|
|
169
|
-
# Auto-generate all views from project.yaml
|
|
170
|
-
data = load_project_yaml(str(yaml_path))
|
|
171
|
-
view_map = {
|
|
172
|
-
"project.toon.yaml": ToonViewGenerator(),
|
|
173
|
-
"context.md": ContextViewGenerator(),
|
|
174
|
-
"dashboard.html": HTMLDashboardGenerator(),
|
|
175
|
-
}
|
|
176
|
-
for filename, generator in view_map.items():
|
|
177
|
-
filepath = output_dir / filename
|
|
178
|
-
generator.generate(data, str(filepath))
|
|
179
|
-
if args.verbose:
|
|
180
|
-
print(f" - Generated view: {filepath}")
|
|
181
|
-
|
|
182
|
-
# Auto-validate consistency
|
|
183
|
-
from ..exporters.validate_project import validate_project_yaml
|
|
184
|
-
|
|
185
|
-
is_valid, issues = validate_project_yaml(output_dir, verbose=args.verbose)
|
|
186
|
-
if not is_valid and not args.verbose:
|
|
187
|
-
print(
|
|
188
|
-
f" ⚠ project.yaml validation: {len(issues)} issue(s)", file=sys.stderr
|
|
189
|
-
)
|
|
190
|
-
for issue in issues:
|
|
191
|
-
print(f" - {issue}", file=sys.stderr)
|
|
167
|
+
_export_project_yaml_bundle(args, result, output_dir)
|
|
192
168
|
|
|
193
169
|
if "yaml" in formats:
|
|
194
170
|
_export_yaml(args, result, output_dir)
|
|
@@ -221,6 +197,28 @@ def _export_yaml(args, result, output_dir: Path):
|
|
|
221
197
|
print(f" - YAML: {filepath}")
|
|
222
198
|
|
|
223
199
|
|
|
200
|
+
def _export_project_yaml_bundle(args, result, output_dir: Path) -> None:
|
|
201
|
+
"""Export project.yaml and auto-generate all derived views, then validate."""
|
|
202
|
+
yaml_path = _export_project_yaml(args, result, output_dir)
|
|
203
|
+
data = load_project_yaml(str(yaml_path))
|
|
204
|
+
view_map = {
|
|
205
|
+
"project.toon.yaml": ToonViewGenerator(),
|
|
206
|
+
"context.md": ContextViewGenerator(),
|
|
207
|
+
"dashboard.html": HTMLDashboardGenerator(),
|
|
208
|
+
}
|
|
209
|
+
for filename, generator in view_map.items():
|
|
210
|
+
filepath = output_dir / filename
|
|
211
|
+
generator.generate(data, str(filepath))
|
|
212
|
+
if args.verbose:
|
|
213
|
+
print(f" - Generated view: {filepath}")
|
|
214
|
+
from ..exporters.validate_project import validate_project_yaml
|
|
215
|
+
is_valid, issues = validate_project_yaml(output_dir, verbose=args.verbose)
|
|
216
|
+
if not is_valid and not args.verbose:
|
|
217
|
+
print(f" \u26a0 project.yaml validation: {len(issues)} issue(s)", file=sys.stderr)
|
|
218
|
+
for issue in issues:
|
|
219
|
+
print(f" - {issue}", file=sys.stderr)
|
|
220
|
+
|
|
221
|
+
|
|
224
222
|
def _export_mermaid_pngs(args, output_dir: Path) -> None:
|
|
225
223
|
"""Attempt PNG generation from .mmd files, with graceful fallback."""
|
|
226
224
|
if args.no_png:
|
|
@@ -354,6 +354,26 @@ def _export_chunked(
|
|
|
354
354
|
_chunked_impl(args, result, output_dir, source_path, formats, requested_formats)
|
|
355
355
|
|
|
356
356
|
|
|
357
|
+
# Maps suffix(es) → (prefix, suffix_str) for inserting after line 1.
|
|
358
|
+
# None = skip (no comment possible). "html" handled separately.
|
|
359
|
+
_COMMENT_PREFIXES = {
|
|
360
|
+
".mmd": ("%%", ""),
|
|
361
|
+
".export": ("%%", ""),
|
|
362
|
+
".yaml": ("#", ""),
|
|
363
|
+
".yml": ("#", ""),
|
|
364
|
+
".txt": ("#", ""),
|
|
365
|
+
".md": ("<!--", " -->"),
|
|
366
|
+
}
|
|
367
|
+
_SKIP_SUFFIXES = {".json"}
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _insert_after_first_line(content: str, tag: str, pre: str, suf: str) -> str:
|
|
371
|
+
"""Return content with a comment line inserted after the first newline."""
|
|
372
|
+
parts = content.split("\n", 1)
|
|
373
|
+
rest = parts[1] if len(parts) == 2 else ""
|
|
374
|
+
return f"{parts[0]}\n{pre} {tag}{suf}\n{rest}"
|
|
375
|
+
|
|
376
|
+
|
|
357
377
|
def _inject_generation_time(filepath: Path, elapsed: float) -> None:
|
|
358
378
|
"""Inject generation time comment into the second line of an exported file."""
|
|
359
379
|
try:
|
|
@@ -361,58 +381,22 @@ def _inject_generation_time(filepath: Path, elapsed: float) -> None:
|
|
|
361
381
|
if not path.exists():
|
|
362
382
|
return
|
|
363
383
|
suffix = path.suffix.lower()
|
|
364
|
-
name = path.name.lower()
|
|
365
|
-
|
|
366
|
-
# Only inject into text-based files
|
|
367
|
-
if suffix not in (
|
|
368
|
-
".yaml",
|
|
369
|
-
".yml",
|
|
370
|
-
".md",
|
|
371
|
-
".txt",
|
|
372
|
-
".mmd",
|
|
373
|
-
".html",
|
|
374
|
-
".json",
|
|
375
|
-
".export",
|
|
376
|
-
):
|
|
377
|
-
return
|
|
378
|
-
|
|
379
|
-
content = path.read_text(encoding="utf-8")
|
|
380
|
-
if not content:
|
|
381
|
-
return
|
|
382
|
-
|
|
383
384
|
tag = f"generated in {elapsed:.2f}s"
|
|
384
385
|
|
|
385
|
-
if suffix in
|
|
386
|
-
# Mermaid uses %% for comments
|
|
387
|
-
lines = content.split("\n", 1)
|
|
388
|
-
if len(lines) == 2:
|
|
389
|
-
content = f"{lines[0]}\n%% {tag}\n{lines[1]}"
|
|
390
|
-
else:
|
|
391
|
-
content = f"{lines[0]}\n%% {tag}\n"
|
|
392
|
-
elif suffix in (".yaml", ".yml", ".txt") or name.endswith(".toon.yaml"):
|
|
393
|
-
# YAML/text: insert '# generated in X.XXs' after first line
|
|
394
|
-
lines = content.split("\n", 1)
|
|
395
|
-
if len(lines) == 2:
|
|
396
|
-
content = f"{lines[0]}\n# {tag}\n{lines[1]}"
|
|
397
|
-
else:
|
|
398
|
-
content = f"{lines[0]}\n# {tag}\n"
|
|
399
|
-
elif suffix == ".md":
|
|
400
|
-
# Markdown: insert HTML comment after first line
|
|
401
|
-
lines = content.split("\n", 1)
|
|
402
|
-
if len(lines) == 2:
|
|
403
|
-
content = f"{lines[0]}\n<!-- {tag} -->\n{lines[1]}"
|
|
404
|
-
else:
|
|
405
|
-
content = f"{lines[0]}\n<!-- {tag} -->\n"
|
|
406
|
-
elif suffix == ".html":
|
|
407
|
-
# HTML: insert comment after <!DOCTYPE or <html>
|
|
408
|
-
content = content.replace("\n", f"\n<!-- {tag} -->\n", 1)
|
|
409
|
-
elif suffix == ".json":
|
|
410
|
-
# JSON doesn't support comments — skip
|
|
386
|
+
if suffix in _SKIP_SUFFIXES:
|
|
411
387
|
return
|
|
412
|
-
|
|
388
|
+
if suffix == ".html":
|
|
389
|
+
path.write_text(
|
|
390
|
+
path.read_text(encoding="utf-8").replace("\n", f"\n<!-- {tag} -->\n", 1),
|
|
391
|
+
encoding="utf-8",
|
|
392
|
+
)
|
|
413
393
|
return
|
|
414
|
-
|
|
415
|
-
|
|
394
|
+
pre, suf_str = _COMMENT_PREFIXES.get(suffix, (None, None))
|
|
395
|
+
if pre is None:
|
|
396
|
+
return
|
|
397
|
+
content = path.read_text(encoding="utf-8")
|
|
398
|
+
if content:
|
|
399
|
+
path.write_text(_insert_after_first_line(content, tag, pre, suf_str), encoding="utf-8")
|
|
416
400
|
except Exception:
|
|
417
401
|
pass # Never fail the export pipeline for a comment
|
|
418
402
|
|
|
@@ -282,63 +282,38 @@ def _build_missing_files_section(output_dir: Path, output_rel_path: str) -> List
|
|
|
282
282
|
return lines
|
|
283
283
|
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
analysis_file
|
|
288
|
-
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
285
|
+
# (has_key, file_key, candidate_filenames, default_path)
|
|
286
|
+
_OUTPUT_FILE_SPECS = [
|
|
287
|
+
("has_analysis_toon", "analysis_file", ("analysis.toon", "analysis.toon.yaml"), "analysis.toon"),
|
|
288
|
+
("has_map_toon", "map_file", ("map.toon.yaml",), "map.toon.yaml"),
|
|
289
|
+
("has_evolution_toon", "evolution_file", ("evolution.toon.yaml",), "evolution.toon.yaml"),
|
|
290
|
+
("has_project_toon_yaml","project_toon_file", ("project.toon.yaml",), "project.toon.yaml"),
|
|
291
|
+
("has_context_md", "context_file", ("context.md",), "context.md"),
|
|
292
|
+
("has_readme_md", "readme_file", ("README.md",), "README.md"),
|
|
293
|
+
("has_project_logic", "project_logic_file", ("project.toon", "project/project.toon", "project.toon.txt"), "project.toon"),
|
|
294
|
+
("has_validation_toon", "validation_file", ("project/validation.toon.yaml",), "project/validation.toon.yaml"),
|
|
295
|
+
("has_duplication_toon", "duplication_file", ("project/duplication.toon.yaml",), "project/duplication.toon.yaml"),
|
|
296
|
+
]
|
|
297
|
+
_ACTIONABLE_KEYS = {"has_analysis_toon", "has_map_toon", "has_evolution_toon",
|
|
298
|
+
"has_project_toon_yaml", "has_project_logic",
|
|
299
|
+
"has_validation_toon", "has_duplication_toon"}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _probe_output_files(output_dir: Path) -> dict:
|
|
303
|
+
"""Probe output_dir for known generated files, returning presence flags and paths."""
|
|
304
|
+
result: dict = {}
|
|
305
|
+
for has_key, file_key, candidates, default in _OUTPUT_FILE_SPECS:
|
|
306
|
+
found = _find_existing_prompt_file(output_dir, candidates)
|
|
307
|
+
result[has_key] = found is not None
|
|
308
|
+
result[file_key] = found or default
|
|
309
|
+
return result
|
|
304
310
|
|
|
305
|
-
analysis = {
|
|
306
|
-
"has_analysis_toon": analysis_file is not None,
|
|
307
|
-
"analysis_file": analysis_file or "analysis.toon",
|
|
308
|
-
"has_map_toon": map_file is not None,
|
|
309
|
-
"map_file": map_file or "map.toon.yaml",
|
|
310
|
-
"has_evolution_toon": evolution_file is not None,
|
|
311
|
-
"evolution_file": evolution_file or "evolution.toon.yaml",
|
|
312
|
-
"has_project_toon_yaml": project_toon_file is not None,
|
|
313
|
-
"project_toon_file": project_toon_file or "project.toon.yaml",
|
|
314
|
-
"has_context_md": context_file is not None,
|
|
315
|
-
"context_file": context_file or "context.md",
|
|
316
|
-
"has_readme_md": readme_file is not None,
|
|
317
|
-
"readme_file": readme_file or "README.md",
|
|
318
|
-
"has_project_logic": project_logic_file is not None,
|
|
319
|
-
"project_logic_file": project_logic_file or "project.toon",
|
|
320
|
-
"has_validation_toon": validation_file is not None,
|
|
321
|
-
"validation_file": validation_file or "project/validation.toon.yaml",
|
|
322
|
-
"has_duplication_toon": duplication_file is not None,
|
|
323
|
-
"duplication_file": duplication_file or "project/duplication.toon.yaml",
|
|
324
|
-
"is_chunked": subprojects is not None and len(subprojects) > 0,
|
|
325
|
-
"file_count": 0,
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
# Count total files
|
|
329
|
-
actionable_keys = {
|
|
330
|
-
"has_analysis_toon",
|
|
331
|
-
"has_map_toon",
|
|
332
|
-
"has_evolution_toon",
|
|
333
|
-
"has_project_toon_yaml",
|
|
334
|
-
"has_project_logic",
|
|
335
|
-
"has_validation_toon",
|
|
336
|
-
"has_duplication_toon",
|
|
337
|
-
}
|
|
338
|
-
for key, exists in analysis.items():
|
|
339
|
-
if key in actionable_keys and exists:
|
|
340
|
-
analysis["file_count"] += 1
|
|
341
311
|
|
|
312
|
+
def _analyze_generated_files(output_dir: Path, subprojects: list = None) -> dict:
|
|
313
|
+
"""Analyze which files were generated and determine appropriate focus areas."""
|
|
314
|
+
analysis = _probe_output_files(output_dir)
|
|
315
|
+
analysis["is_chunked"] = bool(subprojects)
|
|
316
|
+
analysis["file_count"] = sum(1 for k in _ACTIONABLE_KEYS if analysis.get(k))
|
|
342
317
|
return analysis
|
|
343
318
|
|
|
344
319
|
|
|
@@ -40,6 +40,7 @@ class ProjectAnalyzer:
|
|
|
40
40
|
def __init__(
|
|
41
41
|
self, config: Optional[Config] = None, project_path: Optional[Path] = None
|
|
42
42
|
):
|
|
43
|
+
"""Initialise the analyser with optional config and project path."""
|
|
43
44
|
self.config = config or FAST_CONFIG
|
|
44
45
|
self.project_path = project_path
|
|
45
46
|
self.cache = (
|
|
@@ -29,6 +29,7 @@ class FileAnalyzer:
|
|
|
29
29
|
"""Analyzes a single file."""
|
|
30
30
|
|
|
31
31
|
def __init__(self, config: Config, cache=None):
|
|
32
|
+
"""Initialise with project config and optional file cache."""
|
|
32
33
|
self.config = config
|
|
33
34
|
self.cache = cache
|
|
34
35
|
self._file_filter = FastFileFilter(config.filters)
|
|
@@ -149,23 +149,20 @@ def _process_standalone_function(
|
|
|
149
149
|
def _match_method_name(arrow_prop_re, method_re, func_re, line, reserved):
|
|
150
150
|
"""Return matched method name from any of the three patterns, or None."""
|
|
151
151
|
if arrow_prop_re:
|
|
152
|
-
|
|
153
|
-
if
|
|
154
|
-
|
|
155
|
-
if
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
fn = fm.group(1) or (fm.group(2) if len(fm.groups()) > 1 else None)
|
|
167
|
-
if fn and fn not in reserved:
|
|
168
|
-
return fn
|
|
152
|
+
m = arrow_prop_re.match(line)
|
|
153
|
+
if m:
|
|
154
|
+
name = m.group(1)
|
|
155
|
+
if name not in reserved and name != "constructor":
|
|
156
|
+
return name
|
|
157
|
+
for regex in (method_re, func_re):
|
|
158
|
+
if not regex:
|
|
159
|
+
continue
|
|
160
|
+
m = regex.match(line)
|
|
161
|
+
if not m:
|
|
162
|
+
continue
|
|
163
|
+
name = m.group(1) or (m.group(2) if len(m.groups()) > 1 else None)
|
|
164
|
+
if name and name not in reserved:
|
|
165
|
+
return name
|
|
169
166
|
return None
|
|
170
167
|
|
|
171
168
|
|
|
@@ -117,10 +117,12 @@ class TreeSitterParser:
|
|
|
117
117
|
"""
|
|
118
118
|
|
|
119
119
|
def __init__(self):
|
|
120
|
+
"""Initialise the parser pool, loading tree-sitter language modules."""
|
|
120
121
|
self._initialized = _init_tree_sitter()
|
|
121
122
|
|
|
122
123
|
@property
|
|
123
124
|
def available(self) -> bool:
|
|
125
|
+
"""Return True when tree-sitter is available for parsing."""
|
|
124
126
|
return self._initialized
|
|
125
127
|
|
|
126
128
|
def parse(self, source: bytes, ext: str) -> Optional[Any]:
|
|
@@ -11,6 +11,7 @@ class RefactoringAnalyzer:
|
|
|
11
11
|
"""Performs refactoring analysis on code."""
|
|
12
12
|
|
|
13
13
|
def __init__(self, config: Config, file_filter: FastFileFilter):
|
|
14
|
+
"""Initialise with project config and file filter."""
|
|
14
15
|
self.config = config
|
|
15
16
|
self.file_filter = file_filter
|
|
16
17
|
|
|
@@ -194,6 +194,17 @@ def _has_code2llm_output_manifest(path: Path) -> bool:
|
|
|
194
194
|
)
|
|
195
195
|
|
|
196
196
|
|
|
197
|
+
def _is_generated_by_name(name: str, p: Path) -> bool:
|
|
198
|
+
"""Return True when the file name alone marks it as a generated artefact."""
|
|
199
|
+
if name in GENERATED_EXACT_FILENAMES:
|
|
200
|
+
return True
|
|
201
|
+
if any(name.startswith(prefix) for prefix in GENERATED_PREFIXES):
|
|
202
|
+
return True
|
|
203
|
+
if name in GENERATED_SNIFF_FILENAMES and _looks_like_generated_content(p):
|
|
204
|
+
return True
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
|
|
197
208
|
def is_generated_artifact(
|
|
198
209
|
path: str | Path, project_root: Optional[Path] = None
|
|
199
210
|
) -> bool:
|
|
@@ -204,29 +215,14 @@ def is_generated_artifact(
|
|
|
204
215
|
return True
|
|
205
216
|
|
|
206
217
|
if project_root:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
candidate = Path(project_root).joinpath(*parts[: idx + 1])
|
|
212
|
-
if _has_code2llm_output_manifest(candidate):
|
|
218
|
+
for idx, part in enumerate(parts[:-1]):
|
|
219
|
+
if part == "project" and _has_code2llm_output_manifest(
|
|
220
|
+
Path(project_root).joinpath(*parts[: idx + 1])
|
|
221
|
+
):
|
|
213
222
|
return True
|
|
214
223
|
|
|
215
224
|
name = p.name.lower()
|
|
216
|
-
|
|
217
|
-
return True
|
|
218
|
-
if any(name.startswith(prefix) for prefix in GENERATED_PREFIXES):
|
|
219
|
-
return True
|
|
220
|
-
if name in GENERATED_SNIFF_FILENAMES and _looks_like_generated_content(p):
|
|
221
|
-
return True
|
|
222
|
-
|
|
223
|
-
# The default code2llm output folder is commonly named "project". Avoid
|
|
224
|
-
# treating arbitrary project/ directories as generated unless the filename
|
|
225
|
-
# itself is a known code2llm artefact.
|
|
226
|
-
if "project" in parts[:-1] and name in GENERATED_EXACT_FILENAMES:
|
|
227
|
-
return True
|
|
228
|
-
|
|
229
|
-
return False
|
|
225
|
+
return _is_generated_by_name(name, p)
|
|
230
226
|
|
|
231
227
|
|
|
232
228
|
def classify_source_path(path: str | Path, project_root: Optional[Path] = None) -> str:
|