codeprobe 0.3.3__tar.gz → 0.3.5__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 (172) hide show
  1. {codeprobe-0.3.3 → codeprobe-0.3.5}/PKG-INFO +1 -1
  2. {codeprobe-0.3.3 → codeprobe-0.3.5}/pyproject.toml +1 -1
  3. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/_base.py +1 -1
  4. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/claude.py +20 -10
  5. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe.egg-info/PKG-INFO +1 -1
  6. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_adapters.py +46 -13
  7. {codeprobe-0.3.3 → codeprobe-0.3.5}/LICENSE +0 -0
  8. {codeprobe-0.3.3 → codeprobe-0.3.5}/README.md +0 -0
  9. {codeprobe-0.3.3 → codeprobe-0.3.5}/setup.cfg +0 -0
  10. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/__init__.py +0 -0
  11. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/__main__.py +0 -0
  12. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/__init__.py +0 -0
  13. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/codex.py +0 -0
  14. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/copilot.py +0 -0
  15. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/openai_compat.py +0 -0
  16. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/protocol.py +0 -0
  17. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/session.py +0 -0
  18. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/adapters/telemetry.py +0 -0
  19. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/analysis/__init__.py +0 -0
  20. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/analysis/ranking.py +0 -0
  21. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/analysis/report.py +0 -0
  22. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/analysis/stats.py +0 -0
  23. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/api.py +0 -0
  24. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/assess/__init__.py +0 -0
  25. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/assess/heuristics.py +0 -0
  26. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/__init__.py +0 -0
  27. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/assess_cmd.py +0 -0
  28. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/doctor_cmd.py +0 -0
  29. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/experiment_cmd.py +0 -0
  30. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/init_cmd.py +0 -0
  31. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/interpret_cmd.py +0 -0
  32. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/json_display.py +0 -0
  33. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/mine_cmd.py +0 -0
  34. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/preamble_cmd.py +0 -0
  35. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/probe_cmd.py +0 -0
  36. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/ratings_cmd.py +0 -0
  37. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/rich_display.py +0 -0
  38. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/run_cmd.py +0 -0
  39. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/scaffold_cmd.py +0 -0
  40. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/validate_cmd.py +0 -0
  41. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/wizard.py +0 -0
  42. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/cli/yaml_writer.py +0 -0
  43. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/config/__init__.py +0 -0
  44. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/config/loader.py +0 -0
  45. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/__init__.py +0 -0
  46. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/_shared.py +0 -0
  47. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/adaptive.py +0 -0
  48. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/counterfactual.py +0 -0
  49. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/debate.py +0 -0
  50. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/decision_tree.py +0 -0
  51. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/elo.py +0 -0
  52. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/fingerprint.py +0 -0
  53. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/mutation.py +0 -0
  54. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/pareto.py +0 -0
  55. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/sprt.py +0 -0
  56. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/contrib/tournament.py +0 -0
  57. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/__init__.py +0 -0
  58. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/__main__.py +0 -0
  59. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/checkpoint.py +0 -0
  60. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/events.py +0 -0
  61. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/executor.py +0 -0
  62. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/experiment.py +0 -0
  63. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/isolation.py +0 -0
  64. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/llm.py +0 -0
  65. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/mcp_discovery.py +0 -0
  66. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/preamble.py +0 -0
  67. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/registry.py +0 -0
  68. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/sandbox.py +0 -0
  69. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/core/scoring.py +0 -0
  70. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/loaders/__init__.py +0 -0
  71. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/loaders/suite.py +0 -0
  72. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/__init__.py +0 -0
  73. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/_graph.py +0 -0
  74. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/_lang.py +0 -0
  75. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/comprehension.py +0 -0
  76. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/comprehension_writer.py +0 -0
  77. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/curator.py +0 -0
  78. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/curator_backends.py +0 -0
  79. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/curator_tiers.py +0 -0
  80. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/extractor.py +0 -0
  81. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/org_scale.py +0 -0
  82. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/org_scale_families.py +0 -0
  83. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/org_scale_oracle.py +0 -0
  84. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/org_scale_scanner.py +0 -0
  85. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/org_scale_validate.py +0 -0
  86. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/sg_ground_truth.py +0 -0
  87. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/sources.py +0 -0
  88. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/mining/writer.py +0 -0
  89. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/models/__init__.py +0 -0
  90. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/models/evalrc.py +0 -0
  91. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/models/experiment.py +0 -0
  92. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/models/preamble.py +0 -0
  93. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/models/suite.py +0 -0
  94. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/models/task.py +0 -0
  95. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/preambles/__init__.py +0 -0
  96. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/preambles/github.md +0 -0
  97. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/preambles/sourcegraph.md +0 -0
  98. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/probe/__init__.py +0 -0
  99. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/probe/adapter.py +0 -0
  100. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/probe/generator.py +0 -0
  101. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/probe/writer.py +0 -0
  102. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/ratings/__init__.py +0 -0
  103. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/ratings/collector.py +0 -0
  104. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/scaffold/__init__.py +0 -0
  105. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/scaffold/writer.py +0 -0
  106. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/templates/__init__.py +0 -0
  107. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/templates/evalrc-mcp-comparison.yaml +0 -0
  108. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/templates/evalrc-model-comparison.yaml +0 -0
  109. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe/templates/evalrc-prompt-comparison.yaml +0 -0
  110. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe.egg-info/SOURCES.txt +0 -0
  111. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe.egg-info/dependency_links.txt +0 -0
  112. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe.egg-info/entry_points.txt +0 -0
  113. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe.egg-info/requires.txt +0 -0
  114. {codeprobe-0.3.3 → codeprobe-0.3.5}/src/codeprobe.egg-info/top_level.txt +0 -0
  115. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_adapter_contracts.py +0 -0
  116. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_analysis.py +0 -0
  117. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_api.py +0 -0
  118. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_artifact_scorer.py +0 -0
  119. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_assess.py +0 -0
  120. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_changed_symbols.py +0 -0
  121. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_checkpoint.py +0 -0
  122. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_checkpoint_scoring.py +0 -0
  123. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_cli.py +0 -0
  124. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_comprehension.py +0 -0
  125. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_config_loader.py +0 -0
  126. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_contrib.py +0 -0
  127. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_ctrlc_integration.py +0 -0
  128. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_curator_backends.py +0 -0
  129. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_curator_core.py +0 -0
  130. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_curator_integration.py +0 -0
  131. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_curator_tiers.py +0 -0
  132. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_doctor_cmd.py +0 -0
  133. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_events.py +0 -0
  134. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_executor.py +0 -0
  135. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_executor_events.py +0 -0
  136. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_experiment_cmd.py +0 -0
  137. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_experiment_core.py +0 -0
  138. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_init_wizard.py +0 -0
  139. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_json_display.py +0 -0
  140. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_llm.py +0 -0
  141. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_loaders.py +0 -0
  142. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_mcp_families_mining.py +0 -0
  143. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_mcp_validate.py +0 -0
  144. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_mine_goals.py +0 -0
  145. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_mine_presets.py +0 -0
  146. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_mine_profiles.py +0 -0
  147. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_mining.py +0 -0
  148. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_models.py +0 -0
  149. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_new_families.py +0 -0
  150. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_openai_compat.py +0 -0
  151. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_oracle_types.py +0 -0
  152. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_org_scale.py +0 -0
  153. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_pipeline_integration.py +0 -0
  154. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_preamble.py +0 -0
  155. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_preamble_cmd.py +0 -0
  156. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_probe.py +0 -0
  157. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_probe_adapter.py +0 -0
  158. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_ratings.py +0 -0
  159. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_ratings_cmd.py +0 -0
  160. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_registry.py +0 -0
  161. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_run_config_resolution.py +0 -0
  162. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_scaffold.py +0 -0
  163. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_scanner_refactor.py +0 -0
  164. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_scoring.py +0 -0
  165. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_session.py +0 -0
  166. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_sg_ground_truth.py +0 -0
  167. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_shell_shim.py +0 -0
  168. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_show_prompt.py +0 -0
  169. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_suite.py +0 -0
  170. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_telemetry.py +0 -0
  171. {codeprobe-0.3.3 → codeprobe-0.3.5}/tests/test_validate_cmd.py +0 -0
  172. {codeprobe-0.3.3 → codeprobe-0.3.5}/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.3
