sourcecode 1.28.0__tar.gz → 1.30.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.30.0/.continue-here.md +118 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/PKG-INFO +3 -3
- {sourcecode-1.28.0 → sourcecode-1.30.0}/README.md +2 -2
- {sourcecode-1.28.0 → sourcecode-1.30.0}/pyproject.toml +1 -1
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/cli.py +58 -1
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/prepare_context.py +197 -10
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_v1_10_regressions.py +39 -13
- sourcecode-1.28.0/.continue-here.md +0 -108
- {sourcecode-1.28.0 → sourcecode-1.30.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/.gitignore +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/.ruff.toml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/LICENSE +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/SECURITY.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/docs/privacy.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/docs/schema.md +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/raw +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/run_cli.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/__init__.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/conftest.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_cli.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_encoding_regression.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_schema.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.28.0 → sourcecode-1.30.0}/tests/test_workspace_analyzer.py +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Continue Here — atlas-cli session 5
|
|
2
|
+
|
|
3
|
+
**Paused:** 2026-05-16 (sesión 5)
|
|
4
|
+
**Repo:** `/Users/user/Downloads/atlas-cli`
|
|
5
|
+
**Branch:** master
|
|
6
|
+
**Commits esta sesión:** `e5a1a05` + `5c06ef3` (ambos committed, working tree limpio)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Lo que se hizo esta sesión
|
|
11
|
+
|
|
12
|
+
### Fix 1 — Windows encoding crash en `--since` (`e5a1a05`)
|
|
13
|
+
|
|
14
|
+
**Root cause:** `_get_available_refs()` en `prepare_context.py` llamaba `subprocess.run(text=True)` sin `encoding=` — usaba cp1252 en Windows. Byte 0x8d → `UnicodeDecodeError` no capturado. Además `r.stdout.splitlines()` sin guard → `AttributeError` si stdout fuera None.
|
|
15
|
+
|
|
16
|
+
**Fix:** dos líneas en `prepare_context.py:2309-2315`:
|
|
17
|
+
```python
|
|
18
|
+
encoding="utf-8", errors="replace", # añadido
|
|
19
|
+
(r.stdout or "").splitlines() # guard añadido
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Tests añadidos:** `tests/test_encoding_regression.py` — clase `TestGetAvailableRefsWindowsEncoding` (4 casos: stdout=None, bytes inválidos, ref inexistente, returncode!=0).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
### Fix 2 — `review-pr` reescrito como pipeline delta-first (`5c06ef3`)
|
|
27
|
+
|
|
28
|
+
**Problema:** `review-pr` devolvía output genérico de repo completo sin diff real. Inútil para CI/PR review.
|
|
29
|
+
|
|
30
|
+
**Cambios:**
|
|
31
|
+
|
|
32
|
+
| Archivo | Qué cambió |
|
|
33
|
+
|---------|-----------|
|
|
34
|
+
| `prepare_context.py` | Gate 5d: 3 errores tempranos (no_git_repo, no_diff, git_ref_not_found). Reutiliza `_build_delta_impact`. Deriva security_impact/transactional_impact/test_coverage_risk del análisis delta. 7 nuevos campos en TaskOutput. |
|
|
35
|
+
| `cli.py` | Content map review-pr. Bloque output PR-específico. Error JSON + exit(1). Help text. |
|
|
36
|
+
| `test_v1_10_regressions.py` | 3 tests viejos (comportamiento genérico) reemplazados por 4 nuevos (gate git + mocks). |
|
|
37
|
+
|
|
38
|
+
**Nuevos campos en output:**
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"review_type": "pull_request",
|
|
42
|
+
"ci_decision": "analysis_success",
|
|
43
|
+
"base_ref": "origin/main",
|
|
44
|
+
"changed_files": [...],
|
|
45
|
+
"affected_modules": [...],
|
|
46
|
+
"security_impact": {"affected_resources": [...], "risk_level": "high"},
|
|
47
|
+
"transactional_impact": {"affected_transactions": [...], "risk": "..."},
|
|
48
|
+
"test_coverage_risk": {"changed_files_without_tests": [...], "risk_level": "medium"},
|
|
49
|
+
"review_hotspots": [...],
|
|
50
|
+
"suggested_review_order": [...]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**GitHub Actions ready:**
|
|
55
|
+
```yaml
|
|
56
|
+
- run: sourcecode prepare-context review-pr . --since origin/main --output review.json
|
|
57
|
+
# exit 1 si no hay diff o ref no existe
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Estado de tests al pausar
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
770 passed, 3 skipped
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Fallos pre-existentes (no relacionados, ya presentes antes de esta sesión):
|
|
69
|
+
- `test_block2_coverage.py::test_java_marked_unsupported` — `DocRecord.__init__` bug en `doc_analyzer.py`
|
|
70
|
+
- `test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile` — typer version mismatch
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Pendiente / próximos pasos opcionales
|
|
75
|
+
|
|
76
|
+
1. **Smoke test review-pr en saint-server real con diff:**
|
|
77
|
+
```bash
|
|
78
|
+
cd saint-server
|
|
79
|
+
git checkout -b test-review-pr
|
|
80
|
+
# modificar un controller
|
|
81
|
+
sourcecode prepare-context review-pr . --since main --output /tmp/review.json
|
|
82
|
+
cat /tmp/review.json | python3 -c "import json,sys; d=json.load(sys.stdin); print('sections:', list(d.keys()))"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
2. **Verificar `security_impact` detecta correctamente:** cambiar un `SecurityConfig.java` y comprobar que aparece en `security_impact.affected_resources`.
|
|
86
|
+
|
|
87
|
+
3. **`suggested_review_order` calidad:** verificar que el orden security→controller→service es correcto en PR reales con múltiples tipos de archivos.
|
|
88
|
+
|
|
89
|
+
4. **`test_coverage_risk` stem matching sin normalización:** el gap entre `generate-tests` (normaliza `TestFoo→Foo`) y `review-pr` (stem directo). Si da falsos "untested", añadir normalización.
|
|
90
|
+
|
|
91
|
+
5. **Agent token reduction** (trabajo sesiones anteriores): smoke test pendiente en saint-server — ver handoff sesión 4 para comandos.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Para retomar
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
cd /Users/user/Downloads/atlas-cli
|
|
99
|
+
git log --oneline -3
|
|
100
|
+
# debe mostrar:
|
|
101
|
+
# 5c06ef3 (feature) implementando mejoras significativas en prepare-context review-pr...
|
|
102
|
+
# e5a1a05 corrigiendo bug en delta --since en windows
|
|
103
|
+
# f1cb001 corrigiendo bug --agent que duplicaba tokens sin justificación
|
|
104
|
+
|
|
105
|
+
python3 -m pytest tests/ \
|
|
106
|
+
--ignore=tests/test_block2_coverage.py \
|
|
107
|
+
--deselect=tests/test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile_keeps_declared_versions \
|
|
108
|
+
-q
|
|
109
|
+
# Expected: 770 passed, 3 skipped
|
|
110
|
+
|
|
111
|
+
# Smoke test review-pr
|
|
112
|
+
sourcecode prepare-context review-pr . --since HEAD~1 --output /tmp/review.json
|
|
113
|
+
python3 -c "import json; d=json.load(open('/tmp/review.json')); print(list(d.keys()))"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
*Pausado 2026-05-16 sesión 5 — gsd:pause-work*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.30.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.30.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.30.0
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
---
|
|
@@ -1633,7 +1633,7 @@ def prepare_context_cmd(
|
|
|
1633
1633
|
refactor Structural issues, improvement opportunities
|
|
1634
1634
|
generate-tests Untested source files, test gap analysis
|
|
1635
1635
|
onboard Full project context for new agents/developers
|
|
1636
|
-
review-pr
|
|
1636
|
+
review-pr PR diff: changed files, security/transactional impact, test gaps (requires git diff or --since)
|
|
1637
1637
|
delta Incremental context: git-changed files only
|
|
1638
1638
|
|
|
1639
1639
|
\b
|
|
@@ -1642,6 +1642,8 @@ def prepare_context_cmd(
|
|
|
1642
1642
|
sourcecode prepare-context explain /path/to/repo
|
|
1643
1643
|
sourcecode prepare-context fix-bug
|
|
1644
1644
|
sourcecode prepare-context delta --since main
|
|
1645
|
+
sourcecode prepare-context review-pr --since origin/main
|
|
1646
|
+
sourcecode prepare-context review-pr . --since main --output review.json
|
|
1645
1647
|
sourcecode prepare-context onboard --llm-prompt
|
|
1646
1648
|
sourcecode prepare-context --task-help
|
|
1647
1649
|
"""
|
|
@@ -1734,6 +1736,14 @@ def prepare_context_cmd(
|
|
|
1734
1736
|
"test_gaps": False, "code_notes_summary": False,
|
|
1735
1737
|
"changed_files": True, "affected_entry_points": True,
|
|
1736
1738
|
},
|
|
1739
|
+
"review-pr": {
|
|
1740
|
+
"project_summary": False, "architecture_summary": False,
|
|
1741
|
+
"relevant_files": True, "key_dependencies": False,
|
|
1742
|
+
"gaps": True, "confidence": False,
|
|
1743
|
+
"suspected_areas": False, "improvement_opportunities": False,
|
|
1744
|
+
"test_gaps": False, "code_notes_summary": False,
|
|
1745
|
+
"changed_files": True, "affected_entry_points": True,
|
|
1746
|
+
},
|
|
1737
1747
|
}
|
|
1738
1748
|
_content_filter = _TASK_CONTENT_MAP.get(task, {})
|
|
1739
1749
|
|
|
@@ -1813,6 +1823,53 @@ def prepare_context_cmd(
|
|
|
1813
1823
|
out["dependency_graph_summary"] = output.dependency_graph_summary
|
|
1814
1824
|
if output.impact_score_per_file:
|
|
1815
1825
|
out["impact_score_per_file"] = output.impact_score_per_file
|
|
1826
|
+
# review-pr specific fields
|
|
1827
|
+
if task == "review-pr":
|
|
1828
|
+
if output.error_code:
|
|
1829
|
+
_err_out: dict[str, Any] = {
|
|
1830
|
+
"task": output.task,
|
|
1831
|
+
"ci_decision": output.ci_decision or "error",
|
|
1832
|
+
"error": output.error_code,
|
|
1833
|
+
"message": output.error_message,
|
|
1834
|
+
}
|
|
1835
|
+
if output.since:
|
|
1836
|
+
_err_out["since"] = output.since
|
|
1837
|
+
if output.error_hints:
|
|
1838
|
+
_err_out["hint"] = output.error_hints
|
|
1839
|
+
_err_json = json.dumps(_err_out, indent=2, ensure_ascii=False)
|
|
1840
|
+
if output_path is not None:
|
|
1841
|
+
output_path.write_text(_err_json, encoding="utf-8")
|
|
1842
|
+
else:
|
|
1843
|
+
import sys as _sys
|
|
1844
|
+
_sys.stdout.buffer.write(_err_json.encode("utf-8"))
|
|
1845
|
+
_sys.stdout.buffer.write(b"\n")
|
|
1846
|
+
_sys.stdout.buffer.flush()
|
|
1847
|
+
raise typer.Exit(code=1)
|
|
1848
|
+
out["review_type"] = "pull_request"
|
|
1849
|
+
if output.ci_decision:
|
|
1850
|
+
out["ci_decision"] = output.ci_decision
|
|
1851
|
+
if output.base_ref:
|
|
1852
|
+
out["base_ref"] = output.base_ref
|
|
1853
|
+
if output.since:
|
|
1854
|
+
out["since"] = output.since
|
|
1855
|
+
if output.affected_modules:
|
|
1856
|
+
out["affected_modules"] = output.affected_modules
|
|
1857
|
+
if output.security_impact:
|
|
1858
|
+
out["security_impact"] = output.security_impact
|
|
1859
|
+
if output.transactional_impact:
|
|
1860
|
+
out["transactional_impact"] = output.transactional_impact
|
|
1861
|
+
if output.configuration_impact:
|
|
1862
|
+
out["configuration_impact"] = output.configuration_impact
|
|
1863
|
+
if output.test_coverage_risk:
|
|
1864
|
+
out["test_coverage_risk"] = output.test_coverage_risk
|
|
1865
|
+
if output.review_hotspots:
|
|
1866
|
+
out["review_hotspots"] = output.review_hotspots
|
|
1867
|
+
if output.suggested_review_order:
|
|
1868
|
+
out["suggested_review_order"] = output.suggested_review_order
|
|
1869
|
+
if output.impact_summary:
|
|
1870
|
+
out["impact_summary"] = output.impact_summary
|
|
1871
|
+
if output.why_these_files:
|
|
1872
|
+
out["reasoning"] = output.why_these_files
|
|
1816
1873
|
if output.limitations:
|
|
1817
1874
|
out["limitations"] = output.limitations
|
|
1818
1875
|
if output.symptom:
|
|
@@ -338,11 +338,19 @@ class TaskOutput:
|
|
|
338
338
|
error_message: Optional[str] = None
|
|
339
339
|
error_hints: list[str] = field(default_factory=list)
|
|
340
340
|
# CI decision state machine — machine-decidable signal
|
|
341
|
-
ci_decision: Optional[str] = None # "no_changes" | "analysis_success" | "git_ref_error"
|
|
341
|
+
ci_decision: Optional[str] = None # "no_changes" | "analysis_success" | "git_ref_error" | "no_git_repo"
|
|
342
342
|
# git baseline resolution metadata
|
|
343
343
|
resolved_since_ref: Optional[str] = None # actual ref/hash used for the diff
|
|
344
344
|
resolution_path: Optional[str] = None # "exact_local_ref"|"remote_tracking_ref"|"symbolic_ref"|"head_minus_1_fallback"|"uncommitted_changes"|"unresolvable"
|
|
345
345
|
diff_validation_status: Optional[str] = None # "valid_non_empty"|"valid_empty"|"invalid_ref"
|
|
346
|
+
# review-pr specific impact sections
|
|
347
|
+
base_ref: Optional[str] = None
|
|
348
|
+
security_impact: dict = field(default_factory=dict)
|
|
349
|
+
transactional_impact: dict = field(default_factory=dict)
|
|
350
|
+
configuration_impact: dict = field(default_factory=dict)
|
|
351
|
+
test_coverage_risk: dict = field(default_factory=dict)
|
|
352
|
+
review_hotspots: list[str] = field(default_factory=list)
|
|
353
|
+
suggested_review_order: list[str] = field(default_factory=list)
|
|
346
354
|
|
|
347
355
|
|
|
348
356
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -659,6 +667,64 @@ class TaskContextBuilder:
|
|
|
659
667
|
elif _delta_raw:
|
|
660
668
|
_delta_files = set(_delta_raw)
|
|
661
669
|
|
|
670
|
+
# ── 5d. review-pr: git-first gate ──────────────────────────────────────
|
|
671
|
+
if task_name == "review-pr":
|
|
672
|
+
if not self._is_git_repo():
|
|
673
|
+
return TaskOutput(
|
|
674
|
+
task="review-pr", goal=spec.goal,
|
|
675
|
+
project_summary=None, architecture_summary=None,
|
|
676
|
+
relevant_files=[], suspected_areas=[],
|
|
677
|
+
improvement_opportunities=[], test_gaps=[],
|
|
678
|
+
key_dependencies=[], code_notes_summary=None,
|
|
679
|
+
limitations=[], confidence="low",
|
|
680
|
+
error_code="no_git_repo",
|
|
681
|
+
error_message="review-pr requires a git repository.",
|
|
682
|
+
ci_decision="no_git_repo",
|
|
683
|
+
)
|
|
684
|
+
if since is None:
|
|
685
|
+
# review-pr with no --since: check only uncommitted changes.
|
|
686
|
+
# _get_git_changed_files(since=None) defaults to HEAD~1 which
|
|
687
|
+
# returns the last *committed* diff — a false positive here.
|
|
688
|
+
_pr_raw: Optional[list[str]] = self._get_uncommitted_changed_files()
|
|
689
|
+
else:
|
|
690
|
+
_pr_raw = self._get_git_changed_files(since=since)
|
|
691
|
+
if _pr_raw is None:
|
|
692
|
+
_avail_pr, _sug_pr = self._get_available_refs(since or "")
|
|
693
|
+
_pr_hints: list[str] = []
|
|
694
|
+
if _sug_pr:
|
|
695
|
+
_pr_hints.append(f"Did you mean '{_sug_pr}'?")
|
|
696
|
+
if _avail_pr:
|
|
697
|
+
_pr_hints.append(f"Available refs: {', '.join(_avail_pr[:8])}")
|
|
698
|
+
return TaskOutput(
|
|
699
|
+
task="review-pr", goal=spec.goal,
|
|
700
|
+
project_summary=None, architecture_summary=None,
|
|
701
|
+
relevant_files=[], suspected_areas=[],
|
|
702
|
+
improvement_opportunities=[], test_gaps=[],
|
|
703
|
+
key_dependencies=[], code_notes_summary=None,
|
|
704
|
+
limitations=[], confidence="low",
|
|
705
|
+
since=since,
|
|
706
|
+
error_code="git_ref_not_found",
|
|
707
|
+
error_message=f"Base ref '{since}' not found in this repository.",
|
|
708
|
+
error_hints=_pr_hints,
|
|
709
|
+
gaps=[f"Cannot compute PR diff: git ref '{since}' not found."] + _pr_hints,
|
|
710
|
+
ci_decision="git_ref_error",
|
|
711
|
+
)
|
|
712
|
+
if not _pr_raw:
|
|
713
|
+
_no_diff_hint = "review-pr requires changed files or --since <ref>."
|
|
714
|
+
return TaskOutput(
|
|
715
|
+
task="review-pr", goal=spec.goal,
|
|
716
|
+
project_summary=None, architecture_summary=None,
|
|
717
|
+
relevant_files=[], suspected_areas=[],
|
|
718
|
+
improvement_opportunities=[], test_gaps=[],
|
|
719
|
+
key_dependencies=[], code_notes_summary=None,
|
|
720
|
+
limitations=[], confidence="low",
|
|
721
|
+
error_code="no_diff",
|
|
722
|
+
error_message=f"No PR diff detected. {_no_diff_hint}",
|
|
723
|
+
gaps=[f"No PR diff detected. {_no_diff_hint}"],
|
|
724
|
+
ci_decision="no_changes",
|
|
725
|
+
)
|
|
726
|
+
_delta_files = set(_pr_raw)
|
|
727
|
+
|
|
662
728
|
# ── 5c. review-pr suspected_areas (needs git uncommitted_files) ──────
|
|
663
729
|
if task_name == "review-pr" and spec.enable_code_notes:
|
|
664
730
|
pr_areas: dict[str, int] = {}
|
|
@@ -698,7 +764,7 @@ class TaskContextBuilder:
|
|
|
698
764
|
_delta_dep_graph_summary: dict = {}
|
|
699
765
|
_delta_impact_score_per_file: dict = {}
|
|
700
766
|
|
|
701
|
-
if task_name
|
|
767
|
+
if task_name in ("delta", "review-pr"):
|
|
702
768
|
_delta_changed_list: list[str] = sorted(_delta_files) if _delta_files else []
|
|
703
769
|
(
|
|
704
770
|
relevant_files,
|
|
@@ -727,7 +793,88 @@ class TaskContextBuilder:
|
|
|
727
793
|
delta_files=None,
|
|
728
794
|
)
|
|
729
795
|
|
|
730
|
-
# ── 6b.
|
|
796
|
+
# ── 6b. review-pr: derive PR-specific impact sections from delta analysis ──
|
|
797
|
+
_pr_security_impact: dict = {}
|
|
798
|
+
_pr_transactional_impact: dict = {}
|
|
799
|
+
_pr_configuration_impact: dict = {}
|
|
800
|
+
_pr_test_coverage_risk: dict = {}
|
|
801
|
+
_pr_review_hotspots: list[str] = []
|
|
802
|
+
_pr_suggested_review_order: list[str] = []
|
|
803
|
+
_pr_base_ref: Optional[str] = None
|
|
804
|
+
|
|
805
|
+
if task_name == "review-pr":
|
|
806
|
+
_pr_base_ref = since or "HEAD"
|
|
807
|
+
_sys_risk_areas = _delta_system_impact.get("risk_areas", [])
|
|
808
|
+
|
|
809
|
+
_security_files = [
|
|
810
|
+
f for ra in _sys_risk_areas if ra["area"] == "security"
|
|
811
|
+
for f in ra["affected_files"]
|
|
812
|
+
]
|
|
813
|
+
_transaction_files = [
|
|
814
|
+
f for ra in _sys_risk_areas if ra["area"] in ("transactions", "business_logic")
|
|
815
|
+
for f in ra["affected_files"]
|
|
816
|
+
]
|
|
817
|
+
_config_files = [
|
|
818
|
+
f for ra in _sys_risk_areas if ra["area"] in ("api", "config")
|
|
819
|
+
for f in ra["affected_files"]
|
|
820
|
+
if any(kw in f.lower() for kw in ("config", "properties", "yml", "yaml", "xml", "spring"))
|
|
821
|
+
]
|
|
822
|
+
|
|
823
|
+
if _security_files:
|
|
824
|
+
_pr_security_impact = {
|
|
825
|
+
"affected_resources": _security_files,
|
|
826
|
+
"risk_level": "high",
|
|
827
|
+
}
|
|
828
|
+
if _transaction_files:
|
|
829
|
+
_pr_transactional_impact = {
|
|
830
|
+
"affected_transactions": _transaction_files,
|
|
831
|
+
"risk": "possible transaction boundary change",
|
|
832
|
+
}
|
|
833
|
+
if _config_files:
|
|
834
|
+
_pr_configuration_impact = {"changed_configs": _config_files}
|
|
835
|
+
|
|
836
|
+
# Test coverage risk scoped to changed source files only
|
|
837
|
+
_changed_src = [
|
|
838
|
+
f for f in sorted(_delta_files or set())
|
|
839
|
+
if not self._is_test(f) and self._is_source(f)
|
|
840
|
+
]
|
|
841
|
+
_test_stems = {Path(p).stem for p in test_set}
|
|
842
|
+
_untested_changed = [f for f in _changed_src if Path(f).stem not in _test_stems]
|
|
843
|
+
_test_risk_level = (
|
|
844
|
+
"high" if len(_untested_changed) > 3
|
|
845
|
+
else "medium" if _untested_changed
|
|
846
|
+
else "low"
|
|
847
|
+
)
|
|
848
|
+
_pr_test_coverage_risk = {
|
|
849
|
+
"changed_files_without_tests": _untested_changed[:10],
|
|
850
|
+
"risk_level": _test_risk_level,
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
# Review hotspots: top changed files ranked by impact score
|
|
854
|
+
_pr_review_hotspots = sorted(
|
|
855
|
+
_delta_files or set(),
|
|
856
|
+
key=lambda f: _delta_impact_score_per_file.get(f, 0.0),
|
|
857
|
+
reverse=True,
|
|
858
|
+
)[:8]
|
|
859
|
+
|
|
860
|
+
# Suggested review order: security first, then api → service → persistence → config
|
|
861
|
+
_ORDER_TYPES = ["security", "controller", "service", "repository", "mapper",
|
|
862
|
+
"spring_config", "config", "domain_model", "dto"]
|
|
863
|
+
_seen_order: set[str] = set()
|
|
864
|
+
for _otype in _ORDER_TYPES:
|
|
865
|
+
for _ra in _delta_risk_areas:
|
|
866
|
+
for _f in _ra.get("affected_files", []):
|
|
867
|
+
if _f not in _seen_order:
|
|
868
|
+
_cls = self._classify_changed_file(_f)
|
|
869
|
+
if _cls["artifact_type"] == _otype:
|
|
870
|
+
_pr_suggested_review_order.append(_f)
|
|
871
|
+
_seen_order.add(_f)
|
|
872
|
+
for _f in _pr_review_hotspots:
|
|
873
|
+
if _f not in _seen_order:
|
|
874
|
+
_pr_suggested_review_order.append(_f)
|
|
875
|
+
_seen_order.add(_f)
|
|
876
|
+
|
|
877
|
+
# ── 6c. Symptom keyword boost + related notes (fix-bug + --symptom) ──
|
|
731
878
|
symptom_keywords: list[str] = []
|
|
732
879
|
related_notes: list[dict] = []
|
|
733
880
|
symptom_note: Optional[str] = None
|
|
@@ -883,7 +1030,7 @@ class TaskContextBuilder:
|
|
|
883
1030
|
|
|
884
1031
|
conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
|
|
885
1032
|
confidence = conf_summary.overall
|
|
886
|
-
if task_name
|
|
1033
|
+
if task_name in ("delta", "review-pr"):
|
|
887
1034
|
# Use delta-specific gaps; ConfidenceAnalyzer gaps are about full-repo
|
|
888
1035
|
# detection quality and are not meaningful for an incremental diff.
|
|
889
1036
|
gaps = _delta_analysis_gaps
|
|
@@ -895,16 +1042,16 @@ class TaskContextBuilder:
|
|
|
895
1042
|
gaps.append(_mybatis_warning["reason"])
|
|
896
1043
|
|
|
897
1044
|
# ── 9. why_these_files ────────────────────────────────────────────────
|
|
898
|
-
if task_name
|
|
1045
|
+
if task_name in ("delta", "review-pr"):
|
|
899
1046
|
why_these_files = _delta_why
|
|
900
1047
|
else:
|
|
901
1048
|
why_these_files = {rf.path: rf.reason for rf in relevant_files}
|
|
902
1049
|
|
|
903
|
-
# ── 10. Delta: git changed files + entry points
|
|
1050
|
+
# ── 10. Delta / review-pr: git changed files + entry points ──────────
|
|
904
1051
|
changed_files: list[str] = []
|
|
905
1052
|
affected_entry_points: list[str] = []
|
|
906
|
-
if task_name
|
|
907
|
-
changed_files = sorted(_delta_files) if _delta_files else self._get_git_changed_files(since=since)
|
|
1053
|
+
if task_name in ("delta", "review-pr"):
|
|
1054
|
+
changed_files = sorted(_delta_files) if _delta_files else (self._get_git_changed_files(since=since) or [])
|
|
908
1055
|
_ep_set = {ep.path for ep in entry_points}
|
|
909
1056
|
# include framework-detected entry points AND files classified as
|
|
910
1057
|
# entrypoint/controller/security by artifact taxonomy
|
|
@@ -939,16 +1086,24 @@ class TaskContextBuilder:
|
|
|
939
1086
|
impact_summary=_delta_impact_summary,
|
|
940
1087
|
affected_modules=_delta_affected_modules,
|
|
941
1088
|
risk_areas=_delta_risk_areas,
|
|
942
|
-
since=since if task_name
|
|
1089
|
+
since=since if task_name in ("delta", "review-pr") else None,
|
|
943
1090
|
system_impact=_delta_system_impact,
|
|
944
1091
|
change_type=_delta_change_type,
|
|
945
1092
|
dependency_graph_summary=_delta_dep_graph_summary,
|
|
946
1093
|
impact_score_per_file=_delta_impact_score_per_file,
|
|
947
1094
|
ci_decision=(
|
|
948
1095
|
"no_changes" if task_name == "delta" and not changed_files
|
|
949
|
-
else "analysis_success" if task_name
|
|
1096
|
+
else "analysis_success" if task_name in ("delta", "review-pr")
|
|
950
1097
|
else None
|
|
951
1098
|
),
|
|
1099
|
+
# review-pr specific
|
|
1100
|
+
base_ref=_pr_base_ref,
|
|
1101
|
+
security_impact=_pr_security_impact,
|
|
1102
|
+
transactional_impact=_pr_transactional_impact,
|
|
1103
|
+
configuration_impact=_pr_configuration_impact,
|
|
1104
|
+
test_coverage_risk=_pr_test_coverage_risk,
|
|
1105
|
+
review_hotspots=_pr_review_hotspots,
|
|
1106
|
+
suggested_review_order=_pr_suggested_review_order,
|
|
952
1107
|
)
|
|
953
1108
|
|
|
954
1109
|
def render_prompt(self, output: TaskOutput) -> str:
|
|
@@ -1240,6 +1395,19 @@ class TaskContextBuilder:
|
|
|
1240
1395
|
def _is_source(self, path: str) -> bool:
|
|
1241
1396
|
return Path(path).suffix.lower() in _SOURCE_EXTENSIONS
|
|
1242
1397
|
|
|
1398
|
+
def _is_git_repo(self) -> bool:
|
|
1399
|
+
import subprocess
|
|
1400
|
+
try:
|
|
1401
|
+
r = subprocess.run(
|
|
1402
|
+
["git", "rev-parse", "--git-dir"],
|
|
1403
|
+
cwd=str(self.root),
|
|
1404
|
+
capture_output=True, text=True,
|
|
1405
|
+
encoding="utf-8", errors="replace", timeout=5,
|
|
1406
|
+
)
|
|
1407
|
+
return r.returncode == 0
|
|
1408
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
1409
|
+
return False
|
|
1410
|
+
|
|
1243
1411
|
# ── Delta impact analysis ─────────────────────────────────────────────────
|
|
1244
1412
|
|
|
1245
1413
|
@staticmethod
|
|
@@ -2248,6 +2416,25 @@ class TaskContextBuilder:
|
|
|
2248
2416
|
"error": True,
|
|
2249
2417
|
}
|
|
2250
2418
|
|
|
2419
|
+
def _get_uncommitted_changed_files(self) -> list[str]:
|
|
2420
|
+
"""Return files with uncommitted working-tree changes (unstaged only).
|
|
2421
|
+
|
|
2422
|
+
Used by review-pr when no --since ref is given, so we don't confuse
|
|
2423
|
+
the last *committed* diff (HEAD~1 vs HEAD) with an actual PR diff.
|
|
2424
|
+
"""
|
|
2425
|
+
import subprocess
|
|
2426
|
+
try:
|
|
2427
|
+
result = subprocess.run(
|
|
2428
|
+
["git", "diff", "--name-only", "--relative"],
|
|
2429
|
+
cwd=str(self.root), capture_output=True, text=True,
|
|
2430
|
+
encoding="utf-8", errors="replace", timeout=10,
|
|
2431
|
+
)
|
|
2432
|
+
if result.returncode == 0:
|
|
2433
|
+
return [l.strip() for l in (result.stdout or "").splitlines() if l.strip()]
|
|
2434
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
2435
|
+
pass
|
|
2436
|
+
return []
|
|
2437
|
+
|
|
2251
2438
|
def _get_git_changed_files(self, since: Optional[str] = None) -> Optional[list[str]]:
|
|
2252
2439
|
"""Get files changed since a git ref (default: HEAD~1) relative to self.root.
|
|
2253
2440
|
|
|
@@ -530,27 +530,53 @@ class TestArchitecturePatternHeuristic:
|
|
|
530
530
|
# ===========================================================================
|
|
531
531
|
|
|
532
532
|
class TestReviewPrSuspectedAreas:
|
|
533
|
+
"""review-pr now requires a git diff — generic fallback removed."""
|
|
533
534
|
|
|
534
|
-
def
|
|
535
|
+
def test_review_pr_requires_git_diff(self):
|
|
536
|
+
# FIXTURE has no uncommitted changes (or no git repo) — must exit 1 with structured error.
|
|
537
|
+
# When running inside the atlas-cli git tree with no staged changes, error is "no_diff".
|
|
538
|
+
# When running outside any git repo, error is "no_git_repo". Both are valid.
|
|
535
539
|
result = _invoke("prepare-context", "review-pr", str(FIXTURE))
|
|
536
|
-
assert result.exit_code ==
|
|
540
|
+
assert result.exit_code == 1, result.output
|
|
541
|
+
data = _json(result)
|
|
542
|
+
_git_errors = {"no_git_repo", "no_diff", "git_ref_not_found"}
|
|
543
|
+
assert data.get("error") in _git_errors, f"Expected git error, got: {data}"
|
|
544
|
+
assert "ci_decision" in data
|
|
537
545
|
|
|
538
|
-
def
|
|
546
|
+
def test_review_pr_error_json_is_machine_readable(self):
|
|
539
547
|
result = _invoke("prepare-context", "review-pr", str(FIXTURE))
|
|
540
|
-
assert result.exit_code ==
|
|
548
|
+
assert result.exit_code == 1
|
|
541
549
|
data = _json(result)
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
assert "
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
550
|
+
assert "error" in data
|
|
551
|
+
assert "message" in data
|
|
552
|
+
assert "ci_decision" in data
|
|
553
|
+
|
|
554
|
+
def test_review_pr_no_diff_error_when_git_but_no_changes(self, monkeypatch):
|
|
555
|
+
# Simulate: is_git_repo=True but no uncommitted changes
|
|
556
|
+
from sourcecode import prepare_context as _pc
|
|
557
|
+
monkeypatch.setattr(_pc.TaskContextBuilder, "_is_git_repo", lambda self: True)
|
|
558
|
+
monkeypatch.setattr(_pc.TaskContextBuilder, "_get_uncommitted_changed_files", lambda self: [])
|
|
559
|
+
result = _invoke("prepare-context", "review-pr", str(FIXTURE))
|
|
560
|
+
assert result.exit_code == 1
|
|
561
|
+
data = _json(result)
|
|
562
|
+
assert data.get("error") == "no_diff"
|
|
563
|
+
assert data.get("ci_decision") == "no_changes"
|
|
564
|
+
|
|
565
|
+
def test_review_pr_with_mocked_diff_returns_pr_fields(self, monkeypatch):
|
|
566
|
+
# Simulate: valid git repo with one changed controller file (no --since → uncommitted path)
|
|
567
|
+
from sourcecode import prepare_context as _pc
|
|
568
|
+
monkeypatch.setattr(_pc.TaskContextBuilder, "_is_git_repo", lambda self: True)
|
|
569
|
+
monkeypatch.setattr(
|
|
570
|
+
_pc.TaskContextBuilder, "_get_uncommitted_changed_files",
|
|
571
|
+
lambda self: ["src/main/java/com/example/UserController.java"],
|
|
572
|
+
)
|
|
548
573
|
result = _invoke("prepare-context", "review-pr", str(FIXTURE))
|
|
549
574
|
assert result.exit_code == 0, result.output
|
|
550
575
|
data = _json(result)
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
assert
|
|
576
|
+
assert data.get("review_type") == "pull_request"
|
|
577
|
+
assert "changed_files" in data
|
|
578
|
+
assert "test_coverage_risk" in data
|
|
579
|
+
assert data.get("ci_decision") == "analysis_success"
|
|
554
580
|
|
|
555
581
|
def test_fix_bug_still_uses_annotation_density(self):
|
|
556
582
|
result = _invoke("prepare-context", "fix-bug", str(FIXTURE))
|