pi-goal-list-loop-audit 0.26.1 → 0.26.3
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/extensions/loops/goal.ts +14 -5
- package/extensions/reviewer.ts +63 -21
- package/package.json +1 -1
package/extensions/loops/goal.ts
CHANGED
|
@@ -583,11 +583,12 @@ function archiveCurrentGoal(ctx: ExtensionContext, status: Status, stopReason?:
|
|
|
583
583
|
function fireReviewer(
|
|
584
584
|
ctx: ExtensionContext,
|
|
585
585
|
source: { kind: "goal" | "list"; goalId: string; objective: string; terminal: string },
|
|
586
|
-
opts: { manual?: boolean } = {},
|
|
586
|
+
opts: { manual?: boolean; mode?: "default" | "auto" | "report" } = {},
|
|
587
587
|
): void {
|
|
588
588
|
try {
|
|
589
589
|
const settings = loadSettings(ctx.cwd);
|
|
590
590
|
const config = resolveReviewerConfig(settings.reviewer as Partial<ReviewerConfig> | undefined);
|
|
591
|
+
if (opts.mode) config.mode = opts.mode;
|
|
591
592
|
const sources: Array<{ name: string; text: string }> = [];
|
|
592
593
|
try {
|
|
593
594
|
sources.push({ name: "archive", text: fs.readFileSync(archivedGoalPath(ctx.cwd, source.goalId), "utf-8") });
|
|
@@ -2884,9 +2885,16 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
|
|
|
2884
2885
|
|
|
2885
2886
|
/** v0.26.0: /review <archived-goal-id> — manual reviewer invocation. */
|
|
2886
2887
|
async function cmdReview(args: string, ctx: ExtensionContext): Promise<void> {
|
|
2887
|
-
const
|
|
2888
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
2889
|
+
const id = parts[0] ?? "";
|
|
2890
|
+
const modeArg = parts[1];
|
|
2891
|
+
const mode = modeArg === "auto" || modeArg === "report" || modeArg === "default" ? modeArg : undefined;
|
|
2892
|
+
if (modeArg && !mode) {
|
|
2893
|
+
ctx.ui.notify(`Unknown mode "${modeArg}" — use auto | report | default.`, "warning");
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2888
2896
|
if (!id) {
|
|
2889
|
-
ctx.ui.notify("Usage: /review <goal-id> — see /goal archive for ids.", "info");
|
|
2897
|
+
ctx.ui.notify("Usage: /review <goal-id> [auto|report|default] — see /goal archive for ids.", "info");
|
|
2890
2898
|
return;
|
|
2891
2899
|
}
|
|
2892
2900
|
// Resolve the id against the archive (suffix match allowed).
|
|
@@ -2907,7 +2915,7 @@ async function cmdReview(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
2907
2915
|
ctx.ui.notify(`No archive found for ${id}.`, "warning");
|
|
2908
2916
|
return;
|
|
2909
2917
|
}
|
|
2910
|
-
fireReviewer(ctx, { kind: "goal", goalId, objective, terminal: "goal-complete" }, { manual: true });
|
|
2918
|
+
fireReviewer(ctx, { kind: "goal", goalId, objective, terminal: "goal-complete" }, { manual: true, mode });
|
|
2911
2919
|
}
|
|
2912
2920
|
|
|
2913
2921
|
/** v0.26.0: /glla reviewer — the reviewer config menu (project-scoped). */
|
|
@@ -2930,6 +2938,7 @@ async function cmdReviewerSettings(ctx: ExtensionContext): Promise<void> {
|
|
|
2930
2938
|
if (!choice || choice === "Done") return;
|
|
2931
2939
|
try {
|
|
2932
2940
|
if (choice.startsWith("Enabled")) save({ enabled: !cfg.enabled });
|
|
2941
|
+
else if (choice.startsWith("Mode")) save({ mode: cfg.mode === "default" ? "auto" : cfg.mode === "auto" ? "report" : "default" });
|
|
2933
2942
|
else if (choice.startsWith("Leverage mode")) save({ leverageMode: cfg.leverageMode === "fix-without-confirm" ? "confirm-all" : "fix-without-confirm" });
|
|
2934
2943
|
else if (choice.startsWith("Fire on goal-complete")) save({ fireOn: cfg.fireOn.includes("goal-complete") ? cfg.fireOn.filter((e) => e !== "goal-complete") : [...cfg.fireOn, "goal-complete"] });
|
|
2935
2944
|
else if (choice.startsWith("Fire on list-complete")) save({ fireOn: cfg.fireOn.includes("list-complete") ? cfg.fireOn.filter((e) => e !== "list-complete") : [...cfg.fireOn, "list-complete"] });
|
|
@@ -3368,7 +3377,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3368
3377
|
handler: settingsHandler,
|
|
3369
3378
|
});
|
|
3370
3379
|
pi.registerCommand("review", {
|
|
3371
|
-
description: "Manually run the reviewer on an archived goal: /review <goal-id> — extracts findings, writes a report to .pi-glla/reviews/,
|
|
3380
|
+
description: "Manually run the reviewer on an archived goal: /review <goal-id> [auto|report|default] — extracts findings, writes a report to .pi-glla/reviews/, cascades per the mode (auto = auto-loop, no Confirms). Bypasses the trigger gates (explicit user request).",
|
|
3372
3381
|
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdReview(args, ctx); },
|
|
3373
3382
|
});
|
|
3374
3383
|
pi.registerCommand("list", {
|
package/extensions/reviewer.ts
CHANGED
|
@@ -15,8 +15,15 @@
|
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
import * as path from "node:path";
|
|
17
17
|
|
|
18
|
+
export type ReviewerMode = "default" | "auto" | "report";
|
|
19
|
+
|
|
18
20
|
export interface ReviewerConfig {
|
|
19
21
|
enabled: boolean;
|
|
22
|
+
/** v0.26.2: default = Confirm-gated cascade; auto = auto-loop — every
|
|
23
|
+
* finding class (incl. architectural) and the clean-completion audit
|
|
24
|
+
* become /list items with zero Confirms (strategic stays notify-only —
|
|
25
|
+
* decisions never auto-fire); report = write the report + notify only. */
|
|
26
|
+
mode: ReviewerMode;
|
|
20
27
|
fireOn: Array<"goal-complete" | "list-complete">;
|
|
21
28
|
doNotFireOn: string[];
|
|
22
29
|
cascade: Array<"convert-findings-to-list" | "queue-leftovers" | "fire-audit-on-clean" | "notify-and-idle">;
|
|
@@ -30,6 +37,7 @@ export interface ReviewerConfig {
|
|
|
30
37
|
|
|
31
38
|
export const DEFAULT_REVIEWER_CONFIG: ReviewerConfig = {
|
|
32
39
|
enabled: true,
|
|
40
|
+
mode: "default",
|
|
33
41
|
fireOn: ["goal-complete", "list-complete"],
|
|
34
42
|
doNotFireOn: ["goal-aborted", "goal-paused"],
|
|
35
43
|
cascade: ["convert-findings-to-list", "queue-leftovers", "fire-audit-on-clean", "notify-and-idle"],
|
|
@@ -57,16 +65,28 @@ export interface Finding {
|
|
|
57
65
|
/** Leverage classification (contract item 5). Order matters: strategic
|
|
58
66
|
* and architectural win over bug/refactor — "should we rewrite this
|
|
59
67
|
* broken schema" is a decision, not a fix. */
|
|
68
|
+
// v0.26.3: the bare words "architectural"/"strategic" are REMOVED — they
|
|
69
|
+
// self-matched the reviewer's own vocabulary ("architectural-class",
|
|
70
|
+
// "architectural findings", the docs' mode matrix) and produced 3 junk
|
|
71
|
+
// findings on the 0.26.2 completion, observed live.
|
|
60
72
|
const CLASS_PATTERNS: Array<{ class: FindingClass; re: RegExp }> = [
|
|
61
|
-
{ class: "strategic", re: /\bshould we\b|\bdeprecat|ship this
|
|
62
|
-
{ class: "architectural", re: /\brewrite\b|new dependency|schema change
|
|
73
|
+
{ class: "strategic", re: /\bshould we\b|\bdeprecat|ship this\??/i },
|
|
74
|
+
{ class: "architectural", re: /\brewrite\b|new dependency|schema change|\bredesign\b/i },
|
|
63
75
|
{ class: "bug", re: /\bTODO\b|\bFIXME\b|\bbug\b|\bissue\b|regression|broken|\bfixme\b/i },
|
|
64
|
-
{ class: "refactor", re: /could be cleaner|consider refactoring|duplicat|refactor|left ?out|follow[\s-]?up|deferred/i },
|
|
76
|
+
{ class: "refactor", re: /could be cleaner|consider refactoring|duplicat|refactor|left ?out|follow[\s-]?up|deferred|could be improved|improvement|enhancement|consider adding|would be nice|nice to have/i },
|
|
65
77
|
];
|
|
66
78
|
|
|
79
|
+
/** v0.26.3: lines that never carry findings — code, markdown tables, and
|
|
80
|
+
* the reviewer's own report/config vocabulary. Observed false positives
|
|
81
|
+
* from the 0.26.2 completion: a test("…architectural…") name, the
|
|
82
|
+
* INSTALL.md mode-matrix row, and ship-doc prose. */
|
|
83
|
+
const SKIP_LINE = /^\s*(test|it|describe|assert|expect)\s*\(|^\s*(const|let|var|function|import|export|require)\b|\{\s*\.\.\.\s*\}|,\s*\.\.\.$|^\s*\|/;
|
|
84
|
+
const REVIEWER_VOCAB = /architectural-class|bug-class|refactor-class|strategic-class|reviewer found|cascade step|\*\*Mode\*\*|problems\s*\/\s*(improvements|architectural)/i;
|
|
85
|
+
|
|
67
86
|
export function classifyFindingText(line: string): FindingClass | undefined {
|
|
68
87
|
const t = line.trim();
|
|
69
88
|
if (t.length < 8) return undefined;
|
|
89
|
+
if (SKIP_LINE.test(t) || REVIEWER_VOCAB.test(t)) return undefined;
|
|
70
90
|
for (const { class: cls, re } of CLASS_PATTERNS) {
|
|
71
91
|
if (re.test(t)) return cls;
|
|
72
92
|
}
|
|
@@ -110,6 +130,7 @@ export interface ReviewReport {
|
|
|
110
130
|
objective: string;
|
|
111
131
|
findings: Finding[];
|
|
112
132
|
cascadeStep: string;
|
|
133
|
+
mode: ReviewerMode;
|
|
113
134
|
at: string;
|
|
114
135
|
}
|
|
115
136
|
|
|
@@ -120,7 +141,7 @@ export function formatReviewReport(r: ReviewReport): string {
|
|
|
120
141
|
return [
|
|
121
142
|
`# Review — ${r.goalId}`,
|
|
122
143
|
"",
|
|
123
|
-
`**Kind**: ${r.kind} · **At**: ${r.at}`,
|
|
144
|
+
`**Kind**: ${r.kind} · **At**: ${r.at} · **Mode**: ${r.mode}`,
|
|
124
145
|
"",
|
|
125
146
|
"## Summary",
|
|
126
147
|
"",
|
|
@@ -186,7 +207,11 @@ export function runReviewer(
|
|
|
186
207
|
if (config.doNotFireOn.includes(event)) return none(`doNotFireOn: ${event}`);
|
|
187
208
|
if (source.kind === "goal" && source.terminal !== "goal-complete") return none(`not a completion: ${source.terminal}`);
|
|
188
209
|
if (!config.fireOn.includes(source.kind === "goal" ? "goal-complete" : "list-complete")) return none("fireOn excludes this event");
|
|
189
|
-
|
|
210
|
+
// v0.26.2: in auto mode the queue emptying is the cascade's natural
|
|
211
|
+
// rhythm, not a runaway — the refire window must not strangle it.
|
|
212
|
+
// (The per-day cap below still bounds everything.)
|
|
213
|
+
const refireWindowApplies = !(config.mode === "auto" && source.kind === "list");
|
|
214
|
+
if (refireWindowApplies && reviewerFiredRecently(deps.ledgerEntries, REVIEWER_REFIRE_WINDOW_MS, deps.nowMs)) {
|
|
190
215
|
deps.ledger("reviewer_suppressed", { reason: "refire-window", goalId: source.goalId });
|
|
191
216
|
return none("reviewer fired within the last 5 minutes (runaway prevention)");
|
|
192
217
|
}
|
|
@@ -205,32 +230,47 @@ export function runReviewer(
|
|
|
205
230
|
let enqueued = 0;
|
|
206
231
|
let proposed = 0;
|
|
207
232
|
let cascadeStep = "notify-and-idle";
|
|
233
|
+
const auto = config.mode === "auto";
|
|
234
|
+
const reportOnly = config.mode === "report";
|
|
208
235
|
|
|
209
236
|
// Cascade: findings → list items (leverage: fix-without-confirm).
|
|
210
237
|
const convertStep = source.kind === "goal" ? "convert-findings-to-list" : "queue-leftovers";
|
|
211
|
-
if (bugs.length > 0 && config.cascade.includes(convertStep)) {
|
|
238
|
+
if (bugs.length > 0 && config.cascade.includes(convertStep) && !reportOnly) {
|
|
212
239
|
deps.enqueueListItems(bugs.map((f) => f.text));
|
|
213
240
|
enqueued = bugs.length;
|
|
214
241
|
cascadeStep = convertStep;
|
|
215
242
|
}
|
|
216
|
-
// Architectural findings → /goal proposal WITH Confirm
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
243
|
+
// Architectural findings: default mode → /goal proposal WITH Confirm;
|
|
244
|
+
// auto mode → /list items (the auto-loop rolls straight into them).
|
|
245
|
+
if (architectural.length > 0 && !reportOnly) {
|
|
246
|
+
if (auto) {
|
|
247
|
+
deps.enqueueListItems(architectural.map((f) => f.text));
|
|
248
|
+
enqueued += architectural.length;
|
|
249
|
+
cascadeStep = convertStep;
|
|
250
|
+
} else {
|
|
251
|
+
deps.proposeGoal(
|
|
252
|
+
architectural.map((f) => f.text).join("; "),
|
|
253
|
+
`reviewer found ${architectural.length} architectural-class finding(s) — needs your Confirm`,
|
|
254
|
+
);
|
|
255
|
+
proposed += architectural.length;
|
|
256
|
+
cascadeStep = "propose-goal";
|
|
257
|
+
}
|
|
224
258
|
}
|
|
225
|
-
// Clean completion → audit /goal (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
|
|
259
|
+
// Clean completion → audit: default mode proposes a /goal (Confirm);
|
|
260
|
+
// auto mode enqueues the audit as a /list item (no Confirm — the
|
|
261
|
+
// cascade keeps rolling until the findings run dry).
|
|
262
|
+
if (findings.length === 0 && config.cascade.includes("fire-audit-on-clean") && !reportOnly) {
|
|
263
|
+
const auditObjective = `Post-completion regression scan after ${source.goalId} (${config.auditScope})`;
|
|
264
|
+
if (auto) {
|
|
265
|
+
deps.enqueueListItems([auditObjective]);
|
|
266
|
+
enqueued++;
|
|
267
|
+
} else {
|
|
268
|
+
deps.proposeGoal(auditObjective, "reviewer: completion looks clean — firing the audit step");
|
|
269
|
+
proposed++;
|
|
270
|
+
}
|
|
232
271
|
cascadeStep = "fire-audit-on-clean";
|
|
233
272
|
}
|
|
273
|
+
if (reportOnly) cascadeStep = "report-only";
|
|
234
274
|
|
|
235
275
|
const report: ReviewReport = {
|
|
236
276
|
goalId: source.goalId,
|
|
@@ -238,6 +278,7 @@ export function runReviewer(
|
|
|
238
278
|
objective: source.objective,
|
|
239
279
|
findings,
|
|
240
280
|
cascadeStep,
|
|
281
|
+
mode: config.mode,
|
|
241
282
|
at: new Date(deps.nowMs).toISOString(),
|
|
242
283
|
};
|
|
243
284
|
const reportPath = writeReviewReport(deps.cwd, report);
|
|
@@ -265,6 +306,7 @@ export function runReviewer(
|
|
|
265
306
|
export function reviewerMenuOptions(cfg: ReviewerConfig): string[] {
|
|
266
307
|
return [
|
|
267
308
|
`Enabled — ${cfg.enabled ? "ON" : "OFF"}`,
|
|
309
|
+
`Mode — ${cfg.mode} (default = Confirm-gated · auto = auto-loop, no Confirms · report = report only)`,
|
|
268
310
|
`Leverage mode — ${cfg.leverageMode} (bug/refactor findings)`,
|
|
269
311
|
`Fire on goal-complete — ${cfg.fireOn.includes("goal-complete") ? "ON" : "OFF"}`,
|
|
270
312
|
`Fire on list-complete — ${cfg.fireOn.includes("list-complete") ? "ON" : "OFF"}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.3",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. — a pi-coding-agent extension that supervises long-running work, with isolated auditor on each completion. Beat bamboozling by design: the auditor runs in a fresh session with no extensions, no skills, no editor — only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|