# pi-windows-nul-fix

A [Pi](https://pi.dev) extension that rewrites Windows-style `> nul` redirects to `> /dev/null`
before the bash tool runs them, preventing Git Bash from creating literal files named `nul` on Windows.

## The problem

On Windows, Pi runs shell commands through Git Bash (MSYS2). When an LLM emits a command
like `some-tool --quiet > nul` or `build.sh 2> nul`, Git Bash does not treat `nul` as a
null device — it creates a file literally named `nul` in the current directory.

Because `nul` is a reserved device name in Windows, these files are invisible to `ls`,
cannot be opened in most editors, and cannot be deleted with standard tools — only with
`Remove-Item -LiteralPath '\\?\C:\path\nul' -Force` in PowerShell.

## The fix

This extension intercepts the bash tool's `tool_call` event and rewrites every bare
`> nul`, `2> nul`, `&> nul`, `>> NUL`, etc. to `> /dev/null` before execution. It
tracks single quotes, double quotes, and backslash escapes and does not rewrite
filenames like `nul.txt` or `nul-backup`.

On non-Windows platforms it is a no-op.

## Install

```bash
pi install npm:pi-windows-nul-fix
```

Or install from the git source:

```bash
pi install git:github.com/i-snyder/pi-windows-nul-fix
```

To try without installing:

```bash
pi -e npm:pi-windows-nul-fix
```

## What it covers

The extension rewrites redirects that target the bare `nul` token (case-insensitive) at a
redirect boundary. Patterns rewritten:

| Input | Rewritten to |
|---|---|
| `> nul` | `>/dev/null` |
| `> NUL` | `>/dev/null` |
| `>> nul` | `>>/dev/null` |
| `2> nul` | `2>/dev/null` |
| `2>> nul` | `2>>/dev/null` |
| `&> nul` | `&>/dev/null` |
| `&>> nul` | `&>>/dev/null` |

Patterns left untouched:

- `> nul.txt` — filename, not a device reference
- `"foo > nul"` — inside a double-quoted string
- `'foo > nul'` — inside a single-quoted string
- `\> nul` — escaped redirect operator

## Scope and limits

This extension only affects commands invoked by the LLM via the built-in `bash` tool. It
does not affect:

- Commands you run yourself in the Pi shell (`!` prefix)
- Extensions that bypass the built-in bash tool
- The pi CLI itself

## Known limitations

- **Heredoc bodies.** The parser rewrites `> nul` anywhere it appears outside of quotes,
  including inside heredoc bodies. If the LLM writes a script file using a heredoc that
  intentionally contains `> nul` as text content, that text will be rewritten to
  `> /dev/null`. This is unlikely in practice but worth knowing.

- **Quoted redirect targets.** `> "nul"` and `> 'nul'` (where the target itself is
  quoted) are left untouched by this extension, but Git Bash still creates a literal
  `nul` file for them. These forms are uncommon in LLM-generated commands.

- **`>& nul`.** The csh-style combined redirect `>& nul` is not rewritten. Use `&> nul`
  (bash-style) which is what most LLMs emit and is covered.

## How it works

Pi's `tool_call` event fires before each tool executes and exposes `event.input` as
mutable. For bash tool calls, this extension rewrites `event.input.command` in place using
a character-walking parser that tracks quote and escape state before applying a sticky
regex against each redirect position.
