pi-goal-list-loop-audit 0.28.32 → 0.28.34

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 CHANGED
@@ -201,7 +201,7 @@ No external watchdog plugin needed.
201
201
  /glla # open the settings UI
202
202
  /glla model=provider/id # auditor model override → GLOBAL
203
203
  /glla thinking=high # auditor thinking → GLOBAL
204
- /glla notify='cmd "$1"' # push on complete/pause/stop → GLOBAL
204
+ /glla notify='cmd "$1"' # custom push cmd · unset = auto-detect (notify-send/osascript) · off = silent → GLOBAL
205
205
  /glla tokenlimit=10000000 # per-goal token budget (default: off) → GLOBAL
206
206
  /glla tokenlimit=0 # explicitly no cap (the default)
207
207
  /glla wedgealert=30 # hung-command alert minutes (default: 30, 0 = off)
@@ -233,15 +233,14 @@ activates the moment the agent proposes it, with a notification and a
233
233
  `draft_autoaccepted` ledger entry (auto-accept is never silent). The seed
234
234
  carries the intent. Pair with `autoresume=on` for fully unattended rigs.
235
235
 
236
- ## Subagents (`@tintinweb/pi-subagents`)
236
+ ## Subagents
237
237
 
238
- Subagent sessions bind extensions too, so glla loads there — by design the
239
- **main session owns the goal/loop/list; subagents are workers** (v0.23.8):
238
+ glla's guarantees here come from glla itself (session-handle
239
+ discrimination), not from any specific subagent plugin any Agent-tool
240
+ provider gets them. Subagent sessions bind extensions too, so glla loads
241
+ there — by design the **main session owns the goal/loop/list; subagents
242
+ are workers** (v0.23.8):
240
243
 
241
- - Read-only agents (Explore, Plan) get no glla tools (pi-subagents gates
242
- them); general-purpose agents see them but state-mutating calls
243
- (`complete_goal`, `propose_*`, `list_add`, `pause_goal`, …) are refused
244
- with "report back to the main agent".
245
244
  - A subagent session never clobbers the loop's session handle, never runs
246
245
  the restore gate, and never drives continuation — so the heartbeat,
247
246
  wedge alert, and auto-resume machinery always act on the main session.
@@ -249,6 +248,11 @@ Subagent sessions bind extensions too, so glla loads there — by design the
249
248
  is the discriminator.)
250
249
  - Subagent tool activity counts as activity for the wedge clock — a long
251
250
  subagent run is work, not a hang.
251
+ - With `@tintinweb/pi-subagents` specifically (the one we test against):
252
+ read-only agents (Explore, Plan) get no glla tools; general-purpose
253
+ agents see them but state-mutating calls (`complete_goal`, `propose_*`,
254
+ `list_add`, `pause_goal`, …) are refused with "report back to the main
255
+ agent".
252
256
 
253
257
  ## Token guard
254
258
 
@@ -287,17 +291,25 @@ contradictory turns. One driver at a time:
287
291
  fine to keep, never run a ralph loop while a goal/list/loop is active.
288
292
 
289
293
  **Goes well with it**: `@juicesharp/rpiv-ask-user-question` (drafting uses its
290
- structured forms), `@tintinweb/pi-subagents` (spawn research/review subagents
291
- inside goal work), `@tintinweb/pi-tasks` (session-wide DAGs vs our goal-scoped
292
- task lists different granularity), `pi-chrome` (the research/search path for
293
- goals logged-in browsing with no extra services; standalone search skills
294
- like `mmx-cli`/`pi-search-skill` are optional conveniences for bulk queries,
295
- not requirements).
294
+ structured forms), any subagent provider — e.g. `@tintinweb/pi-subagents`
295
+ (spawn research/review subagents inside goal work), `pi-chrome` (the
296
+ research/search path for goals logged-in browsing with no extra services;
297
+ standalone search skills like `mmx-cli`/`pi-search-skill` are optional
298
+ conveniences for bulk queries, not requirements).
299
+
300
+ **Overlaps — pick one**: `@tintinweb/pi-tasks` is a second task list next to
301
+ `/list`, and in practice the glla list *is* the task list (queue, statuses,
302
+ per-item audit trail) while the todos end up the weaker copy. We ran both
303
+ and removed pi-tasks. If you truly need session-wide dependency DAGs beyond
304
+ one ordered queue, it exists — but installing both is not the ideal combo.
296
305
 
297
306
  **Two footnotes**: (1) extension-registered providers work in the main session
298
307
  but not the auditor's extension-less session — if audits fail auth, set the
