pi-review-loop 0.3.1 → 0.4.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/README.md +54 -3
- package/index.ts +111 -30
- package/package.json +1 -1
- package/prompts/double-check-plan.md +4 -2
- package/prompts/double-check.md +13 -1
- package/settings.ts +19 -3
package/README.md
CHANGED
|
@@ -30,13 +30,15 @@ Agents make mistakes. They miss edge cases, introduce typos, forget error handli
|
|
|
30
30
|
|
|
31
31
|
**Smart Exit Detection** - Won't be fooled by "Fixed 3 issues. No further issues found." Detects when issues were fixed and keeps looping.
|
|
32
32
|
|
|
33
|
+
**Fresh Context** - Optional mode that strips prior review iterations from context each pass. The agent is prompted to re-read any relevant plan, spec, or PRD documents, so it truly reviews with fresh eyes instead of through the lens of its previous passes.
|
|
34
|
+
|
|
33
35
|
**Fully Configurable** - Every pattern is customizable. Change what triggers the loop, what exits it, and what prompt gets sent. Extend the defaults or replace them entirely.
|
|
34
36
|
|
|
35
37
|
## Typical Workflow
|
|
36
38
|
|
|
37
39
|
The loop shines in two scenarios:
|
|
38
40
|
|
|
39
|
-
**Before implementing** — You've got a plan doc and want to sanity-check it against the actual codebase. Run `/review-
|
|
41
|
+
**Before implementing** — You've got a plan doc and want to sanity-check it against the actual codebase. Run `/review-plan` and let the agent compare the plan to what exists. It'll catch things like outdated assumptions, conflicting patterns, or unnecessary complexity. The funny thing is, it rarely finds everything on the first pass. Second pass catches different issues. Third pass, more still. That's the whole point of the loop.
|
|
40
42
|
|
|
41
43
|
**After implementing** — You just finished building a feature and want to catch bugs before calling it done. Run `/review-start` and the agent reviews its own work with fresh eyes. Typos, missed edge cases, forgotten error handling — it finds stuff you'd miss staring at the same code. Again, multiple passes tend to surface different issues each time.
|
|
42
44
|
|
|
@@ -66,7 +68,7 @@ The package includes two prompt templates that are automatically installed to `~
|
|
|
66
68
|
These prompts are designed to work with the review loop:
|
|
67
69
|
- They instruct the agent to respond with "No issues found." when done (triggering exit)
|
|
68
70
|
- They tell the agent to end with "Fixed [N] issue(s). Ready for another review." when issues are fixed (continuing the loop)
|
|
69
|
-
-
|
|
71
|
+
- `double-check.md` includes the "fresh eyes" phrase that triggers the loop when `autoTrigger` is enabled
|
|
70
72
|
|
|
71
73
|
**Recommended workflow:** Use `/review-start` to activate review mode, which sends the review prompt automatically. Alternatively, enable auto-trigger (`/review-auto on`) and the `/double-check` template will activate the loop.
|
|
72
74
|
|
|
@@ -140,6 +142,47 @@ Or just type something else. Any non-trigger input exits review mode.
|
|
|
140
142
|
|
|
141
143
|
Changes max iterations for current session.
|
|
142
144
|
|
|
145
|
+
## Fresh Context
|
|
146
|
+
|
|
147
|
+
By default, each review iteration sees the full conversation history including all prior iterations. This means by pass 3, the agent is reviewing code through the lens of its two prior reviews -- not truly fresh eyes. Fresh context mode fixes this.
|
|
148
|
+
|
|
149
|
+
When enabled, prior review iterations are stripped from context before each LLM call. The agent only sees: the original pre-review conversation, a brief pass note instructing it to re-read any relevant plan/spec/PRD documents, and the current iteration's review prompt and tool usage.
|
|
150
|
+
|
|
151
|
+
### Enable Fresh Context
|
|
152
|
+
|
|
153
|
+
Per-session:
|
|
154
|
+
```
|
|
155
|
+
/review-fresh on
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Or via the tool:
|
|
159
|
+
```typescript
|
|
160
|
+
review_loop({ start: true, freshContext: true })
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Or permanently in settings:
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"reviewerLoop": {
|
|
167
|
+
"freshContext": true
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### How It Works
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
iteration 1: [pre-review context] [review prompt] ← full context
|
|
176
|
+
iteration 2: [pre-review context] [pass note] [review prompt] ← iter 1 stripped
|
|
177
|
+
iteration 3: [pre-review context] [pass note] [review prompt] ← iters 1-2 stripped
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The pre-review context is everything from before review mode was activated. Within a review iteration, multi-turn tool usage (read, bash, edit) is preserved -- only completed prior iterations are stripped.
|
|
181
|
+
|
|
182
|
+
The pass note tells the agent which pass it's on and instructs it to re-read any relevant plan, spec, PRD, or progress documents before reviewing. This way the agent re-grounds itself in the source of truth each pass using its own tool calls rather than programmatic injection.
|
|
183
|
+
|
|
184
|
+
If auto-compaction fires during a review loop (large sessions), the fresh context handler gracefully degrades to full context for that session.
|
|
185
|
+
|
|
143
186
|
## Configuration
|
|
144
187
|
|
|
145
188
|
Configure in `~/.pi/agent/settings.json`. Works out of the box, but everything is customizable:
|
|
@@ -149,6 +192,7 @@ Configure in `~/.pi/agent/settings.json`. Works out of the box, but everything i
|
|
|
149
192
|
"reviewerLoop": {
|
|
150
193
|
"maxIterations": 7,
|
|
151
194
|
"autoTrigger": true,
|
|
195
|
+
"freshContext": true,
|
|
152
196
|
"reviewPrompt": "template:double-check",
|
|
153
197
|
"triggerPatterns": {
|
|
154
198
|
"mode": "extend",
|
|
@@ -172,6 +216,7 @@ Configure in `~/.pi/agent/settings.json`. Works out of the box, but everything i
|
|
|
172
216
|
|--------|-------------|
|
|
173
217
|
| `maxIterations` | Max review prompts before auto-exit (default: 7) |
|
|
174
218
|
| `autoTrigger` | Enable keyword-based auto-trigger (default: false) |
|
|
219
|
+
| `freshContext` | Strip prior iterations from context each pass (default: false) |
|
|
175
220
|
| `reviewPrompt` | The prompt to send each iteration |
|
|
176
221
|
| `triggerPatterns` | What activates review mode (requires autoTrigger: true) |
|
|
177
222
|
| `exitPatterns` | What indicates "review complete" |
|
|
@@ -250,6 +295,7 @@ The loop exits when:
|
|
|
250
295
|
| `/review-max <n>` | Set max iterations (session only) |
|
|
251
296
|
| `/review-auto [on\|off]` | Toggle auto-trigger from keywords (session only) |
|
|
252
297
|
| `/review-auto <focus>` | Enable auto-trigger AND start review with custom focus |
|
|
298
|
+
| `/review-fresh [on\|off]` | Toggle fresh context mode (session only) |
|
|
253
299
|
| `/review-status` | Show current state |
|
|
254
300
|
|
|
255
301
|
## Tool API
|
|
@@ -278,15 +324,19 @@ review_loop({ maxIterations: 10 })
|
|
|
278
324
|
// Enable/disable auto-trigger
|
|
279
325
|
review_loop({ autoTrigger: true })
|
|
280
326
|
review_loop({ autoTrigger: false })
|
|
327
|
+
|
|
328
|
+
// Enable fresh context
|
|
329
|
+
review_loop({ start: true, freshContext: true })
|
|
281
330
|
```
|
|
282
331
|
|
|
283
332
|
**Returns:**
|
|
284
333
|
```json
|
|
285
334
|
{
|
|
286
335
|
"active": true,
|
|
287
|
-
"currentIteration":
|
|
336
|
+
"currentIteration": 1,
|
|
288
337
|
"maxIterations": 7,
|
|
289
338
|
"autoTrigger": false,
|
|
339
|
+
"freshContext": true,
|
|
290
340
|
"focus": "focus on error handling",
|
|
291
341
|
"message": "Review mode active: iteration 2/7"
|
|
292
342
|
}
|
|
@@ -316,6 +366,7 @@ otherwise → exit (max reached)
|
|
|
316
366
|
- `session_start` - Reload settings
|
|
317
367
|
- `input` - Detect triggers (if autoTrigger enabled), handle interrupts
|
|
318
368
|
- `before_agent_start` - Check expanded prompts for triggers (if autoTrigger enabled)
|
|
369
|
+
- `context` - Strip prior iterations and inject pass note (if freshContext enabled)
|
|
319
370
|
- `agent_end` - Analyze response, decide to loop or exit
|
|
320
371
|
|
|
321
372
|
## Limitations
|
package/index.ts
CHANGED
|
@@ -7,13 +7,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
7
7
|
let reviewModeActive = false;
|
|
8
8
|
let currentIteration = 0;
|
|
9
9
|
let customPromptSuffix = "";
|
|
10
|
+
let freshContext = settings.freshContext;
|
|
11
|
+
let reviewBoundaryCount = -1;
|
|
12
|
+
let boundaryNeedsCapture = false;
|
|
10
13
|
|
|
11
14
|
function updateStatus(ctx: ExtensionContext) {
|
|
12
15
|
if (reviewModeActive) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
);
|
|
16
|
+
const parts = [`Review mode (${currentIteration + 1}/${settings.maxIterations})`];
|
|
17
|
+
if (freshContext) parts.push("fresh");
|
|
18
|
+
ctx.ui.setStatus("review-loop", parts.join(" | "));
|
|
17
19
|
} else {
|
|
18
20
|
ctx.ui.setStatus("review-loop", undefined);
|
|
19
21
|
}
|
|
@@ -30,12 +32,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
30
32
|
function parseCustomText(args: string): string {
|
|
31
33
|
const trimmed = args.trim();
|
|
32
34
|
if (!trimmed) return "";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const match = trimmed.match(/^"(.+)"$/s) || trimmed.match(/^'(.+)'$/s) ||
|
|
36
|
-
trimmed.match(/"(.+?)"/s) || trimmed.match(/'(.+?)'/s);
|
|
37
|
-
|
|
38
|
-
// If no quotes found, use the entire text as focus
|
|
35
|
+
|
|
36
|
+
const match = trimmed.match(/^"(.+)"$/s) || trimmed.match(/^'(.+)'$/s);
|
|
39
37
|
return match ? match[1].trim() : trimmed;
|
|
40
38
|
}
|
|
41
39
|
|
|
@@ -43,6 +41,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
43
41
|
reviewModeActive = false;
|
|
44
42
|
currentIteration = 0;
|
|
45
43
|
customPromptSuffix = "";
|
|
44
|
+
reviewBoundaryCount = -1;
|
|
45
|
+
boundaryNeedsCapture = false;
|
|
46
46
|
updateStatus(ctx);
|
|
47
47
|
ctx.ui.notify(`Review mode ended: ${reason}`, "info");
|
|
48
48
|
}
|
|
@@ -50,12 +50,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
50
50
|
function enterReviewMode(ctx: ExtensionContext) {
|
|
51
51
|
reviewModeActive = true;
|
|
52
52
|
currentIteration = 0;
|
|
53
|
+
reviewBoundaryCount = -1;
|
|
54
|
+
boundaryNeedsCapture = false;
|
|
53
55
|
updateStatus(ctx);
|
|
54
56
|
ctx.ui.notify("Review mode activated", "info");
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
pi.on("session_start", async () => {
|
|
58
60
|
settings = loadSettings();
|
|
61
|
+
freshContext = settings.freshContext;
|
|
59
62
|
});
|
|
60
63
|
|
|
61
64
|
pi.on("input", async (event, ctx) => {
|
|
@@ -86,6 +89,54 @@ export default function (pi: ExtensionAPI) {
|
|
|
86
89
|
}
|
|
87
90
|
});
|
|
88
91
|
|
|
92
|
+
pi.on("context", async (event) => {
|
|
93
|
+
if (!reviewModeActive || !freshContext) return;
|
|
94
|
+
|
|
95
|
+
const messages = event.messages;
|
|
96
|
+
if (messages.length === 0) return;
|
|
97
|
+
|
|
98
|
+
if (boundaryNeedsCapture) {
|
|
99
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
100
|
+
if (messages[i].role === "user") {
|
|
101
|
+
reviewBoundaryCount = i;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
boundaryNeedsCapture = false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (currentIteration === 0) return;
|
|
109
|
+
if (reviewBoundaryCount < 0) return;
|
|
110
|
+
|
|
111
|
+
let lastUserIdx = -1;
|
|
112
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
113
|
+
if (messages[i].role === "user") {
|
|
114
|
+
lastUserIdx = i;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (lastUserIdx < 0) return;
|
|
119
|
+
if (reviewBoundaryCount >= lastUserIdx) return;
|
|
120
|
+
|
|
121
|
+
const preReview = messages.slice(0, reviewBoundaryCount);
|
|
122
|
+
const currentIterationMsgs = messages.slice(lastUserIdx);
|
|
123
|
+
|
|
124
|
+
const assembled: typeof messages = [...preReview];
|
|
125
|
+
|
|
126
|
+
assembled.push({
|
|
127
|
+
role: "user",
|
|
128
|
+
content: [{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: `[Review pass ${currentIteration + 1}. ${currentIteration} prior pass(es) completed, fixes applied to code. Re-read any relevant plan, spec, PRD, or progress documents before reviewing. Review with fresh eyes.]`,
|
|
131
|
+
}],
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
} as any);
|
|
134
|
+
|
|
135
|
+
assembled.push(...currentIterationMsgs);
|
|
136
|
+
|
|
137
|
+
return { messages: assembled };
|
|
138
|
+
});
|
|
139
|
+
|
|
89
140
|
pi.on("agent_end", async (event, ctx) => {
|
|
90
141
|
if (!ctx.hasUI) return;
|
|
91
142
|
if (!reviewModeActive) return;
|
|
@@ -119,11 +170,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
119
170
|
}
|
|
120
171
|
|
|
121
172
|
currentIteration++;
|
|
122
|
-
if (currentIteration
|
|
173
|
+
if (currentIteration >= settings.maxIterations) {
|
|
123
174
|
exitReviewMode(ctx, `max iterations (${settings.maxIterations}) reached`);
|
|
124
175
|
return;
|
|
125
176
|
}
|
|
126
177
|
|
|
178
|
+
if (freshContext && reviewBoundaryCount < 0) boundaryNeedsCapture = true;
|
|
127
179
|
updateStatus(ctx);
|
|
128
180
|
pi.sendUserMessage(buildReviewPrompt(settings.reviewPromptConfig), {
|
|
129
181
|
deliverAs: "followUp",
|
|
@@ -131,26 +183,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
131
183
|
});
|
|
132
184
|
|
|
133
185
|
pi.registerCommand("review-start", {
|
|
134
|
-
description: "Activate review loop
|
|
186
|
+
description: "Activate review loop with optional custom focus text.",
|
|
135
187
|
handler: async (args, ctx) => {
|
|
136
188
|
if (reviewModeActive) {
|
|
137
189
|
ctx.ui.notify("Review mode is already active", "info");
|
|
138
190
|
} else {
|
|
139
191
|
customPromptSuffix = parseCustomText(args);
|
|
140
192
|
enterReviewMode(ctx);
|
|
193
|
+
if (freshContext && reviewBoundaryCount < 0) boundaryNeedsCapture = true;
|
|
141
194
|
pi.sendUserMessage(buildReviewPrompt(settings.reviewPromptConfig));
|
|
142
195
|
}
|
|
143
196
|
},
|
|
144
197
|
});
|
|
145
198
|
|
|
146
199
|
pi.registerCommand("review-plan", {
|
|
147
|
-
description: "Activate review loop for plans/specs/PRDs
|
|
200
|
+
description: "Activate review loop for plans/specs/PRDs with optional custom focus text.",
|
|
148
201
|
handler: async (args, ctx) => {
|
|
149
202
|
if (reviewModeActive) {
|
|
150
203
|
ctx.ui.notify("Review mode is already active", "info");
|
|
151
204
|
} else {
|
|
152
205
|
customPromptSuffix = parseCustomText(args);
|
|
153
206
|
enterReviewMode(ctx);
|
|
207
|
+
if (freshContext && reviewBoundaryCount < 0) boundaryNeedsCapture = true;
|
|
154
208
|
pi.sendUserMessage(buildReviewPrompt({ type: "template", value: "double-check-plan" }));
|
|
155
209
|
}
|
|
156
210
|
},
|
|
@@ -184,13 +238,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
184
238
|
description: "Show review mode status",
|
|
185
239
|
handler: async (_args, ctx) => {
|
|
186
240
|
if (reviewModeActive) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
);
|
|
241
|
+
const parts = [`iteration ${currentIteration + 1}/${settings.maxIterations}`];
|
|
242
|
+
if (freshContext) parts.push("fresh context");
|
|
243
|
+
ctx.ui.notify(`Review mode active: ${parts.join(", ")}`, "info");
|
|
191
244
|
} else {
|
|
192
245
|
ctx.ui.notify(
|
|
193
|
-
`Review mode inactive (max: ${settings.maxIterations}, auto-trigger: ${settings.autoTrigger ? "on" : "off"})`,
|
|
246
|
+
`Review mode inactive (max: ${settings.maxIterations}, auto-trigger: ${settings.autoTrigger ? "on" : "off"}, fresh: ${freshContext ? "on" : "off"})`,
|
|
194
247
|
"info"
|
|
195
248
|
);
|
|
196
249
|
}
|
|
@@ -202,8 +255,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
202
255
|
handler: async (args, ctx) => {
|
|
203
256
|
const arg = args.trim();
|
|
204
257
|
const argLower = arg.toLowerCase();
|
|
205
|
-
|
|
206
|
-
// Check for standard on/off/toggle keywords first
|
|
258
|
+
|
|
207
259
|
if (argLower === "on" || argLower === "true" || argLower === "1") {
|
|
208
260
|
settings.autoTrigger = true;
|
|
209
261
|
ctx.ui.notify(`Auto-trigger enabled`, "info");
|
|
@@ -222,21 +274,36 @@ export default function (pi: ExtensionAPI) {
|
|
|
222
274
|
);
|
|
223
275
|
return;
|
|
224
276
|
}
|
|
225
|
-
|
|
226
|
-
// Anything else is treated as custom focus text
|
|
227
|
-
const customText = parseCustomText(arg);
|
|
277
|
+
|
|
228
278
|
settings.autoTrigger = true;
|
|
229
|
-
customPromptSuffix =
|
|
279
|
+
customPromptSuffix = parseCustomText(arg);
|
|
230
280
|
if (reviewModeActive) {
|
|
231
281
|
ctx.ui.notify(`Auto-trigger enabled, focus updated for next iteration`, "info");
|
|
232
282
|
} else {
|
|
233
283
|
enterReviewMode(ctx);
|
|
284
|
+
if (freshContext && reviewBoundaryCount < 0) boundaryNeedsCapture = true;
|
|
234
285
|
pi.sendUserMessage(buildReviewPrompt(settings.reviewPromptConfig));
|
|
235
286
|
ctx.ui.notify(`Auto-trigger enabled, review started with custom focus`, "info");
|
|
236
287
|
}
|
|
237
288
|
},
|
|
238
289
|
});
|
|
239
290
|
|
|
291
|
+
pi.registerCommand("review-fresh", {
|
|
292
|
+
description: "Toggle fresh context mode for review iterations",
|
|
293
|
+
handler: async (args, ctx) => {
|
|
294
|
+
const arg = args.trim().toLowerCase();
|
|
295
|
+
|
|
296
|
+
if (arg === "on" || arg === "true" || arg === "1") {
|
|
297
|
+
freshContext = true;
|
|
298
|
+
} else if (arg === "off" || arg === "false" || arg === "0") {
|
|
299
|
+
freshContext = false;
|
|
300
|
+
} else {
|
|
301
|
+
freshContext = !freshContext;
|
|
302
|
+
}
|
|
303
|
+
ctx.ui.notify(`Fresh context ${freshContext ? "enabled" : "disabled"}`, "info");
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
|
|
240
307
|
pi.registerTool({
|
|
241
308
|
name: "review_loop",
|
|
242
309
|
description:
|
|
@@ -268,15 +335,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
268
335
|
description: "Custom focus/instructions to append to the review prompt (e.g., \"focus on error handling\")",
|
|
269
336
|
})
|
|
270
337
|
),
|
|
338
|
+
freshContext: Type.Optional(
|
|
339
|
+
Type.Boolean({
|
|
340
|
+
description: "Enable/disable fresh context mode (strips prior review iterations from context)",
|
|
341
|
+
})
|
|
342
|
+
),
|
|
271
343
|
}),
|
|
272
344
|
|
|
273
345
|
async execute(_toolCallId, params, _onUpdate, ctx) {
|
|
274
|
-
// Update maxIterations if provided
|
|
275
346
|
if (typeof params.maxIterations === "number" && params.maxIterations >= 1) {
|
|
276
347
|
settings.maxIterations = params.maxIterations;
|
|
277
348
|
}
|
|
278
349
|
|
|
279
|
-
// Update autoTrigger if provided
|
|
280
350
|
if (typeof params.autoTrigger === "boolean") {
|
|
281
351
|
settings.autoTrigger = params.autoTrigger;
|
|
282
352
|
ctx.ui.notify(
|
|
@@ -285,12 +355,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
285
355
|
);
|
|
286
356
|
}
|
|
287
357
|
|
|
288
|
-
// Update custom focus if provided
|
|
289
358
|
if (typeof params.focus === "string") {
|
|
290
359
|
customPromptSuffix = params.focus.trim();
|
|
291
360
|
}
|
|
292
361
|
|
|
293
|
-
|
|
362
|
+
if (typeof params.freshContext === "boolean") {
|
|
363
|
+
freshContext = params.freshContext;
|
|
364
|
+
ctx.ui.notify(
|
|
365
|
+
`Fresh context ${freshContext ? "enabled" : "disabled"}`,
|
|
366
|
+
"info"
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
294
370
|
if (params.start) {
|
|
295
371
|
if (reviewModeActive) {
|
|
296
372
|
return {
|
|
@@ -302,6 +378,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
302
378
|
currentIteration,
|
|
303
379
|
maxIterations: settings.maxIterations,
|
|
304
380
|
autoTrigger: settings.autoTrigger,
|
|
381
|
+
freshContext,
|
|
305
382
|
focus: customPromptSuffix || undefined,
|
|
306
383
|
message: "Review mode is already active",
|
|
307
384
|
}),
|
|
@@ -311,6 +388,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
311
388
|
}
|
|
312
389
|
|
|
313
390
|
enterReviewMode(ctx);
|
|
391
|
+
if (freshContext && reviewBoundaryCount < 0) boundaryNeedsCapture = true;
|
|
314
392
|
pi.sendUserMessage(buildReviewPrompt(settings.reviewPromptConfig));
|
|
315
393
|
|
|
316
394
|
return {
|
|
@@ -322,6 +400,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
322
400
|
currentIteration,
|
|
323
401
|
maxIterations: settings.maxIterations,
|
|
324
402
|
autoTrigger: settings.autoTrigger,
|
|
403
|
+
freshContext,
|
|
325
404
|
focus: customPromptSuffix || undefined,
|
|
326
405
|
message: customPromptSuffix
|
|
327
406
|
? `Review mode started with custom focus. Review prompt sent.`
|
|
@@ -343,6 +422,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
343
422
|
currentIteration: 0,
|
|
344
423
|
maxIterations: settings.maxIterations,
|
|
345
424
|
autoTrigger: settings.autoTrigger,
|
|
425
|
+
freshContext,
|
|
346
426
|
message: "Review mode is not active",
|
|
347
427
|
}),
|
|
348
428
|
},
|
|
@@ -361,6 +441,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
361
441
|
currentIteration: 0,
|
|
362
442
|
maxIterations: settings.maxIterations,
|
|
363
443
|
autoTrigger: settings.autoTrigger,
|
|
444
|
+
freshContext,
|
|
364
445
|
message: "Review mode stopped",
|
|
365
446
|
}),
|
|
366
447
|
},
|
|
@@ -368,7 +449,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
368
449
|
};
|
|
369
450
|
}
|
|
370
451
|
|
|
371
|
-
// Default: status
|
|
372
452
|
return {
|
|
373
453
|
content: [
|
|
374
454
|
{
|
|
@@ -378,9 +458,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
378
458
|
currentIteration,
|
|
379
459
|
maxIterations: settings.maxIterations,
|
|
380
460
|
autoTrigger: settings.autoTrigger,
|
|
461
|
+
freshContext,
|
|
381
462
|
focus: customPromptSuffix || undefined,
|
|
382
463
|
message: reviewModeActive
|
|
383
|
-
? `Review mode active: iteration ${currentIteration}/${settings.maxIterations}`
|
|
464
|
+
? `Review mode active: iteration ${currentIteration + 1}/${settings.maxIterations}`
|
|
384
465
|
: "Review mode inactive",
|
|
385
466
|
}),
|
|
386
467
|
},
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Double check / critique implementation plan with fresh eyes
|
|
3
3
|
---
|
|
4
|
-
Great, now I want you to carefully read over the plan/spec and compare against the codebase with "fresh eyes," looking super carefully for any obvious bugs, errors, problems, issues, confusion, etc.
|
|
4
|
+
Great, now I want you to carefully read over the plan/spec and compare against the codebase with "fresh eyes," looking super carefully for any obvious bugs, errors, problems, issues, confusion, etc. Think through how best to implement this elegantly given our architecture and goals. Also keep in mind that this is not an MVP - so don't skip anything. If any issues are found, proceed to fix them without being asked to do so.
|
|
5
5
|
|
|
6
6
|
**Response format:**
|
|
7
7
|
- If you find ANY issues: fix them, then list what you fixed. Do NOT say "no issues found" - instead end with "Fixed [N] issue(s). Ready for another review."
|
|
8
|
-
- If you find ZERO issues:
|
|
8
|
+
- If you find ZERO issues: describe what you examined and verified, then conclude with "No issues found."
|
|
9
|
+
|
|
10
|
+
Do not rush to a verdict. Read all relevant code first, trace through edge cases, and only then decide. I am pushing you to do a genuinely thorough review and not just lazily rubber-stamp it. Make sure you think deeply, then ultrathink some more.
|
|
9
11
|
|
|
10
12
|
$@
|
package/prompts/double-check.md
CHANGED
|
@@ -3,8 +3,20 @@ description: Double check / review implementation with fresh eyes
|
|
|
3
3
|
---
|
|
4
4
|
Great, now I want you to carefully read over all of the new code you just wrote and other existing code with "fresh eyes," looking super carefully for any obvious bugs, errors, problems, issues, confusion, etc. Also, if you notice any pre-existing issues/bugs those should be addressed.
|
|
5
5
|
|
|
6
|
+
Question everything: Does each line of code need to exist? Unused parameters, dead code, and unnecessary complexity should be removed, not dressed up with underscore prefixes or comments.
|
|
7
|
+
|
|
8
|
+
This codebase will outlive you. Every shortcut becomes someone else's burden. Every hack compounds into technical debt that slows the whole team down.
|
|
9
|
+
|
|
10
|
+
You are not just writing code. You are shaping the future of this project. The patterns you establish will be copied. The corners you cut will be cut again.
|
|
11
|
+
|
|
12
|
+
Fight entropy. Leave the codebase better than you found it.
|
|
13
|
+
|
|
14
|
+
You MUST read all relevant code and think deeply (ultrathink!!!) first before you make any edits.
|
|
15
|
+
|
|
6
16
|
**Response format:**
|
|
7
17
|
- If you find ANY issues: fix them, then list what you fixed. Do NOT say "no issues found" - instead end with "Fixed [N] issue(s). Ready for another review."
|
|
8
|
-
- If you find ZERO issues:
|
|
18
|
+
- If you find ZERO issues: describe what you examined and verified, then conclude with "No issues found."
|
|
19
|
+
|
|
20
|
+
Do not rush to a verdict. Read all relevant code first, trace through edge cases, and only then decide. I am pushing you to do a genuinely thorough review and not just lazily rubber-stamp it. Make sure you think deeply, then ultrathink some more.
|
|
9
21
|
|
|
10
22
|
$@
|
package/settings.ts
CHANGED
|
@@ -6,11 +6,23 @@ export const SETTINGS_PATH = join(homedir(), ".pi", "agent", "settings.json");
|
|
|
6
6
|
|
|
7
7
|
export const DEFAULT_MAX_ITERATIONS = 7;
|
|
8
8
|
|
|
9
|
-
export const DEFAULT_REVIEW_PROMPT = `Great, now I want you to carefully read over all of the new code you just wrote and other existing code
|
|
9
|
+
export const DEFAULT_REVIEW_PROMPT = `Great, now I want you to carefully read over all of the new code you just wrote and other existing code with "fresh eyes," looking super carefully for any obvious bugs, errors, problems, issues, confusion, etc. Also, if you notice any pre-existing issues/bugs those should be addressed.
|
|
10
|
+
|
|
11
|
+
Question everything: Does each line of code need to exist? Unused parameters, dead code, and unnecessary complexity should be removed, not dressed up with underscore prefixes or comments.
|
|
12
|
+
|
|
13
|
+
This codebase will outlive you. Every shortcut becomes someone else's burden. Every hack compounds into technical debt that slows the whole team down.
|
|
14
|
+
|
|
15
|
+
You are not just writing code. You are shaping the future of this project. The patterns you establish will be copied. The corners you cut will be cut again.
|
|
16
|
+
|
|
17
|
+
Fight entropy. Leave the codebase better than you found it.
|
|
18
|
+
|
|
19
|
+
You MUST read all relevant code and think deeply (ultrathink!!!) first before you make any edits.
|
|
10
20
|
|
|
11
21
|
**Response format:**
|
|
12
22
|
- If you find ANY issues: fix them, then list what you fixed. Do NOT say "no issues found" - instead end with "Fixed [N] issue(s). Ready for another review."
|
|
13
|
-
- If you find ZERO issues:
|
|
23
|
+
- If you find ZERO issues: describe what you examined and verified, then conclude with "No issues found."
|
|
24
|
+
|
|
25
|
+
Do not rush to a verdict. Read all relevant code first, trace through edge cases, and only then decide. I am pushing you to do a genuinely thorough review and not just lazily rubber-stamp it. Make sure you think deeply, then ultrathink some more.`;
|
|
14
26
|
|
|
15
27
|
export const DEFAULT_TRIGGER_PATTERNS: RegExp[] = [
|
|
16
28
|
/\bimplement\s+(the\s+)?plan\b/i,
|
|
@@ -61,6 +73,7 @@ export interface ReviewerLoopSettingsRaw {
|
|
|
61
73
|
maxIterations?: number;
|
|
62
74
|
reviewPrompt?: string;
|
|
63
75
|
autoTrigger?: boolean;
|
|
76
|
+
freshContext?: boolean;
|
|
64
77
|
triggerPatterns?: PatternConfig;
|
|
65
78
|
exitPatterns?: PatternConfig;
|
|
66
79
|
issuesFixedPatterns?: PatternConfig;
|
|
@@ -70,6 +83,7 @@ export interface ReviewerLoopSettings {
|
|
|
70
83
|
maxIterations: number;
|
|
71
84
|
reviewPromptConfig: ReviewPromptConfig;
|
|
72
85
|
autoTrigger: boolean;
|
|
86
|
+
freshContext: boolean;
|
|
73
87
|
triggerPatterns: RegExp[];
|
|
74
88
|
exitPatterns: RegExp[];
|
|
75
89
|
issuesFixedPatterns: RegExp[];
|
|
@@ -81,7 +95,8 @@ function parsePattern(input: unknown): RegExp | null {
|
|
|
81
95
|
const match = input.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
82
96
|
if (match) {
|
|
83
97
|
try {
|
|
84
|
-
|
|
98
|
+
const flags = match[2].replace(/g/g, "");
|
|
99
|
+
return new RegExp(match[1], flags);
|
|
85
100
|
} catch {
|
|
86
101
|
return null;
|
|
87
102
|
}
|
|
@@ -204,6 +219,7 @@ export function loadSettings(): ReviewerLoopSettings {
|
|
|
204
219
|
: DEFAULT_MAX_ITERATIONS,
|
|
205
220
|
reviewPromptConfig: parseReviewPromptConfig(raw.reviewPrompt),
|
|
206
221
|
autoTrigger: raw.autoTrigger === true,
|
|
222
|
+
freshContext: raw.freshContext === true,
|
|
207
223
|
triggerPatterns: loadPatterns(raw.triggerPatterns, DEFAULT_TRIGGER_PATTERNS),
|
|
208
224
|
exitPatterns: loadPatterns(raw.exitPatterns, DEFAULT_EXIT_PATTERNS),
|
|
209
225
|
issuesFixedPatterns: loadPatterns(
|