@tmustier/pi-agent-teams 0.4.0-beta.3 → 0.5.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 (33) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +72 -9
  3. package/WORKFLOW.md +110 -0
  4. package/docs/claude-parity.md +18 -13
  5. package/docs/hook-contract.md +183 -0
  6. package/docs/smoke-test-plan.md +26 -7
  7. package/extensions/teams/activity-tracker.ts +296 -8
  8. package/extensions/teams/cleanup.ts +216 -3
  9. package/extensions/teams/hooks.ts +57 -5
  10. package/extensions/teams/leader-attach-commands.ts +8 -4
  11. package/extensions/teams/leader-inbox.ts +162 -4
  12. package/extensions/teams/leader-info-commands.ts +105 -3
  13. package/extensions/teams/leader-lifecycle-commands.ts +205 -3
  14. package/extensions/teams/leader-messaging-commands.ts +19 -7
  15. package/extensions/teams/leader-spawn-command.ts +5 -1
  16. package/extensions/teams/leader-team-command.ts +51 -2
  17. package/extensions/teams/leader-teams-tool.ts +387 -11
  18. package/extensions/teams/leader.ts +126 -52
  19. package/extensions/teams/mailbox.ts +6 -1
  20. package/extensions/teams/model-policy.ts +117 -0
  21. package/extensions/teams/spawn-types.ts +4 -0
  22. package/extensions/teams/teammate-rpc.ts +14 -0
  23. package/extensions/teams/teams-panel.ts +117 -19
  24. package/extensions/teams/teams-ui-shared.ts +205 -2
  25. package/extensions/teams/teams-widget.ts +67 -14
  26. package/extensions/teams/worker.ts +18 -6
  27. package/extensions/teams/worktree.ts +143 -0
  28. package/package.json +4 -2
  29. package/scripts/integration-cleanup-test.mts +419 -0
  30. package/scripts/integration-hooks-remediation-test.mts +382 -0
  31. package/scripts/integration-spawn-overrides-test.mts +10 -0
  32. package/scripts/smoke-test.mts +701 -3
  33. package/skills/agent-teams/SKILL.md +28 -7
@@ -73,10 +73,14 @@ export async function handleTeamDmCommand(opts: {
73
73
  }): Promise<void> {
74
74
  const { ctx, rest, teamId, leadName, style } = opts;
75
75
 
76
- const nameRaw = rest[0];
77
- const msg = rest.slice(1).join(" ").trim();
76
+ // Parse --urgent flag from anywhere in the args
77
+ const isUrgent = rest.includes("--urgent");
78
+ const filtered = rest.filter((a) => a !== "--urgent");
79
+
80
+ const nameRaw = filtered[0];
81
+ const msg = filtered.slice(1).join(" ").trim();
78
82
  if (!nameRaw || !msg) {
79
- ctx.ui.notify("Usage: /team dm <name> <msg...>", "error");
83
+ ctx.ui.notify("Usage: /team dm <name> [--urgent] <msg...>", "error");
80
84
  return;
81
85
  }
82
86
  const name = sanitizeName(nameRaw);
@@ -84,8 +88,10 @@ export async function handleTeamDmCommand(opts: {
84
88
  from: leadName,
85
89
  text: msg,
86
90
  timestamp: new Date().toISOString(),
91
+ ...(isUrgent ? { urgent: true } : {}),
87
92
  });
88
- ctx.ui.notify(`DM queued for ${formatMemberDisplayName(style, name)}`, "info");
93
+ const verb = isUrgent ? "Urgent DM" : "DM";
94
+ ctx.ui.notify(`${verb} queued for ${formatMemberDisplayName(style, name)}`, "info");
89
95
  }
90
96
 
