@rhinos0608/pi-subagents

extension

A pi extension that brings Claude Code-style autonomous sub-agents to pi. Fork of @tintinweb/pi-subagents.

by · v0.7.4 · published 2mo ago

$ pi install npm:@rhinos0608/pi-subagents
downloads/mo
141
stars
last push
open issues

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

274 downloads · last 12 weeks (weekly)

README

@rhinos0608/pi-subagents

A pi extension that brings Claude Code-style autonomous sub-agents to pi. Spawn specialized agents that run in isolated sessions — each with its own tools, system prompt, model, and thinking level. Run them in foreground or background, steer them mid-run, resume completed sessions, and define your own custom agent types.

Status: Early release.

🔱 Fork Notice — This is a divergent fork of tintinweb/pi-subagents by rhinos0608. It adds a fork subagent feature: sub-agents can be spawned as children that receive the parent's exact system prompt and conversation context — ideal for delegated work that needs full project awareness without inheriting the parent's session state. See the Fork section for details.

pi-subagents screenshot

https://github.com/user-attachments/assets/8685261b-9338-4fea-8dfe-1c590d5df543

Features

  • Claude Code look & feel — same tool names, calling conventions, and UI patterns (Agent, get_subagent_result, steer_subagent) — feels native
  • Parallel background agents — spawn multiple agents that run concurrently with automatic queuing (configurable concurrency limit, default 4) and smart group join (consolidated notifications)
  • Live widget UI — persistent above-editor widget with animated spinners, live tool activity, token counts, and colored status icons
  • Conversation viewer — select any agent in /agents to open a live-scrolling overlay of its full conversation (auto-follows new content, scroll up to pause)
  • Custom agent types — define agents in .pi/agents/<name>.md with YAML frontmatter: custom system prompts, model selection, thinking levels, tool restrictions
  • Mid-run steering — inject messages into running agents to redirect their work without restarting
  • Session resume — pick up where an agent left off, preserving full conversation context
  • Graceful turn limits — agents get a "wrap up" warning before hard abort, producing clean partial results instead of cut-off output
  • Case-insensitive agent types"explore", "Explore", "EXPLORE" all work. Unknown types fall back to general-purpose with a note
  • Fuzzy model selection — specify models by name ("haiku", "sonnet") instead of full IDs, with automatic filtering to only available/configured models
  • Context inheritance — optionally fork the parent conversation into a sub-agent so it knows what's been discussed
  • Persistent agent memory — three scopes (project, local, user) with automatic read-only fallback for agents without write tools
  • Git worktree isolation — run agents in isolated repo copies; changes auto-committed to branches on completion
  • Skill preloading — inject named skill files from .pi/skills/ into agent system prompts
  • Tool denylist — block specific tools via disallowed_tools frontmatter
  • Styled completion notifications — background agent results render as themed, compact notification boxes (icon, stats, result preview) instead of raw XML. Expandable to show full output. Group completions render each agent individually
  • Event bus — lifecycle events (subagents:created, started, completed, failed, steered, compacted) emitted via pi.events, enabling other extensions to react to sub-agent activity
  • Cross-extension RPC — other pi extensions can spawn and stop subagents via the pi.events event bus (subagents:rpc:ping, subagents:rpc:spawn, subagents:rpc:stop). Standardized reply envelopes with protocol versioning. Emits subagents:ready on load
  • Schedule subagents — pass schedule to the Agent tool to fire on cron / interval / one-shot. Session-scoped jobs with PID-locked persistence; results land via the same subagent-notification followUp path as manual background completions; manage via /agents → Scheduled jobs

Install

pi install npm:@rhinos0608/pi-subagents

Or load directly for development:

pi -e ./src/index.ts

Quick Start

The parent agent spawns sub-agents using the Agent tool:

Agent({
  subagent_type: "Explore",
  prompt: "Find all files that handle authentication",
  description: "Find auth files",
  run_in_background: true,
})

Foreground agents block until complete and return results inline. Background agents return an ID immediately and notify you on completion.

