@tmustier/pi-agent-teams 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +50 -9
  2. package/docs/claude-parity.md +22 -18
  3. package/docs/field-notes-teams-setup.md +6 -4
  4. package/docs/smoke-test-plan.md +139 -0
  5. package/eslint.config.js +74 -0
  6. package/extensions/teams/activity-tracker.ts +234 -0
  7. package/extensions/teams/fs-lock.ts +21 -5
  8. package/extensions/teams/leader-inbox.ts +175 -0
  9. package/extensions/teams/leader-info-commands.ts +139 -0
  10. package/extensions/teams/leader-lifecycle-commands.ts +343 -0
  11. package/extensions/teams/leader-messaging-commands.ts +148 -0
  12. package/extensions/teams/leader-plan-commands.ts +96 -0
  13. package/extensions/teams/leader-spawn-command.ts +57 -0
  14. package/extensions/teams/leader-task-commands.ts +421 -0
  15. package/extensions/teams/leader-team-command.ts +312 -0
  16. package/extensions/teams/leader-teams-tool.ts +227 -0
  17. package/extensions/teams/leader.ts +260 -1562
  18. package/extensions/teams/mailbox.ts +54 -29
  19. package/extensions/teams/names.ts +87 -0
  20. package/extensions/teams/protocol.ts +241 -0
  21. package/extensions/teams/spawn-types.ts +21 -0
  22. package/extensions/teams/task-store.ts +36 -21
  23. package/extensions/teams/team-config.ts +71 -25
  24. package/extensions/teams/teammate-rpc.ts +81 -23
  25. package/extensions/teams/teams-panel.ts +644 -0
  26. package/extensions/teams/teams-style.ts +62 -0
  27. package/extensions/teams/teams-ui-shared.ts +89 -0
  28. package/extensions/teams/teams-widget.ts +182 -0
  29. package/extensions/teams/worker.ts +100 -138
  30. package/extensions/teams/worktree.ts +4 -7
  31. package/package.json +32 -5
  32. package/scripts/integration-claim-test.mts +157 -0
  33. package/scripts/integration-todo-test.mts +532 -0
  34. package/scripts/lib/pi-workers.ts +105 -0
  35. package/scripts/smoke-test.mts +424 -0
  36. package/skills/agent-teams/SKILL.md +139 -0
  37. package/tsconfig.strict.json +22 -0
  38. package/extensions/teams/tasks.ts +0 -95
  39. package/scripts/smoke-test.mjs +0 -199
package/README.md CHANGED
@@ -20,6 +20,17 @@ Additional Pi-specific capabilities:
20
20
  - **Git worktrees** — optionally give each teammate its own worktree so they work on isolated branches without conflicting edits.
21
21
  - **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.
22
22
 
23
+ ## UI style
24
+
25
+ The extension supports two UI styles:
26
+
27
+ - **normal** (default): "Team leader" + "Teammate <name>"
28
+ - **soviet**: "Chairman" + "Comrade <name>" (in soviet mode, the system decides names for you)
29
+
30
+ Configure via:
31
+ - env: `PI_TEAMS_STYLE=normal|soviet`
32
+ - command: `/team style normal` or `/team style soviet`
33
+
23
34
  ## Install
24
35
 
25
36
  **Option A — install from npm:**
@@ -46,9 +57,16 @@ Verify with `/team id` — it should print the current team info.
46
57
 
47
58
  ## Quick start
48
59
 
60
+ The fastest way to get going is `/swarm`:
61
+
49
62
  ```
50
- # In a Pi session with the extension loaded:
63
+ /swarm build the auth module # agent spawns a team and coordinates the work
64
+ /swarm # agent asks you what to do, then swarms on it
65
+ ```
66
+
67
+ Or drive it manually:
51
68
 
