@harms-haus/pi-subagents
Pi extension for spawning parallel sub-agents with live TUI windows
Package details
Install @harms-haus/pi-subagents from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@harms-haus/pi-subagents- Package
@harms-haus/pi-subagents- Version
0.1.0- Published
- May 25, 2026
- Downloads
- not available
- Author
- baharms
- License
- MIT
- Types
- extension
- Size
- 280 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-subagents
A pi extension that allows the main agent to spawn parallel sub-agents, with each sub-agent's latest output rendered in a rolling TUI window inline with the main agent's conversation history.
Sub-agents can optionally use named profiles that pre-configure provider/model, system prompts, thinking levels, and other model settings. Profiles are stored as individual .md files with YAML frontmatter.
Installation
Installed as a pi package (Recommended)
pi install git:github.com/harms-haus/pi-subagents
Or use https:// / ssh:// instead of git: if you prefer.
Local Development
Clone the repo and install the local path (use -l for project-local install):
cd pi-subagents
pi install . -l
Usage
Once installed, the LLM can use the tool:
{
"delegate_to_subagents": {
"tasks": [
{
"name": "linter-src",
"prompt": "Review and fix all linting errors in the src/ directory."
},
{
"name": "linter-tests",
"prompt": "Review and fix all linting errors in the tests/ directory.",
"profile": "fast-worker"
}
]
}
}
Providing File Context
Each task can include a files array to read file contents and prepend them to the sub-agent's prompt:
{
"delegate_to_subagents": {
"tasks": [
{
"name": "fix-lint",
"prompt": "Fix all linting errors in this file.",
"files": ["src/utils.ts"]
}
]
}
}
File specs support line ranges: { "path": "src/main.ts", "start": 10, "end": 50 }, { "path": "log.txt", "tail": 20 }, or { "path": "config.json", "head": 5 }.
See docs/tools-reference.md for complete parameter documentation.
After delegate_to_subagents completes, it returns session IDs for each task. Use get_subagent_output to retrieve the final text output:
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
tasks |
Array<{name, prompt, cwd?, profile?, timeout?, resume?, files?}> |
Yes | Array of tasks to delegate. Each gets its own sub-agent process. |
profile |
string |
No | Default profile for all tasks (overridden by per-task profile) |
Each task:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Display label shown in the TUI window header |
prompt |
string |
Yes | Prompt sent to the sub-agent (same as typing into pi directly) |
cwd |
string |
No | Working directory for the sub-agent (default: current directory) |
profile |
string |
No | Named profile to use for this sub-agent (see below) |
timeout |
number |
No | Timeout in seconds for this sub-agent. Default: 600. Timeouts auto-extend when the sub-agent is actively producing output — after the initial timeout expires, the sub-agent is only killed after a configurable idle period (see extend_timeout_debounce setting). |
resume |
string |
No | Previous session ID to resume from. The resumed sub-agent receives the prior session's transcript as context. Only completed or errored sessions can be resumed. |
files |
Array<FileSpec> |
No | File paths to read and prepend to the sub-agent's prompt. See "Providing File Context" above. |
The maxLinesPerWindow setting is configured in settings.json under subagents.maxLinesPerWindow (default: 15).
Retrieving Sub-agent Output
After delegate_to_subagents completes, each task has a session ID. Use these tools to retrieve results:
get_subagent_output(sessionId)— Returns the last assistant text output from a sub-agent session. For resumed sessions, returns the latest run's output. This is the primary way to get results.get_subagent_session(sessionId)— Returns the full session transcript including all messages, tool calls, and results. For resumed sessions, returns all runs' data concatenated. Use for debugging.list_subagent_profiles()— Lists all available subagent profiles that can be used withdelegate_to_subagents.
{
"get_subagent_output": {
"sessionId": "a1b2c3d4e5f6a7b8"
}
}
Resuming Sessions
Use the resume parameter to continue work from a previously completed (or errored) sub-agent session. The resumed agent receives the full transcript of all prior runs prepended to its prompt:
{
"delegate_to_subagents": {
"tasks": [
{
"name": "continue-refactor",
"prompt": "Continue the refactoring. Focus on the remaining utility modules.",
"resume": "a1b2c3d4e5f6a7b8"
}
]
}
}
Key behaviors:
- The resumed agent's prompt is prefixed with
Previously:\n\n<transcript>\n\nInstructions:\n\n<your prompt>, giving it full context of prior work. - Only completed or errored sessions can be resumed. Running sessions will throw an error.
- A session can be resumed multiple times — each resume creates a new "run" appended to the session record.
get_subagent_outputreturns output from the latest run only.get_subagent_sessionreturns all runs concatenated with run separators.
Subagent Profiles
Profiles let you pre-configure the provider, model, system prompt, thinking level, and other settings for sub-agents. Each profile is a .md file with YAML frontmatter.
Profile Locations
| Scope | Directory |
|---|---|
| Global | ~/.pi/agent/agent-profiles/*.md |
| Project | .pi/agent-profiles/*.md |
Project-local profiles override global profiles with the same name.
Example Profiles
~/.pi/agent/agent-profiles/code-reviewer.md:
---
name: code-reviewer
provider: anthropic
model: claude-sonnet-4-5
thinkingLevel: high
tools: read,bash,grep,find
---
You are an expert code reviewer. Focus on bugs, security issues, and performance problems. Be thorough but concise.
~/.pi/agent/agent-profiles/fast-worker.md:
---
name: fast-worker
model: dashscope/qwen3.5-plus
appendSystemPrompt: Be concise. Skip explanations unless asked.
thinkingLevel: off
---
.pi/agent-profiles/researcher.md (project-local):
---
name: researcher
provider: openai
model: gpt-4o
appendSystemPrompt: Use web search to find information. Cite sources when possible.
---
You are a research assistant.
Profile Fields
| Field | Type | Description |
|---|---|---|
name |
string |
Required. Profile identifier (from YAML frontmatter name field) |
provider |
string |
Provider name (e.g., "anthropic", "openai", "dashscope") |
model |
string |
Model pattern or ID. Supports "provider/id" format and ":thinking" shorthand (e.g., "sonnet:high") |
systemPrompt |
N/A | Set via the body of the markdown file (text after ---). Replaces the default system prompt entirely. |
appendSystemPrompt |
string |
Append text to the default system prompt |
thinkingLevel |
string |
Thinking level: "off", "minimal", "low", "medium", "high", "xhigh" |
noTools |
boolean |
Disable all tools |
tools |
string or string[] |
Comma-separated string or YAML array of tool names to enable (allowlist) |
excludeTools |
string or string[] |
Comma-separated string or YAML array of tool names to exclude (blacklist; mutually exclusive with tools) |
noExtensions |
boolean |
Disable all extensions |
extensions |
string or string[] |
Comma-separated string or YAML array of extension paths to load |
noSkills |
boolean |
Disable skills. Mutually exclusive with suggestedSkills and loadSkills |
suggestedSkills |
string or string[] |
Skill names to suggest to the sub-agent via --skill CLI flags; the model chooses whether to load them |
loadSkills |
string or string[] |
Skill names to pre-load into the sub-agent's system prompt (content wrapped in <loaded_skill> XML tags) |
noContextFiles |
boolean |
Disable AGENTS.md/CLAUDE.md context files |
apiKey |
string |
Custom API key (stored as PI_API_KEY env var, not in CLI args) |
extraArgs |
string or string[] |
Comma-separated string or YAML array of additional CLI arguments |
Array fields (tools, extensions, extraArgs, suggestedSkills, loadSkills) support both YAML arrays and comma-separated strings:
tools:
- read
- bash
- grep
or equivalently:
tools: read,bash,grep
Using Profiles
Per-task profile — each task specifies its own profile:
{
"delegate_to_subagents": {
"tasks": [
{ "name": "review", "prompt": "Review src/...", "profile": "code-reviewer" },
{ "name": "research", "prompt": "Find best practices for...", "profile": "researcher" }
]
}
}
Default profile for all tasks — set at the top level, overridden by per-task profiles:
{
"delegate_to_subagents": {
"profile": "fast-worker",
"tasks": [
{ "name": "task-a", "prompt": "..." },
{ "name": "task-b", "prompt": "...", "profile": "code-reviewer" }
]
}
}
Profile Resolution Order
- Per-task
profilefield (highest priority) - Top-level
profileparameter - If neither is specified, no profile is applied (uses pi defaults)
Profiles are loaded from .md files:
- Global:
~/.pi/agent/agent-profiles/*.md - Project-local:
.pi/agent-profiles/*.md(overrides global profiles with the same name)
The profile cache refreshes every 5 seconds.
Settings
Additional settings are configured in settings.json under the subagents key:
| Setting | Type | Default | Description |
|---|---|---|---|
maxLinesPerWindow |
number |
15 |
Number of lines shown in each sub-agent's rolling TUI window |
commandPreviewWidth |
number |
Terminal width − 4 (TTY) or 160 (non-TTY) |
Controls tool call preview truncation width in the rolling window. In TTY mode, terminal width − 4 is used as a hard override — settings files are never consulted. In non-TTY mode, falls back through settings files (global → project → default 160). Minimum: 20 |
extend_timeout_debounce |
number |
30 |
Seconds of idle time (no output activity) before a timed-out sub-agent is killed. The initial timeout starts this idle window; any output resets it. Range: 0–300. |
looping_tool_count |
number |
5 |
Number of consecutive tool calls checked for loop detection. Set to 0 to disable. Range: 0–50. |
looping_tool_similarity |
number |
0.95 |
Bigram similarity threshold for loop detection. When the last looping_tool_count tool calls are all pairwise similar above this threshold, the sub-agent is killed. Range: 0–1. |
Settings are loaded from ~/.pi/agent/settings.json (global) and .pi/settings.json (project-local, overrides global). Note: commandPreviewWidth settings are only consulted in non-TTY mode.
The /profile Command
Use /profile interactively to manage subagent profiles without editing files by hand:
| Command | Description |
|---|---|
/profile list |
List all profiles with summaries |
/profile show <name> |
Display full details of a profile |
/profile <name> |
Alias for show |
/profile create <name> |
Interactively create a new profile |
/profile edit <name> |
Interactively edit an existing profile |
/profile delete <name> |
Delete a profile |
Interactive Editor
/profile create and /profile edit walk you through each setting:
- Scope — save to global (
~/.pi/agent/agent-profiles/) or project-local (.pi/agent-profiles/) directory - Provider — e.g.
anthropic,openai,dashscope - Model — supports
provider/idand:thinkingshorthand - System prompt — the body text of the
.mdfile (replaces default system prompt) - Append system prompt — optionally append to the default
- Thinking level — off, minimal, low, medium, high, xhigh
- Tools — choose to disable all (
noTools), enable a specific set (tools), or exclude specific tools (excludeTools) - Extensions — restrict or disable
- Skills — if skills are already set, offers to remove them; otherwise asks whether to configure skills, then prompts for comma-separated suggested and/or pre-loaded skill names
- Review & save — shows full profile as markdown before confirming
You can skip any field by answering "No" — it will be omitted from the profile (using pi defaults).
Features
- Parallel execution: Multiple sub-agents run concurrently (up to 4 at a time by default)
- Rolling window: Each sub-agent shows its latest N lines in real-time
- Live updates: The TUI re-renders as sub-agents stream output
- Expandable (Ctrl+O): Collapse to rolling window, expand to see full sub-agent output
- Error handling: Non-zero exit codes and errors are highlighted
- Abort support: Hitting Escape cancels all running sub-agents
- Session resume: Continue work from completed/errored sessions with full transcript context
- Per-task timeouts: Configurable timeout per sub-agent (default 600s), with auto-extension while the agent remains active
- Loop detection: Automatically kills sub-agents that repeat the same tool calls in a tight loop
- Session persistence: Sessions are persisted to the main agent's session log immediately after each sub-agent completes. On agent restart, sessions are reconstructed from the log — no data is lost across restarts.
Architecture
Main Agent TUI
│
└── delegate_to_subagents
│
├── Resolve profiles from .md files
│ ├── Global: ~/.pi/agent/agent-profiles/*.md
│ └── Project: .pi/agent-profiles/*.md
│
├── Validate profile skills (suggestedSkills/loadSkills vs noSkills)
│
├── Resolve skill names → file paths (suggestedSkills) or injected content (loadSkills)
│
├── Validate resume targets (must be completed/errored)
│
├── For each task (concurrency ≤ 4):
│ │
│ ├── [resume?] Inject prior session transcript into prompt
│ │
│ ├── Spawn: pi --mode json -p --no-session [profile args...] "prompt"
│ │ │
│ │ ├── Parse JSONL stdout events
│ │ ├── Update rolling window (latest N lines)
│ │ ├── Track tool calls & results
│ │ └── [timeout?] Abort if task timeout exceeded
│ │
│ ├── Store session data (messages, status, exit code)
│ └── persistSession → pi.appendEntry() (fault-tolerant)
│
├── On session_start (restart/resume):
│ └── Reconstruct sessionStore from persisted entries
│ └── Stale "running" sessions → auto-converted to "error"
│
└── Return session IDs → get_subagent_output / get_subagent_session
Each sub-agent is a separate pi process in JSON mode. We parse JSONL events from stdout and maintain a rolling line buffer per agent. The tool's renderResult builds a Container of Text components, displayed inline with the conversation history.