sourcecode 1.30.30__tar.gz → 1.31.1__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 (231) hide show
  1. sourcecode-1.31.1/.continue-here.md +101 -0
  2. {sourcecode-1.30.30 → sourcecode-1.31.1}/PKG-INFO +7 -3
  3. {sourcecode-1.30.30 → sourcecode-1.31.1}/README.md +2 -2
  4. {sourcecode-1.30.30 → sourcecode-1.31.1}/pyproject.toml +6 -1
  5. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/cli.py +328 -1
  7. sourcecode-1.31.1/src/sourcecode/mcp/__init__.py +5 -0
  8. sourcecode-1.31.1/src/sourcecode/mcp/onboarding/__init__.py +1 -0
  9. sourcecode-1.31.1/src/sourcecode/mcp/onboarding/applier.py +63 -0
  10. sourcecode-1.31.1/src/sourcecode/mcp/onboarding/backup.py +40 -0
  11. sourcecode-1.31.1/src/sourcecode/mcp/onboarding/detector.py +104 -0
  12. sourcecode-1.31.1/src/sourcecode/mcp/onboarding/planner.py +40 -0
  13. sourcecode-1.31.1/src/sourcecode/mcp/runner.py +47 -0
  14. sourcecode-1.31.1/src/sourcecode/mcp/server.py +119 -0
  15. sourcecode-1.31.1/tests/test_mcp_runner.py +65 -0
  16. sourcecode-1.31.1/tests/test_mcp_serve.py +39 -0
  17. sourcecode-1.31.1/tests/test_mcp_tools.py +222 -0
  18. sourcecode-1.30.30/.continue-here.md +0 -150
  19. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-0911b79e.json +0 -3492
  20. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-37df4554.json +0 -262
  21. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-624321f3.json +0 -11466
  22. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-776b4676.json +0 -367
  23. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-9770fba7.json +0 -374
  24. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-c4e3c102.json +0 -262
  25. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-e8bc5fb4.json +0 -122
  26. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-e9801942.json +0 -218
  27. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-ee60e0cd.json +0 -328
  28. sourcecode-1.30.30/.sourcecode-cache/snapshot-85d4217-fdd9d3f7.json +0 -514
  29. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-0911b79e.json +0 -3471
  30. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-37df4554.json +0 -262
  31. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-624321f3.json +0 -11389
  32. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-776b4676.json +0 -367
  33. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-9770fba7.json +0 -374
  34. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-c4e3c102.json +0 -262
  35. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-e8bc5fb4.json +0 -122
  36. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-e9801942.json +0 -218
  37. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-ee60e0cd.json +0 -328
  38. sourcecode-1.30.30/.sourcecode-cache/snapshot-c6e9d39-fdd9d3f7.json +0 -512
  39. {sourcecode-1.30.30 → sourcecode-1.31.1}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  40. {sourcecode-1.30.30 → sourcecode-1.31.1}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  41. {sourcecode-1.30.30 → sourcecode-1.31.1}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  42. {sourcecode-1.30.30 → sourcecode-1.31.1}/.github/workflows/build-windows.yml +0 -0
  43. {sourcecode-1.30.30 → sourcecode-1.31.1}/.gitignore +0 -0
  44. {sourcecode-1.30.30 → sourcecode-1.31.1}/.ruff.toml +0 -0
  45. {sourcecode-1.30.30 → sourcecode-1.31.1}/CHANGELOG.md +0 -0
  46. {sourcecode-1.30.30 → sourcecode-1.31.1}/CONTRIBUTING.md +0 -0
  47. {sourcecode-1.30.30 → sourcecode-1.31.1}/LICENSE +0 -0
  48. {sourcecode-1.30.30 → sourcecode-1.31.1}/SECURITY.md +0 -0
  49. {sourcecode-1.30.30 → sourcecode-1.31.1}/docs/privacy.md +0 -0
  50. {sourcecode-1.30.30 → sourcecode-1.31.1}/docs/schema.md +0 -0
  51. {sourcecode-1.30.30 → sourcecode-1.31.1}/raw +0 -0
  52. {sourcecode-1.30.30 → sourcecode-1.31.1}/run_cli.py +0 -0
  53. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/adaptive_scanner.py +0 -0
  54. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/architecture_analyzer.py +0 -0
  55. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/architecture_summary.py +0 -0
  56. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/ast_extractor.py +0 -0
  57. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/classifier.py +0 -0
  58. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/code_notes_analyzer.py +0 -0
  59. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/confidence_analyzer.py +0 -0
  60. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/context_scorer.py +0 -0
  61. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/context_summarizer.py +0 -0
  62. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/contract_model.py +0 -0
  63. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/contract_pipeline.py +0 -0
  64. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/coverage_parser.py +0 -0
  65. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/dependency_analyzer.py +0 -0
  66. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/__init__.py +0 -0
  67. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/base.py +0 -0
  68. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/csproj_parser.py +0 -0
  69. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/dart.py +0 -0
  70. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/dotnet.py +0 -0
  71. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/elixir.py +0 -0
  72. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/go.py +0 -0
  73. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/heuristic.py +0 -0
  74. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/hybrid.py +0 -0
  75. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/java.py +0 -0
  76. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/jvm_ext.py +0 -0
  77. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/nodejs.py +0 -0
  78. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/parsers.py +0 -0
  79. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/php.py +0 -0
  80. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/project.py +0 -0
  81. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/python.py +0 -0
  82. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/ruby.py +0 -0
  83. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/rust.py +0 -0
  84. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/systems.py +0 -0
  85. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/terraform.py +0 -0
  86. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/detectors/tooling.py +0 -0
  87. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/doc_analyzer.py +0 -0
  88. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/entrypoint_classifier.py +0 -0
  89. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/env_analyzer.py +0 -0
  90. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/file_classifier.py +0 -0
  91. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/flow_analyzer.py +0 -0
  92. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/git_analyzer.py +0 -0
  93. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/graph_analyzer.py +0 -0
  94. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/metrics_analyzer.py +0 -0
  95. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/pr_comment_renderer.py +0 -0
  96. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/prepare_context.py +0 -0
  97. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/progress.py +0 -0
  98. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/ranking_engine.py +0 -0
  99. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/redactor.py +0 -0
  100. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/relevance_scorer.py +0 -0
  101. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/repo_classifier.py +0 -0
  102. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/repository_ir.py +0 -0
  103. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/runtime_classifier.py +0 -0
  104. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/scanner.py +0 -0
  105. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/schema.py +0 -0
  106. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/semantic_analyzer.py +0 -0
  107. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/serializer.py +0 -0
  108. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/summarizer.py +0 -0
  109. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/telemetry/__init__.py +0 -0
  110. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/telemetry/config.py +0 -0
  111. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/telemetry/consent.py +0 -0
  112. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/telemetry/events.py +0 -0
  113. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/telemetry/filters.py +0 -0
  114. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/telemetry/transport.py +0 -0
  115. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/tree_utils.py +0 -0
  116. {sourcecode-1.30.30 → sourcecode-1.31.1}/src/sourcecode/workspace.py +0 -0
  117. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/__init__.py +0 -0
  118. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/conftest.py +0 -0
  119. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/coverage.xml +0 -0
  120. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  121. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/fastapi_app/src/main.py +0 -0
  122. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  123. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/go_service/go.mod +0 -0
  124. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/jacoco.xml +0 -0
  125. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/latin1_sample.java +0 -0
  126. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/latin1_sample_iso.java +0 -0
  127. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/lcov.info +0 -0
  128. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  129. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/nextjs_app/package.json +0 -0
  130. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  131. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  132. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  133. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  134. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  135. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  136. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  137. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  138. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  139. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  140. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  141. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  142. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  143. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  144. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  145. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  146. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  147. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  148. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  149. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  150. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  151. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  152. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  153. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  154. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  155. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  156. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  157. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  158. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  159. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  160. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  161. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  162. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  163. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_architecture_analyzer.py +0 -0
  164. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_architecture_summary.py +0 -0
  165. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_ast_extractor.py +0 -0
  166. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_block1_reliability.py +0 -0
  167. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_block2_coverage.py +0 -0
  168. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_block5_quality.py +0 -0
  169. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_bug_fixes_v1302.py +0 -0
  170. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_bug_fixes_v16.py +0 -0
  171. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_bug_fixes_v2.py +0 -0
  172. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_classifier.py +0 -0
  173. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_cli.py +0 -0
  174. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_code_notes_analyzer.py +0 -0
  175. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_context_scorer.py +0 -0
  176. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_contract_pipeline.py +0 -0
  177. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_coverage_parser.py +0 -0
  178. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_cross_consistency.py +0 -0
  179. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_dependency_analyzer_node_python.py +0 -0
  180. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_dependency_analyzer_polyglot.py +0 -0
  181. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_dependency_schema.py +0 -0
  182. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_dotnet.py +0 -0
  183. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_go_rust_java.py +0 -0
  184. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_nodejs.py +0 -0
  185. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_php_ruby_dart.py +0 -0
  186. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_python.py +0 -0
  187. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_universal_managed.py +0 -0
  188. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detector_universal_systems.py +0 -0
  189. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_detectors_base.py +0 -0
  190. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_doc_analyzer_jsdom.py +0 -0
  191. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_doc_analyzer_python.py +0 -0
  192. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_encoding_regression.py +0 -0
  193. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_graph_analyzer_polyglot.py +0 -0
  194. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_graph_analyzer_python_node.py +0 -0
  195. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_graph_schema.py +0 -0
  196. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_hybrid_inference.py +0 -0
  197. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration.py +0 -0
  198. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_dependencies.py +0 -0
  199. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_detection.py +0 -0
  200. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_docs.py +0 -0
  201. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_graph_modules.py +0 -0
  202. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_lqn.py +0 -0
  203. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_metrics.py +0 -0
  204. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_multistack.py +0 -0
  205. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_semantics.py +0 -0
  206. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_integration_universal.py +0 -0
  207. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_java_spring_integration.py +0 -0
  208. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_metrics_analyzer.py +0 -0
  209. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_output_ux.py +0 -0
  210. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_packaging.py +0 -0
  211. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_phase1_improvements.py +0 -0
  212. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_pipeline_integrity.py +0 -0
  213. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_real_projects.py +0 -0
  214. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_redactor.py +0 -0
  215. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_repository_ir.py +0 -0
  216. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_scanner.py +0 -0
  217. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_schema.py +0 -0
  218. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_schema_normalization.py +0 -0
  219. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_scoring_calibration.py +0 -0
  220. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_semantic_analyzer_node.py +0 -0
  221. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_semantic_analyzer_python.py +0 -0
  222. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_semantic_import_resolution.py +0 -0
  223. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_semantic_schema.py +0 -0
  224. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_signal_hierarchy.py +0 -0
  225. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_summarizer.py +0 -0
  226. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_surface_honesty.py +0 -0
  227. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_task_differentiation.py +0 -0
  228. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_telemetry.py +0 -0
  229. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_v131_improvements.py +0 -0
  230. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_v1_10_regressions.py +0 -0
  231. {sourcecode-1.30.30 → sourcecode-1.31.1}/tests/test_workspace_analyzer.py +0 -0