3
+ Version: 0.3.5
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.3"
3
+ version = "0.3.5"
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"
@@ -158,7 +158,7 @@ class BaseAdapter:
158
158
  text=True,
159
159
  timeout=config.timeout_seconds,
160
160
  cwd=config.cwd,
161
- env=_adapter_safe_env(session_env),
161
+ env=_adapter_safe_env(session_env) if session_env else None,
162
162
  )
163
163
  except subprocess.TimeoutExpired as exc:
164
164
  duration = time.monotonic() - start
@@ -78,22 +78,32 @@ class ClaudeAdapter(BaseAdapter):
78
78
  """Return a per-slot CLAUDE_CONFIG_DIR for session isolation.
79
79
 
80
80
  Copies authentication credentials from the real ``~/.claude/``
81
- directory so the agent subprocess can authenticate.
81
+ directory so the agent subprocess can authenticate. If no credential
82
+ files are found (e.g. auth lives in the system keychain), skips
83
+ isolation and lets the agent use the real config dir.
82
84
  """
85
+ real_config = Path.home() / ".claude"
86
+ _CRED_NAMES = ("credentials.json", ".credentials.json")
87
+
88
+ # Check whether any copyable credential files exist.
89
+ has_creds = real_config.is_dir() and any(
90
+ (real_config / name).is_file() for name in _CRED_NAMES
91
+ )
92
+ if not has_creds:
93
+ # No file-based credentials found — don't override config dir
94
+ # so the CLI can use keychain / default auth.
95
+ return {}
96
+
83
97
  config_dir = (
84
98
  Path(tempfile.gettempdir()) / "codeprobe-claude" / f"slot-{slot_id}"
85
99
  )
