ypi
v0.5.0
Published
ypi — a recursive coding agent. Pi that can call itself via rlm_query.
Maintainers
Readme
ypi
ypi — a recursive coding agent built on Pi.
Named after the Y combinator from lambda calculus — the fixed-point combinator that enables recursion. ypi is Pi that can call itself. (rpi already has another connotation.)
Inspired by Recursive Language Models (RLM), which showed that an LLM with a code REPL and a llm_query() function can recursively decompose problems, analyze massive contexts, and write code — all through self-delegation.
The Idea
Pi already has a bash REPL. We add one function — rlm_query — and a system prompt that teaches Pi to use it recursively. Each child gets its own jj workspace for file isolation. That's the whole trick.
┌──────────────────────────────────────────┐
│ ypi (depth 0) │
│ Tools: bash, rlm_query │
│ Workspace: default │
│ │
│ > grep -n "bug" src/*.py │
│ > sed -n '50,80p' src/app.py \ │
│ | rlm_query "Fix this bug" │
│ │ │
│ ▼ │
│ ┌────────────────────────────┐ │
│ │ ypi (depth 1) │ │
│ │ Workspace: jj isolated │ │
│ │ Edits files safely │ │
│ │ Returns: patch on stdout │ │
│ └────────────────────────────┘ │
│ │
│ > jj squash --from <child-change> │
│ # absorb the fix into our working copy │
└──────────────────────────────────────────┘Using ypi
Install
# npm (global)
npm install -g ypi
# or run without installing
npx ypi "What does this repo do?"
bunx ypi "What does this repo do?"
# or curl
curl -fsSL https://raw.githubusercontent.com/rawwerks/ypi/master/install.sh | bash
# or manual
git clone https://github.com/rawwerks/ypi.git && cd ypi
git submodule update --init --depth 1
export PATH="$PWD:$PATH"Run
# Interactive
ypi
# One-shot
ypi "Refactor the error handling in this repo"
# Different model
ypi --provider anthropic --model claude-sonnet-4-5-20250929 "What does this codebase do?"How It Works
Three pieces (same architecture as Python RLM):
| Piece | Python RLM | ypi |
|---|---|---|
| System prompt | RLM_SYSTEM_PROMPT | SYSTEM_PROMPT.md |
| Context / REPL | Python context variable | $CONTEXT file + bash |
| Sub-call function | llm_query("prompt") | rlm_query "prompt" |
Recursion: rlm_query spawns a child Pi process with the same system prompt and tools. The child can call rlm_query too:
Depth 0 (root) → full Pi with bash + rlm_query
Depth 1 (child) → full Pi with bash + rlm_query, own jj workspace
Depth 2 (leaf) → full Pi with bash, but no rlm_query (max depth)File isolation with jj: Each recursive child gets its own jj workspace. The parent's working copy is untouched. Review child work with jj diff -r <change-id>, absorb with jj squash --from <change-id>.
Why It Works
The design has three properties that compound:
Self-similarity — Every depth runs the same prompt, same tools, same agent. No specialized "scout" or "planner" roles. The intelligence is in decomposition, not specialization. The system prompt teaches one pattern — size-first → search → chunk → delegate → combine — and it works at every scale.
Self-hosting — The system prompt (SECTION 6) contains the full source of
rlm_query. The agent reads its own recursion machinery. When it modifiesrlm_query, it's modifying itself. This isn't a metaphor — it's the actual execution model.Bounded recursion — Five concentric guardrails (depth limit, PATH scrubbing, call count, budget, timeout) guarantee termination. The system prompt also installs cognitive pressure: deeper agents are told to be more conservative, preferring direct action over spawning more children.
Symbolic access — Anything the agent needs to manipulate precisely is a file, not just tokens in context.
$CONTEXTholds the data,$RLM_PROMPT_FILEholds the original prompt, and hashline provides line-addressed edits. Agentsgrep/sed/catinstead of copying tokens from memory.
Guardrails
| Feature | Env var | What it does |
|---------|---------|-------------|
| Budget | RLM_BUDGET=0.50 | Max dollar spend for entire recursive tree |
| Timeout | RLM_TIMEOUT=60 | Wall-clock limit for entire recursive tree |
| Call limit | RLM_MAX_CALLS=20 | Max total rlm_query invocations |
| Model routing | RLM_CHILD_MODEL=haiku | Use cheaper model for sub-calls |
| Depth limit | RLM_MAX_DEPTH=3 | How deep recursion can go |
| jj disable | RLM_JJ=0 | Skip workspace isolation |
| Plain text | RLM_JSON=0 | Disable JSON mode (no cost tracking) |
| Tracing | PI_TRACE_FILE=/tmp/trace.log | Log all calls with timing + cost |
The agent can check spend at any time:
rlm_cost # "$0.042381"
rlm_cost --json # {"cost": 0.042381, "tokens": 12450, "calls": 3}Pi Compatibility
ypi is a thin layer on top of Pi. We strive not to break or duplicate what Pi already does:
| Pi feature | ypi behavior | Tests |
|---|---|---|
| Session history | Uses Pi's native ~/.pi/agent/sessions/ dir. Child sessions go in the same dir with trace-encoded filenames. No separate session store. | G24–G30 |
| Extensions | Passed through to Pi. Children inherit extensions by default. RLM_EXTENSIONS=0 disables. | G34–G38 |
| System prompt | Built from SYSTEM_PROMPT.md + rlm_query source, written to a temp file, passed via --system-prompt (file path, never inlined as shell arg). | T8–T9 |
| -p mode | All child Pi calls run non-interactive (-p). ypi never fakes a terminal. | T3–T4 |
| --session flag | Used when RLM_SESSION_DIR is set; --no-session otherwise. Never both. | G24, G28 |
| Provider/model | Never hardcoded. ypi and rlm_query use Pi's defaults unless the user sets RLM_PROVIDER/RLM_MODEL. | T14, T14c |
If Pi changes how sessions or extensions work, our guardrail tests should catch it.
Contributing
Project Structure
ypi/
├── ypi # Launcher: sets up env, starts Pi as recursive agent
├── rlm_query # The recursive sub-call function (Pi's analog of rlm llm_query())
├── SYSTEM_PROMPT.md # Teaches the LLM to be recursive + edit code
├── AGENTS.md # Meta-instructions for the agent (read by ypi itself)
├── Makefile # test targets
├── tests/
│ ├── test_unit.sh # Mock pi, test bash logic (no LLM, fast)
│ ├── test_guardrails.sh # Test guardrails (no LLM, fast)
│ └── test_e2e.sh # Real LLM calls (slow, costs ~$0.05)
├── pi-mono/ # Git submodule: upstream Pi coding agent
└── README.mdVersion Control
This repo uses jj for version control. Git is only for GitHub sync.
jj status # What's changed
jj describe -m "message" # Describe current change
jj new # Start a new change
jj bookmark set master # Point master at current change
jj git push # Push to GitHubNever use git add/commit/push directly. jj manages git under the hood.
Testing
make test-fast # 54 tests, no LLM calls, seconds
make test-e2e # Real LLM calls, costs ~$0.05
make test # BothBefore any change to rlm_query: run make test-fast. After: run it again. rlm_query is a live dependency of the agent's own execution — breaking it breaks the agent.
History
ypi went through four approaches before landing on the current design:
- Tool-use REPL (exp 010/012) — Pi's
completeWithTools(), ReAct loop. 77.6% on LongMemEval. - Python bridge — HTTP server between Pi and Python RLM. Too complex.
- Pi extension — Custom provider with search tools. Not true recursion.
- Bash RLM (
rlm_query+SYSTEM_PROMPT.md) — True recursion via bash. Current approach.
The key insight: Pi's bash tool is the REPL. rlm_query is llm_query(). No bridge needed.
See Also
- Pi coding agent — the underlying agent
- Recursive Language Models — the library that inspired this
- rlm-cli — Python RLM CLI (budget, timeout, model routing)
