# Override the built-in bash tool instead of intercepting via events

We chose to override pi's built-in `bash` tool (registering a replacement with the same name) rather than intercepting via `tool_call` events. This gives us full control over execution, rendering, and system prompt behavior while keeping the LLM interface identical. The override approach also avoids fragile event-chain dependencies — a `tool_call` handler that mutates `event.input` mid-execution could conflict with other extensions or future pi changes.

**Status:** accepted

**Considered Options:**

- **Intercept via `tool_call` + `tool_result`:** mutate `event.input.command` with the transpiled PowerShell, let the built-in tool handle execution. Rejected because the built-in tool spawns via `getShellConfig()` which may not be PowerShell, and we can't control the shell path from an event handler. Also, custom rendering would require wrapping `tool_result`, which is more fragile.

- **Separate `powershell` tool:** register a new tool with a different name (`powershell`, `shell`) and have the LLM call it instead of `bash`. Rejected because the LLM would need to learn when to use which tool, would write defensive "Windows-safe" commands, and would lose the quality of its native bash generation.

- **Spawn hook on built-in bash tool:** use `createBashTool({ spawnHook })` to rewrite commands. Rejected because the spawn hook can adjust command/cwd/env but cannot change which shell is spawned — the command would still be PowerShell fed to whatever shell `getShellConfig()` resolves to.

**Consequences:**

- The extension must reimplement process spawning, streaming, timeout, and abort logic that the built-in bash tool provides
- On non-Windows platforms, the extension no-ops — the built-in bash tool is unaffected
- Fallback execution on transpilation failure uses `createLocalBashOperations` directly, avoiding circular override issues
