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.
Files changed (52) hide show
  1. package/AGENTS.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +119 -0
  4. package/docs/claude-parity.md +151 -0
  5. package/docs/field-notes-teams-setup.md +107 -0
  6. package/docs/smoke-test-plan.md +146 -0
  7. package/eslint.config.js +74 -0
  8. package/extensions/teams/README.md +23 -0
  9. package/extensions/teams/activity-tracker.ts +234 -0
  10. package/extensions/teams/cleanup.ts +31 -0
  11. package/extensions/teams/fs-lock.ts +87 -0
  12. package/extensions/teams/hooks.ts +363 -0
  13. package/extensions/teams/index.ts +18 -0
  14. package/extensions/teams/leader-attach-commands.ts +221 -0
  15. package/extensions/teams/leader-inbox.ts +214 -0
  16. package/extensions/teams/leader-info-commands.ts +140 -0
  17. package/extensions/teams/leader-lifecycle-commands.ts +559 -0
  18. package/extensions/teams/leader-messaging-commands.ts +148 -0
  19. package/extensions/teams/leader-plan-commands.ts +95 -0
  20. package/extensions/teams/leader-spawn-command.ts +149 -0
  21. package/extensions/teams/leader-task-commands.ts +435 -0
  22. package/extensions/teams/leader-team-command.ts +382 -0
  23. package/extensions/teams/leader-teams-tool.ts +1075 -0
  24. package/extensions/teams/leader.ts +925 -0
  25. package/extensions/teams/mailbox.ts +131 -0
  26. package/extensions/teams/model-policy.ts +142 -0
  27. package/extensions/teams/names.ts +121 -0
  28. package/extensions/teams/paths.ts +37 -0
  29. package/extensions/teams/protocol.ts +241 -0
  30. package/extensions/teams/spawn-types.ts +36 -0
  31. package/extensions/teams/task-store.ts +544 -0
  32. package/extensions/teams/team-attach-claim.ts +205 -0
  33. package/extensions/teams/team-config.ts +335 -0
  34. package/extensions/teams/team-discovery.ts +59 -0
  35. package/extensions/teams/teammate-rpc.ts +261 -0
  36. package/extensions/teams/teams-panel.ts +1186 -0
  37. package/extensions/teams/teams-style.ts +322 -0
  38. package/extensions/teams/teams-ui-shared.ts +89 -0
  39. package/extensions/teams/teams-widget.ts +212 -0
  40. package/extensions/teams/worker.ts +605 -0
  41. package/extensions/teams/worktree.ts +103 -0
  42. package/package.json +53 -0
  43. package/scripts/e2e-rpc-test.mjs +277 -0
  44. package/scripts/integration-claim-test.mts +157 -0
  45. package/scripts/integration-hooks-remediation-test.mts +382 -0
  46. package/scripts/integration-spawn-overrides-test.mts +398 -0
  47. package/scripts/integration-todo-test.mts +533 -0
  48. package/scripts/lib/pi-workers.ts +105 -0
  49. package/scripts/smoke-test.mts +764 -0
  50. package/scripts/start-tmux-team.sh +91 -0
  51. package/skills/agent-teams/SKILL.md +180 -0
  52. 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
+ }