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

|
|
225
225
|

|
|
226
226
|
|
|
227
227
|
---
|
|
@@ -257,7 +257,7 @@ pipx install sourcecode
|
|
|
257
257
|
|
|
258
258
|
```bash
|
|
259
259
|
sourcecode version
|
|
260
|
-
# sourcecode 1.30.
|
|
260
|
+
# sourcecode 1.30.28
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -38,7 +38,7 @@ pipx install sourcecode
|
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
sourcecode version
|
|
41
|
-
# sourcecode 1.30.
|
|
41
|
+
# sourcecode 1.30.28
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
---
|
|
@@ -424,7 +424,7 @@ def main(
|
|
|
424
424
|
no_redact: bool = typer.Option(
|
|
425
425
|
False,
|
|
426
426
|
"--no-redact",
|
|
427
|
-
help="Disable secret redaction.
|
|
427
|
+
help="Disable secret redaction of output strings. Note: env var values from the OS are never included in output regardless of this flag (security policy).",
|
|
428
428
|
),
|
|
429
429
|
version: Optional[bool] = typer.Option(
|
|
430
430
|
None,
|
|
@@ -437,7 +437,7 @@ def main(
|
|
|
437
437
|
depth: int = typer.Option(
|
|
438
438
|
4,
|
|
439
439
|
"--depth",
|
|
440
|
-
help="File tree traversal depth (default: 4). Java/Maven projects auto-adjust to 12.",
|
|
440
|
+
help="File tree traversal depth (default: 4). Java/Maven projects auto-adjust to a minimum of 12; values below 12 have no effect on Java projects.",
|
|
441
441
|
min=1,
|
|
442
442
|
max=20,
|
|
443
443
|
),
|
|
@@ -1734,6 +1734,11 @@ def prepare_context_cmd(
|
|
|
1734
1734
|
help="Emit per-phase timing to stderr (git scan ms, symptom scoring ms, total ms)",
|
|
1735
1735
|
hidden=True,
|
|
1736
1736
|
),
|
|
1737
|
+
fast: bool = typer.Option(
|
|
1738
|
+
False,
|
|
1739
|
+
"--fast",
|
|
1740
|
+
help="Skip deep analysis (content search, test gap discovery, code annotations). Uses manifest/metadata only. Target: < 6 s.",
|
|
1741
|
+
),
|
|
1737
1742
|
) -> None:
|
|
1738
1743
|
"""Task-specific context for AI coding agents.
|
|
1739
1744
|
|
|
@@ -1808,9 +1813,14 @@ def prepare_context_cmd(
|
|
|
1808
1813
|
if since:
|
|
1809
1814
|
_phase += f" since {since}"
|
|
1810
1815
|
_progress.start(_phase)
|
|
1816
|
+
if not fast:
|
|
1817
|
+
import sys as _sys
|
|
1818
|
+
if _sys.stderr.isatty():
|
|
1819
|
+
_sys.stderr.write(f"Analyzing ({task})... (deep scan may take 15–35 s for large codebases)\n")
|
|
1820
|
+
_sys.stderr.flush()
|
|
1811
1821
|
_t0 = _time.perf_counter()
|
|
1812
1822
|
try:
|
|
1813
|
-
output = builder.build(task, since=since, symptom=symptom)
|
|
1823
|
+
output = builder.build(task, since=since, symptom=symptom, fast=fast)
|
|
1814
1824
|
finally:
|
|
1815
1825
|
_progress.finish()
|
|
1816
1826
|
_t_total = (_time.perf_counter() - _t0) * 1000
|
|
@@ -1960,8 +1970,8 @@ def prepare_context_cmd(
|
|
|
1960
1970
|
_sys.stdout.buffer.write(b"\n")
|
|
1961
1971
|
_sys.stdout.buffer.flush()
|
|
1962
1972
|
if copy:
|
|
1963
|
-
_copy_to_clipboard(_nc_json)
|
|
1964
|
-
|
|
1973
|
+
if _copy_to_clipboard(_nc_json):
|
|
1974
|
+
typer.echo("✓ copied to clipboard", err=True)
|
|
1965
1975
|
raise typer.Exit()
|
|
1966
1976
|
if output.ci_decision:
|
|
1967
1977
|
out["ci_decision"] = output.ci_decision
|
|
@@ -2082,6 +2092,10 @@ def prepare_context_cmd(
|
|
|
2082
2092
|
out["symptom_note"] = output.symptom_note
|
|
2083
2093
|
if output.symptom_explain:
|
|
2084
2094
|
out["symptom_explain"] = output.symptom_explain
|
|
2095
|
+
if getattr(output, "symptom_hint", None):
|
|
2096
|
+
out["symptom_hint"] = output.symptom_hint
|
|
2097
|
+
if getattr(output, "warnings", None):
|
|
2098
|
+
out["warnings"] = output.warnings
|
|
2085
2099
|
if llm_prompt:
|
|
2086
2100
|
out["llm_prompt"] = builder.render_prompt(output)
|
|
2087
2101
|
|
|
@@ -335,6 +335,7 @@ class TaskOutput:
|
|
|
335
335
|
related_notes: list[dict] = field(default_factory=list) # fix-bug + symptom only
|
|
336
336
|
symptom_note: Optional[str] = None # fix-bug: cross-layer synonym note
|
|
337
337
|
symptom_explain: Optional[dict] = None # fix-bug: structured evidence breakdown
|
|
338
|
+
symptom_hint: Optional[str] = None # fix-bug: redirect hint when term not found in this module
|
|
338
339
|
# delta-specific impact fields
|
|
339
340
|
impact_summary: Optional[str] = None
|
|
340
341
|
affected_modules: list[str] = field(default_factory=list)
|
|
@@ -348,6 +349,7 @@ class TaskOutput:
|
|
|
348
349
|
error_code: Optional[str] = None
|
|
349
350
|
error_message: Optional[str] = None
|
|
350
351
|
error_hints: list[str] = field(default_factory=list)
|
|
352
|
+
warnings: list[dict] = field(default_factory=list) # structured warnings (REF_NOT_FOUND, etc.)
|
|
351
353
|
# CI decision state machine — machine-decidable signal
|
|
352
354
|
ci_decision: Optional[str] = None # "no_changes" | "analysis_success" | "git_ref_error" | "no_git_repo"
|
|
353
355
|
# git baseline resolution metadata
|
|
@@ -683,7 +685,7 @@ class TaskContextBuilder:
|
|
|
683
685
|
def __init__(self, root: Path) -> None:
|
|
684
686
|
self.root = root
|
|
685
687
|
|
|
686
|
-
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None) -> TaskOutput:
|
|
688
|
+
def build(self, task_name: str, *, since: Optional[str] = None, symptom: Optional[str] = None, fast: bool = False) -> TaskOutput:
|
|
687
689
|
if task_name not in TASKS:
|
|
688
690
|
raise ValueError(
|
|
689
691
|
f"Unknown task '{task_name}'. Available: {', '.join(TASKS)}"
|
|
@@ -880,7 +882,7 @@ class TaskContextBuilder:
|
|
|
880
882
|
improvement_opportunities: list[str] = []
|
|
881
883
|
cn_notes_for_ranking: list = []
|
|
882
884
|
|
|
883
|
-
if spec.enable_code_notes:
|
|
885
|
+
if spec.enable_code_notes and not fast:
|
|
884
886
|
from dataclasses import asdict
|
|
885
887
|
from sourcecode.code_notes_analyzer import CodeNotesAnalyzer
|
|
886
888
|
|
|
@@ -1317,6 +1319,7 @@ class TaskContextBuilder:
|
|
|
1317
1319
|
related_notes: list[dict] = []
|
|
1318
1320
|
symptom_note: Optional[str] = None
|
|
1319
1321
|
symptom_explain: Optional[dict] = None
|
|
1322
|
+
symptom_hint: Optional[str] = None
|
|
1320
1323
|
if task_name == "fix-bug" and symptom:
|
|
1321
1324
|
import re as _re
|
|
1322
1325
|
_camel_expanded = _re.sub(r'([a-z])([A-Z])', r'\1 \2', symptom)
|
|
@@ -1537,9 +1540,30 @@ class TaskContextBuilder:
|
|
|
1537
1540
|
),
|
|
1538
1541
|
}
|
|
1539
1542
|
|
|
1543
|
+
# BUG #4: LOW confidence + 0 content matches → clear suspected_areas,
|
|
1544
|
+
# emit actionable redirect instead of unrelated files.
|
|
1545
|
+
if _sx_confidence == "LOW" and not _sx_content:
|
|
1546
|
+
suspected_areas = []
|
|
1547
|
+
_is_fe_term = any(kw in _FRONTEND_SYMPTOM_MAP for kw in symptom_keywords)
|
|
1548
|
+
_root_name = self.root.name
|
|
1549
|
+
if _is_fe_term:
|
|
1550
|
+
_fe_redirect = (
|
|
1551
|
+
f"Term {symptom!r} not found in sources under {_root_name!r}. "
|
|
1552
|
+
f"This appears to be a frontend symptom. "
|
|
1553
|
+
f"Try: prepare-context fix-bug . --symptom {symptom!r} "
|
|
1554
|
+
f"(monorepo root) or target a frontend sub-project directly."
|
|
1555
|
+
)
|
|
1556
|
+
else:
|
|
1557
|
+
_fe_redirect = (
|
|
1558
|
+
f"Term {symptom!r} not found in sources under {_root_name!r}. "
|
|
1559
|
+
f"Verify the spelling or try a related term. "
|
|
1560
|
+
f"If this is a frontend symptom, run against the frontend sub-project."
|
|
1561
|
+
)
|
|
1562
|
+
symptom_hint = _fe_redirect
|
|
1563
|
+
|
|
1540
1564
|
# ── 7. Test gaps (generate-tests only) ────────────────────────────
|
|
1541
1565
|
test_gaps: list[str] = []
|
|
1542
|
-
if task_name == "generate-tests":
|
|
1566
|
+
if task_name == "generate-tests" and not fast:
|
|
1543
1567
|
def _normalize_test_stem(stem: str) -> str:
|
|
1544
1568
|
# Java: FooTest / FooTests → Foo; TestFoo → Foo
|
|
1545
1569
|
if stem.endswith("Tests"):
|
|
@@ -1683,6 +1707,8 @@ class TaskContextBuilder:
|
|
|
1683
1707
|
resolved_since_ref=_delta_baseline.get("resolved_ref") if task_name == "delta" else None,
|
|
1684
1708
|
resolution_path=_delta_baseline.get("resolution_path") if task_name == "delta" else None,
|
|
1685
1709
|
diff_validation_status=_delta_baseline.get("diff_validation_status") if task_name == "delta" else None,
|
|
1710
|
+
warnings=_delta_baseline.get("warnings", []) if task_name == "delta" else [],
|
|
1711
|
+
symptom_hint=symptom_hint if task_name == "fix-bug" else None,
|
|
1686
1712
|
)
|
|
1687
1713
|
|
|
1688
1714
|
def render_prompt(self, output: TaskOutput) -> str:
|
|
@@ -3261,6 +3287,7 @@ class TaskContextBuilder:
|
|
|
3261
3287
|
"resolution_path": "head_minus_1_fallback",
|
|
3262
3288
|
"diff_validation_status": "invalid_ref", # original ref unresolved
|
|
3263
3289
|
"error": False,
|
|
3290
|
+
"warnings": [{"code": "REF_NOT_FOUND", "ref": since, "resolved_to": "HEAD~1"}],
|
|
3264
3291
|
}
|
|
3265
3292
|
|
|
3266
3293
|
# All stages failed
|
|
@@ -248,6 +248,16 @@ def _compact_git_context(sm: "SourceMap") -> "Optional[dict[str, Any]]":
|
|
|
248
248
|
for f, n in _fc.most_common(5)
|
|
249
249
|
]
|
|
250
250
|
ctx["hotspots_source"] = "recent_commits"
|
|
251
|
+
if gc.recent_commits:
|
|
252
|
+
ctx["recent_commits"] = [
|
|
253
|
+
{
|
|
254
|
+
"hash": c.hash[:8],
|
|
255
|
+
"message": (c.message or "")[:80],
|
|
256
|
+
"date": (c.date or "")[:10],
|
|
257
|
+
"author": c.author or "",
|
|
258
|
+
}
|
|
259
|
+
for c in gc.recent_commits[:5]
|
|
260
|
+
]
|
|
251
261
|
return ctx if ctx else None
|
|
252
262
|
|
|
253
263
|
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"""Tests for v1.30.27 bug fixes and improvements.
|
|
2
|
+
|
|
3
|
+
BUG #1 --copy confirmation goes to stderr (not stdout)
|
|
4
|
+
BUG #2 --since non-existent branch emits warnings[] in output
|
|
5
|
+
BUG #3 --depth help text documents Java auto-adjust behavior
|
|
6
|
+
BUG #4 symptom LOW-confidence: suspected_areas cleared, symptom_hint present
|
|
7
|
+
IMP #5 --fast flag skips deep analysis; progress message gated on TTY
|
|
8
|
+
IMP #6 git_context compact output includes recent_commits
|
|
9
|
+
IMP #7 --no-redact help text documents security policy
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
from unittest.mock import patch, MagicMock
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
from typer.testing import CliRunner
|
|
20
|
+
|
|
21
|
+
from sourcecode.cli import app, _copy_to_clipboard
|
|
22
|
+
from sourcecode import prepare_context as _pc
|
|
23
|
+
from sourcecode.prepare_context import TaskContextBuilder, TaskOutput
|
|
24
|
+
from sourcecode.schema import GitContext, CommitRecord, ChangeHotspot, UncommittedChanges, SourceMap, AnalysisMetadata
|
|
25
|
+
from sourcecode.serializer import _compact_git_context
|
|
26
|
+
|
|
27
|
+
runner = CliRunner()
|
|
28
|
+
|
|
29
|
+
FIXTURE = Path(__file__).parent / "fixtures" / "fastapi_app"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _invoke(*args: str):
|
|
33
|
+
return runner.invoke(app, list(args))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _json(result) -> dict:
|
|
37
|
+
try:
|
|
38
|
+
return json.loads(result.output)
|
|
39
|
+
except json.JSONDecodeError as e:
|
|
40
|
+
raise AssertionError(f"Not valid JSON. output={result.output!r}") from e
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ── BUG #1: --copy confirmation must go to stderr ────────────────────────────
|
|
44
|
+
|
|
45
|
+
class TestCopyToStderr:
|
|
46
|
+
def test_copy_confirmation_uses_err_flag(self, tmp_path):
|
|
47
|
+
"""typer.echo for clipboard confirmation uses err=True (routes to stderr).
|
|
48
|
+
|
|
49
|
+
CliRunner mixes stderr into result.output, so we verify the JSON is
|
|
50
|
+
parseable (as the leading content) and the message appears after the JSON.
|
|
51
|
+
In real piped usage, stdout only carries JSON; stderr carries the message.
|
|
52
|
+
"""
|
|
53
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
54
|
+
with patch("sourcecode.cli._copy_to_clipboard", return_value=True):
|
|
55
|
+
result = runner.invoke(app, [str(tmp_path), "--compact", "--copy"])
|
|
56
|
+
assert result.exit_code == 0
|
|
57
|
+
# The JSON portion at the start must be parseable via raw_decode
|
|
58
|
+
decoder = json.JSONDecoder()
|
|
59
|
+
data, end_idx = decoder.raw_decode(result.output.lstrip())
|
|
60
|
+
assert isinstance(data, dict)
|
|
61
|
+
# Confirmation must appear AFTER the JSON (proves it was stderr, not inline)
|
|
62
|
+
trailing = result.output[end_idx:].strip()
|
|
63
|
+
assert "copied to clipboard" in trailing, (
|
|
64
|
+
"Confirmation must be in stderr (appears after JSON in CliRunner output)"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def test_copy_failure_no_message(self, tmp_path):
|
|
68
|
+
"""No confirmation when clipboard copy fails."""
|
|
69
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
70
|
+
with patch("sourcecode.cli._copy_to_clipboard", return_value=False):
|
|
71
|
+
result = runner.invoke(app, [str(tmp_path), "--compact", "--copy"])
|
|
72
|
+
assert result.exit_code == 0
|
|
73
|
+
assert "copied to clipboard" not in result.output
|
|
74
|
+
|
|
75
|
+
def test_prepare_context_copy_conditional_on_success(self, tmp_path):
|
|
76
|
+
"""prepare-context --copy message only shown when clipboard succeeds."""
|
|
77
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
78
|
+
with patch("sourcecode.cli._copy_to_clipboard", return_value=False):
|
|
79
|
+
result = runner.invoke(
|
|
80
|
+
app, ["prepare-context", "explain", str(tmp_path), "--copy"]
|
|
81
|
+
)
|
|
82
|
+
# No "copied to clipboard" in any output when copy fails
|
|
83
|
+
assert "copied to clipboard" not in result.output
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ── BUG #2: --since non-existent ref emits warnings[] ────────────────────────
|
|
87
|
+
|
|
88
|
+
class TestSinceWarnings:
|
|
89
|
+
def test_head_minus1_fallback_emits_warning(self, tmp_path):
|
|
90
|
+
"""Stage 4 fallback populates warnings[] with REF_NOT_FOUND."""
|
|
91
|
+
builder = TaskContextBuilder(tmp_path)
|
|
92
|
+
|
|
93
|
+
# Simulate Stage 4: ref invalid, HEAD~1 available
|
|
94
|
+
def _mock_resolve(since):
|
|
95
|
+
return {
|
|
96
|
+
"files": ["main.py"],
|
|
97
|
+
"resolved_ref": "HEAD~1",
|
|
98
|
+
"resolution_path": "head_minus_1_fallback",
|
|
99
|
+
"diff_validation_status": "invalid_ref",
|
|
100
|
+
"error": False,
|
|
101
|
+
"warnings": [{"code": "REF_NOT_FOUND", "ref": since, "resolved_to": "HEAD~1"}],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
105
|
+
with patch.object(builder, "_resolve_git_baseline", side_effect=_mock_resolve):
|
|
106
|
+
with patch.object(builder, "_build_delta_impact") as mock_impact:
|
|
107
|
+
mock_impact.return_value = MagicMock(
|
|
108
|
+
relevant_files=[], impact_summary=None, affected_modules=[],
|
|
109
|
+
risk_areas=[], why_these_files={}, analysis_gaps=[],
|
|
110
|
+
system_impact={}, change_type=[], dependency_graph_summary={},
|
|
111
|
+
impact_score_per_file={},
|
|
112
|
+
)
|
|
113
|
+
with patch.object(builder, "_get_pr_scope_files", return_value=(None, "git_diff", [], [])):
|
|
114
|
+
output = builder.build("delta", since="main")
|
|
115
|
+
|
|
116
|
+
assert output.warnings, "warnings must be non-empty for Stage 4 fallback"
|
|
117
|
+
assert output.warnings[0]["code"] == "REF_NOT_FOUND"
|
|
118
|
+
assert output.warnings[0]["ref"] == "main"
|
|
119
|
+
assert output.warnings[0]["resolved_to"] == "HEAD~1"
|
|
120
|
+
|
|
121
|
+
def test_valid_ref_no_warnings(self, tmp_path):
|
|
122
|
+
"""Exact ref resolution (Stage 1) produces no warnings."""
|
|
123
|
+
builder = TaskContextBuilder(tmp_path)
|
|
124
|
+
|
|
125
|
+
def _mock_resolve(since):
|
|
126
|
+
return {
|
|
127
|
+
"files": [],
|
|
128
|
+
"resolved_ref": since,
|
|
129
|
+
"resolution_path": "exact_local_ref",
|
|
130
|
+
"diff_validation_status": "valid_empty",
|
|
131
|
+
"error": False,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
with patch.object(builder, "_resolve_git_baseline", side_effect=_mock_resolve):
|
|
135
|
+
with patch.object(builder, "_build_delta_impact") as mock_impact:
|
|
136
|
+
mock_impact.return_value = MagicMock(
|
|
137
|
+
relevant_files=[], impact_summary=None, affected_modules=[],
|
|
138
|
+
risk_areas=[], why_these_files={}, analysis_gaps=[],
|
|
139
|
+
system_impact={}, change_type=[], dependency_graph_summary={},
|
|
140
|
+
impact_score_per_file={},
|
|
141
|
+
)
|
|
142
|
+
output = builder.build("delta", since="origin/develop")
|
|
143
|
+
|
|
144
|
+
assert not output.warnings
|
|
145
|
+
|
|
146
|
+
def test_warnings_serialized_in_cli_output(self, tmp_path):
|
|
147
|
+
"""warnings[] appears in JSON output when populated."""
|
|
148
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
149
|
+
|
|
150
|
+
mock_output = MagicMock(spec=TaskOutput)
|
|
151
|
+
mock_output.task = "delta"
|
|
152
|
+
mock_output.goal = "g"
|
|
153
|
+
mock_output.project_summary = None
|
|
154
|
+
mock_output.architecture_summary = None
|
|
155
|
+
mock_output.relevant_files = []
|
|
156
|
+
mock_output.suspected_areas = []
|
|
157
|
+
mock_output.improvement_opportunities = []
|
|
158
|
+
mock_output.test_gaps = []
|
|
159
|
+
mock_output.key_dependencies = []
|
|
160
|
+
mock_output.code_notes_summary = None
|
|
161
|
+
mock_output.limitations = []
|
|
162
|
+
mock_output.confidence = "low"
|
|
163
|
+
mock_output.gaps = []
|
|
164
|
+
mock_output.why_these_files = {}
|
|
165
|
+
mock_output.changed_files = []
|
|
166
|
+
mock_output.affected_entry_points = []
|
|
167
|
+
mock_output.symptom = None
|
|
168
|
+
mock_output.related_notes = []
|
|
169
|
+
mock_output.symptom_note = None
|
|
170
|
+
mock_output.symptom_explain = None
|
|
171
|
+
mock_output.symptom_hint = None
|
|
172
|
+
mock_output.impact_summary = None
|
|
173
|
+
mock_output.affected_modules = []
|
|
174
|
+
mock_output.risk_areas = []
|
|
175
|
+
mock_output.since = "main"
|
|
176
|
+
mock_output.system_impact = {}
|
|
177
|
+
mock_output.change_type = []
|
|
178
|
+
mock_output.dependency_graph_summary = {}
|
|
179
|
+
mock_output.impact_score_per_file = {}
|
|
180
|
+
mock_output.error_code = None
|
|
181
|
+
mock_output.error_message = None
|
|
182
|
+
mock_output.error_hints = []
|
|
183
|
+
mock_output.ci_decision = "analysis_success"
|
|
184
|
+
mock_output.resolved_since_ref = "HEAD~1"
|
|
185
|
+
mock_output.resolution_path = "head_minus_1_fallback"
|
|
186
|
+
mock_output.diff_validation_status = "invalid_ref"
|
|
187
|
+
mock_output.warnings = [{"code": "REF_NOT_FOUND", "ref": "main", "resolved_to": "HEAD~1"}]
|
|
188
|
+
mock_output.base_ref = None
|
|
189
|
+
mock_output.security_impact = {}
|
|
190
|
+
mock_output.transactional_impact = {}
|
|
191
|
+
mock_output.configuration_impact = {}
|
|
192
|
+
mock_output.test_coverage_risk = {}
|
|
193
|
+
mock_output.review_hotspots = []
|
|
194
|
+
mock_output.suggested_review_order = []
|
|
195
|
+
mock_output.execution_paths = []
|
|
196
|
+
mock_output.behavioral_impact = []
|
|
197
|
+
mock_output.scope_source = None
|
|
198
|
+
mock_output.scope_files = []
|
|
199
|
+
mock_output.repo_root = None
|
|
200
|
+
mock_output.runtime_changes = []
|
|
201
|
+
mock_output.build_changes = {}
|
|
202
|
+
mock_output.committed_changes = []
|
|
203
|
+
mock_output.uncommitted_changes = []
|
|
204
|
+
mock_output.analysis_scope = {}
|
|
205
|
+
|
|
206
|
+
with patch.object(TaskContextBuilder, "build", return_value=mock_output):
|
|
207
|
+
result = runner.invoke(
|
|
208
|
+
app, ["prepare-context", "delta", str(tmp_path), "--since", "main"]
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
assert result.exit_code == 0
|
|
212
|
+
data = _json(result)
|
|
213
|
+
assert "warnings" in data
|
|
214
|
+
assert data["warnings"][0]["code"] == "REF_NOT_FOUND"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ── BUG #3: --depth help text ─────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
class TestDepthHelp:
|
|
220
|
+
def test_depth_help_documents_java_autoadjust(self):
|
|
221
|
+
result = runner.invoke(app, ["--help"])
|
|
222
|
+
assert result.exit_code == 0
|
|
223
|
+
assert "12" in result.output # Java min depth mentioned
|
|
224
|
+
# The help text should mention auto-adjust or Java
|
|
225
|
+
assert "Java" in result.output or "auto" in result.output.lower()
|
|
226
|
+
|
|
227
|
+
def test_depth_flag_accepted(self, tmp_path):
|
|
228
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
229
|
+
result = runner.invoke(app, [str(tmp_path), "--depth", "6", "--compact"])
|
|
230
|
+
assert result.exit_code == 0
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# ── BUG #4: symptom LOW-confidence clears suspected_areas, adds symptom_hint ──
|
|
234
|
+
|
|
235
|
+
class TestSymptomHint:
|
|
236
|
+
def _make_java_fixture(self, tmp_path: Path) -> Path:
|
|
237
|
+
src = tmp_path / "src" / "main" / "java" / "com" / "example"
|
|
238
|
+
src.mkdir(parents=True)
|
|
239
|
+
(src / "UserService.java").write_text(
|
|
240
|
+
"package com.example;\npublic class UserService {}\n"
|
|
241
|
+
)
|
|
242
|
+
(tmp_path / "pom.xml").write_text(
|
|
243
|
+
"<project><groupId>com.example</groupId></project>\n"
|
|
244
|
+
)
|
|
245
|
+
return tmp_path
|
|
246
|
+
|
|
247
|
+
def test_frontend_symptom_in_java_module_clears_suspected_areas(self, tmp_path):
|
|
248
|
+
"""'spinner' in Java module → suspected_areas=[], symptom_hint present."""
|
|
249
|
+
self._make_java_fixture(tmp_path)
|
|
250
|
+
result = runner.invoke(
|
|
251
|
+
app,
|
|
252
|
+
["prepare-context", "fix-bug", str(tmp_path), "--symptom", "spinner"],
|
|
253
|
+
)
|
|
254
|
+
assert result.exit_code == 0
|
|
255
|
+
data = _json(result)
|
|
256
|
+
# suspected_areas must be empty when confidence is LOW with 0 content matches
|
|
257
|
+
if data.get("symptom_explain", {}).get("confidence") == "LOW":
|
|
258
|
+
assert data.get("suspected_areas", []) == [], (
|
|
259
|
+
"suspected_areas must be [] when symptom confidence is LOW"
|
|
260
|
+
)
|
|
261
|
+
assert "symptom_hint" in data, "symptom_hint must be present when LOW confidence"
|
|
262
|
+
assert "spinner" in data["symptom_hint"].lower() or "frontend" in data["symptom_hint"].lower()
|
|
263
|
+
|
|
264
|
+
def test_java_symptom_no_hint(self, tmp_path):
|
|
265
|
+
"""Java term in Java module → no symptom_hint injected."""
|
|
266
|
+
self._make_java_fixture(tmp_path)
|
|
267
|
+
result = runner.invoke(
|
|
268
|
+
app,
|
|
269
|
+
["prepare-context", "fix-bug", str(tmp_path), "--symptom", "UserService"],
|
|
270
|
+
)
|
|
271
|
+
assert result.exit_code == 0
|
|
272
|
+
data = _json(result)
|
|
273
|
+
# If confidence is not LOW, no symptom_hint should be present
|
|
274
|
+
sx = data.get("symptom_explain", {})
|
|
275
|
+
if sx.get("confidence") != "LOW":
|
|
276
|
+
assert "symptom_hint" not in data
|
|
277
|
+
|
|
278
|
+
def test_symptom_hint_field_on_taskoutput(self):
|
|
279
|
+
"""TaskOutput dataclass has symptom_hint field."""
|
|
280
|
+
out = TaskOutput(
|
|
281
|
+
task="fix-bug", goal="g", project_summary=None,
|
|
282
|
+
architecture_summary=None, relevant_files=[], suspected_areas=[],
|
|
283
|
+
improvement_opportunities=[], test_gaps=[], key_dependencies=[],
|
|
284
|
+
code_notes_summary=None, limitations=[], symptom_hint="redirect hint",
|
|
285
|
+
)
|
|
286
|
+
assert out.symptom_hint == "redirect hint"
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ── IMPROVEMENT #5: --fast flag ───────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
class TestFastFlag:
|
|
292
|
+
def test_fast_flag_accepted(self, tmp_path):
|
|
293
|
+
"""--fast flag accepted without error."""
|
|
294
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
295
|
+
result = runner.invoke(
|
|
296
|
+
app, ["prepare-context", "explain", str(tmp_path), "--fast"]
|
|
297
|
+
)
|
|
298
|
+
assert result.exit_code == 0
|
|
299
|
+
data = _json(result)
|
|
300
|
+
assert "task" in data
|
|
301
|
+
|
|
302
|
+
def test_fast_skips_code_notes(self, tmp_path):
|
|
303
|
+
"""--fast produces output without triggering code notes analysis."""
|
|
304
|
+
(tmp_path / "main.py").write_text("# TODO: fix this\nx = 1\n")
|
|
305
|
+
(tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n')
|
|
306
|
+
|
|
307
|
+
called = []
|
|
308
|
+
orig_analyze = None
|
|
309
|
+
try:
|
|
310
|
+
from sourcecode.code_notes_analyzer import CodeNotesAnalyzer
|
|
311
|
+
orig_analyze = CodeNotesAnalyzer.analyze
|
|
312
|
+
|
|
313
|
+
def _mock_analyze(self, root):
|
|
314
|
+
called.append(root)
|
|
315
|
+
return orig_analyze(self, root)
|
|
316
|
+
|
|
317
|
+
with patch.object(CodeNotesAnalyzer, "analyze", _mock_analyze):
|
|
318
|
+
result = runner.invoke(
|
|
319
|
+
app, ["prepare-context", "fix-bug", str(tmp_path), "--fast"]
|
|
320
|
+
)
|
|
321
|
+
except ImportError:
|
|
322
|
+
pytest.skip("CodeNotesAnalyzer not importable")
|
|
323
|
+
|
|
324
|
+
assert result.exit_code == 0
|
|
325
|
+
# With --fast, code notes analyzer should NOT have been called
|
|
326
|
+
assert not called, "--fast must skip code notes analysis"
|
|
327
|
+
|
|
328
|
+
def test_fast_flag_in_help(self):
|
|
329
|
+
"""--fast appears in prepare-context help."""
|
|
330
|
+
result = runner.invoke(app, ["prepare-context", "--help"])
|
|
331
|
+
assert result.exit_code == 0
|
|
332
|
+
assert "--fast" in result.output
|
|
333
|
+
|
|
334
|
+
def test_no_fast_does_not_contaminate_stdout(self, tmp_path):
|
|
335
|
+
"""Progress message (when not --fast) must not appear in JSON stdout."""
|
|
336
|
+
(tmp_path / "main.py").write_text("x = 1\n")
|
|
337
|
+
result = runner.invoke(
|
|
338
|
+
app, ["prepare-context", "explain", str(tmp_path)]
|
|
339
|
+
)
|
|
340
|
+
assert result.exit_code == 0
|
|
341
|
+
# stdout must parse as valid JSON
|
|
342
|
+
data = _json(result)
|
|
343
|
+
assert "task" in data
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# ── IMPROVEMENT #6: git_context recent_commits ───────────────────────────────
|
|
347
|
+
|
|
348
|
+
class TestRecentCommitsInGitContext:
|
|
349
|
+
def _make_sm_with_commits(self, commits: list) -> SourceMap:
|
|
350
|
+
sm = SourceMap(metadata=AnalysisMetadata(analyzed_path="/tmp/test"))
|
|
351
|
+
sm.git_context = GitContext(
|
|
352
|
+
requested=True,
|
|
353
|
+
branch="main",
|
|
354
|
+
recent_commits=commits,
|
|
355
|
+
change_hotspots=[ChangeHotspot(file="a.py", commit_count=3, last_changed="2026-05-01")],
|
|
356
|
+
uncommitted_changes=UncommittedChanges(staged=[], unstaged=[], untracked=[]),
|
|
357
|
+
)
|
|
358
|
+
return sm
|
|
359
|
+
|
|
360
|
+
def test_recent_commits_present_in_compact_output(self):
|
|
361
|
+
"""_compact_git_context includes recent_commits with top-5 entries."""
|
|
362
|
+
commits = [
|
|
363
|
+
CommitRecord(hash="abcd1234", message="feat: add user endpoint", author="alice", date="2026-05-15", files_changed=["a.py"]),
|
|
364
|
+
CommitRecord(hash="efgh5678", message="fix: null pointer in service", author="bob", date="2026-05-14", files_changed=["b.py"]),
|
|
365
|
+
]
|
|
366
|
+
sm = self._make_sm_with_commits(commits)
|
|
367
|
+
ctx = _compact_git_context(sm)
|
|
368
|
+
assert ctx is not None
|
|
369
|
+
assert "recent_commits" in ctx
|
|
370
|
+
assert len(ctx["recent_commits"]) == 2
|
|
371
|
+
|
|
372
|
+
def test_recent_commits_fields(self):
|
|
373
|
+
"""Each recent_commits entry has hash, message, date, author."""
|
|
374
|
+
commits = [
|
|
375
|
+
CommitRecord(
|
|
376
|
+
hash="a0b438a4deadbeef",
|
|
377
|
+
message="Merge pull request #82 into main — very long message that should be truncated at exactly eighty characters total",
|
|
378
|
+
author="m3-dhl",
|
|
379
|
+
date="2026-05-15",
|
|
380
|
+
files_changed=[],
|
|
381
|
+
),
|
|
382
|
+
]
|
|
383
|
+
sm = self._make_sm_with_commits(commits)
|
|
384
|
+
ctx = _compact_git_context(sm)
|
|
385
|
+
entry = ctx["recent_commits"][0]
|
|
386
|
+
assert entry["hash"] == "a0b438a4" # 8-char short hash
|
|
387
|
+
assert len(entry["message"]) <= 80
|
|
388
|
+
assert entry["date"] == "2026-05-15"
|
|
389
|
+
assert entry["author"] == "m3-dhl"
|
|
390
|
+
|
|
391
|
+
def test_recent_commits_capped_at_5(self):
|
|
392
|
+
"""At most 5 commits in recent_commits."""
|
|
393
|
+
commits = [
|
|
394
|
+
CommitRecord(hash=f"hash{i:08d}", message=f"commit {i}", author="a", date="2026-05-01", files_changed=[])
|
|
395
|
+
for i in range(10)
|
|
396
|
+
]
|
|
397
|
+
sm = self._make_sm_with_commits(commits)
|
|
398
|
+
ctx = _compact_git_context(sm)
|
|
399
|
+
assert len(ctx["recent_commits"]) == 5
|
|
400
|
+
|
|
401
|
+
def test_no_commits_no_key(self):
|
|
402
|
+
"""No recent_commits key when list is empty."""
|
|
403
|
+
sm = self._make_sm_with_commits([])
|
|
404
|
+
ctx = _compact_git_context(sm)
|
|
405
|
+
assert ctx is None or "recent_commits" not in (ctx or {})
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
# ── IMPROVEMENT #7: --no-redact help text ────────────────────────────────────
|
|
409
|
+
|
|
410
|
+
class TestNoRedactHelp:
|
|
411
|
+
def test_no_redact_help_mentions_env_var_policy(self):
|
|
412
|
+
"""--no-redact help text must clarify env var values are never included."""
|
|
413
|
+
result = runner.invoke(app, ["--help"])
|
|
414
|
+
assert result.exit_code == 0
|
|
415
|
+
assert "--no-redact" in result.output
|
|
416
|
+
# The help must not imply env var values will be shown
|
|
417
|
+
# Verify the security policy note is present somewhere in help
|
|
418
|
+
# (exact wording may vary — test for the key concepts)
|
|
419
|
+
assert "security" in result.output.lower() or "policy" in result.output.lower() or "never" in result.output.lower()
|
{sourcecode-1.30.27 → sourcecode-1.30.28}/.agents/skills/source-command-gsd-join-discord/SKILL.md
RENAMED
|
File without changes
|
{sourcecode-1.30.27 → sourcecode-1.30.28}/.agents/skills/source-command-gsd-review-backlog/SKILL.md
RENAMED
|
File without changes
|
{sourcecode-1.30.27 → sourcecode-1.30.28}/.agents/skills/source-command-gsd-workstreams/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|