<div align="center">

# 🔁 pi-recurse

**Programmatic recursive subagent spawning for [pi](https://github.com/earendil-works/pi-coding-agent)**

_LLM makes ONE tool call, extension code handles parallel spawning and result aggregation._

[![pi extension](https://img.shields.io/badge/pi-extension-blueviolet)](https://github.com/earendil-works/pi-coding-agent)
[![license](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)

</div>

---

This project is the spiritual successor to **[ypi](https://github.com/rawwerks/ypi)** by [@rawwerks](https://github.com/rawwerks), originally created as a bash wrapper around Pi that enabled recursive LLM calls with guardrails.

**Why the conversion?** The original `ypi` proved that recursive subagents could work effectively, but required the LLM to generate bash scripts for parallelization. By converting to a proper Pi extension, `pi-recurse` achieves:

- **True programmatic looping** — parallel execution happens in TypeScript code, not bash generated by the LLM
- **Better integration** — hot-reload with `/reload`, automatic tool discovery, native Pi packaging
- **Structured results** — full TypeScript types instead of bash string manipulation
- **Single entry point** — just `pi`, no wrapper scripts needed

Thank you to the original `ypi` for pioneering the concept of depth-limited recursive LLM calls in a coding agent context.

---

## Installation

### Via npm (Recommended)

```bash
pi install npm:pi-recurse
```

### Local Development

```bash
git clone https://github.com/monotykamary/pi-recurse.git
cd pi-recurse
pi install .
```

The extension will be auto-discovered by Pi on next start, or use `/reload` to load it immediately.

---

## Usage

The extension adds a `recurse` tool to Pi, enabling three execution modes:

### Single Mode — One-off delegation

```typescript
recurse({
  mode: 'single',
  prompt: 'Analyze the error handling in src/auth.ts',
  context: fileContent, // optional context to pipe
});
```

### Parallel Mode — Batch processing

```typescript
recurse({
  mode: 'parallel',
  tasks: [
    { id: 'auth', prompt: 'Review auth module' },
    { id: 'db', prompt: 'Review database layer' },
    { id: 'api', prompt: 'Review API routes' },
  ],
  concurrency: 3,
  timeoutPerTask: 120,
});
```

**Key advantage:** All 3 subagents spawn from a **single LLM tool call**. The extension handles `Promise.all()` style concurrency internally — no autoregressive steps between spawns.

### Chain Mode — Sequential dependencies

```typescript
recurse({
  mode: 'chain',
  chain: [
    { id: 'read', prompt: 'Read README.md and summarize' },
    { id: 'analyze', prompt: 'Given this summary: {previous} — identify risks' },
    { id: 'plan', prompt: 'Given these risks: {previous} — suggest mitigations' },
  ],
});
```

---

## Guardrails

Environment variables control recursion limits across the entire call tree:

| Variable              | Default | Description                          |
| --------------------- | ------- | ------------------------------------ |
| `RLM_MAX_DEPTH`       | 3       | Maximum recursion depth (0 = root)   |
| `RLM_MAX_CALLS`       | 100     | Maximum total recurse invocations    |
| `RLM_TIMEOUT`         | 600     | Wall-clock seconds for entire tree   |
| `RLM_BUDGET`          | —       | Max dollar spend (e.g., `0.50`)      |
| `RLM_CHILD_MODEL`     | —       | Model override for depth > 0         |
| `RLM_DISABLE_TOOL_AT` | 3       | Disable recurse tool at this depth   |
| `RLM_TRACE_ID`        | auto    | Links all sessions in recursive tree |

At deep depths (≥ `RLM_DISABLE_TOOL_AT`), the recurse tool automatically disables itself and guides agents to work directly instead of delegating.

---

## Architecture: From Bash to Extension

### Original ypi (Bash Wrapper)

```
ypi "analyze this codebase"
    ↓
Launcher sets env, execs pi with custom system prompt
    ↓
LLM generates bash loop for parallel tasks:
    for f in $(find src -name "*.ts"); do
        rlm_query --async "Review $f"
    done
    ↓
Wait on sentinel files, aggregate in bash
```

**Limitation:** The LLM had to generate the loop structure, and parallelization required autoregressive generation of bash code.

### pi-recurse (Pi Extension)

```
pi (with pi-recurse extension loaded)
    ↓
LLM calls once: recurse({mode: "parallel", tasks: [...]})
    ↓
Extension execute() runs in Node.js:
    ├── Check guardrails (depth, budget, timeout)
    ├── runParallel(tasks, spawnFn, concurrency)
    │   └── Promise.all(concurrent batches)
    ├── Aggregate structured results
    └── Return {results, stats, depth}
    ↓
LLM receives aggregated data, continues reasoning
```

**Advantage:** Parallel execution happens in **code**, not in LLM-generated bash. One tool call → many subagents → aggregated result.

---

## Comparison: ypi vs pi-recurse

| Feature               | ypi (Wrapper)            | pi-recurse (Extension)            |
| --------------------- | ------------------------ | --------------------------------- |
| Entry point           | `ypi "prompt"`           | `pi` (extension auto-loaded)      |
| Parallel spawning     | LLM writes bash loop     | **Code executes `Promise.all()`** |
| Result aggregation    | Bash string manipulation | TypeScript structured objects     |
| Hot reload            | Restart ypi process      | `/reload` in pi                   |
| Distribution          | npm global bin           | `pi install` / git URL            |
| Extension integration | Manual env/PATH setup    | Native auto-discovery             |
| Tool disabling        | Manual depth checks      | Automatic at configured depth     |
| Status visibility     | Footer via ypi.ts        | Native Pi status bar              |

---

## Development

```bash
# Clone and install dependencies
git clone https://github.com/monotykamary/pi-recurse.git
cd pi-recurse
npm install

# Run tests
npm test

# Type check
npm run typecheck

# Build (optional - jiti handles TypeScript at runtime)
npm run build
```

### Testing the Extension

```bash
# Install locally
pi install .

# Or test without installing
pi -e ./index.ts

# In pi, test recursion
/recurse-status
recurse({mode: "single", prompt: "What is 2+2?"})
```

---

## Design Principles

1. **Single tool call, parallel execution** — The LLM describes intent; code handles concurrency
2. **Depth-aware degradation** — At deep levels, disable recursion to prevent runaway costs
3. **Structured over stringly** — Full TypeScript types for inputs and outputs
4. **Guardrails in code** — Budget, timeout, depth checked programmatically
5. **Pi-native integration** — Use Pi's extension API, not wrapper scripts

---

## Related Projects

- **[ypi](https://github.com/rawwerks/ypi)** — The original bash wrapper that inspired this extension
- **[pi-messenger](https://github.com/monotykamary/pi-messenger-swarm)** — Multi-agent coordination for Pi (another extension)
- **[pi](https://github.com/badlogic/pi-mono)** — The Pi coding agent itself

## License

MIT — See [LICENSE](LICENSE) for details.
