@gjczone/pi-swarm 0.3.5 → 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.
- package/README.md +33 -71
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/shared/controller.d.ts +10 -4
- package/dist/shared/controller.d.ts.map +1 -1
- package/dist/shared/controller.js +139 -6
- package/dist/shared/controller.js.map +1 -1
- package/dist/shared/render.d.ts +0 -11
- package/dist/shared/render.d.ts.map +1 -1
- package/dist/shared/render.js +3 -36
- package/dist/shared/render.js.map +1 -1
- package/dist/shared/spawner.d.ts.map +1 -1
- package/dist/shared/spawner.js +212 -17
- package/dist/shared/spawner.js.map +1 -1
- package/dist/shared/types.d.ts +58 -0
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/shared/worktree.d.ts +81 -0
- package/dist/shared/worktree.d.ts.map +1 -0
- package/dist/shared/worktree.js +417 -0
- package/dist/shared/worktree.js.map +1 -0
- package/dist/shared/xml.d.ts +18 -0
- package/dist/shared/xml.d.ts.map +1 -0
- package/dist/shared/xml.js +31 -0
- package/dist/shared/xml.js.map +1 -0
- package/dist/swarm/tool.d.ts.map +1 -1
- package/dist/swarm/tool.js +69 -15
- package/dist/swarm/tool.js.map +1 -1
- package/dist/team/mailbox.d.ts +5 -0
- package/dist/team/mailbox.d.ts.map +1 -1
- package/dist/team/mailbox.js +43 -2
- package/dist/team/mailbox.js.map +1 -1
- package/dist/team/supervisor.d.ts +27 -2
- package/dist/team/supervisor.d.ts.map +1 -1
- package/dist/team/supervisor.js +93 -50
- package/dist/team/supervisor.js.map +1 -1
- package/dist/team/task-graph.d.ts +5 -2
- package/dist/team/task-graph.d.ts.map +1 -1
- package/dist/team/task-graph.js +27 -1
- package/dist/team/task-graph.js.map +1 -1
- package/dist/team/tool.d.ts.map +1 -1
- package/dist/team/tool.js +102 -18
- package/dist/team/tool.js.map +1 -1
- package/dist/tui/progress.d.ts +56 -44
- package/dist/tui/progress.d.ts.map +1 -1
- package/dist/tui/progress.js +497 -179
- package/dist/tui/progress.js.map +1 -1
- package/dist/tui/team-dashboard.d.ts +39 -23
- package/dist/tui/team-dashboard.d.ts.map +1 -1
- package/dist/tui/team-dashboard.js +506 -143
- package/dist/tui/team-dashboard.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree - Git worktree isolation for subagents.
|
|
3
|
+
*
|
|
4
|
+
* Creates a temporary git worktree so an agent works on an isolated copy of the repo.
|
|
5
|
+
* On completion:
|
|
6
|
+
* - If no changes: worktree is removed.
|
|
7
|
+
* - If changes exist: committed to a new branch, worktree is removed.
|
|
8
|
+
* - The caller is responsible for merging the branch back (see mergeBranch).
|
|
9
|
+
*
|
|
10
|
+
* Safety:
|
|
11
|
+
* - Non-git repos silently fall back to cwd (no worktree created).
|
|
12
|
+
* - Symbolic links to node_modules are created to avoid reinstalling dependencies.
|
|
13
|
+
* - Each worktree is created from HEAD in detached mode; changes are committed to a
|
|
14
|
+
* uniquely-named branch to avoid collisions.
|
|
15
|
+
*
|
|
16
|
+
* Reference implementation: gotgenes/pi-subagents-worktrees, pi-crew worktree-manager.
|
|
17
|
+
*/
|
|
18
|
+
import { execFileSync } from "node:child_process";
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { existsSync, mkdirSync, symlinkSync, statSync, } from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { join, dirname } from "node:path";
|
|
23
|
+
/**
|
|
24
|
+
* Detect whether cwd is inside a git repository.
|
|
25
|
+
*/
|
|
26
|
+
export function isGitRepository(cwd) {
|
|
27
|
+
try {
|
|
28
|
+
execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
29
|
+
cwd,
|
|
30
|
+
stdio: "pipe",
|
|
31
|
+
timeout: 5000,
|
|
32
|
+
});
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the current branch name or "HEAD" if detached.
|
|
41
|
+
*/
|
|
42
|
+
function getCurrentBranch(cwd) {
|
|
43
|
+
try {
|
|
44
|
+
const name = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
45
|
+
cwd,
|
|
46
|
+
stdio: "pipe",
|
|
47
|
+
timeout: 5000,
|
|
48
|
+
})
|
|
49
|
+
.toString()
|
|
50
|
+
.trim();
|
|
51
|
+
return name || "HEAD";
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return "HEAD";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the current HEAD commit SHA.
|
|
59
|
+
*/
|
|
60
|
+
function getCurrentHead(cwd) {
|
|
61
|
+
try {
|
|
62
|
+
return execFileSync("git", ["rev-parse", "HEAD"], {
|
|
63
|
+
cwd,
|
|
64
|
+
stdio: "pipe",
|
|
65
|
+
timeout: 5000,
|
|
66
|
+
})
|
|
67
|
+
.toString()
|
|
68
|
+
.trim();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if the working directory is clean (no uncommitted changes).
|
|
76
|
+
*/
|
|
77
|
+
function isWorkingTreeClean(cwd) {
|
|
78
|
+
try {
|
|
79
|
+
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
80
|
+
cwd,
|
|
81
|
+
stdio: "pipe",
|
|
82
|
+
timeout: 5000,
|
|
83
|
+
})
|
|
84
|
+
.toString()
|
|
85
|
+
.trim();
|
|
86
|
+
return status.length === 0;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Files/directories to symlink from repo root into worktree for project context.
|
|
94
|
+
* These contain project rules, config, and state that subagents need access to.
|
|
95
|
+
*/
|
|
96
|
+
const PROJECT_CONTEXT_ENTRIES = [
|
|
97
|
+
"AGENTS.md",
|
|
98
|
+
"CLAUDE.md",
|
|
99
|
+
".pi",
|
|
100
|
+
".cursorrules",
|
|
101
|
+
".cursor",
|
|
102
|
+
".github",
|
|
103
|
+
"docs",
|
|
104
|
+
"rules",
|
|
105
|
+
];
|
|
106
|
+
/**
|
|
107
|
+
* Create a temporary worktree for a subagent.
|
|
108
|
+
* Returns undefined if not in a git repo or worktree creation fails.
|
|
109
|
+
*
|
|
110
|
+
* 业务说明:为子 agent 创建临时 git worktree 实现目录隔离。
|
|
111
|
+
* worktree 基于 HEAD 的 detached 状态创建,确保不污染主分支。
|
|
112
|
+
* 同时符号链接项目上下文文件(AGENTS.md、.pi 等)和 mailbox 目录,
|
|
113
|
+
* 这样子 agent 可以访问项目规则并实时读写 mailbox 消息。
|
|
114
|
+
* 记录原始分支名和 HEAD SHA 用于后续合并。
|
|
115
|
+
*/
|
|
116
|
+
export function createWorktree(cwd, agentId, mailboxPath) {
|
|
117
|
+
if (!isGitRepository(cwd))
|
|
118
|
+
return undefined;
|
|
119
|
+
try {
|
|
120
|
+
execFileSync("git", ["rev-parse", "HEAD"], {
|
|
121
|
+
cwd,
|
|
122
|
+
stdio: "pipe",
|
|
123
|
+
timeout: 5000,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const originalBranch = getCurrentBranch(cwd);
|
|
130
|
+
const originalHead = getCurrentHead(cwd);
|
|
131
|
+
const branch = `pi-agent-${agentId}`;
|
|
132
|
+
const suffix = randomUUID().slice(0, 8);
|
|
133
|
+
const worktreePath = join(tmpdir(), `pi-agent-${agentId}-${suffix}`);
|
|
134
|
+
try {
|
|
135
|
+
execFileSync("git", ["worktree", "add", "--detach", worktreePath, "HEAD"], {
|
|
136
|
+
cwd,
|
|
137
|
+
stdio: "pipe",
|
|
138
|
+
timeout: 30000,
|
|
139
|
+
});
|
|
140
|
+
// Symlink node_modules to avoid reinstalling dependencies in every worktree
|
|
141
|
+
try {
|
|
142
|
+
const nodeModulesSource = join(cwd, "node_modules");
|
|
143
|
+
const nodeModulesTarget = join(worktreePath, "node_modules");
|
|
144
|
+
if (existsSync(nodeModulesSource) && !existsSync(nodeModulesTarget)) {
|
|
145
|
+
symlinkSync(nodeModulesSource, nodeModulesTarget, "dir");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// node_modules symlink failure is non-fatal; agent can still work
|
|
150
|
+
}
|
|
151
|
+
// Symlink project context files that are not tracked by git
|
|
152
|
+
// (AGENTS.md, .pi config, etc.) so subagents inherit project rules
|
|
153
|
+
let mailboxLinkCreated = false;
|
|
154
|
+
for (const entry of PROJECT_CONTEXT_ENTRIES) {
|
|
155
|
+
const sourcePath = join(cwd, entry);
|
|
156
|
+
const targetPath = join(worktreePath, entry);
|
|
157
|
+
if (!existsSync(sourcePath) || existsSync(targetPath))
|
|
158
|
+
continue;
|
|
159
|
+
try {
|
|
160
|
+
const stat = statSync(sourcePath);
|
|
161
|
+
symlinkSync(sourcePath, targetPath, stat.isDirectory() ? "dir" : "file");
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Best effort per-entry symlink
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Symlink mailbox directory into worktree so subagents can read/write messages in real time
|
|
168
|
+
if (mailboxPath && existsSync(mailboxPath)) {
|
|
169
|
+
const mailboxTarget = join(worktreePath, ".pi", "swarm", "mailbox-link");
|
|
170
|
+
try {
|
|
171
|
+
mkdirSync(dirname(mailboxTarget), { recursive: true });
|
|
172
|
+
if (!existsSync(mailboxTarget)) {
|
|
173
|
+
symlinkSync(mailboxPath, mailboxTarget, "dir");
|
|
174
|
+
mailboxLinkCreated = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Best effort mailbox link
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
path: worktreePath,
|
|
183
|
+
branch,
|
|
184
|
+
originalBranch,
|
|
185
|
+
originalHead,
|
|
186
|
+
repoCwd: cwd,
|
|
187
|
+
mailboxLinkCreated,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Clean up the worktree after agent completion.
|
|
196
|
+
*
|
|
197
|
+
* Steps:
|
|
198
|
+
* 1. Check for changes in worktree.
|
|
199
|
+
* 2. If no changes: remove worktree, return { hasChanges: false }.
|
|
200
|
+
* 3. If changes: add + commit to a new branch based on the commit, remove worktree.
|
|
201
|
+
*
|
|
202
|
+
* Note: This function does NOT merge back to the original branch.
|
|
203
|
+
* Call mergeBranch() after all agents in a batch complete for safe merging.
|
|
204
|
+
*
|
|
205
|
+
* 业务说明:子 agent 完成后清理 worktree。有变更则提交到新分支,
|
|
206
|
+
* 不自动合并——合并应在批处理完成后顺序执行以避免竞争条件。
|
|
207
|
+
*/
|
|
208
|
+
export function cleanupWorktree(cwd, worktree, agentDescription) {
|
|
209
|
+
if (!existsSync(worktree.path)) {
|
|
210
|
+
return { hasChanges: false };
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
214
|
+
cwd: worktree.path,
|
|
215
|
+
stdio: "pipe",
|
|
216
|
+
timeout: 10000,
|
|
217
|
+
})
|
|
218
|
+
.toString()
|
|
219
|
+
.trim();
|
|
220
|
+
if (!status) {
|
|
221
|
+
removeWorktree(cwd, worktree.path);
|
|
222
|
+
return { hasChanges: false };
|
|
223
|
+
}
|
|
224
|
+
// Stage all changes
|
|
225
|
+
execFileSync("git", ["add", "-A"], {
|
|
226
|
+
cwd: worktree.path,
|
|
227
|
+
stdio: "pipe",
|
|
228
|
+
timeout: 10000,
|
|
229
|
+
});
|
|
230
|
+
// Commit in worktree
|
|
231
|
+
const safeDesc = agentDescription.slice(0, 200);
|
|
232
|
+
const commitMsg = `pi-agent: ${safeDesc}`;
|
|
233
|
+
execFileSync("git", ["commit", "-m", commitMsg], {
|
|
234
|
+
cwd: worktree.path,
|
|
235
|
+
stdio: "pipe",
|
|
236
|
+
timeout: 10000,
|
|
237
|
+
});
|
|
238
|
+
// Get the commit SHA from worktree
|
|
239
|
+
const commitSha = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
240
|
+
cwd: worktree.path,
|
|
241
|
+
stdio: "pipe",
|
|
242
|
+
timeout: 5000,
|
|
243
|
+
})
|
|
244
|
+
.toString()
|
|
245
|
+
.trim();
|
|
246
|
+
// Remove worktree first so we can create the branch in the main repo
|
|
247
|
+
removeWorktree(cwd, worktree.path);
|
|
248
|
+
// Create branch pointing to the commit in the main repo
|
|
249
|
+
let branchName = worktree.branch;
|
|
250
|
+
try {
|
|
251
|
+
execFileSync("git", ["branch", branchName, commitSha], {
|
|
252
|
+
cwd,
|
|
253
|
+
stdio: "pipe",
|
|
254
|
+
timeout: 5000,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
branchName = `${worktree.branch}-${Date.now()}`;
|
|
259
|
+
try {
|
|
260
|
+
execFileSync("git", ["branch", branchName, commitSha], {
|
|
261
|
+
cwd,
|
|
262
|
+
stdio: "pipe",
|
|
263
|
+
timeout: 5000,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return { hasChanges: true }; // Commit exists but branch creation failed; user can find by SHA
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { hasChanges: true, branch: branchName };
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
try {
|
|
274
|
+
removeWorktree(cwd, worktree.path);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// best effort cleanup
|
|
278
|
+
}
|
|
279
|
+
return { hasChanges: false };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Merge a branch into the current branch.
|
|
284
|
+
* Must be called when the working directory is clean (no parallel agents running).
|
|
285
|
+
* Uses --no-ff --no-edit to create a merge commit preserving history.
|
|
286
|
+
*
|
|
287
|
+
* 业务说明:将子 agent 的分支合并到当前分支。必须在工作目录干净时调用
|
|
288
|
+
* (所有并行 agent 完成后顺序执行)。冲突时返回错误信息并保留分支供手动解决。
|
|
289
|
+
*/
|
|
290
|
+
export function mergeBranch(cwd, branchName) {
|
|
291
|
+
try {
|
|
292
|
+
if (!isWorkingTreeClean(cwd)) {
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
branch: branchName,
|
|
296
|
+
error: `Working directory is dirty. Merge branch '${branchName}' manually after committing/stashing changes.`,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
// Verify the branch exists
|
|
300
|
+
try {
|
|
301
|
+
execFileSync("git", ["rev-parse", "--verify", branchName], {
|
|
302
|
+
cwd,
|
|
303
|
+
stdio: "pipe",
|
|
304
|
+
timeout: 5000,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return {
|
|
309
|
+
success: false,
|
|
310
|
+
branch: branchName,
|
|
311
|
+
error: `Branch '${branchName}' not found.`,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
// Check if already merged by seeing if the branch commit is an ancestor of HEAD
|
|
315
|
+
try {
|
|
316
|
+
execFileSync("git", ["merge-base", "--is-ancestor", branchName, "HEAD"], {
|
|
317
|
+
cwd,
|
|
318
|
+
stdio: "pipe",
|
|
319
|
+
timeout: 5000,
|
|
320
|
+
});
|
|
321
|
+
// Already merged; clean up branch
|
|
322
|
+
try {
|
|
323
|
+
execFileSync("git", ["branch", "-d", branchName], {
|
|
324
|
+
cwd,
|
|
325
|
+
stdio: "pipe",
|
|
326
|
+
timeout: 5000,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
// Best effort cleanup
|
|
331
|
+
}
|
|
332
|
+
return { success: true, branch: branchName };
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// Not yet merged, proceed
|
|
336
|
+
}
|
|
337
|
+
// Attempt merge with --no-ff --no-edit
|
|
338
|
+
execFileSync("git", ["merge", "--no-ff", "--no-edit", branchName], {
|
|
339
|
+
cwd,
|
|
340
|
+
stdio: "pipe",
|
|
341
|
+
timeout: 30000,
|
|
342
|
+
});
|
|
343
|
+
// Clean up feature branch after successful merge
|
|
344
|
+
try {
|
|
345
|
+
execFileSync("git", ["branch", "-d", branchName], {
|
|
346
|
+
cwd,
|
|
347
|
+
stdio: "pipe",
|
|
348
|
+
timeout: 5000,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
// Branch deletion failure is non-fatal
|
|
353
|
+
}
|
|
354
|
+
return { success: true, branch: branchName };
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
const stderr = err instanceof Error ? err.message : String(err);
|
|
358
|
+
// Attempt to abort if a merge is in progress
|
|
359
|
+
try {
|
|
360
|
+
execFileSync("git", ["merge", "--abort"], {
|
|
361
|
+
cwd,
|
|
362
|
+
stdio: "pipe",
|
|
363
|
+
timeout: 5000,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
// Merge abort may fail if no merge was in progress
|
|
368
|
+
}
|
|
369
|
+
if (stderr.includes("CONFLICT") ||
|
|
370
|
+
stderr.includes("Automatic merge failed")) {
|
|
371
|
+
return {
|
|
372
|
+
success: false,
|
|
373
|
+
branch: branchName,
|
|
374
|
+
error: `Merge conflicts detected. Resolve manually: git merge ${branchName}`,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
branch: branchName,
|
|
380
|
+
error: `Auto-merge failed: ${stderr}`,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function removeWorktree(cwd, worktreePath) {
|
|
385
|
+
try {
|
|
386
|
+
execFileSync("git", ["worktree", "remove", "--force", worktreePath], {
|
|
387
|
+
cwd,
|
|
388
|
+
stdio: "pipe",
|
|
389
|
+
timeout: 10000,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
try {
|
|
394
|
+
execFileSync("git", ["worktree", "prune"], {
|
|
395
|
+
cwd,
|
|
396
|
+
stdio: "pipe",
|
|
397
|
+
timeout: 5000,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
// best effort prune
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
export function pruneWorktrees(cwd) {
|
|
406
|
+
try {
|
|
407
|
+
execFileSync("git", ["worktree", "prune"], {
|
|
408
|
+
cwd,
|
|
409
|
+
stdio: "pipe",
|
|
410
|
+
timeout: 5000,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
// best effort
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/shared/worktree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xml — reusable XML escaping utilities.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from render.ts and supervisor.ts to eliminate duplicate
|
|
5
|
+
* implementations that had drifted apart.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Escape a string for use in an XML attribute value.
|
|
9
|
+
* Handles all five XML special characters: & " ' < >
|
|
10
|
+
*/
|
|
11
|
+
export declare function escapeXmlAttr(value: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Escape a string for use in XML element body content.
|
|
14
|
+
* Escapes only the structural characters (& < >) plus CDATA-closing
|
|
15
|
+
* sequence (]]>) which would break XML parsing if left unescaped.
|
|
16
|
+
*/
|
|
17
|
+
export declare function escapeXmlBody(value: string): string;
|
|
18
|
+
//# sourceMappingURL=xml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../../src/shared/xml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xml — reusable XML escaping utilities.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from render.ts and supervisor.ts to eliminate duplicate
|
|
5
|
+
* implementations that had drifted apart.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Escape a string for use in an XML attribute value.
|
|
9
|
+
* Handles all five XML special characters: & " ' < >
|
|
10
|
+
*/
|
|
11
|
+
export function escapeXmlAttr(value) {
|
|
12
|
+
return value
|
|
13
|
+
.replaceAll("&", "&")
|
|
14
|
+
.replaceAll('"', """)
|
|
15
|
+
.replaceAll("'", "'")
|
|
16
|
+
.replaceAll("<", "<")
|
|
17
|
+
.replaceAll(">", ">");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Escape a string for use in XML element body content.
|
|
21
|
+
* Escapes only the structural characters (& < >) plus CDATA-closing
|
|
22
|
+
* sequence (]]>) which would break XML parsing if left unescaped.
|
|
23
|
+
*/
|
|
24
|
+
export function escapeXmlBody(value) {
|
|
25
|
+
return value
|
|
26
|
+
.replaceAll("&", "&")
|
|
27
|
+
.replaceAll("<", "<")
|
|
28
|
+
.replaceAll(">", ">")
|
|
29
|
+
.replaceAll("]]>", "]]>");
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=xml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml.js","sourceRoot":"","sources":["../../src/shared/xml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK;SACT,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK;SACT,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACjC,CAAC"}
|
package/dist/swarm/tool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/swarm/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,YAAY,EAEb,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/swarm/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,YAAY,EAEb,MAAM,iCAAiC,CAAC;AAiEzC,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAmU7D"}
|
package/dist/swarm/tool.js
CHANGED
|
@@ -12,6 +12,7 @@ import { SubagentBatchController, resolveSwarmMaxConcurrency, } from "../shared/
|
|
|
12
12
|
import { renderSwarmResults, toSwarmRunResults } from "../shared/render.js";
|
|
13
13
|
import { spawnSubagent, resumeSubagent, retrySubagent, } from "../shared/spawner.js";
|
|
14
14
|
import { resolveSwarmRoot, createManifest, updateManifest, readManifest, registerAgentInManifest, validateId, } from "../state/persistence.js";
|
|
15
|
+
import { mergeBranch, isGitRepository } from "../shared/worktree.js";
|
|
15
16
|
import { AgentSwarmProgressComponent, snapshotToProgressState, } from "../tui/progress.js";
|
|
16
17
|
/** Widget key used to render the live swarm progress panel. */
|
|
17
18
|
const PROGRESS_WIDGET_KEY = "pi-swarm-progress";
|
|
@@ -22,15 +23,21 @@ const DEFAULT_SUBAGENT_TYPE = "coder";
|
|
|
22
23
|
const PROMPT_TEMPLATE_PLACEHOLDER = "{{item}}";
|
|
23
24
|
const MAX_AGENT_SWARM_SUBAGENTS = 128;
|
|
24
25
|
const DEFAULT_SUBAGENT_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
25
|
-
const AGENT_SWARM_DESCRIPTION =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
const AGENT_SWARM_DESCRIPTION = [
|
|
27
|
+
"Launch 1-128 isolated subagents in parallel from a template or resume list.",
|
|
28
|
+
"",
|
|
29
|
+
"CRITICAL RULES:",
|
|
30
|
+
"1. If `items` is provided, `prompt_template` MUST contain {{item}} exactly once.",
|
|
31
|
+
"2. If only resuming agents, omit both `items` and `prompt_template`.",
|
|
32
|
+
"3. This tool MUST be the ONLY tool call in your response — do not batch.",
|
|
33
|
+
"",
|
|
34
|
+
"QUICK REFERENCE:",
|
|
35
|
+
'- New subagents: { "items": ["a","b"], "prompt_template": "Review {{item}}" }',
|
|
36
|
+
'- Resume only: { "resume_agent_ids": { "ag-1": "retry prompt" } }',
|
|
37
|
+
"- Combined: both fields together (resumes fire first, then spawns).",
|
|
38
|
+
"",
|
|
39
|
+
"Use AgentSwarm for parallel independent tasks. Use SwarmTeam for role-based collaboration.",
|
|
40
|
+
].join("\n");
|
|
34
41
|
// ---------------------------------------------------------------------------
|
|
35
42
|
// Registration
|
|
36
43
|
// ---------------------------------------------------------------------------
|
|
@@ -42,19 +49,29 @@ export function registerAgentSwarmTool(pi) {
|
|
|
42
49
|
parameters: Type.Object({
|
|
43
50
|
description: Type.String({
|
|
44
51
|
description: "Short description for the whole swarm.",
|
|
52
|
+
examples: ["Review all source files for bugs"],
|
|
45
53
|
}),
|
|
46
54
|
subagent_type: Type.Optional(Type.String({
|
|
47
55
|
description: "Subagent type used for every spawned subagent. Defaults to coder when omitted.",
|
|
56
|
+
examples: ["coder"],
|
|
48
57
|
})),
|
|
49
58
|
prompt_template: Type.Optional(Type.String({
|
|
50
|
-
description: `
|
|
59
|
+
description: `REQUIRED when items is provided. Must contain ${PROMPT_TEMPLATE_PLACEHOLDER} exactly once. Each item replaces the placeholder.`,
|
|
60
|
+
examples: [
|
|
61
|
+
"Fix issues in {{item}}",
|
|
62
|
+
"Review {{item}} for security vulnerabilities",
|
|
63
|
+
],
|
|
51
64
|
})),
|
|
52
65
|
items: Type.Optional(Type.Array(Type.String(), {
|
|
53
66
|
maxItems: MAX_AGENT_SWARM_SUBAGENTS,
|
|
54
|
-
description: "
|
|
67
|
+
description: "REQUIRED with prompt_template. Each item replaces {{item}}. Values are used as {{item}} in the template. Min 1 item, max 128.",
|
|
68
|
+
examples: [["src/auth.ts", "src/api.ts", "src/db.ts"]],
|
|
55
69
|
})),
|
|
56
70
|
resume_agent_ids: Type.Optional(Type.Record(Type.String(), Type.String(), {
|
|
57
71
|
description: "Map of existing subagent agent_id to the prompt used to resume that subagent. Resumed subagents launch before new item-based subagents.",
|
|
72
|
+
examples: [
|
|
73
|
+
{ "swarm-abc123": "Retry with more focus on XSS detection" },
|
|
74
|
+
],
|
|
58
75
|
})),
|
|
59
76
|
}, { additionalProperties: false }),
|
|
60
77
|
execute: async (toolCallId, params, signal, _onUpdate, ctxRaw) => {
|
|
@@ -98,6 +115,7 @@ export function registerAgentSwarmTool(pi) {
|
|
|
98
115
|
timeout: DEFAULT_SUBAGENT_TIMEOUT_MS,
|
|
99
116
|
swarmRoot,
|
|
100
117
|
runId,
|
|
118
|
+
useWorktree: true,
|
|
101
119
|
};
|
|
102
120
|
if (spec.kind === "resume") {
|
|
103
121
|
return {
|
|
@@ -113,6 +131,8 @@ export function registerAgentSwarmTool(pi) {
|
|
|
113
131
|
});
|
|
114
132
|
// Run with controller
|
|
115
133
|
const maxConcurrency = resolveSwarmMaxConcurrency(process.cwd());
|
|
134
|
+
const repoCwd = process.cwd();
|
|
135
|
+
const inGitRepo = isGitRepository(repoCwd);
|
|
116
136
|
const controller = new SubagentBatchController({
|
|
117
137
|
spawn: spawnSubagent,
|
|
118
138
|
resume: resumeSubagent,
|
|
@@ -124,6 +144,13 @@ export function registerAgentSwarmTool(pi) {
|
|
|
124
144
|
},
|
|
125
145
|
});
|
|
126
146
|
const results = await controller.run();
|
|
147
|
+
// Collect worktree branches for auto-merge
|
|
148
|
+
const allBranches = [];
|
|
149
|
+
for (const r of results) {
|
|
150
|
+
if (r.worktreeBranch) {
|
|
151
|
+
allBranches.push(r.worktreeBranch);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
127
154
|
// Register all agents in manifest and mark run completed
|
|
128
155
|
for (const r of results) {
|
|
129
156
|
if (r.agentId) {
|
|
@@ -136,11 +163,32 @@ export function registerAgentSwarmTool(pi) {
|
|
|
136
163
|
manifest.completedAt = Date.now();
|
|
137
164
|
updateManifest(swarmRoot, manifest);
|
|
138
165
|
}
|
|
166
|
+
// Auto-merge worktree branches back to original branch (in Git repos)
|
|
167
|
+
const mergeResults = [];
|
|
168
|
+
if (inGitRepo && allBranches.length > 0) {
|
|
169
|
+
for (const branch of allBranches) {
|
|
170
|
+
try {
|
|
171
|
+
const mergeResult = mergeBranch(repoCwd, branch);
|
|
172
|
+
if (mergeResult.success) {
|
|
173
|
+
mergeResults.push(`Merged: ${branch}`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
mergeResults.push(`Merge failed for ${branch}: ${mergeResult.error}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
mergeResults.push(`Merge error for ${branch}: ${err instanceof Error ? err.message : String(err)}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
139
184
|
// Trigger completion animation before tearing down
|
|
140
185
|
progress?.component.complete();
|
|
141
186
|
// Render output
|
|
142
187
|
const swarmResults = toSwarmRunResults(results);
|
|
143
|
-
|
|
188
|
+
let output = renderSwarmResults(swarmResults);
|
|
189
|
+
if (mergeResults.length > 0) {
|
|
190
|
+
output += `\n\n<auto_merge>\n${mergeResults.join("\n")}\n</auto_merge>`;
|
|
191
|
+
}
|
|
144
192
|
return {
|
|
145
193
|
content: [{ type: "text", text: output }],
|
|
146
194
|
details: undefined,
|
|
@@ -245,18 +293,24 @@ function createAgentSwarmSpecs(args) {
|
|
|
245
293
|
const resumeCount = resumeEntries.length;
|
|
246
294
|
const totalCount = resumeCount + itemCount;
|
|
247
295
|
if (!hasMinimumAgentSwarmInputs(itemCount, resumeCount)) {
|
|
248
|
-
throw new Error("AgentSwarm requires at least 1 item or a resume_agent_ids entry."
|
|
296
|
+
throw new Error("AgentSwarm requires at least 1 item or a resume_agent_ids entry. " +
|
|
297
|
+
'Example with items: { "items": ["src/a.ts"], "prompt_template": "Review {{item}}" }. ' +
|
|
298
|
+
'Example with resume: { "resume_agent_ids": { "swarm-abc": "Retry with more detail" } }.');
|
|
249
299
|
}
|
|
250
300
|
if (totalCount > MAX_AGENT_SWARM_SUBAGENTS) {
|
|
251
301
|
throw new Error(`AgentSwarm supports at most ${String(MAX_AGENT_SWARM_SUBAGENTS)} subagents.`);
|
|
252
302
|
}
|
|
253
303
|
const promptTemplate = normalizeOptionalString(args.prompt_template);
|
|
254
304
|
if (items.length > 0 && promptTemplate === undefined) {
|
|
255
|
-
throw new Error("prompt_template is required when items are provided."
|
|
305
|
+
throw new Error("prompt_template is required when items are provided. " +
|
|
306
|
+
'Example: { "prompt_template": "Review {{item}} for bugs", "items": ["src/a.ts", "src/b.ts"] }');
|
|
256
307
|
}
|
|
257
308
|
if (promptTemplate !== undefined &&
|
|
258
309
|
!promptTemplate.includes(PROMPT_TEMPLATE_PLACEHOLDER)) {
|
|
259
|
-
throw new Error(`prompt_template must include the ${PROMPT_TEMPLATE_PLACEHOLDER} placeholder
|
|
310
|
+
throw new Error(`prompt_template must include the ${PROMPT_TEMPLATE_PLACEHOLDER} placeholder. ` +
|
|
311
|
+
`Got: "${promptTemplate.slice(0, 80)}". ` +
|
|
312
|
+
`Add {{item}} where each item value should be inserted. ` +
|
|
313
|
+
'Example: "Fix issues in {{item}}"');
|
|
260
314
|
}
|
|
261
315
|
const seenPrompts = new Map();
|
|
262
316
|
const specs = [];
|