@ilya-lesikov/pi-pi 0.7.0 → 0.9.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-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 +27 -11
- package/extensions/orchestrator/agents/constraints.test.ts +82 -0
- package/extensions/orchestrator/agents/constraints.ts +66 -2
- 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 +39 -1
- package/extensions/orchestrator/ai-comment-cleanup.test.ts +89 -0
- package/extensions/orchestrator/ai-comment-cleanup.ts +73 -0
- package/extensions/orchestrator/command-handlers.test.ts +47 -0
- package/extensions/orchestrator/command-handlers.ts +44 -28
- package/extensions/orchestrator/config.test.ts +40 -1
- package/extensions/orchestrator/config.ts +18 -2
- package/extensions/orchestrator/context.test.ts +54 -0
- package/extensions/orchestrator/context.ts +81 -2
- package/extensions/orchestrator/custom-footer.test.ts +91 -0
- package/extensions/orchestrator/custom-footer.ts +20 -20
- package/extensions/orchestrator/event-handlers.test.ts +412 -2
- package/extensions/orchestrator/event-handlers.ts +556 -227
- package/extensions/orchestrator/flant-infra.test.ts +25 -0
- package/extensions/orchestrator/flant-infra.ts +91 -0
- package/extensions/orchestrator/index.ts +1 -1
- package/extensions/orchestrator/integration.test.ts +411 -20
- package/extensions/orchestrator/messages.test.ts +30 -0
- package/extensions/orchestrator/messages.ts +6 -0
- package/extensions/orchestrator/model-registry.test.ts +48 -1
- package/extensions/orchestrator/model-registry.ts +43 -1
- package/extensions/orchestrator/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/extensions/orchestrator/orchestrator.test.ts +113 -0
- package/extensions/orchestrator/orchestrator.ts +197 -60
- package/extensions/orchestrator/phases/brainstorm.ts +20 -27
- package/extensions/orchestrator/phases/implementation.test.ts +11 -0
- package/extensions/orchestrator/phases/implementation.ts +4 -6
- package/extensions/orchestrator/phases/machine.test.ts +36 -0
- package/extensions/orchestrator/phases/machine.ts +11 -1
- package/extensions/orchestrator/phases/planning.test.ts +16 -0
- package/extensions/orchestrator/phases/planning.ts +11 -4
- package/extensions/orchestrator/phases/review-task.test.ts +20 -0
- package/extensions/orchestrator/phases/review-task.ts +47 -22
- package/extensions/orchestrator/phases/review.test.ts +34 -0
- package/extensions/orchestrator/phases/review.ts +44 -6
- package/extensions/orchestrator/plannotator.ts +9 -6
- package/extensions/orchestrator/pp-menu.test.ts +207 -1
- package/extensions/orchestrator/pp-menu.ts +514 -402
- 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.test.ts +9 -0
- package/extensions/orchestrator/state.ts +15 -0
- package/extensions/orchestrator/test-helpers.ts +4 -1
- package/extensions/orchestrator/transition-controller.test.ts +100 -0
- package/extensions/orchestrator/transition-controller.ts +61 -3
- package/extensions/orchestrator/usage-tracker.ts +5 -1
- package/extensions/orchestrator/validate-artifacts.test.ts +20 -0
- package/extensions/orchestrator/validate-artifacts.ts +2 -2
- package/package.json +1 -1
- package/AGENTS.md +0 -28
|
@@ -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
|
"",
|
|
@@ -115,25 +107,25 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
115
107
|
"",
|
|
116
108
|
registerReposInstruction,
|
|
117
109
|
"",
|
|
118
|
-
"Your job is to produce USER_REQUEST.md and RESEARCH.md — complete enough that",
|
|
119
|
-
"downstream agents can work without re-exploring the codebase or re-interviewing the user.",
|
|
110
|
+
"This is a clarify + research + DESIGN phase. Your job is to produce USER_REQUEST.md and RESEARCH.md — complete enough that",
|
|
111
|
+
"downstream agents can work without re-exploring the codebase or re-interviewing the user. That means not just describing what",
|
|
112
|
+
"exists, but exploring the design space: weigh the viable approaches and their tradeoffs so the plan phase inherits a clear",
|
|
113
|
+
"direction rather than an open-ended problem.",
|
|
120
114
|
"",
|
|
121
115
|
"# Steps:",
|
|
122
116
|
"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.",
|
|
117
|
+
"2. Delegate research to subagents where useful (see the delegation guidance in your system prompt) — spawn multiple explores in parallel for broad searches",
|
|
128
118
|
"3. Use tools to understand code structure:",
|
|
129
119
|
" - cbm_search: natural-language search across all symbols",
|
|
130
120
|
" - cbm_search_code: graph-augmented grep (deduplicates into functions)",
|
|
131
121
|
" - lsp documentSymbol, goToDefinition, findReferences, goToImplementation, hover",
|
|
132
122
|
" - cbm_trace: trace call chains for dependency understanding",
|
|
133
123
|
" - ast_search: find structural patterns across the codebase",
|
|
134
|
-
"4.
|
|
135
|
-
"5.
|
|
136
|
-
"6.
|
|
124
|
+
"4. Explore design options: identify the viable approaches, weigh their tradeoffs, and land on a recommended direction (capture the reasoning in RESEARCH.md / an artifact)",
|
|
125
|
+
"5. Ask the user follow-up questions as needed",
|
|
126
|
+
"6. Actively drive every Open Question to resolution — chase down answers via research or by asking the user; the Open Questions section should be empty (or every entry marked DECIDED) before you hand off, not a passive backlog",
|
|
127
|
+
"7. Write findings into RESEARCH.md as results come back — don't wait for all subagents",
|
|
128
|
+
"8. Keep USER_REQUEST.md current: update it whenever the user's request changes or clarifies, so it reflects what the user actually wants — don't write it once and leave it stale",
|
|
137
129
|
"",
|
|
138
130
|
"Produce two files:",
|
|
139
131
|
`- ${taskDir}/USER_REQUEST.md — MUST follow this exact structure:`,
|
|
@@ -152,13 +144,14 @@ export function brainstormSystemPrompt(taskType: TaskType, taskDescription: stri
|
|
|
152
144
|
" - MUST: <hard requirements discovered from code>",
|
|
153
145
|
" - RISK: <things that could break>",
|
|
154
146
|
" ## Open Questions",
|
|
155
|
-
" <Unresolved items needing user input.
|
|
147
|
+
" <Unresolved items needing user input. Drive these to resolution before advancing — omit the section only when genuinely none remain.>",
|
|
156
148
|
"",
|
|
157
149
|
"These files are validated programmatically. Missing sections or unexpected sections will be rejected.",
|
|
150
|
+
"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
151
|
"",
|
|
159
152
|
"# Optional: focused analysis artifacts",
|
|
160
153
|
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
161
|
-
"for deep dives on specific topics (e.g. architecture analysis, API comparison, risk assessment).",
|
|
154
|
+
"for deep dives on specific topics (e.g. architecture analysis, API comparison, design-option/tradeoff analysis, risk assessment).",
|
|
162
155
|
"Each artifact must start with # <Title>. Content is freeform. These are reviewed alongside USER_REQUEST.md and RESEARCH.md.",
|
|
163
156
|
"Do NOT duplicate content already in RESEARCH.md — artifacts are for supplementary deep dives.",
|
|
164
157
|
].join("\n");
|
|
@@ -210,7 +203,7 @@ export async function spawnBrainstormReviewers(
|
|
|
210
203
|
const agent = createBrainstormReviewerAgent(
|
|
211
204
|
variant,
|
|
212
205
|
reviewerVariants,
|
|
213
|
-
{ userRequest, research, artifacts: artifacts.length > 0 ? artifacts : undefined },
|
|
206
|
+
{ userRequest, research, artifacts: artifacts.length > 0 ? artifacts : undefined, manifest: getArtifactManifest(taskDir) },
|
|
214
207
|
outputPath,
|
|
215
208
|
contextDirs,
|
|
216
209
|
"brainstorm",
|
|
@@ -261,7 +254,7 @@ export async function spawnBrainstormReviewers(
|
|
|
261
254
|
`${reviewOutputFiles.length} brainstorm reviewer(s) completed (round ${round}). Reviews in ${reviewsDir}:`,
|
|
262
255
|
...reviewOutputFiles.map((f) => ` - ${f}`),
|
|
263
256
|
"",
|
|
264
|
-
"Read all reviews and update USER_REQUEST.md
|
|
257
|
+
"Read all reviews and update USER_REQUEST.md, RESEARCH.md, and any artifacts/ files if needed.",
|
|
265
258
|
].join("\n"),
|
|
266
259
|
display: true,
|
|
267
260
|
},
|
|
@@ -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
|
}
|
|
@@ -297,6 +297,42 @@ Fix bug.
|
|
|
297
297
|
expect(validateExitCriteria(pass, "debug", "debug")).toEqual({ ok: true });
|
|
298
298
|
});
|
|
299
299
|
|
|
300
|
+
it("validates review phase — requires artifacts and an ANCHORS-bearing final_pass file", () => {
|
|
301
|
+
const missing = makeTempDir();
|
|
302
|
+
expect(validateExitCriteria(missing, "review", "review")).toEqual({
|
|
303
|
+
ok: false,
|
|
304
|
+
reason: "USER_REQUEST.md does not exist or is empty",
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const noAnchors = makeTempDir();
|
|
308
|
+
writeFileSync(join(noAnchors, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
309
|
+
writeFileSync(join(noAnchors, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
310
|
+
const res1 = validateExitCriteria(noAnchors, "review", "review");
|
|
311
|
+
expect(res1.ok).toBe(false);
|
|
312
|
+
expect((res1 as { reason: string }).reason).toContain("ANCHORS");
|
|
313
|
+
|
|
314
|
+
const emptyAnchorsFile = makeTempDir();
|
|
315
|
+
writeFileSync(join(emptyAnchorsFile, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
316
|
+
writeFileSync(join(emptyAnchorsFile, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
317
|
+
mkdirSync(join(emptyAnchorsFile, "code-reviews"), { recursive: true });
|
|
318
|
+
writeFileSync(join(emptyAnchorsFile, "code-reviews", "1_final_pass-1.md"), "no anchors here", "utf-8");
|
|
319
|
+
expect(validateExitCriteria(emptyAnchorsFile, "review", "review").ok).toBe(false);
|
|
320
|
+
|
|
321
|
+
const zeroFindings = makeTempDir();
|
|
322
|
+
writeFileSync(join(zeroFindings, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
323
|
+
writeFileSync(join(zeroFindings, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
324
|
+
mkdirSync(join(zeroFindings, "code-reviews"), { recursive: true });
|
|
325
|
+
writeFileSync(join(zeroFindings, "code-reviews", "1_final_pass-1.md"), "# Review\nANCHORS: (none)\n", "utf-8");
|
|
326
|
+
expect(validateExitCriteria(zeroFindings, "review", "review")).toEqual({ ok: true });
|
|
327
|
+
|
|
328
|
+
const withFindings = makeTempDir();
|
|
329
|
+
writeFileSync(join(withFindings, "USER_REQUEST.md"), VALID_USER_REQUEST, "utf-8");
|
|
330
|
+
writeFileSync(join(withFindings, "RESEARCH.md"), VALID_RESEARCH, "utf-8");
|
|
331
|
+
mkdirSync(join(withFindings, "code-reviews"), { recursive: true });
|
|
332
|
+
writeFileSync(join(withFindings, "code-reviews", "1_final_pass-2.md"), "ANCHORS:\n- src/a.ts:12 must fix\n", "utf-8");
|
|
333
|
+
expect(validateExitCriteria(withFindings, "review", "review")).toEqual({ ok: true });
|
|
334
|
+
});
|
|
335
|
+
|
|
300
336
|
it("returns unknown phase error for done", () => {
|
|
301
337
|
const dir = makeTempDir();
|
|
302
338
|
expect(validateExitCriteria(dir, "implement", "done")).toEqual({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
import type { TaskType, Phase } from "../state.js";
|
|
4
|
-
import { getLatestSynthesizedPlan } from "../context.js";
|
|
4
|
+
import { getLatestSynthesizedPlan, hasFinalPassAnchors } from "../context.js";
|
|
5
5
|
import { validatePlan, validateResearch, validateUserRequest } from "../validate-artifacts.js";
|
|
6
6
|
|
|
7
7
|
const USER_REQUEST_TEMPLATE = [
|
|
@@ -156,6 +156,16 @@ export function validateExitCriteria(
|
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
if (!hasFinalPassAnchors(taskDir)) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
reason:
|
|
163
|
+
"No ANCHORS-bearing final review file exists. Write the review findings to " +
|
|
164
|
+
"`code-reviews/*_final_pass-*.md` with an `ANCHORS:` block (use `ANCHORS: (none)` " +
|
|
165
|
+
"if there are no findings) before completing the review.",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
159
169
|
return { ok: true };
|
|
160
170
|
}
|
|
161
171
|
|
|
@@ -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",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { reviewSystemPrompt } from "./review-task.js";
|
|
3
|
+
|
|
4
|
+
describe("review-task reviewSystemPrompt", () => {
|
|
5
|
+
it("mandates writing an ANCHORS-bearing final_pass file via the generic write tool", () => {
|
|
6
|
+
const prompt = reviewSystemPrompt("/tmp/task", "/tmp/cwd");
|
|
7
|
+
expect(prompt).toContain("/tmp/task/code-reviews/<unix-epoch-seconds>_final_pass-1.md");
|
|
8
|
+
expect(prompt).toContain("ANCHORS:");
|
|
9
|
+
expect(prompt).toContain("<relative/path/from/repo/root>:<line> — <one-line finding>");
|
|
10
|
+
expect(prompt).toContain("(none)");
|
|
11
|
+
expect(prompt).toContain("GENERIC write tool");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("still captures the structured USER_REQUEST.md / RESEARCH.md deliverables", () => {
|
|
15
|
+
const prompt = reviewSystemPrompt("/tmp/task", "/tmp/cwd");
|
|
16
|
+
expect(prompt).toContain("USER_REQUEST.md");
|
|
17
|
+
expect(prompt).toContain("RESEARCH.md");
|
|
18
|
+
expect(prompt).toContain("pp_write_state_file");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -4,34 +4,59 @@ export function reviewSystemPrompt(taskDir: string, cwd: string): string {
|
|
|
4
4
|
"",
|
|
5
5
|
`First, register all git repositories you'll work in using pp_register_repo (including the root: ${cwd}). For each, determine the base branch by examining the current branch and remote tracking.`,
|
|
6
6
|
"",
|
|
7
|
-
"You are reviewing code changes.
|
|
8
|
-
"
|
|
7
|
+
"You are reviewing code changes. The user will describe what to review in their first chat message",
|
|
8
|
+
"(e.g. a branch, a commit range, uncommitted changes, or a GitHub PR URL). There is NO pre-seeded",
|
|
9
|
+
"USER_REQUEST.md — you must capture the user's request and your findings into the state files yourself.",
|
|
10
|
+
"If the scope is ambiguous, ask the user before diving in.",
|
|
11
|
+
"",
|
|
12
|
+
"If the review scope is a GitHub PR: for each registered repo, resolve the PR with `gh pr view --json number,headRefName,headRefOid,url`, then call pp_checkout_pr_head with that repo's `headRefName`/`headRefOid` to land it on the PR head BEFORE analyzing it. If the tool HALTS (dirty tree, different branch, or diverged branch), relay its message to the user and wait for them to resolve it before continuing. For a branch, commit-range, or uncommitted-changes review, do NOT call pp_checkout_pr_head.",
|
|
9
13
|
"",
|
|
10
14
|
"Use available tools to analyze the changes:",
|
|
11
15
|
"- git diff, git log, git show for examining commits and diffs",
|
|
12
16
|
"- read, lsp, grep, find for understanding the code",
|
|
13
17
|
"- gh pr view for GitHub PR context if a PR URL is mentioned",
|
|
14
|
-
"",
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"",
|
|
24
|
-
"
|
|
25
|
-
"-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"",
|
|
29
|
-
"
|
|
30
|
-
"-
|
|
31
|
-
"
|
|
32
|
-
"-
|
|
33
|
-
"-
|
|
18
|
+
"- cbm_search/cbm_search_code, lsp goToDefinition/findReferences/hover, cbm_trace, ast_search for deeper analysis",
|
|
19
|
+
"",
|
|
20
|
+
"Delegate to subagents where useful (see the delegation guidance in your system prompt) — spawn multiple explores in parallel for broad searches.",
|
|
21
|
+
"",
|
|
22
|
+
"Produce two files:",
|
|
23
|
+
`- ${taskDir}/USER_REQUEST.md — MUST follow this exact structure:`,
|
|
24
|
+
" # User Request",
|
|
25
|
+
" <1-3 sentence distillation of what the user asked you to review>",
|
|
26
|
+
" ## Problem",
|
|
27
|
+
" <What is being reviewed and why, in the user's words. PR/issue link if provided.>",
|
|
28
|
+
" ## Constraints",
|
|
29
|
+
" <Boundaries the user explicitly stated. Only user-stated info, no agent findings.>",
|
|
30
|
+
`- ${taskDir}/RESEARCH.md — MUST follow this exact structure:`,
|
|
31
|
+
" ## Affected Code",
|
|
32
|
+
" <file:symbol — one-line role, per line>",
|
|
33
|
+
" ## Architecture Context",
|
|
34
|
+
" <Dense bullets. How the changed pieces connect. Sub-group by subsystem for complex reviews.>",
|
|
35
|
+
" ## Constraints & Edge Cases",
|
|
36
|
+
" - MUST: <hard requirements the change must satisfy>",
|
|
37
|
+
" - RISK: <bugs, regressions, edge cases the change could break>",
|
|
38
|
+
" ## Open Questions",
|
|
39
|
+
" <Low-confidence concerns or items needing user input. Omit section if none.>",
|
|
40
|
+
"",
|
|
41
|
+
"These files are validated programmatically. Missing sections or unexpected sections will be rejected.",
|
|
42
|
+
"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.",
|
|
43
|
+
"Keep USER_REQUEST.md current: update it whenever the user clarifies the review scope, so it never goes stale.",
|
|
44
|
+
"",
|
|
45
|
+
"# Optional: focused analysis artifacts",
|
|
46
|
+
`You may also write additional analysis files to ${taskDir}/artifacts/<name>.md`,
|
|
47
|
+
"for deep dives on specific findings (e.g. a subtle bug, a design concern, a risk assessment).",
|
|
48
|
+
"Each artifact must start with # <Title>. Content is freeform. These are reviewed alongside USER_REQUEST.md and RESEARCH.md.",
|
|
49
|
+
"Do NOT duplicate content already in RESEARCH.md — artifacts are for supplementary deep dives.",
|
|
34
50
|
"",
|
|
35
51
|
"Focus on: correctness, edge cases, style consistency, missing tests, potential bugs.",
|
|
52
|
+
"You are investigating and documenting findings — you do NOT apply fixes in this phase.",
|
|
53
|
+
"",
|
|
54
|
+
"# Required: findings file with an ANCHORS block",
|
|
55
|
+
`When you finish reviewing, you MUST write your findings to ${taskDir}/code-reviews/<unix-epoch-seconds>_final_pass-1.md (use the GENERIC write tool, NOT pp_write_state_file — code-reviews/ is not a managed state path; the <unix-epoch-seconds> prefix, e.g. the output of \`date +%s\`, keeps it ordered against automated review-cycle files). Create the code-reviews/ directory if it does not exist.`,
|
|
56
|
+
"That file MUST include a machine-readable `ANCHORS:` block for the accepted findings — one line per finding in EXACTLY this format (the /pp Publish step consumes these lines):",
|
|
57
|
+
"ANCHORS:",
|
|
58
|
+
"<relative/path/from/repo/root>:<line> — <one-line finding>",
|
|
59
|
+
"Use the real file:line for each finding — do NOT invent locations. Write `ANCHORS:` followed by `(none)` if there are no anchorable accepted findings.",
|
|
60
|
+
"This is a normal deliverable of every review, independent of any automated review cycle: without it, publishing has nothing to anchor.",
|
|
36
61
|
].join("\n");
|
|
37
62
|
}
|
|
@@ -23,6 +23,14 @@ describe("reviewSystemPrompt apply_feedback wording", () => {
|
|
|
23
23
|
expect(auto).not.toContain("pp_phase_complete");
|
|
24
24
|
});
|
|
25
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
|
+
|
|
26
34
|
it("autonomous plan feedback folds into a new synthesized plan, not a separate fix-plan file", () => {
|
|
27
35
|
// Re-review and the transition read only the latest `*synthesized*` plan, so
|
|
28
36
|
// plan-phase feedback must land there.
|
|
@@ -37,6 +45,32 @@ describe("reviewSystemPrompt apply_feedback wording", () => {
|
|
|
37
45
|
expect(impl).toContain("Implement the fixes");
|
|
38
46
|
});
|
|
39
47
|
|
|
48
|
+
it("standalone review phase omits the fix-plan/implement/afterImplement tail", () => {
|
|
49
|
+
const prompt = reviewSystemPrompt("/tmp/task", 1, "review", "guided");
|
|
50
|
+
expect(prompt).toContain("REVIEW CYCLE");
|
|
51
|
+
expect(prompt).toContain("standalone review");
|
|
52
|
+
expect(prompt).not.toContain("Create a fix plan");
|
|
53
|
+
expect(prompt).not.toContain("Implement the fixes");
|
|
54
|
+
expect(prompt).not.toContain("Run afterImplement commands");
|
|
55
|
+
expect(prompt).toContain("code-reviews/");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("standalone review synthesis emits an ANCHORS block and defers publishing to the user", () => {
|
|
59
|
+
const prompt = reviewSystemPrompt("/tmp/task", 1, "review", "guided");
|
|
60
|
+
expect(prompt).toContain("ANCHORS:");
|
|
61
|
+
expect(prompt).toContain("Do NOT publish findings now");
|
|
62
|
+
expect(prompt).not.toContain("do NOT call `gh` yourself");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("review-cycle synthesis does NOT embed the Review Summary schema (delivered via the closing block)", () => {
|
|
66
|
+
// The schema is injected once by constraintsBlock -> closingBlockInstruction("review"),
|
|
67
|
+
// which is always prepended to the phase prompt; repeating it here would duplicate it.
|
|
68
|
+
for (const mode of ["guided", "autonomous"] as const) {
|
|
69
|
+
const prompt = reviewSystemPrompt("/tmp/task", 1, "review", mode);
|
|
70
|
+
expect(prompt).not.toContain("## Review Summary");
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
40
74
|
it("points each phase at the directory its reviewers actually write to", () => {
|
|
41
75
|
// plan reviewers write to plan-reviews (planning.ts) and outputs load from
|
|
42
76
|
// plan-reviews (context.ts); the prompt must match, not code-reviews.
|
|
@@ -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
|
|
|
@@ -38,13 +38,48 @@ export function reviewSystemPrompt(taskDir: string, pass: number, phase?: string
|
|
|
38
38
|
"# Your job:",
|
|
39
39
|
`1. Read ALL reviewer outputs from ${reviewsDir}/`,
|
|
40
40
|
"2. Identify valid gaps and inaccuracies that would block planning",
|
|
41
|
-
"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",
|
|
42
42
|
"4. If reviewers found no actionable gaps (e.g. task already done, minor suggestions only): do NOT modify the files",
|
|
43
43
|
"5. Ignore suggestions that don't affect downstream planning quality",
|
|
44
44
|
"",
|
|
45
45
|
"USER_REQUEST.md MUST keep exactly: # User Request, ## Problem, ## Constraints",
|
|
46
46
|
"RESEARCH.md MUST keep exactly: ## Affected Code, ## Architecture Context, ## Constraints & Edge Cases, ## Open Questions (optional)",
|
|
47
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.",
|
|
49
|
+
].join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// A standalone review task's "review" phase has nothing to implement: the output
|
|
53
|
+
// is the synthesized findings. So it must NOT get the "create a fix plan /
|
|
54
|
+
// implement / run afterImplement" tail that implement-phase review uses. The user
|
|
55
|
+
// publishes findings (as file comments and/or GitHub PR comments) via the /pp
|
|
56
|
+
// "Publish" menu after synthesis — which consumes the ANCHORS: block below.
|
|
57
|
+
if (phase === "review") {
|
|
58
|
+
return [
|
|
59
|
+
`[PI-PI — REVIEW CYCLE (pass ${pass})]`,
|
|
60
|
+
"",
|
|
61
|
+
"Reviewer outputs are ready.",
|
|
62
|
+
`Read them from ${reviewsDir}/ and synthesize the findings.`,
|
|
63
|
+
"",
|
|
64
|
+
"You are a SYNTHESIZER: merge the reviewer outputs. Do NOT write your own review from scratch.",
|
|
65
|
+
`- Do NOT create the ${reviewsDirName}/ directory yourself — the extension manages it.`,
|
|
66
|
+
"- This is a standalone review: do NOT create a fix plan, implement fixes, run afterImplement commands, or commit.",
|
|
67
|
+
"- Do NOT publish findings now (no source edits, no `gh`); the user triggers publishing from the /pp menu.",
|
|
68
|
+
"",
|
|
69
|
+
"# Your job (in this order):",
|
|
70
|
+
`1. Read ALL reviewer outputs from ${reviewsDir}/`,
|
|
71
|
+
`2. Synthesize into ${reviewsDir}/<unix-epoch-seconds>_final_pass-${pass}.md (prefix with the current Unix epoch seconds, e.g. \`date +%s\`, so the file orders chronologically)`,
|
|
72
|
+
"3. Present the synthesis to the user",
|
|
73
|
+
"",
|
|
74
|
+
"In the synthesized final-review file you MUST include a machine-readable `ANCHORS:` block for the accepted findings — one line per finding in EXACTLY this format (a later publish step consumes these lines):",
|
|
75
|
+
"ANCHORS:",
|
|
76
|
+
"<relative/path/from/repo/root>:<line> — <one-line finding>",
|
|
77
|
+
"Use the `ANCHORS:` blocks the reviewers emitted as the source of file:line — do NOT invent locations. Write `ANCHORS:` followed by `(none)` if there are no anchorable accepted findings.",
|
|
78
|
+
"",
|
|
79
|
+
"PRIVACY: phrase every finding as a self-contained observation about the code. Do NOT reference private or internal details, `the ticket`, issue trackers, or internal design docs — these findings are published as PR/file comments. Say what is wrong in the code, not that it violates a private document's goal.",
|
|
80
|
+
// The Review Summary schema is delivered by the standardized closing block
|
|
81
|
+
// (constraints.ts closingBlockInstruction, gated to phase==="review"), which is
|
|
82
|
+
// always prepended to this prompt — so it is intentionally NOT repeated here.
|
|
48
83
|
].join("\n");
|
|
49
84
|
}
|
|
50
85
|
|
|
@@ -101,7 +136,7 @@ export function reviewSystemPrompt(taskDir: string, pass: number, phase?: string
|
|
|
101
136
|
"",
|
|
102
137
|
"# Your job (in this order):",
|
|
103
138
|
`1. Read ALL reviewer outputs from ${reviewsDir}/`,
|
|
104
|
-
`2. Synthesize into ${reviewsDir}/<
|
|
139
|
+
`2. Synthesize into ${reviewsDir}/<unix-epoch-seconds>_final_pass-${pass}.md (prefix with the current Unix epoch seconds, e.g. \`date +%s\`, so the file orders chronologically)`,
|
|
105
140
|
finalStep,
|
|
106
141
|
...tail,
|
|
107
142
|
].join("\n");
|
|
@@ -131,8 +166,11 @@ export async function spawnCodeReviewers(
|
|
|
131
166
|
|
|
132
167
|
const userRequest = readFileSync(urPath, "utf-8");
|
|
133
168
|
const research = readFileSync(resPath, "utf-8");
|
|
134
|
-
|
|
135
|
-
|
|
169
|
+
// A standalone review task (phase "review") has no synthesized plan by design —
|
|
170
|
+
// reviewers assess the diff against USER_REQUEST.md/RESEARCH.md instead. The
|
|
171
|
+
// plan is only required where one legitimately exists (the implement phase).
|
|
172
|
+
const synthesizedPlan = phase === "review" ? undefined : (getLatestSynthesizedPlan(taskDir) ?? undefined);
|
|
173
|
+
if (phase !== "review" && !synthesizedPlan) {
|
|
136
174
|
send(
|
|
137
175
|
{ customType: "pp-code-reviews-error", content: "Cannot start code review: no synthesized plan found.", display: true },
|
|
138
176
|
"context",
|
|
@@ -159,7 +197,7 @@ export async function spawnCodeReviewers(
|
|
|
159
197
|
const agent = createCodeReviewerAgent(
|
|
160
198
|
variant,
|
|
161
199
|
reviewerVariants,
|
|
162
|
-
{ userRequest, research, synthesizedPlan },
|
|
200
|
+
{ userRequest, research, synthesizedPlan, manifest: getArtifactManifest(taskDir) },
|
|
163
201
|
outputPath,
|
|
164
202
|
contextDirs,
|
|
165
203
|
reviewerPhase,
|
|
@@ -46,7 +46,8 @@ export function cancelPendingPlannotatorWait(orchestrator: Orchestrator): void {
|
|
|
46
46
|
export function waitForPlannotatorResult(
|
|
47
47
|
orchestrator: Orchestrator,
|
|
48
48
|
reviewId: string | null,
|
|
49
|
-
|
|
49
|
+
timeoutMs: number | null = PLANNOTATOR_RESULT_TIMEOUT_MS,
|
|
50
|
+
): Promise<{ approved: boolean; feedback?: string; error?: string }> {
|
|
50
51
|
cancelPendingPlannotatorWait(orchestrator);
|
|
51
52
|
const pi = orchestrator.pi;
|
|
52
53
|
return new Promise((resolve, reject) => {
|
|
@@ -54,7 +55,7 @@ export function waitForPlannotatorResult(
|
|
|
54
55
|
const unsub = pi.events.on("plannotator:review-result", (data: any) => {
|
|
55
56
|
if (reviewId && data?.reviewId && data.reviewId !== reviewId) return;
|
|
56
57
|
cleanup();
|
|
57
|
-
resolve({ approved: !!data?.approved, feedback: data?.feedback });
|
|
58
|
+
resolve({ approved: !!data?.approved, feedback: data?.feedback, error: data?.error });
|
|
58
59
|
});
|
|
59
60
|
orchestrator.plannotatorUnsub = unsub;
|
|
60
61
|
function cleanup() {
|
|
@@ -66,9 +67,11 @@ export function waitForPlannotatorResult(
|
|
|
66
67
|
orchestrator.plannotatorTimer = null;
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
if (timeoutMs !== null) {
|
|
71
|
+
orchestrator.plannotatorTimer = setTimeout(() => {
|
|
72
|
+
cleanup();
|
|
73
|
+
reject(new Error("Plannotator review timed out"));
|
|
74
|
+
}, timeoutMs);
|
|
75
|
+
}
|
|
73
76
|
});
|
|
74
77
|
}
|