Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

rawwerks/ypi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

85 Commits

Repository files navigation

ypi

npm

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:

  1. 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.

  2. Self-hosting — The system prompt (SECTION 6) contains the full source of rlm_query. The agent reads its own recursion machinery. When it modifies rlm_query, it's modifying itself. This isn't a metaphor — it's the actual execution model.

  3. 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.

  4. Symbolic access — Anything the agent needs to manipulate precisely is a file, not just tokens in context. $CONTEXT holds the data, $RLM_PROMPT_FILE holds the original prompt, and hashline provides line-addressed edits. Agents grep/sed/cat instead 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.md

Version 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 GitHub

Never use git add/commit/push directly. jj manages git under the hood.

Testing

make test-fast # unit + guardrails
make test-extensions # extension compatibility with installed pi
make pre-push-checks # shared local/CI gate (recommended before push)
make test-e2e # real LLM calls, costs money
make test # all of the above

Install hooks once per clone to run checks automatically on git push:

make install-hooks

Release/update helper:

make release-preflight # same checks + upstream dry-run in one command

Before 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.

CI helper commands:

make ci-status N=15 # recent workflow runs
make ci-last-failure # dump latest failing workflow log

History

ypi went through four approaches before landing on the current design:

  1. Tool-use REPL (exp 010/012) — Pi's completeWithTools(), ReAct loop. 77.6% on LongMemEval.
  2. Python bridge — HTTP server between Pi and Python RLM. Too complex.
  3. Pi extension — Custom provider with search tools. Not true recursion.
  4. 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

About

A recursive coding agent inpired by RLMs

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /