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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Start a Pi Teams leader + N worker sessions inside tmux.
|
|
5
|
+
#
|
|
6
|
+
# This is primarily a dogfooding helper so we can run a "Claude-like" split-pane
|
|
7
|
+
# setup (leader + interactive workers) while still using the same filesystem
|
|
8
|
+
# primitives (task list + mailboxes).
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ./scripts/start-tmux-team.sh [session-name] [worker1 worker2 ...]
|
|
12
|
+
#
|
|
13
|
+
# Example:
|
|
14
|
+
# ./scripts/start-tmux-team.sh pi-teams alice bob carol
|
|
15
|
+
|
|
16
|
+
SESSION_NAME=${1:-pi-teams}
|
|
17
|
+
shift || true
|
|
18
|
+
|
|
19
|
+
WORKERS=("$@")
|
|
20
|
+
if [[ ${#WORKERS[@]} -eq 0 ]]; then
|
|
21
|
+
WORKERS=("alice" "bob")
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
25
|
+
REPO_DIR=$(cd -- "${SCRIPT_DIR}/.." && pwd)
|
|
26
|
+
EXT_ENTRY="${REPO_DIR}/extensions/teams/index.ts"
|
|
27
|
+
|
|
28
|
+
if [[ ! -f "${EXT_ENTRY}" ]]; then
|
|
29
|
+
echo "ERROR: extension entry not found: ${EXT_ENTRY}" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Use a fresh temp root per run unless the caller provided one.
|
|
34
|
+
TEAMS_ROOT=${PI_TEAMS_ROOT_DIR:-"/tmp/pi-teams-$(date +%Y%m%d-%H%M%S)"}
|
|
35
|
+
mkdir -p "${TEAMS_ROOT}"
|
|
36
|
+
|
|
37
|
+
# If the session already exists, refuse (avoid clobbering a running team).
|
|
38
|
+
if tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
|
|
39
|
+
echo "ERROR: tmux session already exists: ${SESSION_NAME}" >&2
|
|
40
|
+
echo "Attach with: tmux attach -t ${SESSION_NAME}" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
echo "Starting leader..."
|
|
45
|
+
# Leader (interactive)
|
|
46
|
+
tmux new-session -d -s "${SESSION_NAME}" -c "${REPO_DIR}" \
|
|
47
|
+
"PI_TEAMS_ROOT_DIR=${TEAMS_ROOT} indusagi -e ${EXT_ENTRY}"
|
|
48
|
+
|
|
49
|
+
# Wait for the leader to create the team directory.
|
|
50
|
+
TEAM_ID=""
|
|
51
|
+
for _ in {1..80}; do
|
|
52
|
+
# First directory directly under TEAMS_ROOT.
|
|
53
|
+
TEAM_DIR=$(find "${TEAMS_ROOT}" -mindepth 1 -maxdepth 1 -type d -print -quit 2>/dev/null || true)
|
|
54
|
+
if [[ -n "${TEAM_DIR}" ]]; then
|
|
55
|
+
TEAM_ID=$(basename "${TEAM_DIR}")
|
|
56
|
+
break
|
|
57
|
+
fi
|
|
58
|
+
sleep 0.25
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
if [[ -z "${TEAM_ID}" ]]; then
|
|
62
|
+
echo "ERROR: timed out waiting for team directory under ${TEAMS_ROOT}" >&2
|
|
63
|
+
echo "Check leader pane: tmux attach -t ${SESSION_NAME}" >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
echo "TeamId: ${TEAM_ID}"
|
|
68
|
+
|
|
69
|
+
echo "Starting workers: ${WORKERS[*]}"
|
|
70
|
+
for name in "${WORKERS[@]}"; do
|
|
71
|
+
# Each worker is an interactive indusagi session running the extension in worker mode.
|
|
72
|
+
tmux new-window -t "${SESSION_NAME}" -n "${name}" -c "${REPO_DIR}" \
|
|
73
|
+
"PI_TEAMS_ROOT_DIR=${TEAMS_ROOT} PI_TEAMS_WORKER=1 PI_TEAMS_TEAM_ID=${TEAM_ID} PI_TEAMS_AGENT_NAME=${name} indusagi -e ${EXT_ENTRY}"
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
cat <<EOF
|
|
77
|
+
|
|
78
|
+
OK
|
|
79
|
+
|
|
80
|
+
tmux session: ${SESSION_NAME}
|
|
81
|
+
teams root: ${TEAMS_ROOT}
|
|
82
|
+
team id: ${TEAM_ID}
|
|
83
|
+
|
|
84
|
+
Attach:
|
|
85
|
+
tmux attach -t ${SESSION_NAME}
|
|
86
|
+
|
|
87
|
+
In the leader pane, try:
|
|
88
|
+
/team help
|
|
89
|
+
/team task add ${WORKERS[0]}: say hello
|
|
90
|
+
/team task list
|
|
91
|
+
EOF
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-teams
|
|
3
|
+
description: "Coordinate multi-agent teamwork with shared task lists, mailbox messaging, and long-lived teammates. Use when the user asks to spawn workers, delegate tasks, work in parallel with agents, or manage a team of workers via the indus-swarms extension."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Agent Teams
|
|
7
|
+
|
|
8
|
+
Spawn and coordinate teammate agents that work in parallel on shared task lists, communicating via file-based mailboxes. Modeled after Claude Code Agent Teams.
|
|
9
|
+
|
|
10
|
+
## Core concepts
|
|
11
|
+
|
|
12
|
+
- **Leader** (you): orchestrates, delegates, reviews. Runs the `/team` command and the `teams` LLM tool.
|
|
13
|
+
- **Teammates**: child indus processes that poll for tasks, execute them, and report back. Sessions are named `indus-swarms - <role> <name>` where `<role>` depends on the current style (e.g. teammate/comrade/matey).
|
|
14
|
+
- **Task list**: file-per-task store with statuses (pending/in_progress/completed), owners, and dependency tracking.
|
|
15
|
+
- **Mailbox**: file-based message queue. Two namespaces: `team` (DMs, notifications, shutdown) and `taskListId` (task assignments).
|
|
16
|
+
|
|
17
|
+
## UI style (terminology + naming)
|
|
18
|
+
|
|
19
|
+
Built-in styles:
|
|
20
|
+
- `normal` (default): Team leader + Teammate <name>
|
|
21
|
+
- `soviet`: Chairman + Comrade <name>
|
|
22
|
+
- `pirate`: Captain + Matey <name>
|
|
23
|
+
|
|
24
|
+
Configure via `INDUS_TEAMS_STYLE=<name>` or `/team style <name>` (see `/team style list`).
|
|
25
|
+
|
|
26
|
+
Custom styles reside under `~/.indusagi/agent/teams/_styles/<style>.json` and can also be bootstrapped with:
|
|
27
|
+
|
|
28
|
+
- `/team style init <name> [extends <base>]`
|
|
29
|
+
|
|
30
|
+
## Spawning teammates
|
|
31
|
+
|
|
32
|
+
Use the **`teams` tool** (LLM-callable) for delegation, task/messaging mutations, lifecycle, and governance:
|
|
33
|
+
|
|
34
|
+
| Action | Required fields | Notes |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| `delegate` | `tasks` | Spawns as needed, creates and assigns tasks. |
|
|
37
|
+
| `task_assign` | `taskId`, `assignee` | Assign/reassign owner. |
|
|
38
|
+
| `task_unassign` | `taskId` | Clear owner. |
|
|
39
|
+
| `task_set_status` | `taskId`, `status` | `pending` \| `in_progress` \| `completed`. |
|
|
40
|
+
| `task_dep_add` / `task_dep_rm` | `taskId`, `depId` | Dependency graph edits. |
|
|
41
|
+
| `task_dep_ls` | `taskId` | Dependency/block inspection. |
|
|
42
|
+
| `message_dm` | `name`, `message` | Mailbox DM. |
|
|
43
|
+
| `message_broadcast` | `message` | Mailbox broadcast. |
|
|
44
|
+
| `message_steer` | `name`, `message` | RPC steer for running teammate. |
|
|
45
|
+
| `member_spawn` | `name` | Supports context/workspace/model/thinking/plan options. |
|
|
46
|
+
| `member_shutdown` | `name` or `all=true` | Graceful mailbox shutdown request. |
|
|
47
|
+
| `member_kill` | `name` | Force-stop RPC teammate. |
|
|
48
|
+
| `member_prune` | _(none)_ | Mark stale workers offline (`all=true` to force). |
|
|
49
|
+
| `plan_approve` / `plan_reject` | `name` | Resolve pending plan approvals (`feedback` optional for reject). |
|
|
50
|
+
| `hooks_policy_get` | _(none)_ | Read team hooks policy (configured + effective). |
|
|
51
|
+
| `hooks_policy_set` | one or more: `hookFailureAction`, `hookMaxReopensPerTask`, `hookFollowupOwner` | Update team hooks policy at runtime (`hooksPolicyReset=true` clears team overrides first). |
|
|
52
|
+
| `model_policy_get` | _(none)_ | Inspect teammate model policy and current leader inheritance behavior. |
|
|
53
|
+
| `model_policy_check` | optional `model` | Validate a model override before spawn (`<provider>/<modelId>` or `<modelId>`). |
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
teams({ action: "delegate", tasks: [{ text: "Implement auth", assignee: "alice" }] })
|
|
59
|
+
teams({ action: "task_assign", taskId: "12", assignee: "alice" })
|
|
60
|
+
teams({ action: "task_dep_add", taskId: "12", depId: "7" })
|
|
61
|
+
teams({ action: "message_broadcast", message: "Sync: finishing this milestone" })
|
|
62
|
+
teams({ action: "member_kill", name: "alice" })
|
|
63
|
+
teams({ action: "plan_reject", name: "alice", feedback: "Include rollback strategy" })
|
|
64
|
+
teams({ action: "hooks_policy_get" })
|
|
65
|
+
teams({ action: "hooks_policy_set", hookFailureAction: "reopen_followup", hookMaxReopensPerTask: 2, hookFollowupOwner: "member" })
|
|
66
|
+
teams({ action: "model_policy_get" })
|
|
67
|
+
teams({ action: "model_policy_check", model: "openai-codex/gpt-5.1-codex-mini" })
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This covers most day-to-day orchestration without slash commands. For nuanced/manual control, use `/team ...` commands directly.
|
|
71
|
+
|
|
72
|
+
For more control, use `/team spawn`:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
/team spawn alice # default: fresh context, shared workspace
|
|
76
|
+
/team spawn bob branch shared # clone leader session context
|
|
77
|
+
/team spawn carol fresh worktree # git worktree isolation
|
|
78
|
+
/team spawn dave plan # plan-required mode (read-only until approved)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Task management
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
/team task add <text...> # create a task
|
|
85
|
+
/team task add alice: review the API # create + assign (prefix with name:)
|
|
86
|
+
/team task assign <id> <agent> # assign existing task
|
|
87
|
+
/team task unassign <id> # unassign
|
|
88
|
+
/team task list # show all tasks with status + deps
|
|
89
|
+
/team task show <id> # full task details + result
|
|
90
|
+
/team task dep add <id> <depId> # task depends on depId
|
|
91
|
+
/team task dep rm <id> <depId> # remove dependency
|
|
92
|
+
/team task dep ls <id> # show dependency graph
|
|
93
|
+
/team task clear [completed|all] # delete tasks
|
|
94
|
+
/team task use <taskListId> # switch to a different task list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Teammates auto-claim unassigned, unblocked tasks by default.
|
|
98
|
+
|
|
99
|
+
## Communication
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
/team dm <name> <msg...> # direct message to one teammate
|
|
103
|
+
/team broadcast <msg...> # message all teammates
|
|
104
|
+
/team send <name> <msg...> # RPC-based (immediate, for spawned teammates)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Teammates can also message each other directly via the `team_message` tool, with the leader CC'd.
|
|
108
|
+
|
|
109
|
+
## Governance modes
|
|
110
|
+
|
|
111
|
+
### Delegate mode
|
|
112
|
+
|
|
113
|
+
Restricts the leader to coordination-only (blocks bash/edit/write tools). Use when you want to force all implementation through teammates.
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
/team delegate on # enable
|
|
117
|
+
/team delegate off # disable
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Plan approval
|
|
121
|
+
|
|
122
|
+
Spawning with `plan` restricts the teammate to read-only tools. After producing a plan, the teammate submits it for leader approval before proceeding.
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
/team spawn alice plan # spawn in plan-required mode
|
|
126
|
+
/team plan approve alice # approve plan, teammate gets full tools
|
|
127
|
+
/team plan reject alice <feedback...> # reject, teammate revises
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Lifecycle
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
/team panel # interactive overlay with teammate details
|
|
134
|
+
/team list # show teammates and their state
|
|
135
|
+
/team attach list # discover existing teams under <teamsRoot>
|
|
136
|
+
/team attach <teamId> [--claim] # attach this session to an existing team workspace (force takeover with --claim)
|
|
137
|
+
/team detach # return to this session's own team workspace
|
|
138
|
+
/team shutdown # stop all teammates (RPC + best-effort manual) (leader session remains active)
|
|
139
|
+
/team shutdown <name> # graceful shutdown (teammate can reject if busy)
|
|
140
|
+
/team prune [--all] # hide stale manual teammates (mark offline in config)
|
|
141
|
+
/team kill <name> # force-terminate one RPC teammate
|
|
142
|
+
/team cleanup [--force] # delete team directory after all teammates stopped
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Teammates reject shutdown requests when they have an active task. Use `/team kill <name>` to force.
|
|
146
|
+
|
|
147
|
+
## Other commands
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
/team id # show team ID, task list ID, paths
|
|
151
|
+
/team env <n> # print env vars for manually spawning a teammate named <n>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Shared task list across sessions
|
|
155
|
+
|
|
156
|
+
Set `INDUS_TEAMS_TASK_LIST_ID` whenever you start a teammate manually or launch an indus-swarms worker via `indusagi --mode rpc`. New teammates inherit this task list, and you can switch leaders using `/team task use <taskListId>` to persist the selection.
|
|
157
|
+
|
|
158
|
+
The leader switches task lists via:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
/team task use my-persistent-list
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The chosen task list ID is persisted in `config.json`. Teammates spawned after the switch inherit the new task list ID; existing teammates need a restart to pick up changes.
|
|
165
|
+
|
|
166
|
+
## Message protocol
|
|
167
|
+
|
|
168
|
+
Teammates and the leader communicate via JSON messages with a `type` field:
|
|
169
|
+
|
|
170
|
+
| Type | Direction | Purpose |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `task_assignment` | leader -> teammate | Notify of assigned task |
|
|
173
|
+
| `idle_notification` | teammate -> leader | Teammate finished, no more work |
|
|
174
|
+
| `shutdown_request` | leader -> teammate | Ask to shut down |
|
|
175
|
+
| `shutdown_approved` | teammate -> leader | Will shut down |
|
|
176
|
+
| `shutdown_rejected` | teammate -> leader | Busy, can't shut down now |
|
|
177
|
+
| `plan_approval_request` | teammate -> leader | Plan ready for review |
|
|
178
|
+
| `plan_approved` | leader -> teammate | Proceed with implementation |
|
|
179
|
+
| `plan_rejected` | leader -> teammate | Revise plan (includes feedback) |
|
|
180
|
+
| `peer_dm_sent` | teammate -> leader | CC notification of peer message |
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022"],
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noImplicitAny": true,
|
|
11
|
+
"useUnknownInCatchVariables": true,
|
|
12
|
+
"noUncheckedIndexedAccess": true,
|
|
13
|
+
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
|
|
18
|
+
"noEmit": true
|
|
19
|
+
},
|
|
20
|
+
"include": ["extensions/**/*.ts", "scripts/**/*.ts", "scripts/**/*.mts"],
|
|
21
|
+
"exclude": ["node_modules", "dist", "build", "coverage"]
|
|
22
|
+
}
|