pirukas is Estonian for pie — golden yeast dough wrapped around savory fillings, from a Slavic root meaning feast. Split it after pi and you get the agent it's built on plus the dish it's named after. Is it a backronym? Technically, Pie Is Ready — Users Keep Adding Steps. But honestly, rukas isn't a word. Stop trying to expand it. The pie is the meaning.
A multi-specialist orchestrator extension for Pi — the terminal AI coding agent. Spawns role-specialised child Pi processes in parallel, isolates them in git worktrees, runs a mandatory adversarial gate before commit, and gates merge on a six-pass code review (security, error handling, type safety, performance, architecture, simplicity).
Status: alpha. The interfaces work and the workflow runs end-to-end, but the API will change before
1.0. Use on disposable repos until you've kicked the tires. Run whatever Pi version you like — docs/pi-compatibility.md carries the single maintained "last verified against" line (the version to fall back to if you hit trouble on a newer Pi) and the full list of version-claim sites.
Seven slash commands, an orchestrator-shaped system prompt, and nine tools that drive parallel specialist agents:
| Command | What it does |
|---|---|
/start |
Initialises a project session — searches memory, indexes the codebase, gathers git/PR/CI state, reports readiness. |
/research <topic> |
Runs the compiled research spine (start_research_driver): memory inventory → parallel angle retrieval → deterministic verification (URL liveness; codebase claims grounded against the pinned commit) → a dated artifact + provenance sidecar under outputs/ → a typed vipune row superseding the prior run. Tiers: quick / standard / deep / adoption (OSS decision memo). Zero verified findings writes an honest abstention, never a confident fabrication. |
/plan <description> |
Drafts a GitHub issue from your input — auto-classifies as bug/feature/epic/chore/spike, applies the right template, asks before creating. Runs as a compiled pipeline (start_plan_driver): a deterministic pre-triage rejects a too-thin descriptor before any dispatch; typed context blocks and an "EXACTLY N sub-issues" pin thread into the epic decomposition; the drafted body passes deterministic validation before any LLM review; the adversarial gap gate runs for bug/feature/epic (validation is the chore/spike gate); bodies are budgeted to the forge's 65,536-char limit — compacted deterministically or halted with a per-section size breakdown. For an automated filer it also takes labels (checked against the repo, never auto-created, applied inside the single create call) and provenance (an italic attribution line and an HTML-comment marker, both appended by code), and returns the filed issueNumber alongside the url. |
/work <issue#> [<issue#> ...] [--restart] [--merge] [--allow-protected <path>] |
Runs an issue end-to-end through a compiled state-machine driver (extension/src/work-driver.ts): explore → plan → feature branch (always a worktree per workstream, provisioned via .pi/worktree-setup or by symlinking gitignored non-empty dependency dirs (discovered at repoRoot and in depth-1 package dirs)) → developer dispatch → driver-side outcome verification (executed evidence: real diff exists + project typecheck/test passes, verify command from .pi/verify-cmd else auto-detected, optional product smoke command runs from .pi/smoke-cmd, skip-marker ratchet checks for net additions of #[ignore]/it.skip/etc., no LLM judgment) → per-workstream adversarial_loop gate (parallel for N>1) → driver-executed commit + PR (mechanized consolidation of all worktrees, git apply --index + push + gh pr create run as code, LLM ops only as fallback) → outcome verification again (commits exist, PR resolves via gh) → six-pass code review → CI watch → merge, but only when explicitly permitted (an explicit grant in the project's AGENTS.md, or --merge for the run; with neither, the PR is opened and the cycle parks as awaiting-human-merge) and only on executed evidence (gh pr checks + mergeStateStatus, never an agent's claim; skipped/neutral required checks do not count as passing). Accepts multiple issue numbers (/work 561 562 563) to run a deterministic grouping analysis first (link markers, path-overlap Jaccard ≥ 0.5, subsystem-tag prefixes) — related issues share ONE PR, unrelated issues run as separate sequential cycles. Restores the old PM-driven /work's "analyze first, decide the plan" shape but in pure code (see docs/troubleshooting.md for the rules). Halt-on-non-merged between groups: if a group's cycle handoffs / aborts, the queue stops and names the remaining groups so the operator can intervene. Pass --restart (order-independent) to wipe the prior state file and start a fresh cycle — useful after revising the issue body via /plan following a step-back-revise-spec handoff. Pass --allow-protected <path> (repeatable or comma-separated, operator-only, single-issue invocations only) to let develop write exactly the named protected paths (.github/, .pi/, CODEOWNERS, agents.json) for this cycle — for an issue whose work is a CI-file change; see docs/troubleshooting.md. Matching is exact and per-side: a rename is checked on both sides, so a grant naming only a rename's destination does not admit moving a protected file out of the way, and a protected path whose name is non-ASCII cannot be granted at all (PI_ENSEMBLE_PROTECTED_PATHS=0 is the only escape). Resumable state at .pi/work-state/<primary>.json; cap-hits surface as structured handoff comments with verbatim recovery commands. |
/do <description> |
Free-form orchestration counterpart to /work — no GitHub issue required. PM-driven (same dispatch toolkit as /work but no compiled driver / state file). Use when you want to act on lens-review findings, fix something small without filing an issue, or work on a community-submitted PR. |
/review [#PR | path | latest N] |
On-demand six-pass code review of a PR, file, directory, or the latest N PRs. Returns a deduplicated, precedence-merged verdict (APPROVED / ISSUES_FOUND / CRITICAL_ISSUES_FOUND). |
/audit [<path> | "full"] |
Standards-first repo/path audit. Derives expectations from docs/config/CI/memory/examples, then reports misalignments across bugs, dead code, style drift, architecture drift, and quality-gate gaps. |
Plus utility commands: /ensemble-model (per-role subagent model picker), /runs (browse subagent transcripts), /ensemble-debug (show resolved configuration), /work-status (live /work cycle status; postmortem layout in a terminal state).
/start— beginning of a session: load context, check state. Reads only./research <topic>— investigate a topic (web + codebase + memory); deterministic verification, dated artifact + provenance underoutputs/./plan <description>— draft a GitHub issue. No memory writes./work <issue#>— execute an issue end-to-end (implement → review → merge) on a feature branch. Subagents may write to memory./do <description>— free-form work without an issue. Subagents may write to memory./review— evaluate code against the six universal quality lenses; does not write to memory./audit— audit a repo or path against its own intended standards; sparse, durable stores only.
Quick rule of thumb: learn → /research; fix with an issue → /work <issue#>; fix free-form → /do <description>; check code quality → /review; assess overall health → /audit.
Required CLIs on $PATH. The role prompts assume all of these are installed — without them, the agents fail at runtime.
| Tool | Purpose |
|---|---|
| Pi | The terminal coding agent this extends. |
bun |
Runtime for the extension (loads TS via jiti). |
git ≥ 2.20 |
Worktrees, branches, diffs. |
Forge CLI (gh / glab) |
Forge issue / PR / CI ops — gh for GitHub, glab for GitLab. Install at least one; both is fine. |
vipune |
Cross-session memory (fact + observation patterns). All agents call this. (Cargo from source.) |
codebase-memory-mcp |
Knowledge-graph code indexer exposed as MCP — powers mcp__codebase_memory__search_code / mcp__codebase_memory__trace_path / mcp__codebase_memory__detect_changes / mcp__codebase_memory__get_architecture. (curl-to-bash install.) |
oo |
Context-efficient wrapper for chatty CLIs (git, gh). (Cargo from source. Pinned to floor 0.5.0.) |
jq |
Used by build.sh to assemble the capability matrix into the PM prompt. |
parallel-cli |
Web search / fetch / deep research for the explore role. (Homebrew tap.) |
ctx7 |
Current third-party library documentation. ctx7 library <name> → ctx7 docs <id> <query>. Free tier works without login. |
Recent supply-chain attacks (compromised maintainer publishes a malicious version, caught and yanked within hours) make a release-age embargo worth setting up once, globally:
# npm — applies to all `npm install -g …` from now on
npm config set min-release-age 4 # requires npm ≥ 11.10.0
# bun: project-local bunfig.toml already sets minimumReleaseAge = 345600 in THIS repoWith the embargo, the install commands below refuse any version published in the last 4 days — the most common attack window. We also recommend --ignore-scripts on every npm install (Pi's own quickstart recommends it) to disable postinstall hooks — another common supply-chain vector.
Copy-pasteable. All installs use the latest version your package manager allows (with the embargo above, "latest" means ≥4 days old).
# Pi (per https://pi.dev/docs/latest/quickstart)
npm install -g --ignore-scripts @earendil-works/pi-coding-agent@1.0.0
# bun (≥ 1.2.20)
curl -fsSL https://bun.com/install | bash
# git, jq — your OS package manager; the forge CLI (gh and/or glab) the same way
brew install git jq gh # macOS (gh)
# sudo apt install git jq gh # Debian/Ubuntu (gh)
# glab — GitLab forge CLI: brew install --no-quarantine glab (macOS); see
# https://gitlab.com/gitlab-org/cli for the Linux one-liner.
# vipune, oo — cargo from source (Rust toolchain required)
cargo install vipune && cargo install double-o --version 0.5.0
# codebase-memory-mcp (REQUIRED — pi-rukas's code-search doctrine depends on it)
curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh | bash
# Installs the C binary at ~/.local/bin/codebase-memory-mcp (~250 MB); pi-rukas's
# install.sh registers it with Pi 1.0.0's native MCP automatically (see "After
# install" below).
# parallel-cli
brew install parallel-web/tap/parallel-cli
parallel-cli login
# ctx7
npm install -g --ignore-scripts ctx7After install:
vipune versiononce to initialise~/.vipune/.- Run pi-rukas's
./install.shfrom this repo. It detectscodebase-memory-mcpon yourPATH(or in~/.local/bin/) and wires acodebase_memoryentry into~/.pi/agent/mcp.jsonin Pi 1.0.0's native MCP format (full walkthrough in docs/mcp.md). You should not have to hand-edit any MCP config — re-running is safe (idempotent merge). Verify afterpirestarts with/mcp— should listcodebase_memorywith its 7 direct tools (search_code,search_graph,trace_path,detect_changes,get_code_snippet,get_architecture,query_graph). - One-shot index every project the first time pi opens there — ask Pi to run
index_repositoryon the project root (it is granted to the PM role);/startdoes this on first use. The file watcher keeps the index current. Indexed data lives in~/.cache/codebase-memory-mcp/.
Platform. Supported: macOS and Linux. Native Windows is unsupported (every entrypoint is a bash script, the install is symlink-based, and the sandbox bind-mounts the project at its host absolute path). WSL2 is expected to work but untested; sandbox mode additionally needs Docker. Bun ≥ 1.2.20 and Node ≥ 22 (Pi's own requirement) are assumed. You can also defer ctx7 entirely — the explore role tries to call it but everything else works without it.
git clone https://github.com/trail-openers/pi-rukas.git
cd pi-rukas
./install.shThe installer builds the role prompts, symlinks the bundled skills into ~/.pi/agent/skills/, installs the extension's deps, registers the extension with Pi, pulls the sandbox Docker image from ghcr.io/trail-openers/pi-rukas:latest (falls back to a local build if the pull fails), and symlinks the pi-rukas wrapper into ~/.local/bin/. Pass --build to force a local build; --pull-only to hard-fail when the registry is unreachable.
Verify (sandbox mode, recommended): pi-rukas from any project, then run /ensemble-debug in the Pi prompt — it should list 9 slash commands, 9 tools, and the per-role model table, with zero permission prompts inside the container. Host mode (legacy): run pi the same way — 3-tier permission system, expect interactive prompts on novel commands.
pi-rukas launches Pi inside a Docker container where the container fence IS the trust boundary — no per-call permission prompts. Host state that should survive across sessions is bind-mounted in; container-local caches use named volumes. This is the recommended mode; host-mode pi remains available without Docker.
Note: the container has unrestricted network egress for v1 — the sandbox protects your filesystem but not your network. Full detail — the bind-mount table, pi-rukas wrapper subcommands, cross-mode persistence, session resume, tailnet/host aliases, drag-and-drop images, and the docker-socket / SSH trade-off — lives in docs/sandbox.md.
The parent pi you launch becomes the project manager (PM) — when you fire a registered slash command, the extension injects PM doctrine into the system prompt for that turn (one-shot, no global bleed), and the PM runs the workflow body and dispatches specialists. Each specialist is a child pi process spawned with pi --mode rpc --no-extensions --session <transcript> --append-system-prompt <role.md>; --mode rpc keeps stdin open for JSON command injection — the initial prompt arrives as a { type: "prompt", message } RPC command, and the same channel carries mid-flight { type: "steer", message } injections from dispatch_steer. Six roles ship (project-manager, developer, ops, explore, adversarial-developer, code-review-specialist), each with its own system prompt assembled from agents-base/, modules/, and manifests/ via build.sh.
Tools (all async via push-callback — tools return a { jobId } immediately; the final report arrives later as an [ensemble:async] user message):
| Tool | Purpose |
|---|---|
dispatch_specialist |
Spawn exactly ONE specialist (developer / ops / explore / adversarial-developer / code-review-specialist). |
dispatch_parallel |
Fan out 2-10 specialists in parallel; ONE consolidated report arrives when all complete. |
adversarial_loop |
Encapsulated 3-round review-then-fix gate: takes the developer's diff, runs an adversarial review, spawns a fresh developer to address any findings, re-reviews (diff re-read before each round). The mandatory gate before any commit — only CRITICAL_ISSUES_FOUND blocks when the rounds run out; non-blocking findings are carried into the PR body and the six-pass review instead of killing the cycle. |
dispatch_lens_review |
Six-pass code review — six children, each pinned to its lens skill. Findings come back as schema-validated report_finding tool calls, deduped by (path, line, title), precedence-merged into a verdict. |
dispatch_status |
List in-flight async jobs (jobId, role, elapsed). Metadata only — never transcript content. |
dispatch_kill <jobId> |
Abort a running subagent or batch. |
dispatch_peek <jobId> |
Bounded, read-only introspection of a running subagent — last assistant text + last tool call (#21). |
dispatch_steer <jobId> <message> |
Inject a mid-flight steer into a running subagent via Pi's --mode rpc stdin channel — for exceptional rescue only (long-elapsed, stuck-looking) (#152). |
start_work_driver <issues[]> |
Start the compiled /work driver — the same pipeline the slash command runs, with its state file, queue, handoff artifact and review-cap timer. Returns immediately; merge authority is operator-only and has no parameter here (#408). |
load_workflow_doctrine <name> |
Return a workflow command's full instructions (research, plan, review, audit, start, do) as tool output, so PM can run one without the user typing the slash command. /work is deliberately excluded — it is a compiled driver, not prose. |
check_review_cap <key> |
Wall-clock cap helper for the /work fix loop — ok/exceeded against the review-loop budget (90 min by default, configurable via PI_ENSEMBLE_REVIEW_CAP_MS) so the PM stops doom-loops (#4). |
Headless clients (pi --mode rpc). A PM turn that ends after dispatching is not the end of the work: the report arrives later and starts a new run. To tell the two apart, read the pi-rukas-jobs status: an extension_ui_request with method: "setStatus", statusKey: "pi-rukas-jobs", and statusText set to the number of PM-owed reports as a decimal string (Pi's wire fields, per its docs/rpc-extension-ui.md). It is sent on RPC attach (as "0") and whenever the number changes: a job or batch starts, its report is handed to Pi (success, failure or kill), or the session shuts down. "0" means the last report was handed to Pi (the PM's session), not that the PM has read it. A batch whose report could not be built still hands Pi a failure report that names the batch, before the count drops to "0". A setStatus with no statusText clears the entry; this extension never sends one, and before any update arrives the count is 0. A dispatch_parallel batch counts once (one report); the /work driver's own children never count (they report to the driver, and start_work_driver itself does). RPC mode only.
Per-child transcripts are saved to ~/.pi/agent/ensemble-runs/<date>/<runId>-<role>[-<tag>].json — replay with pi --session <path> or browse via /runs. The user inspects these; orchestrating agents do NOT read them (the dispatch tool's report is the bounded summary by design). The live dispatch deck renders one row per RUNNING subagent below the editor (updated every second); roster mode (#834) lets you navigate rows from an empty editor with ↓/↑/j/k; Enter opens the live view (#839) — the child's recent activity (assistant text, tool calls, results) updating as events arrive — with s to steer and Esc to close (no-buffer rows open the steer prompt directly). Finished runs are browsed via /runs. Opt out: PI_ENSEMBLE_QUIET_STATUS=1; batch-header row cap: PI_ENSEMBLE_DECK_MAX_ROWS (default 20 — per-job rows render in full). Full key reference: docs/configuration.md.
You probably want a smarter model for the PM and a faster one for the specialists. The main agent (the pi you launch) is configured via Pi's own --model flag or settings. Subagent model choice is user-authority-only — the orchestrating agent cannot route a dispatch to a different provider on its own (jurisdiction routing is a data-residency / compliance decision, not an agent concern; #92). Resolution order:
- For a code-review lens child only:
PI_ENSEMBLE_MODEL_LENS_<LENS>env var, optionally paired withPI_ENSEMBLE_PROVIDER_LENS_<LENS>(the six lenses all run ascode-review-specialist, so this is how they get different models) /ensemble-modelper-role choice (saved to~/.pi/agent/ensemble-models.json)/ensemble-modelall-subagents default (same file)PI_ENSEMBLE_MODEL_<ROLE>env var, optionally paired withPI_ENSEMBLE_PROVIDER_<ROLE>for custom OpenAI-compatible providersPI_ENSEMBLE_SUBAGENT_MODELenv var (global fallback), optionally paired withPI_ENSEMBLE_SUBAGENT_PROVIDER- Pi default (lowest)
Run /ensemble-model inside Pi to pick interactively from your authenticated provider catalog; add new built-in providers via Pi's /login and pi-rukas picks them up automatically. A custom OpenAI-compatible provider (self-hosted vLLM, an internal endpoint, any OpenAI Chat-Completions-compatible API) is a one-time registration in ~/.pi/agent/models.json — see docs/custom-providers.md.
MCP servers are configured natively — Pi 1.0.0 ships built-in MCP that reads ~/.pi/agent/mcp.json (user-level) and .pi/mcp.json (project-level, after trust); there is no bridge extension to install or forward, and subagents get it via the -e builtin:mcp flag. pi-rukas gates access per role through the mcp__<server>__<tool> names in agents.json. Full walkthrough: docs/mcp.md. All configuration — file and path locations plus the full PI_ENSEMBLE_* environment variable reference (main, outcome-verification, and sandbox-mode tables) — lives in docs/configuration.md. All optional; defaults are reasonable for typical use.
The 28 modules under modules/ (vipune memory patterns, output standards, async-task discipline, workflows, etc.) compose into per-role system prompts via manifests/<role>.manifest. To change behaviour for a role: edit the module (e.g. modules/core/vipune-baseline.md) or add a new one referenced in a manifest, run bun run build from the repo root, and re-launch Pi — children pick up the new prompts on next spawn. pi-prompts/*.md (slash-command bodies) are read at runtime — no rebuild needed.
- Three permission modes. Sandbox (recommended): container fence IS the trust boundary; no per-call gating. Interactive host: also no per-call gating by default — the agent runs as your UID with the same FS / network / creds as you, so per-call prompts at runtime volumes are theatre. Headless (
pi -p) orPI_ENSEMBLE_STRICT_PERMISSIONS=1: 3-tier allow/deny/ask policy applies (agents.json+ per-host and per-project overlays); headless hard-denies on ask. Use sandbox for confined execution, interactive host when you trust the agent, headless or strict-opt-in for automation / paranoia. - Cost. Six-pass review on a typical PR is roughly 6 × ~2K tokens output per child plus context — order of
$0.02–$0.10per cycle on cheap Cerebras models, more on Anthropic. - Worktrees are git CLI calls. Will be migrated to the safer
pi-worktreeplugin when its programmatic API stabilises. - Smoke tests live in
extension/smoke-tests/;*-live.tsfiles spawn real Pi children (a few cents per run), CI runs the offline ones.
See docs/pi-compatibility.md — the single maintained "last verified against" line, the non-normative dev pins in extension/package.json, and the bump procedure.
How to report a vulnerability (GitHub's private vulnerability reporting), what's in and out of scope, the acknowledgement window, and the honest trust model: SECURITY.md.
- Pi (
@earendil-works/pi-coding-agent) by Mario Zechner — the terminal coding agent this extends. - The modular prompt architecture, vipune doctrine, and six-lens code-review pattern originated in an opencode configuration project.
- Sibling Pi extensions
pi-worktreeandpi-permissions— planned integration points for safer worktrees and per-role tool allowlists.
Apache 2.0. See LICENSE.