@@ -0,0 +1,101 @@
1
+ # Continue Here — atlas-cli sesión 16
2
+
3
+ **Paused:** 2026-05-20
4
+ **Repo:** `/Users/user/Downloads/atlas-cli`
5
+ **Branch:** master
6
+ **Working tree:** CHANGELOG.md sin commitear (untracked)
7
+
8
+ ---
9
+
10
+ ## Trabajo de esta sesión (ya commiteado)
11
+
12
+ **Commit:** `0e430c1 fix(cli+serializer): BUG-1..5 + IMP-1 — UTF-8 stdout, --exclude parsing, onboard fast scan, angular_version null, lazy_routes_count zero, test_gaps config filter`
13
+
14
+ | ID | Fix | Archivo | Líneas clave |
15
+ |---|---|---|---|
16
+ | BUG-1 | `repo_ir_cmd` usa `stdout.buffer.write(encode("utf-8"))` en vez de `stdout.write()`. `main_entry` llama `stdout.reconfigure(encoding="utf-8")`. UnicodeEncodeError capturado con hint a `--output`. | `cli.py` | ~2367, ~2443 |
17
+ | BUG-2 | `--exclude` añadido a `_OPTIONS_WITH_VALUE` → valor no consumido como repo path. Warning si valor es directorio existente. | `cli.py` | ~173, ~856 |
18
+ | BUG-3 | `onboard --fast` siempre usa `AdaptiveScanner(base_depth=2)`; ya no usa git-index-only (que devolvía solo `.idea/vcs.xml`). | `prepare_context.py` | ~791 |
19
+ | BUG-4 | `deps = {**(pkg.get("dependencies") or {}), ...}` para manejar `null` explícito en package.json. Añade `peerDependencies` como fallback. | `serializer.py` | ~1663 |
20
+ | BUG-5 | Cuenta `loadChildren:` y `loadComponent:` (property syntax modern Angular) en vez del inexistente `loadChildren(`. | `serializer.py` | ~1644 |
21
+ | IMP-1 | `test_gaps` excluye `.eslintrc*`, `karma.conf.js`, `tsconfig*`, `.claude/**`, etc. por defecto. Nuevo flag `--include-config` para override. | `prepare_context.py`, `cli.py` | ~1663, ~1806 |
22
+ | IMP-2 | Stderr warning con workaround exacto cuando se detecta UnicodeEncodeError (BUG-1) o exclude value = directorio (BUG-2). | `cli.py` | ~2373, ~858 |
23
+
24
+ **Tests:** `tests/test_bug_fixes_v2.py` — 14 tests, todos pasan.
25
+ **Suite completa:** 1010 passed, 0 nuevos failures (3 pre-existentes sin tocar).
26
+
27
+ ---
28
+
29
+ ## Primera acción al retomar
30
+
31
+ CHANGELOG.md quedó fuera del commit (untracked). Commitear:
32
+
33
+ ```bash
34
+ cd /Users/user/Downloads/atlas-cli
35
+ git add CHANGELOG.md
36
+ git commit -m "docs: add CHANGELOG.md with BUG-1..5 and IMP-1 entries for v1.30.29
37
+
38
+ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Pendientes (heredados de sesión 15, siguen vigentes)
44
+
45
+ 1. **Spring bean wiring graph** — `_build_relations` emite `injects` edges para `@Autowired`. Falta: trazar `@Configuration → @Bean → @Autowired` completo, exponer como `wiring_graph`.
46
+
47
+ 2. **Smoke test en repo spring-boot real:**
48
+ ```bash
49
+ cd ~/Documents/workspace/spring-boot-realworld-example-app
50
+ sourcecode repo-ir . --since HEAD~1 2>/dev/null | python3 -c "
51
+ import json, sys; d=json.load(sys.stdin)
52
+ print('nodes:', len(d['graph']['nodes']))
53
+ print('route_surface:', d.get('route_surface', []))
54
+ print('reverse_graph keys:', len(d.get('reverse_graph', {})))
55
+ "
56
+ ```
57
+
58
+ 3. **Transaction boundary tracking** — `@Transactional` detectado. Falta: emitir `transactional_boundary` edges.
59
+
60
+ 4. **`_diff_routes` para `@RequestMapping` en clase** — class-level mapping change debería propagarse a todos los endpoints del controller.
61
+
62
+ 5. **Tests para el symptom fix (sesión 15)** — validar `--symptom "sesiones"` con mock commits produce ranking diferente al baseline. Cubrir en `tests/test_prepare_context_symptom.py`.
63
+
64
+ 6. **`_FRONTEND_SYMPTOM_MAP`** — añadir "sesiones"/"session" con backend terms `["httpsession", "sessionmanager", "sessionservice", "sessionrepository"]`.
65
+
66
+ ---
67
+
68
+ ## Estado de tests al pausar
69
+
70
+ ```
71
+ 1010 passed, 3 skipped (pre-existing)
72
+ ```
73
+
74
+ Pre-existing failures (no tocar):
75
+ - `tests/test_block2_coverage.py::TestDocAnalyzerLanguageCoverage::test_java_marked_unsupported` — `DocRecord.__init__()` unexpected kwarg `name`
76
+ - `tests/test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile_keeps_declared_versions` — typer version mismatch
77
+ - `tests/test_scoring_calibration.py` — varios score calibration tests
78
+
79
+ ---
80
+
81
+ ## Para retomar
82
+
83
+ ```bash
84
+ cd /Users/user/Downloads/atlas-cli
85
+ git log --oneline -3 # 0e430c1 debe estar arriba
86
+ git status # solo CHANGELOG.md untracked
87
+
88
+ # 1. Commitear CHANGELOG.md (comando arriba)
89
+
90
+ # 2. Verificar suite
91
+ python3 -m pytest tests/ \
92
+ --ignore=tests/test_block2_coverage.py \
93
+ --ignore=tests/test_scoring_calibration.py \
94
+ --deselect=tests/test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile_keeps_declared_versions \
95
+ -q
96
+ # Expected: 1010 passed, 3 skipped
97
+ ```
98
+
99
+ ---
100
+
101
+ *Pausado 2026-05-20 — gsd:pause-work*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.30.30
3
+ Version: 1.31.1
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -203,6 +203,7 @@ Classifier: Programming Language :: Python :: 3.12
203
203
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
204
204
  Classifier: Topic :: Utilities
205
205
  Requires-Python: >=3.9
206
+ Requires-Dist: mcp>=1.0.0
206
207
  Requires-Dist: pathspec>=1.0
207
208
  Requires-Dist: ruamel-yaml>=0.18
208
209
  Requires-Dist: tomli>=2.0; python_version < '3.11'
@@ -212,16 +213,19 @@ Requires-Dist: tree-sitter-javascript>=0.21; extra == 'ast'
212
213
  Requires-Dist: tree-sitter-typescript>=0.21; extra == 'ast'
213
214
  Requires-Dist: tree-sitter>=0.21; extra == 'ast'
214
215
  Provides-Extra: dev
216
+ Requires-Dist: mcp>=1.0.0; extra == 'dev'
215
217
  Requires-Dist: mypy>=1.10; extra == 'dev'
216
218
  Requires-Dist: pytest>=8; extra == 'dev'
217
219
  Requires-Dist: ruff>=0.15; extra == 'dev'
220
+ Provides-Extra: mcp
221
+ Requires-Dist: mcp>=1.0.0; extra == 'mcp'
218
222
  Description-Content-Type: text/markdown
219
223
 
220
224
  # sourcecode
221
225
 
222
226
  **Deterministic, behavior-aware codebase context for AI agents and PR review.**
223
227
 
224
- ![Version](https://img.shields.io/badge/version-1.30.30-blue)
228
+ ![Version](https://img.shields.io/badge/version-1.31.1-blue)
225
229
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
226
230
 
227
231
  ---
@@ -257,7 +261,7 @@ pipx install sourcecode
257
261
 
258
262
  ```bash