299
- override once with `/glla model=`. (2) `pi-notify-agent` notifies on every turn;
300
- `/glla notify=` fires only on goal complete/pause/loop stop.
308
+ override once with `/glla model=`. (2) `pi-notify-agent` notifies on every
309
+ turn; glla pushes fire only where there is something to DO (pauses, verdicts,
310
+ storms, wedge) and work out of the box — with no `notify=` configured glla
311
+ auto-detects `notify-send`/`osascript`; `notify=off` silences, `notify='<cmd>'`
312
+ customizes.
301
313
 
302
314
  ## Files
303
315
 
@@ -1932,15 +1932,44 @@ function addSingleItem(ctx: ExtensionContext, raw: string): void {
1932
1932
  }
1933
1933
 
1934
1934
  /**
1935
- * Config-gated push notification: if settings.notifyCmd is set, shell out
1936
- * with the message as $1. Fire-and-forget a broken notify command never
1937
- * blocks the loop. /glla notify='<cmd>' to configure.
1935
+ * Push notification, folded IN by default (v0.28.34 user: "leaving it to
1936
+ * the user to set up sucks, cause then they won't have it"). Resolution:
1937
+ * notifyCmd === "off" → silent (explicit opt-out)
1938
+ * notifyCmd set → that command, message passed as $1
1939
+ * notifyCmd unset → auto-detect ONCE per session: notify-send
1940
+ * (Linux) or osascript (macOS); none → silent.
1941
+ * Pushes fire only where there is something to DO — pauses, auditor
1942
+ * verdicts, storms, wedge, persistence degradation — never per-turn noise.
1943
+ * Fire-and-forget: a broken notifier never blocks the loop.
1938
1944
  */
