sourcecode 1.20.0__tar.gz → 1.21.0__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.
- {sourcecode-1.20.0 → sourcecode-1.21.0}/PKG-INFO +3 -3
- {sourcecode-1.20.0 → sourcecode-1.21.0}/README.md +2 -2
- {sourcecode-1.20.0 → sourcecode-1.21.0}/pyproject.toml +1 -1
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/cli.py +14 -2
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/prepare_context.py +435 -17
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_signal_hierarchy.py +3 -1
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.continue-here.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.gitignore +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/.ruff.toml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/LICENSE +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/SECURITY.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/docs/privacy.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/docs/schema.md +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/raw +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/run_cli.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/__init__.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/conftest.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_cli.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_encoding_regression.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_schema.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_v1_10_regressions.py +0 -0
- {sourcecode-1.20.0 → sourcecode-1.21.0}/tests/test_workspace_analyzer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.21.0
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -221,7 +221,7 @@ Description-Content-Type: text/markdown
|
|
|
221
221
|
|
|
222
222
|
**Compressed AI-ready context for Java/Spring enterprise codebases.**
|
|
223
223
|
|
|
224
|
-

|
|
225
225
|

|
|
226
226
|
|
|
227
227
|
---
|
|
@@ -255,7 +255,7 @@ pipx install sourcecode
|
|
|
255
255
|
|
|
256
256
|
```bash
|
|
257
257
|
sourcecode version
|
|
258
|
-
# sourcecode 1.
|
|
258
|
+
# sourcecode 1.21.0
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Compressed AI-ready context for Java/Spring enterprise codebases.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -36,7 +36,7 @@ pipx install sourcecode
|
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
sourcecode version
|
|
39
|
-
# sourcecode 1.
|
|
39
|
+
# sourcecode 1.21.0
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
---
|
|
@@ -1727,9 +1727,9 @@ def prepare_context_cmd(
|
|
|
1727
1727
|
"changed_files": False, "affected_entry_points": False,
|
|
1728
1728
|
},
|
|
1729
1729
|
"delta": {
|
|
1730
|
-
"project_summary":
|
|
1730
|
+
"project_summary": False, "architecture_summary": False,
|
|
1731
1731
|
"relevant_files": True, "key_dependencies": False,
|
|
1732
|
-
"gaps":
|
|
1732
|
+
"gaps": True, "confidence": True,
|
|
1733
1733
|
"suspected_areas": False, "improvement_opportunities": False,
|
|
1734
1734
|
"test_gaps": False, "code_notes_summary": False,
|
|
1735
1735
|
"changed_files": True, "affected_entry_points": True,
|
|
@@ -1771,6 +1771,18 @@ def prepare_context_cmd(
|
|
|
1771
1771
|
out["changed_files"] = output.changed_files
|
|
1772
1772
|
if _task_include("affected_entry_points") and output.affected_entry_points:
|
|
1773
1773
|
out["affected_entry_points"] = output.affected_entry_points
|
|
1774
|
+
# Delta-specific impact fields
|
|
1775
|
+
if task == "delta":
|
|
1776
|
+
if output.since:
|
|
1777
|
+
out["since"] = output.since
|
|
1778
|
+
if output.impact_summary:
|
|
1779
|
+
out["impact_summary"] = output.impact_summary
|
|
1780
|
+
if output.affected_modules:
|
|
1781
|
+
out["affected_modules"] = output.affected_modules
|
|
1782
|
+
if output.risk_areas:
|
|
1783
|
+
out["risk_areas"] = output.risk_areas
|
|
1784
|
+
if output.why_these_files:
|
|
1785
|
+
out["reasoning"] = output.why_these_files
|
|
1774
1786
|
if output.limitations:
|
|
1775
1787
|
out["limitations"] = output.limitations
|
|
1776
1788
|
if output.symptom:
|
|
@@ -324,6 +324,11 @@ class TaskOutput:
|
|
|
324
324
|
symptom: Optional[str] = None # fix-bug only
|
|
325
325
|
related_notes: list[dict] = field(default_factory=list) # fix-bug + symptom only
|
|
326
326
|
symptom_note: Optional[str] = None # fix-bug: cross-layer synonym note
|
|
327
|
+
# delta-specific impact fields
|
|
328
|
+
impact_summary: Optional[str] = None
|
|
329
|
+
affected_modules: list[str] = field(default_factory=list)
|
|
330
|
+
risk_areas: list[dict] = field(default_factory=list)
|
|
331
|
+
since: Optional[str] = None
|
|
327
332
|
|
|
328
333
|
|
|
329
334
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -640,14 +645,37 @@ class TaskContextBuilder:
|
|
|
640
645
|
test_set = {p for p in all_paths if self._is_test(p)}
|
|
641
646
|
source_set = {p for p in all_paths if not self._is_test(p) and self._is_source(p)}
|
|
642
647
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
648
|
+
# Delta uses a dedicated impact-analysis path — never the generic ranker.
|
|
649
|
+
_delta_impact_summary: Optional[str] = None
|
|
650
|
+
_delta_affected_modules: list[str] = []
|
|
651
|
+
_delta_risk_areas: list[dict] = []
|
|
652
|
+
_delta_why: dict[str, str] = {}
|
|
653
|
+
_delta_analysis_gaps: list[str] = []
|
|
654
|
+
|
|
655
|
+
if task_name == "delta":
|
|
656
|
+
_delta_changed_list: list[str] = sorted(_delta_files) if _delta_files else []
|
|
657
|
+
(
|
|
658
|
+
relevant_files,
|
|
659
|
+
_delta_impact_summary,
|
|
660
|
+
_delta_affected_modules,
|
|
661
|
+
_delta_risk_areas,
|
|
662
|
+
_delta_why,
|
|
663
|
+
_delta_analysis_gaps,
|
|
664
|
+
) = self._build_delta_impact(
|
|
665
|
+
changed_files=_delta_changed_list,
|
|
666
|
+
all_paths=all_paths,
|
|
667
|
+
entry_points=entry_points,
|
|
668
|
+
since=since,
|
|
669
|
+
)
|
|
670
|
+
else:
|
|
671
|
+
relevant_files = self._rank_files(
|
|
672
|
+
task_name, spec, all_paths, entry_set, test_set,
|
|
673
|
+
monorepo_packages=sm.monorepo_packages if sm.monorepo_packages else None,
|
|
674
|
+
git_hotspots=git_hotspots,
|
|
675
|
+
uncommitted_files=uncommitted_files,
|
|
676
|
+
code_notes=cn_notes_for_ranking if cn_notes_for_ranking else None,
|
|
677
|
+
delta_files=None,
|
|
678
|
+
)
|
|
651
679
|
|
|
652
680
|
# ── 6b. Symptom keyword boost + related notes (fix-bug + --symptom) ──
|
|
653
681
|
symptom_keywords: list[str] = []
|
|
@@ -805,22 +833,30 @@ class TaskContextBuilder:
|
|
|
805
833
|
|
|
806
834
|
conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
|
|
807
835
|
confidence = conf_summary.overall
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
836
|
+
if task_name == "delta":
|
|
837
|
+
# Use delta-specific gaps; ConfidenceAnalyzer gaps are about full-repo
|
|
838
|
+
# detection quality and are not meaningful for an incremental diff.
|
|
839
|
+
gaps = _delta_analysis_gaps
|
|
840
|
+
if _mybatis_warning:
|
|
841
|
+
gaps.append(_mybatis_warning["reason"])
|
|
842
|
+
else:
|
|
843
|
+
gaps = [g.reason for g in analysis_gaps]
|
|
844
|
+
if _mybatis_warning:
|
|
845
|
+
gaps.append(_mybatis_warning["reason"])
|
|
811
846
|
|
|
812
847
|
# ── 9. why_these_files ────────────────────────────────────────────────
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
848
|
+
if task_name == "delta":
|
|
849
|
+
why_these_files = _delta_why
|
|
850
|
+
else:
|
|
851
|
+
why_these_files = {rf.path: rf.reason for rf in relevant_files}
|
|
816
852
|
|
|
817
|
-
# ── 10. Delta: git changed files
|
|
853
|
+
# ── 10. Delta: git changed files + entry points ───────────────────────
|
|
818
854
|
changed_files: list[str] = []
|
|
819
855
|
affected_entry_points: list[str] = []
|
|
820
856
|
if task_name == "delta":
|
|
821
857
|
changed_files = sorted(_delta_files) if _delta_files else self._get_git_changed_files(since=since)
|
|
822
|
-
|
|
823
|
-
affected_entry_points = [f for f in changed_files if f in
|
|
858
|
+
_ep_set = {ep.path for ep in entry_points}
|
|
859
|
+
affected_entry_points = [f for f in changed_files if f in _ep_set]
|
|
824
860
|
|
|
825
861
|
return TaskOutput(
|
|
826
862
|
task=task_name,
|
|
@@ -842,6 +878,10 @@ class TaskContextBuilder:
|
|
|
842
878
|
symptom=symptom if task_name == "fix-bug" and symptom else None,
|
|
843
879
|
related_notes=related_notes,
|
|
844
880
|
symptom_note=symptom_note,
|
|
881
|
+
impact_summary=_delta_impact_summary,
|
|
882
|
+
affected_modules=_delta_affected_modules,
|
|
883
|
+
risk_areas=_delta_risk_areas,
|
|
884
|
+
since=since if task_name == "delta" else None,
|
|
845
885
|
)
|
|
846
886
|
|
|
847
887
|
def render_prompt(self, output: TaskOutput) -> str:
|
|
@@ -1133,6 +1173,384 @@ class TaskContextBuilder:
|
|
|
1133
1173
|
def _is_source(self, path: str) -> bool:
|
|
1134
1174
|
return Path(path).suffix.lower() in _SOURCE_EXTENSIONS
|
|
1135
1175
|
|
|
1176
|
+
# ── Delta impact analysis ─────────────────────────────────────────────────
|
|
1177
|
+
|
|
1178
|
+
@staticmethod
|
|
1179
|
+
def _classify_changed_file(path: str) -> dict[str, Any]:
|
|
1180
|
+
"""Classify a changed file by artifact type, risk areas, and impact level.
|
|
1181
|
+
|
|
1182
|
+
Returns dict: artifact_type, risk_areas, impact_level, is_noise, module.
|
|
1183
|
+
Pure path/name heuristics — no file reads, fully deterministic.
|
|
1184
|
+
"""
|
|
1185
|
+
norm = path.replace("\\", "/")
|
|
1186
|
+
name = Path(path).name
|
|
1187
|
+
stem = Path(path).stem
|
|
1188
|
+
suffix = Path(path).suffix.lower()
|
|
1189
|
+
norm_lower = norm.lower()
|
|
1190
|
+
stem_lower = stem.lower()
|
|
1191
|
+
name_lower = name.lower()
|
|
1192
|
+
|
|
1193
|
+
_CODE_EXTS = frozenset({
|
|
1194
|
+
".py", ".js", ".ts", ".tsx", ".jsx", ".java", ".kt", ".go",
|
|
1195
|
+
".rs", ".rb", ".php", ".cs", ".dart", ".mjs", ".cjs", ".scala",
|
|
1196
|
+
})
|
|
1197
|
+
_CONFIG_EXTS = frozenset({
|
|
1198
|
+
".yml", ".yaml", ".json", ".xml", ".toml", ".properties",
|
|
1199
|
+
".env", ".cfg", ".ini", ".conf",
|
|
1200
|
+
})
|
|
1201
|
+
|
|
1202
|
+
# IDE/hidden-tool directories → noise, skip impact analysis
|
|
1203
|
+
_IDE_DIR_NAMES = frozenset({
|
|
1204
|
+
".idea", ".vscode", ".eclipse", ".fleet", ".git", ".github",
|
|
1205
|
+
".circleci", ".travis", ".teamcity", ".gradle", ".mvn",
|
|
1206
|
+
})
|
|
1207
|
+
path_dir_parts = norm_lower.split("/")[:-1] # all components except filename
|
|
1208
|
+
if any(part in _IDE_DIR_NAMES for part in path_dir_parts):
|
|
1209
|
+
return {
|
|
1210
|
+
"artifact_type": "ide_noise",
|
|
1211
|
+
"risk_areas": [],
|
|
1212
|
+
"impact_level": "noise",
|
|
1213
|
+
"is_noise": True,
|
|
1214
|
+
"module": "",
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
module = _extract_ddd_domain(path)
|
|
1218
|
+
|
|
1219
|
+
# Tests (before other checks to avoid misclassifying TestFoo as service etc.)
|
|
1220
|
+
_is_test = (
|
|
1221
|
+
(stem_lower.startswith("test") and len(stem_lower) > 4)
|
|
1222
|
+
or (stem_lower.endswith("test") and len(stem_lower) > 4)
|
|
1223
|
+
or stem_lower.endswith("tests")
|
|
1224
|
+
or stem_lower.endswith("spec")
|
|
1225
|
+
or any(t in f"/{norm_lower}/" for t in (
|
|
1226
|
+
"/test/", "/tests/", "/spec/", "/specs/", "/__tests__/", "/it/",
|
|
1227
|
+
))
|
|
1228
|
+
)
|
|
1229
|
+
if _is_test:
|
|
1230
|
+
return {"artifact_type": "test", "risk_areas": ["tests"], "impact_level": "low", "is_noise": False, "module": module}
|
|
1231
|
+
|
|
1232
|
+
# Security surface
|
|
1233
|
+
_SECURITY_KW = ("security", "auth", "jwt", "token", "permission", "role",
|
|
1234
|
+
"credential", "encrypt", "decrypt", "oauth", "saml", "ldap",
|
|
1235
|
+
"password", "secret")
|
|
1236
|
+
if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _SECURITY_KW):
|
|
1237
|
+
impact = "critical" if any(kw in stem_lower for kw in ("security", "auth", "jwt")) else "high"
|
|
1238
|
+
return {"artifact_type": "security", "risk_areas": ["security"], "impact_level": impact, "is_noise": False, "module": module}
|
|
1239
|
+
|
|
1240
|
+
# API / controller layer
|
|
1241
|
+
_API_KW = ("controller", "restcontroller", "resource", "handler",
|
|
1242
|
+
"router", "route", "endpoint", "servlet")
|
|
1243
|
+
if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _API_KW):
|
|
1244
|
+
return {"artifact_type": "api_endpoint", "risk_areas": ["api"], "impact_level": "high", "is_noise": False, "module": module}
|
|
1245
|
+
|
|
1246
|
+
# Business logic / services
|
|
1247
|
+
if suffix in _CODE_EXTS and "service" in stem_lower:
|
|
1248
|
+
return {"artifact_type": "business_logic", "risk_areas": ["transactions", "business_logic"], "impact_level": "high", "is_noise": False, "module": module}
|
|
1249
|
+
|
|
1250
|
+
# Data access
|
|
1251
|
+
_DAO_KW = ("repository", "repositoryimpl", "dao", "daoimpl", "store")
|
|
1252
|
+
if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _DAO_KW):
|
|
1253
|
+
return {"artifact_type": "data_access", "risk_areas": ["persistence"], "impact_level": "high", "is_noise": False, "module": module}
|
|
1254
|
+
if "mapper" in stem_lower:
|
|
1255
|
+
atype = "mybatis_mapper" if suffix == ".xml" else "data_access"
|
|
1256
|
+
return {"artifact_type": atype, "risk_areas": ["persistence"], "impact_level": "high", "is_noise": False, "module": module}
|
|
1257
|
+
|
|
1258
|
+
# Spring / app config files (by canonical name)
|
|
1259
|
+
if name_lower in ("application.yml", "application.yaml", "application.properties",
|
|
1260
|
+
"bootstrap.yml", "bootstrap.yaml", "bootstrap.properties"):
|
|
1261
|
+
return {"artifact_type": "spring_config", "risk_areas": ["config"], "impact_level": "high", "is_noise": False, "module": module}
|
|
1262
|
+
if name_lower.startswith("application-") and suffix in (".yml", ".yaml", ".properties"):
|
|
1263
|
+
return {"artifact_type": "spring_profile", "risk_areas": ["config"], "impact_level": "medium", "is_noise": False, "module": module}
|
|
1264
|
+
if name_lower in ("pom.xml", "build.gradle", "build.gradle.kts",
|
|
1265
|
+
"settings.gradle", "settings.gradle.kts"):
|
|
1266
|
+
return {"artifact_type": "build_manifest", "risk_areas": ["config", "dependencies"], "impact_level": "medium", "is_noise": False, "module": module}
|
|
1267
|
+
|
|
1268
|
+
# Configuration classes / files
|
|
1269
|
+
_CONFIG_STEM_KW = ("config", "configuration", "properties", "settings")
|
|
1270
|
+
if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _CONFIG_STEM_KW):
|
|
1271
|
+
return {"artifact_type": "configuration", "risk_areas": ["config"], "impact_level": "medium", "is_noise": False, "module": module}
|
|
1272
|
+
|
|
1273
|
+
# DB migrations / SQL
|
|
1274
|
+
if suffix == ".sql" or any(kw in norm_lower for kw in ("migration", "flyway", "liquibase", "changelog")):
|
|
1275
|
+
return {"artifact_type": "db_migration", "risk_areas": ["persistence"], "impact_level": "high", "is_noise": False, "module": module}
|
|
1276
|
+
|
|
1277
|
+
# Domain models / entities
|
|
1278
|
+
_ENTITY_KW = ("entity", "model", "domain", "aggregate", "valueobject")
|
|
1279
|
+
if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _ENTITY_KW):
|
|
1280
|
+
return {"artifact_type": "domain_model", "risk_areas": ["persistence"], "impact_level": "medium", "is_noise": False, "module": module}
|
|
1281
|
+
|
|
1282
|
+
# DTOs / request-response objects
|
|
1283
|
+
_DTO_KW = ("dto", "request", "response", "payload", "command", "query", "event")
|
|
1284
|
+
if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _DTO_KW):
|
|
1285
|
+
return {"artifact_type": "dto", "risk_areas": [], "impact_level": "low", "is_noise": False, "module": module}
|
|
1286
|
+
|
|
1287
|
+
# Generic source code
|
|
1288
|
+
if suffix in _CODE_EXTS:
|
|
1289
|
+
return {"artifact_type": "unknown_source", "risk_areas": [], "impact_level": "medium", "is_noise": False, "module": module}
|
|
1290
|
+
|
|
1291
|
+
# Config / data files
|
|
1292
|
+
if suffix in _CONFIG_EXTS:
|
|
1293
|
+
return {"artifact_type": "unknown_config", "risk_areas": ["config"], "impact_level": "low", "is_noise": False, "module": module}
|
|
1294
|
+
|
|
1295
|
+
# Docs
|
|
1296
|
+
if suffix in (".md", ".rst", ".txt", ".adoc"):
|
|
1297
|
+
return {"artifact_type": "documentation", "risk_areas": [], "impact_level": "low", "is_noise": False, "module": module}
|
|
1298
|
+
|
|
1299
|
+
return {"artifact_type": "binary_or_unknown", "risk_areas": [], "impact_level": "noise", "is_noise": True, "module": module}
|
|
1300
|
+
|
|
1301
|
+
def _build_delta_impact(
|
|
1302
|
+
self,
|
|
1303
|
+
changed_files: list[str],
|
|
1304
|
+
all_paths: list[str],
|
|
1305
|
+
entry_points: list,
|
|
1306
|
+
since: Optional[str],
|
|
1307
|
+
) -> tuple[list[RelevantFile], str, list[str], list[dict[str, Any]], dict[str, str], list[str]]:
|
|
1308
|
+
"""Build incremental impact analysis for changed files.
|
|
1309
|
+
|
|
1310
|
+
Returns:
|
|
1311
|
+
(relevant_files, impact_summary, affected_modules, risk_areas,
|
|
1312
|
+
why_these_files, analysis_gaps)
|
|
1313
|
+
|
|
1314
|
+
Changed files are always included in relevant_files (never dropped by score).
|
|
1315
|
+
Related files from the same module/directory are appended with lower scores.
|
|
1316
|
+
"""
|
|
1317
|
+
_IMPACT_SCORE: dict[str, float] = {
|
|
1318
|
+
"critical": 1.00,
|
|
1319
|
+
"high": 0.85,
|
|
1320
|
+
"medium": 0.65,
|
|
1321
|
+
"low": 0.40,
|
|
1322
|
+
"noise": 0.10,
|
|
1323
|
+
}
|
|
1324
|
+
_SEV_ORDER = ["noise", "low", "medium", "high", "critical"]
|
|
1325
|
+
|
|
1326
|
+
if not changed_files:
|
|
1327
|
+
return (
|
|
1328
|
+
[],
|
|
1329
|
+
"No changes detected — verify the git ref passed to --since",
|
|
1330
|
+
[],
|
|
1331
|
+
[],
|
|
1332
|
+
{},
|
|
1333
|
+
["No changed files found. Check that --since ref exists and the diff is non-empty."],
|
|
1334
|
+
)
|
|
1335
|
+
|
|
1336
|
+
ep_paths = {ep.path for ep in entry_points}
|
|
1337
|
+
|
|
1338
|
+
# ── Step 1: classify every changed file ───────────────────────────────
|
|
1339
|
+
classifications: dict[str, dict[str, Any]] = {
|
|
1340
|
+
f: self._classify_changed_file(f) for f in changed_files
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
# ── Step 2: build relevant_files from the changed set ─────────────────
|
|
1344
|
+
relevant: list[RelevantFile] = []
|
|
1345
|
+
why: dict[str, str] = {}
|
|
1346
|
+
affected_modules_set: set[str] = set()
|
|
1347
|
+
changed_dirs: set[str] = set()
|
|
1348
|
+
risk_acc: dict[str, dict[str, Any]] = {} # area → {files, severity}
|
|
1349
|
+
ref_label = since or "HEAD~1"
|
|
1350
|
+
|
|
1351
|
+
for path, cls in classifications.items():
|
|
1352
|
+
score = _IMPACT_SCORE.get(cls["impact_level"], 0.50)
|
|
1353
|
+
module = cls["module"]
|
|
1354
|
+
|
|
1355
|
+
if module:
|
|
1356
|
+
affected_modules_set.add(module)
|
|
1357
|
+
if not cls["is_noise"]:
|
|
1358
|
+
parent = str(Path(path).parent).replace("\\", "/")
|
|
1359
|
+
if parent and parent != ".":
|
|
1360
|
+
changed_dirs.add(parent)
|
|
1361
|
+
|
|
1362
|
+
for area in cls["risk_areas"]:
|
|
1363
|
+
if area not in risk_acc:
|
|
1364
|
+
risk_acc[area] = {"files": [], "severity": "noise"}
|
|
1365
|
+
risk_acc[area]["files"].append(path)
|
|
1366
|
+
cur_idx = _SEV_ORDER.index(risk_acc[area]["severity"])
|
|
1367
|
+
new_idx = _SEV_ORDER.index(cls["impact_level"])
|
|
1368
|
+
if new_idx > cur_idx:
|
|
1369
|
+
risk_acc[area]["severity"] = cls["impact_level"]
|
|
1370
|
+
|
|
1371
|
+
artifact_display = cls["artifact_type"].replace("_", " ")
|
|
1372
|
+
reason_parts = [f"changed since {ref_label}", f"artifact: {cls['artifact_type']}"]
|
|
1373
|
+
if cls["risk_areas"]:
|
|
1374
|
+
reason_parts.append(f"risk: {', '.join(cls['risk_areas'])}")
|
|
1375
|
+
reason = ", ".join(reason_parts)
|
|
1376
|
+
|
|
1377
|
+
why_parts = [f"Changed {artifact_display}"]
|
|
1378
|
+
if module:
|
|
1379
|
+
why_parts.append(f"in module '{module}'")
|
|
1380
|
+
if cls["risk_areas"]:
|
|
1381
|
+
why_parts.append(f"Risk: {', '.join(cls['risk_areas'])}")
|
|
1382
|
+
why_str = ". ".join(why_parts) + "."
|
|
1383
|
+
|
|
1384
|
+
role = "entrypoint" if path in ep_paths else ("source" if not cls["is_noise"] else "noise")
|
|
1385
|
+
relevant.append(RelevantFile(path=path, role=role, score=round(score, 2), reason=reason, why=why_str))
|
|
1386
|
+
why[path] = why_str
|
|
1387
|
+
|
|
1388
|
+
relevant.sort(key=lambda f: (-f.score, f.path))
|
|
1389
|
+
|
|
1390
|
+
# ── Step 3: expand to related files (same module or same directory) ───
|
|
1391
|
+
existing_paths = {rf.path for rf in relevant}
|
|
1392
|
+
|
|
1393
|
+
_HIGH_IMPACT_STEMS = frozenset({
|
|
1394
|
+
"controller", "restcontroller", "resource", "handler",
|
|
1395
|
+
"service", "serviceimpl", "servicefacade",
|
|
1396
|
+
"repository", "repositoryimpl", "dao", "daoimpl",
|
|
1397
|
+
"mapper", "security", "securityconfig",
|
|
1398
|
+
"config", "configuration", "filter", "authcontroller",
|
|
1399
|
+
})
|
|
1400
|
+
|
|
1401
|
+
related: list[tuple[float, str, RelevantFile]] = []
|
|
1402
|
+
for path in all_paths:
|
|
1403
|
+
if path in existing_paths:
|
|
1404
|
+
continue
|
|
1405
|
+
suffix = Path(path).suffix.lower()
|
|
1406
|
+
if suffix not in _ALL_EXTENSIONS:
|
|
1407
|
+
continue
|
|
1408
|
+
stem_lower = Path(path).stem.lower()
|
|
1409
|
+
if not any(s in stem_lower for s in _HIGH_IMPACT_STEMS):
|
|
1410
|
+
continue
|
|
1411
|
+
|
|
1412
|
+
parent = str(Path(path).parent).replace("\\", "/")
|
|
1413
|
+
path_module = _extract_ddd_domain(path)
|
|
1414
|
+
|
|
1415
|
+
in_same_module = bool(path_module and path_module in affected_modules_set)
|
|
1416
|
+
in_same_dir = parent in changed_dirs
|
|
1417
|
+
|
|
1418
|
+
if not (in_same_module or in_same_dir):
|
|
1419
|
+
continue
|
|
1420
|
+
|
|
1421
|
+
rel_cls = self._classify_changed_file(path)
|
|
1422
|
+
if rel_cls["is_noise"]:
|
|
1423
|
+
continue
|
|
1424
|
+
|
|
1425
|
+
rel_score = _IMPACT_SCORE.get(rel_cls["impact_level"], 0.50) * 0.50
|
|
1426
|
+
ctx_type = "module" if in_same_module else "directory"
|
|
1427
|
+
ctx_val = path_module if in_same_module else parent
|
|
1428
|
+
|
|
1429
|
+
triggers = [
|
|
1430
|
+
Path(f).name for f in changed_files
|
|
1431
|
+
if (
|
|
1432
|
+
(_extract_ddd_domain(f) == path_module if in_same_module
|
|
1433
|
+
else str(Path(f).parent).replace("\\", "/") == parent)
|
|
1434
|
+
)
|
|
1435
|
+
]
|
|
1436
|
+
reason = f"related: same {ctx_type} '{ctx_val}', artifact: {rel_cls['artifact_type']}"
|
|
1437
|
+
why_str = (
|
|
1438
|
+
f"In changed {ctx_type} '{ctx_val}'. "
|
|
1439
|
+
f"May be affected by: {', '.join(triggers[:3])}"
|
|
1440
|
+
)
|
|
1441
|
+
role = "entrypoint" if path in ep_paths else "source"
|
|
1442
|
+
related.append((rel_score, path, RelevantFile(
|
|
1443
|
+
path=path, role=role, score=round(rel_score, 2), reason=reason, why=why_str
|
|
1444
|
+
)))
|
|
1445
|
+
why[path] = why_str
|
|
1446
|
+
|
|
1447
|
+
related.sort(key=lambda x: (-x[0], x[1]))
|
|
1448
|
+
relevant.extend(rf for _, _, rf in related[:10])
|
|
1449
|
+
|
|
1450
|
+
# ── Step 4: impact summary ─────────────────────────────────────────────
|
|
1451
|
+
type_counts: dict[str, int] = {}
|
|
1452
|
+
all_risk_areas: set[str] = set()
|
|
1453
|
+
noise_count = 0
|
|
1454
|
+
for cls in classifications.values():
|
|
1455
|
+
t = cls["artifact_type"]
|
|
1456
|
+
type_counts[t] = type_counts.get(t, 0) + 1
|
|
1457
|
+
all_risk_areas.update(cls["risk_areas"])
|
|
1458
|
+
if cls["is_noise"]:
|
|
1459
|
+
noise_count += 1
|
|
1460
|
+
meaningful = len(changed_files) - noise_count
|
|
1461
|
+
|
|
1462
|
+
_SUMMARY_LABELS: dict[str, str] = {
|
|
1463
|
+
"security": "security file(s)",
|
|
1464
|
+
"api_endpoint": "API endpoint(s)",
|
|
1465
|
+
"business_logic": "service(s)",
|
|
1466
|
+
"data_access": "data access file(s)",
|
|
1467
|
+
"mybatis_mapper": "MyBatis mapper(s)",
|
|
1468
|
+
"spring_config": "Spring config file(s)",
|
|
1469
|
+
"spring_profile": "Spring profile config(s)",
|
|
1470
|
+
"configuration": "configuration file(s)",
|
|
1471
|
+
"build_manifest": "build manifest(s)",
|
|
1472
|
+
"db_migration": "database migration(s)",
|
|
1473
|
+
"domain_model": "domain model(s)",
|
|
1474
|
+
"dto": "DTO(s)",
|
|
1475
|
+
"test": "test file(s)",
|
|
1476
|
+
"unknown_source": "source file(s)",
|
|
1477
|
+
"unknown_config": "config file(s)",
|
|
1478
|
+
"documentation": "documentation file(s)",
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
if meaningful == 0:
|
|
1482
|
+
impact_summary = (
|
|
1483
|
+
f"{noise_count} IDE/tooling file(s) changed"
|
|
1484
|
+
" — no semantic impact on application logic"
|
|
1485
|
+
)
|
|
1486
|
+
else:
|
|
1487
|
+
_sev_rank = {"critical": 4, "high": 3, "medium": 2, "low": 1, "noise": 0}
|
|
1488
|
+
parts = []
|
|
1489
|
+
for atype, count in sorted(
|
|
1490
|
+
type_counts.items(),
|
|
1491
|
+
key=lambda kv: -_sev_rank.get(
|
|
1492
|
+
classifications[next(
|
|
1493
|
+
(f for f in changed_files if classifications[f]["artifact_type"] == kv[0]),
|
|
1494
|
+
changed_files[0],
|
|
1495
|
+
)]["impact_level"], 0,
|
|
1496
|
+
),
|
|
1497
|
+
):
|
|
1498
|
+
if atype in ("ide_noise", "binary_or_unknown"):
|
|
1499
|
+
continue
|
|
1500
|
+
label = _SUMMARY_LABELS.get(atype, f"source file(s) ({atype})")
|
|
1501
|
+
parts.append(f"{count} {label}")
|
|
1502
|
+
impact_summary = "; ".join(parts) if parts else f"{meaningful} source file(s) changed"
|
|
1503
|
+
if all_risk_areas:
|
|
1504
|
+
impact_summary += f" — risk areas: {', '.join(sorted(all_risk_areas))}"
|
|
1505
|
+
if noise_count > 0:
|
|
1506
|
+
impact_summary += f" ({noise_count} IDE/tooling file(s) excluded)"
|
|
1507
|
+
|
|
1508
|
+
# ── Step 5: risk_areas output list ─────────────────────────────────────
|
|
1509
|
+
risk_areas_out: list[dict[str, Any]] = sorted(
|
|
1510
|
+
[
|
|
1511
|
+
{
|
|
1512
|
+
"area": area,
|
|
1513
|
+
"severity": info["severity"],
|
|
1514
|
+
"affected_files": sorted(info["files"])[:5],
|
|
1515
|
+
}
|
|
1516
|
+
for area, info in risk_acc.items()
|
|
1517
|
+
],
|
|
1518
|
+
key=lambda x: (-_SEV_ORDER.index(x["severity"]), x["area"]),
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
# ── Step 6: analysis gaps ──────────────────────────────────────────────
|
|
1522
|
+
analysis_gaps: list[str] = [
|
|
1523
|
+
"Related file expansion uses module/package and directory heuristics — import graph not traced",
|
|
1524
|
+
]
|
|
1525
|
+
if noise_count > 0 and meaningful > 0:
|
|
1526
|
+
analysis_gaps.append(
|
|
1527
|
+
f"{noise_count} IDE/tooling file(s) in diff excluded from impact analysis"
|
|
1528
|
+
)
|
|
1529
|
+
elif noise_count > 0 and meaningful == 0:
|
|
1530
|
+
analysis_gaps.append(
|
|
1531
|
+
"All changed files are IDE/tooling — no actionable semantic impact detected"
|
|
1532
|
+
)
|
|
1533
|
+
unknown_sources = [f for f, cls in classifications.items() if cls["artifact_type"] == "unknown_source"]
|
|
1534
|
+
if unknown_sources:
|
|
1535
|
+
analysis_gaps.append(
|
|
1536
|
+
f"{len(unknown_sources)} source file(s) could not be classified by artifact type: "
|
|
1537
|
+
+ ", ".join(Path(f).name for f in unknown_sources[:3])
|
|
1538
|
+
)
|
|
1539
|
+
if not affected_modules_set and any(not cls["is_noise"] for cls in classifications.values()):
|
|
1540
|
+
analysis_gaps.append(
|
|
1541
|
+
"DDD module/package structure not detected in changed paths"
|
|
1542
|
+
" — related file expansion uses directory proximity only"
|
|
1543
|
+
)
|
|
1544
|
+
|
|
1545
|
+
return (
|
|
1546
|
+
relevant,
|
|
1547
|
+
impact_summary,
|
|
1548
|
+
sorted(affected_modules_set),
|
|
1549
|
+
risk_areas_out,
|
|
1550
|
+
why,
|
|
1551
|
+
analysis_gaps,
|
|
1552
|
+
)
|
|
1553
|
+
|
|
1136
1554
|
def _get_git_changed_files(self, since: Optional[str] = None) -> list[str]:
|
|
1137
1555
|
"""Get files changed since a git ref (default: HEAD~1) relative to self.root.
|
|
1138
1556
|
|
|
@@ -488,9 +488,11 @@ class TestDeltaTask:
|
|
|
488
488
|
assert result.exit_code == 0, result.output
|
|
489
489
|
data = json.loads(result.output)
|
|
490
490
|
assert data["task"] == "delta"
|
|
491
|
-
|
|
491
|
+
# delta is change-focused: no project_summary, has impact fields instead
|
|
492
|
+
assert "project_summary" not in data
|
|
492
493
|
assert "relevant_files" in data
|
|
493
494
|
assert "confidence" in data
|
|
495
|
+
assert "impact_summary" in data
|
|
494
496
|
|
|
495
497
|
def test_delta_task_returns_changed_files_field(self, tmp_project: Path) -> None:
|
|
496
498
|
result = runner.invoke(
|
{sourcecode-1.20.0 → sourcecode-1.21.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md
RENAMED
|
File without changes
|
{sourcecode-1.20.0 → sourcecode-1.21.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md
RENAMED
|
File without changes
|
{sourcecode-1.20.0 → sourcecode-1.21.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md
RENAMED
|
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
|