Scheduling

Add a schedule field to register the agent to fire later instead of running now:

Agent({
  subagent_type: "Explore",
  prompt: "Look at recent commits and summarize what changed since last week",
  description: "Weekly commit review",
  schedule: "0 0 9 * * 1",   // 9am every Monday (6-field cron)
})

Schedule formats:

  • Cron — 6-field (second minute hour day-of-month month day-of-week), e.g. "0 0 9 * * 1" for 9am every Monday, "0 */15 * * * *" for every 15 minutes.
  • Interval"5m", "1h", "30s", "2d". Fires repeatedly at that interval.
  • One-shot relative"+10m", "+2h", "+1d". Fires once at that future time.
  • One-shot absolute — full ISO timestamp, e.g. "2026-12-25T09:00:00.000Z".

When a schedule fires, the spawn runs in background and its completion notification arrives in the conversation through the same subagent-notification followUp path as a manually-spawned background agent — your parent agent reasons about the result the same way.

Schedules are session-scoped: they reset on /new and restore on /resume. List and cancel via /agents → Scheduled jobs (creation is the Agent tool's job — there is no parallel manual-create wizard). Storage at <cwd>/.pi/subagent-schedules/<sessionId>.json with PID-based file locking for cross-instance safety.

Disable the feature entirely: /agents → Settings → Scheduling → disabled removes schedule from the Agent tool spec (no LLM-context cost), hides the menu entry, and stops any active scheduler. The schema-level removal takes effect on the next pi session; the runtime kill is immediate. Re-enable from the same menu.

Restrictions:

  • schedule cannot be combined with inherit_context (no parent conversation exists at fire time) or resume (schedules create fresh agents).
  • run_in_background is forced to true.
  • Scheduled fires bypass the maxConcurrent queue so a 5-minute interval cannot be deferred behind long-running manual agents.
  • Headless pi -p doesn't wait for scheduled subagents.

UI

The extension renders a persistent widget above the editor showing all active agents:

● Agents
├─ ⠹ Agent  Refactor auth module · ⟳5≤30 · 5 tool uses · 33.8k token (62%) · 12.3s
│    ⎿  editing 2 files…
├─ ⠹ Explore  Find auth files · ⟳3 · 3 tool uses · 12.4k token (8%) · 4.1s
│    ⎿  searching…
├─ ⠹ Agent  Long-running task · ⟳42 · 38 tool uses · 91.0k token (84% · ↻2) · 2m17s
│    ⎿  reading…
└─ 2 queued

The token field is annotated with two optional signals inside parens:

  • NN% — context-window utilization (color-coded: <70% dim, 70–85% warning, ≥85% error). Omitted when the model has no declared contextWindow, or briefly right after compaction.
  • ↻N — number of times the session has compacted, when > 0. Stays dim; the percent's color carries urgency.

Individual agent results render Claude Code-style in the conversation:

StateExample
Running⠹ ⟳3≤30 · 3 tool uses · 12.4k token (8%) / ⎿ searching, reading 3 files…
Completed✓ ⟳8 · 5 tool uses · 33.8k token (62%) · 12.3s / ⎿ Done
Wrapped up✓ ⟳50≤50 · 50 tool uses · 89.1k token (84% · ↻2) · 45.2s / ⎿ Wrapped up (turn limit)
Stopped■ ⟳3 · 3 tool uses · 12.4k token (8%) / ⎿ Stopped
Error✗ ⟳3 · 3 tool uses · 12.4k token (8%) / ⎿ Error: timeout
Aborted✗ ⟳55≤50 · 55 tool uses · 102.3k token (95% · ↻3) / ⎿ Aborted (max turns exceeded)

Completed results can be expanded (ctrl+o in pi) to show the full agent output inline.

Background agent completion notifications render as styled boxes:

✓ Find auth files completed
  ⟳3 · 3 tool uses · 12.4k token · 4.1s
  ⎿  Found 5 files related to authentication...
  transcript: .pi/output/agent-abc123.jsonl

Group completions render each agent as a separate block. The LLM receives structured <task-notification> XML for parsing, while the user sees the themed visual.

Default Agent Types

TypeToolsModelPrompt ModeDescription
general-purposeall 7inheritappend (parent twin)Inherits the parent's full system prompt — same rules, CLAUDE.md, project conventions
Exploreread, bash, grep, find, lshaiku (falls back to inherit)replace (standalone)Fast codebase exploration (read-only)
Planread, bash, grep, find, lsinheritreplace (standalone)Software architect for implementation planning (read-only)

The general-purpose agent is a parent twin — it receives the parent's entire system prompt plus a sub-agent context bridge, so it follows the same rules the parent does. Explore and Plan use standalone prompts tailored to their read-only roles.

Default agents can be ejected (/agents → select agent → Eject) to export them as .md files for customization, overridden by creating a .md file with the same name (e.g. .pi/agents/general-purpose.md), or disabled per-project with enabled: false frontmatter.

Custom Agents

Define custom agent types by creating .md files. The filename becomes the agent type name. Any name is allowed — using a default agent's name overrides it.

Agents are discovered from two locations (higher priority wins):

PriorityLocationScope
1 (highest).pi/agents/<name>.mdProject — per-repo agents
2$PI_CODING_AGENT_DIR/agents/<name>.md (default ~/.pi/agent/agents/<name>.md)Global — available everywhere

Project-level agents override global ones with the same name, so you can customize a global agent for a specific project. The global location follows the upstream PI_CODING_AGENT_DIR env var — set it to relocate all pi-coding-agent state (agents, skills, settings) to a custom directory.

Example: .pi/agents/auditor.md

---
description: Security Code Reviewer
tools: read, grep, find, bash
model: anthropic/claude-opus-4-6
thinking: high
max_turns: 30
---

You are a security auditor. Review code for vulnerabilities including:
- Injection flaws (SQL, command, XSS)
- Authentication and authorization issues
- Sensitive data exposure
- Insecure configurations

Report findings with file paths, line numbers, severity, and remediation advice.

Then spawn it like any built-in type:

Agent({ subagent_type: "auditor", prompt: "Review the auth module", description: "Security audit" })

Frontmatter Fields

All fields are optional — sensible defaults for everything.

FieldDefaultDescription
descriptionfilenameAgent description shown in tool listings
display_nameDisplay name for UI (e.g. widget, agent list)
toolsall 7Comma-separated built-in tools: read, bash, edit, write, grep, find, ls. none for no tools
extensionstrueInherit MCP/extension tools. false to disable
skillstrueInherit skills from parent. Can be a comma-separated list of skill names to preload from .pi/skills/
memoryPersistent agent memory scope: project, local, or user. Auto-detects read-only agents
disallowed_toolsComma-separated tools to deny even if extensions provide them
isolationSet to worktree to run in an isolated git worktree
modelinherit parentModel — provider/modelId or fuzzy name ("haiku", "sonnet")
thinkinginheritoff, minimal, low, medium, high, xhigh
max_turnsunlimitedMax agentic turns before graceful shutdown. 0 or omit for unlimited
prompt_modereplacereplace: body is the full system prompt (no AGENTS.md / CLAUDE.md inheritance). append: body appended to parent's prompt (agent acts as a "parent twin" — inherits parent's AGENTS.md / CLAUDE.md)
inherit_contextfalseFork parent conversation into agent
run_in_backgroundfalseRun in background by default
isolatedfalseNo extension/MCP tools, only built-in
enabledtrueSet to false to disable an agent (useful for hiding a default agent per-project)

