2b54b7e79d
Closes the "code_execution: program not found" failure reported by a
Windows contributor and adds the surrounding plumbing so future
external-binary dependencies can be added without each one repeating
the same probe-and-skip logic.
New module `crates/tui/src/dependencies.rs`:
* `PYTHON_CANDIDATES = ["python3", "python", "py -3"]` — try the
Unix-style name first, then the bare name (common on Windows and
modern macOS), then the Windows launcher as a last resort.
* `probe_executable(spec)` — splits the spec on whitespace, spawns
`<program> [args...] --version`, returns true on success. stdout
and stderr are routed to /dev/null so the probe doesn't print to
the TUI's first frame.
* `resolve_python_interpreter()` — OnceLock-cached resolver that
returns the first candidate that probed successfully, or None.
* `resolve_pdftotext()` — same shape, for doctor's PDF-path
diagnostic.
* `split_interpreter_spec("py -3") → ("py", ["-3"])` — needed so
`tokio::process::Command::new(program).args(args).arg(script)`
runs the Windows launcher correctly.
Wiring in `core/engine/tool_catalog.rs::ensure_advanced_tooling`:
* The hardcoded `catalog.push(Tool { name: "code_execution", … })`
is now gated on `resolve_python_interpreter().is_some()`. On a
machine without Python the tool is not advertised — the model
never sees a tool it can't actually run, which removes the
"Failed to execute tool: program not found" class of failure
entirely. On a machine WITH Python the behavior is unchanged
from the user's perspective.
Wiring in `execute_code_execution_tool`:
* Resolves the interpreter at call time (still cached) and splits
on whitespace so `py -3` runs as `py -3 /tmp/.../code.py`.
* Writes the user code to a tempfile under tempdir() and runs the
file rather than passing it through `python3 -c "..."`. Fixes
argv length limits on Windows, multiline code with quote
nesting, and traceback line numbers (real filename instead of
`<string>`). Folds in the contributor's Part 2 proposal.
* Returns a clear actionable error if the interpreter somehow
disappears between startup and the call (uninstall mid-session,
PATH manipulation, etc.).
Doctor (`crates/tui/src/main.rs::run_doctor`) gains two new sections:
* "Tool Dependencies": ✓/✗ for Python and pdftotext with a
platform-specific install hint when missing. The Python miss
explicitly tells users that `code_execution` is not advertised.
* "Terminal Quirks": shows which env-driven auto-overrides are
currently active (VS Code / Ghostty low-motion, Termius/SSH
low-motion per #1433, Ptyxis sync-output-off per v0.8.31).
Answers the "why is my flicker / motion behaving this way"
question without needing the user to read the source.
The contributor's Part 1 proposal (share the sandbox/approval
substrate with task_shell_start) is deferred to v0.8.32 — that
touches `sandbox/` which CLAUDE.md flags as "stop and ask before
changing" and needs an explicit design pass on approval semantics.
Six new unit tests in `dependencies.rs` covering probe behavior,
spec splitting, and cache stability. Existing tool_catalog tests
still pass.