codeprobe 0.3.7__tar.gz → 0.3.9__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. {codeprobe-0.3.7 → codeprobe-0.3.9}/PKG-INFO +1 -1
  2. {codeprobe-0.3.7 → codeprobe-0.3.9}/pyproject.toml +1 -1
  3. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/__init__.py +1 -1
  4. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/init_cmd.py +70 -23
  5. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/wizard.py +16 -7
  6. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe.egg-info/PKG-INFO +1 -1
  7. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_init_wizard.py +14 -8
  8. {codeprobe-0.3.7 → codeprobe-0.3.9}/LICENSE +0 -0
  9. {codeprobe-0.3.7 → codeprobe-0.3.9}/README.md +0 -0
  10. {codeprobe-0.3.7 → codeprobe-0.3.9}/setup.cfg +0 -0
  11. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/__main__.py +0 -0
  12. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/__init__.py +0 -0
  13. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/_base.py +0 -0
  14. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/claude.py +0 -0
  15. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/codex.py +0 -0
  16. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/copilot.py +0 -0
  17. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/openai_compat.py +0 -0
  18. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/protocol.py +0 -0
  19. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/session.py +0 -0
  20. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/adapters/telemetry.py +0 -0
  21. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/analysis/__init__.py +0 -0
  22. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/analysis/ranking.py +0 -0
  23. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/analysis/report.py +0 -0
  24. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/analysis/stats.py +0 -0
  25. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/api.py +0 -0
  26. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/assess/__init__.py +0 -0
  27. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/assess/heuristics.py +0 -0
  28. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/__init__.py +0 -0
  29. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/assess_cmd.py +0 -0
  30. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/auth_cmd.py +0 -0
  31. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/doctor_cmd.py +0 -0
  32. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/experiment_cmd.py +0 -0
  33. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/interpret_cmd.py +0 -0
  34. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/json_display.py +0 -0
  35. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/mine_cmd.py +0 -0
  36. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/preamble_cmd.py +0 -0
  37. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/probe_cmd.py +0 -0
  38. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/ratings_cmd.py +0 -0
  39. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/rich_display.py +0 -0
  40. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/run_cmd.py +0 -0
  41. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/scaffold_cmd.py +0 -0
  42. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/validate_cmd.py +0 -0
  43. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/cli/yaml_writer.py +0 -0
  44. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/config/__init__.py +0 -0
  45. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/config/loader.py +0 -0
  46. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/config/redact.py +0 -0
  47. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/__init__.py +0 -0
  48. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/_shared.py +0 -0
  49. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/adaptive.py +0 -0
  50. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/counterfactual.py +0 -0
  51. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/debate.py +0 -0
  52. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/decision_tree.py +0 -0
  53. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/elo.py +0 -0
  54. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/fingerprint.py +0 -0
  55. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/mutation.py +0 -0
  56. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/pareto.py +0 -0
  57. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/sprt.py +0 -0
  58. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/contrib/tournament.py +0 -0
  59. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/__init__.py +0 -0
  60. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/__main__.py +0 -0
  61. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/checkpoint.py +0 -0
  62. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/events.py +0 -0
  63. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/executor.py +0 -0
  64. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/experiment.py +0 -0
  65. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/isolation.py +0 -0
  66. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/llm.py +0 -0
  67. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/mcp_discovery.py +0 -0
  68. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/preamble.py +0 -0
  69. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/registry.py +0 -0
  70. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/sandbox.py +0 -0
  71. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/core/scoring.py +0 -0
  72. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/loaders/__init__.py +0 -0
  73. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/loaders/suite.py +0 -0
  74. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/__init__.py +0 -0
  75. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/_graph.py +0 -0
  76. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/_lang.py +0 -0
  77. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/comprehension.py +0 -0
  78. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/comprehension_writer.py +0 -0
  79. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/curator.py +0 -0
  80. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/curator_backends.py +0 -0
  81. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/curator_tiers.py +0 -0
  82. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/extractor.py +0 -0
  83. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/multi_repo.py +0 -0
  84. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/org_scale.py +0 -0
  85. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/org_scale_families.py +0 -0
  86. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/org_scale_oracle.py +0 -0
  87. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/org_scale_scanner.py +0 -0
  88. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/org_scale_validate.py +0 -0
  89. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/sg_auth.py +0 -0
  90. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/sg_ground_truth.py +0 -0
  91. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/sources.py +0 -0
  92. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/mining/writer.py +0 -0
  93. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/models/__init__.py +0 -0
  94. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/models/evalrc.py +0 -0
  95. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/models/experiment.py +0 -0
  96. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/models/preamble.py +0 -0
  97. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/models/suite.py +0 -0
  98. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/models/task.py +0 -0
  99. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/preambles/__init__.py +0 -0
  100. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/preambles/github.md +0 -0
  101. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/preambles/sourcegraph.md +0 -0
  102. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/probe/__init__.py +0 -0
  103. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/probe/adapter.py +0 -0
  104. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/probe/generator.py +0 -0
  105. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/probe/writer.py +0 -0
  106. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/ratings/__init__.py +0 -0
  107. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/ratings/collector.py +0 -0
  108. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/scaffold/__init__.py +0 -0
  109. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/scaffold/writer.py +0 -0
  110. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/templates/__init__.py +0 -0
  111. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/templates/evalrc-mcp-comparison.yaml +0 -0
  112. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/templates/evalrc-model-comparison.yaml +0 -0
  113. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe/templates/evalrc-prompt-comparison.yaml +0 -0
  114. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe.egg-info/SOURCES.txt +0 -0
  115. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe.egg-info/dependency_links.txt +0 -0
  116. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe.egg-info/entry_points.txt +0 -0
  117. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe.egg-info/requires.txt +0 -0
  118. {codeprobe-0.3.7 → codeprobe-0.3.9}/src/codeprobe.egg-info/top_level.txt +0 -0
  119. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_adapter_contracts.py +0 -0
  120. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_adapters.py +0 -0
  121. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_analysis.py +0 -0
  122. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_api.py +0 -0
  123. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_artifact_scorer.py +0 -0
  124. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_assess.py +0 -0
  125. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_auth_cmd.py +0 -0
  126. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_changed_symbols.py +0 -0
  127. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_checkpoint.py +0 -0
  128. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_checkpoint_scoring.py +0 -0
  129. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_cli.py +0 -0
  130. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_comprehension.py +0 -0
  131. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_config_loader.py +0 -0
  132. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_contrib.py +0 -0
  133. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_ctrlc_integration.py +0 -0
  134. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_curator_backends.py +0 -0
  135. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_curator_core.py +0 -0
  136. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_curator_integration.py +0 -0
  137. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_curator_tiers.py +0 -0
  138. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_doctor_cmd.py +0 -0
  139. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_events.py +0 -0
  140. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_executor.py +0 -0
  141. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_executor_events.py +0 -0
  142. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_experiment_cmd.py +0 -0
  143. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_experiment_core.py +0 -0
  144. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_isolation.py +0 -0
  145. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_json_display.py +0 -0
  146. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_llm.py +0 -0
  147. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_loaders.py +0 -0
  148. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mcp_families_mining.py +0 -0
  149. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mcp_validate.py +0 -0
  150. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mine_cli.py +0 -0
  151. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mine_goals.py +0 -0
  152. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mine_presets.py +0 -0
  153. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mine_profiles.py +0 -0
  154. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_mining.py +0 -0
  155. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_models.py +0 -0
  156. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_multi_repo_e2e.py +0 -0
  157. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_multi_repo_mining.py +0 -0
  158. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_new_families.py +0 -0
  159. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_openai_compat.py +0 -0
  160. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_oracle_types.py +0 -0
  161. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_org_scale.py +0 -0
  162. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_pipeline_integration.py +0 -0
  163. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_preamble.py +0 -0
  164. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_preamble_cmd.py +0 -0
  165. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_probe.py +0 -0
  166. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_probe_adapter.py +0 -0
  167. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_ratings.py +0 -0
  168. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_ratings_cmd.py +0 -0
  169. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_registry.py +0 -0
  170. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_run_config_resolution.py +0 -0
  171. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_scaffold.py +0 -0
  172. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_scanner_refactor.py +0 -0
  173. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_scoring.py +0 -0
  174. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_secret_redaction.py +0 -0
  175. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_session.py +0 -0
  176. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_sg_auth.py +0 -0
  177. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_sg_ground_truth.py +0 -0
  178. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_shell_shim.py +0 -0
  179. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_show_prompt.py +0 -0
  180. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_suite.py +0 -0
  181. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_suite_manifest.py +0 -0
  182. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_telemetry.py +0 -0
  183. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_validate_cmd.py +0 -0
  184. {codeprobe-0.3.7 → codeprobe-0.3.9}/tests/test_weighted_f1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeprobe
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Benchmark AI coding agents against your own codebase. Mine real tasks from repo history, run agents, interpret results.
5
5
  Author: codeprobe contributors
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "codeprobe"
3
- version = "0.3.7"
3
+ version = "0.3.9"
4
4
  description = "Benchmark AI coding agents against your own codebase. Mine real tasks from repo history, run agents, interpret results."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """codeprobe — Benchmark AI coding agents against your own codebase."""
