()
| 186 | } |
| 187 | |
| 188 | function resolvePythonExecutable(): string { |
| 189 | const explicitCandidates = [ |
| 190 | process.env.NCODE_PY_REPL_PYTHON_PATH, |
| 191 | process.env.CLAUDE_CODE_PY_REPL_PYTHON_PATH, |
| 192 | ].filter((candidate): candidate is string => typeof candidate === 'string' && candidate.trim() !== '') |
| 193 | |
| 194 | const candidates = explicitCandidates.length > 0 |
| 195 | ? explicitCandidates |
| 196 | : ['python3', 'python'] |
| 197 | |
| 198 | for (const candidate of candidates) { |
| 199 | const result = spawnSync( |
| 200 | candidate, |
| 201 | [ |
| 202 | '-c', |
| 203 | 'import sys; print(".".join(str(part) for part in sys.version_info[:3]))', |
| 204 | ], |
| 205 | { |
| 206 | encoding: 'utf8', |
| 207 | stdio: ['ignore', 'pipe', 'pipe'], |
| 208 | }, |
| 209 | ) |
| 210 | if (result.status !== 0) { |
| 211 | continue |
| 212 | } |
| 213 | |
| 214 | const version = parsePythonVersion(result.stdout) |
| 215 | if (!version) { |
| 216 | continue |
| 217 | } |
| 218 | |
| 219 | if (compareVersion(version, PYTHON_MIN_VERSION) >= 0) { |
| 220 | return candidate |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | const requested = explicitCandidates.length > 0 |
| 225 | ? explicitCandidates.join(', ') |
| 226 | : 'python3, python' |
| 227 | throw new Error( |
| 228 | `py_repl requires Python ${PYTHON_MIN_VERSION.join('.')}+; none of the configured runtimes are usable (${requested})`, |
| 229 | ) |
| 230 | } |
| 231 | |
| 232 | function augmentToolsForPythonRepl(toolUseContext: ToolUseContext): Tool[] { |
| 233 | const byName = new Map<string, Tool>() |
no test coverage detected