91
97
  export async function handleTeamBroadcastCommand(opts: {
@@ -102,9 +108,13 @@ export async function handleTeamBroadcastCommand(opts: {
102
108
  const { ctx, rest, teamId, teammates, leadName, style, refreshTasks, getTasks, getTaskListId } = opts;
103
109
  const strings = getTeamsStrings(style);
104
110
 
105
- const msg = rest.join(" ").trim();
111
+ // Parse --urgent flag from anywhere in the args
112
+ const isUrgent = rest.includes("--urgent");
113
+ const filtered = rest.filter((a) => a !== "--urgent");
114
+
115
+ const msg = filtered.join(" ").trim();
106
116
  if (!msg) {
107
- ctx.ui.notify("Usage: /team broadcast <msg...>", "error");
117
+ ctx.ui.notify("Usage: /team broadcast [--urgent] <msg...>", "error");
108
118
  return;
109
119
  }
110
120
 
@@ -137,12 +147,14 @@ export async function handleTeamBroadcastCommand(opts: {
137
147
  from: leadName,
138
148
  text: msg,
139
149
  timestamp: ts,
150
+ ...(isUrgent ? { urgent: true } : {}),
140
151
  }),
141
152
  ),
142
153
  );
143
154
 
155
+ const verb = isUrgent ? "Urgent broadcast" : "Broadcast";
144
156
  ctx.ui.notify(
145
- `Broadcast queued for ${names.length} ${strings.memberTitle.toLowerCase()}(s): ${names.map((n) => formatMemberDisplayName(style, n)).join(", ")}`,
157
+ `${verb} queued for ${names.length} ${strings.memberTitle.toLowerCase()}(s): ${names.map((n) => formatMemberDisplayName(style, n)).join(", ")}`,
146
158
  "info",
147
159
  );
148
160
  }
@@ -142,8 +142,12 @@ export async function handleTeamSpawnCommand(opts: {
142
142
 
143
143
  for (const w of res.warnings) ctx.ui.notify(w, "warning");
144
144
  const displayName = formatMemberDisplayName(style, res.name);
145
+ const extras: string[] = [];
146
+ if (res.model) extras.push(res.model);
147
+ if (res.thinking) extras.push(`thinking:${res.thinking}`);
148
+ const extrasStr = extras.length > 0 ? ` \u00b7 ${extras.join(" \u00b7 ")}` : "";
145
149
  ctx.ui.notify(
146
- `${displayName} ${strings.joinedVerb} (${res.mode}${res.note ? ", " + res.note : ""} \u00b7 ${res.workspaceMode})`,
150
+ `${displayName} ${strings.joinedVerb} (${res.mode}${res.note ? ", " + res.note : ""} \u00b7 ${res.workspaceMode}${extrasStr})`,
147
151
  "info",
148
152
  );
149
153
  }
@@ -4,11 +4,14 @@ import {
4
4
  handleTeamEnvCommand,
5
5
  handleTeamIdCommand,
6
6
  handleTeamListCommand,
7
+ handleTeamStatusCommand,
7
8
  } from "./leader-info-commands.js";
8
9
  import { handleTeamAttachCommand, handleTeamDetachCommand } from "./leader-attach-commands.js";
9
10
  import {
10
11
  handleTeamCleanupCommand,
11
12
  handleTeamDelegateCommand,
13
+ handleTeamDoneCommand,
14
+ handleTeamGcCommand,
12
15
  handleTeamKillCommand,
13
16
  handleTeamPruneCommand,
14
17
  handleTeamShutdownCommand,
@@ -28,6 +31,7 @@ import type { SpawnTeammateFn } from "./spawn-types.js";
28
31
  import type { TeamConfig } from "./team-config.js";
29
32
  import type { TeamTask } from "./task-store.js";
30
33
  import type { TeammateRpc } from "./teammate-rpc.js";
34
+ import type { ActivityTracker } from "./activity-tracker.js";
31
35
  import type { TeamsStyle } from "./teams-style.js";
32
36
 
33
37
  const TEAM_HELP_TEXT = [
@@ -38,10 +42,11 @@ const TEAM_HELP_TEXT = [
38
42
  " /team attach <teamId> [--claim]",
39
43
  " /team detach",
40
44
  " /team spawn <name> [fresh|branch] [shared|worktree] [plan] [--model <provider>/<modelId>] [--thinking <level>]",
45
+ " /team status [name] # real-time worker state (stall detection, time in state, activity)",
41
46
  " /team panel",
42
47
  " /team send <name> <msg...>",
43
- " /team dm <name> <msg...>",
44
- " /team broadcast <msg...>",
48
+ " /team dm <name> [--urgent] <msg...>",
49
+ " /team broadcast [--urgent] <msg...>",
45
50
  " /team steer <name> <msg...>",
46
51
  " /team stop <name> [reason...]",
47
52
  " /team kill <name>",
@@ -54,7 +59,9 @@ const TEAM_HELP_TEXT = [
54
59
  " /team style init <name> [extends <base>]",
55
60
  " /team plan approve <name>",
56
61
  " /team plan reject <name> [feedback...]",
62
+ " /team done [--force] # end run: stop teammates + hide widget",
57
63
  " /team cleanup [--force]",
64
+ " /team gc [--dry-run] [--force] [--max-age-hours=N] # remove old team dirs",
58
65
  " /team prune [--all] # hide stale manual teammates (mark offline)",
59
66
  " /team task add <text...>",
60
67
  " /team task assign <id> <agent>",
@@ -77,9 +84,12 @@ export async function handleTeamCommand(opts: {
77
84
  ctx: ExtensionCommandContext;
78
85
  teammates: Map<string, TeammateRpc>;
79
86
  getTeamConfig: () => TeamConfig | null;
87
+ getTracker: () => ActivityTracker;
80
88
  getTasks: () => TeamTask[];
81
89
  refreshTasks: () => Promise<void>;
82
90
  renderWidget: () => void;
91
+ hideWidget: () => void;
92
+ restoreWidget: () => void;
83
93
  getTaskListId: () => string | null;
84
94
  setTaskListId: (id: string) => void;
85
95
  getActiveTeamId: () => string;
@@ -101,9 +111,12 @@ export async function handleTeamCommand(opts: {
101
111
  ctx,
102
112
  teammates,
103
113
  getTeamConfig,
114
+ getTracker,
104
115
  getTasks,
105
116
  refreshTasks,
106
117
  renderWidget,
118
+ hideWidget,
119
+ restoreWidget,
107
120
  getTaskListId,
108
121
  setTaskListId,
109
122
  getActiveTeamId,
@@ -140,6 +153,7 @@ export async function handleTeamCommand(opts: {
140
153
  ctx,
141
154
  teammates,
142
155
  getTeamConfig,
156
+ getTracker,
143
157
  style,
144
158
  refreshTasks,
145
159
  renderWidget,
@@ -181,6 +195,7 @@ export async function handleTeamCommand(opts: {
181
195
  setTaskListId,
182
196
  refreshTasks,
183
197
  renderWidget,
198
+ restoreWidget,
184
199
  });
185
200
  },
186
201
 
@@ -194,6 +209,23 @@ export async function handleTeamCommand(opts: {
194
209
  setTaskListId,
195
210
  refreshTasks,
196
211
  renderWidget,
212
+ restoreWidget,
213
+ });
214
+ },
215
+
216
+ done: async () => {
217
+ await handleTeamDoneCommand({
218
+ ctx,
219
+ rest,
220
+ teamId: activeTeamId,
221
+ teammates,
222
+ getTeamConfig,
223
+ leadName,
224
+ style,
225
+ stopAllTeammates,
226
+ refreshTasks,
227
+ getTasks,
228
+ hideWidget,
197
229
  });
198
230
  },
199
231
 
@@ -210,6 +242,10 @@ export async function handleTeamCommand(opts: {
210
242
  });
211
243
  },
212
244
 
245
+ gc: async () => {
246
+ await handleTeamGcCommand({ ctx, rest });
247
+ },
248
+
213
249
  prune: async () => {
214
250
  await handleTeamPruneCommand({
215
251
  ctx,
@@ -256,6 +292,19 @@ export async function handleTeamCommand(opts: {
256
292
  await handleTeamSpawnCommand({ ctx, rest, teammates, style, spawnTeammate });
257
293
  },
258
294
 
295
+ status: async () => {
296
+ await handleTeamStatusCommand({
297
+ ctx,
298
+ rest,
299
+ teammates,
300
+ getTeamConfig,
301
+ getTracker,
302
+ teamId: activeTeamId,
303
+ taskListId,
304
+ style,
305
+ });
306
+ },
307
+
259
308
  style: async () => {
260
309
  const teamDir = getTeamDir(activeTeamId);
261
310
  await handleTeamStyleCommand({