69
+ ```
52
70
  /team spawn alice # spawn a teammate (fresh session, shared workspace)
53
71
  /team spawn bob branch worktree # spawn with leader context + isolated worktree
54
72
 
@@ -58,7 +76,10 @@ Verify with `/team id` — it should print the current team info.
58
76
  /team dm alice Check the edge cases too # direct message
59
77
  /team broadcast Wrapping up soon # message everyone
60
78
 
79
+ /tw # open the interactive widget panel
80
+
61
81
  /team shutdown alice # graceful shutdown (handshake)
82
+ /team shutdown # stop all teammates (leader session remains active)
62
83
  /team cleanup # remove team artifacts when done
63
84
  ```
64
85
 
@@ -79,25 +100,35 @@ Or let the model drive it with the delegate tool:
79
100
 
80
101
  ## Commands
81
102
 
82
- All commands live under `/team`.
103
+ ### Shortcuts
83
104
 
84
- ### Teammates
105
+ | Command | Description |
106
+ | --- | --- |
107
+ | `/swarm [task]` | Tell the agent to spawn a team and work on a task |
108
+ | `/tw` | Open the interactive widget panel |
109
+ | `/team-widget` | Open the interactive widget panel (alias for `/tw`) |
110
+
111
+ ### Team management
112
+
113
+ All management commands live under `/team`.
85
114
 
86
115
  | Command | Description |
87
116
  | --- | --- |
88
117
  | `/team spawn <name> [fresh\|branch] [shared\|worktree]` | Start a teammate |
89
118
  | `/team list` | List teammates and their status |
119
+ | `/team panel` | Interactive widget panel (same as `/tw`) |
120
+ | `/team style <normal\|soviet>` | Set UI style (normal/soviet) |
90
121
  | `/team send <name> <msg>` | Send a prompt over RPC |
91
122
  | `/team steer <name> <msg>` | Redirect an in-flight run |
92
123
  | `/team dm <name> <msg>` | Send a mailbox message |
93
124
  | `/team broadcast <msg>` | Message all teammates |
94
125
  | `/team stop <name> [reason]` | Abort current work (resets task to pending) |
95
126
  | `/team shutdown <name> [reason]` | Graceful shutdown (handshake) |
96
- | `/team shutdown` | Shutdown leader + all teammates |
127
+ | `/team shutdown` | Stop all teammates (leader session remains active) |
97
128
  | `/team kill <name>` | Force-terminate |
98
129
  | `/team cleanup [--force]` | Delete team artifacts |
99
130
  | `/team id` | Print team/task-list IDs and paths |
100
- | `/team env <name>` | Print env vars to start a manual worker |
131
+ | `/team env <name>` | Print env vars to start a manual teammate |
101
132
 
102
133
  ### Tasks
103
134
 
@@ -119,6 +150,7 @@ All commands live under `/team`.
119
150
  | --- | --- | --- |
120
151
  | `PI_TEAMS_ROOT_DIR` | Storage root (absolute or relative to `~/.pi/agent`) | `~/.pi/agent/teams` |
121
152
  | `PI_TEAMS_DEFAULT_AUTO_CLAIM` | Whether spawned teammates auto-claim tasks | `1` (on) |
153
+ | `PI_TEAMS_STYLE` | UI style (`normal` or `soviet`) | `normal` |
122
154
 
123
155
  ## Storage layout
124
156
 
@@ -136,13 +168,22 @@ All commands live under `/team`.
136
168
 
137
169
  ## Development
138
170
 
171
+ ### Quality gate
172
+
173
+ ```bash
174
+ npm run check
175
+ ```
176
+
177
+ Runs strict TypeScript typechecking (`npm run typecheck`) and ESLint (`npm run lint`).
178
+
139
179
  ### Smoke test (no API keys)
140
180
 
141
181
  ```bash
142
- node scripts/smoke-test.mjs
182
+ npm run smoke-test
183
+ # or: npx tsx scripts/smoke-test.mts
143
184
  ```
144
185
 
145
- Filesystem-level test of the task store, mailbox, and team config.
186
+ Filesystem-level smoke test of the task store, mailbox, team config, and protocol parsers.
146
187
 
