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.
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).
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)
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" }
}
}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.
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 it
Or 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 done
Or 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" }
]
}| 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" }| 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) |
All management commands live under /team.
| Command | Description |
|---|---|
/team spawn <name> [fresh|branch] [shared|worktree] [plan] [--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": "..." }.
↑/↓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
| 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 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 |
| 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) |
<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 hook
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=1npm run checkRuns strict TypeScript typechecking (npm run typecheck) and ESLint (npm run lint).
npm run smoke-test
# or: npx tsx scripts/smoke-test.mtsFilesystem-level smoke test of the task store, mailbox, team config, and protocol parsers.
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.
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
./scripts/start-tmux-team.sh pi-teams alice bob
tmux attach -t pi-teamsStarts a leader + one tmux window per teammate for interactive testing.
MIT (see LICENSE).