@oh-my-pi/pi-coding-agent 15.5.15 → 15.7.0

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 (274) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/types/capability/rule-buckets.d.ts +30 -0
  3. package/dist/types/capability/rule.d.ts +7 -0
  4. package/dist/types/cli/classify-install-target.d.ts +0 -10
  5. package/dist/types/cli/completion-gen.d.ts +80 -0
  6. package/dist/types/cli/initial-message.d.ts +1 -1
  7. package/dist/types/cli/tiny-models-cli.d.ts +9 -0
  8. package/dist/types/commands/complete.d.ts +6 -0
  9. package/dist/types/commands/completions.d.ts +13 -0
  10. package/dist/types/commands/setup.d.ts +10 -1
  11. package/dist/types/commands/tiny-models.d.ts +22 -0
  12. package/dist/types/commit/analysis/conventional.d.ts +1 -1
  13. package/dist/types/commit/analysis/summary.d.ts +1 -1
  14. package/dist/types/commit/changelog/generate.d.ts +1 -1
  15. package/dist/types/commit/changelog/index.d.ts +2 -2
  16. package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
  17. package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
  18. package/dist/types/config/model-id-affixes.d.ts +10 -0
  19. package/dist/types/config/settings-schema.d.ts +402 -17
  20. package/dist/types/discovery/builtin-defaults.d.ts +1 -0
  21. package/dist/types/discovery/builtin-rules/index.d.ts +7 -0
  22. package/dist/types/discovery/helpers.d.ts +1 -1
  23. package/dist/types/discovery/index.d.ts +1 -0
  24. package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
  25. package/dist/types/edit/hashline/block-resolver.d.ts +9 -0
  26. package/dist/types/edit/hashline/index.d.ts +1 -0
  27. package/dist/types/eval/js/shared/rewrite-imports.d.ts +16 -1
  28. package/dist/types/eval/py/kernel.d.ts +3 -0
  29. package/dist/types/eval/py/runtime.d.ts +11 -1
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/internal-urls/agent-protocol.d.ts +2 -1
  32. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -1
  33. package/dist/types/internal-urls/local-protocol.d.ts +2 -1
  34. package/dist/types/internal-urls/memory-protocol.d.ts +2 -1
  35. package/dist/types/internal-urls/omp-protocol.d.ts +2 -1
  36. package/dist/types/internal-urls/router.d.ts +8 -1
  37. package/dist/types/internal-urls/rule-protocol.d.ts +2 -1
  38. package/dist/types/internal-urls/skill-protocol.d.ts +2 -1
  39. package/dist/types/internal-urls/types.d.ts +26 -0
  40. package/dist/types/main.d.ts +1 -0
  41. package/dist/types/memory-backend/index.d.ts +1 -0
  42. package/dist/types/memory-backend/resolve.d.ts +2 -1
  43. package/dist/types/memory-backend/types.d.ts +7 -1
  44. package/dist/types/mnemosyne/backend.d.ts +4 -0
  45. package/dist/types/mnemosyne/config.d.ts +29 -0
  46. package/dist/types/mnemosyne/index.d.ts +3 -0
  47. package/dist/types/mnemosyne/state.d.ts +72 -0
  48. package/dist/types/modes/components/custom-editor.d.ts +2 -3
  49. package/dist/types/modes/components/hook-selector.d.ts +27 -0
  50. package/dist/types/modes/components/index.d.ts +2 -0
  51. package/dist/types/modes/components/segment-track.d.ts +22 -0
  52. package/dist/types/modes/components/status-line/context-thresholds.d.ts +6 -0
  53. package/dist/types/modes/components/tiny-title-download-progress.d.ts +11 -0
  54. package/dist/types/modes/components/welcome.d.ts +22 -0
  55. package/dist/types/modes/controllers/extension-ui-controller.d.ts +4 -1
  56. package/dist/types/modes/gradient-highlight.d.ts +23 -0
  57. package/dist/types/modes/interactive-mode.d.ts +7 -4
  58. package/dist/types/modes/internal-url-autocomplete.d.ts +43 -0
  59. package/dist/types/modes/orchestrate.d.ts +10 -0
  60. package/dist/types/modes/setup-wizard/index.d.ts +16 -0
  61. package/dist/types/modes/setup-wizard/scenes/glyph.d.ts +2 -0
  62. package/dist/types/modes/setup-wizard/scenes/outro.d.ts +2 -0
  63. package/dist/types/modes/setup-wizard/scenes/providers.d.ts +2 -0
  64. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +19 -0
  65. package/dist/types/modes/setup-wizard/scenes/splash.d.ts +11 -0
  66. package/dist/types/modes/setup-wizard/scenes/theme.d.ts +2 -0
  67. package/dist/types/modes/setup-wizard/scenes/types.d.ts +43 -0
  68. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +19 -0
  69. package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +14 -0
  70. package/dist/types/modes/theme/defaults/index.d.ts +8406 -8406
  71. package/dist/types/modes/theme/shimmer.d.ts +2 -0
  72. package/dist/types/modes/theme/theme.d.ts +11 -0
  73. package/dist/types/modes/types.d.ts +5 -1
  74. package/dist/types/modes/ultrathink.d.ts +3 -3
  75. package/dist/types/modes/utils/keybinding-matchers.d.ts +5 -0
  76. package/dist/types/sdk.d.ts +3 -0
  77. package/dist/types/session/agent-session.d.ts +33 -0
  78. package/dist/types/system-prompt.d.ts +2 -0
  79. package/dist/types/task/executor.d.ts +2 -0
  80. package/dist/types/task/render.d.ts +5 -1
  81. package/dist/types/tiny/device.d.ts +78 -0
  82. package/dist/types/tiny/dtype.d.ts +85 -0
  83. package/dist/types/tiny/models.d.ts +185 -0
  84. package/dist/types/tiny/text.d.ts +19 -0
  85. package/dist/types/tiny/title-client.d.ts +32 -0
  86. package/dist/types/tiny/title-protocol.d.ts +74 -0
  87. package/dist/types/tiny/worker.d.ts +2 -0
  88. package/dist/types/tools/bash.d.ts +3 -2
  89. package/dist/types/tools/eval.d.ts +1 -1
  90. package/dist/types/tools/index.d.ts +7 -4
  91. package/dist/types/tools/memory-edit.d.ts +40 -0
  92. package/dist/types/tools/{hindsight-recall.d.ts → memory-recall.d.ts} +6 -6
  93. package/dist/types/tools/{hindsight-reflect.d.ts → memory-reflect.d.ts} +6 -6
  94. package/dist/types/tools/memory-render.d.ts +60 -0
  95. package/dist/types/tools/{hindsight-retain.d.ts → memory-retain.d.ts} +6 -6
  96. package/dist/types/tools/todo-write.d.ts +8 -0
  97. package/dist/types/tools/tool-result.d.ts +2 -0
  98. package/dist/types/tui/code-cell.d.ts +2 -0
  99. package/dist/types/tui/output-block.d.ts +17 -0
  100. package/dist/types/utils/title-generator.d.ts +3 -0
  101. package/package.json +18 -14
  102. package/scripts/build-binary.ts +1 -0
  103. package/src/capability/rule-buckets.ts +64 -0
  104. package/src/capability/rule.ts +8 -0
  105. package/src/cli/completion-gen.ts +550 -0
  106. package/src/cli/setup-cli.ts +5 -3
  107. package/src/cli/tiny-models-cli.ts +127 -0
  108. package/src/cli-commands.ts +3 -0
  109. package/src/cli.ts +9 -15
  110. package/src/commands/complete.ts +66 -0
  111. package/src/commands/completions.ts +60 -0
  112. package/src/commands/setup.ts +29 -4
  113. package/src/commands/tiny-models.ts +36 -0
  114. package/src/config/model-equivalence.ts +43 -2
  115. package/src/config/model-id-affixes.ts +64 -0
  116. package/src/config/model-registry.ts +84 -10
  117. package/src/config/settings-schema.ts +275 -15
  118. package/src/discovery/builtin-defaults.ts +39 -0
  119. package/src/discovery/builtin-rules/index.ts +48 -0
  120. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  121. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  122. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  123. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  124. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  125. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  126. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  127. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  128. package/src/discovery/builtin-rules/ts-no-any.md +56 -0
  129. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  130. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  131. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +50 -0
  132. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  133. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  134. package/src/discovery/index.ts +1 -0
  135. package/src/edit/hashline/block-resolver.ts +14 -0
  136. package/src/edit/hashline/diff.ts +9 -8
  137. package/src/edit/hashline/execute.ts +2 -1
  138. package/src/edit/hashline/index.ts +1 -0
  139. package/src/eval/__tests__/shared-executors.test.ts +36 -0
  140. package/src/eval/js/shared/local-module-loader.ts +13 -1
  141. package/src/eval/js/shared/rewrite-imports.ts +31 -26
  142. package/src/eval/py/kernel.ts +37 -15
  143. package/src/eval/py/runtime.ts +57 -28
  144. package/src/export/html/template.generated.ts +1 -1
  145. package/src/export/html/template.js +0 -12
  146. package/src/export/ttsr.ts +2 -0
  147. package/src/internal-urls/agent-protocol.ts +18 -1
  148. package/src/internal-urls/artifact-protocol.ts +19 -1
  149. package/src/internal-urls/docs-index.generated.ts +8 -7
  150. package/src/internal-urls/local-protocol.ts +14 -1
  151. package/src/internal-urls/memory-protocol.ts +6 -1
  152. package/src/internal-urls/omp-protocol.ts +5 -1
  153. package/src/internal-urls/router.ts +20 -1
  154. package/src/internal-urls/rule-protocol.ts +8 -1
  155. package/src/internal-urls/skill-protocol.ts +8 -1
  156. package/src/internal-urls/types.ts +27 -0
  157. package/src/lsp/render.ts +1 -1
  158. package/src/main.ts +18 -1
  159. package/src/mcp/oauth-flow.ts +2 -2
  160. package/src/memory-backend/index.ts +1 -0
  161. package/src/memory-backend/resolve.ts +4 -1
  162. package/src/memory-backend/types.ts +8 -1
  163. package/src/mnemosyne/backend.ts +374 -0
  164. package/src/mnemosyne/config.ts +160 -0
  165. package/src/mnemosyne/index.ts +3 -0
  166. package/src/mnemosyne/state.ts +548 -0
  167. package/src/modes/acp/acp-agent.ts +11 -6
  168. package/src/modes/components/agent-dashboard.ts +4 -4
  169. package/src/modes/components/custom-editor.ts +3 -2
  170. package/src/modes/components/diff.ts +2 -2
  171. package/src/modes/components/extensions/extension-list.ts +3 -2
  172. package/src/modes/components/footer.ts +5 -6
  173. package/src/modes/components/history-search.ts +3 -3
  174. package/src/modes/components/hook-selector.ts +92 -8
  175. package/src/modes/components/index.ts +2 -0
  176. package/src/modes/components/mcp-add-wizard.ts +3 -3
  177. package/src/modes/components/model-selector.ts +5 -4
  178. package/src/modes/components/oauth-selector.ts +3 -3
  179. package/src/modes/components/segment-track.ts +52 -0
  180. package/src/modes/components/session-observer-overlay.ts +19 -13
  181. package/src/modes/components/session-selector.ts +3 -3
  182. package/src/modes/components/settings-defs.ts +7 -0
  183. package/src/modes/components/status-line/context-thresholds.ts +11 -0
  184. package/src/modes/components/status-line/segments.ts +2 -2
  185. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  186. package/src/modes/components/tips.txt +13 -0
  187. package/src/modes/components/tool-execution.ts +72 -4
  188. package/src/modes/components/tree-selector.ts +3 -3
  189. package/src/modes/components/user-message-selector.ts +3 -3
  190. package/src/modes/components/welcome.ts +102 -43
  191. package/src/modes/controllers/command-controller.ts +16 -1
  192. package/src/modes/controllers/extension-ui-controller.ts +3 -1
  193. package/src/modes/controllers/input-controller.ts +69 -21
  194. package/src/modes/gradient-highlight.ts +70 -0
  195. package/src/modes/interactive-mode.ts +75 -114
  196. package/src/modes/internal-url-autocomplete.ts +143 -0
  197. package/src/modes/orchestrate.ts +36 -0
  198. package/src/modes/prompt-action-autocomplete.ts +12 -0
  199. package/src/modes/setup-wizard/index.ts +88 -0
  200. package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
  201. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  202. package/src/modes/setup-wizard/scenes/providers.ts +69 -0
  203. package/src/modes/setup-wizard/scenes/sign-in.ts +193 -0
  204. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  205. package/src/modes/setup-wizard/scenes/theme.ts +299 -0
  206. package/src/modes/setup-wizard/scenes/types.ts +48 -0
  207. package/src/modes/setup-wizard/scenes/web-search.ts +128 -0
  208. package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
  209. package/src/modes/theme/shimmer.ts +5 -0
  210. package/src/modes/theme/theme.ts +44 -20
  211. package/src/modes/types.ts +6 -1
  212. package/src/modes/ultrathink.ts +9 -53
  213. package/src/modes/utils/keybinding-matchers.ts +11 -0
  214. package/src/prompts/system/memory-consolidation-system.md +8 -0
  215. package/src/prompts/system/memory-extraction-system.md +26 -0
  216. package/src/prompts/{commands/orchestrate.md → system/orchestrate-notice.md} +6 -17
  217. package/src/prompts/system/system-prompt.md +2 -0
  218. package/src/prompts/system/tiny-title-system.md +8 -0
  219. package/src/prompts/tools/memory-edit.md +8 -0
  220. package/src/prompts/tools/read.md +4 -0
  221. package/src/prompts/tools/task.md +4 -7
  222. package/src/sdk.ts +13 -21
  223. package/src/session/agent-session.ts +128 -44
  224. package/src/slash-commands/builtin-registry.ts +18 -1
  225. package/src/system-prompt.ts +4 -0
  226. package/src/task/commands.ts +1 -5
  227. package/src/task/executor.ts +8 -0
  228. package/src/task/index.ts +2 -0
  229. package/src/task/render.ts +69 -26
  230. package/src/tiny/device.ts +117 -0
  231. package/src/tiny/dtype.ts +101 -0
  232. package/src/tiny/models.ts +218 -0
  233. package/src/tiny/text.ts +54 -0
  234. package/src/tiny/title-client.ts +395 -0
  235. package/src/tiny/title-protocol.ts +51 -0
  236. package/src/tiny/worker.ts +587 -0
  237. package/src/tools/bash.ts +74 -29
  238. package/src/tools/browser/tab-worker.ts +1 -1
  239. package/src/tools/eval.ts +9 -4
  240. package/src/tools/index.ts +17 -22
  241. package/src/tools/memory-edit.ts +59 -0
  242. package/src/tools/memory-recall.ts +100 -0
  243. package/src/tools/memory-reflect.ts +88 -0
  244. package/src/tools/memory-render.ts +185 -0
  245. package/src/tools/memory-retain.ts +91 -0
  246. package/src/tools/read.ts +1 -0
  247. package/src/tools/renderers.ts +4 -2
  248. package/src/tools/todo-write.ts +128 -29
  249. package/src/tools/tool-result.ts +8 -0
  250. package/src/tui/code-cell.ts +6 -1
  251. package/src/tui/output-block.ts +199 -38
  252. package/src/utils/title-generator.ts +115 -13
  253. package/dist/types/tools/recipe/index.d.ts +0 -46
  254. package/dist/types/tools/recipe/render.d.ts +0 -36
  255. package/dist/types/tools/recipe/runner.d.ts +0 -60
  256. package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
  257. package/dist/types/tools/recipe/runners/index.d.ts +0 -2
  258. package/dist/types/tools/recipe/runners/just.d.ts +0 -2
  259. package/dist/types/tools/recipe/runners/make.d.ts +0 -2
  260. package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
  261. package/dist/types/tools/recipe/runners/task.d.ts +0 -2
  262. package/src/prompts/tools/recipe.md +0 -16
  263. package/src/tools/hindsight-recall.ts +0 -69
  264. package/src/tools/hindsight-reflect.ts +0 -58
  265. package/src/tools/hindsight-retain.ts +0 -57
  266. package/src/tools/recipe/index.ts +0 -81
  267. package/src/tools/recipe/render.ts +0 -19
  268. package/src/tools/recipe/runner.ts +0 -219
  269. package/src/tools/recipe/runners/cargo.ts +0 -131
  270. package/src/tools/recipe/runners/index.ts +0 -8
  271. package/src/tools/recipe/runners/just.ts +0 -73
  272. package/src/tools/recipe/runners/make.ts +0 -101
  273. package/src/tools/recipe/runners/pkg.ts +0 -167
  274. package/src/tools/recipe/runners/task.ts +0 -72
