@leing2021/super-pi 0.21.0 → 0.22.1
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/LICENSE +21 -0
- package/README.md +157 -357
- package/THIRD-PARTY-NOTICES.md +35 -0
- package/extensions/subagent/__tests__/async-job-tracker.test.ts +21 -0
- package/extensions/subagent/__tests__/execution-activity.test.ts +15 -0
- package/extensions/subagent/__tests__/parallel-render-stress.test.ts +82 -0
- package/extensions/subagent/__tests__/render-dedup.test.ts +98 -0
- package/extensions/subagent/__tests__/render-widget.test.ts +28 -0
- package/extensions/subagent/__tests__/throttle.test.ts +90 -0
- package/extensions/subagent/agent-management.ts +596 -0
- package/extensions/subagent/agent-manager-chain-detail.ts +163 -0
- package/extensions/subagent/agent-manager-detail.ts +232 -0
- package/extensions/subagent/agent-manager-edit.ts +391 -0
- package/extensions/subagent/agent-manager-list.ts +279 -0
- package/extensions/subagent/agent-manager-parallel.ts +305 -0
- package/extensions/subagent/agent-manager.ts +706 -0
- package/extensions/subagent/agent-scope.ts +9 -0
- package/extensions/subagent/agent-selection.ts +26 -0
- package/extensions/subagent/agent-serializer.ts +124 -0
- package/extensions/subagent/agent-templates.ts +63 -0
- package/extensions/subagent/agents/context-builder.md +37 -0
- package/extensions/subagent/agents/delegate.md +9 -0
- package/extensions/subagent/agents/oracle.md +73 -0
- package/extensions/subagent/agents/planner.md +52 -0
- package/extensions/subagent/agents/researcher.md +50 -0
- package/extensions/subagent/agents/reviewer.md +38 -0
- package/extensions/subagent/agents/scout.md +48 -0
- package/extensions/subagent/agents/worker.md +52 -0
- package/extensions/subagent/agents.ts +762 -0
- package/extensions/subagent/artifacts.ts +101 -0
- package/extensions/subagent/async-execution.ts +521 -0
- package/extensions/subagent/async-job-tracker.ts +240 -0
- package/extensions/subagent/async-status.ts +242 -0
- package/extensions/subagent/chain-clarify.ts +1365 -0
- package/extensions/subagent/chain-execution.ts +854 -0
- package/extensions/subagent/chain-serializer.ts +127 -0
- package/extensions/subagent/completion-dedupe.ts +66 -0
- package/extensions/subagent/doctor.ts +201 -0
- package/extensions/subagent/execution.ts +748 -0
- package/extensions/subagent/file-coalescer.ts +43 -0
- package/extensions/subagent/fork-context.ts +64 -0
- package/extensions/subagent/formatters.ts +123 -0
- package/extensions/subagent/frontmatter.ts +32 -0
- package/extensions/subagent/index.ts +586 -0
- package/extensions/subagent/intercom-bridge.ts +241 -0
- package/extensions/subagent/jsonl-writer.ts +84 -0
- package/extensions/subagent/model-fallback.ts +109 -0
- package/extensions/subagent/notify.ts +111 -0
- package/extensions/subagent/parallel-utils.ts +109 -0
- package/extensions/subagent/pi-args.ts +139 -0
- package/extensions/subagent/pi-spawn.ts +101 -0
- package/extensions/subagent/post-exit-stdio-guard.ts +88 -0
- package/extensions/subagent/prompt-template-bridge.ts +400 -0
- package/extensions/subagent/prompts/gather-context-and-clarify.md +13 -0
- package/extensions/subagent/prompts/parallel-cleanup.md +42 -0
- package/extensions/subagent/prompts/parallel-research.md +50 -0
- package/extensions/subagent/prompts/parallel-review.md +40 -0
- package/extensions/subagent/render-helpers.ts +83 -0
- package/extensions/subagent/render.ts +908 -0
- package/extensions/subagent/result-intercom.ts +238 -0
- package/extensions/subagent/result-watcher.ts +172 -0
- package/extensions/subagent/run-history.ts +58 -0
- package/extensions/subagent/run-status.ts +137 -0
- package/extensions/subagent/schemas.ts +165 -0
- package/extensions/subagent/session-tokens.ts +51 -0
- package/extensions/subagent/settings.ts +368 -0
- package/extensions/subagent/single-output.ts +98 -0
- package/extensions/subagent/skills.ts +627 -0
- package/extensions/subagent/slash-bridge.ts +177 -0
- package/extensions/subagent/slash-commands.ts +304 -0
- package/extensions/subagent/slash-live-state.ts +295 -0
- package/extensions/subagent/subagent-control.ts +151 -0
- package/extensions/subagent/subagent-executor.ts +1915 -0
- package/extensions/subagent/subagent-prompt-runtime.ts +76 -0
- package/extensions/subagent/subagent-runner.ts +1471 -0
- package/extensions/subagent/subagents-status.ts +473 -0
- package/extensions/subagent/text-editor.ts +273 -0
- package/extensions/subagent/throttle.ts +77 -0
- package/extensions/subagent/top-level-async.ts +16 -0
- package/extensions/subagent/types.ts +624 -0
- package/extensions/subagent/utils.ts +457 -0
- package/extensions/subagent/worktree.ts +580 -0
- package/extensions/super-pi-extension/index.ts +2 -55
- package/package.json +14 -6
- package/skills/pi-subagents/SKILL.md +566 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Nico Bailon
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Source: https://github.com/nicobailon/pi-subagents
|
|
4
|
+
export const PROMPT_TEMPLATE_SUBAGENT_REQUEST_EVENT = "prompt-template:subagent:request";
|
|
5
|
+
export const PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT = "prompt-template:subagent:started";
|
|
6
|
+
export const PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT = "prompt-template:subagent:response";
|
|
7
|
+
export const PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT = "prompt-template:subagent:update";
|
|
8
|
+
export const PROMPT_TEMPLATE_SUBAGENT_CANCEL_EVENT = "prompt-template:subagent:cancel";
|
|
9
|
+
|
|
10
|
+
export interface PromptTemplateDelegationTask {
|
|
11
|
+
agent: string;
|
|
12
|
+
task: string;
|
|
13
|
+
model?: string;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PromptTemplateDelegationParallelResult {
|
|
18
|
+
agent: string;
|
|
19
|
+
messages: unknown[];
|
|
20
|
+
isError: boolean;
|
|
21
|
+
errorText?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PromptTemplateDelegationRequest {
|
|
25
|
+
requestId: string;
|
|
26
|
+
agent: string;
|
|
27
|
+
task: string;
|
|
28
|
+
tasks?: PromptTemplateDelegationTask[];
|
|
29
|
+
context: "fresh" | "fork";
|
|
30
|
+
model: string;
|
|
31
|
+
cwd: string;
|
|
32
|
+
worktree?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PromptTemplateDelegationResponse extends PromptTemplateDelegationRequest {
|
|
36
|
+
messages: unknown[];
|
|
37
|
+
parallelResults?: PromptTemplateDelegationParallelResult[];
|
|
38
|
+
contentText?: string;
|
|
39
|
+
isError: boolean;
|
|
40
|
+
errorText?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PromptTemplateDelegationTaskProgress {
|
|
44
|
+
index?: number;
|
|
45
|
+
agent: string;
|
|
46
|
+
status?: string;
|
|
47
|
+
currentTool?: string;
|
|
48
|
+
currentToolArgs?: string;
|
|
49
|
+
recentOutput?: string;
|
|
50
|
+
recentOutputLines?: string[];
|
|
51
|
+
recentTools?: Array<{ tool: string; args: string }>;
|
|
52
|
+
model?: string;
|
|
53
|
+
toolCount?: number;
|
|
54
|
+
durationMs?: number;
|
|
55
|
+
tokens?: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface PromptTemplateDelegationUpdate {
|
|
59
|
+
requestId: string;
|
|
60
|
+
currentTool?: string;
|
|
61
|
+
currentToolArgs?: string;
|
|
62
|
+
recentOutput?: string;
|
|
63
|
+
recentOutputLines?: string[];
|
|
64
|
+
recentTools?: Array<{ tool: string; args: string }>;
|
|
65
|
+
model?: string;
|
|
66
|
+
toolCount?: number;
|
|
67
|
+
durationMs?: number;
|
|
68
|
+
tokens?: number;
|
|
69
|
+
taskProgress?: PromptTemplateDelegationTaskProgress[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PromptTemplateBridgeEvents {
|
|
73
|
+
on(event: string, handler: (data: unknown) => void): (() => void) | void;
|
|
74
|
+
emit(event: string, data: unknown): void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface PromptTemplateBridgeResult {
|
|
78
|
+
isError?: boolean;
|
|
79
|
+
content?: unknown;
|
|
80
|
+
details?: {
|
|
81
|
+
results?: Array<{
|
|
82
|
+
agent?: string;
|
|
83
|
+
messages?: unknown[];
|
|
84
|
+
finalOutput?: string;
|
|
85
|
+
exitCode?: number;
|
|
86
|
+
error?: string;
|
|
87
|
+
model?: string;
|
|
88
|
+
}>;
|
|
89
|
+
progress?: Array<{
|
|
90
|
+
index?: number;
|
|
91
|
+
agent?: string;
|
|
92
|
+
status?: string;
|
|
93
|
+
currentTool?: string;
|
|
94
|
+
currentToolArgs?: string;
|
|
95
|
+
recentOutput?: string[];
|
|
96
|
+
recentTools?: Array<{ tool?: string; args?: string }>;
|
|
97
|
+
toolCount?: number;
|
|
98
|
+
durationMs?: number;
|
|
99
|
+
tokens?: number;
|
|
100
|
+
}>;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface PromptTemplateBridgeOptions<Ctx extends { cwd?: string }> {
|
|
105
|
+
events: PromptTemplateBridgeEvents;
|
|
106
|
+
getContext: () => Ctx | null;
|
|
107
|
+
execute: (
|
|
108
|
+
requestId: string,
|
|
109
|
+
request: PromptTemplateDelegationRequest,
|
|
110
|
+
signal: AbortSignal,
|
|
111
|
+
ctx: Ctx,
|
|
112
|
+
onUpdate: (result: PromptTemplateBridgeResult) => void,
|
|
113
|
+
) => Promise<PromptTemplateBridgeResult>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseDelegationTasks(tasks: unknown): PromptTemplateDelegationTask[] {
|
|
117
|
+
if (!Array.isArray(tasks)) return [];
|
|
118
|
+
const parsed: PromptTemplateDelegationTask[] = [];
|
|
119
|
+
for (const item of tasks) {
|
|
120
|
+
if (!item || typeof item !== "object") return [];
|
|
121
|
+
const value = item as Partial<PromptTemplateDelegationTask>;
|
|
122
|
+
if (typeof value.agent !== "string" || !value.agent.trim()) return [];
|
|
123
|
+
if (typeof value.task !== "string" || !value.task.trim()) return [];
|
|
124
|
+
const model = typeof value.model === "string" && value.model.trim().length > 0 ? value.model : undefined;
|
|
125
|
+
const cwd = typeof value.cwd === "string" && value.cwd.trim().length > 0 ? value.cwd : undefined;
|
|
126
|
+
parsed.push({
|
|
127
|
+
agent: value.agent,
|
|
128
|
+
task: value.task,
|
|
129
|
+
...(model ? { model } : {}),
|
|
130
|
+
...(cwd ? { cwd } : {}),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return parsed;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function parsePromptTemplateRequest(data: unknown): PromptTemplateDelegationRequest | undefined {
|
|
137
|
+
if (!data || typeof data !== "object") return undefined;
|
|
138
|
+
const value = data as Partial<PromptTemplateDelegationRequest> & { tasks?: unknown };
|
|
139
|
+
if (typeof value.requestId !== "string" || !value.requestId) return undefined;
|
|
140
|
+
if (typeof value.model !== "string" || !value.model) return undefined;
|
|
141
|
+
if (typeof value.cwd !== "string" || !value.cwd) return undefined;
|
|
142
|
+
if (value.context !== "fresh" && value.context !== "fork") return undefined;
|
|
143
|
+
const tasks = parseDelegationTasks(value.tasks);
|
|
144
|
+
const worktree = value.worktree === true ? true : undefined;
|
|
145
|
+
const hasSingle =
|
|
146
|
+
typeof value.agent === "string" &&
|
|
147
|
+
value.agent.length > 0 &&
|
|
148
|
+
typeof value.task === "string" &&
|
|
149
|
+
value.task.length > 0;
|
|
150
|
+
if (!hasSingle && tasks.length === 0) return undefined;
|
|
151
|
+
|
|
152
|
+
const fallbackTask = tasks[0];
|
|
153
|
+
return {
|
|
154
|
+
requestId: value.requestId,
|
|
155
|
+
agent: hasSingle ? value.agent : fallbackTask!.agent,
|
|
156
|
+
task: hasSingle ? value.task : fallbackTask!.task,
|
|
157
|
+
...(tasks.length > 0 ? { tasks } : {}),
|
|
158
|
+
context: value.context,
|
|
159
|
+
model: value.model,
|
|
160
|
+
cwd: value.cwd,
|
|
161
|
+
...(worktree ? { worktree } : {}),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function firstTextContent(content: unknown): string | undefined {
|
|
166
|
+
if (!Array.isArray(content)) return undefined;
|
|
167
|
+
for (const part of content) {
|
|
168
|
+
if (!part || typeof part !== "object") continue;
|
|
169
|
+
if ((part as { type?: string }).type !== "text") continue;
|
|
170
|
+
const text = (part as { text?: unknown }).text;
|
|
171
|
+
if (typeof text === "string" && text.trim()) return text.trim();
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function filterRecentOutput(lines: string[] | undefined): string[] | undefined {
|
|
177
|
+
if (!lines || lines.length === 0) return undefined;
|
|
178
|
+
const filtered = lines.filter((line) => typeof line === "string" && line.trim() && line.trim() !== "(running...)");
|
|
179
|
+
if (filtered.length === 0) return undefined;
|
|
180
|
+
return filtered;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function sanitizeRecentTools(
|
|
184
|
+
tools: Array<{ tool?: string; args?: string }> | undefined,
|
|
185
|
+
): Array<{ tool: string; args: string }> | undefined {
|
|
186
|
+
if (!tools || tools.length === 0) return undefined;
|
|
187
|
+
const sanitized = tools.flatMap((entry) => {
|
|
188
|
+
if (typeof entry.tool !== "string" || entry.tool.trim().length === 0) return [];
|
|
189
|
+
return [{
|
|
190
|
+
tool: entry.tool,
|
|
191
|
+
args: typeof entry.args === "string" ? entry.args : String(entry.args ?? ""),
|
|
192
|
+
}];
|
|
193
|
+
});
|
|
194
|
+
return sanitized.length > 0 ? sanitized : undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function resolveProgressModel(
|
|
198
|
+
update: PromptTemplateBridgeResult,
|
|
199
|
+
entry: { index?: number; agent?: string },
|
|
200
|
+
): string | undefined {
|
|
201
|
+
const results = update.details?.results;
|
|
202
|
+
if (!results || results.length === 0) return undefined;
|
|
203
|
+
if (typeof entry.index === "number" && entry.index >= 0) {
|
|
204
|
+
const byIndex = results[entry.index];
|
|
205
|
+
if (typeof byIndex?.model === "string") return byIndex.model;
|
|
206
|
+
}
|
|
207
|
+
if (entry.agent) {
|
|
208
|
+
const byAgent = results.find((result) => result.agent === entry.agent && typeof result.model === "string");
|
|
209
|
+
if (byAgent?.model) return byAgent.model;
|
|
210
|
+
}
|
|
211
|
+
const firstWithModel = results.find((result) => typeof result.model === "string");
|
|
212
|
+
return firstWithModel?.model;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function buildDelegationMessages(result: { messages?: unknown[]; finalOutput?: string }, fallbackText?: string): unknown[] {
|
|
216
|
+
if (Array.isArray(result.messages) && result.messages.length > 0) return result.messages;
|
|
217
|
+
const text = typeof result.finalOutput === "string" && result.finalOutput.trim().length > 0
|
|
218
|
+
? result.finalOutput.trim()
|
|
219
|
+
: fallbackText;
|
|
220
|
+
if (!text) return [];
|
|
221
|
+
return [{ role: "assistant", content: [{ type: "text", text }] }];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function toDelegationUpdate(requestId: string, update: PromptTemplateBridgeResult): PromptTemplateDelegationUpdate | undefined {
|
|
225
|
+
const progress = update.details?.progress?.[0];
|
|
226
|
+
const taskProgress = update.details?.progress?.map((entry) => {
|
|
227
|
+
const lastOutput = entry.recentOutput?.[entry.recentOutput.length - 1];
|
|
228
|
+
const safeLastOutput =
|
|
229
|
+
typeof lastOutput === "string" && lastOutput.trim() && lastOutput !== "(running...)"
|
|
230
|
+
? lastOutput
|
|
231
|
+
: undefined;
|
|
232
|
+
return {
|
|
233
|
+
index: entry.index,
|
|
234
|
+
agent: entry.agent ?? "delegate",
|
|
235
|
+
status: entry.status,
|
|
236
|
+
currentTool: entry.currentTool,
|
|
237
|
+
currentToolArgs: entry.currentToolArgs,
|
|
238
|
+
recentOutput: safeLastOutput,
|
|
239
|
+
recentOutputLines: filterRecentOutput(entry.recentOutput),
|
|
240
|
+
recentTools: sanitizeRecentTools(entry.recentTools),
|
|
241
|
+
model: resolveProgressModel(update, entry),
|
|
242
|
+
toolCount: entry.toolCount,
|
|
243
|
+
durationMs: entry.durationMs,
|
|
244
|
+
tokens: entry.tokens,
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
if (!progress && (!taskProgress || taskProgress.length === 0)) return undefined;
|
|
248
|
+
const lastOutput = progress?.recentOutput?.[progress.recentOutput.length - 1];
|
|
249
|
+
const safeLastOutput =
|
|
250
|
+
typeof lastOutput === "string" && lastOutput.trim() && lastOutput !== "(running...)"
|
|
251
|
+
? lastOutput
|
|
252
|
+
: undefined;
|
|
253
|
+
return {
|
|
254
|
+
requestId,
|
|
255
|
+
currentTool: progress?.currentTool,
|
|
256
|
+
currentToolArgs: progress?.currentToolArgs,
|
|
257
|
+
recentOutput: safeLastOutput,
|
|
258
|
+
recentOutputLines: filterRecentOutput(progress?.recentOutput),
|
|
259
|
+
recentTools: sanitizeRecentTools(progress?.recentTools),
|
|
260
|
+
model: progress ? resolveProgressModel(update, progress) : undefined,
|
|
261
|
+
toolCount: progress?.toolCount,
|
|
262
|
+
durationMs: progress?.durationMs,
|
|
263
|
+
tokens: progress?.tokens,
|
|
264
|
+
taskProgress,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function registerPromptTemplateDelegationBridge<Ctx extends { cwd?: string }>(
|
|
269
|
+
options: PromptTemplateBridgeOptions<Ctx>,
|
|
270
|
+
): {
|
|
271
|
+
cancelAll: () => void;
|
|
272
|
+
dispose: () => void;
|
|
273
|
+
} {
|
|
274
|
+
const controllers = new Map<string, AbortController>();
|
|
275
|
+
const pendingCancels = new Set<string>();
|
|
276
|
+
const subscriptions: Array<() => void> = [];
|
|
277
|
+
|
|
278
|
+
const subscribe = (event: string, handler: (data: unknown) => void): void => {
|
|
279
|
+
const unsubscribe = options.events.on(event, handler);
|
|
280
|
+
if (typeof unsubscribe === "function") subscriptions.push(unsubscribe);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
subscribe(PROMPT_TEMPLATE_SUBAGENT_CANCEL_EVENT, (data) => {
|
|
284
|
+
if (!data || typeof data !== "object") return;
|
|
285
|
+
const requestId = (data as { requestId?: unknown }).requestId;
|
|
286
|
+
if (typeof requestId !== "string") return;
|
|
287
|
+
const controller = controllers.get(requestId);
|
|
288
|
+
if (controller) {
|
|
289
|
+
controller.abort();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
pendingCancels.add(requestId);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
subscribe(PROMPT_TEMPLATE_SUBAGENT_REQUEST_EVENT, async (data) => {
|
|
296
|
+
const request = parsePromptTemplateRequest(data);
|
|
297
|
+
if (!request) return;
|
|
298
|
+
|
|
299
|
+
const ctx = options.getContext();
|
|
300
|
+
if (!ctx) {
|
|
301
|
+
const response: PromptTemplateDelegationResponse = {
|
|
302
|
+
...request,
|
|
303
|
+
messages: [],
|
|
304
|
+
isError: true,
|
|
305
|
+
errorText: "No active extension context for delegated subagent execution.",
|
|
306
|
+
};
|
|
307
|
+
options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const controller = new AbortController();
|
|
312
|
+
controllers.set(request.requestId, controller);
|
|
313
|
+
|
|
314
|
+
if (pendingCancels.delete(request.requestId)) {
|
|
315
|
+
controller.abort();
|
|
316
|
+
const response: PromptTemplateDelegationResponse = {
|
|
317
|
+
...request,
|
|
318
|
+
messages: [],
|
|
319
|
+
isError: true,
|
|
320
|
+
errorText: "Delegated prompt cancelled.",
|
|
321
|
+
};
|
|
322
|
+
options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
|
|
323
|
+
controllers.delete(request.requestId);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
options.events.emit(PROMPT_TEMPLATE_SUBAGENT_STARTED_EVENT, { requestId: request.requestId });
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
const result = await options.execute(
|
|
331
|
+
request.requestId,
|
|
332
|
+
request,
|
|
333
|
+
controller.signal,
|
|
334
|
+
ctx,
|
|
335
|
+
(update) => {
|
|
336
|
+
const payload = toDelegationUpdate(request.requestId, update);
|
|
337
|
+
if (!payload) return;
|
|
338
|
+
options.events.emit(PROMPT_TEMPLATE_SUBAGENT_UPDATE_EVENT, payload);
|
|
339
|
+
},
|
|
340
|
+
);
|
|
341
|
+
const contentText = firstTextContent(result.content);
|
|
342
|
+
const messages = buildDelegationMessages(result.details?.results?.[0] ?? {}, contentText);
|
|
343
|
+
const parallelResults = request.tasks
|
|
344
|
+
? request.tasks.map<PromptTemplateDelegationParallelResult>((task, index) => {
|
|
345
|
+
const step = result.details?.results?.[index];
|
|
346
|
+
if (!step) {
|
|
347
|
+
return {
|
|
348
|
+
agent: task.agent,
|
|
349
|
+
messages: [],
|
|
350
|
+
isError: true,
|
|
351
|
+
errorText: "Missing result for delegated parallel task.",
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const exitCode = typeof step.exitCode === "number" ? step.exitCode : undefined;
|
|
355
|
+
const errorText = step.error;
|
|
356
|
+
return {
|
|
357
|
+
agent: step.agent ?? task.agent,
|
|
358
|
+
messages: buildDelegationMessages(step),
|
|
359
|
+
isError: (exitCode !== undefined && exitCode !== 0) || !!errorText,
|
|
360
|
+
errorText: errorText || undefined,
|
|
361
|
+
};
|
|
362
|
+
})
|
|
363
|
+
: undefined;
|
|
364
|
+
const response: PromptTemplateDelegationResponse = {
|
|
365
|
+
...request,
|
|
366
|
+
messages,
|
|
367
|
+
...(parallelResults ? { parallelResults } : {}),
|
|
368
|
+
...(contentText ? { contentText } : {}),
|
|
369
|
+
isError: result.isError === true,
|
|
370
|
+
errorText: result.isError ? contentText : undefined,
|
|
371
|
+
};
|
|
372
|
+
options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
const response: PromptTemplateDelegationResponse = {
|
|
375
|
+
...request,
|
|
376
|
+
messages: [],
|
|
377
|
+
isError: true,
|
|
378
|
+
errorText: error instanceof Error ? error.message : String(error),
|
|
379
|
+
};
|
|
380
|
+
options.events.emit(PROMPT_TEMPLATE_SUBAGENT_RESPONSE_EVENT, response);
|
|
381
|
+
} finally {
|
|
382
|
+
controllers.delete(request.requestId);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
cancelAll: () => {
|
|
388
|
+
for (const controller of controllers.values()) {
|
|
389
|
+
controller.abort();
|
|
390
|
+
}
|
|
391
|
+
controllers.clear();
|
|
392
|
+
pendingCancels.clear();
|
|
393
|
+
},
|
|
394
|
+
dispose: () => {
|
|
395
|
+
for (const unsubscribe of subscriptions) unsubscribe();
|
|
396
|
+
subscriptions.length = 0;
|
|
397
|
+
pendingCancels.clear();
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use subagents to gather context, then ask clarifying questions
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Based on our discussion and my intent, launch focused context-gathering subagents before planning or implementing.
|
|
6
|
+
|
|
7
|
+
Use `scout` to inspect the relevant local files, existing patterns, constraints, tests, and likely integration points. Use `researcher` when external docs, recent sources, ecosystem context, or primary evidence would improve the answer.
|
|
8
|
+
|
|
9
|
+
Give each subagent a specific meta prompt. Ask them to return concise findings plus the remaining clarification questions that matter for implementation confidence.
|
|
10
|
+
|
|
11
|
+
After they return, synthesize what we know and use the `interview` tool to ask me the unresolved questions needed to reach a shared understanding.
|
|
12
|
+
|
|
13
|
+
$@
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Parallel cleanup review
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Launch two fresh-context reviewer subagents for an adversarial cleanup review of the current work. Reviewers must inspect the repository, relevant instructions, and current diff directly from files and commands. Do not rely on this prompt as a substitute for reading the code.
|
|
6
|
+
|
|
7
|
+
Reviewer 1: deslop pass.
|
|
8
|
+
|
|
9
|
+
Ask this reviewer to look for AI-slop patterns in the changed scope:
|
|
10
|
+
- comments that restate code, placeholder text, stale rationale, or debug leftovers;
|
|
11
|
+
- defensive checks that hide useful errors, return vague defaults, or validate trusted internal data after a real boundary was already crossed;
|
|
12
|
+
- type escapes, broad casts, duplicated type definitions, or object-bag typing where a local source-of-truth type exists;
|
|
13
|
+
- style drift from nearby non-slop code and project instructions;
|
|
14
|
+
- generated-sounding docs, changelog text, UI copy, status text, or test names;
|
|
15
|
+
- pass-through wrappers, dead helpers, duplicate helper signatures, duplicated test harness setup, or abstractions that do not enforce an invariant;
|
|
16
|
+
- UI or CLI copy that is noisy, vague, brittle, or makes the user do extra interpretation.
|
|
17
|
+
|
|
18
|
+
Tell this reviewer to treat tool output and slop-scan-style findings as leads, not verdicts. It should flag only concrete issues in the requested scope with evidence, severity, file/line references, and the smallest safe fix.
|
|
19
|
+
|
|
20
|
+
Reviewer 2: verbosity pass.
|
|
21
|
+
|
|
22
|
+
Ask this reviewer to look for needless verbosity in code, tests, docs, status text, grouped messages, receipts, and changelog wording:
|
|
23
|
+
- single-use helpers that merely paraphrase an expression;
|
|
24
|
+
- temporary variables that only name obvious expressions;
|
|
25
|
+
- nested returns or branches that can become direct returns without hiding intent;
|
|
26
|
+
- multi-line cleanup scaffolding that can use a local direct pattern while preserving cleanup semantics;
|
|
27
|
+
- repeated boilerplate that can use an existing local fixture or a small local helper;
|
|
28
|
+
- tests that restate formatter details already covered at a cheaper layer;
|
|
29
|
+
- prose that says the same thing twice, sounds generic, or buries the important rule.
|
|
30
|
+
|
|
31
|
+
Tell this reviewer that shorter is only better when it is clearer and preserves behavior, error signals, cleanup semantics, useful invariants, and local style.
|
|
32
|
+
|
|
33
|
+
Both reviewers are review-only. They must not edit files unless I explicitly ask for a writer pass. Their response should be review feedback, not a context summary. Ask them to return concise, evidence-backed findings with file/line references and suggested fixes.
|
|
34
|
+
|
|
35
|
+
While reviewers run, do your own narrow inspection if useful. After they return, synthesize the feedback into:
|
|
36
|
+
- fixes worth doing now;
|
|
37
|
+
- optional improvements;
|
|
38
|
+
- feedback to ignore or defer, with a short reason.
|
|
39
|
+
|
|
40
|
+
Do not blindly apply every reviewer suggestion. Ask before applying fixes unless I already told you to address review feedback.
|
|
41
|
+
|
|
42
|
+
$@
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Parallel subagents research
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Launch parallel research subagents to build a grounded answer to the current question or decision.
|
|
6
|
+
|
|
7
|
+
Use fresh context, not forked context, unless I explicitly ask for forked context. Researchers and scouts should inspect sources directly instead of relying on the main conversation history.
|
|
8
|
+
|
|
9
|
+
Use a combination of `researcher` and `scout` subagents:
|
|
10
|
+
- Use `researcher` for web, docs, standards, ecosystem, recent changes, benchmarks, and primary-source evidence.
|
|
11
|
+
- Use `scout` for local codebase context, existing implementation patterns, repo constraints, and files that would be affected.
|
|
12
|
+
|
|
13
|
+
Give each subagent a distinct angle. Unless I specify angles, use these three:
|
|
14
|
+
|
|
15
|
+
1. External evidence
|
|
16
|
+
Use `researcher` to find current, authoritative sources: official docs, specs, release notes, benchmarks, issue threads, or primary explanations.
|
|
17
|
+
|
|
18
|
+
2. Local code context
|
|
19
|
+
Use `scout` to inspect the repository for relevant files, existing patterns, constraints, tests, and likely integration points.
|
|
20
|
+
|
|
21
|
+
3. Practical tradeoffs
|
|
22
|
+
Use `researcher` or `scout`, whichever fits the question, to compare options, risks, edge cases, maintenance cost, and what would be easiest to validate.
|
|
23
|
+
|
|
24
|
+
Adapt the angles when the question calls for it:
|
|
25
|
+
- Library/API questions: include official docs and recent examples.
|
|
26
|
+
- Architecture decisions: include local module boundaries, dependency direction, and migration cost.
|
|
27
|
+
- Debugging questions: include likely failure modes, local call paths, and exact error evidence.
|
|
28
|
+
- UI/product questions: include user flow, accessibility, design precedent, and implementation constraints.
|
|
29
|
+
- Time-sensitive topics: include a recent-developments angle and prefer 2026/2025 sources.
|
|
30
|
+
|
|
31
|
+
Prefer two or three strong subagents over many vague ones. The parent agent should frame the question and assign angles; the child agents should research or scout, not invent broad plans.
|
|
32
|
+
|
|
33
|
+
Ask each subagent to return concise findings with evidence:
|
|
34
|
+
- file paths and line ranges for local findings
|
|
35
|
+
- source links for external findings
|
|
36
|
+
- confidence level and gaps
|
|
37
|
+
- recommended next step or decision implication
|
|
38
|
+
|
|
39
|
+
Do not ask subagents to edit files. This is a research pass only unless I explicitly ask for implementation.
|
|
40
|
+
|
|
41
|
+
After the subagents return, synthesize the answer into:
|
|
42
|
+
- what we know
|
|
43
|
+
- what the local codebase implies
|
|
44
|
+
- tradeoffs and risks
|
|
45
|
+
- gaps or assumptions
|
|
46
|
+
- the recommended next move
|
|
47
|
+
|
|
48
|
+
If findings disagree, call out the disagreement instead of smoothing it over.
|
|
49
|
+
|
|
50
|
+
$@
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Parallel subagents review
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Launch parallel reviewers for an adversarial review of the current work.
|
|
6
|
+
|
|
7
|
+
Use fresh context, not forked context, unless I explicitly ask for forked context. Reviewers should inspect the repository, relevant instructions, and current diff directly from files and commands. Do not rely on the main conversation history.
|
|
8
|
+
|
|
9
|
+
Give each reviewer a distinct angle. Generate the angles dynamically from the user's intent, the plan, the implemented code, and the current diff. If I specify angles, use mine. Otherwise, choose the highest-value review angles for this specific work.
|
|
10
|
+
|
|
11
|
+
These are examples, not fixed defaults:
|
|
12
|
+
|
|
13
|
+
1. Correctness and regressions
|
|
14
|
+
Check whether the change satisfies the request, preserves existing behavior, handles edge cases, and avoids hidden runtime failures.
|
|
15
|
+
|
|
16
|
+
2. Tests and validation
|
|
17
|
+
Check whether tests or validation were added at the right layer, whether assertions are meaningful, and whether the chosen verification commands are enough.
|
|
18
|
+
|
|
19
|
+
3. Simplicity and maintainability
|
|
20
|
+
Check for unnecessary complexity, duplicate structure, single-use wrappers, brittle abstractions, confusing names, verbosity, and cleanup that is clearly worth doing.
|
|
21
|
+
|
|
22
|
+
Choose or adapt angles when the work calls for it:
|
|
23
|
+
- TypeScript-heavy changes: include type safety, source-of-truth types, casts, and error-boundary discipline.
|
|
24
|
+
- UI-heavy changes: include UX, accessibility, copy, and visual quality.
|
|
25
|
+
- Security-sensitive changes: include unsafe input/output handling, auth boundaries, privacy, and data exposure.
|
|
26
|
+
- Docs-heavy changes: include clarity, accuracy, completeness, reader flow, and non-robotic prose.
|
|
27
|
+
- Large multi-file changes: consider a fourth reviewer for structural friction, module boundaries, and testability.
|
|
28
|
+
|
|
29
|
+
Prefer three strong reviewers over many vague reviewers.
|
|
30
|
+
|
|
31
|
+
Give every reviewer a specific task prompt naming its angle. Ask reviewers to return concise, evidence-backed findings with file/line references and suggested fixes. The response should be review feedback, not a context summary. Reviewers must not edit files unless I explicitly ask for a writer pass.
|
|
32
|
+
|
|
33
|
+
While reviewers run, do your own narrow inspection if useful. After they return, synthesize the feedback into:
|
|
34
|
+
- fixes worth doing now
|
|
35
|
+
- optional improvements
|
|
36
|
+
- feedback to ignore or defer, with a short reason
|
|
37
|
+
|
|
38
|
+
Do not blindly apply every reviewer suggestion. Ask before applying fixes unless I already told you to address review feedback.
|
|
39
|
+
|
|
40
|
+
$@
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Nico Bailon
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Source: https://github.com/nicobailon/pi-subagents
|
|
4
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
6
|
+
|
|
7
|
+
function fuzzyScore(query: string, text: string): number {
|
|
8
|
+
const lq = query.toLowerCase();
|
|
9
|
+
const lt = text.toLowerCase();
|
|
10
|
+
if (lt.includes(lq)) return 100 + (lq.length / lt.length) * 50;
|
|
11
|
+
let score = 0;
|
|
12
|
+
let qi = 0;
|
|
13
|
+
let consecutive = 0;
|
|
14
|
+
for (let i = 0; i < lt.length && qi < lq.length; i++) {
|
|
15
|
+
if (lt[i] === lq[qi]) {
|
|
16
|
+
score += 10 + consecutive;
|
|
17
|
+
consecutive += 5;
|
|
18
|
+
qi++;
|
|
19
|
+
} else {
|
|
20
|
+
consecutive = 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return qi === lq.length ? score : 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function fuzzyFilter<T extends { name: string; description: string; model?: string }>(items: T[], query: string): T[] {
|
|
27
|
+
const q = query.trim();
|
|
28
|
+
if (!q) return items;
|
|
29
|
+
return items
|
|
30
|
+
.map((item) => ({ item, score: Math.max(fuzzyScore(q, item.name), fuzzyScore(q, item.description) * 0.8, fuzzyScore(q, item.model ?? "") * 0.6) }))
|
|
31
|
+
.filter((x) => x.score > 0)
|
|
32
|
+
.sort((a, b) => b.score - a.score)
|
|
33
|
+
.map((x) => x.item);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function pad(s: string, len: number): string {
|
|
37
|
+
const vis = visibleWidth(s);
|
|
38
|
+
return s + " ".repeat(Math.max(0, len - vis));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function row(content: string, width: number, theme: Theme): string {
|
|
42
|
+
const innerW = width - 2;
|
|
43
|
+
const singleLine = content.replace(/[\r\n]+/g, " ").replace(/\t/g, " ");
|
|
44
|
+
const clipped = truncateToWidth(singleLine, innerW);
|
|
45
|
+
return theme.fg("border", "│") + pad(clipped, innerW) + theme.fg("border", "│");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function renderHeader(text: string, width: number, theme: Theme): string {
|
|
49
|
+
const innerW = width - 2;
|
|
50
|
+
const padLen = Math.max(0, innerW - visibleWidth(text));
|
|
51
|
+
const padLeft = Math.floor(padLen / 2);
|
|
52
|
+
const padRight = padLen - padLeft;
|
|
53
|
+
return (
|
|
54
|
+
theme.fg("border", "╭" + "─".repeat(padLeft)) +
|
|
55
|
+
theme.fg("accent", text) +
|
|
56
|
+
theme.fg("border", "─".repeat(padRight) + "╮")
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function formatPath(filePath: string): string {
|
|
61
|
+
const home = process.env.HOME;
|
|
62
|
+
if (home && filePath.startsWith(home)) return `~${filePath.slice(home.length)}`;
|
|
63
|
+
return filePath;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function formatScrollInfo(above: number, below: number): string {
|
|
67
|
+
let info = "";
|
|
68
|
+
if (above > 0) info += `↑ ${above} more`;
|
|
69
|
+
if (below > 0) info += `${info ? " " : ""}↓ ${below} more`;
|
|
70
|
+
return info;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function renderFooter(text: string, width: number, theme: Theme): string {
|
|
74
|
+
const innerW = width - 2;
|
|
75
|
+
const padLen = Math.max(0, innerW - visibleWidth(text));
|
|
76
|
+
const padLeft = Math.floor(padLen / 2);
|
|
77
|
+
const padRight = padLen - padLeft;
|
|
78
|
+
return (
|
|
79
|
+
theme.fg("border", "╰" + "─".repeat(padLeft)) +
|
|
80
|
+
theme.fg("dim", text) +
|
|
81
|
+
theme.fg("border", "─".repeat(padRight) + "╯")
|
|
82
|
+
);
|
|
83
|
+
}
|