86
100
  config_dir.mkdir(parents=True, exist_ok=True)
87
101
 
88
- # Copy auth credentials from the user's real config dir.
89
- # Without these the subprocess gets "Not logged in".
90
- real_config = Path.home() / ".claude"
91
- if real_config.is_dir():
92
- for name in ("credentials.json", ".credentials.json"):
93
- src = real_config / name
94
- dst = config_dir / name
95
- if src.is_file():
96
- shutil.copy2(src, dst)
102
+ for name in _CRED_NAMES:
103
+ src = real_config / name
104
+ dst = config_dir / name
105
+ if src.is_file():
106
+ shutil.copy2(src, dst)
97
107
 
98
108
  return {"CLAUDE_CONFIG_DIR": str(config_dir)}
99
109
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeprobe
3
- Version: 0.3.3
3
+ Version: 0.3.5
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
@@ -274,9 +274,10 @@ class _StubAdapter(BaseAdapter):
274
274
 
275
275
 
276
276
  class TestBaseAdapterEnvWhitelist:
277
- """Verify subprocess.run() uses a filtered environment, not full parent env."""
277
+ """Verify subprocess.run() env handling: inherit when no isolation, filter when isolated."""
278
278
 
279
- def test_subprocess_receives_explicit_env(self) -> None:
279
+ def test_inherits_full_env_without_session_env(self) -> None:
280
+ """Without session isolation, subprocess inherits the full parent env."""
280
281
  adapter = _StubAdapter()
281
282
  config = AgentConfig(timeout_seconds=5)
