pi-goal-list-loop-audit 0.25.1 → 0.25.2
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.
|
@@ -157,6 +157,10 @@ export interface Goal {
|
|
|
157
157
|
};
|
|
158
158
|
createdAt: string;
|
|
159
159
|
updatedAt: string;
|
|
160
|
+
/** v0.25.2: per-goal telemetry for /glla stats premature-success
|
|
161
|
+
* detection. Bumped live: turns on agent_end, fileWrites/bashCalls on
|
|
162
|
+
* tool_result while the goal is active. */
|
|
163
|
+
telemetry?: { turns: number; fileWrites: number; bashCalls: number };
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
/**
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// pi-goal-list-loop-audit — v0.25.2
|
|
2
|
+
// extensions/goal-loop-stats.ts
|
|
3
|
+
//
|
|
4
|
+
// /glla stats: per-project ledger rollups. Scans .pi-glla/active.jsonl
|
|
5
|
+
// across every project on the rig and produces the cross-project table
|
|
6
|
+
// the spec-driven verifier (v0.25 design) will be hardened against.
|
|
7
|
+
// Pure helpers take strings/paths so tests drive them from tmpdirs —
|
|
8
|
+
// no dependencies beyond node stdlib (contract boundary).
|
|
9
|
+
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as os from "node:os";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
|
|
14
|
+
export interface LedgerEntry {
|
|
15
|
+
type: string;
|
|
16
|
+
at?: string;
|
|
17
|
+
value?: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface GoalTelemetry {
|
|
21
|
+
turns: number;
|
|
22
|
+
fileWrites: number;
|
|
23
|
+
bashCalls: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Minimal per-goal shape the rollup reasons about (the ledger's `state`
|
|
27
|
+
* snapshots carry the full goal object; archived goals keep their last). */
|
|
28
|
+
export interface GoalRollupSource {
|
|
29
|
+
id: string;
|
|
30
|
+
status?: string;
|
|
31
|
+
createdAt?: string;
|
|
32
|
+
updatedAt?: string;
|
|
33
|
+
usage?: { tokensUsed?: number };
|
|
34
|
+
auditHistory?: Array<{ approved?: boolean; disapproved?: boolean; error?: string }>;
|
|
35
|
+
telemetry?: GoalTelemetry;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ProjectRollup {
|
|
39
|
+
project: string;
|
|
40
|
+
goalsCreated: number;
|
|
41
|
+
auditsApproved: number;
|
|
42
|
+
auditsDisapproved: number;
|
|
43
|
+
auditsError: number;
|
|
44
|
+
avgTurns: number;
|
|
45
|
+
avgWrites: number;
|
|
46
|
+
prematureCount: number;
|
|
47
|
+
/** Total token usage across goals (cost in tokens — no price data on
|
|
48
|
+
* this rig; documented in INSTALL.md). */
|
|
49
|
+
totalCost: number;
|
|
50
|
+
lastActive: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Premature-success thresholds (spec-driven verifier design §3): an
|
|
54
|
+
* approved goal with almost no turns, no real editing, and no
|
|
55
|
+
* verification commands is a "claimed done in 12 turns with 0 file
|
|
56
|
+
* writes" pattern the auditor should have caught. */
|
|
57
|
+
export const PREMATURE_THRESHOLDS = {
|
|
58
|
+
maxTurns: 50,
|
|
59
|
+
maxFileWrites: 5,
|
|
60
|
+
maxBashCalls: 8,
|
|
61
|
+
} as const;
|
|
62
|
+
|
|
63
|
+
export function parseLedgerEntries(jsonl: string): LedgerEntry[] {
|
|
64
|
+
const out: LedgerEntry[] = [];
|
|
65
|
+
for (const line of jsonl.split("\n")) {
|
|
66
|
+
const t = line.trim();
|
|
67
|
+
if (!t) continue;
|
|
68
|
+
try {
|
|
69
|
+
const e = JSON.parse(t);
|
|
70
|
+
if (e && typeof e === "object" && typeof e.type === "string") out.push(e as LedgerEntry);
|
|
71
|
+
} catch {
|
|
72
|
+
/* malformed line — skip */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Flag the "approved too easily" pattern. Goals without telemetry
|
|
79
|
+
* (archived before v0.25.2) are UNKNOWN, not premature — we do not
|
|
80
|
+
* back-convict historical goals on missing data. */
|
|
81
|
+
export function detectPrematureSuccess(goal: GoalRollupSource): boolean {
|
|
82
|
+
const audits = goal.auditHistory ?? [];
|
|
83
|
+
const approved = audits.filter((a) => a.approved).length;
|
|
84
|
+
if (approved === 0) return false;
|
|
85
|
+
const t = goal.telemetry;
|
|
86
|
+
if (!t) return false;
|
|
87
|
+
return (
|
|
88
|
+
t.turns < PREMATURE_THRESHOLDS.maxTurns &&
|
|
89
|
+
t.fileWrites < PREMATURE_THRESHOLDS.maxFileWrites &&
|
|
90
|
+
t.bashCalls < PREMATURE_THRESHOLDS.maxBashCalls
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Roll up one project's ledger. Pure over the parsed entries — the file
|
|
95
|
+
* read happens in rollupProject. */
|
|
96
|
+
export function rollupEntries(project: string, entries: LedgerEntry[]): ProjectRollup {
|
|
97
|
+
let goalsCreated = 0;
|
|
98
|
+
let lastActive = "";
|
|
99
|
+
const finalGoal = new Map<string, GoalRollupSource>();
|
|
100
|
+
for (const e of entries) {
|
|
101
|
+
if (e.at && e.at > lastActive) lastActive = e.at;
|
|
102
|
+
if (e.type === "goal_created") goalsCreated++;
|
|
103
|
+
if (e.type === "state" && e.value?.goal?.id) {
|
|
104
|
+
finalGoal.set(String(e.value.goal.id), e.value.goal as GoalRollupSource);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
let auditsApproved = 0;
|
|
108
|
+
let auditsDisapproved = 0;
|
|
109
|
+
let auditsError = 0;
|
|
110
|
+
let prematureCount = 0;
|
|
111
|
+
let totalCost = 0;
|
|
112
|
+
let turnsSum = 0;
|
|
113
|
+
let turnsN = 0;
|
|
114
|
+
let writesSum = 0;
|
|
115
|
+
let writesN = 0;
|
|
116
|
+
for (const goal of finalGoal.values()) {
|
|
117
|
+
for (const a of goal.auditHistory ?? []) {
|
|
118
|
+
if (a.approved) auditsApproved++;
|
|
119
|
+
else if (a.disapproved) auditsDisapproved++;
|
|
120
|
+
else if (a.error) auditsError++;
|
|
121
|
+
}
|
|
122
|
+
if (detectPrematureSuccess(goal)) prematureCount++;
|
|
123
|
+
totalCost += goal.usage?.tokensUsed ?? 0;
|
|
124
|
+
if (goal.telemetry) {
|
|
125
|
+
turnsSum += goal.telemetry.turns;
|
|
126
|
+
turnsN++;
|
|
127
|
+
writesSum += goal.telemetry.fileWrites;
|
|
128
|
+
writesN++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
project,
|
|
133
|
+
goalsCreated,
|
|
134
|
+
auditsApproved,
|
|
135
|
+
auditsDisapproved,
|
|
136
|
+
auditsError,
|
|
137
|
+
avgTurns: turnsN > 0 ? Math.round((turnsSum / turnsN) * 10) / 10 : 0,
|
|
138
|
+
avgWrites: writesN > 0 ? Math.round((writesSum / writesN) * 10) / 10 : 0,
|
|
139
|
+
prematureCount,
|
|
140
|
+
totalCost,
|
|
141
|
+
lastActive,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function rollupProject(projectPath: string): ProjectRollup | undefined {
|
|
146
|
+
const ledger = path.join(projectPath, ".pi-glla", "active.jsonl");
|
|
147
|
+
let raw: string;
|
|
148
|
+
try {
|
|
149
|
+
raw = fs.readFileSync(ledger, "utf-8");
|
|
150
|
+
} catch {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
return rollupEntries(projectPath, parseLedgerEntries(raw));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Project discovery (contract item 6). Sources:
|
|
157
|
+
* 1. ~/.pi/agent/sessions/ — session dir names encode their cwd
|
|
158
|
+
* (`--home-dracon-chat-` ≈ /home/dracon/chat); cheap, no file scans.
|
|
159
|
+
* 2. A bounded walk under ~ (maxdepth 6, pruning node_modules/.git and
|
|
160
|
+
* hidden dirs) for .pi-glla/ directories — catches projects no
|
|
161
|
+
* session has visited. (Deviation from the contract's one-level walk:
|
|
162
|
+
* one level would miss the very projects cited — polis is depth 5.)
|
|
163
|
+
* 3. The current cwd.
|
|
164
|
+
* Only roots with a real .pi-glla/active.jsonl survive. Budget-guarded:
|
|
165
|
+
* the walk stops after `budgetMs`. */
|
|
166
|
+
export function discoverGllaProjects(opts: { home?: string; cwd?: string; budgetMs?: number } = {}): string[] {
|
|
167
|
+
const home = opts.home ?? os.homedir();
|
|
168
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
169
|
+
const budgetMs = opts.budgetMs ?? 2000;
|
|
170
|
+
const deadline = Date.now() + budgetMs;
|
|
171
|
+
const found = new Set<string>();
|
|
172
|
+
|
|
173
|
+
const hasLedger = (dir: string): boolean => {
|
|
174
|
+
try {
|
|
175
|
+
fs.accessSync(path.join(dir, ".pi-glla", "active.jsonl"));
|
|
176
|
+
return true;
|
|
177
|
+
} catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Source 1: session dir names.
|
|
183
|
+
try {
|
|
184
|
+
const sessionsDir = path.join(home, ".pi", "agent", "sessions");
|
|
185
|
+
for (const name of fs.readdirSync(sessionsDir)) {
|
|
186
|
+
// --home-dracon-chat- → /home/dracon/chat (best-effort decode:
|
|
187
|
+
// strip leading/trailing dashes, then replace -- → /- and - → /).
|
|
188
|
+
const decoded = name.replace(/^-+|-+$/g, "").replace(/--/g, "\0").replace(/-/g, "/").replace(/\0/g, "-");
|
|
189
|
+
const candidate = "/" + decoded;
|
|
190
|
+
if (hasLedger(candidate)) found.add(candidate);
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
/* no sessions dir */
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Source 2: bounded walk — targeted roots FIRST (~/Dev, ~/chat hold the
|
|
197
|
+
// rig's projects; polis sits at depth 5), the general home walk last
|
|
198
|
+
// with whatever budget remains. Deep/wide dirs that never hold projects
|
|
199
|
+
// are pruned.
|
|
200
|
+
const PRUNE = new Set(["node_modules", ".git", ".pi", ".cache", ".npm", ".local", ".config", "Downloads", "Pictures", "Videos", "Music", ".mozilla", ".vscode", "snap", ".steam", ".wine"]);
|
|
201
|
+
const walk = (dir: string, depth: number): void => {
|
|
202
|
+
if (depth > 6 || Date.now() > deadline) return;
|
|
203
|
+
if (hasLedger(dir)) {
|
|
204
|
+
found.add(dir);
|
|
205
|
+
return; // no nested projects below a project root
|
|
206
|
+
}
|
|
207
|
+
let entries: fs.Dirent[];
|
|
208
|
+
try {
|
|
209
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
210
|
+
} catch {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
for (const e of entries) {
|
|
214
|
+
if (Date.now() > deadline) return;
|
|
215
|
+
if (!e.isDirectory()) continue;
|
|
216
|
+
if (PRUNE.has(e.name)) continue;
|
|
217
|
+
walk(path.join(dir, e.name), depth + 1);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
for (const root of [path.join(home, "Dev"), path.join(home, "chat")]) {
|
|
221
|
+
if (Date.now() > deadline) break;
|
|
222
|
+
walk(root, 1);
|
|
223
|
+
}
|
|
224
|
+
walk(home, 0);
|
|
225
|
+
|
|
226
|
+
// Source 3: cwd.
|
|
227
|
+
if (hasLedger(cwd)) found.add(cwd);
|
|
228
|
+
|
|
229
|
+
return [...found].sort();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Contract item 4: premature filter — only projects with
|
|
233
|
+
* premature_count > 0, sorted by premature ratio descending. */
|
|
234
|
+
export function filterPremature(rollups: ProjectRollup[]): ProjectRollup[] {
|
|
235
|
+
return rollups
|
|
236
|
+
.filter((r) => r.prematureCount > 0)
|
|
237
|
+
.sort((a, b) => b.prematureCount / Math.max(1, b.goalsCreated) - a.prematureCount / Math.max(1, a.goalsCreated));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function shortProject(p: string): string {
|
|
241
|
+
const home = os.homedir();
|
|
242
|
+
return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function formatRollupTable(rollups: ProjectRollup[]): string {
|
|
246
|
+
const header = "| project | goals | approved | disapproved | errors | avg turns | avg writes | premature | tokens | last active |";
|
|
247
|
+
const sep = "|---|---|---|---|---|---|---|---|---|---|";
|
|
248
|
+
const rows = rollups.map(
|
|
249
|
+
(r) =>
|
|
250
|
+
`| ${shortProject(r.project)} | ${r.goalsCreated} | ${r.auditsApproved} | ${r.auditsDisapproved} | ${r.auditsError} | ${r.avgTurns} | ${r.avgWrites} | ${r.prematureCount} | ${r.totalCost.toLocaleString()} | ${r.lastActive ? r.lastActive.slice(0, 10) : "—"} |`,
|
|
251
|
+
);
|
|
252
|
+
return [header, sep, ...rows].join("\n");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** JSON schema matches the table exactly (contract item 2). */
|
|
256
|
+
export function formatRollupJson(rollups: ProjectRollup[]): string {
|
|
257
|
+
return JSON.stringify(
|
|
258
|
+
rollups.map((r) => ({
|
|
259
|
+
project: r.project,
|
|
260
|
+
goals_created: r.goalsCreated,
|
|
261
|
+
audits_approved: r.auditsApproved,
|
|
262
|
+
audits_disapproved: r.auditsDisapproved,
|
|
263
|
+
audits_error: r.auditsError,
|
|
264
|
+
avg_turns: r.avgTurns,
|
|
265
|
+
avg_writes: r.avgWrites,
|
|
266
|
+
premature_count: r.prematureCount,
|
|
267
|
+
total_cost: r.totalCost,
|
|
268
|
+
last_active: r.lastActive || null,
|
|
269
|
+
})),
|
|
270
|
+
null,
|
|
271
|
+
2,
|
|
272
|
+
);
|
|
273
|
+
}
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -90,6 +90,14 @@ import {
|
|
|
90
90
|
settingsProvenance,
|
|
91
91
|
type Settings,
|
|
92
92
|
} from "../goal-settings.js";
|
|
93
|
+
import {
|
|
94
|
+
discoverGllaProjects,
|
|
95
|
+
filterPremature,
|
|
96
|
+
formatRollupJson,
|
|
97
|
+
formatRollupTable,
|
|
98
|
+
rollupProject,
|
|
99
|
+
type ProjectRollup,
|
|
100
|
+
} from "../goal-loop-stats.js";
|
|
93
101
|
import { runGoalCompletionAuditor } from "../goal-loop-auditor.js";
|
|
94
102
|
import {
|
|
95
103
|
REPETITION,
|
|
@@ -2667,6 +2675,42 @@ async function openSettingsUI(ctx: ExtensionContext): Promise<void> {
|
|
|
2667
2675
|
}
|
|
2668
2676
|
}
|
|
2669
2677
|
|
|
2678
|
+
/**
|
|
2679
|
+
* v0.25.2: /glla stats — one command, every project's rollup. Args:
|
|
2680
|
+
* (none) markdown table, all discovered projects
|
|
2681
|
+
* json machine-readable rollup (same schema as the table)
|
|
2682
|
+
* premature only projects with premature_success > 0, ratio-sorted
|
|
2683
|
+
* project=<path> limit the scan to one project
|
|
2684
|
+
*/
|
|
2685
|
+
function cmdStats(args: string, ctx: ExtensionContext): void {
|
|
2686
|
+
const asJson = /\bjson\b/.test(args);
|
|
2687
|
+
const prematureOnly = /\bpremature\b/.test(args);
|
|
2688
|
+
const projectMatch = args.match(/project=(\S+)/);
|
|
2689
|
+
let rollups: ProjectRollup[] = [];
|
|
2690
|
+
if (projectMatch) {
|
|
2691
|
+
const p = projectMatch[1]!.replace(/^~/, os.homedir());
|
|
2692
|
+
const r = rollupProject(p);
|
|
2693
|
+
if (!r) {
|
|
2694
|
+
ctx.ui.notify(`/glla stats: no .pi-glla/active.jsonl under ${p}`, "warning");
|
|
2695
|
+
return;
|
|
2696
|
+
}
|
|
2697
|
+
rollups = [r];
|
|
2698
|
+
} else {
|
|
2699
|
+
const projects = discoverGllaProjects({ cwd: ctx.cwd });
|
|
2700
|
+
for (const p of projects) {
|
|
2701
|
+
const r = rollupProject(p);
|
|
2702
|
+
if (r) rollups.push(r);
|
|
2703
|
+
}
|
|
2704
|
+
if (rollups.length === 0) {
|
|
2705
|
+
ctx.ui.notify("/glla stats: no projects with .pi-glla/active.jsonl found on this rig.", "info");
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
if (prematureOnly) rollups = filterPremature(rollups);
|
|
2710
|
+
const out = asJson ? formatRollupJson(rollups) : formatRollupTable(rollups);
|
|
2711
|
+
ctx.ui.notify(`glla stats — ${rollups.length} project(s)${prematureOnly ? " (premature filter)" : ""}\n${out}`, "info");
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2670
2714
|
async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
2671
2715
|
// The plugin's ONE config surface — global by default, rarely opened.
|
|
2672
2716
|
// /glla show effective values + where each comes from
|
|
@@ -2678,7 +2722,13 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
2678
2722
|
// /glla auditfeedbackchars=800 cap executor-visible disapproval report (0=full, the default)
|
|
2679
2723
|
// /glla project model=... write to PROJECT override (rare)
|
|
2680
2724
|
// /glla model=unset remove key (from global; project model=unset for project)
|
|
2725
|
+
// /glla stats [json|premature|project=<path>] per-project ledger rollups (v0.25.2)
|
|
2681
2726
|
const trimmed = args.trim();
|
|
2727
|
+
// v0.25.2: /glla stats sub-mode — cross-project telemetry rollups.
|
|
2728
|
+
if (/^stats\b/.test(trimmed)) {
|
|
2729
|
+
cmdStats(trimmed.slice("stats".length).trim(), ctx);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2682
2732
|
if (!trimmed) {
|
|
2683
2733
|
if (ctx.hasUI) {
|
|
2684
2734
|
await openSettingsUI(ctx);
|
|
@@ -2975,6 +3025,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2975
3025
|
["aggressivemode=", "on: keep-going defaults — autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs"],
|
|
2976
3026
|
["quotaretryminutes=", "N: minutes before auto-retrying a quota-exhausted auditor (default 60)"],
|
|
2977
3027
|
["stuckmax=", "N: consecutive stuck interventions before a loop stops (default 5)"],
|
|
3028
|
+
["stats", "per-project ledger rollups: /glla stats [json|premature|project=<path>]"],
|
|
2978
3029
|
["autoaccept=", "on: drafts activate without the Confirm dialog (unattended rigs)"],
|
|
2979
3030
|
["project", "write a project override: /glla project key=value"],
|
|
2980
3031
|
]),
|
|
@@ -3066,6 +3117,16 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3066
3117
|
loop.iterMetrics = metrics;
|
|
3067
3118
|
}
|
|
3068
3119
|
}
|
|
3120
|
+
// v0.25.2: per-goal tool telemetry (/glla stats premature detection).
|
|
3121
|
+
if (state.goal && state.goal.status === "active") {
|
|
3122
|
+
const toolName = String(event?.toolName ?? "");
|
|
3123
|
+
if (isLoopWriteTool(toolName) || toolName === "bash") {
|
|
3124
|
+
const t = state.goal.telemetry ?? { turns: 0, fileWrites: 0, bashCalls: 0 };
|
|
3125
|
+
if (isLoopWriteTool(toolName)) t.fileWrites++;
|
|
3126
|
+
if (toolName === "bash") t.bashCalls++;
|
|
3127
|
+
state.goal.telemetry = t;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3069
3130
|
if (draftingTarget === null) return;
|
|
3070
3131
|
if (askUserQuestionAnswered(String(event?.toolName ?? ""), event?.details)) {
|
|
3071
3132
|
draftingUserReplies++;
|
|
@@ -3184,6 +3245,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
3184
3245
|
// continuation loop.
|
|
3185
3246
|
if (isForeignCtx(ctx)) return;
|
|
3186
3247
|
noteActivity();
|
|
3248
|
+
// v0.25.2: per-goal turn telemetry (/glla stats).
|
|
3249
|
+
if (state.goal && state.goal.status === "active") {
|
|
3250
|
+
const t = state.goal.telemetry ?? { turns: 0, fileWrites: 0, bashCalls: 0 };
|
|
3251
|
+
t.turns++;
|
|
3252
|
+
state.goal.telemetry = t;
|
|
3253
|
+
}
|
|
3187
3254
|
if (!registeredCtx) {
|
|
3188
3255
|
registerAgentTools(pi, ctx);
|
|
3189
3256
|
registeredCtx = ctx;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.2",
|
|
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",
|