Frontmatter is authoritative. If an agent file sets model, thinking, max_turns, inherit_context, run_in_background, isolated, or isolation, those values are locked for that agent. Agent tool parameters only fill fields the agent config leaves unspecified.

Tools

Agent

Launch a sub-agent.

ParameterTypeRequiredDescription
promptstringyesThe task for the agent
descriptionstringyesShort 3-5 word summary (shown in UI)
subagent_typestringyesAgent type (built-in or custom)
modelstringnoModel — provider/modelId or fuzzy name ("haiku", "sonnet")
thinkingstringnoThinking level: off, minimal, low, medium, high, xhigh
max_turnsnumbernoMax agentic turns. Omit for unlimited (default)
run_in_backgroundbooleannoRun without blocking
resumestringnoAgent ID to resume a previous session
isolatedbooleannoNo extension/MCP tools
isolation"worktree"noRun in an isolated git worktree
inherit_contextbooleannoFork parent conversation into agent

get_subagent_result

Check status and retrieve results from a background agent.

ParameterTypeRequiredDescription
agent_idstringyesAgent ID to check
waitbooleannoWait for completion
verbosebooleannoInclude full conversation log

steer_subagent

Send a steering message to a running agent. The message interrupts after the current tool execution.

ParameterTypeRequiredDescription
agent_idstringyesAgent ID to steer
messagestringyesMessage to inject into agent conversation

