@adix7/pi-agent-teams
v0.4.4
Published
Claude Code agent teams style workflow for Pi.
Readme
pi-agent-teams
An experimental Pi extension that brings Claude Code agent teams to Pi. Spawn teammates, share a task list, and coordinate work across multiple Pi sessions.
Status: MVP (command-driven + status widget). See
docs/claude-parity.mdfor the full roadmap.
Features
Core agent-teams primitives, matching Claude's design:
- Shared task list — file-per-task on disk with three states (pending / in-progress / completed) and dependency tracking so blocked tasks stay blocked until their prerequisites finish.
- Auto-claim — idle teammates automatically pick up the next unassigned, unblocked task. No manual dispatching required (disable with
PI_TEAMS_DEFAULT_AUTO_CLAIM=0). - Direct messages and broadcast — send a message to one teammate or all of them at once, via file-based mailboxes.
- Graceful lifecycle — spawn, stop, shutdown (with handshake), or kill teammates. The leader tracks who's online, idle, or streaming.
- LLM-callable teams tool — the model can spawn teammates, delegate tasks, mutate task assignment/status/dependencies, message teammates, and run lifecycle actions in tool calls (no slash commands needed).
- Team cleanup — tear down all team artifacts (tasks, mailboxes, sessions, worktrees) when you're done.
Additional Pi-specific capabilities:
- Git worktrees — optionally give each teammate its own worktree so they work on isolated branches without conflicting edits.
- Session branching — clone the leader's conversation context into a teammate so it starts with full awareness of the work so far, instead of from scratch.
- Hooks / quality gates — optional leader-side hooks on idle / task completion to run scripts (opt-in).
UI style (terminology + naming)
Built-in styles:
- normal (default): "Team leader" + "Teammate " (spawn requires explicit name)
- soviet: "Chairman" + "Comrade " (spawn can auto-pick names)
- pirate: "Captain" + "Matey " (spawn can auto-pick names)
Configure via:
- env:
PI_TEAMS_STYLE=<name> - command:
/team style <name>(see:/team style list)
Custom styles
You can add your own styles by creating JSON files under:
~/.pi/agent/teams/_styles/<style>.json
The file can override strings and naming rules.
Strings include both terminology and lifecycle copy, e.g. killedVerb, shutdownRequestedVerb, shutdownCompletedVerb, shutdownRefusedVerb, abortRequestedVerb, plus templates like teamEndedAllStopped.
Example:
{
"extends": "pirate",
"strings": {
"memberTitle": "Deckhand",
"memberPrefix": "Deckhand "
},
"naming": {
"requireExplicitSpawnName": false,
"autoNameStrategy": { "kind": "pool", "pool": ["pegleg", "parrot"], "fallbackBase": "deckhand" }
}
}Install
Option A — install from npm:
pi install npm:@tmustier/pi-agent-teamsOption B — load directly (dev):
pi -e ~/projects/pi-agent-teams/extensions/teams/index.tsOption C — install from a local folder:
pi install ~/projects/pi-agent-teamsThen run pi normally; the extension auto-discovers.
Verify with /team id — it should print the current team info.
Quick start
The fastest way to get going is /swarm:
/swarm build the auth module # agent spawns a team and coordinates the work
/swarm # agent asks you what to do, then swarms on itOr drive it manually:
/team spawn alice # spawn a teammate (fresh session, shared workspace)
/team spawn bob branch worktree # spawn with leader context + isolated worktree
/team attach list # discover existing teams under ~/.pi/agent/teams
/team attach <teamId> [--claim] # attach this session to an existing team workspace (force takeover with --claim)
/team detach # return to this session's own team
/team task add alice: Fix failing tests # create a task and assign it to alice
/team task add Refactor auth module # unassigned — auto-claimed by next idle teammate
/team dm alice Check the edge cases too # direct message
/team broadcast Wrapping up soon # message everyone
/tw # open the interactive widget panel
/team shutdown alice # graceful shutdown (handshake)
/team shutdown # stop all teammates (leader session remains active)
/team cleanup # remove team artifacts when doneOr let the model drive it with the delegate tool:
{
"action": "delegate",
"contextMode": "branch",
"workspaceMode": "worktree",
"model": "openai-codex/gpt-5.1-codex-mini",
"thinking": "high",
"teammates": ["alice", "bob"],
"tasks": [
{ "text": "Fix failing unit tests" },
{ "text": "Refactor auth module" }
]
}Teams tool action reference (agent-run)
| Action | Required fields | Purpose |
| --- | --- | --- |
| delegate | tasks | Spawn teammates as needed and create/assign tasks. |
| task_assign | taskId, assignee | Assign/reassign a task owner. |
| task_unassign | taskId | Clear owner (resets to pending for non-completed tasks). |
| task_set_status | taskId, status | Set status to pending, in_progress, or completed. |
| task_dep_add | taskId, depId | Add dependency edge (taskId depends on depId). |
| task_dep_rm | taskId, depId | Remove dependency edge. |
| task_dep_ls | taskId | Inspect dependency/block graph for one task. |
| message_dm | name, message | Send mailbox DM to one teammate. |
| message_broadcast | message | Send mailbox message to all discovered workers. |
| message_steer | name, message | Send steer instruction to a running RPC teammate. |
| member_spawn | name | Spawn one teammate (supports context/workspace/model/thinking/plan options). |
| member_shutdown | name or all=true | Request graceful shutdown via mailbox handshake. |
| member_kill | name | Force-stop one RPC teammate and unassign active tasks. |
| member_prune | (none) | Mark stale non-RPC workers offline (all=true to force). |
| plan_approve | name | Approve pending plan for a plan-required teammate. |
| plan_reject | name | Reject pending plan (feedback optional). |
| hooks_policy_get | (none) | Read team hooks policy (configured + effective with env fallback). |
| hooks_policy_set | one or more of: hookFailureAction, hookMaxReopensPerTask, hookFollowupOwner | Update team hooks policy at runtime (hooksPolicyReset=true clears team overrides first). |
| model_policy_get | (none) | Inspect teammate model policy and default inheritance behavior for the current leader model. |
| model_policy_check | optional model | Validate a model override before spawning (<provider>/<modelId> or <modelId>). |
Example calls:
{ "action": "task_assign", "taskId": "12", "assignee": "alice" }
{ "action": "task_dep_add", "taskId": "12", "depId": "7" }
{ "action": "message_broadcast", "message": "Sync: finishing this milestone" }
{ "action": "member_kill", "name": "alice" }
{ "action": "plan_approve", "name": "alice" }
{ "action": "hooks_policy_get" }
{ "action": "hooks_policy_set", "hookFailureAction": "reopen_followup", "hookMaxReopensPerTask": 2, "hookFollowupOwner": "member" }
{ "action": "model_policy_get" }
{ "action": "model_policy_check", "model": "openai-codex/gpt-5.1-codex-mini" }Commands
Shortcuts
| Command | Description |
| --- | --- |
| /swarm [task] | Tell the agent to spawn a team and work on a task |
| /tw | Open the interactive widget panel |
| /team-widget | Open the interactive widget panel (alias for /tw) |
Team management
All management commands live under /team.
| Command | Description |
| --- | --- |
| /team spawn <name> [fresh\|branch] [shared\|worktree] [plan] [--agent <agent>] [--model <provider>/<modelId>] [--thinking <level>] | Start a teammate |
| /team list | List teammates and their status |
| /team panel | Interactive widget panel (same as /tw) |
| /team attach list | Discover existing team workspaces under <teamsRoot> |
| /team attach <teamId> [--claim] | Attach this session to an existing team workspace (--claim force-takes over an active claim) |
| /team detach | Return to this session's own team workspace |
| /team style | Show current style + usage |
| /team style list | List available styles (built-in + custom) |
| /team style init <name> [extends <base>] | Create a custom style template under ~/.pi/agent/teams/_styles/ |
| /team style <name> | Set style (built-in or custom) |
| /team send <name> <msg> | Send a prompt over RPC |
| /team steer <name> <msg> | Redirect an in-flight run |
| /team dm <name> <msg> | Send a mailbox message |
| /team broadcast <msg> | Message all teammates |
| /team stop <name> [reason] | Abort current work (resets task to pending) |
| /team shutdown <name> [reason] | Graceful shutdown (handshake) |
| /team shutdown | Stop all teammates (RPC + best-effort manual) (leader session remains active) |
| /team prune [--all] | Mark stale manual teammates offline (hides them in widget) |
| /team kill <name> | Force-terminate |
| /team cleanup [--force] | Delete team artifacts |
| /team id | Print team/task-list IDs and paths |
| /team env <name> | Print env vars to start a manual teammate |
Model inheritance note:
- If the leader is running a deprecated model id (e.g. Sonnet 4 non-4.5 variants), teammates will not inherit that id by default.
- Explicit deprecated
--modeloverrides are rejected. - Agents can introspect/check this at runtime via
teamsactions:{ "action": "model_policy_get" }and{ "action": "model_policy_check", "model": "..." }.
Panel shortcuts (/tw / /team panel)
↑/↓orw/s: select teammate / scroll transcript1..9: jump directly to teammate in overviewenter: open selected teammate transcripttorshift+t: open selected teammate task list (task-centric view with deps/blocks); in task view, toggle back (esc/t/shift+t)- task view:
ccomplete,ppending,iin-progress,uunassign,rreassign selected task mord: compose message to selected teammatea: request abortk: kill (SIGTERM)esc: back/close panel- attached mode shows a banner (
attached: ...) with/team detachhint
Tasks
| Command | Description |
| --- | --- |
| /team task add <text> | Create a task (prefix with name: to assign) |
| /team task assign <id> <agent> | Assign a task |
| /team task unassign <id> | Remove assignment |
| /team task list | Show tasks with status, deps, blocks |
| /team task show <id> | Full description + result |
| /team results [completed\|all] [--limit N] [--ids 1,2,3] [--summary] [--model <provider>/<modelId>] | Show task results; --summary uses LLM to show 1–3 line summaries |
| /team task dep add <id> <depId> | Add a dependency |
| /team task dep rm <id> <depId> | Remove a dependency |
| /team task dep ls <id> | Show deps and blocks |
| /team task clear [completed\|all] | Delete task files |
Configuration
| Environment variable | Purpose | Default |
| --- | --- | --- |
| PI_TEAMS_ROOT_DIR | Storage root (absolute or relative to ~/.pi/agent) | ~/.pi/agent/teams |
| PI_TEAMS_DEFAULT_AUTO_CLAIM | Whether spawned teammates auto-claim tasks | 1 (on) |
| PI_TEAMS_STYLE | UI style id (built-in: normal, soviet, pirate, or custom) | normal |
| PI_TEAMS_HOOKS_ENABLED | Enable leader-side hooks/quality gates | 0 (off) |
| PI_TEAMS_HOOKS_DIR | Hooks directory (absolute or relative to PI_TEAMS_ROOT_DIR) | <teamsRoot>/_hooks |
| PI_TEAMS_HOOK_TIMEOUT_MS | Hook execution timeout (ms) | 60000 |
| PI_TEAMS_HOOKS_FAILURE_ACTION | Hook-failure policy: warn, followup, reopen, reopen_followup | warn |
| PI_TEAMS_HOOKS_MAX_REOPENS_PER_TASK | Reopen cap per task when failure action includes reopen (0 disables auto-reopen) | 3 |
| PI_TEAMS_HOOKS_FOLLOWUP_OWNER | Follow-up owner policy: member, lead, none | member |
| PI_TEAMS_HOOKS_CREATE_TASK_ON_FAILURE | Legacy shortcut for PI_TEAMS_HOOKS_FAILURE_ACTION=followup | 0 (off) |
Storage layout
<teamsRoot>/<teamId>/
config.json # team metadata + members
tasks/<taskListId>/
1.json, 2.json, ... # one file per task
.highwatermark # next task ID
mailboxes/<namespace>/inboxes/
<agent>.json # per-agent inbox
sessions/ # teammate session files
worktrees/<agent>/ # git worktrees (when enabled)
<teamsRoot>/_hooks/
on_idle.{js,sh} # optional hook (see below)
on_task_completed.{js,sh} # optional quality gate
on_task_failed.{js,sh} # optional hookHooks / quality gates (optional)
Enable hooks:
export PI_TEAMS_HOOKS_ENABLED=1Then create hook scripts under:
<teamsRoot>/_hooks/(default:~/.pi/agent/teams/_hooks/)
Recognized hook names:
on_idle.(js|mjs|sh)on_task_completed.(js|mjs|sh)on_task_failed.(js|mjs|sh)
Hooks run with working directory = the leader session cwd and receive context via env vars:
PI_TEAMS_HOOK_EVENTPI_TEAMS_HOOK_CONTEXT_VERSION(currently1)PI_TEAMS_HOOK_CONTEXT_JSON(stable JSON payload for agent scripts)PI_TEAMS_TEAM_ID,PI_TEAMS_TEAM_DIR,PI_TEAMS_TASK_LIST_IDPI_TEAMS_STYLEPI_TEAMS_MEMBERPI_TEAMS_TASK_ID,PI_TEAMS_TASK_SUBJECT,PI_TEAMS_TASK_OWNER,PI_TEAMS_TASK_STATUS
Hook policy can be controlled by agents at runtime via teams tool actions:
{ "action": "hooks_policy_get" }{ "action": "hooks_policy_set", ... }
Team-level policy in config.json overrides env defaults for that team.
Hook failure policy (for task_completed / task_failed hooks):
# default behavior: notify + annotate task metadata
export PI_TEAMS_HOOKS_FAILURE_ACTION=warn
# create follow-up remediation task
export PI_TEAMS_HOOKS_FAILURE_ACTION=followup
# reopen completed task to pending (re-blocks downstream dependencies)
export PI_TEAMS_HOOKS_FAILURE_ACTION=reopen
# both reopen and create a follow-up task
export PI_TEAMS_HOOKS_FAILURE_ACTION=reopen_followup
# safety cap for auto-reopen loops (0 = disable auto-reopen)
export PI_TEAMS_HOOKS_MAX_REOPENS_PER_TASK=3
# owner for auto-created follow-up tasks
# member (default), lead, or none
export PI_TEAMS_HOOKS_FOLLOWUP_OWNER=memberAgent-first intent:
- Hook failures are remediated by agents (reopen/follow-up/assignment + teammate notification).
- The user should not need to manually clear task gate state.
Legacy shortcut still supported:
export PI_TEAMS_HOOKS_CREATE_TASK_ON_FAILURE=1Development
Quality gate
npm run checkRuns strict TypeScript typechecking (npm run typecheck) and ESLint (npm run lint).
Smoke test (no API keys)
npm run smoke-test
# or: npx tsx scripts/smoke-test.mtsFilesystem-level smoke test of the task store, mailbox, team config, and protocol parsers.
E2E RPC test (spawns pi + one teammate)
node scripts/e2e-rpc-test.mjsStarts a leader in RPC mode, spawns a teammate, runs a shutdown handshake, verifies cleanup. Sets PI_TEAMS_ROOT_DIR to a temp directory so nothing touches ~/.pi/agent/teams.
Integration: hooks remediation loop
npm run integration-hooks-remediation-testDeterministic leader-side integration flow that verifies failed on_task_completed hook handling end-to-end:
- task is reopened when policy includes
reopen - follow-up task is created/assigned when policy includes
followup - remediation mailbox nudge is emitted for the responsible teammate
tmux dogfooding
./scripts/start-tmux-team.sh pi-teams alice bob
tmux attach -t pi-teamsStarts a leader + one tmux window per teammate for interactive testing.
License
MIT (see LICENSE).