282
283
  fake_result = subprocess.CompletedProcess(
@@ -286,22 +287,36 @@ class TestBaseAdapterEnvWhitelist:
286
287
  adapter.run("test", config)
287
288
 
288
289
  _, kwargs = mock_run.call_args
289
- assert "env" in kwargs, "subprocess.run must receive explicit env"
290
- env = kwargs["env"]
290
+ assert kwargs.get("env") is None, "env=None inherits parent process env"
291
+
292
+ def test_filters_env_with_session_env(self) -> None:
293
+ """With session isolation, subprocess gets a filtered env."""
294
+ adapter = _StubAdapter()
295
+ config = AgentConfig(timeout_seconds=5)
296
+ fake_result = subprocess.CompletedProcess(
297
+ args=["fake-agent"], returncode=0, stdout="ok", stderr=""
298
+ )
299
+ session_env = {"CLAUDE_CONFIG_DIR": "/tmp/test"}
300
+ with patch("subprocess.run", return_value=fake_result) as mock_run:
301
+ adapter.run("test", config, session_env=session_env)
302
+
303
+ env = mock_run.call_args[1]["env"]
291
304
  assert isinstance(env, dict)
305
+ assert env.get("CLAUDE_CONFIG_DIR") == "/tmp/test"
292
306
 
293
- def test_env_includes_path_and_home(self) -> None:
307
+ def test_filtered_env_includes_path_and_home(self) -> None:
294
308
  adapter = _StubAdapter()
295
309
  config = AgentConfig(timeout_seconds=5)
296
310
  fake_result = subprocess.CompletedProcess(
297
311
  args=["fake-agent"], returncode=0, stdout="ok", stderr=""
298
312
  )
313
+ session_env = {"CLAUDE_CONFIG_DIR": "/tmp/test"}
299
314
  with patch("subprocess.run", return_value=fake_result) as mock_run:
300
315
  import os
301
316
 
302
317
  old_path = os.environ.get("PATH", "")
303
318
  old_home = os.environ.get("HOME", "")
304
- adapter.run("test", config)
319
+ adapter.run("test", config, session_env=session_env)
305
320
 
306
321
  env = mock_run.call_args[1]["env"]
307
322
  if old_path:
@@ -309,18 +324,19 @@ class TestBaseAdapterEnvWhitelist:
309
324
  if old_home:
310
325
  assert env.get("HOME") == old_home
311
326
 
312
- def test_env_excludes_random_secrets(self) -> None:
327
+ def test_filtered_env_excludes_random_secrets(self) -> None:
313
328
  adapter = _StubAdapter()
314
329
  config = AgentConfig(timeout_seconds=5)
315
330
  fake_result = subprocess.CompletedProcess(
316
331
  args=["fake-agent"], returncode=0, stdout="ok", stderr=""
317
332
  )
333
+ session_env = {"CLAUDE_CONFIG_DIR": "/tmp/test"}
318
334
  import os
319
335
 
320
336
  os.environ["MY_SUPER_SECRET_DB_PASSWORD"] = "hunter2"
321
337
  try:
322
338
  with patch("subprocess.run", return_value=fake_result) as mock_run:
323
- adapter.run("test", config)
339
+ adapter.run("test", config, session_env=session_env)
324
340
  env = mock_run.call_args[1]["env"]
325
341
  assert "MY_SUPER_SECRET_DB_PASSWORD" not in env
326
342
  finally:
@@ -1146,18 +1162,35 @@ class TestClaudeMcpConfig:
1146
1162
 
1147
1163
 
1148
1164
  class TestIsolateSession:
1149
- def test_claude_isolate_session_returns_config_dir(self) -> None:
1165
+ def test_claude_isolate_session_returns_config_dir(self, tmp_path: Path) -> None:
1150
1166
  adapter = ClaudeAdapter()
1151
- env = adapter.isolate_session(0)
1167
+ fake_home = tmp_path / "home"
1168
+ (fake_home / ".claude").mkdir(parents=True)
1169
+ (fake_home / ".claude" / ".credentials.json").write_text("{}")
1170
+ with patch.object(Path, "home", return_value=fake_home):
1171
+ env = adapter.isolate_session(0)
1152
1172
  assert "CLAUDE_CONFIG_DIR" in env
1153
1173
  assert "slot-0" in env["CLAUDE_CONFIG_DIR"]
1154
1174
 
1155
- def test_claude_isolate_session_different_slots(self) -> None:
1175
+ def test_claude_isolate_session_different_slots(self, tmp_path: Path) -> None:
1156
1176
  adapter = ClaudeAdapter()
1157
- env0 = adapter.isolate_session(0)
1158
- env1 = adapter.isolate_session(1)
1177
+ fake_home = tmp_path / "home"
1178
+ (fake_home / ".claude").mkdir(parents=True)
1179
+ (fake_home / ".claude" / ".credentials.json").write_text("{}")
1180
+ with patch.object(Path, "home", return_value=fake_home):
1181
+ env0 = adapter.isolate_session(0)
1182
+ env1 = adapter.isolate_session(1)
1159
1183
  assert env0["CLAUDE_CONFIG_DIR"] != env1["CLAUDE_CONFIG_DIR"]
1160
1184
 
1185
+ def test_claude_isolate_session_skips_when_no_creds(self, tmp_path: Path) -> None:
1186
+ """When no credential files exist, returns empty dict."""
1187
+ adapter = ClaudeAdapter()
1188
+ fake_home = tmp_path / "home"
1189
+ (fake_home / ".claude").mkdir(parents=True)
1190
+ with patch.object(Path, "home", return_value=fake_home):
1191
+ env = adapter.isolate_session(0)
1192
+ assert env == {}
1193
+
1161
1194
  def test_base_adapter_isolate_session_returns_empty(self) -> None:
1162
1195
  adapter = _StubAdapter()
1163
1196
  env = adapter.isolate_session(42)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes