sourcecode 1.11.0__tar.gz → 1.13.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.13.0}/PKG-INFO +38 -64
- sourcecode-1.13.0/README.md +114 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/pyproject.toml +1 -1
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/architecture_summary.py +3 -3
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/classifier.py +12 -3
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/cli.py +30 -32
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/java.py +106 -9
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/project.py +3 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/prepare_context.py +17 -3
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/schema.py +1 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/serializer.py +55 -10
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/summarizer.py +7 -1
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_cli.py +1 -1
- sourcecode-1.13.0/tests/test_surface_honesty.py +339 -0
- sourcecode-1.11.0/README.md +0 -140
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.continue-here.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.gitignore +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/.ruff.toml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/LICENSE +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/SECURITY.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/docs/privacy.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/docs/schema.md +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/raw +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/run_cli.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/__init__.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/conftest.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.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.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_v1_10_regressions.py +0 -0
- {sourcecode-1.11.0 → sourcecode-1.13.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.13.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 |
|
|
@@ -173,7 +173,7 @@ class ArchitectureSummarizer:
|
|
|
173
173
|
fw_names = list(dict.fromkeys(fw_names))[:3] # dedup, preserve order
|
|
174
174
|
else:
|
|
175
175
|
# Filter out frameworks from auxiliary or tooling stacks (docs/, benchmarks/, etc.)
|
|
176
|
-
fw_names =
|
|
176
|
+
fw_names = list(dict.fromkeys(
|
|
177
177
|
f.name
|
|
178
178
|
for s in sm.stacks
|
|
179
179
|
if not self._is_tooling_path(s.root or "")
|
|
@@ -181,7 +181,7 @@ class ArchitectureSummarizer:
|
|
|
181
181
|
and not self._is_auxiliary_path(s.root or "")
|
|
182
182
|
and not self._is_auxiliary_path(s.workspace or "")
|
|
183
183
|
for f in s.frameworks[:2]
|
|
184
|
-
|
|
184
|
+
))[:3]
|
|
185
185
|
|
|
186
186
|
fw_str = f" using {', '.join(fw_names)}" if fw_names else ""
|
|
187
187
|
if runtime:
|
|
@@ -288,7 +288,7 @@ class ArchitectureSummarizer:
|
|
|
288
288
|
|
|
289
289
|
def _summarize_java_entry(self, path: str, content: str, stacks: list[StackDetection]) -> list[str]:
|
|
290
290
|
lines: list[str] = []
|
|
291
|
-
frameworks =
|
|
291
|
+
frameworks = list(dict.fromkeys(f.name for stack in stacks for f in stack.frameworks))
|
|
292
292
|
if frameworks:
|
|
293
293
|
lines.append(f"Frameworks detectados: {', '.join(frameworks)}.")
|
|
294
294
|
annotations = re.findall(r"@(SpringBootApplication|QuarkusMain|MicronautApplication|Application)\b", content)
|
|
@@ -150,9 +150,18 @@ class TypeClassifier:
|
|
|
150
150
|
score = {"low": 0, "medium": 1, "high": 2}.get(stack.confidence, 0)
|
|
151
151
|
manifest_weight = 1 if stack.manifests else 0
|
|
152
152
|
priority = 0
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
# Backend server-side stacks with manifest evidence outrank frontend stacks
|
|
154
|
+
# in fullstack projects (e.g. Java+Spring wins over nodejs+Angular).
|
|
155
|
+
if (project_type in {"fullstack", "api"}
|
|
156
|
+
and stack.stack in _API_STACKS
|
|
157
|
+
and stack.manifests):
|
|
158
|
+
priority = 4
|
|
159
|
+
elif (project_type in {"webapp", "fullstack"} and stack.stack in {"nodejs", "elixir"}
|
|
160
|
+
or project_type == "api" and stack.stack in _API_STACKS
|
|
161
|
+
or project_type == "cli" and any(
|
|
162
|
+
framework.name in _CLI_FRAMEWORKS for framework in stack.frameworks
|
|
163
|
+
)
|
|
164
|
+
or project_type == "cli" and stack.stack in {"cpp", "dotnet"}):
|
|
156
165
|
priority = 3
|
|
157
166
|
return (priority, score, manifest_weight)
|
|
158
167
|
|
|
@@ -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"):
|
|
@@ -700,6 +705,11 @@ def main(
|
|
|
700
705
|
elif mode not in _CONTRACT_MODES and mode != "raw":
|
|
701
706
|
mode = "contract" # unknown → safe default
|
|
702
707
|
|
|
708
|
+
# --changed-only forces compact output to bound size; full contract/raw scan
|
|
709
|
+
# of a large repo produces 100KB+ even with only 2 changed files.
|
|
710
|
+
if changed_only and not compact and not agent:
|
|
711
|
+
compact = True
|
|
712
|
+
|
|
703
713
|
# Legacy flags imply raw mode unless --mode was explicitly overridden.
|
|
704
714
|
# --format yaml and --graph-modules are now compatible with contract_view:
|
|
705
715
|
# yaml is a serialization format (not an output-section flag)
|
|
@@ -764,6 +774,16 @@ def main(
|
|
|
764
774
|
_java_min_depth = 12
|
|
765
775
|
effective_depth = max(depth, _java_min_depth) if _is_java and depth < _java_min_depth else depth
|
|
766
776
|
|
|
777
|
+
if symbol is not None and _is_java:
|
|
778
|
+
typer.echo(
|
|
779
|
+
f"Error: --symbol is not supported for Java/JVM repositories. "
|
|
780
|
+
"Per-file AST extraction is unavailable for JVM — symbol search only works with Python, TypeScript, and JavaScript. "
|
|
781
|
+
"Alternatives: use --agent --compact to get file relevance scores, "
|
|
782
|
+
"or use --git-context to find recently changed files.",
|
|
783
|
+
err=True,
|
|
784
|
+
)
|
|
785
|
+
raise typer.Exit(code=1)
|
|
786
|
+
|
|
767
787
|
# --agent: enable signal analyzers; output via agent_view (not compact)
|
|
768
788
|
if agent:
|
|
769
789
|
dependencies = True
|
|
@@ -1483,31 +1503,6 @@ def main(
|
|
|
1483
1503
|
content = json.dumps(data, indent=2, ensure_ascii=False)
|
|
1484
1504
|
elif agent:
|
|
1485
1505
|
data = agent_view(sm)
|
|
1486
|
-
# When contract pipeline ran (mode=contract, no legacy flags), include
|
|
1487
|
-
# per-file contracts in agent output so agents get structural context.
|
|
1488
|
-
# Remove file_relevance — contracts cover this signal with more detail.
|
|
1489
|
-
if _is_contract_mode and sm.file_contracts:
|
|
1490
|
-
from sourcecode.serializer import _serialize_contract_minimal
|
|
1491
|
-
data.pop("file_relevance", None)
|
|
1492
|
-
_MAX_AGENT_CONTRACTS = 10
|
|
1493
|
-
_all_contracts = sm.file_contracts
|
|
1494
|
-
_sorted = sorted(_all_contracts, key=lambda c: getattr(c, "relevance_score", 0.0), reverse=True)
|
|
1495
|
-
_sampled = _sorted[:_MAX_AGENT_CONTRACTS]
|
|
1496
|
-
_total_contracts = len(_all_contracts)
|
|
1497
|
-
data["contracts"] = [_serialize_contract_minimal(c) for c in _sampled]
|
|
1498
|
-
data["contracts_meta"] = {
|
|
1499
|
-
"total": _total_contracts,
|
|
1500
|
-
"shown": len(_sampled),
|
|
1501
|
-
"truncated": _total_contracts > _MAX_AGENT_CONTRACTS,
|
|
1502
|
-
}
|
|
1503
|
-
if sm.contract_summary is not None:
|
|
1504
|
-
cs = sm.contract_summary
|
|
1505
|
-
data["contract_summary"] = {
|
|
1506
|
-
"files": cs.extracted_files,
|
|
1507
|
-
"total": cs.total_files,
|
|
1508
|
-
}
|
|
1509
|
-
if cs.method_breakdown:
|
|
1510
|
-
data["contract_summary"]["methods"] = cs.method_breakdown
|
|
1511
1506
|
if not no_redact:
|
|
1512
1507
|
data = redact_dict(data)
|
|
1513
1508
|
content = json.dumps(data, indent=2, ensure_ascii=False)
|
|
@@ -1684,7 +1679,10 @@ def prepare_context_cmd(
|
|
|
1684
1679
|
"project_summary": output.project_summary,
|
|
1685
1680
|
"architecture_summary": output.architecture_summary,
|
|
1686
1681
|
"confidence": output.confidence,
|
|
1687
|
-
"relevant_files": [
|
|
1682
|
+
"relevant_files": [
|
|
1683
|
+
{k: v for k, v in asdict(f).items() if v != ""}
|
|
1684
|
+
for f in output.relevant_files
|
|
1685
|
+
],
|
|
1688
1686
|
"why_these_files": output.why_these_files,
|
|
1689
1687
|
"key_dependencies": output.key_dependencies,
|
|
1690
1688
|
}
|
|
@@ -37,8 +37,37 @@ _REQUEST_METHOD_VERB_RE = re.compile(
|
|
|
37
37
|
# @M3FiltroSeguridad custom security annotation
|
|
38
38
|
_M3_FILTRO_RE = re.compile(r'@M3FiltroSeguridad\b')
|
|
39
39
|
_M3_FILTRO_PARAMS_RE = re.compile(
|
|
40
|
-
r'@M3FiltroSeguridad\s*\(\s*
|
|
41
|
-
r'(?:
|
|
40
|
+
r'@M3FiltroSeguridad\s*\(\s*'
|
|
41
|
+
r'(?:nombreRecurso\s*=\s*(?:"([^"]*)"|([\w.]+)))?' # group 1: string literal, group 2: constant ref
|
|
42
|
+
r'(?:[^)]*nivelRequerido\s*=\s*(\d+))?' # group 3: nivel
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Security config detection
|
|
46
|
+
_WEB_SECURITY_CONFIGURER_RE = re.compile(r'WebSecurityConfigurerAdapter\b')
|
|
47
|
+
_SECURITY_FILTER_CHAIN_RE = re.compile(r'SecurityFilterChain\b')
|
|
48
|
+
_SECURITY_CONFIG_ANNOTATION_RE = re.compile(r'@EnableWebSecurity\b')
|
|
49
|
+
# JWT/token filter detection (files that process every request)
|
|
50
|
+
_ONCE_PER_REQUEST_FILTER_RE = re.compile(r'extends\s+(?:OncePerRequestFilter|GenericFilterBean)\b')
|
|
51
|
+
_JWT_FILTER_KEYWORDS_RE = re.compile(r'\b(?:jwt|token|bearer|authorization)\b', re.IGNORECASE)
|
|
52
|
+
|
|
53
|
+
# @Transactional detection
|
|
54
|
+
_TRANSACTIONAL_RE = re.compile(r'@Transactional\b')
|
|
55
|
+
# Extracts class name: `public class Foo` or `class Foo`
|
|
56
|
+
_CLASS_NAME_RE = re.compile(r'\bclass\s+([A-Z][A-Za-z0-9_]*)')
|
|
57
|
+
|
|
58
|
+
# Gradle plugin Spring Boot version: id 'org.springframework.boot' version '2.6.3'
|
|
59
|
+
_GRADLE_SB_PLUGIN_RE = re.compile(
|
|
60
|
+
r"""id\s*['"]\s*org\.springframework\.boot\s*['"]\s+version\s*['"]([\d.]+)['"']""",
|
|
61
|
+
re.IGNORECASE,
|
|
62
|
+
)
|
|
63
|
+
# Gradle Java version: sourceCompatibility = '11' or sourceCompatibility = JavaVersion.VERSION_11
|
|
64
|
+
_GRADLE_JAVA_VERSION_RE = re.compile(
|
|
65
|
+
r"""(?:sourceCompatibility|targetCompatibility|javaVersion)\s*=\s*['"]?([0-9.]+)['"]?""",
|
|
66
|
+
re.IGNORECASE,
|
|
67
|
+
)
|
|
68
|
+
# JavaVersion.VERSION_11 form
|
|
69
|
+
_GRADLE_JAVA_ENUM_RE = re.compile(
|
|
70
|
+
r"""(?:sourceCompatibility|targetCompatibility)\s*=\s*JavaVersion\.VERSION_(\d+)"""
|
|
42
71
|
)
|
|
43
72
|
|
|
44
73
|
|
|
@@ -69,6 +98,12 @@ class JavaDetector(AbstractDetector):
|
|
|
69
98
|
if "build.gradle" in context.manifests:
|
|
70
99
|
manifests.append("build.gradle")
|
|
71
100
|
frameworks.extend(self._frameworks_from_gradle(context.root / "build.gradle"))
|
|
101
|
+
if language_version is None:
|
|
102
|
+
try:
|
|
103
|
+
gradle_content = "\n".join(read_text_lines(context.root / "build.gradle"))
|
|
104
|
+
language_version = self._extract_gradle_java_version(gradle_content)
|
|
105
|
+
except OSError:
|
|
106
|
+
pass
|
|
72
107
|
|
|
73
108
|
# Detect app server from descriptor files
|
|
74
109
|
all_paths = flatten_file_tree(context.file_tree)
|
|
@@ -81,6 +116,7 @@ class JavaDetector(AbstractDetector):
|
|
|
81
116
|
spring_profiles = self._detect_spring_profiles(context.root, all_paths)
|
|
82
117
|
|
|
83
118
|
entry_points = self._collect_entry_points(context)
|
|
119
|
+
transactional_classes = self._collect_transactional_classes(context, all_paths)
|
|
84
120
|
stack = StackDetection(
|
|
85
121
|
stack="java",
|
|
86
122
|
detection_method="manifest",
|
|
@@ -91,6 +127,7 @@ class JavaDetector(AbstractDetector):
|
|
|
91
127
|
packaging=packaging,
|
|
92
128
|
app_server_hint=app_server_hint,
|
|
93
129
|
spring_profiles=spring_profiles,
|
|
130
|
+
transactional_classes=transactional_classes,
|
|
94
131
|
)
|
|
95
132
|
return [stack], entry_points
|
|
96
133
|
|
|
@@ -196,8 +233,24 @@ class JavaDetector(AbstractDetector):
|
|
|
196
233
|
return frameworks
|
|
197
234
|
|
|
198
235
|
def _frameworks_from_gradle(self, path: Path) -> list[FrameworkDetection]:
|
|
199
|
-
|
|
200
|
-
|
|
236
|
+
original = "\n".join(read_text_lines(path))
|
|
237
|
+
content = original.lower()
|
|
238
|
+
sb_version = self._extract_gradle_sb_version(original)
|
|
239
|
+
return self._detect_jvm_frameworks(content, "build.gradle", sb_version=sb_version)
|
|
240
|
+
|
|
241
|
+
def _extract_gradle_sb_version(self, content: str) -> str | None:
|
|
242
|
+
m = _GRADLE_SB_PLUGIN_RE.search(content)
|
|
243
|
+
return m.group(1) if m else None
|
|
244
|
+
|
|
245
|
+
def _extract_gradle_java_version(self, content: str) -> str | None:
|
|
246
|
+
m = _GRADLE_JAVA_ENUM_RE.search(content)
|
|
247
|
+
if m:
|
|
248
|
+
v = m.group(1)
|
|
249
|
+
return "1." + v if int(v) <= 8 else v
|
|
250
|
+
m = _GRADLE_JAVA_VERSION_RE.search(content)
|
|
251
|
+
if m:
|
|
252
|
+
return m.group(1)
|
|
253
|
+
return None
|
|
201
254
|
|
|
202
255
|
def _detect_jvm_frameworks(self, text: str, source: str, *, sb_version: str | None = None) -> list[FrameworkDetection]:
|
|
203
256
|
frameworks: list[FrameworkDetection] = []
|
|
@@ -319,7 +372,11 @@ class JavaDetector(AbstractDetector):
|
|
|
319
372
|
return []
|
|
320
373
|
|
|
321
374
|
# Quick pre-filter before running regexes
|
|
322
|
-
|
|
375
|
+
has_controller = "Controller" in content
|
|
376
|
+
has_filter = "Filter" in content
|
|
377
|
+
has_security = "WebSecurityConfigurerAdapter" in content or "SecurityFilterChain" in content or "EnableWebSecurity" in content
|
|
378
|
+
has_once_filter = "OncePerRequestFilter" in content or "GenericFilterBean" in content
|
|
379
|
+
if (not has_controller and not has_filter and not has_security
|
|
323
380
|
and "ControllerAdvice" not in content
|
|
324
381
|
and "M3FiltroSeguridad" not in content):
|
|
325
382
|
return []
|
|
@@ -335,8 +392,8 @@ class JavaDetector(AbstractDetector):
|
|
|
335
392
|
security_evidence = None
|
|
336
393
|
m3_match = _M3_FILTRO_PARAMS_RE.search(content)
|
|
337
394
|
if m3_match:
|
|
338
|
-
nombre = m3_match.group(1) or ""
|
|
339
|
-
nivel = m3_match.group(
|
|
395
|
+
nombre = m3_match.group(1) or m3_match.group(2) or ""
|
|
396
|
+
nivel = m3_match.group(3) or ""
|
|
340
397
|
security_evidence = f"@M3FiltroSeguridad(nombreRecurso={nombre!r}, nivelRequerido={nivel})"
|
|
341
398
|
return [EntryPoint(
|
|
342
399
|
path=rel_path, stack="java", kind="rest_controller",
|
|
@@ -360,8 +417,8 @@ class JavaDetector(AbstractDetector):
|
|
|
360
417
|
security_evidence = None
|
|
361
418
|
m3_match = _M3_FILTRO_PARAMS_RE.search(content)
|
|
362
419
|
if m3_match:
|
|
363
|
-
nombre = m3_match.group(1) or ""
|
|
364
|
-
nivel = m3_match.group(
|
|
420
|
+
nombre = m3_match.group(1) or m3_match.group(2) or ""
|
|
421
|
+
nivel = m3_match.group(3) or ""
|
|
365
422
|
security_evidence = f"@M3FiltroSeguridad(nombreRecurso={nombre!r}, nivelRequerido={nivel})"
|
|
366
423
|
return [EntryPoint(
|
|
367
424
|
path=rel_path, stack="java", kind="mvc_controller",
|
|
@@ -379,6 +436,23 @@ class JavaDetector(AbstractDetector):
|
|
|
379
436
|
path=rel_path, stack="java", kind="filter",
|
|
380
437
|
source="annotation", confidence="medium",
|
|
381
438
|
)]
|
|
439
|
+
if has_security and (
|
|
440
|
+
_WEB_SECURITY_CONFIGURER_RE.search(content)
|
|
441
|
+
or _SECURITY_CONFIG_ANNOTATION_RE.search(content)
|
|
442
|
+
or _SECURITY_FILTER_CHAIN_RE.search(content)
|
|
443
|
+
):
|
|
444
|
+
return [EntryPoint(
|
|
445
|
+
path=rel_path, stack="java", kind="security_config",
|
|
446
|
+
source="annotation", confidence="high",
|
|
447
|
+
evidence="Spring Security configuration",
|
|
448
|
+
)]
|
|
449
|
+
if has_once_filter and _ONCE_PER_REQUEST_FILTER_RE.search(content):
|
|
450
|
+
is_jwt = bool(_JWT_FILTER_KEYWORDS_RE.search(content))
|
|
451
|
+
return [EntryPoint(
|
|
452
|
+
path=rel_path, stack="java", kind="security_filter",
|
|
453
|
+
source="annotation", confidence="high",
|
|
454
|
+
evidence="jwt_filter" if is_jwt else "request_filter",
|
|
455
|
+
)]
|
|
382
456
|
return []
|
|
383
457
|
|
|
384
458
|
def _parse_web_xml(self, abs_path: Path, rel_path: str) -> list[EntryPoint]:
|
|
@@ -412,3 +486,26 @@ class JavaDetector(AbstractDetector):
|
|
|
412
486
|
seen.add(framework.name)
|
|
413
487
|
result.append(framework)
|
|
414
488
|
return result
|
|
489
|
+
|
|
490
|
+
def _collect_transactional_classes(self, context: DetectionContext, all_paths: list[str]) -> list[str]:
|
|
491
|
+
"""Scan Java source files for @Transactional and return unique class names."""
|
|
492
|
+
classes: list[str] = []
|
|
493
|
+
seen: set[str] = set()
|
|
494
|
+
java_paths = [p for p in all_paths if p.endswith(".java") and "/test/" not in p and "/tests/" not in p]
|
|
495
|
+
for rel_path in java_paths[:_MAX_JAVA_ENTRY_SCAN]:
|
|
496
|
+
abs_path = context.root / rel_path
|
|
497
|
+
try:
|
|
498
|
+
if abs_path.stat().st_size > _MAX_FILE_SIZE:
|
|
499
|
+
continue
|
|
500
|
+
content = abs_path.read_text(encoding="utf-8", errors="replace")
|
|
501
|
+
except OSError:
|
|
502
|
+
continue
|
|
503
|
+
if not _TRANSACTIONAL_RE.search(content):
|
|
504
|
+
continue
|
|
505
|
+
m = _CLASS_NAME_RE.search(content)
|
|
506
|
+
if m:
|
|
507
|
+
cls = m.group(1)
|
|
508
|
+
if cls not in seen:
|
|
509
|
+
seen.add(cls)
|
|
510
|
+
classes.append(cls)
|
|
511
|
+
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(
|