2
2
 
3
- __version__ = "0.3.7"
3
+ __version__ = "0.3.9"
@@ -194,43 +194,90 @@ def _prompt_sourcegraph_url() -> str | None:
194
194
  return url if url else None
195
195
 
196
196
 
197
+ def _extract_sourcegraph_mcp(
198
+ discovered: list[tuple[Path, list[str]]],
199
+ ) -> dict | None:
200
+ """Load the Sourcegraph MCP config from a discovered config file.
201
+
202
+ Returns the full MCP config dict with only the Sourcegraph server,
203
+ or None if no Sourcegraph server is found.
204
+ """
205
+ import json
206
+
207
+ sg_names = {"sourcegraph", "sg", "sourcegraph-mcp"}
208
+ for path, server_names in discovered:
209
+ matching = [n for n in server_names if n.lower() in sg_names]
210
+ if not matching:
211
+ continue
212
+ try:
213
+ data = json.loads(path.read_text(encoding="utf-8"))
214
+ except (json.JSONDecodeError, OSError):
215
+ continue
216
+ servers = data.get("mcpServers", {})
217
+ for name in matching:
218
+ if name in servers:
219
+ return {"mcpServers": {name: servers[name]}}
220
+ return None
221
+
222
+
223
+ def _load_discovered_config(path: Path) -> dict | None:
224
+ """Load a discovered MCP config file and return its mcpServers dict."""
225
+ import json
226
+
227
+ try:
228
+ data = json.loads(path.read_text(encoding="utf-8"))
229
+ except (json.JSONDecodeError, OSError):
230
+ return None
231
+ servers = data.get("mcpServers", {})
232
+ if servers:
233
+ return {"mcpServers": servers}
234
+ return None
235
+
236
+
197
237
  def _goal_mcp(agents: list[str], name: str) -> _Result:
198
238
  """Goal 1: MCP comparison prompts."""
199
239
  agent = _prompt_agent(agents)
200
240
  model = _prompt_model()
201
241
 
202
- # Check if Sourcegraph is available in discovered MCP configs
203
242
  discovered = discover_mcp_configs()
204
- use_sourcegraph = False
205
243
 
206
- if _detect_sourcegraph_in_mcp(discovered):
244
+ # Auto-detect: if any MCP configs are discovered, let the user pick one
245
+ if discovered:
207
246
  click.echo()
208
- click.echo("Detected Sourcegraph MCP server in your configuration.")
209
- click.echo("codeprobe can use the HTTP endpoint for better performance.")
210
- use_sourcegraph = click.confirm("Use Sourcegraph HTTP MCP?", default=True)
211
- else:
247
+ click.echo("Discovered MCP configurations:")
248
+ for i, (p, servers) in enumerate(discovered, 1):
249
+ server_list = ", ".join(servers)
250
+ click.echo(f" {i}. {p} ({server_list})")
251
+ manual_idx = len(discovered) + 1
252
+ click.echo(f" {manual_idx}. Enter a Sourcegraph token manually")
212
253
  click.echo()
213
- click.echo("Would you like to use Sourcegraph as the MCP server?")
214
- use_sourcegraph = click.confirm("Use Sourcegraph?", default=False)
215
-
216
- if use_sourcegraph:
217
- token = _prompt_sourcegraph_token()
218
- sg_url = _prompt_sourcegraph_url()
219
- return ask_mcp_comparison(
220
- experiment_name=name,
221
- agent=agent,
222
- model=model,
223
- sourcegraph_token=token,
224
- sourcegraph_url=sg_url,
225
- )
226
254
 
227
- # Fall back to generic MCP config path
228
- mcp_path = _prompt_mcp_config()
255
+ choice = click.prompt(
256
+ "Select MCP config",
257
+ type=click.IntRange(1, manual_idx),
258
+ default=1,
259
+ )
260
+ if choice <= len(discovered):
261
+ path = discovered[choice - 1][0]
262
+ mcp_config = _load_discovered_config(path)
263
+ if mcp_config:
264
+ click.echo(f" Using {path}")
265
+ return ask_mcp_comparison(
266
+ experiment_name=name,
267
+ agent=agent,
268
+ model=model,
269
+ mcp_config=mcp_config,
270
+ )
271
+
272
+ # No discovered configs or user chose manual entry
273
+ token = _prompt_sourcegraph_token()
274
+ sg_url = _prompt_sourcegraph_url()
229
275
  return ask_mcp_comparison(
230
276
  experiment_name=name,
231
277
  agent=agent,
232
278
  model=model,
233
- mcp_config_path=mcp_path,
279
+ sourcegraph_token=token,
280
+ sourcegraph_url=sg_url,
234
281
  )
235
282
 
236
283
 
@@ -45,26 +45,35 @@ def ask_mcp_comparison(
45
45
  agent: str,
46
46
  model: str | None,
47
47
  mcp_config_path: str | None = None,
48
+ mcp_config: dict | None = None,
48
49
  sourcegraph_token: str | None = None,
49
50
  sourcegraph_url: str | None = None,
50
51
  ) -> tuple[EvalrcConfig, list[ExperimentConfig]]:
51
52
  """Goal 1: Compare baseline agent vs MCP-augmented agent.
52
53
 
53
- When *sourcegraph_token* is provided, generates an HTTP-based Sourcegraph
54
- MCP config with an ``Authorization`` header and adds the ``sourcegraph``
55
- preamble. Otherwise falls back to loading the MCP config from
56
- *mcp_config_path*.
54
+ Resolution order for MCP config:
55
+ 1. *mcp_config* pre-built dict (e.g. from discovered Claude Code config)
56
+ 2. *sourcegraph_token* build HTTP config with Authorization header
57
+ 3. *mcp_config_path* — load from a JSON file on disk
57
58
  """
58
- if sourcegraph_token is not None:
59
+ if mcp_config is not None:
60
+ mcp_data = mcp_config
61
+ # Detect if this is a Sourcegraph config for preamble
62
+ servers = mcp_data.get("mcpServers", {})
63
+ sg_names = {"sourcegraph", "sg", "sourcegraph-mcp"}
64
+ preambles: tuple[str, ...] = (
65
+ ("sourcegraph",) if any(k.lower() in sg_names for k in servers) else ()
66
+ )
67
+ elif sourcegraph_token is not None:
59
68
  mcp_data = build_sourcegraph_mcp_config(
60
69
  token=sourcegraph_token,
61
70
  url=sourcegraph_url or _DEFAULT_SOURCEGRAPH_URL,
62
71
  )
63
- preambles: tuple[str, ...] = ("sourcegraph",)
72
+ preambles = ("sourcegraph",)
64
73
  else:
65
74
  if mcp_config_path is None:
66
75
  raise click.BadParameter(
67
- "Either sourcegraph_token or mcp_config_path must be provided."
76
+ "Provide mcp_config, sourcegraph_token, or mcp_config_path."
68
77
  )
69
78
  mcp_data = _load_json(mcp_config_path)
70
79
  preambles = ()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeprobe
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: Benchmark AI coding agents against your own codebase. Mine real tasks from repo history, run agents, interpret results.
5
5
  Author: codeprobe contributors
6
6
  License-Expression: Apache-2.0
@@ -465,16 +465,22 @@ class TestInitCliIntegration:
465
465
  def test_goal1_mcp_flow(
466
466
  self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
467
467
  ) -> None:
468
+ """When a discovered config exists, selecting it skips token prompt."""
468
469
  mcp_file = tmp_path / "mcp.json"
469
- mcp_file.write_text(json.dumps({"mcpServers": {}}))
470
+ mcp_file.write_text(
471
+ json.dumps({"mcpServers": {"my-server": {"type": "stdio", "cmd": "echo"}}})
472
+ )
470
473
 
471
- # Patch discovery so the prompt falls through to manual path entry
472
- monkeypatch.setattr("codeprobe.cli.init_cmd.discover_mcp_configs", lambda: [])
474
+ # Patch discovery to return the test config file
475
+ monkeypatch.setattr(
476
+ "codeprobe.cli.init_cmd.discover_mcp_configs",
477
+ lambda: [(mcp_file, ["my-server"])],
478
+ )
473
479
 
474
480
  runner = CliRunner()
475
- # Inputs: goal=1, experiment name (enter=default), agent (enter=default),
476
- # model (enter=skip), decline Sourcegraph, mcp config path
477
- input_text = f"1\n\nclaude\n\nN\n{mcp_file}\n"
481
+ # Inputs: goal=1, name=default, agent=claude, model=skip,
482
+ # select=1 (the discovered config)
483
+ input_text = "1\n\nclaude\n\n1\n"
478
484
  result = runner.invoke(main, ["init", str(tmp_path)], input=input_text)
479
485
  assert result.exit_code == 0, result.output
480
486
  assert not (tmp_path / ".evalrc.yaml").exists()
@@ -489,8 +495,8 @@ class TestInitCliIntegration:
489
495
 
490
496
  runner = CliRunner()
491
497
  # Inputs: goal=1, name=default, agent=claude, model=skip,
492
- # use Sourcegraph=Y, choose=1 (paste token), token, url=default (enter)
493
- input_text = "1\n\nclaude\n\nY\n1\ntok_test123\n\n"
498
+ # choose=1 (PAT), choose=1 (paste now), token, url=default (enter)
499
+ input_text = "1\n\nclaude\n\n1\n1\ntok_test123\n\n"
494
500
  result = runner.invoke(main, ["init", str(tmp_path)], input=input_text)
495
501
  assert result.exit_code == 0, result.output
496
502
  assert not (tmp_path / ".evalrc.yaml").exists()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes