sourcecode 1.11.0__tar.gz → 1.12.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. {sourcecode-1.11.0 → sourcecode-1.12.0}/PKG-INFO +38 -64
  2. sourcecode-1.12.0/README.md +114 -0
  3. {sourcecode-1.11.0 → sourcecode-1.12.0}/pyproject.toml +1 -1
  4. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/cli.py +24 -7
  6. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/java.py +99 -3
  7. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/project.py +3 -0
  8. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/schema.py +1 -0
  9. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/serializer.py +43 -7
  10. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/summarizer.py +7 -1
  11. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_cli.py +1 -1
  12. sourcecode-1.12.0/tests/test_surface_honesty.py +339 -0
  13. sourcecode-1.11.0/README.md +0 -140
  14. {sourcecode-1.11.0 → sourcecode-1.12.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  15. {sourcecode-1.11.0 → sourcecode-1.12.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  16. {sourcecode-1.11.0 → sourcecode-1.12.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  17. {sourcecode-1.11.0 → sourcecode-1.12.0}/.continue-here.md +0 -0
  18. {sourcecode-1.11.0 → sourcecode-1.12.0}/.github/workflows/build-windows.yml +0 -0
  19. {sourcecode-1.11.0 → sourcecode-1.12.0}/.gitignore +0 -0
  20. {sourcecode-1.11.0 → sourcecode-1.12.0}/.ruff.toml +0 -0
  21. {sourcecode-1.11.0 → sourcecode-1.12.0}/CONTRIBUTING.md +0 -0
  22. {sourcecode-1.11.0 → sourcecode-1.12.0}/LICENSE +0 -0
  23. {sourcecode-1.11.0 → sourcecode-1.12.0}/SECURITY.md +0 -0
  24. {sourcecode-1.11.0 → sourcecode-1.12.0}/docs/privacy.md +0 -0
  25. {sourcecode-1.11.0 → sourcecode-1.12.0}/docs/schema.md +0 -0
  26. {sourcecode-1.11.0 → sourcecode-1.12.0}/raw +0 -0
  27. {sourcecode-1.11.0 → sourcecode-1.12.0}/run_cli.py +0 -0
  28. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/adaptive_scanner.py +0 -0
  29. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/architecture_analyzer.py +0 -0
  30. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/architecture_summary.py +0 -0
  31. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/ast_extractor.py +0 -0
  32. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/classifier.py +0 -0
  33. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  34. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/confidence_analyzer.py +0 -0
  35. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/context_scorer.py +0 -0
  36. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/context_summarizer.py +0 -0
  37. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/contract_model.py +0 -0
  38. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/contract_pipeline.py +0 -0
  39. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/coverage_parser.py +0 -0
  40. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/dependency_analyzer.py +0 -0
  41. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/__init__.py +0 -0
  42. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/base.py +0 -0
  43. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  44. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/dart.py +0 -0
  45. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/dotnet.py +0 -0
  46. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/elixir.py +0 -0
  47. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/go.py +0 -0
  48. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/heuristic.py +0 -0
  49. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/hybrid.py +0 -0
  50. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  51. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/nodejs.py +0 -0
  52. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/parsers.py +0 -0
  53. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/php.py +0 -0
  54. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/python.py +0 -0
  55. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/ruby.py +0 -0
  56. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/rust.py +0 -0
  57. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/systems.py +0 -0
  58. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/terraform.py +0 -0
  59. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/detectors/tooling.py +0 -0
  60. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/doc_analyzer.py +0 -0
  61. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  62. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/env_analyzer.py +0 -0
  63. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/file_classifier.py +0 -0
  64. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/git_analyzer.py +0 -0
  65. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/graph_analyzer.py +0 -0
  66. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/metrics_analyzer.py +0 -0
  67. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/prepare_context.py +0 -0
  68. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/progress.py +0 -0
  69. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/ranking_engine.py +0 -0
  70. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/redactor.py +0 -0
  71. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/relevance_scorer.py +0 -0
  72. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/repo_classifier.py +0 -0
  73. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/runtime_classifier.py +0 -0
  74. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/scanner.py +0 -0
  75. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/semantic_analyzer.py +0 -0
  76. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/__init__.py +0 -0
  77. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/config.py +0 -0
  78. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/consent.py +0 -0
  79. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/events.py +0 -0
  80. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/filters.py +0 -0
  81. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/telemetry/transport.py +0 -0
  82. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/tree_utils.py +0 -0
  83. {sourcecode-1.11.0 → sourcecode-1.12.0}/src/sourcecode/workspace.py +0 -0
  84. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/__init__.py +0 -0
  85. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/conftest.py +0 -0
  86. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/coverage.xml +0 -0
  87. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  88. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
  89. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  90. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/go_service/go.mod +0 -0
  91. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/jacoco.xml +0 -0
  92. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/lcov.info +0 -0
  93. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  94. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/nextjs_app/package.json +0 -0
  95. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  96. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  97. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  98. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  99. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  100. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  101. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  102. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  103. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  104. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  105. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  106. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  107. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  108. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  109. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  110. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  111. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  112. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  113. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  114. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  115. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  116. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  117. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  118. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  119. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  120. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  121. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  122. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  123. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  124. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  125. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  126. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  127. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  128. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_architecture_analyzer.py +0 -0
  129. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_architecture_summary.py +0 -0
  130. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_ast_extractor.py +0 -0
  131. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_block1_reliability.py +0 -0
  132. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_block2_coverage.py +0 -0
  133. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_block5_quality.py +0 -0
  134. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_bug_fixes_v16.py +0 -0
  135. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_classifier.py +0 -0
  136. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_code_notes_analyzer.py +0 -0
  137. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_context_scorer.py +0 -0
  138. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_contract_pipeline.py +0 -0
  139. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_coverage_parser.py +0 -0
  140. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_cross_consistency.py +0 -0
  141. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_dependency_analyzer_node_python.py +0 -0
  142. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
  143. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_dependency_schema.py +0 -0
  144. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_dotnet.py +0 -0
  145. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_go_rust_java.py +0 -0
  146. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_nodejs.py +0 -0
  147. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_php_ruby_dart.py +0 -0
  148. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_python.py +0 -0
  149. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_universal_managed.py +0 -0
  150. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detector_universal_systems.py +0 -0
  151. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_detectors_base.py +0 -0
  152. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_doc_analyzer_jsdom.py +0 -0
  153. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_doc_analyzer_python.py +0 -0
  154. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_graph_analyzer_polyglot.py +0 -0
  155. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_graph_analyzer_python_node.py +0 -0
  156. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_graph_schema.py +0 -0
  157. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_hybrid_inference.py +0 -0
  158. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration.py +0 -0
  159. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_dependencies.py +0 -0
  160. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_detection.py +0 -0
  161. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_docs.py +0 -0
  162. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_graph_modules.py +0 -0
  163. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_lqn.py +0 -0
  164. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_metrics.py +0 -0
  165. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_multistack.py +0 -0
  166. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_semantics.py +0 -0
  167. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_integration_universal.py +0 -0
  168. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_java_spring_integration.py +0 -0
  169. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_metrics_analyzer.py +0 -0
  170. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_packaging.py +0 -0
  171. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_phase1_improvements.py +0 -0
  172. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_pipeline_integrity.py +0 -0
  173. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_real_projects.py +0 -0
  174. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_redactor.py +0 -0
  175. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_scanner.py +0 -0
  176. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_schema.py +0 -0
  177. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_schema_normalization.py +0 -0
  178. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_analyzer_node.py +0 -0
  179. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_analyzer_python.py +0 -0
  180. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_import_resolution.py +0 -0
  181. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_semantic_schema.py +0 -0
  182. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_signal_hierarchy.py +0 -0
  183. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_summarizer.py +0 -0
  184. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_telemetry.py +0 -0
  185. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_v1_10_regressions.py +0 -0
  186. {sourcecode-1.11.0 → sourcecode-1.12.0}/tests/test_workspace_analyzer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.11.0
3
+ Version: 1.12.0
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -254,48 +254,45 @@ sourcecode version
254
254
 
255
255
  ## Quickstart
256
256
 
257
- The 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.12.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.12.0"
@@ -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"):
@@ -764,6 +769,16 @@ def main(
764
769
  _java_min_depth = 12
765
770
  effective_depth = max(depth, _java_min_depth) if _is_java and depth < _java_min_depth else depth
766
771
 
772
+ if symbol is not None and _is_java:
773
+ typer.echo(
774
+ f"Error: --symbol is not supported for Java/JVM repositories. "
775
+ "Per-file AST extraction is unavailable for JVM — symbol search only works with Python, TypeScript, and JavaScript. "
776
+ "Alternatives: use --agent --compact to get file relevance scores, "
777
+ "or use --git-context to find recently changed files.",
778
+ err=True,
779
+ )
780
+ raise typer.Exit(code=1)
781
+
767
782
  # --agent: enable signal analyzers; output via agent_view (not compact)
768
783
  if agent:
769
784
  dependencies = True
@@ -1489,7 +1504,9 @@ def main(
1489
1504
  if _is_contract_mode and sm.file_contracts:
1490
1505
  from sourcecode.serializer import _serialize_contract_minimal
1491
1506
  data.pop("file_relevance", None)
1492
- _MAX_AGENT_CONTRACTS = 10
1507
+ _jvm_stacks_agent = {"java", "kotlin", "scala", "groovy"}
1508
+ _is_jvm_agent = any(s.stack in _jvm_stacks_agent for s in sm.stacks)
1509
+ _MAX_AGENT_CONTRACTS = 50 if _is_jvm_agent else 10
1493
1510
  _all_contracts = sm.file_contracts
1494
1511
  _sorted = sorted(_all_contracts, key=lambda c: getattr(c, "relevance_score", 0.0), reverse=True)
1495
1512
  _sampled = _sorted[:_MAX_AGENT_CONTRACTS]
@@ -41,6 +41,34 @@ _M3_FILTRO_PARAMS_RE = re.compile(
41
41
  r'(?:[^)]*nivelRequerido\s*=\s*(\d+))?'
42
42
  )
43
43
 
44
+ # Security config detection
45
+ _WEB_SECURITY_CONFIGURER_RE = re.compile(r'WebSecurityConfigurerAdapter\b')
46
+ _SECURITY_FILTER_CHAIN_RE = re.compile(r'SecurityFilterChain\b')
47
+ _SECURITY_CONFIG_ANNOTATION_RE = re.compile(r'@EnableWebSecurity\b')
48
+ # JWT/token filter detection (files that process every request)
49
+ _ONCE_PER_REQUEST_FILTER_RE = re.compile(r'extends\s+(?:OncePerRequestFilter|GenericFilterBean)\b')
50
+ _JWT_FILTER_KEYWORDS_RE = re.compile(r'\b(?:jwt|token|bearer|authorization)\b', re.IGNORECASE)
51
+
52
+ # @Transactional detection
53
+ _TRANSACTIONAL_RE = re.compile(r'@Transactional\b')
54
+ # Extracts class name: `public class Foo` or `class Foo`
55
+ _CLASS_NAME_RE = re.compile(r'\bclass\s+([A-Z][A-Za-z0-9_]*)')
56
+
57
+ # Gradle plugin Spring Boot version: id 'org.springframework.boot' version '2.6.3'
58
+ _GRADLE_SB_PLUGIN_RE = re.compile(
59
+ r"""id\s*['"]\s*org\.springframework\.boot\s*['"]\s+version\s*['"]([\d.]+)['"']""",
60
+ re.IGNORECASE,
61
+ )
62
+ # Gradle Java version: sourceCompatibility = '11' or sourceCompatibility = JavaVersion.VERSION_11
63
+ _GRADLE_JAVA_VERSION_RE = re.compile(
64
+ r"""(?:sourceCompatibility|targetCompatibility|javaVersion)\s*=\s*['"]?([0-9.]+)['"]?""",
65
+ re.IGNORECASE,
66
+ )
67
+ # JavaVersion.VERSION_11 form
68
+ _GRADLE_JAVA_ENUM_RE = re.compile(
69
+ r"""(?:sourceCompatibility|targetCompatibility)\s*=\s*JavaVersion\.VERSION_(\d+)"""
70
+ )
71
+
44
72
 
45
73
  class JavaDetector(AbstractDetector):
46
74
  name = "java"
@@ -69,6 +97,12 @@ class JavaDetector(AbstractDetector):
69
97
  if "build.gradle" in context.manifests:
70
98
  manifests.append("build.gradle")
71
99
  frameworks.extend(self._frameworks_from_gradle(context.root / "build.gradle"))
100
+ if language_version is None:
101
+ try:
102
+ gradle_content = "\n".join(read_text_lines(context.root / "build.gradle"))
103
+ language_version = self._extract_gradle_java_version(gradle_content)
104
+ except OSError:
105
+ pass
72
106
 
73
107
  # Detect app server from descriptor files
74
108
  all_paths = flatten_file_tree(context.file_tree)
@@ -81,6 +115,7 @@ class JavaDetector(AbstractDetector):
81
115
  spring_profiles = self._detect_spring_profiles(context.root, all_paths)
82
116
 
83
117
  entry_points = self._collect_entry_points(context)
118
+ transactional_classes = self._collect_transactional_classes(context, all_paths)
84
119
  stack = StackDetection(
85
120
  stack="java",
86
121
  detection_method="manifest",
@@ -91,6 +126,7 @@ class JavaDetector(AbstractDetector):
91
126
  packaging=packaging,
92
127
  app_server_hint=app_server_hint,
93
128
  spring_profiles=spring_profiles,
129
+ transactional_classes=transactional_classes,
94
130
  )
95
131
  return [stack], entry_points
96
132
 
@@ -196,8 +232,24 @@ class JavaDetector(AbstractDetector):
196
232
  return frameworks
197
233
 
198
234
  def _frameworks_from_gradle(self, path: Path) -> list[FrameworkDetection]:
199
- content = "\n".join(read_text_lines(path)).lower()
200
- return self._detect_jvm_frameworks(content, "build.gradle")
235
+ original = "\n".join(read_text_lines(path))
236
+ content = original.lower()
237
+ sb_version = self._extract_gradle_sb_version(original)
238
+ return self._detect_jvm_frameworks(content, "build.gradle", sb_version=sb_version)
239
+
240
+ def _extract_gradle_sb_version(self, content: str) -> str | None:
241
+ m = _GRADLE_SB_PLUGIN_RE.search(content)
242
+ return m.group(1) if m else None
243
+
244
+ def _extract_gradle_java_version(self, content: str) -> str | None:
245
+ m = _GRADLE_JAVA_ENUM_RE.search(content)
246
+ if m:
247
+ v = m.group(1)
248
+ return "1." + v if int(v) <= 8 else v
249
+ m = _GRADLE_JAVA_VERSION_RE.search(content)
250
+ if m:
251
+ return m.group(1)
252
+ return None
201
253
 
202
254
  def _detect_jvm_frameworks(self, text: str, source: str, *, sb_version: str | None = None) -> list[FrameworkDetection]:
203
255
  frameworks: list[FrameworkDetection] = []
@@ -319,7 +371,11 @@ class JavaDetector(AbstractDetector):
319
371
  return []
320
372
 
321
373
  # Quick pre-filter before running regexes
322
- if ("Controller" not in content and "Filter" not in content
374
+ has_controller = "Controller" in content
375
+ has_filter = "Filter" in content
376
+ has_security = "WebSecurityConfigurerAdapter" in content or "SecurityFilterChain" in content or "EnableWebSecurity" in content
377
+ has_once_filter = "OncePerRequestFilter" in content or "GenericFilterBean" in content
378
+ if (not has_controller and not has_filter and not has_security
323
379
  and "ControllerAdvice" not in content
324
380
  and "M3FiltroSeguridad" not in content):
325
381
  return []
@@ -379,6 +435,23 @@ class JavaDetector(AbstractDetector):
379
435
  path=rel_path, stack="java", kind="filter",
380
436
  source="annotation", confidence="medium",
381
437
  )]
438
+ if has_security and (
439
+ _WEB_SECURITY_CONFIGURER_RE.search(content)
440
+ or _SECURITY_CONFIG_ANNOTATION_RE.search(content)
441
+ or _SECURITY_FILTER_CHAIN_RE.search(content)
442
+ ):
443
+ return [EntryPoint(
444
+ path=rel_path, stack="java", kind="security_config",
445
+ source="annotation", confidence="high",
446
+ evidence="Spring Security configuration",
447
+ )]
448
+ if has_once_filter and _ONCE_PER_REQUEST_FILTER_RE.search(content):
449
+ is_jwt = bool(_JWT_FILTER_KEYWORDS_RE.search(content))
450
+ return [EntryPoint(
451
+ path=rel_path, stack="java", kind="security_filter",
452
+ source="annotation", confidence="high",
453
+ evidence="jwt_filter" if is_jwt else "request_filter",
454
+ )]
382
455
  return []
383
456
 
384
457
  def _parse_web_xml(self, abs_path: Path, rel_path: str) -> list[EntryPoint]:
@@ -412,3 +485,26 @@ class JavaDetector(AbstractDetector):
412
485
  seen.add(framework.name)
413
486
  result.append(framework)
414
487
  return result
488
+
489
+ def _collect_transactional_classes(self, context: DetectionContext, all_paths: list[str]) -> list[str]:
490
+ """Scan Java source files for @Transactional and return unique class names."""
491
+ classes: list[str] = []
492
+ seen: set[str] = set()
493
+ java_paths = [p for p in all_paths if p.endswith(".java") and "/test/" not in p and "/tests/" not in p]
494
+ for rel_path in java_paths[:_MAX_JAVA_ENTRY_SCAN]:
495
+ abs_path = context.root / rel_path
496
+ try:
497
+ if abs_path.stat().st_size > _MAX_FILE_SIZE:
498
+ continue
499
+ content = abs_path.read_text(encoding="utf-8", errors="replace")
500
+ except OSError:
501
+ continue
502
+ if not _TRANSACTIONAL_RE.search(content):
503
+ continue
504
+ m = _CLASS_NAME_RE.search(content)
505
+ if m:
506
+ cls = m.group(1)
507
+ if cls not in seen:
508
+ seen.add(cls)
509
+ classes.append(cls)
510
+ return classes
@@ -121,6 +121,7 @@ class ProjectDetector:
121
121
  packaging=stack.packaging,
122
122
  app_server_hint=stack.app_server_hint,
123
123
  spring_profiles=list(stack.spring_profiles),
124
+ transactional_classes=list(stack.transactional_classes),
124
125
  )
125
126
 
126
127
  def _merge_stack(self, current: StackDetection, incoming: StackDetection) -> StackDetection:
@@ -144,6 +145,8 @@ class ProjectDetector:
144
145
  current.app_server_hint = incoming.app_server_hint
145
146
  if incoming.spring_profiles and not current.spring_profiles:
146
147
  current.spring_profiles = list(incoming.spring_profiles)
148
+ if incoming.transactional_classes and not current.transactional_classes:
149
+ current.transactional_classes = list(incoming.transactional_classes)
147
150
  return current
148
151
 
149
152
  def _merge_frameworks(
@@ -68,6 +68,7 @@ class StackDetection:
68
68
  packaging: Optional[str] = None # e.g. "war" | "jar"
69
69
  app_server_hint: Optional[str] = None # e.g. "weblogic" | "wildfly"
70
70
  spring_profiles: list[str] = field(default_factory=list) # detected Spring profiles
71
+ transactional_classes: list[str] = field(default_factory=list) # classes with @Transactional
71
72
 
72
73
 
73
74
  @dataclass
@@ -282,6 +282,24 @@ def _mybatis_pairing(sm: "SourceMap") -> "Optional[dict[str, Any]]":
282
282
  return result
283
283
 
284
284
 
285
+ def _spring_boot_version(sm: "SourceMap") -> "Optional[str]":
286
+ """Extract Spring Boot version from detected frameworks."""
287
+ for s in sm.stacks:
288
+ for fw in s.frameworks:
289
+ if fw.name == "Spring Boot" and fw.version:
290
+ return fw.version
291
+ return None
292
+
293
+
294
+ def _transactional_summary(sm: "SourceMap") -> "Optional[dict[str, Any]]":
295
+ """Surface @Transactional class boundaries from the Java stack detection."""
296
+ for s in sm.stacks:
297
+ classes = getattr(s, "transactional_classes", [])
298
+ if classes:
299
+ return {"count": len(classes), "classes": classes[:10]}
300
+ return None
301
+
302
+
285
303
  def _security_surface_from_eps(eps: list) -> "Optional[dict[str, Any]]":
286
304
  """Extract @M3FiltroSeguridad resource names from entry point evidence strings."""
287
305
  import re as _re
@@ -715,9 +733,12 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
715
733
  _language_version = getattr(sm, "language_version", None)
716
734
  _packaging = getattr(sm, "packaging", None)
717
735
  _app_server = getattr(sm, "app_server_hint", None)
736
+ _sb_version = _spring_boot_version(sm)
718
737
  _deployment: Any = None
719
- if _packaging or _app_server:
738
+ if _packaging or _app_server or _sb_version:
720
739
  _deployment = {}
740
+ if _sb_version:
741
+ _deployment["spring_boot_version"] = _sb_version
721
742
  if _packaging:
722
743
  _deployment["packaging"] = _packaging
723
744
  if _app_server:
@@ -725,8 +746,15 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
725
746
  _deploy_risks = _project_deployment_risks(sm)
726
747
  _security_surface = _security_surface_from_eps(sm.entry_points)
727
748
  _mybatis = _mybatis_pairing(sm)
749
+ _transactional = _transactional_summary(sm)
728
750
  _git_ctx = _compact_git_context(sm)
729
751
 
752
+ # Suppress empty optional sections (no signal value)
753
+ _effective_env_summary = env_summary_dict if (env_summary_dict and env_summary_dict.get("total", 0) > 0) else None
754
+ _effective_env_map = env_map_items if _effective_env_summary else None
755
+ _effective_notes_summary = code_notes_summary_dict if (code_notes_summary_dict and code_notes_summary_dict.get("total", 0) > 0) else None
756
+ _effective_notes = code_notes_items if _effective_notes_summary else None
757
+
730
758
  result: dict[str, Any] = {
731
759
  "schema_version": sm.metadata.schema_version,
732
760
  "project_type": sm.project_type,
@@ -736,10 +764,10 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
736
764
  "entry_points": entry_points_compact,
737
765
  "dependency_summary": dep_summary_dict,
738
766
  "key_dependencies": key_deps,
739
- "env_summary": env_summary_dict,
740
- "env_map": env_map_items,
741
- "code_notes_summary": code_notes_summary_dict,
742
- "code_notes": code_notes_items,
767
+ "env_summary": _effective_env_summary,
768
+ "env_map": _effective_env_map,
769
+ "code_notes_summary": _effective_notes_summary,
770
+ "code_notes": _effective_notes,
743
771
  "confidence_summary": conf_dict,
744
772
  "analysis_gaps": gaps_list,
745
773
  }
@@ -753,6 +781,8 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
753
781
  result["security_surface"] = _security_surface
754
782
  if _mybatis:
755
783
  result["mybatis"] = _mybatis
784
+ if _transactional:
785
+ result["transactional_boundaries"] = _transactional
756
786
  if _git_ctx:
757
787
  result["git_context"] = _git_ctx
758
788
  _always_include = {"project_type", "project_summary", "architecture_summary", "dependency_summary"}
@@ -1076,10 +1106,13 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
1076
1106
  _lv = getattr(sm, "language_version", None)
1077
1107
  _pkg = getattr(sm, "packaging", None)
1078
1108
  _app_srv = getattr(sm, "app_server_hint", None)
1109
+ _sb_ver = _spring_boot_version(sm)
1079
1110
  if _lv:
1080
1111
  project["language_version"] = _lv
1081
- if _pkg or _app_srv:
1112
+ if _pkg or _app_srv or _sb_ver:
1082
1113
  _depl: dict[str, Any] = {}
1114
+ if _sb_ver:
1115
+ _depl["spring_boot_version"] = _sb_ver
1083
1116
  if _pkg:
1084
1117
  _depl["packaging"] = _pkg
1085
1118
  if _app_srv:
@@ -1215,13 +1248,16 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
1215
1248
  sem_info["hotspots"] = sem.hotspots[:10]
1216
1249
  signals["semantic_graph"] = sem_info
1217
1250
 
1218
- # Java/Spring: security surface and ORM structure
1251
+ # Java/Spring: security surface, ORM structure, transactional boundaries
1219
1252
  _sec_surf = _security_surface_from_eps(sm.entry_points)
1220
1253
  if _sec_surf:
1221
1254
  signals["security_surface"] = _sec_surf
1222
1255
  _mb = _mybatis_pairing(sm)
1223
1256
  if _mb:
1224
1257
  signals["mybatis"] = _mb
1258
+ _txn = _transactional_summary(sm)
1259
+ if _txn:
1260
+ signals["transactional_boundaries"] = _txn
1225
1261
 
1226
1262
  if signals:
1227
1263
  result["signals"] = signals
@@ -188,6 +188,9 @@ class ProjectSummarizer:
188
188
  return None
189
189
 
190
190
  def _extract_first_useful_paragraph(self, content: str) -> str | None:
191
+ import re as _re
192
+ _BADGE_RE = _re.compile(r"^\[?!\[") # [![badge](...)] or ![img](...)
193
+ _LINK_ONLY_RE = _re.compile(r"^\[.*?\]\(.*?\)$") # pure link line
191
194
  lines: list[str] = []
192
195
  in_code_block = False
193
196
  for raw_line in content.splitlines():
@@ -195,10 +198,13 @@ class ProjectSummarizer:
195
198
  if line.startswith("```"):
196
199
  in_code_block = not in_code_block
197
200
  continue
198
- if in_code_block or not line or line.startswith(("#", "<!--")):
201
+ if in_code_block or not line or line.startswith(("#", "<!--", ">")):
199
202
  if lines:
200
203
  break
201
204
  continue
205
+ # Skip badge-only lines and pure-link lines — they are metadata, not descriptions
206
+ if _BADGE_RE.match(line) or (not lines and _LINK_ONLY_RE.match(line)):
207
+ continue
202
208
  lines.append(line)
203
209
  if not lines:
204
210
  return None