@@ -17,7 +17,7 @@ import { Settings } from "../../config/settings";
17
17
  import { type KernelDisplayOutput, renderKernelDisplay } from "./display";
18
18
  import { PYTHON_PRELUDE } from "./prelude";
19
19
  import RUNNER_SCRIPT from "./runner.py" with { type: "text" };
20
- import { filterEnv, resolvePythonRuntime } from "./runtime";
20
+ import { enumeratePythonRuntimes, filterEnv, type PythonRuntime, resolvePythonRuntime } from "./runtime";
21
21
 
22
22
  export type { KernelDisplayOutput, PythonStatusEvent } from "./display";
23
23
  export { renderKernelDisplay } from "./display";
@@ -106,6 +106,8 @@ export interface PythonKernelAvailability {
106
106
  ok: boolean;
107
107
  pythonPath?: string;
108
108
  reason?: string;
109
+ /** The probed-working runtime, when one was found. */
110
+ runtime?: PythonRuntime;
109
111
  }
110
112
 
111
113
  function getRemainingTimeMs(deadlineMs?: number): number | undefined {
@@ -134,19 +136,34 @@ export async function checkPythonKernelAvailability(cwd: string): Promise<Python
134
136
  const settings = await Settings.init();
135
137
  const { env } = settings.getShellConfig();
136
138
  const baseEnv = filterEnv(env);
137
- const runtime = resolvePythonRuntime(cwd, baseEnv);
138
- const probe = await $`${runtime.pythonPath} -c "import sys;sys.exit(0)"`
139
- .quiet()
140
- .nothrow()
141
- .cwd(cwd)
142
- .env(runtime.env);
143
- if (probe.exitCode === 0) {
144
- return { ok: true, pythonPath: runtime.pythonPath };
139
+ const runtimes = enumeratePythonRuntimes(cwd, baseEnv);
140
+ if (runtimes.length === 0) {
141
+ return { ok: false, reason: "Python executable not found on PATH" };
142
+ }
143
+ // Probe each candidate in priority order and use the first that actually
144
+ // runs. A managed env left behind by a removed `uv` install can exist on
145
+ // disk yet fail to execute; falling through to the next candidate lets a
146
+ // working system Python take over instead of failing the whole session.
147
+ const failures: string[] = [];
148
+ for (const runtime of runtimes) {
149
+ try {
150
+ const probe = await $`${runtime.pythonPath} -c "import sys;sys.exit(0)"`
151
+ .quiet()
152
+ .nothrow()
153
+ .cwd(cwd)
154
+ .env(runtime.env);
155
+ if (probe.exitCode === 0) {
156
+ return { ok: true, pythonPath: runtime.pythonPath, runtime };
157
+ }
158
+ failures.push(`${runtime.pythonPath} (exit code ${probe.exitCode})`);
159
+ } catch (err) {
160
+ failures.push(`${runtime.pythonPath} (${err instanceof Error ? err.message : String(err)})`);
161
+ }
145
162
  }
146
163
  return {
147
164
  ok: false,
148
- pythonPath: runtime.pythonPath,
149
- reason: `Python interpreter at ${runtime.pythonPath} returned exit code ${probe.exitCode}`,
165
+ pythonPath: runtimes[0].pythonPath,
166
+ reason: `No working Python interpreter found. Tried: ${failures.join("; ")}`,
150
167
  };
151
168
  } catch (err) {
152
169
  return { ok: false, reason: err instanceof Error ? err.message : String(err) };
@@ -207,10 +224,15 @@ export class PythonKernel {
207
224
  throw new Error(availability.reason ?? "Python kernel unavailable");
208
225
  }
209
226
 
210
- const settings = await Settings.init();
211
- const { env: shellEnv } = settings.getShellConfig();
212
- const baseEnv = filterEnv(shellEnv);
213
- const runtime = resolvePythonRuntime(options.cwd, baseEnv);
227
+ // Reuse the interpreter the availability probe selected so the spawned
228
+ // kernel matches what we verified actually runs. The fallback computes a
229
+ // runtime only for the skip-check fast path (test runtime /
230
+ // PI_PYTHON_SKIP_CHECK), where no candidate was probed.
231
+ let runtime = availability.runtime;
232
+ if (!runtime) {
233
+ const { env: shellEnv } = (await Settings.init()).getShellConfig();
234
+ runtime = resolvePythonRuntime(options.cwd, filterEnv(shellEnv));
235
+ }
214
236
  const spawnEnv: Record<string, string> = {};
215
237
  for (const [key, value] of Object.entries(runtime.env)) {
216
238
  if (typeof value === "string") spawnEnv[key] = value;
@@ -162,49 +162,78 @@ export function resolveVenvPath(cwd: string): string | undefined {
162
162
  }
163
163
 
164
164
  /**
165
- * Resolve Python runtime including executable path, environment, and venv detection.
165
+ * Apply a venv-style PATH/VIRTUAL_ENV layout onto a fresh copy of `baseEnv` for
166
+ * the interpreter living in `binDir`.
166
167
  */
167
- export function resolvePythonRuntime(cwd: string, baseEnv: Record<string, string | undefined>): PythonRuntime {
168
+ function applyVenvEnv(
169
+ baseEnv: Record<string, string | undefined>,
170
+ venvPath: string,
171
+ binDir: string,
172
+ ): Record<string, string | undefined> {
168
173
  const env = { ...baseEnv };
169
- const venvPath = env.VIRTUAL_ENV ?? resolveVenvPath(cwd);
174
+ env.VIRTUAL_ENV = venvPath;
175
+ const pathKey = resolvePathKey(env);
176
+ const currentPath = env[pathKey];
177
+ env[pathKey] = currentPath ? `${binDir}${path.delimiter}${currentPath}` : binDir;
178
+ return env;
179
+ }
180
+
181
+ function venvBinDir(venvPath: string): string {
182
+ return process.platform === "win32" ? path.join(venvPath, "Scripts") : path.join(venvPath, "bin");
183
+ }
184
+
185
+ /**
186
+ * Enumerate candidate Python runtimes in priority order: an active/project venv,
187
+ * the managed `~/.omp/python-env`, then the system interpreter on PATH. Every
188
+ * candidate that physically exists is returned so callers can probe each in turn
189
+ * rather than committing to the first — a managed env left behind by a removed
190
+ * `uv` install no longer shadows a working system Python.
191
+ */
192
+ export function enumeratePythonRuntimes(cwd: string, baseEnv: Record<string, string | undefined>): PythonRuntime[] {
193
+ const runtimes: PythonRuntime[] = [];
194
+ const seen = new Set<string>();
195
+ const push = (runtime: PythonRuntime): void => {
196
+ if (seen.has(runtime.pythonPath)) return;
197
+ seen.add(runtime.pythonPath);
198
+ runtimes.push(runtime);
199
+ };
170
200
 
201
+ const venvPath = baseEnv.VIRTUAL_ENV ?? resolveVenvPath(cwd);
171
202
  if (venvPath) {
172
- env.VIRTUAL_ENV = venvPath;
173
- const binDir = process.platform === "win32" ? path.join(venvPath, "Scripts") : path.join(venvPath, "bin");
203
+ const binDir = venvBinDir(venvPath);
174
204
  const pythonCandidate = path.join(binDir, process.platform === "win32" ? "python.exe" : "python");
175
205
  if (fs.existsSync(pythonCandidate)) {
176
- const pathKey = resolvePathKey(env);
177
- const currentPath = env[pathKey];
178
- env[pathKey] = currentPath ? `${binDir}${path.delimiter}${currentPath}` : binDir;
179
- return {
180
- pythonPath: pythonCandidate,
181
- env,
182
- venvPath,
183
- };
206
+ push({ pythonPath: pythonCandidate, env: applyVenvEnv(baseEnv, venvPath, binDir), venvPath });
184
207
  }
185
208
  }
186
209
 
187
210
  const managed = resolveManagedPythonCandidate();
188
211
  if (fs.existsSync(managed.pythonPath)) {
189
- env.VIRTUAL_ENV = managed.venvPath;
190
- const pathKey = resolvePathKey(env);
191
- const currentPath = env[pathKey];
192
- const managedBin =
193
- process.platform === "win32" ? path.join(managed.venvPath, "Scripts") : path.join(managed.venvPath, "bin");
194
- env[pathKey] = currentPath ? `${managedBin}${path.delimiter}${currentPath}` : managedBin;
195
- return {
212
+ const managedBin = path.dirname(managed.pythonPath);
213
+ push({
196
214
  pythonPath: managed.pythonPath,
197
- env,
215
+ env: applyVenvEnv(baseEnv, managed.venvPath, managedBin),
198
216
  venvPath: managed.venvPath,
199
- };
217
+ });
200
218
  }
201
219
 
202
- const pythonPath = $which("python") ?? $which("python3");
203
- if (!pythonPath) {
220
+ const systemPath = $which("python") ?? $which("python3");
221
+ if (systemPath) {
222
+ push({ pythonPath: systemPath, env: { ...baseEnv } });
223
+ }
224
+
225
+ return runtimes;
226
+ }
227
+
228
+ /**
229
+ * Resolve the highest-priority Python runtime. Prefer {@link enumeratePythonRuntimes}
230
+ * when you can probe candidates; this returns only the first one and throws when
231
+ * no interpreter exists.
232
+ */
233
+ export function resolvePythonRuntime(cwd: string, baseEnv: Record<string, string | undefined>): PythonRuntime {
234
+ const [runtime] = enumeratePythonRuntimes(cwd, baseEnv);
235
+ if (!runtime) {
204
236
  throw new Error("Python executable not found on PATH");
205
237
  }
206
- return {
207
- pythonPath,
208
- env,
209
- };
238
+ return runtime;
210
239
  }