pi-extended-teams 2.1.16

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 (130) hide show
  1. package/README.md +105 -0
  2. package/assets/pi-extended-teams-agent-navigation.png +0 -0
  3. package/assets/pi-extended-teams-in-action.png +0 -0
  4. package/extensions/agents/read-agent-report.ts +181 -0
  5. package/extensions/agents/read-agent-session-lifecycle.test.ts +605 -0
  6. package/extensions/agents/read-agent-session-lifecycle.ts +676 -0
  7. package/extensions/agents/read-agent.test.ts +3077 -0
  8. package/extensions/agents/read-agent.ts +1119 -0
  9. package/extensions/agents/write-agent.test.ts +513 -0
  10. package/extensions/agents/write-agent.ts +392 -0
  11. package/extensions/events/register-events.test.ts +465 -0
  12. package/extensions/events/register-events.ts +409 -0
  13. package/extensions/index.test.ts +1659 -0
  14. package/extensions/index.ts +1228 -0
  15. package/extensions/internal/agent-session-files.test.ts +164 -0
  16. package/extensions/internal/agent-session-files.ts +320 -0
  17. package/extensions/internal/debug.ts +44 -0
  18. package/extensions/internal/model-selection.ts +82 -0
  19. package/extensions/internal/pi-command.test.ts +191 -0
  20. package/extensions/internal/pi-command.ts +224 -0
  21. package/extensions/internal/pi-runtime-api.test.ts +43 -0
  22. package/extensions/internal/pi-runtime-api.ts +78 -0
  23. package/extensions/internal/schema.ts +19 -0
  24. package/extensions/internal/session-context-reference.test.ts +290 -0
  25. package/extensions/internal/session-context-reference.ts +430 -0
  26. package/extensions/internal/session-files.test.ts +225 -0
  27. package/extensions/internal/session-files.ts +273 -0
  28. package/extensions/internal/session-usage.ts +59 -0
  29. package/extensions/resources/spawn-resource-plan.test.ts +233 -0
  30. package/extensions/resources/spawn-resource-plan.ts +247 -0
  31. package/extensions/runtime/active-agent-sleep.test.ts +157 -0
  32. package/extensions/runtime/active-agent-sleep.ts +117 -0
  33. package/extensions/runtime/nested-read-agents.ts +22 -0
  34. package/extensions/runtime/pending-child-controller.test.ts +169 -0
  35. package/extensions/runtime/pending-child-controller.ts +280 -0
  36. package/extensions/runtime/types.ts +68 -0
  37. package/extensions/team/contracts.test.ts +111 -0
  38. package/extensions/team/lifecycle.test.ts +1095 -0
  39. package/extensions/team/lifecycle.ts +502 -0
  40. package/extensions/team/recipient-closure.test.ts +97 -0
  41. package/extensions/team/recipient-closure.ts +120 -0
  42. package/extensions/team/roster.test.ts +129 -0
  43. package/extensions/team/roster.ts +165 -0
  44. package/extensions/team/team-contracts.json +43 -0
  45. package/extensions/team/writer-screens.test.ts +50 -0
  46. package/extensions/team/writer-screens.ts +156 -0
  47. package/extensions/tools/agent-communication-tools.test.ts +306 -0
  48. package/extensions/tools/agent-communication-tools.ts +183 -0
  49. package/extensions/tools/coordination-tools.test.ts +670 -0
  50. package/extensions/tools/coordination-tools.ts +304 -0
  51. package/extensions/tools/delegation-guard.test.ts +95 -0
  52. package/extensions/tools/delegation-guard.ts +65 -0
  53. package/extensions/tools/file-claim-tools.test.ts +104 -0
  54. package/extensions/tools/file-claim-tools.ts +68 -0
  55. package/extensions/tools/model-tools.ts +53 -0
  56. package/extensions/tools/predefined-tools.test.ts +647 -0
  57. package/extensions/tools/predefined-tools.ts +331 -0
  58. package/extensions/tools/read-helper.test.ts +35 -0
  59. package/extensions/tools/task-runtime-tools.test.ts +334 -0
  60. package/extensions/tools/task-runtime-tools.ts +227 -0
  61. package/extensions/tools/team-tools.read-agent.test.ts +1924 -0
  62. package/extensions/tools/team-tools.ts +1270 -0
  63. package/extensions/ui/agent-follow-view.test.ts +520 -0
  64. package/extensions/ui/agent-follow-view.ts +779 -0
  65. package/extensions/ui/agent-navigation.test.ts +52 -0
  66. package/extensions/ui/agent-navigation.ts +55 -0
  67. package/extensions/ui/ansi.ts +16 -0
  68. package/extensions/ui/extensions-command.test.ts +336 -0
  69. package/extensions/ui/extensions-command.ts +282 -0
  70. package/extensions/ui/favorite-models-command.test.ts +280 -0
  71. package/extensions/ui/favorite-models-command.ts +423 -0
  72. package/extensions/ui/frame.ts +77 -0
  73. package/extensions/ui/input.ts +17 -0
  74. package/extensions/ui/read-agent-status.test.ts +117 -0
  75. package/extensions/ui/read-agent-status.ts +125 -0
  76. package/extensions/ui/renderers.ts +243 -0
  77. package/extensions/ui/status-widget.test.ts +143 -0
  78. package/extensions/ui/status-widget.ts +440 -0
  79. package/extensions/ui-frame.test.ts +69 -0
  80. package/package.json +75 -0
  81. package/skills/teams.md +256 -0
  82. package/src/adapters/terminal-registry.ts +78 -0
  83. package/src/adapters/tmux-adapter.test.ts +276 -0
  84. package/src/adapters/tmux-adapter.ts +200 -0
  85. package/src/orchestration/index.ts +437 -0
  86. package/src/orchestration/orchestrator.test.ts +396 -0
  87. package/src/orchestration/types.ts +125 -0
  88. package/src/utils/atomic-json.ts +21 -0
  89. package/src/utils/claims.test.ts +137 -0
  90. package/src/utils/claims.ts +169 -0
  91. package/src/utils/hooks.test.ts +75 -0
  92. package/src/utils/hooks.ts +35 -0
  93. package/src/utils/lifecycle-tombstone.test.ts +105 -0
  94. package/src/utils/lifecycle-tombstone.ts +268 -0
  95. package/src/utils/lock.race.child.ts +44 -0
  96. package/src/utils/lock.race.test.ts +198 -0
  97. package/src/utils/lock.test.ts +90 -0
  98. package/src/utils/lock.ts +186 -0
  99. package/src/utils/messaging.test.ts +337 -0
  100. package/src/utils/messaging.ts +441 -0
  101. package/src/utils/model-resolution.test.ts +231 -0
  102. package/src/utils/model-resolution.ts +322 -0
  103. package/src/utils/models.test.ts +8 -0
  104. package/src/utils/models.ts +115 -0
  105. package/src/utils/paths.ts +81 -0
  106. package/src/utils/predefined-teams/types.ts +48 -0
  107. package/src/utils/predefined-teams.save-template.test.ts +74 -0
  108. package/src/utils/predefined-teams.test.ts +441 -0
  109. package/src/utils/predefined-teams.ts +471 -0
  110. package/src/utils/read-helper-queue.ts +99 -0
  111. package/src/utils/report-events.test.ts +154 -0
  112. package/src/utils/report-events.ts +222 -0
  113. package/src/utils/runtime.test.ts +314 -0
  114. package/src/utils/runtime.ts +261 -0
  115. package/src/utils/security.test.ts +43 -0
  116. package/src/utils/settings.test.ts +480 -0
  117. package/src/utils/settings.ts +645 -0
  118. package/src/utils/shared-memory.test.ts +80 -0
  119. package/src/utils/shared-memory.ts +81 -0
  120. package/src/utils/tasks.race.test.ts +44 -0
  121. package/src/utils/tasks.test.ts +229 -0
  122. package/src/utils/tasks.ts +396 -0
  123. package/src/utils/teams.ts +231 -0
  124. package/src/utils/terminal-adapter.ts +103 -0
  125. package/src/utils/thinking-levels.test.ts +56 -0
  126. package/src/utils/thinking-levels.ts +47 -0
  127. package/src/utils/workflow-metadata.test.ts +11 -0
  128. package/src/utils/workflow-metadata.ts +88 -0
  129. package/src/utils/write-queue.test.ts +251 -0
  130. package/src/utils/write-queue.ts +202 -0
