pi-subagents 0.18.0 → 0.19.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/CHANGELOG.md +24 -0
- package/README.md +4 -4
- package/agent-management.ts +11 -2
- package/agent-manager-chain-detail.ts +50 -6
- package/agent-manager-detail.ts +15 -2
- package/agent-manager.ts +76 -23
- package/async-execution.ts +47 -20
- package/chain-execution.ts +12 -2
- package/execution.ts +8 -10
- package/index.ts +104 -11
- package/intercom-bridge.ts +3 -3
- package/notify.ts +20 -10
- package/package.json +5 -1
- package/pi-args.ts +1 -0
- package/prompts/parallel-review.md +8 -0
- package/render.ts +371 -57
- package/schemas.ts +52 -14
- package/settings.ts +5 -0
- package/skills.ts +61 -0
- package/slash-commands.ts +82 -39
- package/slash-live-state.ts +3 -5
- package/subagent-executor.ts +110 -24
- package/subagent-runner.ts +8 -0
- package/subagents-status.ts +216 -2
- package/worktree.ts +19 -10
package/execution.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import type { Message } from "@mariozechner/pi-ai";
|
|
7
8
|
import type { AgentConfig } from "./agents.ts";
|
|
8
9
|
import {
|
|
@@ -242,13 +243,11 @@ async function runSingleAttempt(
|
|
|
242
243
|
};
|
|
243
244
|
|
|
244
245
|
const unsubscribeIntercomDetach = options.intercomEvents?.on?.(INTERCOM_DETACH_REQUEST_EVENT, (payload) => {
|
|
245
|
-
if (!options.allowIntercomDetach || detached || processClosed) return;
|
|
246
|
+
if (!options.allowIntercomDetach || detached || processClosed || !intercomStarted) return;
|
|
246
247
|
if (!payload || typeof payload !== "object") return;
|
|
247
248
|
const requestId = (payload as { requestId?: unknown }).requestId;
|
|
248
249
|
if (typeof requestId !== "string" || requestId.length === 0) return;
|
|
249
|
-
|
|
250
|
-
options.intercomEvents?.emit(INTERCOM_DETACH_RESPONSE_EVENT, { requestId, accepted });
|
|
251
|
-
if (!accepted) return;
|
|
250
|
+
options.intercomEvents?.emit(INTERCOM_DETACH_RESPONSE_EVENT, { requestId, accepted: true });
|
|
252
251
|
detachForIntercom();
|
|
253
252
|
});
|
|
254
253
|
|
|
@@ -726,12 +725,11 @@ export async function runSync(
|
|
|
726
725
|
if (truncationResult.truncated) result.truncation = truncationResult;
|
|
727
726
|
}
|
|
728
727
|
|
|
729
|
-
if (
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
728
|
+
if (options.sessionFile && (existsSync(options.sessionFile) || result.messages?.length)) {
|
|
729
|
+
result.sessionFile = options.sessionFile;
|
|
730
|
+
} else if (shareEnabled && options.sessionDir) {
|
|
731
|
+
const sessionFile = findLatestSessionFile(options.sessionDir);
|
|
732
|
+
if (sessionFile) result.sessionFile = sessionFile;
|
|
735
733
|
}
|
|
736
734
|
|
|
737
735
|
return result;
|
package/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWi
|
|
|
21
21
|
import { discoverAgents } from "./agents.ts";
|
|
22
22
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "./artifacts.ts";
|
|
23
23
|
import { cleanupOldChainDirs } from "./settings.ts";
|
|
24
|
-
import { renderWidget, renderSubagentResult } from "./render.ts";
|
|
24
|
+
import { renderWidget, renderSubagentResult, stopResultAnimations, stopWidgetAnimation, syncResultAnimation } from "./render.ts";
|
|
25
25
|
import { SubagentParams } from "./schemas.ts";
|
|
26
26
|
import { createSubagentExecutor } from "./subagent-executor.ts";
|
|
27
27
|
import { createAsyncJobTracker } from "./async-job-tracker.ts";
|
|
@@ -32,7 +32,8 @@ import { registerPromptTemplateDelegationBridge } from "./prompt-template-bridge
|
|
|
32
32
|
import { registerSlashSubagentBridge } from "./slash-bridge.ts";
|
|
33
33
|
import { clearSlashSnapshots, getSlashRenderableSnapshot, resolveSlashMessageDetails, restoreSlashFinalSnapshots, type SlashMessageDetails } from "./slash-live-state.ts";
|
|
34
34
|
import { inspectSubagentStatus } from "./run-status.ts";
|
|
35
|
-
import registerSubagentNotify from "./notify.ts";
|
|
35
|
+
import registerSubagentNotify, { type SubagentNotifyDetails } from "./notify.ts";
|
|
36
|
+
import { formatDuration, shortenPath } from "./formatters.ts";
|
|
36
37
|
import {
|
|
37
38
|
type ControlEvent,
|
|
38
39
|
type Details,
|
|
@@ -130,12 +131,15 @@ function createSlashResultComponent(
|
|
|
130
131
|
details: SlashMessageDetails,
|
|
131
132
|
options: { expanded: boolean },
|
|
132
133
|
theme: ExtensionContext["ui"]["theme"],
|
|
134
|
+
requestRender: () => void,
|
|
133
135
|
): Container {
|
|
134
136
|
const container = new Container();
|
|
137
|
+
const animationState: { subagentResultAnimationTimer?: ReturnType<typeof setInterval> } = {};
|
|
135
138
|
let lastVersion = -1;
|
|
136
139
|
container.render = (width: number): string[] => {
|
|
137
140
|
const snapshot = getSlashRenderableSnapshot(details);
|
|
138
|
-
|
|
141
|
+
syncResultAnimation(snapshot.result, { state: animationState, invalidate: requestRender });
|
|
142
|
+
if (snapshot.version !== lastVersion || isSlashResultRunning(snapshot.result)) {
|
|
139
143
|
lastVersion = snapshot.version;
|
|
140
144
|
rebuildSlashResultContainer(container, snapshot.result, options, theme);
|
|
141
145
|
}
|
|
@@ -162,6 +166,38 @@ function formatSubagentControlNotice(details: SubagentControlMessageDetails, con
|
|
|
162
166
|
return details.noticeText ?? content ?? formatControlNoticeMessage(details.event, controlNoticeTarget(details));
|
|
163
167
|
}
|
|
164
168
|
|
|
169
|
+
function parseSubagentNotifyContent(content: string): SubagentNotifyDetails | undefined {
|
|
170
|
+
const lines = content.split("\n");
|
|
171
|
+
const header = lines[0] ?? "";
|
|
172
|
+
const match = header.match(/^Background task (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
173
|
+
if (!match) return undefined;
|
|
174
|
+
const body = lines.slice(2);
|
|
175
|
+
let sessionIndex = -1;
|
|
176
|
+
for (let i = body.length - 1; i >= 1; i--) {
|
|
177
|
+
if (body[i - 1]?.trim() === "" && /^(Session|Session file|Session share error):\s+/.test(body[i]!)) {
|
|
178
|
+
sessionIndex = i;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const sessionLine = sessionIndex >= 0 ? body[sessionIndex] : undefined;
|
|
183
|
+
const resultLines = sessionIndex >= 0 ? body.slice(0, sessionIndex) : body;
|
|
184
|
+
const resultPreview = resultLines.join("\n").trim() || "(no output)";
|
|
185
|
+
let sessionLabel: string | undefined;
|
|
186
|
+
let sessionValue: string | undefined;
|
|
187
|
+
if (sessionLine) {
|
|
188
|
+
const separator = sessionLine.indexOf(":");
|
|
189
|
+
sessionLabel = sessionLine.slice(0, separator).toLowerCase();
|
|
190
|
+
sessionValue = sessionLine.slice(separator + 1).trim();
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
agent: match[2]!,
|
|
194
|
+
status: match[1] as SubagentNotifyDetails["status"],
|
|
195
|
+
...(match[3] ? { taskInfo: match[3] } : {}),
|
|
196
|
+
resultPreview,
|
|
197
|
+
...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
165
201
|
class SubagentControlNoticeComponent implements Component {
|
|
166
202
|
constructor(
|
|
167
203
|
private readonly details: SubagentControlMessageDetails,
|
|
@@ -191,6 +227,17 @@ class SubagentControlNoticeComponent implements Component {
|
|
|
191
227
|
}
|
|
192
228
|
|
|
193
229
|
export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
230
|
+
const globalStore = globalThis as Record<string, unknown>;
|
|
231
|
+
const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
|
|
232
|
+
const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
|
|
233
|
+
if (typeof previousRuntimeCleanup === "function") {
|
|
234
|
+
try {
|
|
235
|
+
previousRuntimeCleanup();
|
|
236
|
+
} catch {
|
|
237
|
+
// Best effort cleanup for stale timers from an older reload.
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
194
241
|
ensureAccessibleDir(RESULTS_DIR);
|
|
195
242
|
ensureAccessibleDir(ASYNC_DIR);
|
|
196
243
|
cleanupOldChainDirs();
|
|
@@ -227,6 +274,16 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
227
274
|
startResultWatcher();
|
|
228
275
|
primeExistingResults();
|
|
229
276
|
|
|
277
|
+
const runtimeCleanup = () => {
|
|
278
|
+
stopWidgetAnimation();
|
|
279
|
+
stopResultAnimations();
|
|
280
|
+
if (state.poller) {
|
|
281
|
+
clearInterval(state.poller);
|
|
282
|
+
state.poller = null;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
|
|
286
|
+
|
|
230
287
|
const { ensurePoller, handleStarted, handleComplete, resetJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
|
|
231
288
|
const executor = createSubagentExecutor({
|
|
232
289
|
pi,
|
|
@@ -242,7 +299,37 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
242
299
|
pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
|
|
243
300
|
const details = resolveSlashMessageDetails(message.details);
|
|
244
301
|
if (!details) return undefined;
|
|
245
|
-
return createSlashResultComponent(details, options, theme);
|
|
302
|
+
return createSlashResultComponent(details, options, theme, () => state.lastUiContext?.ui.requestRender?.());
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
pi.registerMessageRenderer<SubagentNotifyDetails>("subagent-notify", (message, options, theme) => {
|
|
306
|
+
const content = typeof message.content === "string" ? message.content : "";
|
|
307
|
+
const details = (message.details as SubagentNotifyDetails | undefined) ?? parseSubagentNotifyContent(content);
|
|
308
|
+
if (!details) return new Text(content, 0, 0);
|
|
309
|
+
const icon = details.status === "completed"
|
|
310
|
+
? theme.fg("success", "✓")
|
|
311
|
+
: details.status === "paused"
|
|
312
|
+
? theme.fg("warning", "■")
|
|
313
|
+
: theme.fg("error", "✗");
|
|
314
|
+
const parts: string[] = [];
|
|
315
|
+
if (details.taskInfo) parts.push(details.taskInfo);
|
|
316
|
+
if (details.durationMs !== undefined) parts.push(formatDuration(details.durationMs));
|
|
317
|
+
let text = `${icon} ${theme.bold(details.agent)} ${theme.fg("dim", details.status)}`;
|
|
318
|
+
if (parts.length > 0) text += ` ${theme.fg("dim", "·")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "·")} `)}`;
|
|
319
|
+
const trimmedPreview = details.resultPreview.trim();
|
|
320
|
+
const previewLines = options.expanded
|
|
321
|
+
? trimmedPreview.split("\n").filter((line) => line.trim())
|
|
322
|
+
: [trimmedPreview.split("\n", 1)[0] ?? ""].filter((line) => line.trim());
|
|
323
|
+
for (const line of previewLines.length > 0 ? previewLines : ["(no output)"]) {
|
|
324
|
+
text += `\n ${theme.fg("dim", `⎿ ${line}`)}`;
|
|
325
|
+
}
|
|
326
|
+
if (!options.expanded && trimmedPreview.includes("\n")) {
|
|
327
|
+
text += `\n ${theme.fg("dim", "Ctrl+O full notification")}`;
|
|
328
|
+
}
|
|
329
|
+
if (details.sessionLabel && details.sessionValue) {
|
|
330
|
+
text += `\n ${theme.fg("muted", `${details.sessionLabel}: ${shortenPath(details.sessionValue)}`)}`;
|
|
331
|
+
}
|
|
332
|
+
return new Text(text, 0, 0);
|
|
246
333
|
});
|
|
247
334
|
|
|
248
335
|
pi.registerMessageRenderer<SubagentControlMessageDetails>(SUBAGENT_CONTROL_MESSAGE_TYPE, (message, _options, theme) => {
|
|
@@ -311,9 +398,10 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
311
398
|
description: `Delegate to subagents or manage agent definitions.
|
|
312
399
|
|
|
313
400
|
EXECUTION (use exactly ONE mode):
|
|
314
|
-
•
|
|
315
|
-
•
|
|
316
|
-
•
|
|
401
|
+
• Before executing, use { action: "list" } to inspect configured agents/chains. Only execute agents listed as executable/non-disabled.
|
|
402
|
+
• SINGLE: { agent, task? } - one task; omit task for self-contained agents
|
|
403
|
+
• CHAIN: { chain: [{agent:"agent-a"}, {parallel:[{agent:"agent-b",count:3}]}] } - sequential pipeline with optional parallel fan-out
|
|
404
|
+
• PARALLEL: { tasks: [{agent,task,count?,output?,reads?,progress?}, ...], concurrency?: number, worktree?: true } - concurrent execution (worktree: isolate each task in a git worktree)
|
|
317
405
|
• Optional context: { context: "fresh" | "fork" } (default: "fresh")
|
|
318
406
|
|
|
319
407
|
CHAIN TEMPLATE VARIABLES (use in task strings):
|
|
@@ -321,10 +409,10 @@ CHAIN TEMPLATE VARIABLES (use in task strings):
|
|
|
321
409
|
• {previous} - Text response from the previous step (empty for first step)
|
|
322
410
|
• {chain_dir} - Shared directory for chain files (e.g., <tmpdir>/pi-subagents-<scope>/chain-runs/abc123/)
|
|
323
411
|
|
|
324
|
-
Example: { chain: [{agent:"
|
|
412
|
+
Example: { chain: [{agent:"agent-a", task:"Analyze {task}"}, {agent:"agent-b", task:"Plan based on {previous}"}] }
|
|
325
413
|
|
|
326
414
|
MANAGEMENT (use action field, omit agent/task/chain/tasks):
|
|
327
|
-
• { action: "list" } - discover agents/chains
|
|
415
|
+
• { action: "list" } - discover executable agents/chains and any disabled builtins
|
|
328
416
|
• { action: "get", agent: "name" } - full detail
|
|
329
417
|
• { action: "create", config: { name, systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, ... } }
|
|
330
418
|
• { action: "update", agent: "name", config: { ... } } - merge
|
|
@@ -370,7 +458,8 @@ CONTROL:
|
|
|
370
458
|
);
|
|
371
459
|
},
|
|
372
460
|
|
|
373
|
-
renderResult(result, options, theme) {
|
|
461
|
+
renderResult(result, options, theme, context) {
|
|
462
|
+
syncResultAnimation(result, context);
|
|
374
463
|
return renderSubagentResult(result, options, theme);
|
|
375
464
|
},
|
|
376
465
|
|
|
@@ -381,7 +470,6 @@ CONTROL:
|
|
|
381
470
|
|
|
382
471
|
const eventUnsubscribeStoreKey = "__piSubagentEventUnsubscribes";
|
|
383
472
|
const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
|
|
384
|
-
const globalStore = globalThis as Record<string, unknown>;
|
|
385
473
|
const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
|
|
386
474
|
if (Array.isArray(previousEventUnsubscribes)) {
|
|
387
475
|
for (const unsubscribe of previousEventUnsubscribes) {
|
|
@@ -480,6 +568,11 @@ CONTROL:
|
|
|
480
568
|
slashBridge.dispose();
|
|
481
569
|
promptTemplateBridge.cancelAll();
|
|
482
570
|
promptTemplateBridge.dispose();
|
|
571
|
+
stopWidgetAnimation();
|
|
572
|
+
stopResultAnimations();
|
|
573
|
+
if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
|
|
574
|
+
delete globalStore[runtimeCleanupStoreKey];
|
|
575
|
+
}
|
|
483
576
|
if (state.lastUiContext?.hasUI) {
|
|
484
577
|
state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
485
578
|
}
|
package/intercom-bridge.ts
CHANGED
|
@@ -8,14 +8,14 @@ const DEFAULT_INTERCOM_EXTENSION_DIR = path.join(os.homedir(), ".pi", "agent", "
|
|
|
8
8
|
const DEFAULT_INTERCOM_CONFIG_PATH = path.join(os.homedir(), ".pi", "agent", "intercom", "config.json");
|
|
9
9
|
const DEFAULT_SUBAGENT_CONFIG_DIR = path.join(os.homedir(), ".pi", "agent", "extensions", "subagent");
|
|
10
10
|
const DEFAULT_INTERCOM_TARGET_PREFIX = "subagent-chat";
|
|
11
|
-
const INTERCOM_BRIDGE_MARKER = "Intercom orchestration channel:";
|
|
11
|
+
export const INTERCOM_BRIDGE_MARKER = "Intercom orchestration channel:";
|
|
12
12
|
const DEFAULT_INTERCOM_BRIDGE_TEMPLATE = `The inherited thread is reference-only. Do not continue that conversation or send questions, status updates, or completion handoffs to the orchestrator in normal assistant text.
|
|
13
13
|
|
|
14
14
|
Use intercom only for coordination with the orchestrator session "{orchestratorTarget}".
|
|
15
15
|
- Need a decision or blocked: intercom({ action: "ask", to: "{orchestratorTarget}", message: "<question>" })
|
|
16
|
-
-
|
|
16
|
+
- Blocked or explicitly asked to send progress: intercom({ action: "send", to: "{orchestratorTarget}", message: "UPDATE: <summary>" })
|
|
17
17
|
|
|
18
|
-
If no
|
|
18
|
+
Do not send routine completion handoffs through intercom. If no coordination is needed, return a focused task result.`;
|
|
19
19
|
|
|
20
20
|
export interface IntercomBridgeState {
|
|
21
21
|
active: boolean;
|
package/notify.ts
CHANGED
|
@@ -12,6 +12,16 @@ interface ChainStepResult {
|
|
|
12
12
|
success: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export interface SubagentNotifyDetails {
|
|
16
|
+
agent: string;
|
|
17
|
+
status: "completed" | "failed" | "paused";
|
|
18
|
+
taskInfo?: string;
|
|
19
|
+
resultPreview: string;
|
|
20
|
+
durationMs?: number;
|
|
21
|
+
sessionLabel?: string;
|
|
22
|
+
sessionValue?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
interface SubagentResult {
|
|
16
26
|
id: string | null;
|
|
17
27
|
agent: string | null;
|
|
@@ -20,6 +30,7 @@ interface SubagentResult {
|
|
|
20
30
|
exitCode?: number;
|
|
21
31
|
state?: string;
|
|
22
32
|
timestamp: number;
|
|
33
|
+
durationMs?: number;
|
|
23
34
|
sessionFile?: string;
|
|
24
35
|
shareUrl?: string;
|
|
25
36
|
gistUrl?: string;
|
|
@@ -64,22 +75,21 @@ export default function registerSubagentNotify(pi: ExtensionAPI): void {
|
|
|
64
75
|
? ` (${result.taskIndex + 1}/${result.totalTasks})`
|
|
65
76
|
: "";
|
|
66
77
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
78
|
+
const sessionLine = result.shareUrl
|
|
79
|
+
? `Session: ${result.shareUrl}`
|
|
80
|
+
: result.shareError
|
|
81
|
+
? `Session share error: ${result.shareError}`
|
|
82
|
+
: result.sessionFile
|
|
83
|
+
? `Session file: ${result.sessionFile}`
|
|
84
|
+
: undefined;
|
|
75
85
|
|
|
76
86
|
const displaySummary = summary.trim() ? summary : "(no output)";
|
|
77
87
|
const content = [
|
|
78
88
|
`Background task ${status}: **${agent}**${taskInfo}`,
|
|
79
89
|
"",
|
|
80
90
|
displaySummary,
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
sessionLine ? "" : undefined,
|
|
92
|
+
sessionLine,
|
|
83
93
|
]
|
|
84
94
|
.filter((line) => line !== undefined)
|
|
85
95
|
.join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-subagents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
|
|
5
5
|
"author": "Nico Bailon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"*.mjs",
|
|
32
32
|
"agents/",
|
|
33
33
|
"skills/**/*",
|
|
34
|
+
"prompts/**/*",
|
|
34
35
|
"README.md",
|
|
35
36
|
"CHANGELOG.md"
|
|
36
37
|
],
|
|
@@ -46,6 +47,9 @@
|
|
|
46
47
|
],
|
|
47
48
|
"skills": [
|
|
48
49
|
"./skills"
|
|
50
|
+
],
|
|
51
|
+
"prompts": [
|
|
52
|
+
"./prompts"
|
|
49
53
|
]
|
|
50
54
|
},
|
|
51
55
|
"peerDependencies": {
|
package/pi-args.ts
CHANGED
|
@@ -43,6 +43,7 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
43
43
|
const args = [...input.baseArgs];
|
|
44
44
|
|
|
45
45
|
if (input.sessionFile) {
|
|
46
|
+
fs.mkdirSync(path.dirname(input.sessionFile), { recursive: true });
|
|
46
47
|
args.push("--session", input.sessionFile);
|
|
47
48
|
} else {
|
|
48
49
|
if (!input.sessionEnabled) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Parallel subagents review
|
|
3
|
+
---
|
|
4
|
+
Great. Now let's launch parallel reviewers to conduct an adversarial review.
|
|
5
|
+
|
|
6
|
+
Important: launch reviewers with fresh context, not forked context. Reviewers should inspect the repository and current diff directly from files and commands, without inheriting the main agent chat. Use forked context only if I explicitly ask for it.
|
|
7
|
+
|
|
8
|
+
$@
|