pi-goal-list-loop-audit 0.24.3 → 0.24.4

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
@@ -60,7 +60,7 @@ matches `/list show`.
60
60
  /list cancel # stop the whole list: abort the active item + drop all waiting
61
61
  /loop # draft the loop (agent grills; measure is test-run before you confirm)
62
62
  /loop start "keep polishing the UI" # infinite metricless loop (v0.23.6): no plateau, no cap — ends at time=/tokens= or /loop stop
63
- /loop respec # infinite metricless loop reconciling the codebase against the root SPEC.md / spec.md (v0.24.3)
63
+ /loop respec # infinite metricless loop reconciling the codebase against the root SPEC.md / spec.md (v0.24.3) — 2 specs = you pick, 0 specs = drafting, 1 spec = auto-start (v0.24.4)
64
64
  /loop start "reduce TODOs" measure="grep -c TODO src.txt | head -1" direction=min
65
65
  /loop start "shrink the bundle" measure="..." direction=min time=4 tokens=500000 # arbitrary bounds
66
66
  /loop start "reduce TODOs" measure="..." direction=min branch=1 # scratch-branch mode
@@ -302,15 +302,21 @@ export function parseLoopStartArgs(raw: string): {
302
302
  /** Root-only spec candidates, in priority order. No fuzzy search. */
303
303
  export const RESPEC_SPEC_CANDIDATES = ["SPEC.md", "spec.md"] as const;
304
304
 
305
- /** Resolve the project spec in the root only; null when absent. */
306
- export function resolveSpecFile(cwd: string): string | null {
305
+ /** Resolve every root spec candidate that exists (priority order). */
306
+ export function resolveSpecFiles(cwd: string): string[] {
307
+ const found: string[] = [];
307
308
  for (const name of RESPEC_SPEC_CANDIDATES) {
308
309
  const p = join(cwd, name);
309
310
  try {
310
- if (existsSync(p) && statSync(p).isFile()) return p;
311
+ if (existsSync(p) && statSync(p).isFile()) found.push(p);
311
312
  } catch { /* unreadable — keep looking */ }
312
313
  }
313
- return null;
314
+ return found;
315
+ }
316
+
317
+ /** Resolve the project spec in the root only; null when absent. */
318
+ export function resolveSpecFile(cwd: string): string | null {
319
+ return resolveSpecFiles(cwd)[0] ?? null;
314
320
  }
315
321
 
316
322
  /**
@@ -82,8 +82,7 @@ import {
82
82
  parseLoopStartArgs,
83
83
  parseMetric,
84
84
  LOOP_DEFAULTS,
85
- RESPEC_SPEC_CANDIDATES,
86
- resolveSpecFile,
85
+ resolveSpecFiles,
87
86
  respecTarget,
88
87
  type LoopState,
89
88
  } from "../goal-loop-forever.js";
@@ -1425,14 +1424,37 @@ async function cmdLoop(args: string, ctx: ExtensionContext): Promise<void> {
1425
1424
  ctx.ui.notify("A loop is already active. /loop stop first.", "warning");
1426
1425
  return;
1427
1426
  }
1428
- const specPath = resolveSpecFile(ctx.cwd);
1429
- if (!specPath) {
1430
- ctx.ui.notify(
1431
- `/loop respec: no spec in the project root (looked for ${RESPEC_SPEC_CANDIDATES.join(", ")}). Create one, or use /loop start "<target>" directly.`,
1432
- "warning",
1427
+ const specs = resolveSpecFiles(ctx.cwd);
1428
+ if (specs.length === 0) {
1429
+ // No spec → the target is undetermined; grill instead of dead-ending
1430
+ // on an error (v0.24.4).
1431
+ ctx.ui.notify("No SPEC.md / spec.md in the project root — drafting the loop target with you (or bootstrap a spec first).", "info");
1432
+ await startDrafting(
1433
+ ctx,
1434
+ "loop",
1435
+ "reconcile the codebase against the project spec — but NO SPEC.md / spec.md exists in the root. Grill the user: should the first work be bootstrapping a SPEC.md from the current code (then reconcile against it), or is the reconciliation target better stated in prose? Challenge vague answers.",
1433
1436
  );
1434
1437
  return;
1435
1438
  }
1439
+ let specPath = specs[0]!;
1440
+ if (specs.length > 1) {
1441
+ // Two specs = ambiguous — never silently pick (v0.24.4). One
1442
+ // slash-bar select, plus a nudge to consolidate.
1443
+ const names = specs.map((p) => path.basename(p));
1444
+ const choice = await ctx.ui.select(
1445
+ "Both SPEC.md and spec.md exist in the root — which one is the spec?",
1446
+ names,
1447
+ );
1448
+ if (choice === undefined) {
1449
+ ctx.ui.notify("respec cancelled.", "info");
1450
+ return;
1451
+ }
1452
+ specPath = specs[names.indexOf(choice)]!;
1453
+ ctx.ui.notify(
1454
+ `Using ${path.basename(specPath)} as the spec. Both files exist — worth consolidating; the loop treats only ${path.basename(specPath)} as the spec.`,
1455
+ "info",
1456
+ );
1457
+ }
1436
1458
  const target = respecTarget(path.basename(specPath));
1437
1459
  await startLoopFromConfig(ctx, {
1438
1460
  target,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goal-list-loop-audit",
3
- "version": "0.24.3",
3
+ "version": "0.24.4",
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",