npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jordyvanvorselen/pi-interactive-subagents

v1.2.0

Published

Interactive subagents for pi - spawn, orchestrate, and manage sub-agent sessions in Herdr panes

Readme

pi-interactive-subagents

Async subagents for pi, running in Herdr panes. Spawn a sub-agent, keep working in the main session, and get the result steered back when it finishes. Fully non-blocking.

Herdr-only fork. See Acknowledgements for the upstream project, which supports tmux, cmux, zellij, and WezTerm.

Installation

pi install npm:@jordyvanvorselen/pi-interactive-subagents

Add -l to install for the current project only (.pi/settings.json). Update later with pi update.

How it works

subagent() returns immediately. The sub-agent runs in its own Herdr surface, created with --no-focus so it never steals keyboard focus:

  • Spawned by the main session — a new tab, labelled with the sub-agent's name. One tab per sub-agent keeps the tab bar readable as a list of what is running.
  • Spawned by another sub-agent — a split off the parent's pane ($HERDR_PANE_ID), so a whole lineage stays inside one tab.

The pane carries the same name in the Herdr sidebar. Set PI_SUBAGENT_TOP_LEVEL_SURFACE=pane to put top-level sub-agents in split panes instead of tabs (the default constant is SUBAGENT_TOP_LEVEL_SURFACE in pi-extension/subagents/herdr.ts). A live widget above the input tracks every running sub-agent, and when one finishes, its result is steered into the main session as a notification that triggers a new turn.

╭─ Subagents ──────────────────────────── 2 running ─╮
│ 00:23  map-auth-flow    active · bash 7m           │
│ 00:45  research-oauth   waiting 2m                 │
╰────────────────────────────────────────────────────╯

Spawn several in parallel — they run concurrently and steer results back independently as each finishes.

Split direction follows the Herdr geometry rule: a wide parent pane splits right, a narrow or tall one splits down. Force one direction with PI_SUBAGENT_SPLIT_DIRECTION=right|down|auto (the default constant is SUBAGENT_SPLIT_DIRECTION in pi-extension/subagents/herdr.ts).

Panes are kept evenly sized: after every spawn and exit (debounced) the extension reads herdr pane layout and issues herdr pane resize calls so each column and row gets an equal share. Herdr has no named layouts, so this is the equivalent of tmux's even-horizontal.

Steering a running sub-agent goes through herdr agent prompt, so the message respects bracketed paste and is refused with agent_blocked if the child is sitting at an approval or question dialog.

Herdr desktop app

In the Herdr desktop app (herdr-app), terminals run in herdr-core and the extension drives the hc CLI instead of herdr. It detects the app by HC_TERMINAL_ID. When pi runs in a Herdr TUI pane inside the app, the TUI path above applies.

  • Sub-agents land in the same places: a new named tab for the main session, a split in the parent's tab for nested sub-agents. Neither moves your focus.
  • The app's Agents list nests every sub-agent under the agent that started it, with its status.
  • Nothing is typed into a shell. The launch script runs directly through your login shell, so the pane opens straight into pi with your usual PATH. The 500 ms shell-ready delay does not apply.
  • The extension learns that a sub-agent ended from herdr-core, not from its screen. The pane closes when the sub-agent exits.
  • The app chooses the split side, so PI_SUBAGENT_SPLIT_DIRECTION and the even pane sizing apply to the TUI only.

If your shell startup is slow and launch commands get dropped before the prompt is ready, raise the delay:

export PI_SUBAGENT_SHELL_READY_DELAY_MS=2500   # default: 500

Tools

| Tool | Description | | --- | --- | | subagent | Spawn a sub-agent in a dedicated Herdr tab (or pane, when nested) (async) | | subagent_message | Message a sub-agent by name — steers it if running, resumes its session if finished | | subagents_list | List available agent definitions | | ask_question | (sub-agent sessions only) Ask the orchestrator a question and wait for the reply |

There is also a /subagent <agent> <task> command for spawning directly.

Spawning

subagent({ agent: "scout", task: "Analyze the auth module" });
subagent({ agent: "worker", name: "dark-mode", task: "Implement the dark mode toggle" });

