@jmcombs/pi-relay
Relay roles for Pi: run any Pi subagent on an external coding agent (headless Claude Opus via `claude -p`, Grok Build via `grok -p`, or Cursor Agent via `cursor-agent -p`) through a provider seam, driven by the subagent's `model` field.
Package details
Install @jmcombs/pi-relay from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@jmcombs/pi-relay- Package
@jmcombs/pi-relay- Version
1.3.0- Published
- Sep 12, 2026
- Downloads
- 705/mo · 313/wk
- Author
- jmcombs
- License
- MIT
- Types
- extension
- Size
- 81.5 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
],
"image": "https://raw.githubusercontent.com/jmcombs/pi-extensions/main/assets/relay/preview.png"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@jmcombs/pi-relay
Relay roles for the Pi coding agent: run any Pi subagent on an external coding agent instead of a local model — just by setting its
model. Relay registers pi providers (relay-claude,relay-grok,relay-cursor); a subagent whosemodelisrelay-claude/opus,relay-grok/grok-4.5, orrelay-cursor/opusroutes through relay to a headless Claude Opus (claude -p), Grok Build (grok -p), or Cursor Agent (cursor-agent -p), which runs its own tool loop and returns the final result.
Not affiliated with or endorsed by Anthropic, xAI, or Anysphere. Claude and Opus are trademarks of Anthropic, PBC; Grok is a trademark of xAI; Cursor is a trademark of Anysphere, Inc.
Fixes
- oh-my-pi system prompts are normalized. oh-my-pi supplies
systemPromptas astring[]; relay joins those sections before building the backend prompt. - oh-my-pi tasks terminate through local
yield. When the host context exposes oh-my-pi's task-onlyyieldtool, relay returns the backend text plus one synthetic terminal yield call. oh-my-pi therefore finalizes after one external CLI run instead of issuing text-only reminders and repeating the request.
How It Works
A relay role is an existing pi-subagent (its persona .md + referenced
SKILL.mds). Nothing about the subagent changes except the processor:
- Trigger + model — set a subagent's
modeltorelay-claude/opus,relay-grok/grok-4.5, orrelay-cursor/opus. pi's nativeresolveModelroutes the completion to relay's registered provider →claudeDriver/grokDriver/cursorDriver→claude -p … --model opus --effort <level>/grok -p … --model grok-4.5 --reasoning-effort <level>/cursor-agent -p … --model claude-opus-4-8-thinking-high(Pi thinking selects the listed Cursor id). - Persona + skills — when pi runs a subagent it assembles the persona body +
a skill injection into the (child) session's system prompt, where skills are
<available_skills>references (name/description/location). Relay reads each referencedSKILL.mdand inlines its full content into the prompt it writes via the backend's own system-prompt mechanism (claude's--system-prompt-file,grok's inline--system-prompt-override/--rules, or prepended onto Cursor's user prompt — Cursor has no system-prompt flag), so the methodology is guaranteed present (deterministic — no model re-echo, no drift). - Tools — each driver maps the subagent's pi tools onto its backend's own
permission model (
read → Read,bash → Bash,edit → Edit,write → Write,grep → Grep,find → Glob); pi-only tools with no external equivalent (e.g.subagent,ls) are dropped. Claude gets--allowedTools; Grok gets one--allow <Tool>flag per tool plus--permission-mode dontAsk(fail-closed — unlisted tools are silently declined, never a hang or a blanket bypass). The map is a driver function (D10). Cursor has no--allowedToolsargv flag;cursorDrivermaps the same pi names onto a tempcli-config.jsonallow/deny list (Read(**/*),Shell(*),Write(**/*)) and pointsCURSOR_CONFIG_DIRat it.--force/--yoloare never passed. - Single external run — the relayed subagent has no external equivalent for
pi/oh-my-pi orchestration tools. The external agent runs its own tool loop once
and returns final text. pi consumes that text directly; oh-my-pi receives the same
text plus a local terminal
yieldcall generated by relay.
The flagship consumer is phase verification: the verifier subagent runs as a
relayed subagent (model: relay-claude/opus, read-only tools) — no bespoke tool,
no inline prompt.
Backend
The verify quality bar is Claude Opus only (D1), reached through the
subscription claude -p CLI (billed to your Claude subscription via
oauthAccount — never the Anthropic API, never a local model). The verify role
is read-only: claude is invoked with a scoped --allowedTools allowlist and
never with --dangerously-skip-permissions. On a cut run (wall-cap or abort)
relay surfaces an UNVERIFIED error result — it never auto-passes.
relay-grok (Grok Build, grok -p) is a second live driver available for generic
subagent dispatch — it does not change the verify quality bar. Per D1, a new
backend only becomes verify-eligible after it clears the accuracy benchmark; until
then, route the verifier role to relay-claude/opus and use relay-grok for
other subagents. Grok is invoked with --permission-mode dontAsk plus one
--allow <Tool> per allowed tool (verified fail-closed and non-interactive —
never --always-approve or --permission-mode auto/bypassPermissions).
relay-cursor (Cursor Agent, cursor-agent -p) is a third live driver. Cursor is
invoked with --output-format json and --trust (skip the workspace-trust prompt).
--force / --yolo (Cursor's permission bypass) and --sandbox are never
passed. Pi relay-cursor/auto maps to --model auto. Pi relay-cursor/opus maps
by thinking level onto Cursor listed ids (opus / :off → claude-opus-4-8-high;
:high → claude-opus-4-8-thinking-high). The mapper strips the relay-cursor/
provider prefix first; an id that does not resolve to a listed id is rejected up
front rather than forwarded. Cursor has no system-prompt flag, so persona +
skills are prepended onto the user prompt. Tool scoping is a temp cli-config.json
allow/deny list via CURSOR_CONFIG_DIR, not an argv allowlist.
Claude and Grok keep --model as the alias (opus, grok-4.5) and apply Pi
thinking as --effort / --reasoning-effort. Relay catalogs these models with
reasoning: true so Pi's thinking UI matches what the drivers send.
A driver/adapter seam (AgentDriver in drivers/claude.ts) keeps the provider
backend-agnostic. claudeDriver, grokDriver, and cursorDriver are the live
implementations, each owning its own pi→backend tool-name map (D10);
drivers/codex.ts is a documented seam-only stub (codex exec, -s read-only) for
a future OpenAI Codex backend. roles/resolver.ts is backend-neutral: it inlines skill references to
full content (expandSkillReferences) and resolves a persona+skills role from
disk (used off the pi-subagents path). The provider streams the completion
through pi's own createAssistantMessageEventStream() (@earendil-works/pi-ai).
Requirements
- Pi (loads the extension via jiti — no build step)
- Node
>= 22.19.0 - The
claudeCLI onPATH, authenticated via your Claude subscription (oauthAccount), forrelay-claude - The
grok(Grok Build) CLI onPATH, authenticated (grok loginorXAI_API_KEY), forrelay-grok - The
cursor-agentCLI onPATH, authenticated (cursor-agent loginorCURSOR_API_KEY), forrelay-cursor
Configuration
PI_RELAY_WALL_MS— wall-cap backstop for a single relayed run, in milliseconds (default600000). On a cut run relay reports an UNVERIFIED error result.PI_RELAY_HEARTBEAT_MS— interval, in milliseconds, at which the provider pushes a no-op stream beat while a relayed run is in flight (default20000; set0to disable). A singleclaude -pcompletion emits nothing until it finishes, so without a beat pi-subagents' parent run sees "no observed activity" and falsely flips the child toneeds_attentionat its 60s threshold. Each beat surfaces as a pimessage_update, advancing the parent's activity clock; the verdict still rides only on the terminal result, so the beats never affect it.
Install
# Globally (recommended)
pi install npm:@jmcombs/pi-relay
# For a single session, without installing
pi -e npm:@jmcombs/pi-relay
# Oh My Pi
omp plugin install @jmcombs/pi-relay
See the Pi packages documentation for git, local path, project-scoped install, and filtering options.
Usage
Relay registers the relay-claude, relay-grok, and relay-cursor providers;
you use any of them by pointing a subagent (or a whole session) at it through
model:
# Route a whole session through the relay provider
pi --model relay-claude/opus "…"
pi --model relay-grok/grok-4.5 "…"
pi --model relay-cursor/auto "…"
pi --model relay-cursor/opus "…"
To run an existing subagent through relay, set its model frontmatter to
relay-claude/opus, relay-grok/grok-4.5, or relay-cursor/opus and make relay
discoverable in the subagent's child pi (an installed package, or the agent's
extensions field).
oh-my-pi discovers custom task agents from ~/.omp/agent/agents/*.md and
.omp/agents/*.md. Set the agent's model normally:
---
name: relay-reviewer
description: Review a change through Claude Code.
model: relay-claude/opus
tools: read, grep
---
oh-my-pi keeps yield local; relay never forwards it to Claude, Grok, or Cursor.
Extending — adding a driver
Relay is backend-agnostic through the AgentDriver seam (D10). claudeDriver,
grokDriver, and cursorDriver are the live implementations; drivers/codex.ts is
a documented seam-only stub for a future OpenAI Codex backend. To add a driver for
another coding agent (Codex, Gemini CLI, …) — the AgentDriver API, the pi→backend
tool-name mapping, the read-only/fail-safe constraints, and a step-by-step guide —
see CONTRIBUTING.md in this package.
Development
This package lives in the pi-extensions monorepo.
See the repo-root CONTRIBUTING.md for project conventions, and
this package's CONTRIBUTING.md for the driver seam.
# From the repo root
npm ci
npm run check # full quality gate
node packages/relay/scripts/harness.mjs # manual provider proof vs. real `claude -p`
node packages/relay/scripts/harness.mjs --model relay-grok/grok-4.5 # same, vs. real `grok -p`
node packages/relay/scripts/harness.mjs --model relay-cursor/auto # same, vs. real `cursor-agent -p`
# Unreleased worktree only (--no-extensions -e ./packages/relay):
./packages/relay/scripts/prove-thinking-map.sh catalog # list-models vs expected context/max/thinking
./packages/relay/scripts/prove-thinking-map.sh argv # Pi thinking → real CLI flags (six -p runs)
License
MIT © Jeremy Combs
