sourcecode 1.9.0__tar.gz → 1.11.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 (184) hide show
  1. {sourcecode-1.9.0 → sourcecode-1.11.0}/PKG-INFO +1 -3
  2. {sourcecode-1.9.0 → sourcecode-1.11.0}/README.md +0 -2
  3. {sourcecode-1.9.0 → sourcecode-1.11.0}/pyproject.toml +1 -1
  4. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/cli.py +4 -0
  6. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/prepare_context.py +19 -0
  7. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/serializer.py +323 -40
  8. sourcecode-1.11.0/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +17 -0
  9. sourcecode-1.11.0/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +16 -0
  10. sourcecode-1.11.0/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +8 -0
  11. sourcecode-1.11.0/tests/test_v1_10_regressions.py +560 -0
  12. {sourcecode-1.9.0 → sourcecode-1.11.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  13. {sourcecode-1.9.0 → sourcecode-1.11.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  14. {sourcecode-1.9.0 → sourcecode-1.11.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  15. {sourcecode-1.9.0 → sourcecode-1.11.0}/.continue-here.md +0 -0
  16. {sourcecode-1.9.0 → sourcecode-1.11.0}/.github/workflows/build-windows.yml +0 -0
  17. {sourcecode-1.9.0 → sourcecode-1.11.0}/.gitignore +0 -0
  18. {sourcecode-1.9.0 → sourcecode-1.11.0}/.ruff.toml +0 -0
  19. {sourcecode-1.9.0 → sourcecode-1.11.0}/CONTRIBUTING.md +0 -0
  20. {sourcecode-1.9.0 → sourcecode-1.11.0}/LICENSE +0 -0
  21. {sourcecode-1.9.0 → sourcecode-1.11.0}/SECURITY.md +0 -0
  22. {sourcecode-1.9.0 → sourcecode-1.11.0}/docs/privacy.md +0 -0
  23. {sourcecode-1.9.0 → sourcecode-1.11.0}/docs/schema.md +0 -0
  24. {sourcecode-1.9.0 → sourcecode-1.11.0}/raw +0 -0
  25. {sourcecode-1.9.0 → sourcecode-1.11.0}/run_cli.py +0 -0
  26. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/adaptive_scanner.py +0 -0
  27. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/architecture_analyzer.py +0 -0
  28. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/architecture_summary.py +0 -0
  29. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/ast_extractor.py +0 -0
  30. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/classifier.py +0 -0
  31. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  32. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/confidence_analyzer.py +0 -0
  33. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/context_scorer.py +0 -0
  34. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/context_summarizer.py +0 -0
  35. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/contract_model.py +0 -0
  36. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/contract_pipeline.py +0 -0
  37. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/coverage_parser.py +0 -0
  38. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/dependency_analyzer.py +0 -0
  39. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/__init__.py +0 -0
  40. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/base.py +0 -0
  41. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  42. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/dart.py +0 -0
  43. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/dotnet.py +0 -0
  44. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/elixir.py +0 -0
  45. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/go.py +0 -0
  46. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/heuristic.py +0 -0
  47. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/hybrid.py +0 -0
  48. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/java.py +0 -0
  49. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  50. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/nodejs.py +0 -0
  51. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/parsers.py +0 -0
  52. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/php.py +0 -0
  53. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/project.py +0 -0
  54. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/python.py +0 -0
  55. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/ruby.py +0 -0
  56. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/rust.py +0 -0
  57. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/systems.py +0 -0
  58. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/terraform.py +0 -0
  59. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/detectors/tooling.py +0 -0
  60. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/doc_analyzer.py +0 -0
  61. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  62. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/env_analyzer.py +0 -0
  63. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/file_classifier.py +0 -0
  64. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/git_analyzer.py +0 -0
  65. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/graph_analyzer.py +0 -0
  66. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/metrics_analyzer.py +0 -0
  67. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/progress.py +0 -0
  68. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/ranking_engine.py +0 -0
  69. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/redactor.py +0 -0
  70. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/relevance_scorer.py +0 -0
  71. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/repo_classifier.py +0 -0
  72. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/runtime_classifier.py +0 -0
  73. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/scanner.py +0 -0
  74. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/schema.py +0 -0
  75. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/semantic_analyzer.py +0 -0
  76. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/summarizer.py +0 -0
  77. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/telemetry/__init__.py +0 -0
  78. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/telemetry/config.py +0 -0
  79. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/telemetry/consent.py +0 -0
  80. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/telemetry/events.py +0 -0
  81. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/telemetry/filters.py +0 -0
  82. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/telemetry/transport.py +0 -0
  83. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/tree_utils.py +0 -0
  84. {sourcecode-1.9.0 → sourcecode-1.11.0}/src/sourcecode/workspace.py +0 -0
  85. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/__init__.py +0 -0
  86. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/conftest.py +0 -0
  87. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/coverage.xml +0 -0
  88. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  89. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
  90. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  91. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/go_service/go.mod +0 -0
  92. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/jacoco.xml +0 -0
  93. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/lcov.info +0 -0
  94. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  95. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/nextjs_app/package.json +0 -0
  96. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  97. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  98. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  99. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  100. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  101. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  102. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  103. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  104. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  105. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  106. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  107. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  108. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  109. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  110. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  111. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  112. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  113. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  114. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  115. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  116. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  117. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  118. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  119. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  120. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  121. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  122. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  123. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  124. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  125. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  126. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_architecture_analyzer.py +0 -0
  127. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_architecture_summary.py +0 -0
  128. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_ast_extractor.py +0 -0
  129. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_block1_reliability.py +0 -0
  130. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_block2_coverage.py +0 -0
  131. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_block5_quality.py +0 -0
  132. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_bug_fixes_v16.py +0 -0
  133. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_classifier.py +0 -0
  134. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_cli.py +0 -0
  135. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_code_notes_analyzer.py +0 -0
  136. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_context_scorer.py +0 -0
  137. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_contract_pipeline.py +0 -0
  138. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_coverage_parser.py +0 -0
  139. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_cross_consistency.py +0 -0
  140. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_dependency_analyzer_node_python.py +0 -0
  141. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
  142. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_dependency_schema.py +0 -0
  143. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_dotnet.py +0 -0
  144. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_go_rust_java.py +0 -0
  145. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_nodejs.py +0 -0
  146. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_php_ruby_dart.py +0 -0
  147. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_python.py +0 -0
  148. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_universal_managed.py +0 -0
  149. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detector_universal_systems.py +0 -0
  150. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_detectors_base.py +0 -0
  151. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_doc_analyzer_jsdom.py +0 -0
  152. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_doc_analyzer_python.py +0 -0
  153. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_graph_analyzer_polyglot.py +0 -0
  154. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_graph_analyzer_python_node.py +0 -0
  155. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_graph_schema.py +0 -0
  156. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_hybrid_inference.py +0 -0
  157. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration.py +0 -0
  158. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_dependencies.py +0 -0
  159. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_detection.py +0 -0
  160. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_docs.py +0 -0
  161. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_graph_modules.py +0 -0
  162. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_lqn.py +0 -0
  163. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_metrics.py +0 -0
  164. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_multistack.py +0 -0
  165. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_semantics.py +0 -0
  166. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_integration_universal.py +0 -0
  167. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_java_spring_integration.py +0 -0
  168. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_metrics_analyzer.py +0 -0
  169. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_packaging.py +0 -0
  170. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_phase1_improvements.py +0 -0
  171. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_pipeline_integrity.py +0 -0
  172. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_real_projects.py +0 -0
  173. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_redactor.py +0 -0
  174. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_scanner.py +0 -0
  175. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_schema.py +0 -0
  176. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_schema_normalization.py +0 -0
  177. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_semantic_analyzer_node.py +0 -0
  178. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_semantic_analyzer_python.py +0 -0
  179. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_semantic_import_resolution.py +0 -0
  180. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_semantic_schema.py +0 -0
  181. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_signal_hierarchy.py +0 -0
  182. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_summarizer.py +0 -0
  183. {sourcecode-1.9.0 → sourcecode-1.11.0}/tests/test_telemetry.py +0 -0
  184. {sourcecode-1.9.0 → sourcecode-1.11.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.9.0
3
+ Version: 1.11.0
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -357,5 +357,3 @@ Generates task-specific context for AI agents.
357
357
  | `refactor` | Structural problems, improvement opportunities | 🧪 EXP |
358
358
  | `generate-tests` | Files without tests, coverage gap analysis | 🧪 EXP |
359
359
  | `review-pr` | Changed files + architectural impact | 🧪 EXP |
360
-
361
- ...
@@ -138,5 +138,3 @@ Generates task-specific context for AI agents.
138
138
  | `refactor` | Structural problems, improvement opportunities | 🧪 EXP |
139
139
  | `generate-tests` | Files without tests, coverage gap analysis | 🧪 EXP |
140
140
  | `review-pr` | Changed files + architectural impact | 🧪 EXP |
141
-
142
- ...
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.9.0"
7
+ version = "1.11.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.9.0"
3
+ __version__ = "1.11.0"
@@ -629,6 +629,10 @@ def main(
629
629
  )
630
630
  raise typer.Exit(code=1)
631
631
 
632
+ if symbol is not None and not symbol.strip():
633
+ typer.echo("symbol query cannot be empty", err=True)
634
+ raise typer.Exit(code=2)
635
+
632
636
  if symbol and mode not in ("contract", "standard"):
633
637
  typer.echo(
634
638
  f"Error: --symbol requires --mode contract or standard (got '{mode}'). "
@@ -563,6 +563,25 @@ class TaskContextBuilder:
563
563
  except Exception:
564
564
  pass
565
565
 
566
+ # ── 5c. review-pr suspected_areas (needs git uncommitted_files) ──────
567
+ if task_name == "review-pr" and spec.enable_code_notes:
568
+ pr_areas: dict[str, int] = {}
569
+ for path in uncommitted_files:
570
+ pr_areas[path] = pr_areas.get(path, 0) + 10
571
+ review_kinds = {"FIXME", "TODO", "BUG", "HACK"}
572
+ for note in cn_notes_for_ranking:
573
+ if note.kind in review_kinds:
574
+ pr_areas[note.path] = pr_areas.get(note.path, 0) + 1
575
+ _BOOST_STEMS = ("Controller", "Service", "Repository", "Mapper", "Filter", "Security")
576
+ for path in all_paths:
577
+ stem = Path(path).stem
578
+ if any(k in stem for k in _BOOST_STEMS):
579
+ pr_areas[path] = pr_areas.get(path, 0) + 2
580
+ suspected_areas = [
581
+ p for p, _ in sorted(pr_areas.items(), key=lambda x: -x[1])[:8]
582
+ if not self._is_test(p)
583
+ ]
584
+
566
585
  # ── 6. Rank files ──────────────────────────────────────────────────
567
586
  entry_set = {ep.path for ep in entry_points}
568
587
  test_set = {p for p in all_paths if self._is_test(p)}
@@ -38,6 +38,19 @@ _MONOREPO_PKGS_CAP = 8 # max workspace/runtime packages shown
38
38
  _KEY_DEPS_CAP = 50 # max key dependencies shown
39
39
  _CODE_NOTES_CAP = 15 # max code notes in default output
40
40
  _ENV_MAP_CAP = 15 # max env var entries in default output
41
+ _MAX_DEFAULT_CONTRACTS = 20 # max contracts in default/standard contract output
42
+ _MAX_HARD_SIGNALS_DEFAULT = 20 # max hard_signals entries in default output
43
+
44
+
45
+ def _cap_contracts_for_output(
46
+ contracts: list[Any],
47
+ max_count: int = _MAX_DEFAULT_CONTRACTS,
48
+ ) -> tuple[list[Any], dict[str, Any]]:
49
+ """Sort contracts by relevance_score desc, cap to max_count, return (sampled, meta)."""
50
+ total = len(contracts)
51
+ sampled = sorted(contracts, key=lambda c: getattr(c, "relevance_score", 0.0), reverse=True)[:max_count]
52
+ meta: dict[str, Any] = {"total": total, "shown": len(sampled), "truncated": total > max_count}
53
+ return sampled, meta
41
54
 
42
55
 
43
56
  def to_json(sm: SourceMap | dict[str, Any], indent: int = 2) -> str:
@@ -191,6 +204,178 @@ def _dep_import_key(name: str) -> str:
191
204
  return lowered.split("/")[0].replace("_", "-")
192
205
 
193
206
 
207
+ # ---------------------------------------------------------------------------
208
+ # Java/Spring compact-mode helpers (v1.10.0)
209
+ # ---------------------------------------------------------------------------
210
+
211
+ def _compact_git_context(sm: "SourceMap") -> "Optional[dict[str, Any]]":
212
+ """Lightweight git_context for compact/agent output. Top-5 hotspots only."""
213
+ gc = sm.git_context
214
+ if gc is None or not gc.requested:
215
+ return None
216
+ _bad = {"no_git_repo", "git_not_found", "git_timeout"}
217
+ if _bad & set(gc.limitations):
218
+ return None
219
+ ctx: dict[str, Any] = {}
220
+ if gc.branch:
221
+ ctx["branch"] = gc.branch
222
+ if gc.uncommitted_changes is not None:
223
+ uc = gc.uncommitted_changes
224
+ ctx["uncommitted_files"] = len(uc.staged) + len(uc.unstaged) + len(uc.untracked)
225
+ if gc.change_hotspots:
226
+ ctx["top_hotspots"] = [
227
+ {"file": h.file, "commits": h.commit_count}
228
+ for h in gc.change_hotspots[:5]
229
+ ]
230
+ return ctx if ctx else None
231
+
232
+
233
+ def _dep_risk_flags(name: str, version: "Optional[str]") -> list[str]:
234
+ """Static heuristic risk flags for a single dependency. No external lookups."""
235
+ flags: list[str] = []
236
+ nl = name.lower()
237
+ if "spring-boot" in nl or "spring.boot" in nl:
238
+ if version and version.startswith("2."):
239
+ flags.append("spring-boot-2.x-eol")
240
+ if nl.startswith("javax.") or nl == "javax":
241
+ flags.append("javax-to-jakarta-migration-risk")
242
+ if "ojdbc" in nl or nl in {"com.oracle.database.jdbc", "oracle.jdbc.driver.oracledriver"}:
243
+ flags.append("oracle-vendor-lock")
244
+ return flags
245
+
246
+
247
+ def _project_deployment_risks(sm: "SourceMap") -> list[str]:
248
+ """Project-level deployment risk flags derived from Java version and app server."""
249
+ risks: list[str] = []
250
+ lv = sm.language_version or ""
251
+ if lv in ("1.8", "8", "1.7", "7"):
252
+ risks.append("legacy-java-runtime")
253
+ if getattr(sm, "app_server_hint", None) == "weblogic" and getattr(sm, "packaging", None) == "war":
254
+ risks.append("legacy-app-server-deployment")
255
+ return risks
256
+
257
+
258
+ def _mybatis_pairing(sm: "SourceMap") -> "Optional[dict[str, Any]]":
259
+ """Lightweight MyBatis mapper interface <-> XML file pairing from file_paths."""
260
+ from pathlib import Path as _Path
261
+ has_mybatis = any(
262
+ any(f.name.lower() == "mybatis" for f in s.frameworks)
263
+ for s in sm.stacks
264
+ )
265
+ if not has_mybatis:
266
+ return None
267
+ non_test = [p for p in sm.file_paths if "/test/" not in p and "/tests/" not in p]
268
+ mapper_interfaces = [p for p in non_test if p.endswith("Mapper.java")]
269
+ xml_files = [p for p in sm.file_paths if p.endswith("Mapper.xml")]
270
+ interface_index = {_Path(p).stem: p for p in mapper_interfaces}
271
+ xml_index = {_Path(p).stem: p for p in xml_files}
272
+ orphan_xml = [xml_index[s] for s in xml_index if s not in interface_index]
273
+ missing_xml = [s for s in interface_index if s not in xml_index]
274
+ result: dict[str, Any] = {
275
+ "mapper_interfaces": len(mapper_interfaces),
276
+ "xml_files": len(xml_files),
277
+ }
278
+ if orphan_xml:
279
+ result["orphan_xml"] = orphan_xml[:5]
280
+ if missing_xml:
281
+ result["missing_xml"] = missing_xml[:5]
282
+ return result
283
+
284
+
285
+ def _security_surface_from_eps(eps: list) -> "Optional[dict[str, Any]]":
286
+ """Extract @M3FiltroSeguridad resource names from entry point evidence strings."""
287
+ import re as _re
288
+ _NOMBRE_RE = _re.compile(r"nombreRecurso=[\"']([^\"']+)[\"']")
289
+ resource_names: list[str] = []
290
+ seen: set[str] = set()
291
+ for ep in eps:
292
+ evidence = getattr(ep, "evidence", None)
293
+ if not evidence:
294
+ continue
295
+ for m in _NOMBRE_RE.finditer(evidence):
296
+ nm = m.group(1)
297
+ if nm and nm not in seen:
298
+ seen.add(nm)
299
+ resource_names.append(nm)
300
+ return {"resource_names": resource_names} if resource_names else None
301
+
302
+
303
+ def _bootstrap_structured(eps: list) -> "Optional[dict[str, Any]]":
304
+ """Separate Java entry points into bootstrap / security / controllers groups."""
305
+ from pathlib import Path as _Path
306
+ bootstrap: list[str] = []
307
+ security: list[str] = []
308
+ controllers: list[dict] = []
309
+ seen_b: set[str] = set()
310
+ seen_s: set[str] = set()
311
+ seen_c: set[str] = set()
312
+
313
+ for ep in eps:
314
+ path = getattr(ep, "path", "")
315
+ kind = getattr(ep, "kind", "")
316
+ stem = _Path(path).stem
317
+
318
+ if kind == "application" or any(k in stem for k in ("Application", "Main", "Initializer", "Bootstrap")):
319
+ if path not in seen_b:
320
+ seen_b.add(path)
321
+ bootstrap.append(path)
322
+ elif kind == "filter" or any(k in stem for k in ("Filter", "Security", "Auth", "Jwt", "WebSecurity")):
323
+ if path not in seen_s:
324
+ seen_s.add(path)
325
+ security.append(path)
326
+ elif kind in ("rest_controller", "mvc_controller"):
327
+ if path not in seen_c:
328
+ seen_c.add(path)
329
+ item: dict[str, Any] = {"path": path}
330
+ http_path = getattr(ep, "http_path", None)
331
+ if http_path:
332
+ item["http_path"] = http_path
333
+ controllers.append(item)
334
+
335
+ if not bootstrap and not security:
336
+ return None
337
+
338
+ result: dict[str, Any] = {}
339
+ if bootstrap:
340
+ result["bootstrap"] = bootstrap
341
+ if security:
342
+ result["security"] = security
343
+ if controllers:
344
+ result["controllers"] = {
345
+ "count": len(controllers),
346
+ "sample": [{"path": c["path"]} for c in controllers[:5]],
347
+ }
348
+ return result
349
+
350
+
351
+ def _lightweight_arch_pattern(sm: "SourceMap") -> "Optional[dict[str, Any]]":
352
+ """Heuristic architecture pattern from directory names alone."""
353
+ if not sm.file_paths:
354
+ return None
355
+ dir_names: set[str] = set()
356
+ for p in sm.file_paths:
357
+ for part in p.replace("\\", "/").split("/")[:-1]:
358
+ dir_names.add(part.lower())
359
+
360
+ has_controller = bool({"controller", "controllers", "api", "rest", "web", "handler", "handlers"} & dir_names)
361
+ has_service = bool({"service", "services", "usecase", "usecases", "application"} & dir_names)
362
+ has_repository = bool({"repository", "repositories", "repo", "repos", "dao", "persistence"} & dir_names)
363
+ has_domain = bool({"domain", "domains", "core", "model", "models", "entity", "entities"} & dir_names)
364
+ has_infra = bool({"infrastructure", "infra", "adapter", "adapters"} & dir_names)
365
+
366
+ if has_controller and has_service and has_repository and has_domain:
367
+ return {"pattern": "ddd-layered", "confidence": 0.72 if has_infra else 0.55}
368
+ if bool({"ports", "port"} & dir_names) and bool({"adapter", "adapters"} & dir_names):
369
+ return {"pattern": "hexagonal-like", "confidence": 0.65}
370
+ if has_controller and bool({"model", "models", "entity", "entities"} & dir_names):
371
+ return {"pattern": "mvc", "confidence": 0.55}
372
+ if has_controller and has_service and has_repository:
373
+ return {"pattern": "layered", "confidence": 0.70}
374
+ if has_controller and has_service:
375
+ return {"pattern": "layered", "confidence": 0.42}
376
+ return None
377
+
378
+
194
379
  def _file_relevance(sm: SourceMap, *, limit: int = _FILE_RELEVANCE_LIMIT) -> list[dict[str, Any]]:
195
380
  from sourcecode.ranking_engine import RankingEngine
196
381
 
@@ -300,7 +485,20 @@ def _architecture_context(sm: SourceMap) -> dict[str, Any]:
300
485
  arch = sm.architecture
301
486
  if arch is not None and arch.requested:
302
487
  pattern = arch.pattern if arch.pattern not in (None, "unknown", "flat") else None
303
- ctx: dict[str, Any] = {
488
+ if not pattern:
489
+ _hint = _lightweight_arch_pattern(sm)
490
+ if _hint:
491
+ ctx: dict[str, Any] = {
492
+ "summary": sm.architecture_summary,
493
+ "pattern": _hint["pattern"],
494
+ "confidence": arch.confidence,
495
+ "pattern_confidence": _hint["confidence"],
496
+ "method": arch.method,
497
+ }
498
+ if arch.limitations:
499
+ ctx["limitations"] = arch.limitations
500
+ return ctx
501
+ ctx = {
304
502
  "summary": sm.architecture_summary,
305
503
  "pattern": pattern or "insufficient_evidence",
306
504
  "confidence": arch.confidence,
@@ -326,6 +524,18 @@ def _architecture_context(sm: SourceMap) -> dict[str, Any]:
326
524
  if arch.limitations:
327
525
  ctx["limitations"] = arch.limitations
328
526
  return ctx
527
+ _hint = _lightweight_arch_pattern(sm)
528
+ if _hint:
529
+ return {
530
+ "summary": sm.architecture_summary,
531
+ "pattern": _hint["pattern"],
532
+ "pattern_confidence": _hint["confidence"],
533
+ "confidence": "low",
534
+ "method": "filesystem_heuristic",
535
+ "limitations": [
536
+ "architecture analyzer not requested; pattern inferred from directory names only"
537
+ ],
538
+ }
329
539
  return {
330
540
  "summary": sm.architecture_summary,
331
541
  "pattern": "insufficient_evidence",
@@ -382,18 +592,23 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
382
592
 
383
593
  Excludes: file_tree, raw dependency lists, docs, module_graph, verbose metadata.
384
594
  """
385
- # Key dependencies — name + version + role only (no ecosystem, source, manifests)
595
+ # Key dependencies — name + version + role + risk_flags
386
596
  key_deps: Any = None
387
597
  if sm.dependency_summary is not None and sm.dependency_summary.requested:
388
- key_deps = [
389
- {
390
- "name": d.name,
391
- **({"version": d.declared_version} if d.declared_version else {}),
392
- **({"role": d.role} if d.role and d.role != "runtime" else {}),
393
- }
394
- for d in sm.key_dependencies
395
- if (d.role or "unknown") in _PRODUCTION_DEP_ROLES and d.scope not in {"dev"}
396
- ][:_KEY_DEPS_CAP]
598
+ key_deps = []
599
+ for d in sm.key_dependencies:
600
+ if (d.role or "unknown") not in _PRODUCTION_DEP_ROLES or d.scope in {"dev"}:
601
+ continue
602
+ entry: dict[str, Any] = {"name": d.name}
603
+ if d.declared_version:
604
+ entry["version"] = d.declared_version
605
+ if d.role and d.role != "runtime":
606
+ entry["role"] = d.role
607
+ flags = _dep_risk_flags(d.name, d.declared_version)
608
+ if flags:
609
+ entry["risk_flags"] = flags
610
+ key_deps.append(entry)
611
+ key_deps = key_deps[:_KEY_DEPS_CAP]
397
612
 
398
613
  # Dependency summary — requested flag + count + source only
399
614
  dep_summary_dict: Any = None
@@ -452,16 +667,20 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
452
667
  for n in _sorted_notes[:_CODE_NOTES_CAP]
453
668
  ]
454
669
 
455
- # Entry points — path + kind + confidence only
670
+ # Entry points — bootstrap-prioritized; structured when bootstrap classes detected
456
671
  ep_groups = _entry_point_groups(sm.entry_points)
457
- entry_points_compact = [
458
- {
459
- "path": ep["path"],
460
- **({"kind": ep["kind"]} if ep.get("kind") else {}),
461
- **({"confidence": ep["confidence"]} if ep.get("confidence") else {}),
462
- }
463
- for ep in ep_groups["production"][:_EP_PRODUCTION_CAP]
464
- ]
672
+ _bootstrap_struct = _bootstrap_structured(sm.entry_points)
673
+ if _bootstrap_struct:
674
+ entry_points_compact: Any = _bootstrap_struct
675
+ else:
676
+ entry_points_compact = [
677
+ {
678
+ "path": ep["path"],
679
+ **({"kind": ep["kind"]} if ep.get("kind") else {}),
680
+ **({"confidence": ep["confidence"]} if ep.get("confidence") else {}),
681
+ }
682
+ for ep in ep_groups["production"][:_EP_PRODUCTION_CAP]
683
+ ]
465
684
 
466
685
  # Stacks — name + method + confidence + frameworks (names only)
467
686
  stacks_compact = [
@@ -492,6 +711,22 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
492
711
  for g in sm.analysis_gaps
493
712
  ]
494
713
 
714
+ # Java/Spring operational context
715
+ _language_version = getattr(sm, "language_version", None)
716
+ _packaging = getattr(sm, "packaging", None)
717
+ _app_server = getattr(sm, "app_server_hint", None)
718
+ _deployment: Any = None
719
+ if _packaging or _app_server:
720
+ _deployment = {}
721
+ if _packaging:
722
+ _deployment["packaging"] = _packaging
723
+ if _app_server:
724
+ _deployment["app_server_hint"] = _app_server
725
+ _deploy_risks = _project_deployment_risks(sm)
726
+ _security_surface = _security_surface_from_eps(sm.entry_points)
727
+ _mybatis = _mybatis_pairing(sm)
728
+ _git_ctx = _compact_git_context(sm)
729
+
495
730
  result: dict[str, Any] = {
496
731
  "schema_version": sm.metadata.schema_version,
497
732
  "project_type": sm.project_type,
@@ -508,6 +743,18 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
508
743
  "confidence_summary": conf_dict,
509
744
  "analysis_gaps": gaps_list,
510
745
  }
746
+ if _language_version:
747
+ result["language_version"] = _language_version
748
+ if _deployment:
749
+ result["deployment"] = _deployment
750
+ if _deploy_risks:
751
+ result["deployment_risks"] = _deploy_risks
752
+ if _security_surface:
753
+ result["security_surface"] = _security_surface
754
+ if _mybatis:
755
+ result["mybatis"] = _mybatis
756
+ if _git_ctx:
757
+ result["git_context"] = _git_ctx
511
758
  _always_include = {"project_type", "project_summary", "architecture_summary", "dependency_summary"}
512
759
  return {k: v for k, v in result.items() if v is not None or k in _always_include}
513
760
 
@@ -825,15 +1072,34 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
825
1072
  if secondary:
826
1073
  project["secondary_stacks"] = sorted({s.stack for s in secondary})
827
1074
 
1075
+ # Java operational context in project block
1076
+ _lv = getattr(sm, "language_version", None)
1077
+ _pkg = getattr(sm, "packaging", None)
1078
+ _app_srv = getattr(sm, "app_server_hint", None)
1079
+ if _lv:
1080
+ project["language_version"] = _lv
1081
+ if _pkg or _app_srv:
1082
+ _depl: dict[str, Any] = {}
1083
+ if _pkg:
1084
+ _depl["packaging"] = _pkg
1085
+ if _app_srv:
1086
+ _depl["app_server_hint"] = _app_srv
1087
+ project["deployment"] = _depl
1088
+ _proj_risks = _project_deployment_risks(sm)
1089
+ if _proj_risks:
1090
+ project["deployment_risks"] = _proj_risks
1091
+
828
1092
  result: dict[str, Any] = {"project": project}
829
1093
 
830
- # ── 2. Entry points: production/runtime only, capped ─────────────────────
831
- # Auxiliary entries are not actionable for agents — omitted entirely.
832
- # Development entries shown in a separate channel, capped.
833
- ep_groups = _entry_point_groups(sm.entry_points)
834
- result["entry_points"] = ep_groups["production"][:_EP_PRODUCTION_CAP]
835
- if ep_groups["development"]:
836
- result["development_entry_points"] = ep_groups["development"][:_EP_DEV_CAP]
1094
+ # ── 2. Entry points: bootstrap-prioritized, then production ─────────────
1095
+ _bs = _bootstrap_structured(sm.entry_points)
1096
+ if _bs:
1097
+ result["entry_points"] = _bs
1098
+ else:
1099
+ ep_groups = _entry_point_groups(sm.entry_points)
1100
+ result["entry_points"] = ep_groups["production"][:_EP_PRODUCTION_CAP]
1101
+ if ep_groups["development"]:
1102
+ result["development_entry_points"] = ep_groups["development"][:_EP_DEV_CAP]
837
1103
 
838
1104
  # ── 3. Architecture ───────────────────────────────────────────────────────
839
1105
  result["architecture"] = _architecture_context(sm)
@@ -863,17 +1129,21 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
863
1129
  if dep_groups[dep_key]:
864
1130
  result[dep_key] = dep_groups[dep_key][:_SECONDARY_DEPS_CAP]
865
1131
 
866
- # Backward-compatible compact list, now production-only.
1132
+ # Backward-compatible compact list, now production-only, with risk_flags.
867
1133
  production_key_deps = [
868
1134
  d for d in sm.key_dependencies
869
1135
  if (d.role or "unknown") in _PRODUCTION_DEP_ROLES and d.scope not in {"dev"}
870
1136
  ]
871
1137
  if sm.dependency_summary and sm.dependency_summary.requested and production_key_deps:
872
1138
  _dep_skip = {"parent", "manifest_path", "workspace", "source", "ecosystem"}
873
- result["key_dependencies"] = [
874
- {k: v for k, v in asdict(d).items() if v is not None and k not in _dep_skip}
875
- for d in production_key_deps[:_KEY_DEPS_CAP]
876
- ]
1139
+ _kd_list = []
1140
+ for d in production_key_deps[:_KEY_DEPS_CAP]:
1141
+ item = {k: v for k, v in asdict(d).items() if v is not None and k not in _dep_skip}
1142
+ flags = _dep_risk_flags(d.name, d.declared_version)
1143
+ if flags:
1144
+ item["risk_flags"] = flags
1145
+ _kd_list.append(item)
1146
+ result["key_dependencies"] = _kd_list
877
1147
 
878
1148
  # ── 5. Signals — compact operational context ─────────────────────────────
879
1149
  signals: dict[str, Any] = {}
@@ -945,9 +1215,22 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
945
1215
  sem_info["hotspots"] = sem.hotspots[:10]
946
1216
  signals["semantic_graph"] = sem_info
947
1217
 
1218
+ # Java/Spring: security surface and ORM structure
1219
+ _sec_surf = _security_surface_from_eps(sm.entry_points)
1220
+ if _sec_surf:
1221
+ signals["security_surface"] = _sec_surf
1222
+ _mb = _mybatis_pairing(sm)
1223
+ if _mb:
1224
+ signals["mybatis"] = _mb
1225
+
948
1226
  if signals:
949
1227
  result["signals"] = signals
950
1228
 
1229
+ # Git context — lightweight (top-5 hotspots, branch, uncommitted count)
1230
+ _gc = _compact_git_context(sm)
1231
+ if _gc:
1232
+ result["git_context"] = _gc
1233
+
951
1234
  # ── 6. Confidence summary ─────────────────────────────────────────────────
952
1235
  if sm.confidence_summary is not None:
953
1236
  cs = sm.confidence_summary
@@ -1230,13 +1513,11 @@ def _contract_view_minimal(
1230
1513
  if getattr(sm, "app_server_hint", None):
1231
1514
  result["app_server_hint"] = sm.app_server_hint
1232
1515
 
1233
- # Per-file contracts
1516
+ # Per-file contracts — capped to avoid token bloat on large projects
1234
1517
  if contracts:
1235
- serialized: list[dict[str, Any]] = []
1236
- for c in contracts:
1237
- item = _serialize_contract_minimal(c)
1238
- serialized.append(item)
1239
- result["contracts"] = serialized
1518
+ _capped, _meta = _cap_contracts_for_output(contracts)
1519
+ result["contracts"] = [_serialize_contract_minimal(c) for c in _capped]
1520
+ result["contracts_meta"] = _meta
1240
1521
 
1241
1522
  # Optional analysis sections — included when the analyzer explicitly ran
1242
1523
  # (user passed --dependencies, --env-map, --code-notes, --git-context)
@@ -1587,10 +1868,11 @@ def _contract_view_standard(
1587
1868
  "stack": sm.confidence_summary.stack_confidence,
1588
1869
  }
1589
1870
 
1590
- # Per-file contracts (full detail)
1871
+ # Per-file contracts (full detail) — capped to avoid token bloat on large projects
1591
1872
  if contracts:
1873
+ _capped, _meta = _cap_contracts_for_output(contracts)
1592
1874
  serialized: list[dict[str, Any]] = []
1593
- for c in contracts:
1875
+ for c in _capped:
1594
1876
  if getattr(c, "language", None) == "mybatis-xml":
1595
1877
  item = _serialize_contract_mybatis_xml(c)
1596
1878
  item["relevance_score"] = round(c.relevance_score, 3)
@@ -1646,6 +1928,7 @@ def _contract_view_standard(
1646
1928
  item["method"] = c.extraction_method
1647
1929
  serialized.append(item)
1648
1930
  result["contracts"] = serialized
1931
+ result["contracts_meta"] = _meta
1649
1932
 
1650
1933
  # Optional analysis sections (deep mode or when analyzers ran)
1651
1934
  if include_optional:
@@ -0,0 +1,17 @@
1
+ package com.example.demo.config;
2
+
3
+ import org.springframework.boot.web.servlet.FilterRegistrationBean;
4
+ import org.springframework.context.annotation.Bean;
5
+ import org.springframework.context.annotation.Configuration;
6
+
7
+ @Configuration
8
+ public class FilterConfig {
9
+
10
+ @Bean
11
+ public FilterRegistrationBean<SecurityFilter> securityFilter() {
12
+ FilterRegistrationBean<SecurityFilter> bean = new FilterRegistrationBean<>();
13
+ bean.setFilter(new SecurityFilter());
14
+ bean.addUrlPatterns("/*");
15
+ return bean;
16
+ }
17
+ }
@@ -0,0 +1,16 @@
1
+ package com.example.demo.web;
2
+
3
+ import org.springframework.web.bind.annotation.GetMapping;
4
+ import org.springframework.web.bind.annotation.RequestMapping;
5
+ import org.springframework.web.bind.annotation.RestController;
6
+
7
+ @RestController
8
+ @RequestMapping("/nominas")
9
+ @M3FiltroSeguridad(nombreRecurso = "nominas")
10
+ public class NominaRestController {
11
+
12
+ @GetMapping
13
+ public String list() {
14
+ return "[]";
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
3
+ "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
4
+ <mapper namespace="com.example.demo.mapper.HealthMapper">
5
+ <select id="findStatus" parameterType="long" resultType="String">
6
+ SELECT status FROM health WHERE id = #{id}
7
+ </select>
8
+ </mapper>