indus-swarms 0.1.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.
- package/AGENTS.md +14 -0
- package/LICENSE +21 -0
- package/README.md +119 -0
- package/docs/claude-parity.md +151 -0
- package/docs/field-notes-teams-setup.md +107 -0
- package/docs/smoke-test-plan.md +146 -0
- package/eslint.config.js +74 -0
- package/extensions/teams/README.md +23 -0
- package/extensions/teams/activity-tracker.ts +234 -0
- package/extensions/teams/cleanup.ts +31 -0
- package/extensions/teams/fs-lock.ts +87 -0
- package/extensions/teams/hooks.ts +363 -0
- package/extensions/teams/index.ts +18 -0
- package/extensions/teams/leader-attach-commands.ts +221 -0
- package/extensions/teams/leader-inbox.ts +214 -0
- package/extensions/teams/leader-info-commands.ts +140 -0
- package/extensions/teams/leader-lifecycle-commands.ts +559 -0
- package/extensions/teams/leader-messaging-commands.ts +148 -0
- package/extensions/teams/leader-plan-commands.ts +95 -0
- package/extensions/teams/leader-spawn-command.ts +149 -0
- package/extensions/teams/leader-task-commands.ts +435 -0
- package/extensions/teams/leader-team-command.ts +382 -0
- package/extensions/teams/leader-teams-tool.ts +1075 -0
- package/extensions/teams/leader.ts +925 -0
- package/extensions/teams/mailbox.ts +131 -0
- package/extensions/teams/model-policy.ts +142 -0
- package/extensions/teams/names.ts +121 -0
- package/extensions/teams/paths.ts +37 -0
- package/extensions/teams/protocol.ts +241 -0
- package/extensions/teams/spawn-types.ts +36 -0
- package/extensions/teams/task-store.ts +544 -0
- package/extensions/teams/team-attach-claim.ts +205 -0
- package/extensions/teams/team-config.ts +335 -0
- package/extensions/teams/team-discovery.ts +59 -0
- package/extensions/teams/teammate-rpc.ts +261 -0
- package/extensions/teams/teams-panel.ts +1186 -0
- package/extensions/teams/teams-style.ts +322 -0
- package/extensions/teams/teams-ui-shared.ts +89 -0
- package/extensions/teams/teams-widget.ts +212 -0
- package/extensions/teams/worker.ts +605 -0
- package/extensions/teams/worktree.ts +103 -0
- package/package.json +53 -0
- package/scripts/e2e-rpc-test.mjs +277 -0
- package/scripts/integration-claim-test.mts +157 -0
- package/scripts/integration-hooks-remediation-test.mts +382 -0
- package/scripts/integration-spawn-overrides-test.mts +398 -0
- package/scripts/integration-todo-test.mts +533 -0
- package/scripts/lib/pi-workers.ts +105 -0
- package/scripts/smoke-test.mts +764 -0
- package/scripts/start-tmux-team.sh +91 -0
- package/skills/agent-teams/SKILL.md +180 -0
- package/tsconfig.strict.json +22 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Project-specific guidance for AI agents working in this repository.
|
|
4
|
+
|
|
5
|
+
## Operating Intent (Critical)
|
|
6
|
+
|
|
7
|
+
This project is **agent-driven**, not user-operated.
|
|
8
|
+
|
|
9
|
+
- Treat the human as setting goals/outcomes, not executing operational steps.
|
|
10
|
+
- Prefer autonomous agent flows (tool actions, policies, retries, task transitions).
|
|
11
|
+
- Do **not** rely on manual user remediation as the default path.
|
|
12
|
+
- UI panels and slash commands are primarily for observability/debugging and emergency override.
|
|
13
|
+
- Quality-gate failures should be handled by agent policy (warn/followup/reopen/reopen_followup), not by asking the user to manually clear state.
|
|
14
|
+
- Human-in-the-loop steps should happen only when explicitly required by policy or requested by the user.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Mustier
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# indus-swarms (pi-agent-teams)
|
|
2
|
+
|
|
3
|
+
A lightweight extension for the `indusagi` CLI that brings Claude-style agent teams to your Pi workflows. The leader process manages shared task lists, mailboxes, and team status while spawning autonomous teammates (workers) that claim, execute, and report on tasks.
|
|
4
|
+
|
|
5
|
+
## What it provides
|
|
6
|
+
|
|
7
|
+
1. **Team coordination primitives** — shared task list files, dependency tracking, and auto-claiming keep multiple agents working without manual juggling.
|
|
8
|
+
2. **Messaging layer** — direct `DM` mailboxes, broadcasts, and steering instructions let you nudge teammates from the leader session.
|
|
9
|
+
3. **Lifecycle control** — graceful shutdown, kill, prune, and cleanup keep team artifacts tidy; widgets show real-time status.
|
|
10
|
+
4. **Model/workspace control** — spawn teammates with fresh or branched contexts, shared workspaces or per-agent git worktrees, and optional plan/approval flows.
|
|
11
|
+
5. **Hook + widget support** — leader-side hooks can enforce quality gates and the `/tw` widget surfaces tasks, inboxes, and shortcuts like `m` for message and `t` to toggle task view.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
> Requires Node 20+ and the `indusagi` CLI. Install once per machine, then install the extension however you prefer:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install indusagi indusagi-coding-agent
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Option A — install from npm:**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
indusagi install npm:@tmustier/pi-agent-teams
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Option A2 — install the renamed package (if published):**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
indusagi install indus-swarms
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Option B — load directly for development:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
indusagi -e /Users/varunisrani/indusagi-ts/indus-swarms/extensions/teams/index.ts
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Option C — install from the local folder:**
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
indusagi install /Users/varunisrani/indusagi-ts/indus-swarms
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Once installed, run `indusagi` as usual and the extension auto-discovers.
|
|
46
|
+
|
|
47
|
+
If you prefer installing directly via npm (without `indusagi install`), run:
|
|
48
|
+
```
|
|
49
|
+
npm install indus-swarms
|
|
50
|
+
```
|
|
51
|
+
Then load it via
|
|
52
|
+
```
|
|
53
|
+
indusagi -e ./node_modules/indus-swarms/extensions/teams/index.ts
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick usage
|
|
57
|
+
|
|
58
|
+
1. Start `indusagi`.
|
|
59
|
+
2. Confirm the extension is active with `/team id` or `/team help`.
|
|
60
|
+
3. Spawn teammates manually:
|
|
61
|
+
```text
|
|
62
|
+
/team spawn alice shared
|
|
63
|
+
/team spawn bob branch worktree
|
|
64
|
+
```
|
|
65
|
+
4. Create tasks and assign them:
|
|
66
|
+
```text
|
|
67
|
+
/team task add alice: Explore AGENTS.md
|
|
68
|
+
/team task list
|
|
69
|
+
/team task assign 1 bob
|
|
70
|
+
```
|
|
71
|
+
5. Communicate:
|
|
72
|
+
- `/team dm <name> <msg>` — mailbox DM
|
|
73
|
+
- `/team broadcast <msg>` — message everyone
|
|
74
|
+
- `/team steer <name> <msg>` — steering for RPC workers
|
|
75
|
+
6. Monitor progress with `/tw` (or `/team panel`) and use shortcuts (`t` for current agent tasks, `m` for a DM, `k` to kill, etc.).
|
|
76
|
+
7. When you are done:
|
|
77
|
+
```text
|
|
78
|
+
/team shutdown
|
|
79
|
+
/team cleanup
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Alternatively, `/swarm <task>` lets the model spawn teammates, delegate tasks, and coordinate the work for you.
|
|
83
|
+
|
|
84
|
+
## Environment variables
|
|
85
|
+
|
|
86
|
+
| Variable | Purpose | Default |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `INDUS_TEAMS_ROOT_DIR` | Root directory for all team artifacts (task lists, mailboxes, sessions). Defaults to `~/.indusagi/agent/teams` (legacy `~/.pi/agent/teams` also works). | `~/.indusagi/agent/teams` |
|
|
89
|
+
| `INDUS_TEAMS_DEFAULT_AUTO_CLAIM` | Auto-claim new tasks when teammates idle. | `1` |
|
|
90
|
+
| `INDUS_TEAMS_STYLE` | UI style (`normal`, `soviet`, `pirate`, or custom). | `normal` |
|
|
91
|
+
| `INDUS_TEAMS_HOOKS_ENABLED` | Enable leader-side hooks/quality gates. | `0` |
|
|
92
|
+
| `INDUS_TEAMS_HOOKS_DIR` | Directory holding `on_idle`, `on_task_completed`, `on_task_failed` hooks. | `<teamsRoot>/_hooks` |
|
|
93
|
+
| `INDUS_TEAMS_HOOK_TIMEOUT_MS` | Hook timeout in milliseconds. | `60000` |
|
|
94
|
+
| `INDUS_TEAMS_HOOKS_FAILURE_ACTION` | Policy when a hook fails (`warn`, `followup`, `reopen`, `reopen_followup`). | `warn` |
|
|
95
|
+
| `INDUS_TEAMS_HOOKS_MAX_REOPENS_PER_TASK` | Max reopen count when hooks requeue tasks. | `3` |
|
|
96
|
+
| `INDUS_TEAMS_HOOKS_FOLLOWUP_OWNER` | Owner of follow-up tasks created after hook failures (`member`, `lead`, `none`). | `member` |
|
|
97
|
+
|
|
98
|
+
Add custom styles under `~/.indusagi/agent/teams/_styles/<style>.json` to override naming and lifecycle copy.
|
|
99
|
+
|
|
100
|
+
## Development & tests
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm run check # typecheck + lint via tsx/eslint
|
|
104
|
+
npm run smoke-test # automated filesystem smoke test
|
|
105
|
+
node scripts/e2e-rpc-test.mjs # leader + RPC teammate handshake
|
|
106
|
+
./scripts/start-tmux-team.sh # helper that launches leader + tmux worker panes
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Worker processes run `indusagi --mode rpc` with the same extension in worker mode via `scripts/lib/pi-workers.ts`.
|
|
110
|
+
|
|
111
|
+
## Tips
|
|
112
|
+
|
|
113
|
+
- Use `/team attach list` and `/team attach <teamId>` to reconnect to another leader session.
|
|
114
|
+
- For structured automation, invoke the built-in `teams` tool manually via JSON (see `extensions/teams/leader-teams-tool.ts`).
|
|
115
|
+
- Keep `PI_TEAMS_ROOT_DIR` isolated per experiment to easily clean team directories.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Claude Agent Teams parity roadmap (indus-swarms / pi-agent-teams)
|
|
2
|
+
|
|
3
|
+
Last updated: 2026-02-10
|
|
4
|
+
|
|
5
|
+
This document tracks **feature parity gaps** between:
|
|
6
|
+
|
|
7
|
+
- Claude Code **Agent Teams** (official docs)
|
|
8
|
+
- https://code.claude.com/docs/en/agent-teams#control-your-agent-team
|
|
9
|
+
- https://code.claude.com/docs/en/interactive-mode#task-list
|
|
10
|
+
|
|
11
|
+
…and this repository’s implementation:
|
|
12
|
+
|
|
13
|
+
- `indus-swarms` (pi-agent-teams Pi extension)
|
|
14
|
+
|
|
15
|
+
> Terminology note: this extension supports `PI_TEAMS_STYLE=<style>`.
|
|
16
|
+
> This doc often uses “comrade” as a generic stand-in for “worker/teammate”, but **styles can customize terminology, naming, and lifecycle copy**.
|
|
17
|
+
> Built-ins: `normal`, `soviet`, `pirate`. Custom styles live under `~/.pi/agent/teams/_styles/`.
|
|
18
|
+
|
|
19
|
+
## Scope / philosophy
|
|
20
|
+
|
|
21
|
+
- Target the **same coordination primitives** as Claude Teams:
|
|
22
|
+
- shared task list
|
|
23
|
+
- mailbox messaging
|
|
24
|
+
- long-lived workers
|
|
25
|
+
- Prefer **inspectable, local-first artifacts** (files + lock files).
|
|
26
|
+
- Avoid guidance that bypasses Claude feature gating; we only document behavior.
|
|
27
|
+
- Accept that some Claude UX (terminal keybindings + split-pane integration) may not be achievable in Pi without deeper TUI/terminal integration.
|
|
28
|
+
|
|
29
|
+
## Pi-specific extras (not Claude parity)
|
|
30
|
+
|
|
31
|
+
These are intentional differences / additions:
|
|
32
|
+
|
|
33
|
+
- **Configurable styles** (`/team style …`) for terminology + naming + lifecycle copy.
|
|
34
|
+
- **Git worktrees** for isolation (`/team spawn <name> … worktree`).
|
|
35
|
+
- **Session branching** (clone leader context into a teammate).
|
|
36
|
+
- A **status widget + interactive panel** (`/tw`, `/team panel`).
|
|
37
|
+
|
|
38
|
+
## Parity matrix (docs-oriented)
|
|
39
|
+
|
|
40
|
+
Legend: ✅ implemented • 🟡 partial • ❌ missing
|
|
41
|
+
|
|
42
|
+
| Area | Claude docs behavior | Pi Teams status | Notes / next step | Priority |
|
|
43
|
+
| --- | --- | --- | --- | --- |
|
|
44
|
+
| Enablement | `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` + settings | N/A | Pi extension is available when installed/loaded. | — |
|
|
45
|
+
| Team config | `~/.claude/teams/<team>/config.json` w/ members | ✅ | `extensions/teams/team-config.ts` (under `~/.pi/agent/teams/...` or `PI_TEAMS_ROOT_DIR`). | P0 |
|
|
46
|
+
| 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 |
|
|
47
|
+
| Self-claim | Comrades self-claim next unassigned, unblocked task; file locking | ✅ | `claimNextAvailableTask()` + locks; enabled by default (`PI_TEAMS_DEFAULT_AUTO_CLAIM=1`). | P0 |
|
|
48
|
+
| Explicit assign | Lead assigns task to comrade | ✅ | `/team task assign` sets owner + pings via mailbox. | P0 |
|
|
49
|
+
| “Message” vs “broadcast” | Send to one comrade or all comrades | ✅ | `/team dm` + `/team broadcast` use mailbox; `/team send` uses RPC. Recipients = config workers + RPC map + active task owners. | P0 |
|
|
50
|
+
| Comrade↔comrade messaging | Comrades message each other directly | ✅ | Worker tool `team_message`; messages via mailbox + CC leader via `peer_dm_sent`. | P1 |
|
|
51
|
+
| Display modes | In-process selection (Shift+Up/Down); split panes (tmux/iTerm) | ❌ | Pi has widget/panel + commands, but no terminal-level comrade navigation/panes. | P2 |
|
|
52
|
+
| Delegate mode | Lead restricted to coordination-only tools | ✅ | `/team delegate [on|off]`; `tool_call` blocks `bash/edit/write`; widget shows `[delegate]`. | P1 |
|
|
53
|
+
| Plan approval | Comrade can be “plan required” and needs lead approval to implement | ✅ | `/team spawn <name> plan` → read-only tools; sends `plan_approval_request`; `/team plan approve|reject`. | P1 |
|
|
54
|
+
| Shutdown handshake | Lead requests shutdown; comrade can approve/reject | ✅ | Protocol: `shutdown_request` → `shutdown_approved` / `shutdown_rejected`. `/team shutdown <name>` (graceful), `/team kill <name>` (SIGTERM). Wording is style-controlled (e.g. “was asked to shut down”, “walked the plank”). | P1 |
|
|
55
|
+
| Cleanup team | “Clean up the team” removes shared resources after comrades stopped | ✅ | `/team cleanup [--force]` deletes only `<teamsRoot>/<teamId>` after safety checks. | P1 |
|
|
56
|
+
| Hooks / quality gates | `ComradeIdle`, `TaskCompleted` hooks | 🟡 | Optional leader-side hook runner (idle/task-complete/task-fail) via `PI_TEAMS_HOOKS_ENABLED=1` + scripts under `_hooks/`; inline failure surfacing + failure-action policies (`warn`/`followup`/`reopen`/`reopen_followup`) implemented; stable hook context payload exposed via `PI_TEAMS_HOOK_CONTEXT_JSON` + auto-remediation flow (reopen cap / follow-up owner policy / teammate notification). Runtime policy changes are agent-invocable via `teams` actions (`hooks_policy_get` / `hooks_policy_set`). | P2 |
|
|
57
|
+
| Task list UX | Ctrl+T toggle; show all/clear tasks by asking | 🟡 | Widget + `/team task list` + `/team task show` + `/team task clear`; panel supports fast `t`/`shift+t` toggle into task-centric view (`Ctrl+T` is reserved by Pi for thinking blocks). | P0 |
|
|
58
|
+
| Shared task list across sessions | `CLAUDE_CODE_TASK_LIST_ID=...` | ✅ | Worker env: `PI_TEAMS_TASK_LIST_ID` (manual workers). Leader: `/team task use <taskListId>` (persisted). Newly spawned workers inherit; existing workers need restart. | P1 |
|
|
59
|
+
| Join/attach flow | Join existing team context from another running session | 🟡 | `/team attach list`, `/team attach <teamId> [--claim]`, `/team detach` plus claim heartbeat/takeover handshake added. Widget/panel now show attached-mode banner + detach hint. | P2 |
|
|
60
|
+
|
|
61
|
+
## Prioritized roadmap
|
|
62
|
+
|
|
63
|
+
### P0 (done): collaboration primitives parity
|
|
64
|
+
|
|
65
|
+
1) **Task dependency commands + UX** ✅
|
|
66
|
+
- `/team task dep add <id> <depId>` / `dep rm ...` / `dep ls <id>`
|
|
67
|
+
- `task list` output shows blocked status + deps/blocks summary
|
|
68
|
+
- `/team task show <id>` shows full description + `metadata.result`
|
|
69
|
+
|
|
70
|
+
2) **Broadcast messaging** ✅
|
|
71
|
+
- `/team broadcast <msg...>` (mailbox broadcast)
|
|
72
|
+
|
|
73
|
+
3) **Task list hygiene** ✅
|
|
74
|
+
- `/team task clear [completed|all] [--force]` (safe delete within `teamsRoot/teamId`)
|
|
75
|
+
|
|
76
|
+
### P1 (done): governance + lifecycle parity
|
|
77
|
+
|
|
78
|
+
4) **Shutdown handshake** ✅
|
|
79
|
+
- `shutdown_request` → `shutdown_approved` / `shutdown_rejected`
|
|
80
|
+
- Worker rejects when busy (streaming + active task), auto-approves when idle
|
|
81
|
+
- `/team shutdown <name> [reason...]` (graceful), `/team kill <name>` (force)
|
|
82
|
+
|
|
83
|
+
5) **Plan approval** ✅
|
|
84
|
+
- `/team spawn <name> ... plan` sets `PI_TEAMS_PLAN_REQUIRED=1`
|
|
85
|
+
- Worker starts read-only; submits `plan_approval_request`
|
|
86
|
+
- `/team plan approve|reject <name>`
|
|
87
|
+
- Agent-driven equivalent via `teams` tool: `plan_approve` / `plan_reject`
|
|
88
|
+
|
|
89
|
+
6) **Delegate mode (leader)** ✅
|
|
90
|
+
- `/team delegate [on|off]` (or `PI_TEAMS_DELEGATE_MODE=1`)
|
|
91
|
+
- Blocks `bash/edit/write` while active
|
|
92
|
+
|
|
93
|
+
7) **Cleanup** ✅
|
|
94
|
+
- `/team cleanup [--force]` deletes only `<teamsRoot>/<teamId>` after safety checks.
|
|
95
|
+
|
|
96
|
+
8) **Peer-to-peer messaging** ✅
|
|
97
|
+
- Worker tool `team_message`
|
|
98
|
+
- Mailbox transport; leader CC notifications
|
|
99
|
+
|
|
100
|
+
9) **Shared task list across sessions** ✅
|
|
101
|
+
- `/team task use <taskListId>` + persisted `config.json`
|
|
102
|
+
|
|
103
|
+
### P2: UX + “product-level” parity
|
|
104
|
+
|
|
105
|
+
10) **Hooks / quality gates** 🟡 (partial)
|
|
106
|
+
- Implemented: optional leader-side hook runner (opt-in + timeout + logs).
|
|
107
|
+
- Implemented: inline failure surfacing (task metadata + widget warning + task-panel/task-show quality-gate details).
|
|
108
|
+
- Implemented: hook-failure policy controls via `PI_TEAMS_HOOKS_FAILURE_ACTION` (`warn`, `followup`, `reopen`, `reopen_followup`).
|
|
109
|
+
- Implemented: runtime team-level policy overrides (via `teams` tool: `hooks_policy_get` / `hooks_policy_set`) layered over env defaults.
|
|
110
|
+
- Implemented: standardized hook context contract (`PI_TEAMS_HOOK_CONTEXT_VERSION=1`, `PI_TEAMS_HOOK_CONTEXT_JSON`).
|
|
111
|
+
- Implemented: autonomous remediation helpers (auto-reopen cap, follow-up owner policy, teammate remediation notification).
|
|
112
|
+
- Implemented: deterministic integration coverage (`scripts/integration-hooks-remediation-test.mts`) for failed hook -> reopen/follow-up/nudge flow.
|
|
113
|
+
- Still missing: broader contract versioning story + richer policy visualization UX.
|
|
114
|
+
|
|
115
|
+
11) **Better comrade interaction UX (within Pi constraints)** 🟡 (partial)
|
|
116
|
+
- Implemented: panel overview shows selected teammate context (active/last completed task + last transcript event).
|
|
117
|
+
- Implemented: faster keyboard controls (`w/s`, `1-9`, `m/d`).
|
|
118
|
+
- Implemented: task-centric panel mode (`t`/`shift+t`) with owned-task drilldown, dependency/block visibility, and quick jump back to transcript (`Ctrl+T` reserved by Pi).
|
|
119
|
+
- Implemented: in-panel task mutations for selected task (`c` complete, `p` pending, `i` in-progress, `u` unassign).
|
|
120
|
+
- Implemented: in-panel reassignment flow (`r`) with teammate picker.
|
|
121
|
+
- Implemented: agent-invocable task mutations via `teams` tool (`task_assign`, `task_unassign`, `task_set_status`) so flows do not require manual panel interaction.
|
|
122
|
+
- Implemented: agent-invocable dependency/messaging actions via `teams` tool (`task_dep_add|rm|ls`, `message_dm|broadcast|steer`).
|
|
123
|
+
- Implemented: agent-invocable lifecycle actions via `teams` tool (`member_spawn|shutdown|kill|prune`).
|
|
124
|
+
- Implemented: agent-invocable governance actions via `teams` tool (`plan_approve|plan_reject`).
|
|
125
|
+
- Implemented: agent-invocable model policy introspection/check actions via `teams` tool (`model_policy_get|model_policy_check`) to validate spawn overrides before execution.
|
|
126
|
+
- Next: optional tmux split-pane integration and deeper dependency/task editing flows in panel.
|
|
127
|
+
|
|
128
|
+
12) **Join/attach flow** 🟡 (partial)
|
|
129
|
+
- Implemented: `/team attach list`, `/team attach <teamId> [--claim]`, `/team detach`.
|
|
130
|
+
- Implemented: explicit attach claim handshake with heartbeat + force takeover (`--claim`).
|
|
131
|
+
- Implemented: attached-mode affordances in widget/panel (external team banner + `/team detach` hint).
|
|
132
|
+
|
|
133
|
+
## Where changes would land (code map)
|
|
134
|
+
|
|
135
|
+
- Leader orchestration: `extensions/teams/leader.ts`
|
|
136
|
+
- Leader `/team` command dispatch: `extensions/teams/leader-team-command.ts`
|
|
137
|
+
- Attach/discovery commands: `extensions/teams/leader-attach-commands.ts`, `extensions/teams/team-discovery.ts`, `extensions/teams/team-attach-claim.ts`
|
|
138
|
+
- Leader LLM tool (`teams`): `extensions/teams/leader-teams-tool.ts`
|
|
139
|
+
- Worker mailbox polling + self-claim + protocols: `extensions/teams/worker.ts`
|
|
140
|
+
- Task store + locking: `extensions/teams/task-store.ts`, `extensions/teams/fs-lock.ts`
|
|
141
|
+
- Mailbox store + locking: `extensions/teams/mailbox.ts`
|
|
142
|
+
- Team config: `extensions/teams/team-config.ts`
|
|
143
|
+
- Styles + naming: `extensions/teams/teams-style.ts`
|
|
144
|
+
- Optional workspace isolation: `extensions/teams/worktree.ts`
|
|
145
|
+
|
|
146
|
+
## Testing strategy
|
|
147
|
+
|
|
148
|
+
- Keep tests hermetic by setting `PI_TEAMS_ROOT_DIR` to a temp directory.
|
|
149
|
+
- Extend:
|
|
150
|
+
- `scripts/smoke-test.mts` (run via `npm run smoke-test`) for filesystem-only behaviors
|
|
151
|
+
- `scripts/e2e-rpc-test.mjs` for protocol flows (shutdown handshake, plan approval)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Field notes: using `indus-swarms` (pi-agent-teams) for real (setup + surprises)
|
|
2
|
+
|
|
3
|
+
Date: 2026-02-07
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
> Terminology note: the extension supports `PI_TEAMS_STYLE=<style>`. Built-ins: `normal`, `soviet`, `pirate`. You can also add custom styles via `~/.pi/agent/teams/_styles/<style>.json`.
|
|
8
|
+
|
|
9
|
+
## Test run: test1
|
|
10
|
+
|
|
11
|
+
Decisions: tmux session `pi-teams-test1`; `PI_TEAMS_ROOT_DIR=~/projects/indus-swarms/test1`; `teamId=0baaa0e6-8020-4d9a-bf33-c1a65f99a2f7`; workers started manually in tmux (not `/team spawn`).
|
|
12
|
+
|
|
13
|
+
First impressions:
|
|
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.
|
|
15
|
+
- Pinning `PI_TEAMS_ROOT_DIR` made reruns/id discovery predictable (no “find the new folder” step).
|
|
16
|
+
- tmux workflow feels close to Claude-style split panes; bootstrap ergonomics still need smoothing.
|
|
17
|
+
- Surprise: `/team spawn <name> branch` failed once with `Entry <id> not found` (branch-from leaf missing on disk); `/team spawn <name> fresh` worked.
|
|
18
|
+
- Surprise (automation): when driving the leader via `tmux send-keys`, back-to-back `/team ...` commands sometimes only executed the first one unless we inserted a small delay.
|
|
19
|
+
|
|
20
|
+
## Setup (tmux-based)
|
|
21
|
+
|
|
22
|
+
### Why tmux?
|
|
23
|
+
|
|
24
|
+
- Pi sessions are long-lived and interactive.
|
|
25
|
+
- Our harness (and many CI environments) dislike background processes that keep stdio open.
|
|
26
|
+
- tmux gives us:
|
|
27
|
+
- a stable place to run a leader session
|
|
28
|
+
- optional separate panes/windows for worker sessions
|
|
29
|
+
- the ability to attach/detach without killing the team
|
|
30
|
+
|
|
31
|
+
### Environment knobs used
|
|
32
|
+
|
|
33
|
+
- `PI_TEAMS_ROOT_DIR` — isolate Teams artifacts from `~/.pi/agent/teams` while experimenting.
|
|
34
|
+
- Recommendation: use a *fresh, empty* temp directory per run so it’s easy to discover the current teamId by listing the directory.
|
|
35
|
+
|
|
36
|
+
### Session bootstrap (manual)
|
|
37
|
+
|
|
38
|
+
(There is also a helper script now: `./scripts/start-tmux-team.sh`.)
|
|
39
|
+
|
|
40
|
+
1. Pick a temp Teams root:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
export PI_TEAMS_ROOT_DIR="/tmp/pi-teams-$(date +%Y%m%d-%H%M%S)"
|
|
44
|
+
mkdir -p "$PI_TEAMS_ROOT_DIR"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
2. Start leader in tmux:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
tmux new -s pi-teams -c ~/projects/indus-swarms \
|
|
51
|
+
"PI_TEAMS_ROOT_DIR=$PI_TEAMS_ROOT_DIR indusagi -e ~/projects/indus-swarms/extensions/teams/index.ts"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. In the leader session:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
/team help
|
|
58
|
+
/team spawn alice branch
|
|
59
|
+
/team spawn bob branch
|
|
60
|
+
/team task add alice: add /team broadcast command
|
|
61
|
+
/team task add bob: add task dependency commands
|
|
62
|
+
/team task list
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
4. Optional: start **interactive worker panes** (instead of leader-spawned RPC workers).
|
|
66
|
+
|
|
67
|
+
This currently requires discovering the leader’s `teamId` first (see “Surprises” below).
|
|
68
|
+
|
|
69
|
+
## Surprises / confusion points (so far)
|
|
70
|
+
|
|
71
|
+
- **TeamId discoverability**: the leader uses `sessionId` as `teamId`. That’s convenient internally, but not obvious externally.
|
|
72
|
+
- Workaround used: point `PI_TEAMS_ROOT_DIR` at a fresh directory and `ls` it to find the generated `<teamId>` folder.
|
|
73
|
+
- Note: the team directory isn’t necessarily created instantly on process start (we saw a short delay), so scripts may need a small retry loop.
|
|
74
|
+
- Update: implemented `/team id` and `/team env <name>` (prints env vars + a one-liner to start a manual worker).
|
|
75
|
+
|
|
76
|
+
- **tmux vs `/team spawn`**: `/team spawn` uses `indusagi --mode rpc` subprocesses.
|
|
77
|
+
- Pros: simple, managed lifecycle.
|
|
78
|
+
- Cons: you don’t see a full interactive comrade UI like Claude’s split-pane mode.
|
|
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.
|
|
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).
|
|
81
|
+
- Improvement idea: optional spawn mode that starts a worker in a new tmux pane/window.
|
|
82
|
+
|
|
83
|
+
- **Two messaging paths** (`/team send` vs `/team dm`):
|
|
84
|
+
- `/team send` = RPC prompt (immediate “user message”)
|
|
85
|
+
- `/team dm` = mailbox message (Claude-style)
|
|
86
|
+
- Improvement idea: clearer naming and/or a single “message” command with a mode flag.
|
|
87
|
+
|
|
88
|
+
- **Runaway tasks / timeboxing**: a vague task prompt can turn into a long “research spiral”.
|
|
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).
|
|
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.
|
|
91
|
+
|
|
92
|
+
- **Failure semantics are underspecified**: tool failures show up in the worker UI, but our task store currently only supports `pending|in_progress|completed`.
|
|
93
|
+
- Update: `/team stop <name>` now sends a mailbox `abort_request`; workers treat aborts as aborts and reset the task back to `pending` (keeping the `owner`) instead of marking it `completed` with an empty result.
|
|
94
|
+
- Improvement idea: add `failed` status (and have workers write `metadata.failureReason` + include it in idle notifications), and only mark `completed` when we have an explicit success signal.
|
|
95
|
+
|
|
96
|
+
- **Worker shutdown + self-claim interaction**: when a worker receives SIGTERM it unassigns its non-completed tasks; other idle workers may immediately self-claim those now-unowned tasks.
|
|
97
|
+
- This is good for liveness, but surprising the first time you see task ownership “jump”.
|
|
98
|
+
|
|
99
|
+
- **Nice surprise: results are persisted with the task**: on completion, the worker writes `metadata.result` + `metadata.completedAt` into the task JSON file.
|
|
100
|
+
- This made it easy to recover outputs even after closing tmux windows.
|
|
101
|
+
|
|
102
|
+
## Next notes to capture
|
|
103
|
+
|
|
104
|
+
- How easy it is to recover after restarting the leader
|
|
105
|
+
- How often we hit file/lock contention
|
|
106
|
+
- Whether auto-claim behavior matches expectations in mixed assigned/unassigned task lists
|
|
107
|
+
- Whether worktree mode is essential in practice (and what breaks when no git repo exists)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# indus-swarms (pi-agent-teams extension) — Runtime Smoke Test Plan
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- `indusagi` CLI installed (`indusagi --version` → `0.12.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
|
+
| `indusagi` CLI | `indusagi --version` executes (skipped in CI if `indusagi` 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
|
+
indusagi --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
|
+
indusagi --no-extensions -e extensions/teams/index.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or, if the extension is symlinked into `~/.pi/agent/extensions/indus-swarms`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
indusagi # 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
|
+
If old/manual teammates still show as idle (stale config entries), prune them:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
/team prune
|
|
119
|
+
# or: /team prune --all
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 4. Worker-side Smoke (verifying child process)
|
|
123
|
+
|
|
124
|
+
To test the worker role directly:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
PI_TEAMS_WORKER=1 \
|
|
128
|
+
PI_TEAMS_TEAM_ID=test-team \
|
|
129
|
+
PI_TEAMS_AGENT_NAME=agent1 \
|
|
130
|
+
PI_TEAMS_LEAD_NAME=team-lead \
|
|
131
|
+
PI_TEAMS_STYLE=normal \
|
|
132
|
+
indusagi --no-extensions -e extensions/teams/index.ts --mode rpc
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Expected:** process starts in RPC mode, registers `team_message` tool, polls mailbox.
|
|
136
|
+
|
|
137
|
+
## 5. Checklist Summary
|
|
138
|
+
|
|
139
|
+
| # | Test | Method | Status |
|
|
140
|
+
|---|-------------------------------|------------|--------|
|
|
141
|
+
| 1 | Unit primitives (60 asserts) | Automated | ✅ |
|
|
142
|
+
| 2 | Extension loading | CLI | ✅ |
|
|
143
|
+
| 3 | Interactive spawn/task/dm | Manual | 📋 |
|
|
144
|
+
| 4 | Worker-side RPC | Manual | 📋 |
|
|
145
|
+
|
|
146
|
+
✅ = verified in this run, 📋 = documented for manual execution.
|
package/eslint.config.js
ADDED
|
@@ -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
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# pi-teams (extension)
|
|
2
|
+
|
|
3
|
+
This directory contains the **Teams** extension entrypoint:
|
|
4
|
+
|
|
5
|
+
- `index.ts` (leader/worker dispatch)
|
|
6
|
+
|
|
7
|
+
The full project README (usage, commands, tests) lives at the repo root:
|
|
8
|
+
|
|
9
|
+
- `../../README.md`
|
|
10
|
+
|
|
11
|
+
## Storage root override
|
|
12
|
+
|
|
13
|
+
By default, all Teams artifacts are stored under the Pi agent directory:
|
|
14
|
+
|
|
15
|
+
- `~/.pi/agent/teams/<teamId>/...`
|
|
16
|
+
|
|
17
|
+
For tests/CI (or if you want to keep Teams state separate), set:
|
|
18
|
+
|
|
19
|
+
- `PI_TEAMS_ROOT_DIR=/absolute/path`
|
|
20
|
+
|
|
21
|
+
Then the extension will store:
|
|
22
|
+
|
|
23
|
+
- `<PI_TEAMS_ROOT_DIR>/<teamId>/...`
|