| Parameter | Type | Default | Description | | --------- | ---- | ------- | ----------- | | agent | string | required | Which agent to spawn (must be known and permitted) | | task | string | required | Task prompt | | name | string | agent name | Short descriptive label for the tab/pane and widget, e.g. auth-token-refactor. Always set it. Must be unique — duplicates are auto-suffixed (scout, scout-2, …) | | model | string | agent's model | Override the model for this spawn | | cwd | string | agent's cwd | Working directory (see Role folders) |

Messaging

subagent_message is addressed by name only. Names are unique per session and persist after a sub-agent finishes, so the same name works either way:

subagent_message({ name: "scout", message: "Also check the auth middleware" });
  • Running — the message is typed into the live pane (newlines flattened) and picked up at the next turn boundary. The call returns immediately; the eventual completion still arrives as a steer message.
  • Finished — the session is resumed with the message as the follow-up task, like a fresh spawn: fire-and-forget, always autonomous, result steered back later. The resumed run reclaims its original name.

Every spawn records name → session file in artifacts/<sessionId>/subagent-registry.json, so names stay addressable across pi restarts. A nested sub-agent that spawns children gets its own registry keyed by its own session id. Resume is refused with a clear error (listing known names) if the name isn't registered, the session file is gone, or the session predates sandboxed resume.

Resume replays the original sandbox. At spawn time the fully-resolved loadout — tool allowlist, backing extensions, model, thinking level, system prompt, spawn whitelist, cwd — is snapshotted to <session>.loadout.json. Resume rebuilds the exact same restricted process from that snapshot rather than relaunching unrestricted.

ask_question

A sub-agent can ask its orchestrator a single freeform question when requirements are ambiguous or a decision materially affects the work. The session stays open (parked as waiting) instead of exiting; the parent is notified with the sub-agent's name, replies via subagent_message({ name, message }), and the reply arrives as the sub-agent's next turn. Parallel questions are supported — each waiting sub-agent has its own name.

If the reply arrives while the sub-agent is still mid-turn, it is absorbed into the current turn — either way the question is marked answered and the session exits normally when the work is done. If the parent never replies, the pane stays open until a human closes it. Only available inside sub-agent sessions.

Bundled agents

| Agent | Model | Tools | Role | | ----- | ----- | ----- | ---- | | scout | openrouter/z-ai/glm-5.3 | read, grep, find, ls | Fast read-only codebase recon | | researcher | openrouter/z-ai/glm-5.3 | web_search, web_fetch, safe_bash | Web research, synthesized into a sourced brief | | worker | openrouter/z-ai/glm-5.3 | read, write, edit, bash, web_search, web_fetch + spawning | General implementer; may spawn scout and researcher |

All three are autonomous (auto-exit: true) and carry their identity in the system prompt (system-prompt: append).

Custom agents

Place a .md file in .pi/agents/ (project) or ~/.pi/agent/agents/ (global). Discovery priority: project > global > package-bundled — a project-local file overrides a bundled agent with the same name.

---
name: my-agent
description: Does something specific
model: openrouter/z-ai/glm-5.3
thinking: medium
tools: read, edit, write, safe_bash, web_search
session-mode: lineage-only
auto-exit: true
---

You are a specialized agent that does X...

Frontmatter reference

| Field | Type | Description | | ----- | ---- | ----------- | | name | string | Agent name (used in agent: "my-agent") | | description | string | Shown in subagents_list | | model | string | Default model | | thinking | string | minimal, low, medium, or high | | tools | string | Strict tool allowlist. Built-ins: read, write, edit, bash, grep, find, ls. Extension-backed: web_search, web_fetch, safe_bash, video_extract, youtube_search, google_image_search. Only the extensions backing the listed tools are loaded into the child | | subagent_agents | string | Comma-separated agent names this agent may spawn. Presence of this field grants the spawning toolset (subagent, subagent_message, subagents_list) and restricts spawn targets to the list. Omit it and the agent cannot spawn at all | | skills | string | Comma-separated skill names to auto-load | | session-mode | string | standalone (default), lineage-only, or fork — see below | | system-prompt | string | append or replace: pass the body as the child's --append-system-prompt / --system-prompt. Omit and the body is prepended to the task prompt instead | | auto-exit | boolean | Auto-shutdown when the agent finishes (see below) | | interactive | boolean | Whether stall/recovery transitions wake the parent (see below) | | cwd | string | Default working directory | | disable-model-invocation | boolean | Hide from subagents_list; still spawnable by explicit name | | cli | string | claude runs the agent via the Claude Code CLI instead of pi |

