sourcecode 1.6.0__tar.gz → 1.8.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.6.0 → sourcecode-1.8.0}/PKG-INFO +1 -1
- {sourcecode-1.6.0 → sourcecode-1.8.0}/pyproject.toml +1 -1
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/adaptive_scanner.py +2 -1
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/cli.py +104 -112
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/contract_pipeline.py +7 -1
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/prepare_context.py +74 -5
- sourcecode-1.8.0/src/sourcecode/progress.py +110 -0
- sourcecode-1.8.0/tests/test_bug_fixes_v16.py +250 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_cli.py +11 -6
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.continue-here.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.gitignore +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/.ruff.toml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/LICENSE +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/README.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/SECURITY.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/docs/privacy.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/docs/schema.md +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/raw +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/run_cli.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/__init__.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/conftest.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_schema.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.6.0 → sourcecode-1.8.0}/tests/test_workspace_analyzer.py +0 -0
|
@@ -135,7 +135,8 @@ class AdaptiveScanner:
|
|
|
135
135
|
if current_depth >= n:
|
|
136
136
|
# At or inside the source root
|
|
137
137
|
if rel_parts[:n] == src_parts:
|
|
138
|
-
|
|
138
|
+
# base_depth acts as a floor — explicit --depth always wins
|
|
139
|
+
return max(src_max, self.base_depth)
|
|
139
140
|
else:
|
|
140
141
|
# Ancestor check: src_parts starts with rel_parts?
|
|
141
142
|
if src_parts[:current_depth] == rel_parts:
|
|
@@ -10,6 +10,7 @@ import typer
|
|
|
10
10
|
|
|
11
11
|
from sourcecode import __version__
|
|
12
12
|
from sourcecode.entrypoint_classifier import is_production_entry_point, normalize_entry_point
|
|
13
|
+
from sourcecode.progress import Progress
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
# ---------------------------------------------------------------------------
|
|
@@ -138,18 +139,19 @@ def _check_pipeline_coherence(sm: "SourceMap") -> list[str]: # type: ignore[nam
|
|
|
138
139
|
return issues
|
|
139
140
|
|
|
140
141
|
_HELP = """\
|
|
141
|
-
|
|
142
|
+
Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
142
143
|
|
|
143
|
-
[bold]
|
|
144
|
-
sourcecode
|
|
145
|
-
sourcecode
|
|
146
|
-
sourcecode --
|
|
144
|
+
[bold]Examples:[/bold]
|
|
145
|
+
sourcecode saint-server --compact
|
|
146
|
+
sourcecode . --changed-only --git-context
|
|
147
|
+
sourcecode saint-server --symbol SeguridadRestController
|
|
148
|
+
sourcecode prepare-context onboard saint-server
|
|
149
|
+
sourcecode prepare-context delta . --since main
|
|
147
150
|
|
|
148
151
|
[bold]Subcommands:[/bold]
|
|
149
|
-
prepare-context TASK [PATH] [dim]# task-specific context[/dim]
|
|
152
|
+
prepare-context TASK [PATH] [dim]# task-specific context (onboard, delta, fix-bug, ...)[/dim]
|
|
150
153
|
telemetry status|enable|disable
|
|
151
154
|
version
|
|
152
|
-
config
|
|
153
155
|
"""
|
|
154
156
|
|
|
155
157
|
# Known subcommand names — tokens matching these are routed as subcommands,
|
|
@@ -352,58 +354,55 @@ def main(
|
|
|
352
354
|
"json",
|
|
353
355
|
"--format",
|
|
354
356
|
"-f",
|
|
355
|
-
help="Output format: json (default) or yaml.
|
|
357
|
+
help="Output format: json (default) or yaml.",
|
|
356
358
|
show_default=True,
|
|
357
359
|
),
|
|
358
360
|
output: Optional[Path] = typer.Option(
|
|
359
361
|
None,
|
|
360
362
|
"--output",
|
|
361
363
|
"-o",
|
|
362
|
-
help="Write output to a file instead of stdout.
|
|
364
|
+
help="Write output to a file instead of stdout.",
|
|
363
365
|
),
|
|
364
366
|
compact: bool = typer.Option(
|
|
365
367
|
False,
|
|
366
368
|
"--compact",
|
|
367
369
|
help=(
|
|
368
|
-
"
|
|
369
|
-
"dependency summary, confidence
|
|
370
|
-
"
|
|
371
|
-
"Designed for agent context windows. Automatically enables --dependencies, --env-map, and --code-notes."
|
|
370
|
+
"High-signal summary (~600–800 tokens): stacks, entry points, "
|
|
371
|
+
"dependency summary, confidence, and gaps. "
|
|
372
|
+
"Optimized for agent context windows."
|
|
372
373
|
),
|
|
373
374
|
),
|
|
374
375
|
dependencies: bool = typer.Option(
|
|
375
376
|
False,
|
|
376
377
|
"--dependencies",
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
"and lockfiles when available. Adds dependency_summary and key_dependencies to output."
|
|
380
|
-
),
|
|
378
|
+
hidden=True,
|
|
379
|
+
help="Analyze direct dependencies from manifests and lockfiles.",
|
|
381
380
|
),
|
|
382
381
|
graph_modules: bool = typer.Option(
|
|
383
382
|
False,
|
|
384
383
|
"--graph-modules",
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
"Useful for understanding coupling and call flows. Adds module_graph to output. "
|
|
388
|
-
"Combine with --graph-detail and --graph-edges to control scope."
|
|
389
|
-
),
|
|
384
|
+
hidden=True,
|
|
385
|
+
help="Include a structural module graph in output.",
|
|
390
386
|
),
|
|
391
387
|
graph_detail: str = typer.Option(
|
|
392
388
|
"high",
|
|
393
389
|
"--graph-detail",
|
|
394
|
-
|
|
390
|
+
hidden=True,
|
|
391
|
+
help="Detail level for --graph-modules: high, medium, full.",
|
|
395
392
|
show_default=True,
|
|
396
393
|
),
|
|
397
394
|
max_nodes: Optional[int] = typer.Option(
|
|
398
395
|
None,
|
|
399
396
|
"--max-nodes",
|
|
400
|
-
|
|
397
|
+
hidden=True,
|
|
398
|
+
help="Maximum nodes in --graph-modules output.",
|
|
401
399
|
min=1,
|
|
402
400
|
),
|
|
403
401
|
graph_edges: Optional[str] = typer.Option(
|
|
404
402
|
None,
|
|
405
403
|
"--graph-edges",
|
|
406
|
-
|
|
404
|
+
hidden=True,
|
|
405
|
+
help="Edge types for --graph-modules, comma-separated: imports,calls,contains,extends.",
|
|
407
406
|
),
|
|
408
407
|
no_tree: bool = typer.Option(
|
|
409
408
|
False,
|
|
@@ -414,18 +413,13 @@ def main(
|
|
|
414
413
|
tree: bool = typer.Option(
|
|
415
414
|
False,
|
|
416
415
|
"--tree",
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
"Adds significant size — use when the agent needs to browse the full file structure."
|
|
420
|
-
),
|
|
416
|
+
hidden=True,
|
|
417
|
+
help="Include the full file_tree and flat file_paths list in output.",
|
|
421
418
|
),
|
|
422
419
|
no_redact: bool = typer.Option(
|
|
423
420
|
False,
|
|
424
421
|
"--no-redact",
|
|
425
|
-
help=
|
|
426
|
-
"Disable automatic secret redaction. By default, potential secrets (API keys, tokens, passwords) "
|
|
427
|
-
"are replaced with [REDACTED]. Use with caution — output may contain sensitive values."
|
|
428
|
-
),
|
|
422
|
+
help="Disable secret redaction. Use with caution — output may contain sensitive values.",
|
|
429
423
|
),
|
|
430
424
|
version: Optional[bool] = typer.Option(
|
|
431
425
|
None,
|
|
@@ -438,122 +432,97 @@ def main(
|
|
|
438
432
|
depth: int = typer.Option(
|
|
439
433
|
4,
|
|
440
434
|
"--depth",
|
|
441
|
-
help=(
|
|
442
|
-
"Maximum depth for file tree traversal (default: 4, range: 1–20). "
|
|
443
|
-
"Increase for deeply nested projects — Maven/Java requires at least 8 (src/main/java/...)."
|
|
444
|
-
),
|
|
435
|
+
help="File tree traversal depth (default: 4). Java/Maven projects auto-adjust to 12.",
|
|
445
436
|
min=1,
|
|
446
437
|
max=20,
|
|
447
438
|
),
|
|
448
439
|
docs: bool = typer.Option(
|
|
449
440
|
False,
|
|
450
441
|
"--docs",
|
|
451
|
-
|
|
442
|
+
hidden=True,
|
|
443
|
+
help="Extract documentation: docstrings, function signatures, and module-level comments.",
|
|
452
444
|
),
|
|
453
445
|
docs_depth: str = typer.Option(
|
|
454
446
|
"symbols",
|
|
455
447
|
"--docs-depth",
|
|
456
|
-
|
|
448
|
+
hidden=True,
|
|
449
|
+
help="Documentation extraction depth: module, symbols, full.",
|
|
457
450
|
show_default=True,
|
|
458
451
|
),
|
|
459
452
|
full_metrics: bool = typer.Option(
|
|
460
453
|
False,
|
|
461
454
|
"--full-metrics",
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
"Produces file_metrics and metrics_summary. "
|
|
465
|
-
"Not included in --agent output — designed for CI pipelines and code review tools, not as primary agent context."
|
|
466
|
-
),
|
|
455
|
+
hidden=True,
|
|
456
|
+
help="Technical audit: LOC, complexity, test coverage per file.",
|
|
467
457
|
),
|
|
468
458
|
semantics: bool = typer.Option(
|
|
469
459
|
False,
|
|
470
460
|
"--semantics",
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
"Adds semantic_calls, semantic_symbols, semantic_links, semantic_summary, and hotspots (files ranked by fan-in/fan-out). "
|
|
474
|
-
"Slower than default analysis — skip for quick scans. "
|
|
475
|
-
"Confidence degrades on dynamic dispatch, decorators, and generated code."
|
|
476
|
-
),
|
|
461
|
+
hidden=True,
|
|
462
|
+
help="Cross-file symbol resolution and call graph analysis.",
|
|
477
463
|
),
|
|
478
464
|
architecture: bool = typer.Option(
|
|
479
465
|
False,
|
|
480
466
|
"--architecture",
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
"and dominant structural patterns. Adds architecture to output. "
|
|
484
|
-
"Confidence is low when based on directory names alone — combine with --semantics for higher accuracy."
|
|
485
|
-
),
|
|
467
|
+
hidden=True,
|
|
468
|
+
help="Architectural layer inference (MVC/hexagonal/layered).",
|
|
486
469
|
),
|
|
487
470
|
git_context: bool = typer.Option(
|
|
488
471
|
False,
|
|
489
472
|
"--git-context",
|
|
490
473
|
"-g",
|
|
491
|
-
help="Include git activity: recent commits, change hotspots
|
|
474
|
+
help="Include git activity: recent commits, change hotspots, and uncommitted changes.",
|
|
492
475
|
),
|
|
493
476
|
git_depth: int = typer.Option(
|
|
494
477
|
20,
|
|
495
478
|
"--git-depth",
|
|
496
|
-
|
|
479
|
+
hidden=True,
|
|
480
|
+
help="Number of recent commits to include with --git-context (default: 20).",
|
|
497
481
|
min=1,
|
|
498
482
|
max=100,
|
|
499
483
|
),
|
|
500
484
|
git_days: int = typer.Option(
|
|
501
485
|
90,
|
|
502
486
|
"--git-days",
|
|
503
|
-
|
|
487
|
+
hidden=True,
|
|
488
|
+
help="Time window in days for change hotspot detection (default: 90).",
|
|
504
489
|
min=1,
|
|
505
490
|
max=3650,
|
|
506
491
|
),
|
|
507
492
|
env_map: bool = typer.Option(
|
|
508
493
|
False,
|
|
509
494
|
"--env-map",
|
|
510
|
-
|
|
495
|
+
hidden=True,
|
|
496
|
+
help="Map environment variables referenced across the codebase.",
|
|
511
497
|
),
|
|
512
498
|
code_notes: bool = typer.Option(
|
|
513
499
|
False,
|
|
514
500
|
"--code-notes",
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
"with file location and enclosing symbol. "
|
|
518
|
-
"Also detects Architecture Decision Records (ADRs) in docs/decisions/, docs/adr/, and similar paths."
|
|
519
|
-
),
|
|
501
|
+
hidden=True,
|
|
502
|
+
help="Extract inline annotations: TODO, FIXME, HACK, DEPRECATED, ADRs.",
|
|
520
503
|
),
|
|
521
504
|
agent: bool = typer.Option(
|
|
522
505
|
False,
|
|
523
506
|
"--agent",
|
|
524
|
-
help=
|
|
525
|
-
"Agent-optimized output: structured, noise-free JSON for AI consumption. "
|
|
526
|
-
"Automatically enables --dependencies, --env-map, and --code-notes. Suppresses file tree. "
|
|
527
|
-
"Output includes: identity, entry points, architecture, runtime dependencies, "
|
|
528
|
-
"operational signals, confidence summary, and analysis gaps. No empty sections."
|
|
529
|
-
),
|
|
507
|
+
help="Structured noise-free JSON for AI agents: identity, entry points, dependencies, confidence, gaps.",
|
|
530
508
|
),
|
|
531
509
|
trace_pipeline: bool = typer.Option(
|
|
532
510
|
False,
|
|
533
511
|
"--trace-pipeline",
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
"and data origin across all pipeline stages. "
|
|
537
|
-
"Use to diagnose unexpected or contaminated results. Not intended for normal agent use."
|
|
538
|
-
),
|
|
512
|
+
hidden=True,
|
|
513
|
+
help="Diagnostic: include full pipeline trace in output.",
|
|
539
514
|
),
|
|
540
515
|
mode: str = typer.Option(
|
|
541
516
|
"contract",
|
|
542
517
|
"--mode",
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
"contract: minimal per-file contracts — exports, signatures, deps. "
|
|
546
|
-
"Smallest output, recommended for AI agents. "
|
|
547
|
-
"minimal is accepted as an alias for contract. "
|
|
548
|
-
"standard: full per-file detail with imports, relevance scores, extraction method. "
|
|
549
|
-
"raw: project-level analysis only (stacks, entry points, dependency summary). "
|
|
550
|
-
"No per-file contracts."
|
|
551
|
-
),
|
|
518
|
+
hidden=True,
|
|
519
|
+
help="Output mode: contract (default) | standard | raw.",
|
|
552
520
|
),
|
|
553
521
|
max_symbols: Optional[int] = typer.Option(
|
|
554
522
|
None,
|
|
555
523
|
"--max-symbols",
|
|
556
|
-
|
|
524
|
+
hidden=True,
|
|
525
|
+
help="Limit total exported semantic nodes across all file contracts.",
|
|
557
526
|
min=1,
|
|
558
527
|
),
|
|
559
528
|
dependency_depth: int = typer.Option(
|
|
@@ -567,22 +536,25 @@ def main(
|
|
|
567
536
|
entrypoints_only: bool = typer.Option(
|
|
568
537
|
False,
|
|
569
538
|
"--entrypoints-only",
|
|
570
|
-
|
|
539
|
+
hidden=True,
|
|
540
|
+
help="Include only files with exported symbols or runtime entrypoints.",
|
|
571
541
|
),
|
|
572
542
|
changed_only: bool = typer.Option(
|
|
573
543
|
False,
|
|
574
544
|
"--changed-only",
|
|
575
|
-
help="
|
|
545
|
+
help="Limit output to git-modified files (staged, unstaged, untracked).",
|
|
576
546
|
),
|
|
577
547
|
rank_by: str = typer.Option(
|
|
578
548
|
"relevance",
|
|
579
549
|
"--rank-by",
|
|
550
|
+
hidden=True,
|
|
580
551
|
help="Contract ranking strategy: relevance (default) | centrality | git-churn.",
|
|
581
552
|
),
|
|
582
553
|
emit_graph: bool = typer.Option(
|
|
583
554
|
False,
|
|
584
555
|
"--emit-graph",
|
|
585
|
-
|
|
556
|
+
hidden=True,
|
|
557
|
+
help="Include a compact dependency graph in contract output.",
|
|
586
558
|
),
|
|
587
559
|
compress_types: bool = typer.Option(
|
|
588
560
|
False,
|
|
@@ -593,16 +565,13 @@ def main(
|
|
|
593
565
|
symbol: Optional[str] = typer.Option(
|
|
594
566
|
None,
|
|
595
567
|
"--symbol",
|
|
596
|
-
help="
|
|
568
|
+
help="Extract context for a specific symbol: defining file + all importers.",
|
|
597
569
|
),
|
|
598
570
|
max_importers: int = typer.Option(
|
|
599
571
|
50,
|
|
600
572
|
"--max-importers",
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
"Popular symbols can have hundreds of importers — this prevents output explosion. "
|
|
604
|
-
"Defining files are never truncated. Override: --symbol Foo --max-importers 200."
|
|
605
|
-
),
|
|
573
|
+
hidden=True,
|
|
574
|
+
help="Maximum importer files returned by --symbol (default: 50).",
|
|
606
575
|
min=1,
|
|
607
576
|
max=10000,
|
|
608
577
|
),
|
|
@@ -788,7 +757,7 @@ def main(
|
|
|
788
757
|
# Require at least 8: src(1)+main(2)+java(3)+com(4)+co(5)+app(6)+module(7)+file.
|
|
789
758
|
_java_manifest_names = {"pom.xml", "build.gradle", "build.gradle.kts"}
|
|
790
759
|
_is_java = any(Path(m).name in _java_manifest_names for m in manifests)
|
|
791
|
-
_java_min_depth =
|
|
760
|
+
_java_min_depth = 12
|
|
792
761
|
effective_depth = max(depth, _java_min_depth) if _is_java and depth < _java_min_depth else depth
|
|
793
762
|
|
|
794
763
|
# --agent: enable signal analyzers; output via agent_view (not compact)
|
|
@@ -797,22 +766,9 @@ def main(
|
|
|
797
766
|
env_map = True
|
|
798
767
|
code_notes = True
|
|
799
768
|
no_tree = True # agents never need the raw file tree
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
if full_metrics:
|
|
804
|
-
_agent_suppressed.append("--full-metrics")
|
|
805
|
-
if graph_modules:
|
|
806
|
-
_agent_suppressed.append("--graph-modules")
|
|
807
|
-
if docs:
|
|
808
|
-
_agent_suppressed.append("--docs")
|
|
809
|
-
if _agent_suppressed:
|
|
810
|
-
typer.echo(
|
|
811
|
-
f"[agent] warning: {', '.join(_agent_suppressed)} computed but excluded "
|
|
812
|
-
"from --agent output — agent_view does not include these sections. "
|
|
813
|
-
"Remove these flags to skip unnecessary computation.",
|
|
814
|
-
err=True,
|
|
815
|
-
)
|
|
769
|
+
|
|
770
|
+
_progress = Progress()
|
|
771
|
+
_progress.start("scanning files")
|
|
816
772
|
|
|
817
773
|
scanner = AdaptiveScanner(target, topology=_topology, base_depth=effective_depth)
|
|
818
774
|
raw_tree = scanner.scan_tree()
|
|
@@ -850,6 +806,7 @@ def main(
|
|
|
850
806
|
node = child
|
|
851
807
|
return pruned
|
|
852
808
|
|
|
809
|
+
_progress.update("parsing manifests")
|
|
853
810
|
file_tree = filter_sensitive_files(raw_tree)
|
|
854
811
|
detector = ProjectDetector(build_default_detectors())
|
|
855
812
|
workspace_analysis = WorkspaceAnalyzer().analyze(target, manifests)
|
|
@@ -1318,6 +1275,7 @@ def main(
|
|
|
1318
1275
|
|
|
1319
1276
|
# Git Context (--git-context flag)
|
|
1320
1277
|
if git_context:
|
|
1278
|
+
_progress.update("git analysis")
|
|
1321
1279
|
from sourcecode.git_analyzer import GitAnalyzer
|
|
1322
1280
|
sm.git_context = GitAnalyzer().analyze(target, depth=git_depth, days=git_days)
|
|
1323
1281
|
|
|
@@ -1399,7 +1357,33 @@ def main(
|
|
|
1399
1357
|
))
|
|
1400
1358
|
sm = _replace(sm, pipeline_trace=_trace.build_trace())
|
|
1401
1359
|
|
|
1360
|
+
# Pre-compute uncommitted files for --changed-only.
|
|
1361
|
+
# The contract pipeline filter and git_context are two separate subsystems;
|
|
1362
|
+
# wire them here so the pipeline uses git_context data, not an independent git call.
|
|
1363
|
+
_allowed_changed_files: Optional[set[str]] = None
|
|
1364
|
+
if changed_only:
|
|
1365
|
+
from sourcecode.git_analyzer import GitAnalyzer as _GitAnalyzerEarly
|
|
1366
|
+
try:
|
|
1367
|
+
_gc_early = _GitAnalyzerEarly().analyze(target, depth=1, days=1)
|
|
1368
|
+
_bad_gc = {"no_git_repo", "git_not_found", "git_timeout"}
|
|
1369
|
+
if _gc_early and not (_bad_gc & set(_gc_early.limitations)):
|
|
1370
|
+
_uc = _gc_early.uncommitted_changes
|
|
1371
|
+
if _uc:
|
|
1372
|
+
_allowed_changed_files = (
|
|
1373
|
+
set(_uc.staged) | set(_uc.unstaged) | set(_uc.untracked)
|
|
1374
|
+
)
|
|
1375
|
+
if not _allowed_changed_files:
|
|
1376
|
+
typer.echo(
|
|
1377
|
+
"[changed-only] git unavailable or no uncommitted changes — falling back to full scan.",
|
|
1378
|
+
err=True,
|
|
1379
|
+
)
|
|
1380
|
+
changed_only = False
|
|
1381
|
+
except Exception:
|
|
1382
|
+
typer.echo("[changed-only] git error — falling back to full scan.", err=True)
|
|
1383
|
+
changed_only = False
|
|
1384
|
+
|
|
1402
1385
|
# Contract pipeline — runs for mode=contract|standard|deep|hybrid (skip for raw)
|
|
1386
|
+
_progress.update("extracting contracts")
|
|
1403
1387
|
_is_contract_mode = mode in ("contract", "standard")
|
|
1404
1388
|
_pipeline_error = False
|
|
1405
1389
|
if _is_contract_mode:
|
|
@@ -1432,6 +1416,7 @@ def main(
|
|
|
1432
1416
|
max_importers=max_importers,
|
|
1433
1417
|
semantic_calls=sm.semantic_calls or None,
|
|
1434
1418
|
code_notes=sm.code_notes or None,
|
|
1419
|
+
allowed_changed_files=_allowed_changed_files,
|
|
1435
1420
|
**_java_pipeline_kwargs,
|
|
1436
1421
|
)
|
|
1437
1422
|
except Exception as _exc:
|
|
@@ -1469,6 +1454,7 @@ def main(
|
|
|
1469
1454
|
)
|
|
1470
1455
|
|
|
1471
1456
|
# 4. Serialize
|
|
1457
|
+
_progress.update("serializing output")
|
|
1472
1458
|
if _is_contract_mode and not agent:
|
|
1473
1459
|
from sourcecode.serializer import contract_view as _contract_view
|
|
1474
1460
|
_depth = _CONTRACT_DEPTH.get(mode, "minimal")
|
|
@@ -1560,6 +1546,7 @@ def main(
|
|
|
1560
1546
|
pass
|
|
1561
1547
|
|
|
1562
1548
|
# 6. Write output (CLI-04)
|
|
1549
|
+
_progress.finish()
|
|
1563
1550
|
write_output(content, output=output)
|
|
1564
1551
|
|
|
1565
1552
|
if _pipeline_error:
|
|
@@ -1706,8 +1693,13 @@ def prepare_context_cmd(
|
|
|
1706
1693
|
if llm_prompt:
|
|
1707
1694
|
out["llm_prompt"] = builder.render_prompt(output)
|
|
1708
1695
|
|
|
1696
|
+
import sys as _sys
|
|
1709
1697
|
_pc_content = json.dumps(out, indent=2, ensure_ascii=False)
|
|
1710
|
-
|
|
1698
|
+
_pc_bytes = _pc_content.encode("utf-8")
|
|
1699
|
+
_sys.stdout.buffer.write(_pc_bytes)
|
|
1700
|
+
if not _pc_content.endswith("\n"):
|
|
1701
|
+
_sys.stdout.buffer.write(b"\n")
|
|
1702
|
+
_sys.stdout.buffer.flush()
|
|
1711
1703
|
|
|
1712
1704
|
if copy:
|
|
1713
1705
|
_trimmed = _pc_content.strip()
|
|
@@ -183,6 +183,7 @@ class ContractPipeline:
|
|
|
183
183
|
code_notes: Optional[list] = None,
|
|
184
184
|
max_contracts: Optional[int] = _MAX_CONTRACTS,
|
|
185
185
|
min_score: Optional[float] = None,
|
|
186
|
+
allowed_changed_files: Optional[set[str]] = None,
|
|
186
187
|
) -> tuple[list[FileContract], ContractSummary]:
|
|
187
188
|
"""Run the full extraction pipeline.
|
|
188
189
|
|
|
@@ -200,9 +201,14 @@ class ContractPipeline:
|
|
|
200
201
|
engine = RankingEngine(monorepo_packages)
|
|
201
202
|
|
|
202
203
|
# 1. Changed files (for --changed-only and ranking)
|
|
204
|
+
# When allowed_changed_files is provided (from pre-computed git uncommitted_changes),
|
|
205
|
+
# use it directly. Otherwise fall back to independent git commands.
|
|
203
206
|
changed_files: set[str] = set()
|
|
204
207
|
if changed_only or rank_by == "git-churn":
|
|
205
|
-
|
|
208
|
+
if allowed_changed_files is not None:
|
|
209
|
+
changed_files = allowed_changed_files
|
|
210
|
+
else:
|
|
211
|
+
changed_files = _get_changed_files(root)
|
|
206
212
|
|
|
207
213
|
# 2. Select files to extract
|
|
208
214
|
# Exclude test files by default — they dominate by count but add noise
|
|
@@ -398,11 +398,27 @@ class TaskContextBuilder:
|
|
|
398
398
|
from sourcecode.tree_utils import flatten_file_tree
|
|
399
399
|
|
|
400
400
|
_topology = RepoClassifier().classify(self.root)
|
|
401
|
-
|
|
401
|
+
# Shallow pre-scan to detect Java manifests before choosing depth.
|
|
402
|
+
from sourcecode.scanner import FileScanner as _FileScanner
|
|
403
|
+
_pre = _FileScanner(self.root, max_depth=1)
|
|
404
|
+
_pre_manifests = _pre.find_manifests()
|
|
405
|
+
_java_names = {"pom.xml", "build.gradle", "build.gradle.kts"}
|
|
406
|
+
_is_java = any(Path(m).name in _java_names for m in _pre_manifests)
|
|
407
|
+
_base_depth = 12 if _is_java else 6
|
|
408
|
+
scanner = AdaptiveScanner(self.root, topology=_topology, base_depth=_base_depth)
|
|
402
409
|
file_tree = scanner.scan_tree()
|
|
403
410
|
manifests = scanner.find_manifests()
|
|
404
411
|
all_paths = [p.replace("\\", "/") for p in flatten_file_tree(file_tree)]
|
|
405
412
|
|
|
413
|
+
# Warn when Java project has no Mapper.xml — suggests files below scan depth.
|
|
414
|
+
_mybatis_warning: dict | None = None
|
|
415
|
+
if _is_java and not any(p.endswith("Mapper.xml") for p in all_paths):
|
|
416
|
+
_mybatis_warning = {
|
|
417
|
+
"area": "mybatis",
|
|
418
|
+
"reason": "Mapper XML files may exist below scan depth. Re-run with --depth 12.",
|
|
419
|
+
"impact": "high",
|
|
420
|
+
}
|
|
421
|
+
|
|
406
422
|
# ── 2. Detect stacks + entry points ───────────────────────────────
|
|
407
423
|
from dataclasses import replace as _replace
|
|
408
424
|
from sourcecode.detectors import ProjectDetector, build_default_detectors
|
|
@@ -500,6 +516,7 @@ class TaskContextBuilder:
|
|
|
500
516
|
code_notes_summary: Optional[dict[str, Any]] = None
|
|
501
517
|
suspected_areas: list[str] = []
|
|
502
518
|
improvement_opportunities: list[str] = []
|
|
519
|
+
cn_notes_for_ranking: list = []
|
|
503
520
|
|
|
504
521
|
if spec.enable_code_notes:
|
|
505
522
|
from dataclasses import asdict
|
|
@@ -507,6 +524,7 @@ class TaskContextBuilder:
|
|
|
507
524
|
|
|
508
525
|
cn_notes, _cn_adrs, cn_summary = CodeNotesAnalyzer().analyze(self.root)
|
|
509
526
|
code_notes_summary = asdict(cn_summary)
|
|
527
|
+
cn_notes_for_ranking = cn_notes
|
|
510
528
|
|
|
511
529
|
if task_name == "fix-bug":
|
|
512
530
|
bug_kinds = {"FIXME", "BUG", "HACK", "XXX"}
|
|
@@ -555,6 +573,7 @@ class TaskContextBuilder:
|
|
|
555
573
|
monorepo_packages=sm.monorepo_packages if sm.monorepo_packages else None,
|
|
556
574
|
git_hotspots=git_hotspots,
|
|
557
575
|
uncommitted_files=uncommitted_files,
|
|
576
|
+
code_notes=cn_notes_for_ranking if cn_notes_for_ranking else None,
|
|
558
577
|
)
|
|
559
578
|
|
|
560
579
|
# ── 7. Test gaps (generate-tests only) ────────────────────────────
|
|
@@ -594,6 +613,8 @@ class TaskContextBuilder:
|
|
|
594
613
|
conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
|
|
595
614
|
confidence = conf_summary.overall
|
|
596
615
|
gaps = [g.reason for g in analysis_gaps]
|
|
616
|
+
if _mybatis_warning:
|
|
617
|
+
gaps.append(_mybatis_warning["reason"])
|
|
597
618
|
|
|
598
619
|
# ── 9. why_these_files ────────────────────────────────────────────────
|
|
599
620
|
why_these_files: dict[str, str] = {
|
|
@@ -705,6 +726,7 @@ class TaskContextBuilder:
|
|
|
705
726
|
monorepo_packages: Optional[list] = None,
|
|
706
727
|
git_hotspots: Optional[dict[str, int]] = None,
|
|
707
728
|
uncommitted_files: Optional[set[str]] = None,
|
|
729
|
+
code_notes: Optional[list] = None,
|
|
708
730
|
) -> list[RelevantFile]:
|
|
709
731
|
from sourcecode.ranking_engine import RankingEngine
|
|
710
732
|
from sourcecode.file_classifier import FileClassifier
|
|
@@ -719,6 +741,35 @@ class TaskContextBuilder:
|
|
|
719
741
|
_uncommitted = uncommitted_files or set()
|
|
720
742
|
_max_churn = max(_hotspots.values(), default=1)
|
|
721
743
|
|
|
744
|
+
# Pre-compute fix-bug signals (used only when task_name == "fix-bug")
|
|
745
|
+
_annotated_files: set[str] = set()
|
|
746
|
+
_dominant_stack = ""
|
|
747
|
+
_recently_changed_stacks: set[str] = set()
|
|
748
|
+
if task_name == "fix-bug":
|
|
749
|
+
_bug_kinds = {"FIXME", "BUG"}
|
|
750
|
+
for _n in (code_notes or []):
|
|
751
|
+
if getattr(_n, "kind", "").upper() in _bug_kinds:
|
|
752
|
+
_annotated_files.add(getattr(_n, "path", ""))
|
|
753
|
+
|
|
754
|
+
def _file_stack(p: str) -> str:
|
|
755
|
+
ext = Path(p).suffix.lower()
|
|
756
|
+
if ext == ".java": return "java"
|
|
757
|
+
if ext in (".ts", ".tsx", ".js", ".jsx", ".mjs"): return "typescript"
|
|
758
|
+
if ext == ".py": return "python"
|
|
759
|
+
if ext == ".go": return "go"
|
|
760
|
+
if ext in (".kt", ".kts"): return "kotlin"
|
|
761
|
+
if ext == ".rs": return "rust"
|
|
762
|
+
if ext == ".rb": return "ruby"
|
|
763
|
+
return "unknown"
|
|
764
|
+
|
|
765
|
+
from collections import Counter as _Counter
|
|
766
|
+
_stk_counts: _Counter[str] = _Counter(
|
|
767
|
+
_file_stack(f) for f in _uncommitted if _file_stack(f) != "unknown"
|
|
768
|
+
)
|
|
769
|
+
if _stk_counts:
|
|
770
|
+
_dominant_stack = _stk_counts.most_common(1)[0][0]
|
|
771
|
+
_recently_changed_stacks = set(_stk_counts.keys())
|
|
772
|
+
|
|
722
773
|
scored: list[tuple[float, str, RelevantFile]] = []
|
|
723
774
|
|
|
724
775
|
for path in all_paths:
|
|
@@ -762,10 +813,28 @@ class TaskContextBuilder:
|
|
|
762
813
|
|
|
763
814
|
# Task-specific boosts for differentiated file weighting
|
|
764
815
|
path_lower = path.lower()
|
|
816
|
+
_fix_bug_why = ""
|
|
765
817
|
if task_name == "fix-bug":
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
818
|
+
_why_parts: list[str] = []
|
|
819
|
+
if path in _uncommitted:
|
|
820
|
+
content_boost += 0.40
|
|
821
|
+
_why_parts.append("uncommitted change (+0.40)")
|
|
822
|
+
_recency = min(0.30, _hotspots.get(path, 0) * 0.05)
|
|
823
|
+
if _recency > 0:
|
|
824
|
+
content_boost += _recency
|
|
825
|
+
_why_parts.append(f"recent commits (+{_recency:.2f})")
|
|
826
|
+
if path in _annotated_files:
|
|
827
|
+
content_boost += 0.20
|
|
828
|
+
_why_parts.append("FIXME/BUG annotation (+0.20)")
|
|
829
|
+
_file_stk = _file_stack(path)
|
|
830
|
+
if _dominant_stack and _file_stk == _dominant_stack:
|
|
831
|
+
content_boost += 0.10
|
|
832
|
+
_why_parts.append("dominant changed stack (+0.10)")
|
|
833
|
+
if _recently_changed_stacks and _file_stk not in _recently_changed_stacks and _file_stk != "unknown":
|
|
834
|
+
content_boost -= 0.30
|
|
835
|
+
_why_parts.append("different stack from recent changes (-0.30)")
|
|
836
|
+
if _why_parts:
|
|
837
|
+
_fix_bug_why = ", ".join(_why_parts)
|
|
769
838
|
elif task_name == "generate-tests":
|
|
770
839
|
stem = Path(path).stem.lower()
|
|
771
840
|
has_test = any(
|
|
@@ -797,7 +866,7 @@ class TaskContextBuilder:
|
|
|
797
866
|
)
|
|
798
867
|
all_reasons = [r for r in fs.reasons if r != "source file"] + content_reasons
|
|
799
868
|
reason_str = ", ".join(all_reasons) if all_reasons else "source file"
|
|
800
|
-
why_str = _java_why(path, file_class)
|
|
869
|
+
why_str = _fix_bug_why if _fix_bug_why else _java_why(path, file_class)
|
|
801
870
|
|
|
802
871
|
scored.append((total, path, RelevantFile(
|
|
803
872
|
path=path,
|