---
slug: "pi-subprocess"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/juanje/pi-subprocess@main/README.md"
repo: "https://github.com/juanje/pi-subprocess"
source_file: "README.md"
branch: "main"
---
# pi-subprocess

Minimal subprocess isolation for [Pi](https://pi.dev) agents. Spawns isolated Pi subprocesses and returns curated results — keeps the parent's context clean.

## Install

From npm:

```bash
pi install npm:pi-subprocess
```

From git:

```bash
pi install git+https://github.com/juanje/pi-subprocess.git
```

Or load directly without installing:

```bash
pi -e /path/to/pi-subprocess/extensions/subprocess.ts
```

## Quick start

Once installed, a `subprocess` tool becomes available in your Pi session:

```
> Use subprocess to investigate how error handling works in the tools/ directory
```

The agent spawns an isolated Pi subprocess that investigates independently and returns a summary. The parent session's context grows by the summary size only, not by all the files the subagent read.

## How it works

The extension registers a `subprocess` tool that:

1. **Spawns** a child Pi process in JSON mode with its own system prompt
2. **Streams** the child's JSONL output, collecting `message_end` events
3. **Extracts** the final assistant text from the child session
4. **Truncates** the output to a configurable maximum (default: 100 lines)
5. **Returns** the curated text + stats (turns, tool calls, tokens, cost, duration)

### Context isolation

The child runs in its own process with a fresh context window. It inherits the parent's working directory and environment (including permission policies from `pi-permission-gate` if installed), but none of the parent's conversation history.

### Project system prompt

If `.pi/SUBPROCESS_SYSTEM.md` exists in the working directory, its content replaces the default generic worker prompt. This lets a project define subprocess behavior once — identity, rules, tool constraints — instead of repeating it in every call.

Per-call specialization appends on top of the base via `system_prompt` (inline text) or `system_prompt_file` (path to a file on disk). Use `system_prompt_file` for static per-phase identities that don't change between invocations — the parent agent passes a path and never loads the content into its own context.

Prompt hierarchy:

```
.pi/SUBPROCESS_SYSTEM.md (or built-in default)
  └── system_prompt OR system_prompt_file (per-call specialization)
        └── task parameter (the work item for this subprocess)
```

Example with multiple worker types:

```
.pi/
├── SUBPROCESS_SYSTEM.md        # Shared base (authority, atomicity, return rules)
└── workers/
    ├── extract.md              # Extraction identity + manifest format
    ├── write.md                # Page-writing rules + link format
    └── fix.md                  # Link-fixing constraints
```

```
subprocess(
  task = "Read /path/to/manifest.md and write pages...",
  system_prompt_file = ".pi/workers/write.md",
  tools = "read,write",
  timeout_ms = 600000
)
```

### Recursion prevention

The extension sets `PI_SUBPROCESS_CHILD=1` in the child's environment. If this variable is already set, the extension skips registration entirely — no infinite recursion.

### Session persistence

Child sessions are saved alongside the parent session for post-hoc analysis:

```
~/.pi/agent/sessions/my-project/
├── 2026-06-19_abc123.jsonl          # parent session
└── 2026-06-19_abc123/
    └── subprocess-x7k9m2/           # child session
        ├── session.jsonl
        └── ...
```

## Tool parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `task` | string | yes | — | What the worker should do |
| `system_prompt` | string | no | — | Custom prompt text appended to the worker prompt. Mutually exclusive with `system_prompt_file`. |
| `system_prompt_file` | string | no | — | Path (relative to cwd) to a file appended to the worker prompt. Use for static identities. Mutually exclusive with `system_prompt`. |
| `tools` | string | no | `read,bash,grep,find,ls` | Comma-separated tools for the worker |
| `max_lines` | number | no | `100` | Maximum output lines returned |
| `effort` | `fast` \| `balanced` \| `thorough` | no | — | Thinking depth (maps to Pi `--thinking` low/medium/high) |
| `timeout_ms` | number | no | `900000` (15 min) | Kill the subprocess after this duration |

## Tool result

The tool returns:

- **`content`** — the subprocess's final text (truncated if over `max_lines`)
- **`details`** — stats object:

| Field | Type | Description |
|-------|------|-------------|
| `turns` | number | Assistant turns in the child session |
| `toolCalls` | number | Total tool invocations |
| `totalTokens` | number | Token consumption |
| `cost` | number | API cost |
| `durationMs` | number | Wall-clock duration |
| `sessionDir` | string | Path to child session JSONL |
| `timedOut` | boolean | Whether the timeout killed the child |
| `outputTruncated` | boolean | Whether output exceeded `max_lines` |
| `fullOutputFile` | string? | Path to untruncated output (only when truncated) |

The `details` field is visible to the parent agent but not injected into the context text. When output is truncated, the full text is saved to `full-output.md` in the session directory for post-hoc review.

## When to use

- **Investigation would flood context** — reading multiple files, grepping large codebases, fetching logs
- **Research before synthesis** — delegate the fact-finding, keep the parent focused on analysis
- **Skill-directed isolation** — skills can explicitly invoke `subprocess` for specific substeps

## When NOT to use

- Simple file reads or single tool calls — the overhead of spawning a subprocess isn't worth it
- Tasks requiring parent conversation history — the child starts fresh
- Tasks requiring write access to parent state — the child's writes don't propagate back

## Development

```bash
git clone https://github.com/juanje/pi-subprocess.git
cd pi-subprocess
npm install
npm run check    # typecheck + lint + test
```

## License

MIT — see [LICENSE](https://github.com/juanje/pi-subprocess/tree/HEAD/LICENSE).
