Finishes the WIP that landed unfinished in fe47c901. The `PromptMode`
+ `find_binary` + `scrubbed_child_env` helpers all now flow through
`SubprocessProvider::send`:
- **PromptMode::PositionalArg** (Claude Code today, since `--print`
mode expects `-- <prompt>` argv and an empty stdin): the
user_message is appended to argv after `--`; stdin is closed
immediately without writing.
- **PromptMode::Stdin** (Gemini / Copilot / generic): argv passed
verbatim; user_message written to stdin then closed.
- Per-CLI templates in `for_cli` now select the right mode +
resolve the binary via `find_binary` (PATH probe + Windows
PATHEXT fallback + per-OS npm / yarn / bun / volta / homebrew
install locations from bartolli/anthropic-agent-sdk's
`find_cli`). Claude Code defaults bumped to
`--print --verbose --output-format stream-json` (the `--verbose`
flag was missing — Claude Code's stream-json output omits
granular event detail without it).
- `scrubbed_child_env()` builds the child env from the parent env
minus LD_PRELOAD / DYLD_INSERT_LIBRARIES / NODE_OPTIONS /
PYTHONPATH / PERL5LIB / RUBYLIB / LD_LIBRARY_PATH /
DYLD_LIBRARY_PATH so the spawned CLI can't be hijacked by
interposition vars left in the parent shell. `env_clear()` first
so tokio Command's default-inherit is overridden.
- Test assertions on `.binary` loosened to `.ends_with("claude")` /
`.ends_with("gemini")` etc. — `find_binary` returns an absolute
path when one of the well-known locations matches, so the bare
name check was failing on hosts that actually have these CLIs
installed. Behavior contract is "basename matches", not "exact
string equals", which the new assertions encode.
Cross-platform invariants preserved from fe47c901: Windows cmd /c
wrapping for PATHEXT, CREATE_NO_WINDOW for GUI builds, Unix
process_group(0) for signal isolation, `exit_status_label` for
signal-killed children on Unix.
Tests: 16 chat tests pass on macOS host with actual `claude` /
`gemini` binaries installed (the new code paths exercise PATH +
fallback resolution).