259
263
  sourcecode version
260
- # sourcecode 1.30.30
264
+ # sourcecode 1.31.1
261
265
  ```
262
266
 
263
267
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Deterministic, behavior-aware codebase context for AI agents and PR review.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.30.30-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.31.1-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
7
7
 
8
8
  ---
@@ -38,7 +38,7 @@ pipx install sourcecode
38
38
 
39
39
  ```bash
40
40
  sourcecode version
41
- # sourcecode 1.30.30
41
+ # sourcecode 1.31.1
42
42
  ```
43
43
 
44
44
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.30.30"
7
+ version = "1.31.1"
8
8
  description = "Deterministic codebase context for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -29,6 +29,7 @@ dependencies = [
29
29
  "pathspec>=1.0",
30
30
  "ruamel.yaml>=0.18",
31
31
  "tomli>=2.0; python_version < '3.11'",
32
+ "mcp>=1.0.0",
32
33
  ]
33
34
 
34
35
  [project.scripts]
@@ -39,12 +40,16 @@ dev = [
39
40
  "pytest>=8",
40
41
  "ruff>=0.15",
41
42
  "mypy>=1.10",
43
+ "mcp>=1.0.0",
42
44
  ]
43
45
  ast = [
44
46
  "tree-sitter>=0.21",
45
47
  "tree-sitter-typescript>=0.21",
46
48
  "tree-sitter-javascript>=0.21",
47
49
  ]
50
+ mcp = [
51
+ "mcp>=1.0.0",
52
+ ]
48
53
 
49
54
  [[tool.mypy.overrides]]
50
55
  module = ["tree_sitter", "tree_sitter_typescript", "tree_sitter_javascript"]
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.30.30"
3
+ __version__ = "1.31.1"
@@ -154,6 +154,10 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
154
154
 
155
155
  [bold]Subcommands:[/bold]
156
156
  prepare-context TASK [PATH] [dim]# task-specific context (onboard, delta, fix-bug, ...)[/dim]
157
+ mcp init [dim]# setup MCP integration (Claude Desktop, Cursor)[/dim]
158
+ mcp status [dim]# show MCP integration status[/dim]
159
+ mcp remove [dim]# remove MCP integration safely[/dim]
160
+ mcp serve [dim]# start MCP server for AI agent integration[/dim]
157
161
  telemetry status|enable|disable
158
162
  version
159
163
  """
@@ -161,7 +165,7 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
161
165
  # Known subcommand names — tokens matching these are routed as subcommands,
162
166
  # not consumed as a repository path.
163
167
  _SUBCOMMANDS: frozenset[str] = frozenset(
164
- {"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir"}
168
+ {"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir", "mcp"}
165
169
  )
166
170
 
167
171
  # Mutable container holding the path extracted by _preprocess_argv().
@@ -300,6 +304,9 @@ except Exception:
300
304
  telemetry_app = typer.Typer(help="Manage anonymous telemetry (opt-in).", rich_markup_mode="rich")
301
305
  app.add_typer(telemetry_app, name="telemetry")
302
306
 
307
+ mcp_app = typer.Typer(help="MCP integration: setup, status, serve, remove.", rich_markup_mode="rich")
308
+ app.add_typer(mcp_app, name="mcp")
309
+
303
310
 
304
311
  def _maybe_ask_consent() -> None:
305
312
  """Show first-run consent prompt once, on interactive TTYs only."""
@@ -317,6 +324,26 @@ def _maybe_ask_consent() -> None:
317
324
  pass
318
325
 
319
326
 
327
+ def _maybe_show_mcp_hint() -> None:
328
+ """Show MCP integration hint once after first install, on TTY only."""
329
+ import sys as _sys
330
+ try:
331
+ if not _sys.stderr.isatty():
332
+ return
333
+ from sourcecode.telemetry.config import _CONFIG_FILE, _load, _save
334
+ data = _load()
335
+ if data.get("mcp", {}).get("hint_shown"):
336
+ return
337
+ typer.echo("", err=True)
338
+ typer.echo(" MCP integration available:", err=True)
339
+ typer.echo(" → sourcecode mcp init", err=True)
340
+ typer.echo("", err=True)
341
+ data.setdefault("mcp", {})["hint_shown"] = True
342
+ _save(data)
343
+ except Exception:
344
+ pass
345
+
346
+
320
347
  def _active_flags(
321
348
  dependencies: bool, graph_modules: bool, docs: bool, full_metrics: bool,
322
349
  semantics: bool, architecture: bool, git_context: bool, env_map: bool,
@@ -616,6 +643,7 @@ def main(
616
643
  # First-run consent (skip for telemetry/version/config subcommands)
617
644
  if ctx.invoked_subcommand not in ("telemetry", "version", "config"):
618
645
  _maybe_ask_consent()
646
+ _maybe_show_mcp_hint()
619
647
 
620
648
  # When a subcommand is invoked, skip the main analysis.
621
649
  if ctx.invoked_subcommand is not None:
@@ -2432,6 +2460,305 @@ def analyze_cmd(
2432
2460
  raise typer.Exit(code=1)
2433
2461
 
2434
2462
 
2463
+ # ── MCP server ────────────────────────────────────────────────────────────────
2464
+
2465
+ @mcp_app.command("serve")
2466
+ def mcp_serve() -> None:
2467
+ """Start the MCP server on stdio for AI agent integration.
2468
+
2469
+ \b
2470
+ Configure in your MCP client (e.g. Claude Desktop):
2471
+ {
2472
+ "sourcecode": {
2473
+ "command": "sourcecode",
2474
+ "args": ["mcp", "serve"]
2475
+ }
2476
+ }
2477
+ """
2478
+ import logging
2479
+ import sys as _sys
2480
+
2481
+ logging.basicConfig(
2482
+ stream=_sys.stderr,
2483
+ level=logging.INFO,
2484
+ format="[sourcecode-mcp] %(levelname)s %(message)s",
2485
+ )
2486
+ from sourcecode.mcp.server import mcp as _mcp
2487
+
2488
+ log = logging.getLogger(__name__)
2489
+ log.info("sourcecode-mcp starting (stdio transport)")
2490
+ try:
2491
+ _mcp.run()
2492
+ except KeyboardInterrupt:
2493
+ log.info("sourcecode-mcp stopped")
2494
+ except Exception as exc:
2495
+ log.critical("sourcecode-mcp fatal error: %s", exc, exc_info=True)
2496
+ raise typer.Exit(code=1)
2497
+
2498
+
2499
+ # ── MCP onboarding ────────────────────────────────────────────────────────────
2500
+
2501
+ @mcp_app.command("init")
2502
+ def mcp_init(
2503
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
2504
+ target: Optional[str] = typer.Option(
2505
+ None,
2506
+ "--target",
2507
+ "-t",
2508
+ help="Target client: claude-desktop | cursor. Default: auto-detect all.",
2509
+ ),
2510
+ ) -> None:
2511
+ """Setup MCP integration for Claude Desktop, Cursor, and other clients.
2512
+
2513
+ \b
2514
+ Detects installed MCP clients, backs up their config files, and safely
2515
+ inserts the sourcecode server entry. Fully idempotent — safe to re-run.
2516
+
2517
+ \b
2518
+ Examples:
2519
+ sourcecode mcp init
2520
+ sourcecode mcp init --target claude-desktop
2521
+ sourcecode mcp init --target cursor --yes
2522
+ """
2523
+ from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
2524
+ from sourcecode.mcp.onboarding.planner import build_install_plan
2525
+ from sourcecode.mcp.onboarding import backup, applier
2526
+
2527
+ typer.echo("Detecting MCP clients...")
2528
+ typer.echo("")
2529
+
2530
+ all_clients = detect_clients()
2531
+
2532
+ if target:
2533
+ target_slug = target.lower()
2534
+ clients = [c for c in all_clients if c.slug == target_slug]
2535
+ if not clients:
2536
+ valid = ", ".join(c.slug for c in all_clients)
2537
+ typer.echo(f"Unknown target '{target}'. Valid: {valid}", err=True)
2538
+ raise typer.Exit(code=1)
2539
+ else:
2540
+ clients = all_clients
2541
+
2542
+ if not clients:
2543
+ typer.echo("No MCP clients found on this system.")
2544
+ typer.echo("")
2545
+ typer.echo("Manual setup — add to your MCP client config:")
2546
+ typer.echo(' "sourcecode": {"command": "sourcecode", "args": ["mcp", "serve"]}')
2547
+ raise typer.Exit(code=0)
2548
+
2549
+ # Show detection results
2550
+ for client in clients:
2551
+ mark = "✓" if client.app_installed else "○"
2552
+ note = "" if client.app_installed else " (not found)"
2553
+ typer.echo(f" {mark} {client.name:<18} {client.config_path}{note}")
2554
+ typer.echo("")
2555
+
2556
+ # Build plan
2557
+ plan = build_install_plan(clients)
2558
+ actionable = [a for a in plan if a.client.app_installed and not a.already_installed]
2559
+ already_done = [a for a in plan if a.client.app_installed and a.already_installed]
2560
+
2561
+ if already_done and not actionable:
2562
+ typer.echo("Already configured:")
2563
+ for a in already_done:
2564
+ typer.echo(f" ✓ {a.client.name} {a.client.config_path}")
2565
+ typer.echo("")
2566
+ typer.echo("Nothing to do. Remove: sourcecode mcp remove")
2567
+ raise typer.Exit(code=0)
2568
+
2569
+ if already_done:
2570
+ typer.echo("Already configured:")
2571
+ for a in already_done:
2572
+ typer.echo(f" ✓ {a.client.name} {a.client.config_path}")
2573
+ typer.echo("")
2574
+
2575
+ # Show plan for actionable items
2576
+ typer.echo("This will:")
2577
+ for a in actionable:
2578
+ verb = "Create " if a.will_create_file else "Modify "
2579
+ typer.echo(f" {verb} {a.client.config_path}")
2580
+ typer.echo(f" Backup → ~/.config/sourcecode/mcp-backups/")
2581
+ typer.echo("")
2582
+
2583
+ if not yes:
2584
+ confirmed = typer.confirm("Proceed?", default=False)
2585
+ if not confirmed:
2586
+ typer.echo("Aborted.")
2587
+ raise typer.Exit(code=0)
2588
+ typer.echo("")
2589
+
2590
+ # Apply
2591
+ errors: list[str] = []
2592
+ for a in actionable:
2593
+ try:
2594
+ config = applier.read_config(a.client.config_path)
2595
+ if a.client.config_path.exists():
2596
+ bak = backup.create(a.client.config_path)
2597
+ typer.echo(f" ✓ Backup {bak}")
2598
+ updated = applier.apply_entry(config)
2599
+ applier.write_config(a.client.config_path, updated)
2600
+ if not applier.validate(a.client.config_path):
2601
+ errors.append(f"{a.client.name}: JSON validation failed after write")
2602
+ continue
2603
+ typer.echo(f" ✓ Updated {a.client.config_path}")
2604
+ except Exception as exc:
2605
+ errors.append(f"{a.client.name}: {exc}")
2606
+
2607
+ typer.echo("")
2608
+
2609
+ if errors:
2610
+ for err in errors:
2611
+ typer.echo(f" ✗ {err}", err=True)
2612
+ raise typer.Exit(code=1)
2613
+
2614
+ typer.echo("MCP integration active.")
2615
+ typer.echo("")
2616
+
2617
+ # Post-write: validate config and warn if client not running
2618
+ for a in actionable:
2619
+ if not is_client_running(a.client):
2620
+ typer.echo(
2621
+ f" ⚠ Config written but {a.client.name} is not running. "
2622
+ f"Start {a.client.name} and run sourcecode mcp status to verify.",
2623
+ err=False,
2624
+ )
2625
+ else:
2626
+ restart_msg = "" if a.will_create_file else f" Restart {a.client.name} to apply."
2627
+ typer.echo(f" ✓ {a.client.name} is running.{restart_msg}")
2628
+
2629
+ typer.echo("")
2630
+ typer.echo(" Remove: sourcecode mcp remove")
2631
+
2632
+
2633
+ @mcp_app.command("status")
2634
+ def mcp_status() -> None:
2635
+ """Show MCP integration status: dependencies, config files, and connectivity."""
2636
+ from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
2637
+ from sourcecode.mcp.onboarding import applier
2638
+
2639
+ sep = "─" * 46
2640
+
2641
+ typer.echo("MCP Status")
2642
+ typer.echo(sep)
2643
+
2644
+ # Stage 1: Dependencies
2645
+ try:
2646
+ import mcp as _mcp_pkg # noqa: F401
2647
+ typer.echo("Dependencies ✓ installed")
2648
+ except ImportError:
2649
+ typer.echo("Dependencies ✗ missing")
2650
+ typer.echo(" Fix: pip install sourcecode[mcp]")
2651
+ typer.echo("")
2652
+
2653
+ clients = detect_clients()
2654
+ if not clients:
2655
+ typer.echo(" No MCP clients detected on this system.")
2656
+ typer.echo(sep)
2657
+ typer.echo(" Setup: sourcecode mcp init")
2658
+ raise typer.Exit(code=0)
2659
+
2660
+ # Stage 2: Config files
2661
+ typer.echo("Config files")
2662
+ for client in clients:
2663
+ if not client.app_installed:
2664
+ typer.echo(f" {client.name:<20} ✗ not found")
2665
+ typer.echo(f" Expected: {client.config_path}")
2666
+ typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
2667
+ continue
2668
+ config = applier.read_config(client.config_path)
2669
+ if applier.is_installed(config):
2670
+ typer.echo(f" {client.name:<20} ✓ configured {client.config_path}")
2671
+ else:
2672
+ typer.echo(f" {client.name:<20} ✗ not configured")
2673
+ typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
2674
+ typer.echo("")
2675
+
2676
+ # Stage 3: Connectivity
2677
+ typer.echo("Connectivity")
2678
+ any_installed = any(c.app_installed for c in clients)
2679
+ if not any_installed:
2680
+ typer.echo(" (no clients to check)")
2681
+ else:
2682
+ for client in clients:
2683
+ if not client.app_installed:
2684
+ continue
2685
+ if is_client_running(client):
2686
+ typer.echo(f" {client.name:<20} ✓ running")
2687
+ else:
2688
+ typer.echo(f" {client.name:<20} ✗ not running")
2689
+ typer.echo(f" Fix: open {client.name}, then run sourcecode mcp status")
2690
+
2691
+ typer.echo(sep)
2692
+ typer.echo(" Setup: sourcecode mcp init")
2693
+ typer.echo(" Remove: sourcecode mcp remove")
2694
+
2695
+
2696
+ @mcp_app.command("remove")
2697
+ def mcp_remove(
2698
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
2699
+ ) -> None:
2700
+ """Remove sourcecode MCP integration from all configured clients.
2701
+
2702
+ \b
2703
+ Backs up config files before modifying. Restores from backup when available,
2704
+ otherwise removes the sourcecode entry while preserving all other config.
2705
+ """
2706
+ from sourcecode.mcp.onboarding.detector import detect_clients
2707
+ from sourcecode.mcp.onboarding.planner import build_remove_plan
2708
+ from sourcecode.mcp.onboarding import backup, applier
2709
+
2710
+ clients = detect_clients()
2711
+ plan = build_remove_plan(clients)
2712
+ installed = [a for a in plan if a.already_installed]
2713
+
2714
+ if not installed:
2715
+ typer.echo("sourcecode MCP integration not found in any client config.")
2716
+ typer.echo(" Setup: sourcecode mcp init")
2717
+ raise typer.Exit(code=0)
2718
+
2719
+ typer.echo("Remove sourcecode MCP integration from:")
2720
+ typer.echo("")
2721
+ for a in installed:
2722
+ typer.echo(f" {a.client.name} {a.client.config_path}")
2723
+ bak = backup.latest(a.client.config_path)
2724
+ if bak:
2725
+ typer.echo(f" Backup available: {bak}")
2726
+ typer.echo("")
2727
+
2728
+ if not yes:
2729
+ confirmed = typer.confirm("Proceed?", default=False)
2730
+ if not confirmed:
2731
+ typer.echo("Aborted.")
2732
+ raise typer.Exit(code=0)
2733
+ typer.echo("")
2734
+
2735
+ errors: list[str] = []
2736
+ for a in installed:
2737
+ try:
2738
+ bak = backup.create(a.client.config_path)
2739
+ typer.echo(f" ✓ Backup {bak}")
2740
+ config = applier.read_config(a.client.config_path)
2741
+ updated = applier.remove_entry(config)
2742
+ applier.write_config(a.client.config_path, updated)
2743
+ if not applier.validate(a.client.config_path):
2744
+ errors.append(f"{a.client.name}: JSON validation failed — restoring backup")
2745
+ backup.restore(bak, a.client.config_path)
2746
+ continue
2747
+ typer.echo(f" ✓ Updated {a.client.config_path}")
2748
+ except Exception as exc:
2749
+ errors.append(f"{a.client.name}: {exc}")
2750
+
2751
+ typer.echo("")
2752
+
2753
+ if errors:
2754
+ for err in errors:
2755
+ typer.echo(f" ✗ {err}", err=True)
2756
+ raise typer.Exit(code=1)
2757
+
2758
+ typer.echo("MCP integration removed.")
2759
+ typer.echo(" Re-add: sourcecode mcp init")
2760
+
2761
+
2435
2762
  # ── Entry point ───────────────────────────────────────────────────────────────
2436
2763
 
2437
2764
  def main_entry() -> None:
@@ -0,0 +1,5 @@
1
+ """MCP server integration for sourcecode CLI.
2
+
3
+ Exposes all CLI capabilities as MCP tools callable by AI agents.
4
+ Install the 'mcp' extra to enable: pip install sourcecode[mcp]
5
+ """
@@ -0,0 +1 @@
1
+ """MCP client onboarding: detect, plan, apply, backup, remove."""
@@ -0,0 +1,63 @@
1
+ """Safe JSON config applier for MCP client configuration files."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from pathlib import Path
6
+
7
+ _MCP_SERVERS_KEY = "mcpServers"
8
+ _ENTRY_NAME = "sourcecode"
9
+ _ENTRY_VALUE: dict[str, object] = {
10
+ "command": "sourcecode",
11
+ "args": ["mcp", "serve"],
12
+ }
13
+
14
+
15
+ def read_config(path: Path) -> dict:
16
+ """Parse JSON config from path. Returns empty dict if missing or empty."""
17
+ if not path.exists():
18
+ return {}
19
+ raw = path.read_text(encoding="utf-8").strip()
20
+ if not raw:
21
+ return {}
22
+ return json.loads(raw) # type: ignore[no-any-return]
23
+
24
+
25
+ def is_installed(config: dict) -> bool:
26
+ """True if sourcecode entry already present in mcpServers."""
27
+ return _ENTRY_NAME in config.get(_MCP_SERVERS_KEY, {})
28
+
29
+
30
+ def apply_entry(config: dict) -> dict:
31
+ """Return new config dict with sourcecode merged into mcpServers."""
32
+ config = dict(config)
33
+ servers: dict = dict(config.get(_MCP_SERVERS_KEY, {}))
34
+ servers[_ENTRY_NAME] = _ENTRY_VALUE
35
+ config[_MCP_SERVERS_KEY] = servers
36
+ return config
37
+
38
+
39
+ def remove_entry(config: dict) -> dict:
40
+ """Return new config dict with sourcecode removed from mcpServers."""
41
+ config = dict(config)
42
+ servers: dict = dict(config.get(_MCP_SERVERS_KEY, {}))
43
+ servers.pop(_ENTRY_NAME, None)
44
+ if servers:
45
+ config[_MCP_SERVERS_KEY] = servers
46
+ elif _MCP_SERVERS_KEY in config:
47
+ del config[_MCP_SERVERS_KEY]
48
+ return config
49
+
50
+
51
+ def write_config(path: Path, config: dict) -> None:
52
+ """Atomically write config as formatted JSON."""
53
+ path.parent.mkdir(parents=True, exist_ok=True)
54
+ path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8")
55
+
56
+
57
+ def validate(path: Path) -> bool:
58
+ """True if path contains parseable JSON."""
59
+ try:
60
+ json.loads(path.read_text(encoding="utf-8"))
61
+ return True
62
+ except Exception:
63
+ return False
@@ -0,0 +1,40 @@
1
+ """Timestamped backup management for MCP config files."""
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+
7
+ _BACKUP_DIR = Path.home() / ".config" / "sourcecode" / "mcp-backups"
8
+
9
+
10
+ def _backup_stem(config_path: Path) -> str:
11
+ """Stable prefix derived from the config path, safe for filenames."""
12
+ parts = config_path.parts
13
+ # Use last two path components to keep names readable but unique enough.
14
+ label = "_".join(p for p in parts[-2:] if p).replace(".", "_")
15
+ return label
16
+
17
+
18
+ def create(config_path: Path) -> Path:
19
+ """Copy config_path to a timestamped backup file. Returns backup path."""
20
+ _BACKUP_DIR.mkdir(parents=True, exist_ok=True)
21
+ ts = datetime.now().strftime("%Y%m%dT%H%M%S")
22
+ stem = _backup_stem(config_path)
23
+ backup_path = _BACKUP_DIR / f"{stem}.{ts}.bak"
24
+ backup_path.write_bytes(config_path.read_bytes())
25
+ return backup_path
26
+
27
+
28
+ def restore(backup_path: Path, target_path: Path) -> None:
29
+ """Overwrite target_path with contents of backup_path."""
30
+ target_path.parent.mkdir(parents=True, exist_ok=True)
31
+ target_path.write_bytes(backup_path.read_bytes())
32
+
33
+
34
+ def latest(config_path: Path) -> Path | None:
35
+ """Find the most recent backup for config_path, or None."""
36
+ if not _BACKUP_DIR.exists():
37
+ return None
38
+ stem = _backup_stem(config_path)
39
+ matches = sorted(_BACKUP_DIR.glob(f"{stem}.*.bak"))
40
+ return matches[-1] if matches else None