Commands

CommandDescription
/agentsInteractive agent management menu

The /agents command opens an interactive menu:

Running agents (2) — 1 running, 1 done     ← only shown when agents exist
Agent types (6)                             ← unified list: defaults + custom
Create new agent                            ← manual wizard or AI-generated
Settings                                    ← max concurrency, max turns, grace turns, join mode
  • Agent types — unified list with source indicators: (project), (global), (disabled). Select an agent to manage it:
    • Default agents (no override): Eject (export as .md), Disable
    • Default agents (ejected/overridden): Edit, Disable, Reset to default, Delete
    • Custom agents: Edit, Disable, Delete
    • Disabled agents: Enable, Edit, Delete
  • Eject — writes the embedded default config as a .md file to project or personal location, so you can customize it
  • Disable/Enable — toggle agent availability. Disabled agents stay visible in the list (marked ) and can be re-enabled
  • Create new agent — choose project/personal location, then manual wizard (step-by-step prompts for name, tools, model, thinking, system prompt) or AI-generated (describe what the agent should do and a sub-agent writes the .md file). Any name is allowed, including default agent names (overrides them)
  • Settings — configure max concurrency, default max turns, grace turns, and join mode at runtime

Graceful Max Turns

Instead of hard-aborting at the turn limit, agents get a graceful shutdown:

  1. At max_turns — steering message: "Wrap up immediately — provide your final answer now."
  2. Up to 5 grace turns to finish cleanly
  3. Hard abort only after the grace period
StatusMeaningIcon
completedFinished naturally green
steeredHit limit, wrapped up in time yellow
abortedGrace period exceeded red
stoppedUser-initiated abort dim

Concurrency

Background agents are subject to a configurable concurrency limit (default: 4). Excess agents are automatically queued and start as running agents complete. The widget shows queued agents as a collapsed count.

Foreground agents bypass the queue — they block the parent anyway.

Join Strategies

When background agents complete, they notify the main agent. The join mode controls how these notifications are delivered. It applies only to background agents.

ModeBehavior
smart (default)2+ background agents spawned in the same turn are auto-grouped into a single consolidated notification. Solo agents notify individually.
asyncEach agent sends its own notification on completion (original behavior). Best when results need incremental processing.
groupForce grouping even when spawning a single agent. Useful when you know more agents will follow.

Timeout behavior: When agents are grouped, a 30-second timeout starts after the first agent completes. If not all agents finish in time, a partial notification is sent with completed results and remaining agents continue with a shorter 15-second re-batch window for stragglers.

Configuration:

  • Configure join mode in /agents → Settings → Join mode

Persistent Settings

Runtime tuning values set via /agents → Settings (max concurrency, default max turns, grace turns, default join mode) persist across pi restarts. Two files, merged on load:

  • Global: ~/.pi/agent/subagents.json — your machine-wide defaults. Edit by hand; the /agents menu never writes here.
  • Project: <cwd>/.pi/subagents.json — per-project overrides. Written by /agents → Settings.