session-mode

  • standalone — fresh session, no lineage link to the caller (default)
  • lineage-only — fresh session with parentSession linkage for discovery/fork UX, but no copied turns
  • fork — child session seeded with the caller's conversation context

auto-exit

With auto-exit: true, the session shuts down when the agent's turn ends — the agent just writes its final message and stops (there is no "done" tool). The last assistant message becomes the summary returned to the parent. Recommended for all autonomous agents.

Notes:

  • Manual input does not strand an auto-exit sub-agent. If a human types into the pane, the session still closes once that turn completes normally — only an escape/abort leaves it open.
  • Auto-exit is suppressed while work is in flight: the session parks as waiting instead of exiting when an ask_question is still unanswered, or when the agent's own child sub-agents are still running (a worker can stop after dispatching children and stays open until the last result returns).

interactive

Controls whether stalled/recovered status transitions send a steer message to the parent session. Defaults to the inverse of auto-exit: autonomous agents get stall pings; user-driven agents stay quiet (the user is already working in that pane — the widget still updates). Set explicitly to override.

Tool access control

Access is whitelist-only. Every sub-agent process is launched with --no-extensions (extension discovery disabled) and --tools <allowlist>; only the extensions backing the listed tools are loaded back in explicitly. There is no default toolset and no deny-list — an agent gets exactly what its frontmatter lists. The restriction survives resume via the loadout snapshot.

Spawns must name a known agent at every depth. A top-level session may spawn anything discoverable; a sub-agent may only spawn the agents in its subagent_agents list (enforced via PI_SUBAGENT_ALLOWED). There is no agentless spawn route, so a child can never escalate to a full-toolset profile by omitting its agent.

Extensions can register additional tools for sub-agents at runtime via registerToolExtension(name, path) on the __pi_interactive_subagents process global.

Role folders

cwd starts a sub-agent in a directory with its own config, so role-specific setups (CLAUDE.md, skills, extensions) apply:

project/
└── agents/
    ├── game-designer/   ← CLAUDE.md, .pi/…
    └── sre/             ← CLAUDE.md, .pi/…
subagent({ agent: "worker", cwd: "agents/sre", task: "Review the deployment pipeline" });

Set a per-agent default with cwd: in frontmatter.

Status widget & configuration

The widget tracks each sub-agent from a runtime activity snapshot written by the child: starting, active (turn/provider/tool work), waiting (open for input or another stage), stalled (no valid snapshot for too long), or running (fallback). Sub-agent sessions also show their own tools widget — toggle it with Ctrl+Alt+O. Completion messages expand with Ctrl+O.

Status display is configured via config.json in the extension directory (copy config.json.example; it's gitignored):

{
  "status": { "enabled": true }
}

Requirements

  • pi
  • Herdr 0.9 or later. The extension drives the herdr CLI and needs HERDR_ENV=1, which Herdr sets in every pane it manages.
  • Or the Herdr desktop app, with hc on your PATH. The extension needs HC_TERMINAL_ID, which herdr-core sets in every terminal.
herdr        # open or attach the Herdr session
pi           # start pi inside a Herdr pane

In the desktop app, start pi in any of its terminals. Running pi outside Herdr disables the subagent tools with a setup hint.

Acknowledgements

Forked from HazAT/pi-interactive-subagents, which originated the subagent architecture, the multi-multiplexer surface layer, and the status widget; its supervision features were inspired by RepoPrompt. The direct ancestor of this Herdr port is the tmux-only fork amosblomqvist/pi-interactive-subagents.

License

MIT