pi-goal-list-loop-audit 0.19.3 → 0.20.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.
|
@@ -37,6 +37,18 @@ function sinceIso(iso: string): number {
|
|
|
37
37
|
return Number.isFinite(t) ? Date.now() - t : 0;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// ---- semantic colors (optional; tests call without a theme → plain strings) ----
|
|
41
|
+
|
|
42
|
+
export type DisplayColor = "accent" | "success" | "warning" | "error" | "muted" | "dim";
|
|
43
|
+
export interface DisplayTheme {
|
|
44
|
+
fg(color: DisplayColor, text: string): string;
|
|
45
|
+
}
|
|
46
|
+
const paint = (theme: DisplayTheme | undefined, color: DisplayColor, text: string): string => (theme ? theme.fg(color, text) : text);
|
|
47
|
+
|
|
48
|
+
/** Pause reasons that mean "something broke", not "waiting on the user". */
|
|
49
|
+
const ERROR_PAUSE = /token limit|stalled|infra|auditor.*fail/i;
|
|
50
|
+
const pauseIsError = (g: Goal): boolean => ERROR_PAUSE.test(g.pauseReason ?? "");
|
|
51
|
+
|
|
40
52
|
// ---- status line (one-liner, always-on) ----
|
|
41
53
|
|
|
42
54
|
export interface AuditDisplayProgress {
|
|
@@ -49,25 +61,28 @@ export interface AuditDisplayProgress {
|
|
|
49
61
|
* One-line status for ctx.ui.setStatus("pi-glla", …).
|
|
50
62
|
* Returns undefined when nothing is being supervised (clears the segment).
|
|
51
63
|
*/
|
|
52
|
-
export function buildStatusText(state: State, audit?: AuditDisplayProgress | null, now = Date.now()): string | undefined {
|
|
64
|
+
export function buildStatusText(state: State, audit?: AuditDisplayProgress | null, now = Date.now(), theme?: DisplayTheme): string | undefined {
|
|
53
65
|
if (state.loop?.active) {
|
|
54
66
|
const l = state.loop;
|
|
55
|
-
const arrow = l.direction === "min" ? "↓" : "↑";
|
|
56
|
-
|
|
67
|
+
const arrow = paint(theme, "accent", l.direction === "min" ? "↓" : "↑");
|
|
68
|
+
const stallText = `stall ${l.stallCount}/${l.plateauWindow}`;
|
|
69
|
+
const stall = l.stallCount >= l.plateauWindow - 1 ? paint(theme, "warning", stallText) : stallText;
|
|
70
|
+
return `glla: loop ${arrow} iter ${l.iteration}/${l.maxIterations} · best ${l.bestValue ?? "n/a"} · ${stall}`;
|
|
57
71
|
}
|
|
58
72
|
const g = state.goal;
|
|
59
73
|
if (!g) return undefined;
|
|
60
74
|
if (g.status === "auditing") {
|
|
61
75
|
const tool = audit?.currentTool ? ` · ${audit.currentTool}` : "";
|
|
62
|
-
return `glla: auditing
|
|
76
|
+
return `glla: ${paint(theme, "accent", "auditing…")}${tool}`;
|
|
63
77
|
}
|
|
64
78
|
if (g.status === "paused") {
|
|
65
|
-
|
|
79
|
+
const label = `paused ⏸ ${truncate(g.pauseReason ?? "", 40)}`;
|
|
80
|
+
return `glla: ${paint(theme, pauseIsError(g) ? "error" : "warning", label)}`;
|
|
66
81
|
}
|
|
67
82
|
if (g.status === "active") {
|
|
68
83
|
const queue = state.list?.length ? ` · list ${state.list.length}` : "";
|
|
69
84
|
const tasks = g.taskList ? ` ${countDone(g)}/${countTotal(g)} tasks ·` : "";
|
|
70
|
-
return `glla: ${g.policy}
|
|
85
|
+
return `glla: ${g.policy} ${paint(theme, "success", "●")}${tasks} ${fmtElapsed(now - Date.parse(g.createdAt))}${queue}`;
|
|
71
86
|
}
|
|
72
87
|
return undefined; // complete/aborted → clear
|
|
73
88
|
}
|
|
@@ -102,52 +117,60 @@ function countTotal(g: Goal): number {
|
|
|
102
117
|
* Widget lines for ctx.ui.setWidget("pi-glla", lines).
|
|
103
118
|
* Returns undefined when nothing is worth showing.
|
|
104
119
|
*/
|
|
105
|
-
export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | null, now = Date.now()): string[] | undefined {
|
|
106
|
-
if (state.loop?.active) return loopLines(state.loop, now);
|
|
120
|
+
export function buildWidgetLines(state: State, audit?: AuditDisplayProgress | null, now = Date.now(), theme?: DisplayTheme): string[] | undefined {
|
|
121
|
+
if (state.loop?.active) return loopLines(state.loop, now, theme);
|
|
107
122
|
const g = state.goal;
|
|
108
123
|
if (!g) return undefined;
|
|
109
124
|
if (g.status === "complete" || g.status === "aborted") return undefined;
|
|
110
|
-
return goalLines(g, state, audit, now);
|
|
125
|
+
return goalLines(g, state, audit, now, theme);
|
|
111
126
|
}
|
|
112
127
|
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
// Branch lines indent one space (pi-tasks convention): the tree sits under
|
|
129
|
+
// the head glyph, text column consistent across widgets.
|
|
130
|
+
function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | undefined, now: number, theme?: DisplayTheme): string[] {
|
|
131
|
+
const icon =
|
|
132
|
+
g.status === "paused"
|
|
133
|
+
? paint(theme, pauseIsError(g) ? "error" : "warning", "⏸")
|
|
134
|
+
: g.status === "auditing"
|
|
135
|
+
? paint(theme, "accent", "⟡")
|
|
136
|
+
: paint(theme, "success", "◆");
|
|
115
137
|
const head = `${icon} ${truncate(g.objective.replace(/\s+/g, " "), 64)}`;
|
|
116
|
-
const
|
|
138
|
+
const statusWord = g.status === "active" ? paint(theme, "success", "active") : g.status;
|
|
139
|
+
const tokens = paint(theme, "dim", `${fmtTokens(g.usage?.tokensUsed ?? 0)}/${fmtTokens(g.usage?.tokensLimit ?? 10_000_000)} tok`);
|
|
140
|
+
const lines = [head, ` ├─ ${statusWord} · ${fmtElapsed(now - Date.parse(g.createdAt))} · ${tokens}`];
|
|
117
141
|
if (g.status === "auditing") {
|
|
118
|
-
lines.push(
|
|
119
|
-
if (audit?.elapsedMs) lines.push(
|
|
120
|
-
else lines.push(
|
|
142
|
+
lines.push(` ├─ auditor: ${audit?.label ?? "running"}${audit?.currentTool ? ` · ${truncate(audit.currentTool, 30)}` : ""}`);
|
|
143
|
+
if (audit?.elapsedMs) lines.push(` └─ ${paint(theme, "dim", `${fmtElapsed(audit.elapsedMs)} in isolated session`)}`);
|
|
144
|
+
else lines.push(` └─ ${paint(theme, "dim", "isolated session, read-only tools")}`);
|
|
121
145
|
return lines;
|
|
122
146
|
}
|
|
123
147
|
if (g.status === "paused" && g.pauseReason) {
|
|
124
|
-
lines.push(
|
|
125
|
-
if (g.pauseSuggestedAction) lines.push(
|
|
148
|
+
lines.push(` ├─ ${paint(theme, pauseIsError(g) ? "error" : "warning", truncate(g.pauseReason, 60))}`);
|
|
149
|
+
if (g.pauseSuggestedAction) lines.push(` └─ ${paint(theme, "dim", truncate(g.pauseSuggestedAction, 60))}`);
|
|
126
150
|
return lines;
|
|
127
151
|
}
|
|
128
152
|
const next = nextPending(g);
|
|
129
|
-
if (next) lines.push(
|
|
153
|
+
if (next) lines.push(` ├─ next: ${truncate(next, 56)}`);
|
|
130
154
|
const queue = state.list?.length ?? 0;
|
|
131
|
-
lines.push(
|
|
155
|
+
lines.push(` └─ ${paint(theme, "dim", `${queue > 0 ? `list ${queue} · ` : ""}/goal status · /glla`)}`);
|
|
132
156
|
return lines;
|
|
133
157
|
}
|
|
134
158
|
|
|
135
|
-
function loopLines(l: LoopState, now: number): string[] {
|
|
136
|
-
const arrow = l.direction === "min" ? "↓" : "↑";
|
|
159
|
+
function loopLines(l: LoopState, now: number, theme?: DisplayTheme): string[] {
|
|
160
|
+
const arrow = paint(theme, "accent", l.direction === "min" ? "↓" : "↑");
|
|
161
|
+
const best = paint(theme, "success", `${l.bestValue ?? "n/a"}`);
|
|
162
|
+
const stallText = `stall ${l.stallCount}/${l.plateauWindow}`;
|
|
163
|
+
const stall = l.stallCount >= l.plateauWindow - 1 ? paint(theme, "warning", stallText) : stallText;
|
|
137
164
|
const lines = [
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
165
|
+
`${paint(theme, "accent", "◆")} ${truncate(l.target, 64)}`,
|
|
166
|
+
` ├─ loop ${arrow} iter ${l.iteration}/${l.maxIterations} · ${fmtElapsed(now - Date.parse(l.startedAt))}`,
|
|
167
|
+
` ├─ best ${best} · last ${l.lastValue ?? "n/a"} · ${stall}`,
|
|
168
|
+
` └─ ${paint(theme, "dim", truncate(l.measureCmd, 56))}`,
|
|
142
169
|
];
|
|
143
|
-
if (l.branchName) lines.push(`
|
|
170
|
+
if (l.branchName) lines.push(` ⎇ ${paint(theme, "muted", truncate(l.branchName, 50))}`);
|
|
144
171
|
return lines;
|
|
145
172
|
}
|
|
146
173
|
|
|
147
|
-
function statusLabel(s: string): string {
|
|
148
|
-
return s === "active" ? "active" : s;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
174
|
function nextPending(g: Goal): string | undefined {
|
|
152
175
|
const tasks = g.taskList?.tasks ?? [];
|
|
153
176
|
const queue = [...tasks];
|
|
@@ -60,7 +60,7 @@ export interface LoopState {
|
|
|
60
60
|
export function loopBranchName(startedAtIso: string, target: string): string {
|
|
61
61
|
const stamp = startedAtIso.replace(/[^0-9]/g, "").slice(0, 14);
|
|
62
62
|
const slug = target.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 30) || "loop";
|
|
63
|
-
return `pi-
|
|
63
|
+
return `pi-glla-loop/${stamp}-${slug}`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export const LOOP_DEFAULTS = {
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -145,8 +145,9 @@ let uiTicker: NodeJS.Timeout | null = null;
|
|
|
145
145
|
function refreshUI(ctx: ExtensionContext): void {
|
|
146
146
|
if (!ctx.hasUI) return;
|
|
147
147
|
try {
|
|
148
|
-
ctx.ui.
|
|
149
|
-
ctx.ui.
|
|
148
|
+
const theme = ctx.ui.theme as unknown as import("../goal-loop-display.js").DisplayTheme | undefined;
|
|
149
|
+
ctx.ui.setStatus("pi-glla", buildStatusText(state, latestAuditProgress, Date.now(), theme));
|
|
150
|
+
ctx.ui.setWidget("pi-glla", buildWidgetLines(state, latestAuditProgress, Date.now(), theme));
|
|
150
151
|
} catch {
|
|
151
152
|
// stale ctx — next event refreshes
|
|
152
153
|
}
|
|
@@ -1018,7 +1019,7 @@ async function runLoopTick(ctx: ExtensionContext, event?: any): Promise<void> {
|
|
|
1018
1019
|
if (loop.branchName && outcome.kind === "continue") {
|
|
1019
1020
|
if (outcome.improved) {
|
|
1020
1021
|
await runGit(ctx, ["add", "-A"]);
|
|
1021
|
-
const committed = await runGit(ctx, ["commit", "-m", `pi-
|
|
1022
|
+
const committed = await runGit(ctx, ["commit", "-m", `pi-glla-loop: iteration ${loop.iteration} (${loop.direction}=${loop.bestValue})`]);
|
|
1022
1023
|
appendLedger(ctx.cwd, "loop_git", { action: "commit", iteration: loop.iteration, ok: committed.ok });
|
|
1023
1024
|
} else {
|
|
1024
1025
|
const reset = await runGit(ctx, ["reset", "--hard", "HEAD"]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
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",
|