@@ -0,0 +1,441 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import {
6
+ parseAgentFrontmatter,
7
+ parseTeamsYaml,
8
+ discoverAgents,
9
+ discoverTeams,
10
+ getAllAgentDefinitions,
11
+ getAllPredefinedTeams,
12
+ getAgentDefinition,
13
+ getPredefinedTeam,
14
+ } from "./predefined-teams";
15
+
16
+ describe("parseAgentFrontmatter", () => {
17
+ it("parses a valid agent definition with all fields", () => {
18
+ const content = `---
19
+ name: scout
20
+ description: Fast recon and codebase exploration
21
+ tools: read,grep,find,ls
22
+ model: claude-sonnet-4
23
+ thinking: high
24
+ ---
25
+ You are a scout agent. Investigate the codebase quickly.`;
26
+
27
+ const result = parseAgentFrontmatter(content, "/test/scout.md");
28
+
29
+ expect(result).not.toBeNull();
30
+ expect(result?.name).toBe("scout");
31
+ expect(result?.description).toBe("Fast recon and codebase exploration");
32
+ expect(result?.tools).toEqual(["read", "grep", "find", "ls"]);
33
+ expect(result?.model).toBe("claude-sonnet-4");
34
+ expect(result?.thinking).toBe("high");
35
+ expect(result?.prompt).toBe("You are a scout agent. Investigate the codebase quickly.");
36
+ expect(result?.filePath).toBe("/test/scout.md");
37
+ });
38
+
39
+ it("parses an agent definition with space-separated tools", () => {
40
+ const content = `---
41
+ name: builder
42
+ description: Code builder
43
+ tools: read write edit bash
44
+ ---
45
+ You build things.`;
46
+
47
+ const result = parseAgentFrontmatter(content, "/test/builder.md");
48
+
49
+ expect(result).not.toBeNull();
50
+ expect(result?.tools).toEqual(["read", "write", "edit", "bash"]);
51
+ });
52
+
53
+ it("parses an agent definition without optional fields", () => {
54
+ const content = `---
55
+ name: simple
56
+ description: Simple agent
57
+ ---
58
+ You are simple.`;
59
+
60
+ const result = parseAgentFrontmatter(content, "/test/simple.md");
61
+
62
+ expect(result).not.toBeNull();
63
+ expect(result?.name).toBe("simple");
64
+ expect(result?.description).toBe("Simple agent");
65
+ expect(result?.tools).toBeUndefined();
66
+ expect(result?.model).toBeUndefined();
67
+ expect(result?.thinking).toBeUndefined();
68
+ expect(result?.prompt).toBe("You are simple.");
69
+ });
70
+
71
+ it("returns null for content without frontmatter", () => {
72
+ const content = "This is just regular markdown without frontmatter.";
73
+ const result = parseAgentFrontmatter(content, "/test/no-frontmatter.md");
74
+ expect(result).toBeNull();
75
+ });
76
+
77
+ it("returns null for frontmatter without name", () => {
78
+ const content = `---
79
+ description: Missing name field
80
+ ---
81
+ Some prompt`;
82
+ const result = parseAgentFrontmatter(content, "/test/no-name.md");
83
+ expect(result).toBeNull();
84
+ });
85
+ });
86
+
87
+ describe("parseTeamsYaml", () => {
88
+ it("parses a valid teams.yaml content", () => {
89
+ const content = `
90
+ full:
91
+ - scout
92
+ - planner
93
+ - builder
94
+
95
+ plan-build:
96
+ - planner
97
+ - builder
98
+ - reviewer
99
+ `;
100
+
101
+ const result = parseTeamsYaml(content);
102
+
103
+ expect(result).toHaveLength(2);
104
+ expect(result[0].name).toBe("full");
105
+ expect(result[0].agents).toEqual(["scout", "planner", "builder"]);
106
+ expect(result[1].name).toBe("plan-build");
107
+ expect(result[1].agents).toEqual(["planner", "builder", "reviewer"]);
108
+ });
109
+
110
+ it("handles comments and empty lines", () => {
111
+ const content = `
112
+ # This is a comment
113
+ full:
114
+ - scout
115
+ # Another comment
116
+ - planner
117
+
118
+ # Empty line above
119
+ minimal:
120
+ - scout
121
+ `;
122
+
123
+ const result = parseTeamsYaml(content);
124
+
125
+ expect(result).toHaveLength(2);
126
+ expect(result[0].agents).toEqual(["scout", "planner"]);
127
+ expect(result[1].agents).toEqual(["scout"]);
128
+ });
129
+
130
+ it("returns empty array for empty content", () => {
131
+ expect(parseTeamsYaml("")).toEqual([]);
132
+ expect(parseTeamsYaml("# Just comments\n\n")).toEqual([]);
133
+ });
134
+
135
+ it("handles tab indentation", () => {
136
+ const content = `
137
+ full:
138
+ \t- scout
139
+ \t- planner
140
+ `;
141
+
142
+ const result = parseTeamsYaml(content);
143
+
144
+ expect(result).toHaveLength(1);
145
+ expect(result[0].agents).toEqual(["scout", "planner"]);
146
+ });
147
+ });
148
+
149
+ describe("discoverAgents", () => {
150
+ const testDir = path.join(os.tmpdir(), "pi-extended-teams-test-agents-" + Date.now());
151
+
152
+ beforeEach(() => {
153
+ if (fs.existsSync(testDir)) {
154
+ fs.rmSync(testDir, { recursive: true });
155
+ }
156
+ fs.mkdirSync(testDir, { recursive: true });
157
+ });
158
+
159
+ afterEach(() => {
160
+ if (fs.existsSync(testDir)) {
161
+ fs.rmSync(testDir, { recursive: true });
162
+ }
163
+ });
164
+
165
+ it("discovers agent definitions from markdown files", () => {
166
+ fs.writeFileSync(path.join(testDir, "scout.md"), `---
167
+ name: scout
168
+ description: Scout agent
169
+ ---
170
+ Scout prompt`);
171
+
172
+ fs.writeFileSync(path.join(testDir, "builder.md"), `---
173
+ name: builder
174
+ description: Builder agent
175
+ ---
176
+ Builder prompt`);
177
+
178
+ const result = discoverAgents(testDir);
179
+
180
+ expect(result).toHaveLength(2);
181
+ expect(result.find(a => a.name === "scout")).toBeDefined();
182
+ expect(result.find(a => a.name === "builder")).toBeDefined();
183
+ });
184
+
185
+ it("discovers agents from SKILL.md in subdirectories", () => {
186
+ const subDir = path.join(testDir, "special-agent");
187
+ fs.mkdirSync(subDir, { recursive: true });
188
+
189
+ fs.writeFileSync(path.join(subDir, "SKILL.md"), `---
190
+ name: special
191
+ description: Special agent
192
+ ---
193
+ Special prompt`);
194
+
195
+ const result = discoverAgents(testDir);
196
+
197
+ expect(result).toHaveLength(1);
198
+ expect(result[0].name).toBe("special");
199
+ });
200
+
201
+ it("returns empty array for non-existent directory", () => {
202
+ const result = discoverAgents("/non/existent/path");
203
+ expect(result).toEqual([]);
204
+ });
205
+
206
+ it("ignores files without valid frontmatter", () => {
207
+ fs.writeFileSync(path.join(testDir, "invalid.md"), "No frontmatter here");
208
+ fs.writeFileSync(path.join(testDir, "valid.md"), `---
209
+ name: valid
210
+ description: Valid agent
211
+ ---
212
+ Valid prompt`);
213
+
214
+ const result = discoverAgents(testDir);
215
+
216
+ expect(result).toHaveLength(1);
217
+ expect(result[0].name).toBe("valid");
218
+ });
219
+ });
220
+
221
+ describe("discoverTeams", () => {
222
+ const testDir = path.join(os.tmpdir(), "pi-extended-teams-test-teams-" + Date.now());
223
+
224
+ beforeEach(() => {
225
+ if (fs.existsSync(testDir)) {
226
+ fs.rmSync(testDir, { recursive: true });
227
+ }
228
+ fs.mkdirSync(testDir, { recursive: true });
229
+ });
230
+
231
+ afterEach(() => {
232
+ if (fs.existsSync(testDir)) {
233
+ fs.rmSync(testDir, { recursive: true });
234
+ }
235
+ });
236
+
237
+ it("discovers teams from teams.yaml", () => {
238
+ fs.writeFileSync(path.join(testDir, "teams.yaml"), `
239
+ full:
240
+ - scout
241
+ - planner
242
+ `);
243
+
244
+ const result = discoverTeams(testDir);
245
+
246
+ expect(result).toHaveLength(1);
247
+ expect(result[0].name).toBe("full");
248
+ expect(result[0].agents).toEqual(["scout", "planner"]);
249
+ });
250
+
251
+ it("returns empty array when teams.yaml does not exist", () => {
252
+ const result = discoverTeams(testDir);
253
+ expect(result).toEqual([]);
254
+ });
255
+ });
256
+
257
+ describe("getAllAgentDefinitions and getAllPredefinedTeams", () => {
258
+ const globalDir = path.join(os.homedir(), ".pi", "agent", "agents");
259
+ const globalTeamsDir = path.join(os.homedir(), ".pi");
260
+ const legacyGlobalTeamsDir = path.join(os.homedir(), ".pi", "agent");
261
+ const projectDir = path.join(os.tmpdir(), "pi-extended-teams-test-project-" + Date.now());
262
+ const projectAgentsDir = path.join(projectDir, ".pi", "agents");
263
+ const projectTeamsDir = path.join(projectDir, ".pi");
264
+
265
+ // Store original files to restore later
266
+ let originalGlobalAgents: string[] = [];
267
+ let originalGlobalTeams: string | null = null;
268
+ let originalLegacyGlobalTeams: string | null = null;
269
+
270
+ beforeEach(() => {
271
+ // Create project directory
272
+ if (fs.existsSync(projectDir)) {
273
+ fs.rmSync(projectDir, { recursive: true });
274
+ }
275
+ fs.mkdirSync(projectAgentsDir, { recursive: true });
276
+
277
+ // Backup global files if they exist
278
+ if (fs.existsSync(globalDir)) {
279
+ originalGlobalAgents = fs.readdirSync(globalDir);
280
+ }
281
+ if (fs.existsSync(path.join(globalTeamsDir, "teams.yaml"))) {
282
+ originalGlobalTeams = fs.readFileSync(path.join(globalTeamsDir, "teams.yaml"), "utf-8");
283
+ }
284
+ if (fs.existsSync(path.join(legacyGlobalTeamsDir, "teams.yaml"))) {
285
+ originalLegacyGlobalTeams = fs.readFileSync(path.join(legacyGlobalTeamsDir, "teams.yaml"), "utf-8");
286
+ }
287
+ });
288
+
289
+ afterEach(() => {
290
+ if (fs.existsSync(projectDir)) {
291
+ fs.rmSync(projectDir, { recursive: true });
292
+ }
293
+
294
+ const globalTeamsPath = path.join(globalTeamsDir, "teams.yaml");
295
+ if (originalGlobalTeams === null) {
296
+ if (fs.existsSync(globalTeamsPath)) fs.rmSync(globalTeamsPath);
297
+ } else {
298
+ fs.mkdirSync(globalTeamsDir, { recursive: true });
299
+ fs.writeFileSync(globalTeamsPath, originalGlobalTeams);
300
+ }
301
+
302
+ const legacyGlobalTeamsPath = path.join(legacyGlobalTeamsDir, "teams.yaml");
303
+ if (originalLegacyGlobalTeams === null) {
304
+ if (fs.existsSync(legacyGlobalTeamsPath)) fs.rmSync(legacyGlobalTeamsPath);
305
+ } else {
306
+ fs.mkdirSync(legacyGlobalTeamsDir, { recursive: true });
307
+ fs.writeFileSync(legacyGlobalTeamsPath, originalLegacyGlobalTeams);
308
+ }
309
+ });
310
+
311
+ it("combines global and project-local agents", () => {
312
+ // Create project-local agent
313
+ fs.writeFileSync(path.join(projectAgentsDir, "project-agent.md"), `---
314
+ name: project-agent
315
+ description: Project local agent
316
+ ---
317
+ Project prompt`);
318
+
319
+ const result = getAllAgentDefinitions(projectDir);
320
+
321
+ // Should include project-local agent
322
+ expect(result.find(a => a.name === "project-agent")).toBeDefined();
323
+ });
324
+
325
+ it("project-local agents override global agents", () => {
326
+ // Create project-local agent with same name as global
327
+ fs.writeFileSync(path.join(projectAgentsDir, "scout.md"), `---
328
+ name: scout
329
+ description: Project override scout
330
+ ---
331
+ Project scout prompt`);
332
+
333
+ const result = getAllAgentDefinitions(projectDir);
334
+ const scout = result.find(a => a.name === "scout");
335
+
336
+ expect(scout).toBeDefined();
337
+ expect(scout?.description).toBe("Project override scout");
338
+ });
339
+
340
+ it("combines global and project-local teams", () => {
341
+ // Create project-local teams.yaml
342
+ fs.writeFileSync(path.join(projectTeamsDir, "teams.yaml"), `
343
+ custom:
344
+ - agent1
345
+ - agent2
346
+ `);
347
+
348
+ const result = getAllPredefinedTeams(projectDir);
349
+
350
+ // Should include project-local team
351
+ expect(result.find(t => t.name === "custom")).toBeDefined();
352
+ expect(result.find(t => t.name === "custom")?.agents).toEqual(["agent1", "agent2"]);
353
+ });
354
+
355
+ it("reads global predefined teams from ~/.pi/teams.yaml", () => {
356
+ fs.mkdirSync(globalTeamsDir, { recursive: true });
357
+ fs.writeFileSync(path.join(globalTeamsDir, "teams.yaml"), `
358
+ root-global:
359
+ - scout
360
+ `);
361
+
362
+ const result = getAllPredefinedTeams();
363
+
364
+ expect(result.find(t => t.name === "root-global")).toBeDefined();
365
+ expect(result.find(t => t.name === "root-global")?.agents).toEqual(["scout"]);
366
+ });
367
+
368
+ it("falls back to legacy ~/.pi/agent/teams.yaml when needed", () => {
369
+ const globalTeamsPath = path.join(globalTeamsDir, "teams.yaml");
370
+ if (fs.existsSync(globalTeamsPath)) {
371
+ fs.rmSync(globalTeamsPath);
372
+ }
373
+
374
+ fs.mkdirSync(legacyGlobalTeamsDir, { recursive: true });
375
+ fs.writeFileSync(path.join(legacyGlobalTeamsDir, "teams.yaml"), `
376
+ legacy-global:
377
+ - scout
378
+ `);
379
+
380
+ const result = getAllPredefinedTeams();
381
+
382
+ expect(result.find(t => t.name === "legacy-global")).toBeDefined();
383
+ expect(result.find(t => t.name === "legacy-global")?.agents).toEqual(["scout"]);
384
+ });
385
+ });
386
+
387
+ describe("getAgentDefinition and getPredefinedTeam", () => {
388
+ const projectDir = path.join(os.tmpdir(), "pi-extended-teams-test-get-" + Date.now());
389
+ const projectAgentsDir = path.join(projectDir, ".pi", "agents");
390
+ const projectTeamsDir = path.join(projectDir, ".pi");
391
+
392
+ beforeEach(() => {
393
+ if (fs.existsSync(projectDir)) {
394
+ fs.rmSync(projectDir, { recursive: true });
395
+ }
396
+ fs.mkdirSync(projectAgentsDir, { recursive: true });
397
+
398
+ fs.writeFileSync(path.join(projectAgentsDir, "test-agent.md"), `---
399
+ name: test-agent
400
+ description: Test agent
401
+ ---
402
+ Test prompt`);
403
+
404
+ fs.writeFileSync(path.join(projectTeamsDir, "teams.yaml"), `
405
+ test-team:
406
+ - test-agent
407
+ `);
408
+ });
409
+
410
+ afterEach(() => {
411
+ if (fs.existsSync(projectDir)) {
412
+ fs.rmSync(projectDir, { recursive: true });
413
+ }
414
+ });
415
+
416
+ it("gets a specific agent definition by name", () => {
417
+ const result = getAgentDefinition("test-agent", projectDir);
418
+
419
+ expect(result).toBeDefined();
420
+ expect(result?.name).toBe("test-agent");
421
+ expect(result?.description).toBe("Test agent");
422
+ });
423
+
424
+ it("returns undefined for non-existent agent", () => {
425
+ const result = getAgentDefinition("non-existent", projectDir);
426
+ expect(result).toBeUndefined();
427
+ });
428
+
429
+ it("gets a specific predefined team by name", () => {
430
+ const result = getPredefinedTeam("test-team", projectDir);
431
+
432
+ expect(result).toBeDefined();
433
+ expect(result?.name).toBe("test-team");
434
+ expect(result?.agents).toEqual(["test-agent"]);
435
+ });
436
+
437
+ it("returns undefined for non-existent team", () => {
438
+ const result = getPredefinedTeam("non-existent", projectDir);
439
+ expect(result).toBeUndefined();
440
+ });
441
+ });