1945
+ let autoNotifyCmd: string | null | undefined; // undefined = not probed yet
1946
+
1947
+ function probeAutoNotify(ctx: ExtensionContext): void {
1948
+ if (autoNotifyCmd !== undefined || !extensionApi) return;
1949
+ autoNotifyCmd = null; // probing sentinel — drops at most the first push
1950
+ void extensionApi
1951
+ .exec("bash", ["-c", "command -v notify-send || command -v osascript || true"], { cwd: ctx.cwd })
1952
+ .then((r) => {
1953
+ const found = String((r as { stdout?: string }).stdout ?? "").trim();
1954
+ if (found.endsWith("notify-send")) autoNotifyCmd = `notify-send "pi-goal-list-loop-audit" "$1"`;
1955
+ // env-var handoff: the message never touches AppleScript quoting.
1956
+ else if (found.endsWith("osascript")) autoNotifyCmd = `GLLA_MSG="$1" osascript -e 'display notification (system attribute "GLLA_MSG") with title "pi-goal-list-loop-audit"'`;
1957
+ else autoNotifyCmd = null;
1958
+ })
1959
+ .catch(() => {
1960
+ autoNotifyCmd = null;
1961
+ });
1962
+ }
1963
+
1939
1964
  function notifyExternal(ctx: ExtensionContext, message: string): void {
1940
1965
  try {
1941
1966
  const settings = loadSettings(ctx.cwd);
1942
- const cmd = settings.notifyCmd;
1943
- if (!cmd || !extensionApi) return;
1967
+ if (settings.notifyCmd === "off" || !extensionApi) return;
1968
+ const cmd = settings.notifyCmd ?? autoNotifyCmd;
1969
+ if (!cmd) {
1970
+ if (settings.notifyCmd === undefined && autoNotifyCmd === undefined) probeAutoNotify(ctx);
1971
+ return;
1972
+ }
1944
1973
  void extensionApi.exec("bash", ["-c", cmd, "pi-goal-list-loop-audit", message], { cwd: ctx.cwd }).catch(() => {});
1945
1974
  } catch {
1946
1975
  // non-fatal by design
@@ -3892,7 +3921,7 @@ export async function handleSettingChoice(id: string, ctx: ExtensionContext): Pr
3892
3921
  // current effective subagent models. Treat as no-op.
3893
3922
  return;
3894
3923
  case "notifyCmd": {
3895
- const v = await ctx.ui.input("Notify command — the event message is passed as $1", "e.g. a desktop-notification or push command; empty = off");
3924
+ const v = await ctx.ui.input("Notify command — the event message is passed as $1", "custom command · empty = auto-detect (notify-send/osascript) · 'off' = silent");
3896
3925
  if (v !== undefined) saveSettings("global", ctx.cwd, { notifyCmd: v.trim() || undefined });
3897
3926
  return;
3898
3927
  }
@@ -4186,7 +4215,7 @@ function cmdLog(args: string, ctx: ExtensionContext): void {
4186
4215
  }
4187
4216
 
4188
4217
  /**
4189
- * v0.28.31: /glla reset — ONE confirmed command that leaves a project with
4218
+ * v0.28.31 (renamed v0.28.33): /glla wipe — ONE confirmed command that leaves a project with
4190
4219
  * zero live glla state. User directive: "make sure we only have one goal or
4191
4220
  * loop or list at a time — many of my older projects have leftovers" (the
4192
4221
  * fleet scan found queued lists up to 56 deep, held loops at iter 50, and
@@ -4195,7 +4224,7 @@ function cmdLog(args: string, ctx: ExtensionContext): void {
4195
4224
  * the list is cleared, the loop record is wiped after a graceful stop.
4196
4225
  * History stays in .pi-glla; only the live state goes.
4197
4226
  */
4198
- async function cmdGllaReset(ctx: ExtensionContext): Promise<void> {
4227
+ async function cmdGllaWipe(ctx: ExtensionContext): Promise<void> {
4199
4228
  const g = state.goal;
4200
4229
  const live = g && (g.status === "active" || g.status === "paused" || g.status === "auditing");
4201
4230
  const n = listQueue().length;
@@ -4211,36 +4240,36 @@ async function cmdGllaReset(ctx: ExtensionContext): Promise<void> {
4211
4240
  if (loop) parts.push(`loop ${loop.active ? "stopped" : "cleared"} (iter ${loop.iteration}${loop.bestValue !== null && loop.bestValue !== undefined ? `, best ${loop.bestValue}` : ""})`);
4212
4241
  if (ctx.hasUI) {
4213
4242
  try {
4214
- const ok = await ctx.ui.confirm("Reset glla state?", `${parts.map((p) => ` ${p}`).join("\n")}\n\nHistory stays in .pi-glla (archive + ledger); the live state is wiped.`);
4243
+ const ok = await ctx.ui.confirm("Wipe glla state?", `${parts.map((p) => ` ${p}`).join("\n")}\n\nHistory stays in .pi-glla (archive + ledger); the live state is wiped.`);
4215
4244
  if (!ok) {
4216
- ctx.ui.notify("Reset cancelled.", "info");
4245
+ ctx.ui.notify("Wipe cancelled.", "info");
4217
4246
  return;
4218
4247
  }
4219
4248
  } catch {
4220
- ctx.ui.notify("Reset cancelled.", "info");
4249
+ ctx.ui.notify("Wipe cancelled.", "info");
4221
4250
  return;
4222
4251
  }
4223
4252
  }
4224
- appendLedger(ctx.cwd, "glla_reset", { goalId: live ? g!.id : undefined, listCleared: n, loop: loop ? { iteration: loop.iteration, active: loop.active } : undefined });
4253
+ appendLedger(ctx.cwd, "glla_wipe", { goalId: live ? g!.id : undefined, listCleared: n, loop: loop ? { iteration: loop.iteration, active: loop.active } : undefined });
4225
4254
  if (live) {
4226
- archiveCurrentGoal(ctx, "aborted", "user reset (/glla reset)");
4255
+ archiveCurrentGoal(ctx, "aborted", "user wipe (/glla wipe)");
4227
4256
  ctx.abort();
4228
4257
  } else if (g) {
4229
4258
  state = { ...state, goal: null };
4230
4259
  }
4231
4260
  if (n > 0) {
4232
4261
  state = { ...state, list: [] };
4233
- appendLedger(ctx.cwd, "list_cleared", { via: "glla_reset" });
4262
+ appendLedger(ctx.cwd, "list_cleared", { via: "glla_wipe" });
4234
4263
  }
4235
4264
  if (loop) {
4236
4265
  clearLoopTimer();
4237
4266
  state.loop = undefined;
4238
4267
  await finishLoopGit(ctx, loop);
4239
- appendLedger(ctx.cwd, "loop_stopped", { reason: "user reset (/glla reset)", iterations: loop.iteration, best: loop.bestValue });
4268
+ appendLedger(ctx.cwd, "loop_stopped", { reason: "user wipe (/glla wipe)", iterations: loop.iteration, best: loop.bestValue });
4240
4269
  }
4241
4270
  persistState(ctx);
4242
- ctx.ui.notify(`glla reset done: ${parts.join(" · ")}. Clean slate.`, "info");
4243
- notifyExternal(ctx, "glla state reset by user — clean slate.");
4271
+ ctx.ui.notify(`glla wipe done: ${parts.join(" · ")}. Clean slate.`, "info");
4272
+ notifyExternal(ctx, "glla state wiped by user — clean slate.");
4244
4273
  }
4245
4274
 
4246
4275
  /**
@@ -4297,7 +4326,7 @@ async function cmdGllaResume(ctx: ExtensionContext): Promise<void> {
4297
4326
  * list item is archived as aborted (its queue is untouched), an active or
4298
4327
  * held loop is stopped. Same outcome shape regardless of hidden type —
4299
4328
  * the user's caveat ("this sucks if one command doesn't work for others")
4300
- * is why /list cancel (item + drop queue) and /glla reset (nuke all)
4329
+ * is why /list cancel (item + drop queue) and /glla wipe (nuke all)
4301
4330
  * remain the power verbs instead of being folded in.
4302
4331
  */
4303
4332
  async function cmdGllaCancel(ctx: ExtensionContext): Promise<void> {
@@ -4310,7 +4339,7 @@ async function cmdGllaCancel(ctx: ExtensionContext): Promise<void> {
4310
4339
  await cmdLoop("stop", ctx);
4311
4340
  return;
4312
4341
  }
4313
- ctx.ui.notify("Nothing to cancel — no active/paused goal/list-item, no loop. Queued list items: /list clear; everything: /glla reset.", "info");
4342
+ ctx.ui.notify("Nothing to cancel — no active/paused goal/list-item, no loop. Queued list items: /list clear; everything: /glla wipe.", "info");
4314
4343
  }
4315
4344
 
4316
4345
  function cmdAudits(args: string, ctx: ExtensionContext): void {
@@ -4370,9 +4399,15 @@ async function cmdSettings(args: string, ctx: ExtensionContext): Promise<void> {
4370
4399
  cmdLog(trimmed.slice("log".length).trim(), ctx);
4371
4400
  return;
4372
4401
  }
4373
- // v0.28.31: /glla reset — one-shot clean slate for leftover-laden projects.
4402
+ // v0.28.33: renamed reset → wipe "reset" sat at edit-distance 2 from
4403
+ // "resume" in the same namespace, and it's the destructive one (user
4404
+ // catch, same day it shipped, before any muscle memory formed).
4405
+ if (/^wipe\b/.test(trimmed)) {
4406
+ await cmdGllaWipe(ctx);
4407
+ return;
4408
+ }
4374
4409
  if (/^reset\b/.test(trimmed)) {
4375
- await cmdGllaReset(ctx);
4410
+ ctx.ui.notify("/glla reset is now /glla wipe (renamed — too close to /glla resume). Nothing was done.", "info");
4376
4411
  return;
4377
4412
  }
4378
4413
  // v0.28.32: /glla resume + /glla cancel — type-blind verbs over the ONE
@@ -4776,9 +4811,9 @@ export default function (pi: ExtensionAPI): void {
4776
4811
  ["decisionpopup=", "on|off: decision pauses pop the select() picker (default on; the widget card always lists the options, /goal decide reopens the picker)"],
4777
4812
  ["auditcap=", "N: pause goal after N consecutive auditor disapprovals (default 5, 0 = unlimited)"],
4778
4813
  ["log", "event-trail tail: /glla log [N] — who created/resumed/paused what, from where (v0.28.28)"],
4779
- ["reset", "wipe live glla state (goal archived, list cleared, loop stopped) — one-shot cleanup for leftover-laden projects"],
4814
+ ["wipe", "WIPE live glla state (goal archived, list cleared, loop stopped) — one-shot cleanup for leftover-laden projects"],
4780
4815
  ["resume", "resume WHATEVER is paused/held (goal, list item, or held loop) — no need to know the type"],
4781
- ["cancel", "cancel the ONE live thing uniformly (goal/list item archived, loop stopped) — queue untouched; /list clear or /glla reset for more"],
4816
+ ["cancel", "cancel the ONE live thing uniformly (goal/list item archived, loop stopped) — queue untouched; /list clear or /glla wipe for more"],
4782
4817
  ["auditfeedbackchars=", "cap on executor-visible disapproval report chars (0 = full report, the default)"],
4783
4818
  ["aggressivemode=", "on: keep-going defaults — autoResume, cap 10, stuck 10, wedge off, quota auto-retry, cap→TODOs"],
4784
4819
  ["quotaretryminutes=", "N: minutes before auto-retrying a quota-exhausted auditor (default 60)"],
@@ -323,9 +323,9 @@ export function buildSettingsRows(
323
323
  id: "notifyCmd",
324
324
  section: "other",
325
325
  label: "Notify command",
326
- valueText: show("notifyCmd", "off"),
326
+ valueText: show("notifyCmd", "auto"),
327
327
  sourceText: src("notifyCmd"),
328
- description: "desktop push command; the event message is passed as $1",
328
+ description: "custom command ($1 = message) · unset = auto-detect notify-send/osascript · 'off' = silent",
329
329
  },
330
330
  {
331
331
  id: "tokenLimit",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.28.32",
3
+ "version": "0.28.34",
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",