147
188
  ### E2E RPC test (spawns pi + one teammate)
148
189
 
@@ -150,7 +191,7 @@ Filesystem-level test of the task store, mailbox, and team config.
150
191
  node scripts/e2e-rpc-test.mjs
151
192
  ```
152
193
 
153
- Starts a leader in RPC mode, spawns a worker, runs a shutdown handshake, verifies cleanup. Sets `PI_TEAMS_ROOT_DIR` to a temp directory so nothing touches `~/.pi/agent/teams`.
194
+ Starts 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`.
154
195
 
155
196
  ### tmux dogfooding
156
197
 
@@ -159,7 +200,7 @@ Starts a leader in RPC mode, spawns a worker, runs a shutdown handshake, verifie
159
200
  tmux attach -t pi-teams
160
201
  ```
161
202
 
162
- Starts a leader + one tmux window per worker for interactive testing.
203
+ Starts a leader + one tmux window per teammate for interactive testing.
163
204
 
164
205
  ## License
165
206
 
@@ -12,12 +12,14 @@ This document tracks **feature parity gaps** between:
12
12
 
13
13
  - `pi-agent-teams` (Pi extension)
14
14
 
15
+ > Terminology note: the extension supports `PI_TEAMS_STYLE=normal|soviet`. These docs often say "comrade" for parity with Claude Teams, but in **normal** style you’ll see "teammate" and "team leader" in the UI.
16
+
15
17
  ## Scope / philosophy
16
18
 
17
19
  - Target the **same coordination primitives** as Claude Teams:
18
20
  - shared task list
19
21
  - mailbox messaging
20
- - long-lived teammates
22
+ - long-lived comrades
21
23
  - Prefer **inspectable, local-first artifacts** (files + lock files).
22
24
  - Avoid guidance that bypasses Claude feature gating; we only document behavior.
23
25
  - Accept that some Claude UX (terminal keybindings + split-pane integration) may not be achievable in Pi without deeper TUI/terminal integration.
@@ -31,18 +33,18 @@ Legend: ✅ implemented • 🟡 partial • ❌ missing
31
33
  | Enablement | `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` + settings | N/A | Pi extension is always available when installed/loaded. | — |
32
34
  | Team config | `~/.claude/teams/<team>/config.json` w/ members | ✅ | Implemented via `extensions/teams/team-config.ts` (stored under `~/.pi/agent/teams/...` or `PI_TEAMS_ROOT_DIR`). | P0 |
33
35
  | Task list (shared) | `~/.claude/tasks/<taskListId>/` + states + deps | ✅ | File-per-task + deps (`blockedBy`/`blocks`); `/team task dep add|rm|ls`; self-claim skips blocked tasks. | P0 |
34
- | Self-claim | Teammates can self-claim next unassigned, unblocked task; file locking | ✅ | Implemented: `claimNextAvailableTask()` + locks; enabled by default (`PI_TEAMS_DEFAULT_AUTO_CLAIM=1`). | P0 |
35
- | Explicit assign | Lead assigns task to teammate | ✅ | `/team task assign` sets owner + pings via mailbox. | P0 |
36
- | “Message” vs “broadcast” | Send to one teammate or all teammates | ✅ | `/team dm` + `/team broadcast` use mailbox; `/team send` uses RPC. Broadcast recipients = team config workers + RPC-spawned map + active task owners; manual tmux workers self-register into `config.json` on startup. | P0 |
37
- | Teammateteammate messaging | Teammates can message each other directly | ✅ | Workers register `team_message` LLM-callable tool; sends via mailbox + CC's leader with `peer_dm_sent` notification. | P1 |
38
- | Display modes | In-process selection (Shift+Up/Down); split panes (tmux/iTerm) | ❌ | Pi has a widget + commands, but no terminal-level teammate navigation/panes. | P2 |
36
+ | Self-claim | Comrades can self-claim next unassigned, unblocked task; file locking | ✅ | Implemented: `claimNextAvailableTask()` + locks; enabled by default (`PI_TEAMS_DEFAULT_AUTO_CLAIM=1`). | P0 |
37
+ | Explicit assign | Lead assigns task to comrade | ✅ | `/team task assign` sets owner + pings via mailbox. | P0 |
38
+ | “Message” vs “broadcast” | Send to one comrade or all comrades | ✅ | `/team dm` + `/team broadcast` use mailbox; `/team send` uses RPC. Broadcast recipients = team config workers + RPC-spawned map + active task owners; manual tmux workers self-register into `config.json` on startup. | P0 |
39
+ | Comradecomrade messaging | Comrades can message each other directly | ✅ | Workers register `team_message` LLM-callable tool; sends via mailbox + CC's leader with `peer_dm_sent` notification. | P1 |
40
+ | Display modes | In-process selection (Shift+Up/Down); split panes (tmux/iTerm) | ❌ | Pi has a widget + commands, but no terminal-level comrade navigation/panes. | P2 |
39
41
  | Delegate mode | Lead restricted to coordination-only tools | ✅ | `/team delegate [on|off]` toggles; `pi.on("tool_call")` blocks `bash/edit/write`; `PI_TEAMS_DELEGATE_MODE=1` env. Widget shows `[delegate]`. | P1 |
40
- | Plan approval | Teammate can be "plan required" and needs lead approval to implement | ✅ | `/team spawn <name> plan` sets `PI_TEAMS_PLAN_REQUIRED=1`; worker restricted to read-only tools; submits plan via `plan_approval_request`; `/team plan approve|reject <name>`. | P1 |
41
- | Shutdown handshake | Lead requests shutdown; teammate can approve/reject | ✅ | Full protocol: `shutdown_request` → `shutdown_approved` or `shutdown_rejected` (when worker is busy). `/team shutdown <name>` (graceful) + `/team kill` (force). | P1 |
42
- | Cleanup team | “Clean up the team” removes shared resources after teammates stopped | ✅ | `/team cleanup [--force]` deletes only `<teamsRoot>/<teamId>` after safety checks. | P1 |
43
- | Hooks / quality gates | `TeammateIdle`, `TaskCompleted` hooks | ❌ | Add optional hook runner in leader on idle/task-complete events (script execution + exit-code gating). | P2 |
42
+ | Plan approval | Comrade can be "plan required" and needs lead approval to implement | ✅ | `/team spawn <name> plan` sets `PI_TEAMS_PLAN_REQUIRED=1`; worker restricted to read-only tools; submits plan via `plan_approval_request`; `/team plan approve|reject <name>`. | P1 |
43
+ | Shutdown handshake | Lead requests shutdown; comrade can approve/reject | ✅ | Full protocol: `shutdown_request` → `shutdown_approved` or `shutdown_rejected` (when worker is busy). `/team shutdown <name>` (graceful) + `/team kill <name>` (force). | P1 |
44
+ | Cleanup team | “Clean up the team” removes shared resources after comrades stopped | ✅ | `/team cleanup [--force]` deletes only `<teamsRoot>/<teamId>` after safety checks. | P1 |
45
+ | Hooks / quality gates | `ComradeIdle`, `TaskCompleted` hooks | ❌ | Add optional hook runner in leader on idle/task-complete events (script execution + exit-code gating). | P2 |
44
46
  | Task list UX | Ctrl+T toggle; show all/clear tasks by asking | 🟡 | Widget + `/team task list` show blocked/deps; `/team task show <id>`; `/team task clear [completed|all]`. No Ctrl+T toggle yet. | P0 |
45
- | Shared task list across sessions | `CLAUDE_CODE_TASK_LIST_ID=...` | ✅ | `PI_TEAMS_TASK_LIST_ID` env on leader + worker; `/team task use <taskListId>` switches the leader (and newly spawned workers). Existing workers need a restart to pick up changes. Persisted in config.json. | P1 |
47
+ | Shared task list across sessions | `CLAUDE_CODE_TASK_LIST_ID=...` | ✅ | `PI_TEAMS_TASK_LIST_ID` env is **worker-side** (for manually started workers). The leader switches task lists via `/team task use <taskListId>` (persisted in `config.json`). Newly spawned workers inherit the new task list ID; existing workers need a restart to pick up changes. | P1 |
46
48
 
47
49
  ## Prioritized roadmap
48
50
 
@@ -64,7 +66,7 @@ Legend: ✅ implemented • 🟡 partial • ❌ missing
64
66
  4) **Shutdown handshake** ✅
65
67
  - Full protocol: `shutdown_request` → `shutdown_approved` / `shutdown_rejected`
66
68
  - Worker rejects when busy (streaming + active task), auto-approves when idle
67
- - Leader command: `/team shutdown <name> [reason...]` (graceful), `/team kill` as force
69
+ - Leader command: `/team shutdown <name> [reason...]` (graceful), `/team kill <name>` as force
68
70
 
69
71
  5) **Plan approval** ✅
70
72
  - `/team spawn <name> [fresh|branch] [shared|worktree] plan` sets `PI_TEAMS_PLAN_REQUIRED=1`
@@ -80,7 +82,7 @@ Legend: ✅ implemented • 🟡 partial • ❌ missing
80
82
 
81
83
  7) **Cleanup** ✅
82
84
  - `/team cleanup [--force]` deletes only `<teamsRoot>/<teamId>` after safety checks.
83
- - Refuses if RPC teammates are running or there are `in_progress` tasks unless `--force`.
85
+ - Refuses if RPC comrades are running or there are `in_progress` tasks unless `--force`.
84
86
 
85
87
  8) **Peer-to-peer messaging** ✅
86
88
  - Workers register `team_message` LLM-callable tool (recipient + message params)
@@ -93,10 +95,10 @@ Legend: ✅ implemented • 🟡 partial • ❌ missing
93
95
 
94
96
  ### P2: UX + “product-level” parity
95
97
 
96
- 10) **Better teammate interaction UX**
98
+ 10) **Better comrade interaction UX**
97
99
  - Explore whether Pi’s TUI API can support:
98
- - selecting a teammate from the widget
99
- - “entering” a teammate transcript view
100
+ - selecting a comrade from the widget
101
+ - “entering” a comrade transcript view
100
102
  - (Optional) tmux integration for split panes.
101
103
 
102
104
  11) **Hooks / quality gates**
@@ -107,7 +109,9 @@ Legend: ✅ implemented • 🟡 partial • ❌ missing
107
109
 
108
110
  ## Where changes would land (code map)
109
111
 
110
- - Leader orchestration + commands + tool: `extensions/teams/leader.ts`
112
+ - Leader orchestration: `extensions/teams/leader.ts`
113
+ - Leader `/team` command dispatch: `extensions/teams/leader-team-command.ts`
114
+ - Leader LLM tool (`teams`): `extensions/teams/leader-teams-tool.ts`
111
115
  - Worker mailbox polling + self-claim + protocols: `extensions/teams/worker.ts`
112
116
  - Task store + locking: `extensions/teams/task-store.ts`, `extensions/teams/fs-lock.ts`
113
117
  - Mailbox store + locking: `extensions/teams/mailbox.ts`
@@ -118,5 +122,5 @@ Legend: ✅ implemented • 🟡 partial • ❌ missing
118
122
 
119
123
  - Keep tests hermetic by setting `PI_TEAMS_ROOT_DIR` to a temp directory.
120
124
  - Extend:
121
- - `scripts/smoke-test.mjs` for filesystem-only behaviors (deps, claiming, locking)
125
+ - `scripts/smoke-test.mts` (run via `npm run smoke-test`) for filesystem-only behaviors (deps, claiming, locking)
122
126
  - `scripts/e2e-rpc-test.mjs` for protocol flows (shutdown handshake, plan approval)
@@ -4,12 +4,14 @@ Date: 2026-02-07
4
4
 
5
5
  Goal: dogfood the Teams extension to implement its own roadmap (Claude parity), and capture anything that surprised/confused us while setting it up.
6
6
 
7
+ > Terminology note: the extension supports `PI_TEAMS_STYLE=normal|soviet`. In normal style the UI says "teammate" and "team leader"; in soviet style it says "comrade" and "chairman".
8
+
7
9
  ## Test run: test1
8
10
 
9
11
  Decisions: tmux session `pi-teams-test1`; `PI_TEAMS_ROOT_DIR=~/projects/pi-agent-teams/test1`; `teamId=0baaa0e6-8020-4d9a-bf33-c1a65f99a2f7`; workers started manually in tmux (not `/team spawn`).
10
12
 
11
13
  First impressions:
12
- - Manual tmux workers are usable. Initially the leader showed “(no teammates)” because it only tracked RPC-spawned workers; now manual workers **upsert themselves into `config.json` on startup**, and the leader widget renders online workers from team config.
14
+ - Manual tmux workers are usable. Initially the leader showed “(no comrades)” because it only tracked RPC-spawned workers; now manual workers **upsert themselves into `config.json` on startup**, and the leader widget renders online workers from team config.
13
15
  - Pinning `PI_TEAMS_ROOT_DIR` made reruns/id discovery predictable (no “find the new folder” step).
14
16
  - tmux workflow feels close to Claude-style split panes; bootstrap ergonomics still need smoothing.
15
17
  - Surprise: `/team spawn <name> branch` failed once with `Entry <id> not found` (branch-from leaf missing on disk); `/team spawn <name> fresh` worked.
@@ -73,9 +75,9 @@ First impressions:
73
75
 
74
76
  - **tmux vs `/team spawn`**: `/team spawn` uses `pi --mode rpc` subprocesses.
75
77
  - Pros: simple, managed lifecycle.
76
- - Cons: you don’t see a full interactive teammate UI like Claude’s split-pane mode.
78
+ - Cons: you don’t see a full interactive comrade UI like Claude’s split-pane mode.
77
79
  - We manually started workers in separate tmux windows (setting `PI_TEAMS_WORKER=1`, `PI_TEAMS_TEAM_ID=...`, etc). This now shows up in the leader widget because workers upsert themselves into `config.json`, and the leader renders online workers from team config.
78
- - Update: leader now renders teammates from `team config` and also auto-adds unknown senders on idle notifications (so manual tmux workers feel first-class).
80
+ - Update: leader now renders comrades from `team config` and also auto-adds unknown senders on idle notifications (so manual tmux workers feel first-class).
79
81
  - Improvement idea: optional spawn mode that starts a worker in a new tmux pane/window.
80
82
 
81
83
  - **Two messaging paths** (`/team send` vs `/team dm`):
@@ -84,7 +86,7 @@ First impressions:
84
86
  - Improvement idea: clearer naming and/or a single “message” command with a mode flag.
85
87
 
86
88
  - **Runaway tasks / timeboxing**: a vague task prompt can turn into a long “research spiral”.
87
- - In manual-tmux mode, there isn’t a great way (yet) for the leader to *steer* an in-flight run (unlike `/team steer` for RPC-spawned teammates).
89
+ - In manual-tmux mode, there isn’t a great way (yet) for the leader to *steer* an in-flight run (unlike `/team steer` for RPC-spawned comrades).
88
90
  - Improvement idea: add a mailbox-level “steer” protocol message that workers can treat as an in-flight follow-up if they’re currently running.
89
91
 
90
92
  - **Failure semantics are underspecified**: tool failures show up in the worker UI, but our task store currently only supports `pending|in_progress|completed`.
@@ -0,0 +1,139 @@
1
+ # pi-agent-teams — Runtime Smoke Test Plan
2
+
3
+ ## Prerequisites
4
+
5
+ - `pi` CLI installed (`pi --version` → `0.52.x+`)
6
+ - `node_modules/` present (run `npm install` or symlink from main repo)
7
+ - `npx tsx` available for running `.mts` test scripts
8
+
9
+ ## 1. Automated Unit Smoke Test (no interactive session)
10
+
11
+ Exercises all core primitives directly via `tsx`:
12
+
13
+ ```bash
14
+ npx tsx scripts/smoke-test.mts
15
+ # or: npm run smoke-test
16
+ ```
17
+
18
+ **What it tests** (overview):
19
+
20
+ | Module | Coverage |
21
+ |------------------|-----------------------------------------------------------------|
22
+ | `names.ts` | `sanitizeName` character replacement, edge cases |
23
+ | `fs-lock.ts` | `withLock` returns value, cleans up lock file |
24
+ | `mailbox.ts` | `writeToMailbox`, `popUnreadMessages`, read-once semantics |
25
+ | `task-store.ts` | CRUD, `startAssignedTask`, `completeTask`, `claimNextAvailable`,|
26
+ | | `unassignTasksForAgent`, dependencies, `clearTasks` |
27
+ | `team-config.ts` | `ensureTeamConfig` (idempotent), `upsertMember`, `setMemberStatus`, `loadTeamConfig` |
28
+ | `protocol.ts` | Structured message parsers (valid + invalid JSON + wrong type) |
29
+ | Pi CLI | `pi --version` executes (skipped in CI if `pi` not on PATH) |
30
+
31
+ **Expected result:** `PASSED: <n> FAILED: 0`
32
+
33
+ ## 2. Extension Loading Test
34
+
35
+ Verify Pi can load the extension entry point without crashing:
36
+
37
+ ```bash
38
+ pi --no-extensions -e extensions/teams/index.ts --help
39
+ ```
40
+
41
+ **Expected:** exits 0, shows Pi help output (no TypeScript/import errors).
42
+
43
+ ## 3. Interactive Smoke Test (manual)
44
+
45
+ ### 3a. Launch Pi with the extension
46
+
47
+ ```bash
48
+ # From the repo root:
49
+ pi --no-extensions -e extensions/teams/index.ts
50
+ ```
51
+
52
+ Or, if the extension is symlinked into `~/.pi/agent/extensions/pi-agent-teams`:
53
+
54
+ ```bash
55
+ pi # auto-loads from extensions dir
56
+ ```
57
+
58
+ ### 3b. Check extension is active
59
+
60
+ ```
61
+ /team help
62
+ ```
63
+
64
+ **Expected:** shows usage lines for `/team spawn`, `/team task`, etc.
65
+
66
+ ### 3c. Spawn a teammate ("comrade" in soviet style)
67
+
68
+ ```
69
+ /team spawn agent1 fresh shared
70
+ ```
71
+
72
+ **Expected:** notification "Spawned agent1" or similar, widget shows `Teammate agent1: idle` (or `Comrade agent1: idle` in soviet style).
73
+
74
+ ### 3d. Create and assign a task
75
+
76
+ ```
77
+ /team task add agent1: Say hello
78
+ /team task list
79
+ ```
80
+
81
+ **Expected:** task #1 created, assigned to agent1, status `pending` → `in_progress`.
82
+
83
+ ### 3e. Verify mailbox delivery
84
+
85
+ ```
86
+ /team dm agent1 ping from lead
87
+ ```
88
+
89
+ **Expected:** "DM queued for agent1" notification.
90
+
91
+ ### 3f. Delegate via tool
92
+
93
+ Ask the model:
94
+ > "Delegate a task to agent1: write a haiku about coding"
95
+
96
+ **Expected:** the `teams` tool is invoked, task created and assigned.
97
+
98
+ ### 3g. Shutdown
99
+
100
+ ```
101
+ /team shutdown agent1
102
+ /team kill agent1
103
+ ```
104
+
105
+ **Expected:** agent1 goes offline, widget updates.
106
+
107
+ Optional: stop all teammates without ending the leader session:
108
+
109
+ ```
110
+ /team shutdown
111
+ ```
112
+
113
+ **Expected:** all teammates stop; leader remains active until you exit it (e.g. ctrl+d).
114
+
115
+ ## 4. Worker-side Smoke (verifying child process)
116
+
117
+ To test the worker role directly:
118
+
119
+ ```bash
120
+ PI_TEAMS_WORKER=1 \
121
+ PI_TEAMS_TEAM_ID=test-team \
122
+ PI_TEAMS_AGENT_NAME=agent1 \
123
+ PI_TEAMS_LEAD_NAME=team-lead \
124
+ PI_TEAMS_STYLE=normal \
125
+ pi --no-extensions -e extensions/teams/index.ts --mode rpc
126
+ ```
127
+
128
+ **Expected:** process starts in RPC mode, registers `team_message` tool, polls mailbox.
129
+
130
+ ## 5. Checklist Summary
131
+
132
+ | # | Test | Method | Status |
133
+ |---|-------------------------------|------------|--------|
134
+ | 1 | Unit primitives (60 asserts) | Automated | ✅ |
135
+ | 2 | Extension loading | CLI | ✅ |
136
+ | 3 | Interactive spawn/task/dm | Manual | 📋 |
137
+ | 4 | Worker-side RPC | Manual | 📋 |
138
+
139
+ ✅ = verified in this run, 📋 = documented for manual execution.
@@ -0,0 +1,74 @@
1
+ // @ts-check
2
+ import eslint from "@eslint/js";
3
+ import tseslint from "typescript-eslint";
4
+
5
+ export default tseslint.config(
6
+ {
7
+ ignores: [
8
+ "node_modules/**",
9
+ "dist/**",
10
+ "build/**",
11
+ "coverage/**",
12
+ ".artifacts/**",
13
+ ".research/**",
14
+ ".resume-sessions/**",
15
+ ],
16
+ },
17
+
18
+ eslint.configs.recommended,
19
+ ...tseslint.configs.recommended,
20
+
21
+ {
22
+ files: ["extensions/**/*.ts", "scripts/**/*.ts", "scripts/**/*.mts"],
23
+ rules: {
24
+ // ━━ Project invariants (AGENTS.md) ━━
25
+ "@typescript-eslint/no-explicit-any": "error",
26
+ "@typescript-eslint/no-non-null-assertion": "error",
27
+ "@typescript-eslint/ban-ts-comment": [
28
+ "error",
29
+ {
30
+ "ts-ignore": true,
31
+ "ts-expect-error": true,
32
+ "ts-nocheck": true,
33
+ "ts-check": false,
34
+ },
35
+ ],
36
+
37
+ // Imports/types
38
+ "@typescript-eslint/consistent-type-imports": [
39
+ "error",
40
+ { prefer: "type-imports", disallowTypeAnnotations: false },
41
+ ],
42
+
43
+ // General correctness
44
+ eqeqeq: ["error", "always"],
45
+
46
+ // Prefer TS-aware unused-vars
47
+ "no-unused-vars": "off",
48
+ "@typescript-eslint/no-unused-vars": [
49
+ "warn",
50
+ {
51
+ argsIgnorePattern: "^_",
52
+ varsIgnorePattern: "^_",
53
+ caughtErrorsIgnorePattern: "^_",
54
+ },
55
+ ],
56
+ },
57
+ },
58
+
59
+ // Scripts/tests: console is expected
60
+ {
61
+ files: ["scripts/**/*.ts", "scripts/**/*.mts"],
62
+ rules: {
63
+ "no-console": "off",
64
+ },
65
+ },
66
+
67
+ // Extension source: discourage stray console.log
68
+ {
69
+ files: ["extensions/**/*.ts"],
70
+ rules: {
71
+ "no-console": "warn",
72
+ },
73
+ },
74
+ );