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.
Files changed (186) hide show
  1. {sourcecode-1.11.0 → sourcecode-1.13.0}/PKG-INFO +38 -64
  2. sourcecode-1.13.0/README.md +114 -0
  3. {sourcecode-1.11.0 → sourcecode-1.13.0}/pyproject.toml +1 -1
  4. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/architecture_summary.py +3 -3
  6. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/classifier.py +12 -3
  7. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/cli.py +30 -32
  8. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/java.py +106 -9
  9. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/project.py +3 -0
  10. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/prepare_context.py +17 -3
  11. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/schema.py +1 -0
  12. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/serializer.py +55 -10
  13. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/summarizer.py +7 -1
  14. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_cli.py +1 -1
  15. sourcecode-1.13.0/tests/test_surface_honesty.py +339 -0
  16. sourcecode-1.11.0/README.md +0 -140
  17. {sourcecode-1.11.0 → sourcecode-1.13.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  18. {sourcecode-1.11.0 → sourcecode-1.13.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  19. {sourcecode-1.11.0 → sourcecode-1.13.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  20. {sourcecode-1.11.0 → sourcecode-1.13.0}/.continue-here.md +0 -0
  21. {sourcecode-1.11.0 → sourcecode-1.13.0}/.github/workflows/build-windows.yml +0 -0
  22. {sourcecode-1.11.0 → sourcecode-1.13.0}/.gitignore +0 -0
  23. {sourcecode-1.11.0 → sourcecode-1.13.0}/.ruff.toml +0 -0
  24. {sourcecode-1.11.0 → sourcecode-1.13.0}/CONTRIBUTING.md +0 -0
  25. {sourcecode-1.11.0 → sourcecode-1.13.0}/LICENSE +0 -0
  26. {sourcecode-1.11.0 → sourcecode-1.13.0}/SECURITY.md +0 -0
  27. {sourcecode-1.11.0 → sourcecode-1.13.0}/docs/privacy.md +0 -0
  28. {sourcecode-1.11.0 → sourcecode-1.13.0}/docs/schema.md +0 -0
  29. {sourcecode-1.11.0 → sourcecode-1.13.0}/raw +0 -0
  30. {sourcecode-1.11.0 → sourcecode-1.13.0}/run_cli.py +0 -0
  31. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/adaptive_scanner.py +0 -0
  32. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/architecture_analyzer.py +0 -0
  33. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/ast_extractor.py +0 -0
  34. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  35. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/confidence_analyzer.py +0 -0
  36. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/context_scorer.py +0 -0
  37. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/context_summarizer.py +0 -0
  38. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/contract_model.py +0 -0
  39. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/contract_pipeline.py +0 -0
  40. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/coverage_parser.py +0 -0
  41. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/dependency_analyzer.py +0 -0
  42. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/__init__.py +0 -0
  43. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/base.py +0 -0
  44. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  45. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/dart.py +0 -0
  46. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/dotnet.py +0 -0
  47. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/elixir.py +0 -0
  48. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/go.py +0 -0
  49. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/heuristic.py +0 -0
  50. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/hybrid.py +0 -0
  51. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  52. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/nodejs.py +0 -0
  53. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/parsers.py +0 -0
  54. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/php.py +0 -0
  55. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/python.py +0 -0
  56. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/ruby.py +0 -0
  57. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/rust.py +0 -0
  58. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/systems.py +0 -0
  59. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/terraform.py +0 -0
  60. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/detectors/tooling.py +0 -0
  61. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/doc_analyzer.py +0 -0
  62. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  63. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/env_analyzer.py +0 -0
  64. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/file_classifier.py +0 -0
  65. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/git_analyzer.py +0 -0
  66. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/graph_analyzer.py +0 -0
  67. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/metrics_analyzer.py +0 -0
  68. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/progress.py +0 -0
  69. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/ranking_engine.py +0 -0
  70. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/redactor.py +0 -0
  71. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/relevance_scorer.py +0 -0
  72. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/repo_classifier.py +0 -0
  73. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/runtime_classifier.py +0 -0
  74. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/scanner.py +0 -0
  75. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/semantic_analyzer.py +0 -0
  76. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/__init__.py +0 -0
  77. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/config.py +0 -0
  78. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/consent.py +0 -0
  79. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/events.py +0 -0
  80. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/filters.py +0 -0
  81. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/telemetry/transport.py +0 -0
  82. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/tree_utils.py +0 -0
  83. {sourcecode-1.11.0 → sourcecode-1.13.0}/src/sourcecode/workspace.py +0 -0
  84. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/__init__.py +0 -0
  85. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/conftest.py +0 -0
  86. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/coverage.xml +0 -0
  87. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  88. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
  89. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  90. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/go_service/go.mod +0 -0
  91. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/jacoco.xml +0 -0
  92. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/lcov.info +0 -0
  93. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  94. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/nextjs_app/package.json +0 -0
  95. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  96. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  97. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  98. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  99. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  100. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  101. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  102. {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
  103. {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
  104. {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
  105. {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
  106. {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
  107. {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
  108. {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
  109. {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
  110. {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
  111. {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
  112. {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
  113. {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
  114. {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
  115. {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
  116. {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
  117. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  118. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  119. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  120. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  121. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  122. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  123. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  124. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  125. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  126. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  127. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  128. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_architecture_analyzer.py +0 -0
  129. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_architecture_summary.py +0 -0
  130. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_ast_extractor.py +0 -0
  131. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_block1_reliability.py +0 -0
  132. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_block2_coverage.py +0 -0
  133. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_block5_quality.py +0 -0
  134. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_bug_fixes_v16.py +0 -0
  135. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_classifier.py +0 -0
  136. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_code_notes_analyzer.py +0 -0
  137. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_context_scorer.py +0 -0
  138. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_contract_pipeline.py +0 -0
  139. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_coverage_parser.py +0 -0
  140. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_cross_consistency.py +0 -0
  141. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_dependency_analyzer_node_python.py +0 -0
  142. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
  143. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_dependency_schema.py +0 -0
  144. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_dotnet.py +0 -0
  145. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_go_rust_java.py +0 -0
  146. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_nodejs.py +0 -0
  147. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_php_ruby_dart.py +0 -0
  148. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_python.py +0 -0
  149. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_universal_managed.py +0 -0
  150. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detector_universal_systems.py +0 -0
  151. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_detectors_base.py +0 -0
  152. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_doc_analyzer_jsdom.py +0 -0
  153. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_doc_analyzer_python.py +0 -0
  154. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_graph_analyzer_polyglot.py +0 -0
  155. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_graph_analyzer_python_node.py +0 -0
  156. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_graph_schema.py +0 -0
  157. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_hybrid_inference.py +0 -0
  158. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration.py +0 -0
  159. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_dependencies.py +0 -0
  160. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_detection.py +0 -0
  161. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_docs.py +0 -0
  162. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_graph_modules.py +0 -0
  163. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_lqn.py +0 -0
  164. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_metrics.py +0 -0
  165. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_multistack.py +0 -0
  166. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_semantics.py +0 -0
  167. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_integration_universal.py +0 -0
  168. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_java_spring_integration.py +0 -0
  169. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_metrics_analyzer.py +0 -0
  170. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_packaging.py +0 -0
  171. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_phase1_improvements.py +0 -0
  172. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_pipeline_integrity.py +0 -0
  173. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_real_projects.py +0 -0
  174. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_redactor.py +0 -0
  175. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_scanner.py +0 -0
  176. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_schema.py +0 -0
  177. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_schema_normalization.py +0 -0
  178. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_analyzer_node.py +0 -0
  179. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_analyzer_python.py +0 -0
  180. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_import_resolution.py +0 -0
  181. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_semantic_schema.py +0 -0
  182. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_signal_hierarchy.py +0 -0
  183. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_summarizer.py +0 -0
  184. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_telemetry.py +0 -0
  185. {sourcecode-1.11.0 → sourcecode-1.13.0}/tests/test_v1_10_regressions.py +0 -0
  186. {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.11.0
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 most useful command for integrating `sourcecode` into an AI agent:
257
+ The primary command a high-signal, low-noise summary for Java/Spring codebases:
258
258
 
259
259
  ```bash
260
- sourcecode --agent
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
- It produces a structured JSON with the essential sections (no noise, no file tree), ready to paste into an LLM context:
270
+ Example output for a Spring Boot project:
264
271
 
265
272
  ```json
266
273
  {
267
- "project": {
268
- "type": "fullstack",
269
- "summary": "Full-stack project in Nodejs, mvc, 4075 source files. Domains: atlas-client, atlas-server, atlas-hub, atlas-reports. 3300 dependencies (java, nodejs).",
270
- "primary_stack": "nodejs",
271
- "secondary_stacks": ["java"]
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
- "entry_points": [
274
- {
275
- "path": "atlas-server/src/main/java/com/example/atlas/AtlasServerApplication.java",
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
- "runtime_packages": [ ... ],
288
- "dependencies": { ... },
289
- "env_map": { ... },
290
- "code_notes": [ ... ]
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 large repositories where context matters, use `--compact` to reduce to ~600-800 tokens:
292
+ For full structured output with per-file contracts and signals, use `--agent`:
295
293
 
296
294
  ```bash
297
- sourcecode --compact --copy
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 | Status |
308
- |------|-------|------|---------|-------------|--------|
309
- | `--format` | `-f` | `json\|yaml` | `json` | Output format. YAML is more readable, JSON preferred in pipelines. | ✅ CORE |
310
- | `--output` | `-o` | `PATH` | stdout | Writes output to a file instead of stdout. | CORE |
311
- | `--compact` | | flag | off | ~600-800 token output: stacks, entry points, deps, gaps. No file tree. | ✅ CORE |
312
- | `--agent` | | flag | off | JSON optimized for agents. Automatically enables `--dependencies`, `--env-map`, `--code-notes`. | CORE |
313
- | `--dependencies` | | flag | off | Analyzes direct and transitive deps from manifests and lockfiles. | ✅ CORE |
314
- | `--git-context` | `-g` | flag | off | Includes recent commits, change hotspots, uncommitted changes, contributors. | ✅ CORE |
315
- | `--git-depth` | | `INT [1–100]` | `20` | Number of recent commits with `--git-context`. | CORE |
316
- | `--git-days` | | `INT [1–3650]` | `90` | Window in days to detect hotspots with `--git-context`. | ✅ CORE |
317
- | `--env-map` | | flag | off | Maps environment variables: key, type, category, files that reference them. | ✅ CORE |
318
- | `--code-notes` | | flag | off | Extracts inline annotations: TODO, FIXME, HACK, BUG, DEPRECATED, NOTE, etc. | ✅ CORE |
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
+ ![Version](https://img.shields.io/badge/version-1.0.0-blue)
6
+ ![Status](https://img.shields.io/badge/status-MVP-orange)
7
+ ![Python](https://img.shields.io/badge/python-3.10%2B-green)
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 |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.11.0"
7
+ version = "1.13.0"
8
8
  description = "Deterministic codebase context for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.11.0"
3
+ __version__ = "1.13.0"
@@ -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
- ][:3]
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 = [f.name for stack in stacks for f in stack.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
- if project_type in {"webapp", "fullstack"} and stack.stack in {"nodejs", "elixir"} or project_type == "api" and stack.stack in _API_STACKS or project_type == "cli" and any(
154
- framework.name in _CLI_FRAMEWORKS for framework in stack.frameworks
155
- ) or project_type == "cli" and stack.stack in {"cpp", "dotnet"}:
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
- help="Extract context for a specific symbol: defining file + all importers.",
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 analyze current directory
590
- sourcecode /path/to/repo analyze specific path
591
- sourcecode --agent agent-optimized output
592
- sourcecode --agent --git-context include git activity signals
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": [asdict(f) for f in output.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*(?:nombreRecurso\s*=\s*"([^"]*)")?'
41
- r'(?:[^)]*nivelRequerido\s*=\s*(\d+))?'
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
- content = "\n".join(read_text_lines(path)).lower()
200
- return self._detect_jvm_frameworks(content, "build.gradle")
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
- if ("Controller" not in content and "Filter" not in content
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(2) or ""
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(2) or ""
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(