pi-goal-list-loop-audit 0.28.13 → 0.28.14
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/goal-settings.ts +6 -0
- package/extensions/loops/goal.ts +140 -7
- package/package.json +1 -1
|
@@ -33,6 +33,11 @@ export interface Settings {
|
|
|
33
33
|
/** on → restored goals/loops/lists auto-resume even in fresh sessions
|
|
34
34
|
* (unattended rigs). Default off: restore holds until /goal resume. */
|
|
35
35
|
autoResume?: boolean;
|
|
36
|
+
/** v0.28.14: what happens to stale carryover (paused goal, waiting list,
|
|
37
|
+
* held loop from before this session) when NEW work activates.
|
|
38
|
+
* pause (default) = leave it + ONE summary; clear = drop it all honestly;
|
|
39
|
+
* resume = legacy silent stacking. */
|
|
40
|
+
carryover?: "resume" | "pause" | "clear";
|
|
36
41
|
/** v0.24.2: pause the goal after N consecutive auditor disapprovals (0 = unlimited).
|
|
37
42
|
* Default 5 (raised from 3 in v0.25.0, contract item 7). */
|
|
38
43
|
auditCap?: number;
|
|
@@ -146,6 +151,7 @@ export const SETTINGS_KEYS: Array<keyof Settings> = [
|
|
|
146
151
|
"tokenLimit",
|
|
147
152
|
"wedgeAlertMinutes",
|
|
148
153
|
"autoResume",
|
|
154
|
+
"carryover",
|
|
149
155
|
"autoAcceptDrafts",
|
|
150
156
|
"auditCap",
|
|
151
157
|
"auditFeedbackChars",
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -289,6 +289,56 @@ async function confirmDraft(ctx: ExtensionContext, title: string, body: string):
|
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
// v0.28.14: ONE summary + policy application for stale carryover when NEW
|
|
293
|
+
// work activates. pause (default): surface what's waiting, stack nothing
|
|
294
|
+
// silently. clear: drop the queue, dismiss the held loop, archive the
|
|
295
|
+
// paused goal — honestly, with a ledger trail. resume: legacy silent
|
|
296
|
+
// behavior. A new GOAL replacing a paused one archives it in every policy
|
|
297
|
+
// (one-active-thing: state.goal holds exactly one goal).
|
|
298
|
+
function resolveCarryover(ctx: ExtensionContext, trigger: "goal" | "loop"): void {
|
|
299
|
+
if (carryoverResolved || !carryoverSnapshot) return;
|
|
300
|
+
carryoverResolved = true;
|
|
301
|
+
const snap = carryoverSnapshot;
|
|
302
|
+
carryoverSnapshot = null;
|
|
303
|
+
const policy = loadSettings(ctx.cwd).carryover ?? "pause";
|
|
304
|
+
if (policy === "resume") return; // legacy silent stacking
|
|
305
|
+
const done: string[] = [];
|
|
306
|
+
const waiting: string[] = [];
|
|
307
|
+
const pausedGoal = state.goal && state.goal.status === "paused" ? state.goal : null;
|
|
308
|
+
if (pausedGoal && (trigger === "goal" || policy === "clear")) {
|
|
309
|
+
archiveCurrentGoal(ctx, "aborted", trigger === "goal" ? "replaced by new goal (carryover)" : "carryover cleared");
|
|
310
|
+
done.push(`archived paused goal "${(snap.pausedGoal ?? pausedGoal.objective).slice(0, 60)}"`);
|
|
311
|
+
} else if (snap.pausedGoal) {
|
|
312
|
+
waiting.push(`paused goal "${snap.pausedGoal}" (/goal resume)`);
|
|
313
|
+
}
|
|
314
|
+
if (snap.listCount > 0) {
|
|
315
|
+
if (policy === "clear") {
|
|
316
|
+
state = { ...state, list: [] };
|
|
317
|
+
done.push(`dropped ${snap.listCount} waiting list item(s)`);
|
|
318
|
+
} else {
|
|
319
|
+
waiting.push(`${snap.listCount} waiting list item(s) (/list next)`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (snap.heldLoop) {
|
|
323
|
+
if (policy === "clear" && state.loop && !state.loop.active && state.loop.stopReason === HELD_ON_RESTORE) {
|
|
324
|
+
state.loop = { ...state.loop, stopReason: "cleared: carryover" };
|
|
325
|
+
done.push(`dismissed held loop "${snap.heldLoop}"`);
|
|
326
|
+
} else {
|
|
327
|
+
waiting.push(`held loop "${snap.heldLoop}" (/loop to resume)`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
persistState(ctx);
|
|
331
|
+
appendLedger(ctx.cwd, "carryover_resolved", { policy, trigger, cleared: done.length, waiting: waiting.length });
|
|
332
|
+
const summary = [...done.map((d) => `✂ ${d}`), ...waiting.map((w) => `⏸ ${w}`)].join(" · ");
|
|
333
|
+
if (!summary) return;
|
|
334
|
+
ctx.ui.notify(
|
|
335
|
+
policy === "clear"
|
|
336
|
+
? `Carryover cleared (${trigger}): ${summary}`
|
|
337
|
+
: `Carryover from before this session: ${summary}${waiting.length > 0 ? " — /glla carryover=clear drops these automatically." : ""}`,
|
|
338
|
+
"info",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
292
342
|
// The most recent ExtensionContext seen from any event or command handler.
|
|
293
343
|
// pi replaces sessions (newSession/fork/reload) and stale ctx throws on use,
|
|
294
344
|
// so timers must never capture a ctx — they read lastCtx at fire time.
|
|
@@ -352,6 +402,11 @@ let postRestoreGraceTurns = 0;
|
|
|
352
402
|
// refire's own noteActivity, which is what made the hegemon zombie spin
|
|
353
403
|
// self-sustaining (619 refires / 23.5h / zero turns).
|
|
354
404
|
let consecutiveStalls = 0;
|
|
405
|
+
// v0.28.14: carryover snapshot — unfinished work loaded from disk at
|
|
406
|
+
// session_start (predates this session). Resolved ONCE per session at the
|
|
407
|
+
// first NEW activation (new goal / new loop) per the carryover setting.
|
|
408
|
+
let carryoverSnapshot: { pausedGoal?: string; listCount: number; heldLoop?: string } | null = null;
|
|
409
|
+
let carryoverResolved = true;
|
|
355
410
|
// v0.26.6: precise replacement for the removed ship-recency suppression —
|
|
356
411
|
// set while complete_goal's isolated audit runs, so the heartbeat never
|
|
357
412
|
// refires into an in-flight completion.
|
|
@@ -807,7 +862,13 @@ function notifyPersistenceState(ctx: ExtensionContext): void {
|
|
|
807
862
|
}
|
|
808
863
|
|
|
809
864
|
function setGoal(goal: Goal, ctx: ExtensionContext): void {
|
|
810
|
-
|
|
865
|
+
// v0.28.14: never silently orphan a live goal — a paused/active goal
|
|
866
|
+
// being replaced is archived honestly first (the old behavior left it in
|
|
867
|
+
// goals/ but untracked: "older goals lying around leading to confusion").
|
|
868
|
+
if (state.goal && state.goal.id !== goal.id && (state.goal.status === "active" || state.goal.status === "paused")) {
|
|
869
|
+
archiveCurrentGoal(ctx, "aborted", `replaced by goal ${goal.id}`);
|
|
870
|
+
}
|
|
871
|
+
state = { ...state, goal }; // preserve list AND loop (v0.28.14: the bare reconstruction used to nuke a held/active loop whenever a goal was set)
|
|
811
872
|
const file = writeGoalMd(ctx.cwd, goal);
|
|
812
873
|
state.goal!.activePath = path.relative(ctx.cwd, file) || file;
|
|
813
874
|
persistState(ctx);
|
|
@@ -838,7 +899,7 @@ function archiveCurrentGoal(ctx: ExtensionContext, status: Status, stopReason?:
|
|
|
838
899
|
if (archived) {
|
|
839
900
|
try { fs.unlinkSync(goalMdPath(ctx.cwd, goal.id)); } catch {}
|
|
840
901
|
}
|
|
841
|
-
state = { goal: { ...goal, status, archivedPath: path.relative(ctx.cwd, target) || target, stopReason }
|
|
902
|
+
state = { ...state, goal: { ...goal, status, archivedPath: path.relative(ctx.cwd, target) || target, stopReason } };
|
|
842
903
|
appendLedger(ctx.cwd, "goal_archived", { goalId: goal.id, status, stopReason });
|
|
843
904
|
persistState(ctx);
|
|
844
905
|
// Loop 2: a list-sourced goal COMPLETED → auto-activate the next item.
|
|
@@ -957,6 +1018,13 @@ function listQueue(): NonNullable<State["list"]> {
|
|
|
957
1018
|
}
|
|
958
1019
|
|
|
959
1020
|
function activateNextListItem(ctx: ExtensionContext, n = 1): boolean {
|
|
1021
|
+
// v0.28.14: one-active-thing choke point — NO call site (session_start,
|
|
1022
|
+
// completion cascade, /list next, list_activate, list-draft auto-activate)
|
|
1023
|
+
// may activate a list item over a live loop, present or future.
|
|
1024
|
+
if (isLoopActive()) {
|
|
1025
|
+
appendLedger(ctx.cwd, "list_activation_blocked_loop", {});
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
960
1028
|
const queue = listQueue();
|
|
961
1029
|
const taken = takeAt(queue, n);
|
|
962
1030
|
if (!taken) return false;
|
|
@@ -1110,6 +1178,7 @@ async function cmdSet(args: string, ctx: ExtensionContext, skipDraft = false): P
|
|
|
1110
1178
|
return;
|
|
1111
1179
|
}
|
|
1112
1180
|
draftingTarget = null; // explicit objective cancels any drafting session
|
|
1181
|
+
resolveCarryover(ctx, "goal"); // v0.28.14: surface/clear stale leftovers
|
|
1113
1182
|
const goal = createGoal(raw, ctx);
|
|
1114
1183
|
setGoal(goal, ctx);
|
|
1115
1184
|
// Reset counters
|
|
@@ -1194,10 +1263,17 @@ async function cmdResume(ctx: ExtensionContext): Promise<void> {
|
|
|
1194
1263
|
}
|
|
1195
1264
|
|
|
1196
1265
|
async function cmdCancel(ctx: ExtensionContext): Promise<void> {
|
|
1197
|
-
if (!state.goal)
|
|
1266
|
+
if (!state.goal) {
|
|
1267
|
+
// v0.28.14: users reach for /goal cancel to kill a LOOP (no goal
|
|
1268
|
+
// active) — point at the right verb instead of doing nothing silently.
|
|
1269
|
+
if (isLoopActive()) {
|
|
1270
|
+
ctx.ui.notify("No goal to cancel — a LOOP is active: /loop stop (or /loop cancel) ends it.", "info");
|
|
1271
|
+
}
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1198
1274
|
archiveCurrentGoal(ctx, "aborted", "user cancelled");
|
|
1199
1275
|
ctx.abort();
|
|
1200
|
-
ctx.ui.notify(
|
|
1276
|
+
ctx.ui.notify(`Goal aborted.${isLoopActive() ? " A loop is still active — /loop stop ends it." : ""}`, "info");
|
|
1201
1277
|
}
|
|
1202
1278
|
|
|
1203
1279
|
async function cmdGoals(ctx: ExtensionContext): Promise<void> {
|
|
@@ -1464,6 +1540,11 @@ async function cmdList(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1464
1540
|
ctx.ui.notify(`Usage: /list next [1-${listQueue().length || 1}]`, "info");
|
|
1465
1541
|
return;
|
|
1466
1542
|
}
|
|
1543
|
+
// v0.28.14: one-active-thing — /list next must not jump a live loop.
|
|
1544
|
+
if (isLoopActive()) {
|
|
1545
|
+
ctx.ui.notify("A loop is active — /loop stop it before activating a list item.", "warning");
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1467
1548
|
if (state.goal && state.goal.status === "active") {
|
|
1468
1549
|
archiveCurrentGoal(ctx, "aborted", `skipped via /list next ${n > 1 ? n : ""}`.trim());
|
|
1469
1550
|
}
|
|
@@ -1912,6 +1993,7 @@ async function startLoopFromConfig(ctx: ExtensionContext, cfg: LoopConfig): Prom
|
|
|
1912
1993
|
);
|
|
1913
1994
|
return false;
|
|
1914
1995
|
}
|
|
1996
|
+
resolveCarryover(ctx, "loop"); // v0.28.14: surface/clear stale leftovers
|
|
1915
1997
|
state = {
|
|
1916
1998
|
...state,
|
|
1917
1999
|
loop: {
|
|
@@ -1965,6 +2047,12 @@ async function cmdLoop(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
1965
2047
|
}
|
|
1966
2048
|
const stored = state.loop;
|
|
1967
2049
|
if (stored && !stored.active && stored.stopReason === HELD_ON_RESTORE) {
|
|
2050
|
+
// v0.28.14: one-active-thing — a held loop must not resume over an
|
|
2051
|
+
// active goal/list-item (this was the last unguarded stacking path).
|
|
2052
|
+
if (state.goal && state.goal.status === "active") {
|
|
2053
|
+
ctx.ui.notify("A goal is active — the held loop stays held. /goal pause or /goal cancel it first, then /loop to resume.", "warning");
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
1968
2056
|
state.loop = { ...stored, active: true, stopReason: undefined };
|
|
1969
2057
|
persistState(ctx);
|
|
1970
2058
|
scheduleLoopTick(ctx);
|
|
@@ -2026,13 +2114,15 @@ async function cmdLoop(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
2026
2114
|
return;
|
|
2027
2115
|
}
|
|
2028
2116
|
|
|
2029
|
-
|
|
2117
|
+
// v0.28.14: /loop cancel is a first-class alias — users reached for
|
|
2118
|
+
// /goal cancel to kill loops because "cancel" is the verb they know.
|
|
2119
|
+
if (sub === "stop" || sub === "cancel") {
|
|
2030
2120
|
if (!state.loop) {
|
|
2031
2121
|
ctx.ui.notify("No loop to stop.", "info");
|
|
2032
2122
|
return;
|
|
2033
2123
|
}
|
|
2034
2124
|
clearLoopTimer();
|
|
2035
|
-
state.loop = { ...state.loop, active: false, stopReason: state.loop.stopReason ??
|
|
2125
|
+
state.loop = { ...state.loop, active: false, stopReason: state.loop.stopReason ?? `stopped by user (/loop ${sub})` };
|
|
2036
2126
|
persistState(ctx);
|
|
2037
2127
|
await finishLoopGit(ctx, state.loop);
|
|
2038
2128
|
appendLedger(ctx.cwd, "loop_stopped", { reason: "user", iterations: state.loop.iteration, best: state.loop.bestValue });
|
|
@@ -2646,6 +2736,12 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2646
2736
|
};
|
|
2647
2737
|
}
|
|
2648
2738
|
const liveCtx = (execCtx as ExtensionContext | undefined) ?? ctx;
|
|
2739
|
+
// v0.28.14: one-active-thing EARLY guard — refuse the whole interview
|
|
2740
|
+
// when a loop is live (the post-confirm backstop below stays: state
|
|
2741
|
+
// can change mid-interview).
|
|
2742
|
+
if (isLoopActive()) {
|
|
2743
|
+
return { content: [{ type: "text", text: "A loop is active — one active thing at a time. The user must /loop stop it before a goal or list item can activate; do not re-propose until then." }], details: {} };
|
|
2744
|
+
}
|
|
2649
2745
|
// v0.14.0: the interview floor — no Confirm until the user replied.
|
|
2650
2746
|
// v0.23.8: /glla autoaccept=on skips the floor AND the Confirm —
|
|
2651
2747
|
// the seed carries the intent (unattended rigs). Default off.
|
|
@@ -2745,6 +2841,11 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2745
2841
|
const confirmedTarget = draftingTarget;
|
|
2746
2842
|
draftingTarget = null;
|
|
2747
2843
|
const full = p.objective.trim() + (normContract ? `\nDone when:\n${normContract}` : "");
|
|
2844
|
+
// v0.28.14: one-active-thing — no goal/list activation over a live loop.
|
|
2845
|
+
if (isLoopActive()) {
|
|
2846
|
+
return { content: [{ type: "text", text: "A loop is active — one active thing at a time. The user must /loop stop it before a goal or list item can activate; do not re-propose until then." }], details: {} };
|
|
2847
|
+
}
|
|
2848
|
+
resolveCarryover(liveCtx, "goal"); // v0.28.14: surface/clear stale leftovers
|
|
2748
2849
|
// List drafting: the confirmed contract goes into the QUEUE, not active.
|
|
2749
2850
|
if (confirmedTarget === "list") {
|
|
2750
2851
|
const extracted = extractVerificationContract(full);
|
|
@@ -2795,6 +2896,11 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2795
2896
|
details: {},
|
|
2796
2897
|
};
|
|
2797
2898
|
}
|
|
2899
|
+
// v0.28.14: one-active-thing EARLY guard — refuse before the
|
|
2900
|
+
// interview floor (a live goal blocks any loop proposal).
|
|
2901
|
+
if (state.goal && state.goal.status === "active") {
|
|
2902
|
+
return { content: [{ type: "text", text: "A goal is active — one active thing at a time. The user must /goal pause or /goal cancel it before a loop can start; do not re-propose until then." }], details: {} };
|
|
2903
|
+
}
|
|
2798
2904
|
// v0.14.0: the interview floor — no Confirm until the user replied.
|
|
2799
2905
|
if (draftingUserReplies === 0) draftingBlockedProposals++;
|
|
2800
2906
|
const loopBlock = draftProposalBlock(draftingUserReplies, draftingBlockedProposals);
|
|
@@ -2810,6 +2916,12 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
2810
2916
|
return { content: [{ type: "text", text: 'direction=min|max is required for a measured loop (omit measureCmd or pass "none" for a metricless spec loop).' }], details: {} };
|
|
2811
2917
|
}
|
|
2812
2918
|
const liveCtx = (execCtx as ExtensionContext | undefined) ?? ctx;
|
|
2919
|
+
// v0.28.14: one-active-thing — refuse to even test-run a loop measure
|
|
2920
|
+
// while a goal/list-item is active (the /loop start COMMAND guards
|
|
2921
|
+
// this; the tool path used to skip it and stack a loop over a goal).
|
|
2922
|
+
if (state.goal && state.goal.status === "active") {
|
|
2923
|
+
return { content: [{ type: "text", text: "A goal is active — one active thing at a time. The user must /goal pause or /goal cancel it before a loop can start; do not re-propose until then." }], details: {} };
|
|
2924
|
+
}
|
|
2813
2925
|
// THE TEST-RUN: orchestrator runs the proposed measure once. The user
|
|
2814
2926
|
// sees the real number before a single iteration burns tokens.
|
|
2815
2927
|
// (Metricless loops skip this — there is no measure to test-run.)
|
|
@@ -3019,6 +3131,10 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
3019
3131
|
return { content: [{ type: "text", text: "n must be a positive integer (1-based position)." }], details: {} };
|
|
3020
3132
|
}
|
|
3021
3133
|
const liveCtx = (execCtx as ExtensionContext | undefined) ?? ctx;
|
|
3134
|
+
// v0.28.14: one-active-thing — a list item must not jump a live loop.
|
|
3135
|
+
if (isLoopActive()) {
|
|
3136
|
+
return { content: [{ type: "text", text: "A loop is active — one active thing at a time. The user must /loop stop it before a list item can activate." }], details: {} };
|
|
3137
|
+
}
|
|
3022
3138
|
if (state.goal && state.goal.status === "active") {
|
|
3023
3139
|
archiveCurrentGoal(liveCtx, "aborted", "skipped via list_activate");
|
|
3024
3140
|
}
|
|
@@ -3812,6 +3928,14 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
3812
3928
|
} else {
|
|
3813
3929
|
ctx.ui.notify(`autoresume must be on or off, got: ${value}`, "warning");
|
|
3814
3930
|
}
|
|
3931
|
+
} else if (key === "carryover") {
|
|
3932
|
+
if (["resume", "pause", "clear"].includes(value)) {
|
|
3933
|
+
patch.carryover = value as "resume" | "pause" | "clear";
|
|
3934
|
+
changed = true;
|
|
3935
|
+
ctx.ui.notify(`carryover=${value}: ${value === "clear" ? "stale goals/lists/held-loops are dropped when new work activates" : value === "pause" ? "stale carryover is surfaced in one summary when new work activates (default)" : "legacy behavior — carryover stacks silently"}.`, "info");
|
|
3936
|
+
} else {
|
|
3937
|
+
ctx.ui.notify(`carryover must be resume, pause, or clear, got: ${value}`, "warning");
|
|
3938
|
+
}
|
|
3815
3939
|
} else if (key === "autoaccept") {
|
|
3816
3940
|
if (["on", "true", "1", "yes"].includes(value)) {
|
|
3817
3941
|
patch.autoAcceptDrafts = true;
|
|
@@ -3935,7 +4059,7 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
3935
4059
|
}
|
|
3936
4060
|
}
|
|
3937
4061
|
if (!changed) {
|
|
3938
|
-
ctx.ui.notify("Nothing changed. Use key=value (model, thinking, notify, tokenlimit, autoresume, auditcap, auditfeedbackchars, aggressivemode, quotaretryminutes, stuckmax), optionally prefixed with 'project'.", "info");
|
|
4062
|
+
ctx.ui.notify("Nothing changed. Use key=value (model, thinking, notify, tokenlimit, autoresume, carryover, auditcap, auditfeedbackchars, aggressivemode, quotaretryminutes, stuckmax), optionally prefixed with 'project'.", "info");
|
|
3939
4063
|
return;
|
|
3940
4064
|
}
|
|
3941
4065
|
saveSettings(scope, ctx.cwd, patch);
|
|
@@ -4253,6 +4377,15 @@ export default function (pi: ExtensionAPI): void {
|
|
|
4253
4377
|
// a foreign session.
|
|
4254
4378
|
if (isForeignCtx(ctx)) return;
|
|
4255
4379
|
state = readState(ctx.cwd);
|
|
4380
|
+
// v0.28.14: snapshot carryover BEFORE any restore logic mutates state —
|
|
4381
|
+
// a paused goal, waiting list items, or a loop that was live/held when
|
|
4382
|
+
// the last session ended. Resolved once at the first NEW activation.
|
|
4383
|
+
carryoverSnapshot = {
|
|
4384
|
+
pausedGoal: state.goal && state.goal.status === "paused" ? state.goal.objective.slice(0, 60) : undefined,
|
|
4385
|
+
listCount: listQueue().length,
|
|
4386
|
+
heldLoop: state.loop && (state.loop.active || state.loop.stopReason === HELD_ON_RESTORE) ? state.loop.target.slice(0, 60) : undefined,
|
|
4387
|
+
};
|
|
4388
|
+
carryoverResolved = !(carryoverSnapshot.pausedGoal || carryoverSnapshot.listCount > 0 || carryoverSnapshot.heldLoop);
|
|
4256
4389
|
if (!registeredCtx) {
|
|
4257
4390
|
registerAgentTools(pi, ctx);
|
|
4258
4391
|
registeredCtx = ctx;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.14",
|
|
4
4
|
"description": "Goal. Loop. Audit. Done. \u2014 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 \u2014 only the read tools needed to verify your goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dracon",
|