pi-goal-list-loop-audit 0.23.5 → 0.23.6

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
@@ -58,10 +58,11 @@ matches `/list show`.
58
58
  /list remove <n> # drop item n from the queue
59
59
  /list clear # empty the queue
60
60
  /loop # draft the loop (agent grills; measure is test-run before you confirm)
61
+ /loop start "keep polishing the UI" # infinite metricless loop (v0.23.6): no plateau, no cap — ends at time=/tokens= or /loop stop
61
62
  /loop start "reduce TODOs" measure="grep -c TODO src.txt | head -1" direction=min
62
63
  /loop start "shrink the bundle" measure="..." direction=min time=4 tokens=500000 # arbitrary bounds
63
64
  /loop start "reduce TODOs" measure="..." direction=min branch=1 # scratch-branch mode
64
- /loop start "keep improving SPEC.md" measure=none max=20 # metricless spec loop (v0.23.0)
65
+ /loop start "keep improving SPEC.md" measure=none max=20 # metricless with an explicit cap (v0.23.0)
65
66
  /loop status # iteration, best, stall, recent values
66
67
  /loop stop # halt with summary
67
68
  ```
@@ -237,12 +237,17 @@ export function parseLoopStartArgs(raw: string): {
237
237
  const measureRaw = (kv.get("measure") ?? "").trim();
238
238
  // v0.23.0: measure=none → metricless "spec loop" (Sisyphus mode). No
239
239
  // metric, no direction, no plateau — bounds and /loop stop only.
240
- const metricless = measureRaw.toLowerCase() === "none";
241
- if (!measureRaw) throw new Error('missing measure="<shell command that prints a number>" (or measure=none for a metricless spec loop no plateau, ends only at bounds or /loop stop)');
240
+ // v0.23.6: a bare `/loop start "<target>"` IS the infinite command —
241
+ // no measure= means metricless too. The Confirm dialog names "NO
242
+ // plateau · NO iteration cap · /loop stop" before anything runs, so
243
+ // the choice is never silent (the v0.23.0 rule). Metric loops keep
244
+ // the 50-iteration default cap; metricless loops default to UNBOUNDED
245
+ // (max=0) unless max= is given explicitly.
246
+ const metricless = !measureRaw || measureRaw.toLowerCase() === "none";
242
247
  const dirRaw = (kv.get("direction") ?? "").toLowerCase();
243
- if (metricless && dirRaw) throw new Error("direction= is meaningless with measure=nonethere is no metric to have a direction");
244
- if (!metricless && dirRaw !== "min" && dirRaw !== "max") throw new Error("missing direction=min|max");
245
- if (!target) throw new Error("missing target (what to improve), e.g. /loop start \"reduce test failures\" measure=\"...\" direction=min");
248
+ if (metricless && dirRaw) throw new Error("direction= is meaningless without a metric add measure=\"<cmd>\" or drop direction=");
249
+ if (!metricless && dirRaw !== "min" && dirRaw !== "max") throw new Error("missing direction=min|max (a metric loop needs to know which way is better; a bare /loop start \"<target>\" with no measure= is the infinite metricless form)");
250
+ if (!target) throw new Error("missing target (what to improve), e.g. /loop start \"keep polishing the UI\" — bare start is metricless + unbounded; add measure=\"<cmd>\" direction=min|max for a metric loop");
246
251
 
247
252
  const window = Number.parseInt(kv.get("window") ?? "", 10);
248
253
  const max = Number.parseInt(kv.get("max") ?? "", 10);
@@ -263,8 +268,11 @@ export function parseLoopStartArgs(raw: string): {
263
268
  measureCmd: metricless ? "" : measureRaw,
264
269
  direction: metricless ? undefined : dirRaw as LoopDirection,
265
270
  plateauWindow: Number.isFinite(window) && window > 0 ? window : LOOP_DEFAULTS.plateauWindow,
266
- // v0.23.0: max=0 = truly unbounded (no iteration cap); absent = 50.
267
- maxIterations: kv.has("max") ? (Number.isFinite(max) && max >= 0 ? max : LOOP_DEFAULTS.maxIterations) : LOOP_DEFAULTS.maxIterations,
271
+ // v0.23.0: max=0 = truly unbounded (no iteration cap).
272
+ // v0.23.6: metricless with no explicit max= defaults to UNBOUNDED
273
+ // an infinite loop is the point of the bare form. Metric loops keep
274
+ // the 50-cap default.
275
+ maxIterations: kv.has("max") ? (Number.isFinite(max) && max >= 0 ? max : LOOP_DEFAULTS.maxIterations) : metricless ? 0 : LOOP_DEFAULTS.maxIterations,
268
276
  branch: branchRaw === "1" || branchRaw === "true" || branchRaw === "yes",
269
277
  force: forceRaw === "1" || forceRaw === "true" || forceRaw === "yes",
270
278
  timeLimitHours: Number.isFinite(timeRaw) && timeRaw > 0 ? timeRaw : undefined,
@@ -467,7 +467,7 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
467
467
  target === "list"
468
468
  ? `${label}: the objective has no "Done when:" clause — the agent will grill you about it first (nothing activates until you confirm). Add directly instead: include a "Done when:" clause.`
469
469
  : target === "loop"
470
- ? `${label}: a loop target needs a metric and a direction — the agent will help you design them first (nothing activates until you confirm). Skip the interview entirely: /loop start "<target>" measure="<cmd>" direction=min|max [window=5] [max=50] [time=h] [tokens=n] [branch=1].`
470
+ ? `${label}: a loop target needs a metric and a direction — the agent will help you design them first (nothing activates until you confirm). Skip the interview entirely: /loop start "<target>" (bare = infinite metricless) or /loop start "<target>" measure="<cmd>" direction=min|max [window=5] [max=50] [time=h] [tokens=n] [branch=1].`
471
471
  : `${label}: the objective has no "Done when:" clause — the agent will grill you about it first (nothing activates until you confirm). Skip the interview entirely: /goal start <objective>.`;
472
472
  ctx.ui.notify(
473
473
  seed
@@ -1258,7 +1258,7 @@ async function cmdLoop(args: string, ctx: ExtensionContext): Promise<void> {
1258
1258
  if (sub === "status") {
1259
1259
  const loop = state.loop;
1260
1260
  if (!loop) {
1261
- ctx.ui.notify("No loop. /loop to draft one, or /loop start \"<target>\" measure=\"<cmd>\" direction=min|max [window=5] [max=50] [time=<hours>] [tokens=<budget>]", "info");
1261
+ ctx.ui.notify("No loop. /loop to draft one, /loop start \"<target>\" for an infinite metricless loop, or add measure=\"<cmd>\" direction=min|max for a metric loop [window=5] [max=50] [time=<hours>] [tokens=<budget>]", "info");
1262
1262
  return;
1263
1263
  }
1264
1264
  const lines = [
@@ -2433,7 +2433,7 @@ export default function (pi: ExtensionAPI): void {
2433
2433
  handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdList(args, ctx); },
2434
2434
  });
2435
2435
  pi.registerCommand("loop", {
2436
- description: "Loop 3: metric-driven process — it never completes. /loop <target> drafts the metric with you · /loop start \"<target>\" measure=\"<cmd>\" direction=min|max [window=5] [max=50] [time=<hours>] [tokens=<budget>] [branch=1] skips drafting · measure=none = metricless spec loop (no plateau; max=0 = unbounded) · /loop status · /loop stop. 'Improve until X' is a /goal, not a loop.",
2436
+ description: "Loop 3: metric-driven process — it never completes. /loop <target> drafts the metric with you · /loop start \"<target>\" = infinite metricless loop (no plateau, no cap; ends at time=/tokens= or /loop stop) · add measure=\"<cmd>\" direction=min|max [window=5] [max=50] [branch=1] for a metric loop · /loop status · /loop stop. 'Improve until X' is a /goal, not a loop.",
2437
2437
  getArgumentCompletions: completions([
2438
2438
  ["start", "skip drafting: /loop start \"<target>\" measure=\"<cmd>\" direction=min|max [window=5] [max=50]"],
2439
2439
  ["status", "show metric, iteration, best/last values, stall count"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.23.5",
3
+ "version": "0.23.6",
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",