@ilya-lesikov/pi-pi 0.6.0 → 0.8.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/3p/pi-ask-user/index.ts +1 -1
- package/3p/pi-plannotator/apps/pi-extension/README.md +6 -5
- package/3p/pi-plannotator/apps/pi-extension/plannotator-browser.ts +25 -10
- package/3p/pi-plannotator/apps/pi-extension/plannotator-events.code-review.test.ts +172 -0
- package/3p/pi-plannotator/apps/pi-extension/plannotator-events.ts +50 -12
- package/3p/pi-plannotator/apps/pi-extension/server/project.test.ts +45 -0
- package/3p/pi-plannotator/apps/pi-extension/server/project.ts +7 -6
- package/3p/pi-plannotator/apps/pi-extension/server/serverReview.ts +41 -25
- package/3p/pi-subagents/src/agent-manager.ts +34 -1
- package/3p/pi-subagents/src/agent-runner.ts +66 -33
- package/3p/pi-subagents/src/index.ts +3 -38
- package/3p/pi-subagents/src/types.ts +4 -0
- package/extensions/orchestrator/agents/advisor.ts +35 -0
- package/extensions/orchestrator/agents/brainstorm-reviewer.ts +7 -7
- package/extensions/orchestrator/agents/code-reviewer.ts +7 -7
- package/extensions/orchestrator/agents/constraints.test.ts +44 -0
- package/extensions/orchestrator/agents/constraints.ts +3 -0
- package/extensions/orchestrator/agents/deep-debugger.ts +35 -0
- package/extensions/orchestrator/agents/plan-reviewer.ts +7 -7
- package/extensions/orchestrator/agents/planner.ts +9 -8
- package/extensions/orchestrator/agents/prompts.test.ts +120 -0
- package/extensions/orchestrator/agents/reviewer.ts +48 -0
- package/extensions/orchestrator/agents/task.ts +6 -20
- package/extensions/orchestrator/agents/tool-routing.ts +23 -1
- package/extensions/orchestrator/command-handlers.ts +1 -1
- package/extensions/orchestrator/config.ts +8 -4
- package/extensions/orchestrator/context.test.ts +54 -0
- package/extensions/orchestrator/context.ts +65 -2
- package/extensions/orchestrator/custom-footer.ts +5 -2
- package/extensions/orchestrator/doctor.test.ts +3 -1
- package/extensions/orchestrator/doctor.ts +40 -2
- package/extensions/orchestrator/event-handlers.test.ts +97 -1
- package/extensions/orchestrator/event-handlers.ts +222 -48
- package/extensions/orchestrator/flant-infra.test.ts +312 -0
- package/extensions/orchestrator/flant-infra.ts +407 -44
- package/extensions/orchestrator/index.ts +1 -1
- package/extensions/orchestrator/integration.test.ts +312 -18
- package/extensions/orchestrator/messages.test.ts +30 -0
- package/extensions/orchestrator/messages.ts +6 -0
- package/extensions/orchestrator/model-registry.test.ts +124 -13
- package/extensions/orchestrator/model-registry.ts +91 -33
- package/extensions/orchestrator/orchestrator.test.ts +113 -0
- package/extensions/orchestrator/orchestrator.ts +163 -3
- package/extensions/orchestrator/phases/brainstorm.ts +9 -20
- package/extensions/orchestrator/phases/implementation.test.ts +11 -0
- package/extensions/orchestrator/phases/implementation.ts +4 -6
- package/extensions/orchestrator/phases/planning.test.ts +16 -0
- package/extensions/orchestrator/phases/planning.ts +11 -4
- package/extensions/orchestrator/phases/review-task.ts +1 -4
- package/extensions/orchestrator/phases/review.test.ts +62 -0
- package/extensions/orchestrator/phases/review.ts +58 -15
- package/extensions/orchestrator/plannotator.ts +9 -6
- package/extensions/orchestrator/pp-menu.test.ts +74 -1
- package/extensions/orchestrator/pp-menu.ts +366 -94
- package/extensions/orchestrator/pp-state-tools.test.ts +192 -0
- package/extensions/orchestrator/pp-state-tools.ts +249 -0
- package/extensions/orchestrator/rate-limit-fallback-flow.test.ts +128 -0
- package/extensions/orchestrator/rate-limit-fallback.test.ts +98 -0
- package/extensions/orchestrator/rate-limit-fallback.ts +243 -0
- package/extensions/orchestrator/state.ts +41 -20
- package/extensions/orchestrator/test-helpers.ts +18 -3
- package/extensions/orchestrator/usage-tracker.test.ts +131 -3
- package/extensions/orchestrator/usage-tracker.ts +78 -11
- package/extensions/orchestrator/validate-artifacts.test.ts +20 -0
- package/extensions/orchestrator/validate-artifacts.ts +2 -2
- package/package.json +1 -1
|
@@ -24,7 +24,10 @@ import { registerAgentDefinitions, unregisterAgentDefinitions } from "./agents/r
|
|
|
24
24
|
import { createExploreAgent } from "./agents/explore.js";
|
|
25
25
|
import { createLibrarianAgent } from "./agents/librarian.js";
|
|
26
26
|
import { createTaskAgent } from "./agents/task.js";
|
|
27
|
-
import {
|
|
27
|
+
import { createAdvisorAgent } from "./agents/advisor.js";
|
|
28
|
+
import { createDeepDebuggerAgent } from "./agents/deep-debugger.js";
|
|
29
|
+
import { createReviewerAgent } from "./agents/reviewer.js";
|
|
30
|
+
import { resolveModel, getModelInfo, findLatestFamilyMatch, setSubscriptionFallbackActive } from "./model-registry.js";
|
|
28
31
|
import { buildRepoContext } from "./agents/repo-context.js";
|
|
29
32
|
import { getLogger, addTaskDestination, removeTaskDestination, setLogLevel } from "./log.js";
|
|
30
33
|
import { handleSpawnResult } from "./spawn-cleanup.js";
|
|
@@ -77,10 +80,36 @@ export class Orchestrator {
|
|
|
77
80
|
nudgeHalted = false;
|
|
78
81
|
pendingSubagentSpawns = 0;
|
|
79
82
|
errorRetryCount = 0;
|
|
83
|
+
// Halts the API-error auto-retry once errorRetryCount exceeds its cap, mirroring
|
|
84
|
+
// nudgeHalted. Without this, a benign intervening turn (e.g. the retried turn
|
|
85
|
+
// ends as a text-only "I'll wait") reset errorRetryCount to 0, so the 5-retry
|
|
86
|
+
// cap never accumulated and the "Previous request failed" nudge could fire
|
|
87
|
+
// unbounded (hundreds of times) against transient errors. Cleared only on
|
|
88
|
+
// genuine (non-[PI-PI]) user re-engagement, like nudgeHalted.
|
|
89
|
+
errorNudgeHalted = false;
|
|
80
90
|
commitReminderSent = false;
|
|
81
91
|
phaseStartTime = 0;
|
|
82
92
|
pendingRetryTimer: ReturnType<typeof setTimeout> | null = null;
|
|
93
|
+
// Unsubscribe for the direct ESC interrupt armed while pendingRetryTimer is
|
|
94
|
+
// live. pi-pi's own post-error retry is NOT covered by any SDK/interactive ESC
|
|
95
|
+
// binding (the turn already ended in error, the session is not streaming), so
|
|
96
|
+
// without this ESC would not cancel it.
|
|
97
|
+
pendingRetryEscUnsub: (() => void) | null = null;
|
|
83
98
|
activeTaskToken = 0;
|
|
99
|
+
// Subscription rate-limit fallback (Issue 5). subFallbackActive mirrors the
|
|
100
|
+
// model-registry override flag; subFallbackDialogPending guards against
|
|
101
|
+
// opening more than one switch dialogue at a time (across main + subagents);
|
|
102
|
+
// subFallbackModelId records the sub model that hit the limit (used by the
|
|
103
|
+
// switch-back probe); subSwitchBackTimer is the fixed-interval probe timer.
|
|
104
|
+
subFallbackActive = false;
|
|
105
|
+
subFallbackDialogPending = false;
|
|
106
|
+
// Set SYNCHRONOUSLY the moment a sub-429 is detected (before any async dialog),
|
|
107
|
+
// and cleared once the decision resolves. The autonomous planner/reviewer
|
|
108
|
+
// auto-retry consults this to avoid re-spawning a failed variant on the still-
|
|
109
|
+
// sub-routed model while the fallback decision is in flight.
|
|
110
|
+
subFallbackPendingDecision = false;
|
|
111
|
+
subFallbackModelId: string | null = null;
|
|
112
|
+
subSwitchBackTimer: ReturnType<typeof setTimeout> | null = null;
|
|
84
113
|
userGatePending = false;
|
|
85
114
|
lastCtx: any = null;
|
|
86
115
|
failedPlannerVariants: string[] = [];
|
|
@@ -123,6 +152,87 @@ export class Orchestrator {
|
|
|
123
152
|
};
|
|
124
153
|
}
|
|
125
154
|
|
|
155
|
+
// Arm a direct ESC interrupt for the post-error retry window. Idempotent: a
|
|
156
|
+
// single onTerminalInput handler stays registered until the retry is delivered,
|
|
157
|
+
// cancelled, or the task is reset. While pendingRetryTimer is live, ESC cancels
|
|
158
|
+
// the pending retry (no other binding covers this window).
|
|
159
|
+
armRetryEscInterrupt(ctx: any): void {
|
|
160
|
+
if (this.pendingRetryEscUnsub) return;
|
|
161
|
+
const onTerminalInput = ctx?.ui?.onTerminalInput;
|
|
162
|
+
if (typeof onTerminalInput !== "function") return;
|
|
163
|
+
const unsub = onTerminalInput.call(ctx.ui, (data: string) => {
|
|
164
|
+
if (!this.pendingRetryTimer) return undefined;
|
|
165
|
+
// Match a STANDALONE ESC only. Arrow/function/mouse sequences also start
|
|
166
|
+
// with 0x1b (e.g. "\x1b[A"), so `includes` would misfire on navigation
|
|
167
|
+
// keys and swallow them; a bare ESC is exactly the one-byte string.
|
|
168
|
+
if (data === "\x1b") {
|
|
169
|
+
this.cancelPendingRetry();
|
|
170
|
+
ctx?.ui?.notify?.("Retry cancelled.", "info");
|
|
171
|
+
return { consume: true };
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
});
|
|
175
|
+
this.pendingRetryEscUnsub = typeof unsub === "function" ? unsub : null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
disarmRetryEscInterrupt(): void {
|
|
179
|
+
if (this.pendingRetryEscUnsub) {
|
|
180
|
+
try {
|
|
181
|
+
this.pendingRetryEscUnsub();
|
|
182
|
+
} catch {
|
|
183
|
+
// ignore unsubscribe failures
|
|
184
|
+
}
|
|
185
|
+
this.pendingRetryEscUnsub = null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Cancel a pending post-error retry (timer + ESC interrupt) and reset the retry
|
|
190
|
+
// counter. Used by the ESC interrupt handler and by abort paths.
|
|
191
|
+
cancelPendingRetry(): void {
|
|
192
|
+
if (this.pendingRetryTimer) {
|
|
193
|
+
clearTimeout(this.pendingRetryTimer);
|
|
194
|
+
this.pendingRetryTimer = null;
|
|
195
|
+
}
|
|
196
|
+
this.disarmRetryEscInterrupt();
|
|
197
|
+
this.errorRetryCount = 0;
|
|
198
|
+
this.errorNudgeHalted = false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Deliver a queued message only once the main session is idle. Firing a
|
|
202
|
+
// followUp while the SDK still has an active run triggers an async, runtime-
|
|
203
|
+
// swallowed "Agent is already processing" rejection (surfaces as
|
|
204
|
+
// Extension "<runtime>" error), so we PRE-CHECK idle and DEFER (bounded poll)
|
|
205
|
+
// rather than dropping the nudge. Guarded by activeTaskToken; the poll reuses
|
|
206
|
+
// pendingRetryTimer so ESC/abort cancels it.
|
|
207
|
+
sendUserMessageWhenIdle(text: string, taskToken: number, attempt = 0): void {
|
|
208
|
+
const log = getLogger();
|
|
209
|
+
if (this.activeTaskToken !== taskToken || !this.active) {
|
|
210
|
+
this.disarmRetryEscInterrupt();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const idleFn = this.lastCtx?.isIdle;
|
|
214
|
+
const idle = typeof idleFn === "function" ? !!idleFn.call(this.lastCtx) : true;
|
|
215
|
+
if (idle) {
|
|
216
|
+
this.disarmRetryEscInterrupt();
|
|
217
|
+
this.safeSendUserMessage(text);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const MAX_ATTEMPTS = 120; // ~2min at 1s poll
|
|
221
|
+
if (attempt >= MAX_ATTEMPTS) {
|
|
222
|
+
log.warn({ s: "orchestrator", attempt }, "sendUserMessageWhenIdle gave up waiting for idle");
|
|
223
|
+
this.disarmRetryEscInterrupt();
|
|
224
|
+
this.lastCtx?.ui?.notify?.(
|
|
225
|
+
"pi-pi stopped waiting for the agent to go idle; auto-continuation was dropped. Send any message to resume.",
|
|
226
|
+
"warning",
|
|
227
|
+
);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.pendingRetryTimer = setTimeout(() => {
|
|
231
|
+
this.pendingRetryTimer = null;
|
|
232
|
+
this.sendUserMessageWhenIdle(text, taskToken, attempt + 1);
|
|
233
|
+
}, 1000);
|
|
234
|
+
}
|
|
235
|
+
|
|
126
236
|
safeSendUserMessage(text: string): void {
|
|
127
237
|
const log = getLogger();
|
|
128
238
|
const attempt = (retries: number) => {
|
|
@@ -175,6 +285,16 @@ export class Orchestrator {
|
|
|
175
285
|
(m) => m.provider.toLowerCase() === provider && m.id.toLowerCase() === modelId,
|
|
176
286
|
);
|
|
177
287
|
}
|
|
288
|
+
if (!resolved) {
|
|
289
|
+
const allSpecs = allModels.map((m) => `${m.provider}/${m.id}`);
|
|
290
|
+
const familyMatch = findLatestFamilyMatch(spec, allSpecs);
|
|
291
|
+
if (familyMatch) {
|
|
292
|
+
const fmLower = familyMatch.toLowerCase();
|
|
293
|
+
resolved = allModels.find(
|
|
294
|
+
(m) => `${m.provider.toLowerCase()}/${m.id.toLowerCase()}` === fmLower,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
178
298
|
if (!resolved) {
|
|
179
299
|
const pattern = spec.toLowerCase();
|
|
180
300
|
const matches = allModels.filter(
|
|
@@ -301,7 +421,7 @@ export class Orchestrator {
|
|
|
301
421
|
|
|
302
422
|
if (this.active.state.reviewCycle?.step === "apply_feedback") {
|
|
303
423
|
const pass = this.active.state.reviewCycle.pass;
|
|
304
|
-
return reviewCycleSystemPrompt(this.active.dir, pass, this.active.state.phase);
|
|
424
|
+
return reviewCycleSystemPrompt(this.active.dir, pass, this.active.state.phase, mode);
|
|
305
425
|
}
|
|
306
426
|
|
|
307
427
|
switch (this.active.state.phase) {
|
|
@@ -445,6 +565,7 @@ export class Orchestrator {
|
|
|
445
565
|
type === "debug" ? "debug"
|
|
446
566
|
: type === "brainstorm" ? "brainstorm"
|
|
447
567
|
: type === "review" ? "review"
|
|
568
|
+
: type === "quick" ? "quick"
|
|
448
569
|
: "implement"
|
|
449
570
|
];
|
|
450
571
|
const modelOk = await this.switchModel(ctx, modelConfig.model, modelConfig.thinking);
|
|
@@ -528,6 +649,7 @@ export class Orchestrator {
|
|
|
528
649
|
this.agentLifecycle.clear();
|
|
529
650
|
this.pendingSubagentSpawns = 0;
|
|
530
651
|
this.errorRetryCount = 0;
|
|
652
|
+
this.errorNudgeHalted = false;
|
|
531
653
|
this.commitReminderSent = false;
|
|
532
654
|
this.consecutiveNudges = 0;
|
|
533
655
|
this.nudgeHalted = false;
|
|
@@ -541,10 +663,27 @@ export class Orchestrator {
|
|
|
541
663
|
clearTimeout(this.pendingRetryTimer);
|
|
542
664
|
this.pendingRetryTimer = null;
|
|
543
665
|
}
|
|
666
|
+
this.disarmRetryEscInterrupt();
|
|
544
667
|
if (this.staleAgentTimer) {
|
|
545
668
|
clearInterval(this.staleAgentTimer);
|
|
546
669
|
this.staleAgentTimer = null;
|
|
547
670
|
}
|
|
671
|
+
this.clearSubscriptionFallback();
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Reset the subscription rate-limit fallback: cancel the switch-back probe
|
|
675
|
+
// timer, clear the model-registry override, and reset guards. Called on task
|
|
676
|
+
// reset/cleanup so the sticky override never leaks across tasks.
|
|
677
|
+
clearSubscriptionFallback(): void {
|
|
678
|
+
if (this.subSwitchBackTimer) {
|
|
679
|
+
clearTimeout(this.subSwitchBackTimer);
|
|
680
|
+
this.subSwitchBackTimer = null;
|
|
681
|
+
}
|
|
682
|
+
this.subFallbackActive = false;
|
|
683
|
+
this.subFallbackDialogPending = false;
|
|
684
|
+
this.subFallbackPendingDecision = false;
|
|
685
|
+
this.subFallbackModelId = null;
|
|
686
|
+
setSubscriptionFallbackActive(false);
|
|
548
687
|
}
|
|
549
688
|
|
|
550
689
|
async cleanupActive(): Promise<void> {
|
|
@@ -567,7 +706,10 @@ export class Orchestrator {
|
|
|
567
706
|
const log = getLogger();
|
|
568
707
|
const explore = createExploreAgent(this.config);
|
|
569
708
|
const librarian = createLibrarianAgent(this.config);
|
|
570
|
-
const taskAgent = createTaskAgent(this.config
|
|
709
|
+
const taskAgent = createTaskAgent(this.config);
|
|
710
|
+
const advisor = createAdvisorAgent(this.config);
|
|
711
|
+
const deepDebugger = createDeepDebuggerAgent(this.config);
|
|
712
|
+
const reviewer = createReviewerAgent(this.config);
|
|
571
713
|
const phase = this.active?.state.phase;
|
|
572
714
|
const repos = this.active?.state.repos ?? [];
|
|
573
715
|
log.debug({ s: "agents", phase, repoCount: repos.length }, "registering agent definitions");
|
|
@@ -604,6 +746,24 @@ export class Orchestrator {
|
|
|
604
746
|
...taskAgent,
|
|
605
747
|
prompt: appendContext("task", taskAgent.prompt, getModelInfo(resolveModel(this.config.agents.subagents.simple.task.model))),
|
|
606
748
|
},
|
|
749
|
+
{
|
|
750
|
+
type: "advisor",
|
|
751
|
+
variant: null,
|
|
752
|
+
...advisor,
|
|
753
|
+
prompt: appendContext("advisor", advisor.prompt, getModelInfo(resolveModel(this.config.agents.subagents.simple.advisor.model))),
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
type: "deep-debugger",
|
|
757
|
+
variant: null,
|
|
758
|
+
...deepDebugger,
|
|
759
|
+
prompt: appendContext("deep-debugger", deepDebugger.prompt, getModelInfo(resolveModel(this.config.agents.subagents.simple["deep-debugger"].model))),
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
type: "reviewer",
|
|
763
|
+
variant: null,
|
|
764
|
+
...reviewer,
|
|
765
|
+
prompt: appendContext("reviewer", reviewer.prompt, getModelInfo(resolveModel(this.config.agents.subagents.simple.reviewer.model))),
|
|
766
|
+
},
|
|
607
767
|
]);
|
|
608
768
|
}
|
|
609
769
|
|
|
@@ -4,7 +4,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
import { resolvePreset, type PiPiConfig, type VariantConfig } from "../config.js";
|
|
5
5
|
import { registerAgentDefinitions, spawnViaRpc, waitForCompletion } from "../agents/registry.js";
|
|
6
6
|
import { createBrainstormReviewerAgent } from "../agents/brainstorm-reviewer.js";
|
|
7
|
-
import { getContextDirs } from "../context.js";
|
|
7
|
+
import { getContextDirs, getArtifactManifest } from "../context.js";
|
|
8
8
|
import type { RepoInfo } from "../repo-utils.js";
|
|
9
9
|
import type { TaskType } from "../state.js";
|
|
10
10
|
import type { PhaseSend } from "../transition-controller.js";
|
|
@@ -26,13 +26,8 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
26
26
|
"",
|
|
27
27
|
"# Your job:",
|
|
28
28
|
"1. Clarify the problem with the user if needed",
|
|
29
|
-
"2.
|
|
30
|
-
|
|
31
|
-
' - Agent(subagent_type="Librarian", ...) — external docs, library APIs, web research.',
|
|
32
|
-
' - Agent(subagent_type="Task", ...) — only when you need a subtask that writes files or runs complex multi-step commands.',
|
|
33
|
-
" Explore is fast and cheap. Use it liberally for codebase questions. Spawn multiple in parallel.",
|
|
34
|
-
"3. Use bash to run commands, check logs, reproduce issues",
|
|
35
|
-
"4. Use tools to trace the bug:",
|
|
29
|
+
"2. Delegate research to subagents where useful (see the delegation guidance in your system prompt), and use bash to run commands, check logs, reproduce issues",
|
|
30
|
+
"3. Use tools to trace the bug:",
|
|
36
31
|
" - cbm_search/cbm_search_code: find relevant code by concept or text",
|
|
37
32
|
" - lsp goToDefinition, findReferences, hover: precise navigation and type info",
|
|
38
33
|
" - lsp goToImplementation: check interface implementors",
|
|
@@ -60,6 +55,7 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
60
55
|
" <Unresolved items needing user input. Omit section if none.>",
|
|
61
56
|
"",
|
|
62
57
|
"These files are validated programmatically. Missing sections or unexpected sections will be rejected.",
|
|
58
|
+
"Use pp_write_state_file / pp_edit_state_file (NOT the generic write/edit) for .pp state files — they keep the output compact and validate structure.",
|
|
63
59
|
"",
|
|
64
60
|
"# Optional: focused analysis artifacts",
|
|
65
61
|
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
@@ -84,11 +80,7 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
84
80
|
"",
|
|
85
81
|
"# How to work:",
|
|
86
82
|
"- Discuss the topic with the user. Ask clarifying questions. Propose approaches. Analyze tradeoffs.",
|
|
87
|
-
|
|
88
|
-
' Agent(subagent_type="Explore", ...) — codebase research. Prefer this for most lookups. Fast and cheap.',
|
|
89
|
-
' Agent(subagent_type="Librarian", ...) — external docs, library APIs, web research.',
|
|
90
|
-
' Agent(subagent_type="Task", ...) — only when you need a subtask that writes files or runs complex multi-step commands.',
|
|
91
|
-
" Spawn multiple Explore agents in parallel for broad searches.",
|
|
83
|
+
"- Delegate research to subagents where useful (see the delegation guidance in your system prompt).",
|
|
92
84
|
"- Use tools directly for quick lookups (cbm_search, lsp, ast_search, grep, etc.)",
|
|
93
85
|
"- Present findings to the user and discuss them. Don't just dump raw results.",
|
|
94
86
|
"",
|
|
@@ -120,11 +112,7 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
120
112
|
"",
|
|
121
113
|
"# Steps:",
|
|
122
114
|
"1. Clarify requirements with the user if anything is ambiguous",
|
|
123
|
-
"2.
|
|
124
|
-
' - Agent(subagent_type="Explore", ...) — codebase research. Prefer this for most lookups. Fast and cheap.',
|
|
125
|
-
' - Agent(subagent_type="Librarian", ...) — external docs, library APIs, web research.',
|
|
126
|
-
' - Agent(subagent_type="Task", ...) — only when you need a subtask that writes files or runs complex multi-step commands.',
|
|
127
|
-
" Spawn multiple Explore agents in parallel for broad searches.",
|
|
115
|
+
"2. Delegate research to subagents where useful (see the delegation guidance in your system prompt) — spawn multiple explores in parallel for broad searches",
|
|
128
116
|
"3. Use tools to understand code structure:",
|
|
129
117
|
" - cbm_search: natural-language search across all symbols",
|
|
130
118
|
" - cbm_search_code: graph-augmented grep (deduplicates into functions)",
|
|
@@ -155,6 +143,7 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
155
143
|
" <Unresolved items needing user input. Omit section if none.>",
|
|
156
144
|
"",
|
|
157
145
|
"These files are validated programmatically. Missing sections or unexpected sections will be rejected.",
|
|
146
|
+
"Use pp_write_state_file / pp_edit_state_file (NOT the generic write/edit) for .pp state files — they keep the output compact and validate structure.",
|
|
158
147
|
"",
|
|
159
148
|
"# Optional: focused analysis artifacts",
|
|
160
149
|
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
@@ -210,7 +199,7 @@ export async function spawnBrainstormReviewers(
|
|
|
210
199
|
const agent = createBrainstormReviewerAgent(
|
|
211
200
|
variant,
|
|
212
201
|
reviewerVariants,
|
|
213
|
-
{ userRequest, research, artifacts: artifacts.length > 0 ? artifacts : undefined },
|
|
202
|
+
{ userRequest, research, artifacts: artifacts.length > 0 ? artifacts : undefined, manifest: getArtifactManifest(taskDir) },
|
|
214
203
|
outputPath,
|
|
215
204
|
contextDirs,
|
|
216
205
|
"brainstorm",
|
|
@@ -261,7 +250,7 @@ export async function spawnBrainstormReviewers(
|
|
|
261
250
|
`${reviewOutputFiles.length} brainstorm reviewer(s) completed (round ${round}). Reviews in ${reviewsDir}:`,
|
|
262
251
|
...reviewOutputFiles.map((f) => ` - ${f}`),
|
|
263
252
|
"",
|
|
264
|
-
"Read all reviews and update USER_REQUEST.md
|
|
253
|
+
"Read all reviews and update USER_REQUEST.md, RESEARCH.md, and any artifacts/ files if needed.",
|
|
265
254
|
].join("\n"),
|
|
266
255
|
display: true,
|
|
267
256
|
},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { implementationSystemPrompt } from "./implementation.js";
|
|
3
|
+
|
|
4
|
+
describe("implementationSystemPrompt self-complete directive", () => {
|
|
5
|
+
it("instructs the agent to call pp_phase_complete when the implement phase is complete", () => {
|
|
6
|
+
const prompt = implementationSystemPrompt("/tmp/task", "/tmp");
|
|
7
|
+
expect(prompt).toContain("pp_phase_complete");
|
|
8
|
+
expect(prompt).toContain("Do NOT instead ask the user to run /pp manually");
|
|
9
|
+
expect(prompt).not.toContain("do NOT wait for the user");
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -11,7 +11,7 @@ export function implementationSystemPrompt(taskDir: string, cwd: string): string
|
|
|
11
11
|
"# Instructions:",
|
|
12
12
|
"1. Review the synthesized plan in your context",
|
|
13
13
|
"2. Implement each item in order",
|
|
14
|
-
"3. Check off items in the plan (change - [ ] to - [x]) as you complete them",
|
|
14
|
+
"3. Check off items in the plan (change - [ ] to - [x]) as you complete them — use pp_edit_state_file (NOT the generic edit) to update the plan compactly",
|
|
15
15
|
"4. After editing files, run lsp diagnostics to check for errors; use lsp codeActions for auto-fixes",
|
|
16
16
|
"5. Before modifying code, understand it first:",
|
|
17
17
|
" - cbm_search/cbm_search_code: find relevant functions and their context",
|
|
@@ -21,11 +21,7 @@ export function implementationSystemPrompt(taskDir: string, cwd: string): string
|
|
|
21
21
|
" - cbm_trace: trace dependencies across the codebase",
|
|
22
22
|
" - ast_search: find structural patterns (e.g. error handling conventions)",
|
|
23
23
|
"6. After completing changes, run cbm_changes to verify blast radius",
|
|
24
|
-
"7.
|
|
25
|
-
' - Agent(subagent_type="Explore", ...) — codebase research. Prefer this for most lookups. Fast and cheap.',
|
|
26
|
-
' - Agent(subagent_type="Librarian", ...) — external docs, library APIs, web research.',
|
|
27
|
-
' - Agent(subagent_type="Task", ...) — parallelizable, self-contained implementation subtasks.',
|
|
28
|
-
" Spawn multiple Explore agents in parallel for broad searches.",
|
|
24
|
+
"7. Delegate to subagents where useful (see the delegation guidance in your system prompt) — spawn multiple explores in parallel for broad searches, and use task for parallelizable self-contained implementation subtasks",
|
|
29
25
|
"",
|
|
30
26
|
"# Commits:",
|
|
31
27
|
"After completing a logical unit of work (a plan item, a bug fix, a test), call pp_commit",
|
|
@@ -34,5 +30,7 @@ export function implementationSystemPrompt(taskDir: string, cwd: string): string
|
|
|
34
30
|
"Don't batch all changes into one commit.",
|
|
35
31
|
"",
|
|
36
32
|
"Fix issues found by lsp diagnostics before moving on.",
|
|
33
|
+
"",
|
|
34
|
+
"When you judge the implement phase complete, call pp_phase_complete — the extension opens the advance gate for the user to review and confirm. Do NOT instead ask the user to run /pp manually.",
|
|
37
35
|
].join("\n");
|
|
38
36
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { planningSystemPrompt } from "./planning.js";
|
|
3
|
+
|
|
4
|
+
describe("planningSystemPrompt self-complete directive", () => {
|
|
5
|
+
it("guided synthesis instructs the agent to call pp_phase_complete when synthesis is complete", () => {
|
|
6
|
+
const prompt = planningSystemPrompt("/tmp/task", "guided");
|
|
7
|
+
expect(prompt).toContain("pp_phase_complete");
|
|
8
|
+
expect(prompt).toContain("Do NOT instead ask the user to run /pp manually");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("autonomous synthesis does not add the guided self-complete directive", () => {
|
|
12
|
+
const prompt = planningSystemPrompt("/tmp/task", "autonomous");
|
|
13
|
+
expect(prompt).not.toContain("pp_phase_complete");
|
|
14
|
+
expect(prompt).not.toContain("Do NOT instead ask the user to run /pp manually");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -5,7 +5,7 @@ import { resolvePreset, type PiPiConfig, type VariantConfig } from "../config.js
|
|
|
5
5
|
import { registerAgentDefinitions, spawnViaRpc, waitForCompletion } from "../agents/registry.js";
|
|
6
6
|
import { createPlannerAgent } from "../agents/planner.js";
|
|
7
7
|
import { createPlanReviewerAgent } from "../agents/plan-reviewer.js";
|
|
8
|
-
import { getContextDirs, getLatestSynthesizedPlan } from "../context.js";
|
|
8
|
+
import { getContextDirs, getLatestSynthesizedPlan, getArtifactManifest } from "../context.js";
|
|
9
9
|
import type { RepoInfo } from "../repo-utils.js";
|
|
10
10
|
import { validatePlan } from "../validate-artifacts.js";
|
|
11
11
|
import type { TaskMode } from "../state.js";
|
|
@@ -21,6 +21,10 @@ export function planningSystemPrompt(taskDir: string, mode: TaskMode): string {
|
|
|
21
21
|
mode === "autonomous"
|
|
22
22
|
? " If planner outputs CONTRADICT each other on a locked decision, resolve it by favoring USER_REQUEST.md, RECORD the contradiction and your chosen resolution in the plan, and proceed — do NOT stall waiting for the user (there is none)."
|
|
23
23
|
: " If planner outputs CONTRADICT each other on a locked decision, surface the contradiction to the user and let them decide — do NOT silently invent a compromise.";
|
|
24
|
+
const synthesizeCompletionRule =
|
|
25
|
+
mode === "autonomous"
|
|
26
|
+
? ""
|
|
27
|
+
: "When you judge the plan synthesis complete, call pp_phase_complete — the extension opens the advance gate for the user to review and confirm. Do NOT instead ask the user to run /pp manually.";
|
|
24
28
|
return [
|
|
25
29
|
"[PI-PI — PLAN PHASE]",
|
|
26
30
|
"",
|
|
@@ -46,9 +50,12 @@ export function planningSystemPrompt(taskDir: string, mode: TaskMode): string {
|
|
|
46
50
|
"- ## Scope: 2-4 lines — what changes, what doesn't, critical constraints",
|
|
47
51
|
"- ## Checklist: each item is - [ ] <outcome> — Done when: <observable condition>",
|
|
48
52
|
" Each item = one independently verifiable outcome. No code snippets or file-by-file instructions.",
|
|
53
|
+
"- ## Pattern constraints: include this section whenever the task adds a type, function, parser, annotation, config key, enum, or any user-facing value. For each, name the CLOSEST EXISTING analog in the codebase (found by behavior, not filename) and the exact conventions the implementer MUST mirror: data shape (prefer one existing shape over inventing parallel/duplicated state), spelling/casing of user-facing values (match existing values — never invent a new casing), and parser/validation/error-handling shape. These are acceptance criteria, not suggestions. Omit the section only if the task adds none of the above.",
|
|
49
54
|
"- ## Blockers: unresolved issues blocking implementation (omit if none)",
|
|
55
|
+
"Write/update the synthesized plan with pp_write_state_file / pp_edit_state_file (NOT the generic write/edit) — they keep the output compact and validate structure.",
|
|
50
56
|
"- No other top-level sections allowed",
|
|
51
|
-
"- Describe outcomes, not code-level mechanics",
|
|
57
|
+
"- Describe outcomes, not code-level mechanics, EXCEPT in ## Pattern constraints where naming the concrete analog and conventions is required",
|
|
58
|
+
...(synthesizeCompletionRule ? ["", synthesizeCompletionRule] : []),
|
|
52
59
|
].join("\n");
|
|
53
60
|
}
|
|
54
61
|
|
|
@@ -84,7 +91,7 @@ export async function spawnPlanners(
|
|
|
84
91
|
|
|
85
92
|
for (const [variant] of enabledVariants) {
|
|
86
93
|
const outputPath = join(plansDir, `${timestamp}_${variant}.md`);
|
|
87
|
-
const agent = createPlannerAgent(variant, plannerVariants, { userRequest, research }, outputPath, contextDirs, "plan", repos);
|
|
94
|
+
const agent = createPlannerAgent(variant, plannerVariants, { userRequest, research, manifest: getArtifactManifest(taskDir) }, outputPath, contextDirs, "plan", repos);
|
|
88
95
|
|
|
89
96
|
registerAgentDefinitions(pi, [{ type: "planner", variant, ...agent }]);
|
|
90
97
|
|
|
@@ -211,7 +218,7 @@ export async function spawnPlanReviewers(
|
|
|
211
218
|
const agent = createPlanReviewerAgent(
|
|
212
219
|
variant,
|
|
213
220
|
reviewerVariants,
|
|
214
|
-
{ userRequest, research, synthesizedPlan },
|
|
221
|
+
{ userRequest, research, synthesizedPlan, manifest: getArtifactManifest(taskDir) },
|
|
215
222
|
outputPath,
|
|
216
223
|
contextDirs,
|
|
217
224
|
"plan",
|
|
@@ -12,10 +12,7 @@ export function reviewSystemPrompt(taskDir: string, cwd: string): string {
|
|
|
12
12
|
"- read, lsp, grep, find for understanding the code",
|
|
13
13
|
"- gh pr view for GitHub PR context if a PR URL is mentioned",
|
|
14
14
|
"",
|
|
15
|
-
"
|
|
16
|
-
'- Agent(subagent_type="Explore", ...) — codebase research. Prefer this for most lookups. Fast and cheap.',
|
|
17
|
-
'- Agent(subagent_type="Librarian", ...) — external docs, library APIs, web research.',
|
|
18
|
-
"Spawn multiple Explore agents in parallel for broad searches.",
|
|
15
|
+
"Delegate to subagents where useful (see the delegation guidance in your system prompt) — spawn multiple explores in parallel for broad searches.",
|
|
19
16
|
"",
|
|
20
17
|
"Write your findings:",
|
|
21
18
|
`- ${taskDir}/USER_REQUEST.md — update with a clear problem statement of issues found`,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { reviewSystemPrompt } from "./review.js";
|
|
3
|
+
|
|
4
|
+
describe("reviewSystemPrompt apply_feedback wording", () => {
|
|
5
|
+
it("autonomous plan/implement mandates re-calling pp_phase_complete and does not tell the agent to wait for the user", () => {
|
|
6
|
+
const prompt = reviewSystemPrompt("/tmp/task", 1, "plan", "autonomous");
|
|
7
|
+
expect(prompt).toContain("pp_phase_complete");
|
|
8
|
+
expect(prompt).not.toContain("Present the synthesis to the user");
|
|
9
|
+
expect(prompt).not.toContain("A new review pass will begin");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("guided plan/implement keeps the user-facing synthesis behavior", () => {
|
|
13
|
+
const prompt = reviewSystemPrompt("/tmp/task", 1, "plan", "guided");
|
|
14
|
+
expect(prompt).toContain("Present the synthesis to the user");
|
|
15
|
+
expect(prompt).not.toContain("Call pp_phase_complete again to finalize");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("brainstorm prompt is unaffected by mode (never instructs pp_phase_complete)", () => {
|
|
19
|
+
const auto = reviewSystemPrompt("/tmp/task", 1, "brainstorm", "autonomous");
|
|
20
|
+
const guided = reviewSystemPrompt("/tmp/task", 1, "brainstorm", "guided");
|
|
21
|
+
expect(auto).toBe(guided);
|
|
22
|
+
expect(auto).toContain("BRAINSTORM REVIEW CYCLE");
|
|
23
|
+
expect(auto).not.toContain("pp_phase_complete");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("brainstorm apply-feedback prompt permits artifact updates and steers to the compact tools", () => {
|
|
27
|
+
const prompt = reviewSystemPrompt("/tmp/task", 1, "brainstorm", "autonomous");
|
|
28
|
+
expect(prompt).toContain("artifacts/");
|
|
29
|
+
expect(prompt).toContain("pp_write_state_file");
|
|
30
|
+
// The no-new-sections rule for the two structured files is retained.
|
|
31
|
+
expect(prompt).toContain("Do NOT add, rename, or remove sections in USER_REQUEST.md or RESEARCH.md");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("autonomous plan feedback folds into a new synthesized plan, not a separate fix-plan file", () => {
|
|
35
|
+
// Re-review and the transition read only the latest `*synthesized*` plan, so
|
|
36
|
+
// plan-phase feedback must land there.
|
|
37
|
+
const plan = reviewSystemPrompt("/tmp/task", 1, "plan", "autonomous");
|
|
38
|
+
expect(plan).toContain("synthesized.md");
|
|
39
|
+
expect(plan).not.toContain("do NOT modify the original synthesized plan");
|
|
40
|
+
|
|
41
|
+
// The implement phase keeps the fix-plan/implement pattern (synthesized plan
|
|
42
|
+
// is code guidance there, not the reviewed artifact).
|
|
43
|
+
const impl = reviewSystemPrompt("/tmp/task", 1, "implement", "autonomous");
|
|
44
|
+
expect(impl).toContain("Create a fix plan");
|
|
45
|
+
expect(impl).toContain("Implement the fixes");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("points each phase at the directory its reviewers actually write to", () => {
|
|
49
|
+
// plan reviewers write to plan-reviews (planning.ts) and outputs load from
|
|
50
|
+
// plan-reviews (context.ts); the prompt must match, not code-reviews.
|
|
51
|
+
const plan = reviewSystemPrompt("/tmp/task", 1, "plan", "autonomous");
|
|
52
|
+
expect(plan).toContain("plan-reviews/");
|
|
53
|
+
expect(plan).not.toContain("code-reviews/");
|
|
54
|
+
|
|
55
|
+
const impl = reviewSystemPrompt("/tmp/task", 1, "implement", "autonomous");
|
|
56
|
+
expect(impl).toContain("code-reviews/");
|
|
57
|
+
expect(impl).not.toContain("plan-reviews/");
|
|
58
|
+
|
|
59
|
+
const brainstorm = reviewSystemPrompt("/tmp/task", 1, "brainstorm", "guided");
|
|
60
|
+
expect(brainstorm).toContain("brainstorm-reviews/");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -4,7 +4,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
import { resolvePreset, type PiPiConfig, type VariantConfig } from "../config.js";
|
|
5
5
|
import { registerAgentDefinitions, spawnViaRpc, waitForCompletion } from "../agents/registry.js";
|
|
6
6
|
import { createCodeReviewerAgent } from "../agents/code-reviewer.js";
|
|
7
|
-
import { getContextDirs, getLatestSynthesizedPlan } from "../context.js";
|
|
7
|
+
import { getContextDirs, getLatestSynthesizedPlan, getArtifactManifest } from "../context.js";
|
|
8
8
|
import type { RepoInfo } from "../repo-utils.js";
|
|
9
9
|
import type { PhaseSend } from "../transition-controller.js";
|
|
10
10
|
|
|
@@ -12,8 +12,14 @@ function isEnabled(value: { enabled?: boolean } | undefined): boolean {
|
|
|
12
12
|
return value?.enabled !== false;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function reviewSystemPrompt(taskDir: string, pass: number, phase?: string): string {
|
|
16
|
-
|
|
15
|
+
export function reviewSystemPrompt(taskDir: string, pass: number, phase?: string, mode?: "guided" | "autonomous"): string {
|
|
16
|
+
// Each phase writes/loads its review outputs in a distinct directory:
|
|
17
|
+
// brainstorm -> brainstorm-reviews, plan -> plan-reviews, everything else
|
|
18
|
+
// (implement/review) -> code-reviews. The apply_feedback prompt must point the
|
|
19
|
+
// agent at the SAME directory the reviewers wrote to (see planning.ts /
|
|
20
|
+
// context.ts), otherwise it synthesizes against the wrong (empty) directory.
|
|
21
|
+
const reviewsDirName = phase === "brainstorm" ? "brainstorm-reviews" : phase === "plan" ? "plan-reviews" : "code-reviews";
|
|
22
|
+
const reviewsDir = join(taskDir, reviewsDirName);
|
|
17
23
|
const plansDir = join(taskDir, "plans");
|
|
18
24
|
|
|
19
25
|
if (phase === "brainstorm") {
|
|
@@ -32,36 +38,73 @@ export function reviewSystemPrompt(taskDir: string, pass: number, phase?: string
|
|
|
32
38
|
"# Your job:",
|
|
33
39
|
`1. Read ALL reviewer outputs from ${reviewsDir}/`,
|
|
34
40
|
"2. Identify valid gaps and inaccuracies that would block planning",
|
|
35
|
-
"3. If changes are needed: update
|
|
41
|
+
"3. If changes are needed: update the content within existing sections of USER_REQUEST.md / RESEARCH.md, and add/update artifacts/ files as needed",
|
|
36
42
|
"4. If reviewers found no actionable gaps (e.g. task already done, minor suggestions only): do NOT modify the files",
|
|
37
43
|
"5. Ignore suggestions that don't affect downstream planning quality",
|
|
38
44
|
"",
|
|
39
45
|
"USER_REQUEST.md MUST keep exactly: # User Request, ## Problem, ## Constraints",
|
|
40
46
|
"RESEARCH.md MUST keep exactly: ## Affected Code, ## Architecture Context, ## Constraints & Edge Cases, ## Open Questions (optional)",
|
|
41
47
|
"Any other sections will fail validation.",
|
|
48
|
+
"Use pp_write_state_file / pp_edit_state_file (NOT the generic write/edit) for these .pp state files — they keep the output compact and validate structure.",
|
|
42
49
|
].join("\n");
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
// In autonomous plan/implement the phase does NOT complete until the agent
|
|
53
|
+
// re-calls pp_phase_complete: that is what finalizes the pass and advances the
|
|
54
|
+
// phase. Telling it to "present to the user" and wait causes the plan phase to
|
|
55
|
+
// stall after applying feedback. Guided mode keeps the user-facing behavior.
|
|
56
|
+
const finalStep =
|
|
57
|
+
mode === "autonomous"
|
|
58
|
+
? "3. Call pp_phase_complete again to finalize this review pass. The phase is NOT complete until you do — do NOT stop or wait for the user"
|
|
59
|
+
: "3. Present the synthesis to the user";
|
|
60
|
+
|
|
61
|
+
// In the plan phase the synthesized plan IS the reviewed artifact: re-review
|
|
62
|
+
// and the phase transition both read only the LATEST `*synthesized*` file (see
|
|
63
|
+
// getLatestSynthesizedPlan). So autonomous plan feedback must be folded into a
|
|
64
|
+
// new synthesized plan, NOT a separate fix-plan file (which those readers
|
|
65
|
+
// ignore). In the implement phase the synthesized plan is code guidance, so
|
|
66
|
+
// the fix-plan/implement/afterImplement pattern is correct there.
|
|
67
|
+
const tail =
|
|
68
|
+
mode === "autonomous"
|
|
69
|
+
? phase === "plan"
|
|
70
|
+
? [
|
|
71
|
+
"",
|
|
72
|
+
"If the reviewers require changes:",
|
|
73
|
+
`1. Fold the required changes into a NEW synthesized plan at ${plansDir}/<timestamp>_synthesized.md (re-review and the phase transition read only the latest \`*synthesized*\` plan, so the fixes MUST land there — do not write them to a separate fix-plan file)`,
|
|
74
|
+
"2. Then call pp_phase_complete again — the extension will start a new review pass or advance the phase as appropriate. Do NOT wait for the user.",
|
|
75
|
+
]
|
|
76
|
+
: [
|
|
77
|
+
"",
|
|
78
|
+
"If changes are needed:",
|
|
79
|
+
`1. Create a fix plan at ${plansDir}/<timestamp>_<description>.md (do NOT modify the original synthesized plan)`,
|
|
80
|
+
"2. Implement the fixes",
|
|
81
|
+
"3. Run afterImplement commands",
|
|
82
|
+
"4. Then call pp_phase_complete again — the extension will start a new review pass or advance the phase as appropriate. Do NOT wait for the user.",
|
|
83
|
+
]
|
|
84
|
+
: [
|
|
85
|
+
"",
|
|
86
|
+
"If changes are needed:",
|
|
87
|
+
`1. Create a fix plan at ${plansDir}/<timestamp>_<description>.md (do NOT modify the original synthesized plan)`,
|
|
88
|
+
"2. Implement the fixes",
|
|
89
|
+
"3. Run afterImplement commands",
|
|
90
|
+
"4. A new review pass will begin",
|
|
91
|
+
];
|
|
92
|
+
|
|
45
93
|
return [
|
|
46
94
|
`[PI-PI — REVIEW CYCLE (pass ${pass})]`,
|
|
47
95
|
"",
|
|
48
|
-
"
|
|
96
|
+
"Reviewer outputs are ready.",
|
|
49
97
|
`Read them from ${reviewsDir}/, synthesize feedback, and implement fixes if needed.`,
|
|
50
98
|
"",
|
|
51
|
-
"You are a SYNTHESIZER: merge the reviewer outputs. Do NOT write your own
|
|
52
|
-
|
|
99
|
+
"You are a SYNTHESIZER: merge the reviewer outputs. Do NOT write your own review from scratch.",
|
|
100
|
+
`- Do NOT create the ${reviewsDirName}/ directory yourself — the extension manages it.`,
|
|
53
101
|
"- Do NOT call plannotator_submit_plan.",
|
|
54
102
|
"",
|
|
55
103
|
"# Your job (in this order):",
|
|
56
104
|
`1. Read ALL reviewer outputs from ${reviewsDir}/`,
|
|
57
105
|
`2. Synthesize into ${reviewsDir}/<timestamp>_final_pass-${pass}.md`,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"If changes are needed:",
|
|
61
|
-
`1. Create a fix plan at ${plansDir}/<timestamp>_<description>.md (do NOT modify the original synthesized plan)`,
|
|
62
|
-
"2. Implement the fixes",
|
|
63
|
-
"3. Run afterImplement commands",
|
|
64
|
-
"4. A new review pass will begin",
|
|
106
|
+
finalStep,
|
|
107
|
+
...tail,
|
|
65
108
|
].join("\n");
|
|
66
109
|
}
|
|
67
110
|
|
|
@@ -117,7 +160,7 @@ export async function spawnCodeReviewers(
|
|
|
117
160
|
const agent = createCodeReviewerAgent(
|
|
118
161
|
variant,
|
|
119
162
|
reviewerVariants,
|
|
120
|
-
{ userRequest, research, synthesizedPlan },
|
|
163
|
+
{ userRequest, research, synthesizedPlan, manifest: getArtifactManifest(taskDir) },
|
|
121
164
|
outputPath,
|
|
122
165
|
contextDirs,
|
|
123
166
|
reviewerPhase,
|