sourcecode 1.11.0__tar.gz → 1.12.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.11.0 → sourcecode-1.12.0}/PKG-INFO +38 -64
- sourcecode-1.12.0/README.md +114 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/pyproject.toml +1 -1
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/cli.py +24 -7
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/java.py +99 -3
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/project.py +3 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/schema.py +1 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/serializer.py +43 -7
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/summarizer.py +7 -1
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_cli.py +1 -1
- sourcecode-1.12.0/tests/test_surface_honesty.py +339 -0
- sourcecode-1.11.0/README.md +0 -140
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.continue-here.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.gitignore +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/.ruff.toml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/LICENSE +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/SECURITY.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/docs/privacy.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/docs/schema.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/raw +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/run_cli.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/__init__.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/conftest.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_v1_10_regressions.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_workspace_analyzer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -254,48 +254,45 @@ sourcecode version
|
|
|
254
254
|
|
|
255
255
|
## Quickstart
|
|
256
256
|
|
|
257
|
-
The
|
|
257
|
+
The primary command — a high-signal, low-noise summary for Java/Spring codebases:
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
|
-
sourcecode --
|
|
260
|
+
sourcecode --compact
|
|
261
|
+
# ~600-800 tokens: stack, entry points, dependencies, risk flags, confidence.
|
|
262
|
+
|
|
263
|
+
sourcecode --compact --git-context
|
|
264
|
+
# Adds git hotspots and uncommitted file count.
|
|
265
|
+
|
|
266
|
+
sourcecode --compact --copy
|
|
267
|
+
# Copies the result to clipboard.
|
|
261
268
|
```
|
|
262
269
|
|
|
263
|
-
|
|
270
|
+
Example output for a Spring Boot project:
|
|
264
271
|
|
|
265
272
|
```json
|
|
266
273
|
{
|
|
267
|
-
"
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
"
|
|
271
|
-
"
|
|
274
|
+
"project_type": "api",
|
|
275
|
+
"stacks": [{ "stack": "java", "frameworks": ["Spring Boot", "MyBatis"] }],
|
|
276
|
+
"entry_points": {
|
|
277
|
+
"bootstrap": ["src/main/java/io/spring/RealWorldApplication.java"],
|
|
278
|
+
"security": ["src/main/java/io/spring/api/security/WebSecurityConfig.java"],
|
|
279
|
+
"controllers": { "count": 8, "sample": [...] }
|
|
272
280
|
},
|
|
273
|
-
"
|
|
274
|
-
{
|
|
275
|
-
"
|
|
276
|
-
"stack": "java",
|
|
277
|
-
"kind": "application",
|
|
278
|
-
"confidence": "high"
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
"path": "atlas-client/src/main.ts",
|
|
282
|
-
"stack": "nodejs",
|
|
283
|
-
"kind": "entrypoint",
|
|
284
|
-
"confidence": "high"
|
|
285
|
-
}
|
|
281
|
+
"key_dependencies": [
|
|
282
|
+
{ "name": "org.mybatis.spring.boot:mybatis-spring-boot-starter",
|
|
283
|
+
"version": "2.2.2", "risk_flags": ["spring-boot-2.x-eol"] }
|
|
286
284
|
],
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
285
|
+
"language_version": "11",
|
|
286
|
+
"deployment": { "spring_boot_version": "2.6.3" },
|
|
287
|
+
"mybatis": { "mapper_interfaces": 4, "xml_files": 4 },
|
|
288
|
+
"confidence_summary": { "overall": "high" }
|
|
291
289
|
}
|
|
292
290
|
```
|
|
293
291
|
|
|
294
|
-
For
|
|
292
|
+
For full structured output with per-file contracts and signals, use `--agent`:
|
|
295
293
|
|
|
296
294
|
```bash
|
|
297
|
-
sourcecode --
|
|
298
|
-
# Copies the summary to the clipboard. Ready to paste.
|
|
295
|
+
sourcecode --agent
|
|
299
296
|
```
|
|
300
297
|
|
|
301
298
|
---
|
|
@@ -304,41 +301,18 @@ sourcecode --compact --copy
|
|
|
304
301
|
|
|
305
302
|
### Global options
|
|
306
303
|
|
|
307
|
-
| Flag | Alias | Type | Default | Description |
|
|
308
|
-
|
|
309
|
-
| `--
|
|
310
|
-
| `--
|
|
311
|
-
| `--
|
|
312
|
-
| `--
|
|
313
|
-
| `--
|
|
314
|
-
| `--
|
|
315
|
-
| `--
|
|
316
|
-
| `--
|
|
317
|
-
| `--
|
|
318
|
-
| `--
|
|
319
|
-
| `--copy` | `-c` | flag | off | Copies output to the clipboard after successful execution. | ✅ CORE |
|
|
320
|
-
| `--depth` | | `INT [1–20]` | `4` | Maximum file tree traversal depth. Java/Maven requires ≥8. | ✅ CORE |
|
|
321
|
-
| `--mode` | | `contract\|standard\|raw` | `contract` | `contract`: minimal contracts per file. `standard`: full detail. `raw`: project level only. | ✅ CORE |
|
|
322
|
-
| `--tree` | | flag | off | Includes full `file_tree` and `file_paths` in the output. Increases size significantly. | ✅ CORE |
|
|
323
|
-
| `--changed-only` | | flag | off | Contract mode: only files modified in git (staged, unstaged, untracked). | ✅ CORE |
|
|
324
|
-
| `--rank-by` | | `relevance\|centrality\|git-churn` | `relevance` | File ranking strategy in contract mode. | ✅ CORE |
|
|
325
|
-
| `--semantics` | | flag | off | Cross-file symbol resolution, call graph with confidence levels, fan-in/fan-out hotspots. Slower. | 🧪 EXP |
|
|
326
|
-
| `--architecture` | | flag | off | Architectural layer inference (MVC/hexagonal/bounded contexts). Low confidence without `--semantics`. | 🧪 EXP |
|
|
327
|
-
| `--graph-modules` | | flag | off | Structural module graph: nodes (files/symbols) and edges (imports, calls, contains). | 🧪 EXP |
|
|
328
|
-
| `--graph-detail` | | `high\|medium\|full` | `high` | Module graph detail level. | 🧪 EXP |
|
|
329
|
-
| `--max-nodes` | | `INT [≥1]` | — | Maximum nodes in `--graph-modules`. Prevents huge graphs in large repos. | 🧪 EXP |
|
|
330
|
-
| `--graph-edges` | | `TEXT` | all | Edge types for `--graph-modules`, comma-separated: `imports,calls,contains`. | 🧪 EXP |
|
|
331
|
-
| `--docs` | | flag | off | Extracts docstrings, function signatures, and module comments. | 🧪 EXP |
|
|
332
|
-
| `--docs-depth` | | `module\|symbols\|full` | `symbols` | Docs extraction depth. `full` includes private symbols. | 🧪 EXP |
|
|
333
|
-
| `--symbol` | | `TEXT` | — | Contract mode: localized context for a specific symbol. Python, TS, JS only. **Does not support Java.** | 🧪 EXP |
|
|
334
|
-
| `--max-importers` | | `INT [1–10000]` | `50` | Limit on importer files returned by `--symbol`. | 🧪 EXP |
|
|
335
|
-
| `--full-metrics` | | flag | off | Per-file technical metrics: LOC, cyclomatic complexity, coverage. Aimed at CI, not at agents. | 🧪 EXP |
|
|
336
|
-
| `--emit-graph` | | flag | off | Contract mode: includes a compact dependency graph (nodes + edges) in the output. | 🚧 WIP |
|
|
337
|
-
| `--entrypoints-only` | | flag | off | Contract mode: only files with exports or entry points. Note: includes *all* files with exports. | 🚧 WIP |
|
|
338
|
-
| `--max-symbols` | | `INT [≥1]` | — | Limits total exported symbols in contract mode. Discards lower-ranked files. | 🚧 WIP |
|
|
339
|
-
| `--no-redact` | | flag | off | Disables automatic secret redaction. Output may contain sensitive values. | 🚧 WIP |
|
|
340
|
-
| `--trace-pipeline` | | flag | off | Diagnostic mode: includes a trace of each candidate and filtering decision. Debugging only. | 🚧 WIP |
|
|
341
|
-
| `--version` | `-v` | flag | — | Shows version and exits. | ✅ CORE |
|
|
304
|
+
| Flag | Alias | Type | Default | Description |
|
|
305
|
+
|------|-------|------|---------|-------------|
|
|
306
|
+
| `--compact` | | flag | off | **Recommended.** ~600-800 token summary: stack, entry points, deps, risk flags, confidence. |
|
|
307
|
+
| `--git-context` | `-g` | flag | off | Adds git hotspots (top changed files), branch, uncommitted file count. Use with `--compact`. |
|
|
308
|
+
| `--agent` | | flag | off | Full structured JSON for AI agents. Auto-enables dependency, env-var, and code-notes analysis. |
|
|
309
|
+
| `--changed-only` | | flag | off | Limit output to git-modified files (staged, unstaged, untracked). |
|
|
310
|
+
| `--depth` | | `INT [1–20]` | `4` | File tree traversal depth. Java projects auto-adjust to 12. |
|
|
311
|
+
| `--format` | `-f` | `json\|yaml` | `json` | Output format. JSON preferred in pipelines. |
|
|
312
|
+
| `--output` | `-o` | `PATH` | stdout | Write output to a file instead of stdout. |
|
|
313
|
+
| `--copy` | `-c` | flag | off | Copy output to clipboard after a successful run. |
|
|
314
|
+
| `--no-redact` | | flag | off | Disable automatic secret redaction. Output may contain sensitive values. |
|
|
315
|
+
| `--version` | `-v` | flag | — | Show version and exit. |
|
|
342
316
|
|
|
343
317
|
---
|
|
344
318
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# sourcecode
|
|
2
|
+
|
|
3
|
+
**Deterministic codebase context for AI coding agents.**
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What is it?
|
|
12
|
+
|
|
13
|
+
`sourcecode` analyzes a repository and produces a structured context map (JSON or YAML) designed to be consumed by AI agents or language models. It solves the "stuff the whole repo into the prompt" problem by instead producing a deterministic extract: entry points, dependencies, stacks, inline annotations, environment variables, and git activity. It is an MVP tool under active evolution — the semantic analysis and module graph features work but have known limitations that are explicitly documented below.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
**Prerequisites:** Python 3.10+
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install sourcecode
|
|
23
|
+
# or with pipx for isolation:
|
|
24
|
+
pipx install sourcecode
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Verify installation:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
sourcecode version
|
|
31
|
+
# sourcecode 1.0.0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
The primary command — a high-signal, low-noise summary for Java/Spring codebases:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
sourcecode --compact
|
|
42
|
+
# ~600-800 tokens: stack, entry points, dependencies, risk flags, confidence.
|
|
43
|
+
|
|
44
|
+
sourcecode --compact --git-context
|
|
45
|
+
# Adds git hotspots and uncommitted file count.
|
|
46
|
+
|
|
47
|
+
sourcecode --compact --copy
|
|
48
|
+
# Copies the result to clipboard.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Example output for a Spring Boot project:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"project_type": "api",
|
|
56
|
+
"stacks": [{ "stack": "java", "frameworks": ["Spring Boot", "MyBatis"] }],
|
|
57
|
+
"entry_points": {
|
|
58
|
+
"bootstrap": ["src/main/java/io/spring/RealWorldApplication.java"],
|
|
59
|
+
"security": ["src/main/java/io/spring/api/security/WebSecurityConfig.java"],
|
|
60
|
+
"controllers": { "count": 8, "sample": [...] }
|
|
61
|
+
},
|
|
62
|
+
"key_dependencies": [
|
|
63
|
+
{ "name": "org.mybatis.spring.boot:mybatis-spring-boot-starter",
|
|
64
|
+
"version": "2.2.2", "risk_flags": ["spring-boot-2.x-eol"] }
|
|
65
|
+
],
|
|
66
|
+
"language_version": "11",
|
|
67
|
+
"deployment": { "spring_boot_version": "2.6.3" },
|
|
68
|
+
"mybatis": { "mapper_interfaces": 4, "xml_files": 4 },
|
|
69
|
+
"confidence_summary": { "overall": "high" }
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For full structured output with per-file contracts and signals, use `--agent`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sourcecode --agent
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Flags reference
|
|
82
|
+
|
|
83
|
+
### Global options
|
|
84
|
+
|
|
85
|
+
| Flag | Alias | Type | Default | Description |
|
|
86
|
+
|------|-------|------|---------|-------------|
|
|
87
|
+
| `--compact` | | flag | off | **Recommended.** ~600-800 token summary: stack, entry points, deps, risk flags, confidence. |
|
|
88
|
+
| `--git-context` | `-g` | flag | off | Adds git hotspots (top changed files), branch, uncommitted file count. Use with `--compact`. |
|
|
89
|
+
| `--agent` | | flag | off | Full structured JSON for AI agents. Auto-enables dependency, env-var, and code-notes analysis. |
|
|
90
|
+
| `--changed-only` | | flag | off | Limit output to git-modified files (staged, unstaged, untracked). |
|
|
91
|
+
| `--depth` | | `INT [1–20]` | `4` | File tree traversal depth. Java projects auto-adjust to 12. |
|
|
92
|
+
| `--format` | `-f` | `json\|yaml` | `json` | Output format. JSON preferred in pipelines. |
|
|
93
|
+
| `--output` | `-o` | `PATH` | stdout | Write output to a file instead of stdout. |
|
|
94
|
+
| `--copy` | `-c` | flag | off | Copy output to clipboard after a successful run. |
|
|
95
|
+
| `--no-redact` | | flag | off | Disable automatic secret redaction. Output may contain sensitive values. |
|
|
96
|
+
| `--version` | `-v` | flag | — | Show version and exit. |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Subcommands
|
|
101
|
+
|
|
102
|
+
### `prepare-context TASK [PATH]`
|
|
103
|
+
|
|
104
|
+
Generates task-specific context for AI agents.
|
|
105
|
+
|
|
106
|
+
| Task | Description | Status |
|
|
107
|
+
|------|-------------|--------|
|
|
108
|
+
| `explain` | Architecture, entry points, key dependencies | ✅ CORE |
|
|
109
|
+
| `fix-bug` | Files prioritized by risk, inline annotations | ✅ CORE |
|
|
110
|
+
| `onboard` | Full context for new agents or developers | ✅ CORE |
|
|
111
|
+
| `delta` | Incremental context: only files changed in git | ✅ CORE |
|
|
112
|
+
| `refactor` | Structural problems, improvement opportunities | 🧪 EXP |
|
|
113
|
+
| `generate-tests` | Files without tests, coverage gap analysis | 🧪 EXP |
|
|
114
|
+
| `review-pr` | Changed files + architectural impact | 🧪 EXP |
|
|
@@ -141,10 +141,14 @@ def _check_pipeline_coherence(sm: "SourceMap") -> list[str]: # type: ignore[nam
|
|
|
141
141
|
_HELP = """\
|
|
142
142
|
Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
143
143
|
|
|
144
|
+
[bold]Primary usage:[/bold]
|
|
145
|
+
sourcecode --compact high-signal summary (~600-800 tokens)
|
|
146
|
+
sourcecode --compact --git-context include git hotspots and uncommitted files
|
|
147
|
+
|
|
144
148
|
[bold]Examples:[/bold]
|
|
145
149
|
sourcecode saint-server --compact
|
|
150
|
+
sourcecode . --compact --git-context --copy
|
|
146
151
|
sourcecode . --changed-only --git-context
|
|
147
|
-
sourcecode saint-server --symbol SeguridadRestController
|
|
148
152
|
sourcecode prepare-context onboard saint-server
|
|
149
153
|
sourcecode prepare-context delta . --since main
|
|
150
154
|
|
|
@@ -565,7 +569,8 @@ def main(
|
|
|
565
569
|
symbol: Optional[str] = typer.Option(
|
|
566
570
|
None,
|
|
567
571
|
"--symbol",
|
|
568
|
-
|
|
572
|
+
hidden=True,
|
|
573
|
+
help="Extract context for a specific symbol (Python/TS/JS only — not supported for Java).",
|
|
569
574
|
),
|
|
570
575
|
max_importers: int = typer.Option(
|
|
571
576
|
50,
|
|
@@ -586,10 +591,10 @@ def main(
|
|
|
586
591
|
|
|
587
592
|
\b
|
|
588
593
|
Examples:
|
|
589
|
-
sourcecode
|
|
590
|
-
sourcecode
|
|
591
|
-
sourcecode --
|
|
592
|
-
sourcecode --agent
|
|
594
|
+
sourcecode --compact high-signal summary (recommended)
|
|
595
|
+
sourcecode --compact --git-context include git hotspots
|
|
596
|
+
sourcecode /path/to/repo --compact analyze specific path
|
|
597
|
+
sourcecode --agent agent-optimized output (full detail)
|
|
593
598
|
"""
|
|
594
599
|
# First-run consent (skip for telemetry/version/config subcommands)
|
|
595
600
|
if ctx.invoked_subcommand not in ("telemetry", "version", "config"):
|
|
@@ -764,6 +769,16 @@ def main(
|
|
|
764
769
|
_java_min_depth = 12
|
|
765
770
|
effective_depth = max(depth, _java_min_depth) if _is_java and depth < _java_min_depth else depth
|
|
766
771
|
|
|
772
|
+
if symbol is not None and _is_java:
|
|
773
|
+
typer.echo(
|
|
774
|
+
f"Error: --symbol is not supported for Java/JVM repositories. "
|
|
775
|
+
"Per-file AST extraction is unavailable for JVM — symbol search only works with Python, TypeScript, and JavaScript. "
|
|
776
|
+
"Alternatives: use --agent --compact to get file relevance scores, "
|
|
777
|
+
"or use --git-context to find recently changed files.",
|
|
778
|
+
err=True,
|
|
779
|
+
)
|
|
780
|
+
raise typer.Exit(code=1)
|
|
781
|
+
|
|
767
782
|
# --agent: enable signal analyzers; output via agent_view (not compact)
|
|
768
783
|
if agent:
|
|
769
784
|
dependencies = True
|
|
@@ -1489,7 +1504,9 @@ def main(
|
|
|
1489
1504
|
if _is_contract_mode and sm.file_contracts:
|
|
1490
1505
|
from sourcecode.serializer import _serialize_contract_minimal
|
|
1491
1506
|
data.pop("file_relevance", None)
|
|
1492
|
-
|
|
1507
|
+
_jvm_stacks_agent = {"java", "kotlin", "scala", "groovy"}
|
|
1508
|
+
_is_jvm_agent = any(s.stack in _jvm_stacks_agent for s in sm.stacks)
|
|
1509
|
+
_MAX_AGENT_CONTRACTS = 50 if _is_jvm_agent else 10
|
|
1493
1510
|
_all_contracts = sm.file_contracts
|
|
1494
1511
|
_sorted = sorted(_all_contracts, key=lambda c: getattr(c, "relevance_score", 0.0), reverse=True)
|
|
1495
1512
|
_sampled = _sorted[:_MAX_AGENT_CONTRACTS]
|
|
@@ -41,6 +41,34 @@ _M3_FILTRO_PARAMS_RE = re.compile(
|
|
|
41
41
|
r'(?:[^)]*nivelRequerido\s*=\s*(\d+))?'
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
+
# Security config detection
|
|
45
|
+
_WEB_SECURITY_CONFIGURER_RE = re.compile(r'WebSecurityConfigurerAdapter\b')
|
|
46
|
+
_SECURITY_FILTER_CHAIN_RE = re.compile(r'SecurityFilterChain\b')
|
|
47
|
+
_SECURITY_CONFIG_ANNOTATION_RE = re.compile(r'@EnableWebSecurity\b')
|
|
48
|
+
# JWT/token filter detection (files that process every request)
|
|
49
|
+
_ONCE_PER_REQUEST_FILTER_RE = re.compile(r'extends\s+(?:OncePerRequestFilter|GenericFilterBean)\b')
|
|
50
|
+
_JWT_FILTER_KEYWORDS_RE = re.compile(r'\b(?:jwt|token|bearer|authorization)\b', re.IGNORECASE)
|
|
51
|
+
|
|
52
|
+
# @Transactional detection
|
|
53
|
+
_TRANSACTIONAL_RE = re.compile(r'@Transactional\b')
|
|
54
|
+
# Extracts class name: `public class Foo` or `class Foo`
|
|
55
|
+
_CLASS_NAME_RE = re.compile(r'\bclass\s+([A-Z][A-Za-z0-9_]*)')
|
|
56
|
+
|
|
57
|
+
# Gradle plugin Spring Boot version: id 'org.springframework.boot' version '2.6.3'
|
|
58
|
+
_GRADLE_SB_PLUGIN_RE = re.compile(
|
|
59
|
+
r"""id\s*['"]\s*org\.springframework\.boot\s*['"]\s+version\s*['"]([\d.]+)['"']""",
|
|
60
|
+
re.IGNORECASE,
|
|
61
|
+
)
|
|
62
|
+
# Gradle Java version: sourceCompatibility = '11' or sourceCompatibility = JavaVersion.VERSION_11
|
|
63
|
+
_GRADLE_JAVA_VERSION_RE = re.compile(
|
|
64
|
+
r"""(?:sourceCompatibility|targetCompatibility|javaVersion)\s*=\s*['"]?([0-9.]+)['"]?""",
|
|
65
|
+
re.IGNORECASE,
|
|
66
|
+
)
|
|
67
|
+
# JavaVersion.VERSION_11 form
|
|
68
|
+
_GRADLE_JAVA_ENUM_RE = re.compile(
|
|
69
|
+
r"""(?:sourceCompatibility|targetCompatibility)\s*=\s*JavaVersion\.VERSION_(\d+)"""
|
|
70
|
+
)
|
|
71
|
+
|
|
44
72
|
|
|
45
73
|
class JavaDetector(AbstractDetector):
|
|
46
74
|
name = "java"
|
|
@@ -69,6 +97,12 @@ class JavaDetector(AbstractDetector):
|
|
|
69
97
|
if "build.gradle" in context.manifests:
|
|
70
98
|
manifests.append("build.gradle")
|
|
71
99
|
frameworks.extend(self._frameworks_from_gradle(context.root / "build.gradle"))
|
|
100
|
+
if language_version is None:
|
|
101
|
+
try:
|
|
102
|
+
gradle_content = "\n".join(read_text_lines(context.root / "build.gradle"))
|
|
103
|
+
language_version = self._extract_gradle_java_version(gradle_content)
|
|
104
|
+
except OSError:
|
|
105
|
+
pass
|
|
72
106
|
|
|
73
107
|
# Detect app server from descriptor files
|
|
74
108
|
all_paths = flatten_file_tree(context.file_tree)
|
|
@@ -81,6 +115,7 @@ class JavaDetector(AbstractDetector):
|
|
|
81
115
|
spring_profiles = self._detect_spring_profiles(context.root, all_paths)
|
|
82
116
|
|
|
83
117
|
entry_points = self._collect_entry_points(context)
|
|
118
|
+
transactional_classes = self._collect_transactional_classes(context, all_paths)
|
|
84
119
|
stack = StackDetection(
|
|
85
120
|
stack="java",
|
|
86
121
|
detection_method="manifest",
|
|
@@ -91,6 +126,7 @@ class JavaDetector(AbstractDetector):
|
|
|
91
126
|
packaging=packaging,
|
|
92
127
|
app_server_hint=app_server_hint,
|
|
93
128
|
spring_profiles=spring_profiles,
|
|
129
|
+
transactional_classes=transactional_classes,
|
|
94
130
|
)
|
|
95
131
|
return [stack], entry_points
|
|
96
132
|
|
|
@@ -196,8 +232,24 @@ class JavaDetector(AbstractDetector):
|
|
|
196
232
|
return frameworks
|
|
197
233
|
|
|
198
234
|
def _frameworks_from_gradle(self, path: Path) -> list[FrameworkDetection]:
|
|
199
|
-
|
|
200
|
-
|
|
235
|
+
original = "\n".join(read_text_lines(path))
|
|
236
|
+
content = original.lower()
|
|
237
|
+
sb_version = self._extract_gradle_sb_version(original)
|
|
238
|
+
return self._detect_jvm_frameworks(content, "build.gradle", sb_version=sb_version)
|
|
239
|
+
|
|
240
|
+
def _extract_gradle_sb_version(self, content: str) -> str | None:
|
|
241
|
+
m = _GRADLE_SB_PLUGIN_RE.search(content)
|
|
242
|
+
return m.group(1) if m else None
|
|
243
|
+
|
|
244
|
+
def _extract_gradle_java_version(self, content: str) -> str | None:
|
|
245
|
+
m = _GRADLE_JAVA_ENUM_RE.search(content)
|
|
246
|
+
if m:
|
|
247
|
+
v = m.group(1)
|
|
248
|
+
return "1." + v if int(v) <= 8 else v
|
|
249
|
+
m = _GRADLE_JAVA_VERSION_RE.search(content)
|
|
250
|
+
if m:
|
|
251
|
+
return m.group(1)
|
|
252
|
+
return None
|
|
201
253
|
|
|
202
254
|
def _detect_jvm_frameworks(self, text: str, source: str, *, sb_version: str | None = None) -> list[FrameworkDetection]:
|
|
203
255
|
frameworks: list[FrameworkDetection] = []
|
|
@@ -319,7 +371,11 @@ class JavaDetector(AbstractDetector):
|
|
|
319
371
|
return []
|
|
320
372
|
|
|
321
373
|
# Quick pre-filter before running regexes
|
|
322
|
-
|
|
374
|
+
has_controller = "Controller" in content
|
|
375
|
+
has_filter = "Filter" in content
|
|
376
|
+
has_security = "WebSecurityConfigurerAdapter" in content or "SecurityFilterChain" in content or "EnableWebSecurity" in content
|
|
377
|
+
has_once_filter = "OncePerRequestFilter" in content or "GenericFilterBean" in content
|
|
378
|
+
if (not has_controller and not has_filter and not has_security
|
|
323
379
|
and "ControllerAdvice" not in content
|
|
324
380
|
and "M3FiltroSeguridad" not in content):
|
|
325
381
|
return []
|
|
@@ -379,6 +435,23 @@ class JavaDetector(AbstractDetector):
|
|
|
379
435
|
path=rel_path, stack="java", kind="filter",
|
|
380
436
|
source="annotation", confidence="medium",
|
|
381
437
|
)]
|
|
438
|
+
if has_security and (
|
|
439
|
+
_WEB_SECURITY_CONFIGURER_RE.search(content)
|
|
440
|
+
or _SECURITY_CONFIG_ANNOTATION_RE.search(content)
|
|
441
|
+
or _SECURITY_FILTER_CHAIN_RE.search(content)
|
|
442
|
+
):
|
|
443
|
+
return [EntryPoint(
|
|
444
|
+
path=rel_path, stack="java", kind="security_config",
|
|
445
|
+
source="annotation", confidence="high",
|
|
446
|
+
evidence="Spring Security configuration",
|
|
447
|
+
)]
|
|
448
|
+
if has_once_filter and _ONCE_PER_REQUEST_FILTER_RE.search(content):
|
|
449
|
+
is_jwt = bool(_JWT_FILTER_KEYWORDS_RE.search(content))
|
|
450
|
+
return [EntryPoint(
|
|
451
|
+
path=rel_path, stack="java", kind="security_filter",
|
|
452
|
+
source="annotation", confidence="high",
|
|
453
|
+
evidence="jwt_filter" if is_jwt else "request_filter",
|
|
454
|
+
)]
|
|
382
455
|
return []
|
|
383
456
|
|
|
384
457
|
def _parse_web_xml(self, abs_path: Path, rel_path: str) -> list[EntryPoint]:
|
|
@@ -412,3 +485,26 @@ class JavaDetector(AbstractDetector):
|
|
|
412
485
|
seen.add(framework.name)
|
|
413
486
|
result.append(framework)
|
|
414
487
|
return result
|
|
488
|
+
|
|
489
|
+
def _collect_transactional_classes(self, context: DetectionContext, all_paths: list[str]) -> list[str]:
|
|
490
|
+
"""Scan Java source files for @Transactional and return unique class names."""
|
|
491
|
+
classes: list[str] = []
|
|
492
|
+
seen: set[str] = set()
|
|
493
|
+
java_paths = [p for p in all_paths if p.endswith(".java") and "/test/" not in p and "/tests/" not in p]
|
|
494
|
+
for rel_path in java_paths[:_MAX_JAVA_ENTRY_SCAN]:
|
|
495
|
+
abs_path = context.root / rel_path
|
|
496
|
+
try:
|
|
497
|
+
if abs_path.stat().st_size > _MAX_FILE_SIZE:
|
|
498
|
+
continue
|
|
499
|
+
content = abs_path.read_text(encoding="utf-8", errors="replace")
|
|
500
|
+
except OSError:
|
|
501
|
+
continue
|
|
502
|
+
if not _TRANSACTIONAL_RE.search(content):
|
|
503
|
+
continue
|
|
504
|
+
m = _CLASS_NAME_RE.search(content)
|
|
505
|
+
if m:
|
|
506
|
+
cls = m.group(1)
|
|
507
|
+
if cls not in seen:
|
|
508
|
+
seen.add(cls)
|
|
509
|
+
classes.append(cls)
|
|
510
|
+
return classes
|
|
@@ -121,6 +121,7 @@ class ProjectDetector:
|
|
|
121
121
|
packaging=stack.packaging,
|
|
122
122
|
app_server_hint=stack.app_server_hint,
|
|
123
123
|
spring_profiles=list(stack.spring_profiles),
|
|
124
|
+
transactional_classes=list(stack.transactional_classes),
|
|
124
125
|
)
|
|
125
126
|
|
|
126
127
|
def _merge_stack(self, current: StackDetection, incoming: StackDetection) -> StackDetection:
|
|
@@ -144,6 +145,8 @@ class ProjectDetector:
|
|
|
144
145
|
current.app_server_hint = incoming.app_server_hint
|
|
145
146
|
if incoming.spring_profiles and not current.spring_profiles:
|
|
146
147
|
current.spring_profiles = list(incoming.spring_profiles)
|
|
148
|
+
if incoming.transactional_classes and not current.transactional_classes:
|
|
149
|
+
current.transactional_classes = list(incoming.transactional_classes)
|
|
147
150
|
return current
|
|
148
151
|
|
|
149
152
|
def _merge_frameworks(
|
|
@@ -68,6 +68,7 @@ class StackDetection:
|
|
|
68
68
|
packaging: Optional[str] = None # e.g. "war" | "jar"
|
|
69
69
|
app_server_hint: Optional[str] = None # e.g. "weblogic" | "wildfly"
|
|
70
70
|
spring_profiles: list[str] = field(default_factory=list) # detected Spring profiles
|
|
71
|
+
transactional_classes: list[str] = field(default_factory=list) # classes with @Transactional
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
@dataclass
|
|
@@ -282,6 +282,24 @@ def _mybatis_pairing(sm: "SourceMap") -> "Optional[dict[str, Any]]":
|
|
|
282
282
|
return result
|
|
283
283
|
|
|
284
284
|
|
|
285
|
+
def _spring_boot_version(sm: "SourceMap") -> "Optional[str]":
|
|
286
|
+
"""Extract Spring Boot version from detected frameworks."""
|
|
287
|
+
for s in sm.stacks:
|
|
288
|
+
for fw in s.frameworks:
|
|
289
|
+
if fw.name == "Spring Boot" and fw.version:
|
|
290
|
+
return fw.version
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _transactional_summary(sm: "SourceMap") -> "Optional[dict[str, Any]]":
|
|
295
|
+
"""Surface @Transactional class boundaries from the Java stack detection."""
|
|
296
|
+
for s in sm.stacks:
|
|
297
|
+
classes = getattr(s, "transactional_classes", [])
|
|
298
|
+
if classes:
|
|
299
|
+
return {"count": len(classes), "classes": classes[:10]}
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
|
|
285
303
|
def _security_surface_from_eps(eps: list) -> "Optional[dict[str, Any]]":
|
|
286
304
|
"""Extract @M3FiltroSeguridad resource names from entry point evidence strings."""
|
|
287
305
|
import re as _re
|
|
@@ -715,9 +733,12 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
|
|
|
715
733
|
_language_version = getattr(sm, "language_version", None)
|
|
716
734
|
_packaging = getattr(sm, "packaging", None)
|
|
717
735
|
_app_server = getattr(sm, "app_server_hint", None)
|
|
736
|
+
_sb_version = _spring_boot_version(sm)
|
|
718
737
|
_deployment: Any = None
|
|
719
|
-
if _packaging or _app_server:
|
|
738
|
+
if _packaging or _app_server or _sb_version:
|
|
720
739
|
_deployment = {}
|
|
740
|
+
if _sb_version:
|
|
741
|
+
_deployment["spring_boot_version"] = _sb_version
|
|
721
742
|
if _packaging:
|
|
722
743
|
_deployment["packaging"] = _packaging
|
|
723
744
|
if _app_server:
|
|
@@ -725,8 +746,15 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
|
|
|
725
746
|
_deploy_risks = _project_deployment_risks(sm)
|
|
726
747
|
_security_surface = _security_surface_from_eps(sm.entry_points)
|
|
727
748
|
_mybatis = _mybatis_pairing(sm)
|
|
749
|
+
_transactional = _transactional_summary(sm)
|
|
728
750
|
_git_ctx = _compact_git_context(sm)
|
|
729
751
|
|
|
752
|
+
# Suppress empty optional sections (no signal value)
|
|
753
|
+
_effective_env_summary = env_summary_dict if (env_summary_dict and env_summary_dict.get("total", 0) > 0) else None
|
|
754
|
+
_effective_env_map = env_map_items if _effective_env_summary else None
|
|
755
|
+
_effective_notes_summary = code_notes_summary_dict if (code_notes_summary_dict and code_notes_summary_dict.get("total", 0) > 0) else None
|
|
756
|
+
_effective_notes = code_notes_items if _effective_notes_summary else None
|
|
757
|
+
|
|
730
758
|
result: dict[str, Any] = {
|
|
731
759
|
"schema_version": sm.metadata.schema_version,
|
|
732
760
|
"project_type": sm.project_type,
|
|
@@ -736,10 +764,10 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
|
|
|
736
764
|
"entry_points": entry_points_compact,
|
|
737
765
|
"dependency_summary": dep_summary_dict,
|
|
738
766
|
"key_dependencies": key_deps,
|
|
739
|
-
"env_summary":
|
|
740
|
-
"env_map":
|
|
741
|
-
"code_notes_summary":
|
|
742
|
-
"code_notes":
|
|
767
|
+
"env_summary": _effective_env_summary,
|
|
768
|
+
"env_map": _effective_env_map,
|
|
769
|
+
"code_notes_summary": _effective_notes_summary,
|
|
770
|
+
"code_notes": _effective_notes,
|
|
743
771
|
"confidence_summary": conf_dict,
|
|
744
772
|
"analysis_gaps": gaps_list,
|
|
745
773
|
}
|
|
@@ -753,6 +781,8 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
|
|
|
753
781
|
result["security_surface"] = _security_surface
|
|
754
782
|
if _mybatis:
|
|
755
783
|
result["mybatis"] = _mybatis
|
|
784
|
+
if _transactional:
|
|
785
|
+
result["transactional_boundaries"] = _transactional
|
|
756
786
|
if _git_ctx:
|
|
757
787
|
result["git_context"] = _git_ctx
|
|
758
788
|
_always_include = {"project_type", "project_summary", "architecture_summary", "dependency_summary"}
|
|
@@ -1076,10 +1106,13 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
|
|
|
1076
1106
|
_lv = getattr(sm, "language_version", None)
|
|
1077
1107
|
_pkg = getattr(sm, "packaging", None)
|
|
1078
1108
|
_app_srv = getattr(sm, "app_server_hint", None)
|
|
1109
|
+
_sb_ver = _spring_boot_version(sm)
|
|
1079
1110
|
if _lv:
|
|
1080
1111
|
project["language_version"] = _lv
|
|
1081
|
-
if _pkg or _app_srv:
|
|
1112
|
+
if _pkg or _app_srv or _sb_ver:
|
|
1082
1113
|
_depl: dict[str, Any] = {}
|
|
1114
|
+
if _sb_ver:
|
|
1115
|
+
_depl["spring_boot_version"] = _sb_ver
|
|
1083
1116
|
if _pkg:
|
|
1084
1117
|
_depl["packaging"] = _pkg
|
|
1085
1118
|
if _app_srv:
|
|
@@ -1215,13 +1248,16 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
|
|
|
1215
1248
|
sem_info["hotspots"] = sem.hotspots[:10]
|
|
1216
1249
|
signals["semantic_graph"] = sem_info
|
|
1217
1250
|
|
|
1218
|
-
# Java/Spring: security surface
|
|
1251
|
+
# Java/Spring: security surface, ORM structure, transactional boundaries
|
|
1219
1252
|
_sec_surf = _security_surface_from_eps(sm.entry_points)
|
|
1220
1253
|
if _sec_surf:
|
|
1221
1254
|
signals["security_surface"] = _sec_surf
|
|
1222
1255
|
_mb = _mybatis_pairing(sm)
|
|
1223
1256
|
if _mb:
|
|
1224
1257
|
signals["mybatis"] = _mb
|
|
1258
|
+
_txn = _transactional_summary(sm)
|
|
1259
|
+
if _txn:
|
|
1260
|
+
signals["transactional_boundaries"] = _txn
|
|
1225
1261
|
|
|
1226
1262
|
if signals:
|
|
1227
1263
|
result["signals"] = signals
|
|
@@ -188,6 +188,9 @@ class ProjectSummarizer:
|
|
|
188
188
|
return None
|
|
189
189
|
|
|
190
190
|
def _extract_first_useful_paragraph(self, content: str) -> str | None:
|
|
191
|
+
import re as _re
|
|
192
|
+
_BADGE_RE = _re.compile(r"^\[?!\[") # [] or 
|
|
193
|
+
_LINK_ONLY_RE = _re.compile(r"^\[.*?\]\(.*?\)$") # pure link line
|
|
191
194
|
lines: list[str] = []
|
|
192
195
|
in_code_block = False
|
|
193
196
|
for raw_line in content.splitlines():
|
|
@@ -195,10 +198,13 @@ class ProjectSummarizer:
|
|
|
195
198
|
if line.startswith("```"):
|
|
196
199
|
in_code_block = not in_code_block
|
|
197
200
|
continue
|
|
198
|
-
if in_code_block or not line or line.startswith(("#", "<!--")):
|
|
201
|
+
if in_code_block or not line or line.startswith(("#", "<!--", ">")):
|
|
199
202
|
if lines:
|
|
200
203
|
break
|
|
201
204
|
continue
|
|
205
|
+
# Skip badge-only lines and pure-link lines — they are metadata, not descriptions
|
|
206
|
+
if _BADGE_RE.match(line) or (not lines and _LINK_ONLY_RE.match(line)):
|
|
207
|
+
continue
|
|
202
208
|
lines.append(line)
|
|
203
209
|
if not lines:
|
|
204
210
|
return None
|