Precedence: project overrides global on any field present in both. Missing fields fall back to the hardcoded defaults (max concurrency 4, default max turns unlimited, grace turns 5, join mode smart).

Example — global defaults for a beefy machine:

mkdir -p ~/.pi/agent
cat > ~/.pi/agent/subagents.json <<'EOF'
{
  "maxConcurrent": 16,
  "graceTurns": 10
}
EOF

Every project now starts with concurrency 16 and grace 10, without ever touching the menu. Individual projects can still override via /agents → Settings.

Failure behavior: missing file is silent; malformed JSON logs a [pi-subagents] Ignoring malformed settings at … warning to stderr; invalid/out-of-range field values are dropped per-field; write failures downgrade the /agents toast to a warning with (session only; failed to persist).

Events

Agent lifecycle events are emitted via pi.events.emit() so other extensions can react:

EventWhenKey fields
subagents:createdBackground agent registeredid, type, description, isBackground
subagents:startedAgent transitions to running (including queued→running)id, type, description
subagents:completedAgent finished successfullyid, type, durationMs, tokens (lifetime { input, output, total }), toolUses, result
subagents:failedAgent errored, stopped, or abortedsame as completed + error, status
subagents:steeredSteering message sentid, message
subagents:compactedAgent's session successfully compactedid, type, description, reason ("manual" / "threshold" / "overflow"), tokensBefore, compactionCount
subagents:scheduledSchedule lifecycle change{ type: "added" | "removed" | "updated" | "fired" | "error", … } (job/agentId/error fields per type)
subagents:scheduler_readyScheduler bound to session, enabled jobs armedsessionId, jobCount
subagents:readyExtension loaded and RPC handlers registered
subagents:settings_loadedPersisted settings applied at extension initsettings (merged global + project)
subagents:settings_changed/agents → Settings mutation was appliedsettings, persisted (booleanfalse on write failure)

tokens.total = input + output + cacheWrite. cacheRead is excluded — each turn's cacheRead is the cumulative cached prefix re-read on that one API call, so summing per-message would over-count it. Use contextUsage.percent (surfaced as (NN%) in the widget) for current context size.

Cross-Extension RPC

Other pi extensions can spawn and stop subagents programmatically via the pi.events event bus, without importing this package directly.

All RPC replies use a standardized envelope: { success: true, data?: T } on success, { success: false, error: string } on failure.

Discovery

Listen for subagents:ready to know when RPC handlers are available:

pi.events.on("subagents:ready", () => {
  // RPC handlers are registered — safe to call ping/spawn/stop
});

Ping

Check if the subagents extension is loaded and get the protocol version:

const requestId = crypto.randomUUID();
const unsub = pi.events.on(`subagents:rpc:ping:reply:${requestId}`, (reply) => {
  unsub();
  if (reply.success) console.log("Protocol version:", reply.data.version);
});
pi.events.emit("subagents:rpc:ping", { requestId });

Spawn

Spawn a subagent and receive its ID:

const requestId = crypto.randomUUID();
const unsub = pi.events.on(`subagents:rpc:spawn:reply:${requestId}`, (reply) => {
  unsub();
  if (!reply.success) {
    console.error("Spawn failed:", reply.error);
  } else {
    console.log("Agent ID:", reply.data.id);
  }
});
pi.events.emit("subagents:rpc:spawn", {
  requestId,
  type: "general-purpose",
  prompt: "Do something useful",
  options: { description: "My task", run_in_background: true },
});

Stop

Stop a running agent by ID:

const requestId = crypto.randomUUID();
const unsub = pi.events.on(`subagents:rpc:stop:reply:${requestId}`, (reply) => {
  unsub();
  if (!reply.success) console.error("Stop failed:", reply.error);
});
pi.events.emit("subagents:rpc:stop", { requestId, agentId: "agent-id-here" });

Reply channels are scoped per requestId, so concurrent requests don't interfere.

Persistent Agent Memory

Agents can have persistent memory across sessions. Set memory in frontmatter to enable:

---
memory: project   # project | local | user
---
ScopeLocationUse case
project.pi/agent-memory/<name>/Shared across the team (committed)
local.pi/agent-memory-local/<name>/Machine-specific (gitignored)
user~/.pi/agent-memory/<name>/Global personal memory

Memory uses a MEMORY.md index file and individual memory files with frontmatter. Agents with write tools get full read-write access. Read-only agents (no write/edit tools) automatically get read-only memory — they can consume memories written by other agents but cannot modify them. This prevents unintended tool escalation.

The disallowed_tools field is respected when determining write capability — an agent with tools: write + disallowed_tools: write correctly gets read-only memory.

Worktree Isolation

Set isolation: worktree to run an agent in a temporary git worktree:

Agent({ subagent_type: "refactor", prompt: "...", isolation: "worktree" })

The agent gets a full, isolated copy of the repository. On completion:

  • No changes: worktree is cleaned up automatically
  • Changes made: changes are committed to a new branch (pi-agent-<id>) and returned in the result

If the worktree cannot be created (not a git repo, no commits, or git worktree add fails), the Agent tool returns a clear error instead of running unisolated — isolation: "worktree" is a strict guarantee, not a hint. Initialize git and commit at least once, or omit isolation.

Skill Preloading

Skills can be preloaded as named files from .pi/skills/ or ~/.pi/skills/:

---
skills: api-conventions, error-handling
---

Skill files (.md, .txt, or extensionless) are read and injected into the agent's system prompt. Project-level skills take priority over global ones. Symlinked skill files are rejected for security.

Tool Denylist

Block specific tools from an agent even if extensions provide them:

---
tools: read, bash, grep, write
disallowed_tools: write, edit
---

This is useful for creating agents that inherit extension tools but should not have write access.

Architecture

src/
  index.ts            # Extension entry: tool/command registration, rendering
  types.ts            # Type definitions (AgentConfig, AgentRecord, etc.)
  default-agents.ts   # Embedded default agent configs (general-purpose, Explore, Plan)
  agent-types.ts      # Unified agent registry (defaults + user), tool name resolution
  agent-runner.ts     # Session creation, execution, graceful max_turns, steer/resume
  agent-manager.ts    # Agent lifecycle, concurrency queue, completion notifications
  cross-extension-rpc.ts # RPC handlers for cross-extension spawn/ping via pi.events
  group-join.ts       # Group join manager: batched completion notifications with timeout
  custom-agents.ts    # Load user-defined agents from .pi/agents/*.md
  memory.ts           # Persistent agent memory (resolve, read, build prompt blocks)
  skill-loader.ts     # Preload skill files from .pi/skills/
  output-file.ts      # Streaming output file transcripts for agent sessions
  worktree.ts         # Git worktree isolation (create, cleanup, prune)
  prompts.ts          # Config-driven system prompt builder
  context.ts          # Parent conversation context for inherit_context
  env.ts              # Environment detection (git, platform)
  ui/
    agent-widget.ts       # Persistent widget: spinners, activity, status icons, theming
    conversation-viewer.ts # Live conversation overlay for viewing agent sessions

Fork Divergence

This is a divergent fork of tintinweb/pi-subagents (v0.7.1).

What's different

  • Fork subagent mode — Sub-agents can be spawned as explicit child processes of the parent agent. Omit subagent_type to trigger fork mode, which clones the parent's exact system prompt byte-for-byte and seeds the child conversation with the parent's last tool-call turn for prompt-cache sharing.
  • Recursive fork guard — Fork detection scans for fork boilerplate in messages to prevent infinite nesting.
  • /fork command — Quick-access slash command to fork a subagent inline.
  • New default agent type fork — Registered as a hidden type (not user-selectable) for programmatic fork execution.

Upstream

The original @tintinweb/pi-subagents package is maintained by tintinweb and can be found at github.com/tintinweb/pi-subagents.

License

MIT — rhinos0608 (fork) / tintinweb (original)