pi-subagents 0.19.0 → 0.19.2
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 +16 -0
- package/README.md +596 -846
- package/doctor.ts +198 -0
- package/index.ts +4 -1
- package/intercom-bridge.ts +58 -6
- package/package.json +1 -1
- package/prompts/gather-context-and-clarify.md +13 -0
- package/prompts/oracle-executor.md +9 -0
- package/prompts/parallel-research.md +50 -0
- package/prompts/parallel-review.md +32 -2
- package/schemas.ts +1 -1
- package/skills/pi-subagents/SKILL.md +124 -20
- package/slash-commands.ts +83 -29
- package/slash-live-state.ts +3 -3
- package/subagent-executor.ts +35 -1
- package/subagents-status.ts +13 -7
package/doctor.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { discoverAgentsAll, type AgentSource } from "./agents.ts";
|
|
4
|
+
import { isAsyncAvailable } from "./async-execution.ts";
|
|
5
|
+
import { diagnoseIntercomBridge, type IntercomBridgeDiagnostic } from "./intercom-bridge.ts";
|
|
6
|
+
import { discoverAvailableSkills, type SkillSource } from "./skills.ts";
|
|
7
|
+
import {
|
|
8
|
+
ASYNC_DIR,
|
|
9
|
+
CHAIN_RUNS_DIR,
|
|
10
|
+
RESULTS_DIR,
|
|
11
|
+
TEMP_ROOT_DIR,
|
|
12
|
+
type ExtensionConfig,
|
|
13
|
+
type SubagentState,
|
|
14
|
+
} from "./types.ts";
|
|
15
|
+
|
|
16
|
+
interface DoctorPaths {
|
|
17
|
+
tempRootDir: string;
|
|
18
|
+
asyncDir: string;
|
|
19
|
+
resultsDir: string;
|
|
20
|
+
chainRunsDir: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface DoctorDeps {
|
|
24
|
+
isAsyncAvailable: () => boolean;
|
|
25
|
+
discoverAgentsAll: typeof discoverAgentsAll;
|
|
26
|
+
discoverAvailableSkills: typeof discoverAvailableSkills;
|
|
27
|
+
diagnoseIntercomBridge: typeof diagnoseIntercomBridge;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DoctorReportInput {
|
|
31
|
+
cwd: string;
|
|
32
|
+
config: ExtensionConfig;
|
|
33
|
+
state: SubagentState;
|
|
34
|
+
context?: "fresh" | "fork";
|
|
35
|
+
requestedSessionDir?: string;
|
|
36
|
+
currentSessionFile?: string | null;
|
|
37
|
+
currentSessionId?: string | null;
|
|
38
|
+
orchestratorTarget?: string;
|
|
39
|
+
sessionError?: string;
|
|
40
|
+
expandTilde?: (value: string) => string;
|
|
41
|
+
paths?: DoctorPaths;
|
|
42
|
+
deps?: Partial<DoctorDeps>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DEFAULT_PATHS: DoctorPaths = {
|
|
46
|
+
tempRootDir: TEMP_ROOT_DIR,
|
|
47
|
+
asyncDir: ASYNC_DIR,
|
|
48
|
+
resultsDir: RESULTS_DIR,
|
|
49
|
+
chainRunsDir: CHAIN_RUNS_DIR,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const DEFAULT_DEPS: DoctorDeps = {
|
|
53
|
+
isAsyncAvailable,
|
|
54
|
+
discoverAgentsAll,
|
|
55
|
+
discoverAvailableSkills,
|
|
56
|
+
diagnoseIntercomBridge,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function errorText(error: unknown): string {
|
|
60
|
+
return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function lineFromCheck(label: string, check: () => string): string {
|
|
64
|
+
try {
|
|
65
|
+
return check();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return `- ${label}: failed — ${errorText(error)}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function formatExistingDirectory(label: string, dirPath: string): string {
|
|
72
|
+
try {
|
|
73
|
+
if (!fs.existsSync(dirPath)) return `- ${label}: missing (${dirPath})`;
|
|
74
|
+
const stats = fs.statSync(dirPath);
|
|
75
|
+
if (!stats.isDirectory()) throw new Error(`not a directory: ${dirPath}`);
|
|
76
|
+
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
77
|
+
return `- ${label}: ok (${dirPath})`;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return `- ${label}: failed (${dirPath}) — ${errorText(error)}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatSourceCounts(counts: Record<AgentSource, number>): string {
|
|
84
|
+
return `builtin ${counts.builtin}, user ${counts.user}, project ${counts.project}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function formatSkillSourceCounts(skills: Array<{ source: SkillSource }>): string {
|
|
88
|
+
const counts = new Map<SkillSource, number>();
|
|
89
|
+
for (const skill of skills) counts.set(skill.source, (counts.get(skill.source) ?? 0) + 1);
|
|
90
|
+
const ordered: SkillSource[] = [
|
|
91
|
+
"project",
|
|
92
|
+
"project-settings",
|
|
93
|
+
"project-package",
|
|
94
|
+
"user",
|
|
95
|
+
"user-settings",
|
|
96
|
+
"user-package",
|
|
97
|
+
"extension",
|
|
98
|
+
"builtin",
|
|
99
|
+
"unknown",
|
|
100
|
+
];
|
|
101
|
+
const parts = ordered
|
|
102
|
+
.map((source) => `${source} ${counts.get(source) ?? 0}`)
|
|
103
|
+
.filter((part) => !part.endsWith(" 0"));
|
|
104
|
+
return parts.length > 0 ? parts.join(", ") : "none";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatConfiguredSessionDir(input: DoctorReportInput): string {
|
|
108
|
+
if (input.requestedSessionDir) {
|
|
109
|
+
return path.resolve(input.expandTilde?.(input.requestedSessionDir) ?? input.requestedSessionDir);
|
|
110
|
+
}
|
|
111
|
+
if (input.config.defaultSessionDir) {
|
|
112
|
+
return path.resolve(input.expandTilde?.(input.config.defaultSessionDir) ?? input.config.defaultSessionDir);
|
|
113
|
+
}
|
|
114
|
+
return "not configured";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function formatSessionLines(input: DoctorReportInput): string[] {
|
|
118
|
+
const sessionFile = input.currentSessionFile ?? null;
|
|
119
|
+
const lines = [
|
|
120
|
+
lineFromCheck("configured session dir", () => `- configured session dir: ${formatConfiguredSessionDir(input)}`),
|
|
121
|
+
`- current session file: ${sessionFile ?? "not available"}`,
|
|
122
|
+
`- current session dir: ${sessionFile ? path.dirname(sessionFile) : "not available"}`,
|
|
123
|
+
`- current session id: ${input.currentSessionId ?? input.state.currentSessionId ?? "not available"}`,
|
|
124
|
+
];
|
|
125
|
+
if (input.sessionError) lines.push(`- session manager: failed — ${input.sessionError}`);
|
|
126
|
+
return lines;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function formatDiscovery(input: DoctorReportInput, deps: DoctorDeps): string[] {
|
|
130
|
+
return [
|
|
131
|
+
lineFromCheck("agents/chains", () => {
|
|
132
|
+
const discovered = deps.discoverAgentsAll(input.cwd);
|
|
133
|
+
const agentCounts = {
|
|
134
|
+
builtin: discovered.builtin.length,
|
|
135
|
+
user: discovered.user.length,
|
|
136
|
+
project: discovered.project.length,
|
|
137
|
+
};
|
|
138
|
+
const chainCounts = discovered.chains.reduce<Record<AgentSource, number>>((counts, chain) => {
|
|
139
|
+
counts[chain.source] += 1;
|
|
140
|
+
return counts;
|
|
141
|
+
}, { builtin: 0, user: 0, project: 0 });
|
|
142
|
+
return [
|
|
143
|
+
`- agents: total ${agentCounts.builtin + agentCounts.user + agentCounts.project} (${formatSourceCounts(agentCounts)})`,
|
|
144
|
+
`- chains: total ${discovered.chains.length} (${formatSourceCounts(chainCounts)})`,
|
|
145
|
+
].join("\n");
|
|
146
|
+
}),
|
|
147
|
+
lineFromCheck("skills", () => {
|
|
148
|
+
const skills = deps.discoverAvailableSkills(input.cwd);
|
|
149
|
+
return `- skills: total ${skills.length} (${formatSkillSourceCounts(skills)})`;
|
|
150
|
+
}),
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function formatIntercomDiagnostic(diagnostic: IntercomBridgeDiagnostic, context: "fresh" | "fork" | undefined): string[] {
|
|
155
|
+
const lines = [
|
|
156
|
+
`- bridge: ${diagnostic.active ? "active" : "inactive"}${diagnostic.reason ? ` (${diagnostic.reason})` : ""}`,
|
|
157
|
+
`- mode: ${diagnostic.mode}; context: ${context ?? "unspecified"}`,
|
|
158
|
+
`- orchestrator target: ${diagnostic.orchestratorTarget ?? "not available"}`,
|
|
159
|
+
`- pi-intercom: ${diagnostic.piIntercomAvailable ? "available" : "unavailable"} at ${diagnostic.extensionDir}`,
|
|
160
|
+
];
|
|
161
|
+
if (diagnostic.configPath && diagnostic.intercomConfigEnabled !== undefined) {
|
|
162
|
+
lines.push(`- intercom config: ${diagnostic.intercomConfigEnabled === false ? "disabled" : "enabled or absent"} (${diagnostic.configPath})`);
|
|
163
|
+
}
|
|
164
|
+
if (diagnostic.intercomConfigError) {
|
|
165
|
+
lines.push(`- intercom config warning: ${diagnostic.intercomConfigError}; runtime assumes enabled`);
|
|
166
|
+
}
|
|
167
|
+
return lines;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function buildDoctorReport(input: DoctorReportInput): string {
|
|
171
|
+
const paths = input.paths ?? DEFAULT_PATHS;
|
|
172
|
+
const deps = { ...DEFAULT_DEPS, ...input.deps };
|
|
173
|
+
const lines = [
|
|
174
|
+
"Subagents doctor report",
|
|
175
|
+
"",
|
|
176
|
+
"Runtime",
|
|
177
|
+
`- cwd: ${input.cwd}`,
|
|
178
|
+
lineFromCheck("async support", () => `- async support: ${deps.isAsyncAvailable() ? "available" : "unavailable"}`),
|
|
179
|
+
...formatSessionLines(input),
|
|
180
|
+
"",
|
|
181
|
+
"Filesystem",
|
|
182
|
+
formatExistingDirectory("temp root", paths.tempRootDir),
|
|
183
|
+
formatExistingDirectory("async runs", paths.asyncDir),
|
|
184
|
+
formatExistingDirectory("results", paths.resultsDir),
|
|
185
|
+
formatExistingDirectory("chain runs", paths.chainRunsDir),
|
|
186
|
+
"",
|
|
187
|
+
"Discovery",
|
|
188
|
+
...formatDiscovery(input, deps),
|
|
189
|
+
"",
|
|
190
|
+
"Intercom bridge",
|
|
191
|
+
...lineFromCheck("intercom bridge", () => formatIntercomDiagnostic(deps.diagnoseIntercomBridge({
|
|
192
|
+
config: input.config.intercomBridge,
|
|
193
|
+
context: input.context,
|
|
194
|
+
orchestratorTarget: input.orchestratorTarget,
|
|
195
|
+
}), input.context).join("\n")).split("\n"),
|
|
196
|
+
];
|
|
197
|
+
return lines.join("\n");
|
|
198
|
+
}
|
package/index.ts
CHANGED
|
@@ -421,7 +421,10 @@ MANAGEMENT (use action field, omit agent/task/chain/tasks):
|
|
|
421
421
|
|
|
422
422
|
CONTROL:
|
|
423
423
|
• { action: "status", id: "..." } - inspect an async/background run by id or prefix
|
|
424
|
-
• { action: "interrupt", id?: "..." } - soft-interrupt the current child turn and leave the run paused
|
|
424
|
+
• { action: "interrupt", id?: "..." } - soft-interrupt the current child turn and leave the run paused
|
|
425
|
+
|
|
426
|
+
DIAGNOSTICS:
|
|
427
|
+
• { action: "doctor" } - read-only report for runtime paths, discovery, sessions, and intercom`,
|
|
425
428
|
parameters: SubagentParams,
|
|
426
429
|
|
|
427
430
|
execute(id, params, signal, onUpdate, ctx) {
|
package/intercom-bridge.ts
CHANGED
|
@@ -25,6 +25,19 @@ export interface IntercomBridgeState {
|
|
|
25
25
|
instruction: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export interface IntercomBridgeDiagnostic {
|
|
29
|
+
active: boolean;
|
|
30
|
+
mode: IntercomBridgeMode;
|
|
31
|
+
wantsIntercom: boolean;
|
|
32
|
+
piIntercomAvailable: boolean;
|
|
33
|
+
extensionDir: string;
|
|
34
|
+
configPath?: string;
|
|
35
|
+
orchestratorTarget?: string;
|
|
36
|
+
reason?: string;
|
|
37
|
+
intercomConfigEnabled?: boolean;
|
|
38
|
+
intercomConfigError?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
interface ResolveIntercomBridgeInput {
|
|
29
42
|
config: ExtensionConfig["intercomBridge"];
|
|
30
43
|
context: "fresh" | "fork" | undefined;
|
|
@@ -68,14 +81,13 @@ function resolveIntercomBridgeConfig(value: ExtensionConfig["intercomBridge"]):
|
|
|
68
81
|
};
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
function
|
|
72
|
-
if (!fs.existsSync(configPath)) return true;
|
|
84
|
+
function intercomConfigStatus(configPath: string): { enabled: boolean; error?: unknown } {
|
|
85
|
+
if (!fs.existsSync(configPath)) return { enabled: true };
|
|
73
86
|
try {
|
|
74
87
|
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8")) as { enabled?: unknown };
|
|
75
|
-
return parsed.enabled !== false;
|
|
88
|
+
return { enabled: parsed.enabled !== false };
|
|
76
89
|
} catch (error) {
|
|
77
|
-
|
|
78
|
-
return true;
|
|
90
|
+
return { enabled: true, error };
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
|
|
@@ -119,6 +131,44 @@ function buildIntercomBridgeInstruction(orchestratorTarget: string, template: st
|
|
|
119
131
|
${instruction}`;
|
|
120
132
|
}
|
|
121
133
|
|
|
134
|
+
export function diagnoseIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeDiagnostic {
|
|
135
|
+
const config = resolveIntercomBridgeConfig(input.config);
|
|
136
|
+
const mode = config.mode;
|
|
137
|
+
const extensionDir = path.resolve(input.extensionDir ?? DEFAULT_INTERCOM_EXTENSION_DIR);
|
|
138
|
+
const orchestratorTarget = input.orchestratorTarget?.trim();
|
|
139
|
+
const configPath = path.resolve(input.configPath ?? DEFAULT_INTERCOM_CONFIG_PATH);
|
|
140
|
+
const wantsIntercom = mode !== "off" && !(mode === "fork-only" && input.context !== "fork");
|
|
141
|
+
const piIntercomAvailable = fs.existsSync(extensionDir);
|
|
142
|
+
let configStatus: ReturnType<typeof intercomConfigStatus> | undefined;
|
|
143
|
+
let reason: string | undefined;
|
|
144
|
+
if (mode === "off") reason = "bridge mode is off";
|
|
145
|
+
else if (mode === "fork-only" && input.context !== "fork") reason = "bridge mode is fork-only and context is not fork";
|
|
146
|
+
else if (!orchestratorTarget) reason = "orchestrator target is not available";
|
|
147
|
+
else if (!piIntercomAvailable) reason = "pi-intercom extension was not found";
|
|
148
|
+
else {
|
|
149
|
+
configStatus = intercomConfigStatus(configPath);
|
|
150
|
+
if (!configStatus.enabled) reason = "intercom config is disabled";
|
|
151
|
+
}
|
|
152
|
+
let intercomConfigError: string | undefined;
|
|
153
|
+
if (configStatus?.error) {
|
|
154
|
+
const error = configStatus.error;
|
|
155
|
+
intercomConfigError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
active: reason === undefined,
|
|
160
|
+
mode,
|
|
161
|
+
wantsIntercom,
|
|
162
|
+
piIntercomAvailable,
|
|
163
|
+
extensionDir,
|
|
164
|
+
configPath,
|
|
165
|
+
...(orchestratorTarget ? { orchestratorTarget } : {}),
|
|
166
|
+
...(reason ? { reason } : {}),
|
|
167
|
+
...(configStatus ? { intercomConfigEnabled: configStatus.enabled } : {}),
|
|
168
|
+
...(intercomConfigError ? { intercomConfigError } : {}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
122
172
|
export function resolveIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeState {
|
|
123
173
|
const config = resolveIntercomBridgeConfig(input.config);
|
|
124
174
|
const mode = config.mode;
|
|
@@ -144,7 +194,9 @@ export function resolveIntercomBridge(input: ResolveIntercomBridgeInput): Interc
|
|
|
144
194
|
}
|
|
145
195
|
|
|
146
196
|
const configPath = path.resolve(input.configPath ?? DEFAULT_INTERCOM_CONFIG_PATH);
|
|
147
|
-
|
|
197
|
+
const intercomStatus = intercomConfigStatus(configPath);
|
|
198
|
+
if (intercomStatus.error) console.warn(`Failed to parse intercom config at '${configPath}'. Assuming enabled.`, intercomStatus.error);
|
|
199
|
+
if (!intercomStatus.enabled) {
|
|
148
200
|
return { active: false, mode, extensionDir, instruction: defaultInstruction };
|
|
149
201
|
}
|
|
150
202
|
|
package/package.json
CHANGED
|
@@ -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,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Send an explicitly approved task to oracle-executor with full inherited context.
|
|
3
|
+
subagent: oracle-executor
|
|
4
|
+
inheritContext: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Launch the oracle-executor subagent with a strict implementation meta prompt that points it at the plan and asks it to execute:
|
|
8
|
+
|
|
9
|
+
$@
|
|
@@ -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
|
+
$@
|
|
@@ -1,8 +1,38 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Parallel subagents review
|
|
3
3
|
---
|
|
4
|
-
Great. Now let's launch parallel reviewers to conduct an adversarial review.
|
|
5
4
|
|
|
6
|
-
|
|
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. Unless I specify angles, use these three:
|
|
10
|
+
|
|
11
|
+
1. Correctness and regressions
|
|
12
|
+
Check whether the change satisfies the request, preserves existing behavior, handles edge cases, and avoids hidden runtime failures.
|
|
13
|
+
|
|
14
|
+
2. Tests and validation
|
|
15
|
+
Check whether tests or validation were added at the right layer, whether assertions are meaningful, and whether the chosen verification commands are enough.
|
|
16
|
+
|
|
17
|
+
3. Simplicity and maintainability
|
|
18
|
+
Check for unnecessary complexity, duplicate structure, single-use wrappers, brittle abstractions, confusing names, verbosity, and cleanup that is clearly worth doing.
|
|
19
|
+
|
|
20
|
+
Adapt the angles when the work calls for it:
|
|
21
|
+
- TypeScript-heavy changes: include type safety, source-of-truth types, casts, and error-boundary discipline.
|
|
22
|
+
- UI-heavy changes: include UX, accessibility, copy, and visual quality.
|
|
23
|
+
- Security-sensitive changes: include unsafe input/output handling, auth boundaries, privacy, and data exposure.
|
|
24
|
+
- Docs-heavy changes: include clarity, accuracy, completeness, reader flow, and non-robotic prose.
|
|
25
|
+
- Large multi-file changes: consider a fourth reviewer for structural friction, module boundaries, and testability.
|
|
26
|
+
|
|
27
|
+
Prefer three strong reviewers over many vague reviewers.
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+
While reviewers run, do your own narrow inspection if useful. After they return, synthesize the feedback into:
|
|
32
|
+
- fixes worth doing now
|
|
33
|
+
- optional improvements
|
|
34
|
+
- feedback to ignore or defer, with a short reason
|
|
35
|
+
|
|
36
|
+
Do not blindly apply every reviewer suggestion. Ask before applying fixes unless I already told you to address review feedback.
|
|
7
37
|
|
|
8
38
|
$@
|
package/schemas.ts
CHANGED
|
@@ -106,7 +106,7 @@ export const SubagentParams = Type.Object({
|
|
|
106
106
|
task: Type.Optional(Type.String({ description: "Task (SINGLE mode, optional for self-contained agents)" })),
|
|
107
107
|
// Management action (when present, tool operates in management mode)
|
|
108
108
|
action: Type.Optional(Type.String({
|
|
109
|
-
description: "Action:
|
|
109
|
+
description: "Action: 'list', 'get', 'create', 'update', 'delete', 'status', 'interrupt', or 'doctor'. Omit for execution mode."
|
|
110
110
|
})),
|
|
111
111
|
id: Type.Optional(Type.String({
|
|
112
112
|
description: "Run id or prefix for action='status' or action='interrupt'."
|