pi-goal-list-loop-audit 0.17.0 → 0.18.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.
- package/README.md +2 -1
- package/extensions/goal-loop-core.ts +29 -0
- package/extensions/goal-loop-display.ts +1 -1
- package/extensions/loops/goal.ts +52 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,8 +41,9 @@ Four top-level commands, that's all:
|
|
|
41
41
|
/goal tweak "<new objective>" # edit in place (Confirm dialog)
|
|
42
42
|
/goal archive # archived goals, newest first
|
|
43
43
|
/glla # open the settings UI (or /glla key=value)
|
|
44
|
+
/list fix the login bug, add dark mode, write docs # dump it — the agent decomposes into items, one Confirm
|
|
44
45
|
/list add # draft a contract (or a whole batch via items[])
|
|
45
|
-
/list add "<objective>" # queue one directly
|
|
46
|
+
/list add "<objective>" # queue one directly — no interview (the /goal start of lists)
|
|
46
47
|
/list add plan.md # file detected → bulk import, one Confirm
|
|
47
48
|
/list add <paste a checklist> # multi-line paste → same batch flow
|
|
48
49
|
|
|
@@ -201,6 +201,35 @@ export function parseListImport(content: string): string[] {
|
|
|
201
201
|
return items;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Route natural-language text handed to `/list` with no subcommand verb
|
|
206
|
+
* (v0.18.0). The user typed a dump — "fix x, do y, write docs" — not a
|
|
207
|
+
* command. Flexible by detection, never a usage error:
|
|
208
|
+
* file path → bulk import (sisyphus/Ralph plan file)
|
|
209
|
+
* multi-line paste → batch add (structure is already explicit)
|
|
210
|
+
* has "Done when:" → one direct item (explicit contract)
|
|
211
|
+
* anything else → conversational decomposition (drafting session;
|
|
212
|
+
* the agent shapes it into items[], one Confirm)
|
|
213
|
+
* The explicit verb `/list add` stays the direct escape hatch (symmetric
|
|
214
|
+
* with `/goal start`): it skips the draft branch.
|
|
215
|
+
*/
|
|
216
|
+
export type ListTextRoute =
|
|
217
|
+
| { kind: "file"; path: string }
|
|
218
|
+
| { kind: "batch"; items: string[] }
|
|
219
|
+
| { kind: "direct"; text: string }
|
|
220
|
+
| { kind: "draft"; seed: string };
|
|
221
|
+
|
|
222
|
+
export function routeListText(cwd: string, raw: string): ListTextRoute {
|
|
223
|
+
const importFile = resolveImportFile(cwd, raw);
|
|
224
|
+
if (importFile) return { kind: "file", path: importFile };
|
|
225
|
+
if (raw.includes("\n")) {
|
|
226
|
+
const pasted = parseListImport(raw);
|
|
227
|
+
if (pasted.length > 1) return { kind: "batch", items: pasted };
|
|
228
|
+
}
|
|
229
|
+
if (!goalArgsNeedDrafting(raw)) return { kind: "direct", text: raw };
|
|
230
|
+
return { kind: "draft", seed: raw };
|
|
231
|
+
}
|
|
232
|
+
|
|
204
233
|
/**
|
|
205
234
|
* Detect whether a `/list add` argument is a readable file (v0.8.2). File
|
|
206
235
|
* detection, not a separate verb: `/list add plan.md` bulk-imports when the
|
|
@@ -128,7 +128,7 @@ function goalLines(g: Goal, state: State, audit: AuditDisplayProgress | null | u
|
|
|
128
128
|
const next = nextPending(g);
|
|
129
129
|
if (next) lines.push(`├─ next: ${truncate(next, 56)}`);
|
|
130
130
|
const queue = state.list?.length ?? 0;
|
|
131
|
-
lines.push(`└─ ${queue > 0 ? `list ${queue} · ` : ""}/goal status · /
|
|
131
|
+
lines.push(`└─ ${queue > 0 ? `list ${queue} · ` : ""}/goal status · /glla`);
|
|
132
132
|
return lines;
|
|
133
133
|
}
|
|
134
134
|
|
package/extensions/loops/goal.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
parseListImport,
|
|
36
36
|
resolveImportFile,
|
|
37
37
|
routeGoalArgs,
|
|
38
|
+
routeListText,
|
|
38
39
|
sumNewAssistantTokens,
|
|
39
40
|
takeAt,
|
|
40
41
|
goalArgsNeedDrafting,
|
|
@@ -434,7 +435,7 @@ async function startDrafting(ctx: ExtensionContext, target: "goal" | "list" | "l
|
|
|
434
435
|
const [file, label, tool] = prompts[target]!;
|
|
435
436
|
ctx.ui.notify(
|
|
436
437
|
seed
|
|
437
|
-
? `${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
|
|
438
|
+
? `${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: ${target === "list" ? "/list add <objective>" : "/goal start <objective>"}.`
|
|
438
439
|
: `${label} started. The agent will grill until the contract is concrete, then ${tool} opens a Confirm dialog. No work begins before confirmation.`,
|
|
439
440
|
"info",
|
|
440
441
|
);
|
|
@@ -724,7 +725,7 @@ async function cmdList(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
724
725
|
lines.push("Active: (none)");
|
|
725
726
|
}
|
|
726
727
|
if (queue.length === 0) {
|
|
727
|
-
lines.push("List: empty. /list add <
|
|
728
|
+
lines.push("List: empty. /list <describe your tasks> | /list add <one item, no interview> | /list add <file>");
|
|
728
729
|
} else {
|
|
729
730
|
lines.push(`Queue (${queue.length}):`);
|
|
730
731
|
const PAGE = 15;
|
|
@@ -759,8 +760,9 @@ async function cmdList(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
759
760
|
await startDrafting(ctx, "list");
|
|
760
761
|
return;
|
|
761
762
|
}
|
|
762
|
-
//
|
|
763
|
-
// multi-line
|
|
763
|
+
// Explicit verb = explicit intent (the /goal start of lists): file and
|
|
764
|
+
// multi-line structure are still honored, but vague text adds AS ONE
|
|
765
|
+
// ITEM — no interview. The conversational path is bare /list <text>.
|
|
764
766
|
const importFile = resolveImportFile(ctx.cwd, raw);
|
|
765
767
|
if (importFile) {
|
|
766
768
|
await bulkAddFromFile(ctx, importFile);
|
|
@@ -773,17 +775,7 @@ async function cmdList(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
773
775
|
return;
|
|
774
776
|
}
|
|
775
777
|
}
|
|
776
|
-
|
|
777
|
-
const item = { id: newGoalId(), objective, verificationContract: verificationContract || undefined, addedAt: nowIso() };
|
|
778
|
-
state = { ...state, list: [...listQueue(), item] };
|
|
779
|
-
persistState(ctx);
|
|
780
|
-
appendLedger(ctx.cwd, "list_added", { id: item.id, objective: item.objective });
|
|
781
|
-
// Nothing active → activate immediately.
|
|
782
|
-
if (!state.goal || state.goal.status === "complete" || state.goal.status === "aborted") {
|
|
783
|
-
activateNextListItem(ctx);
|
|
784
|
-
} else {
|
|
785
|
-
ctx.ui.notify(`Queued (${listQueue().length} waiting): ${objective.slice(0, 80)}`, "info");
|
|
786
|
-
}
|
|
778
|
+
addSingleItem(ctx, raw);
|
|
787
779
|
return;
|
|
788
780
|
}
|
|
789
781
|
|
|
@@ -828,13 +820,50 @@ async function cmdList(args: string, ctx: ExtensionContext): Promise<void> {
|
|
|
828
820
|
return;
|
|
829
821
|
}
|
|
830
822
|
|
|
831
|
-
|
|
823
|
+
// v0.18.0: an unknown first word isn't an error — it's a natural-language
|
|
824
|
+
// dump. "/list fix the login bug, add dark mode, write docs" should MAKE
|
|
825
|
+
// a list, not print usage. Detection chain: file → batch → contract →
|
|
826
|
+
// conversational decomposition (drafting). The explicit verb for adding
|
|
827
|
+
// one item verbatim is /list add.
|
|
828
|
+
let raw = args.trim();
|
|
829
|
+
if (raw.length >= 2 && ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'")))) {
|
|
830
|
+
raw = raw.slice(1, -1).trim();
|
|
831
|
+
}
|
|
832
|
+
const route = routeListText(ctx.cwd, raw);
|
|
833
|
+
if (route.kind === "file") {
|
|
834
|
+
await bulkAddFromFile(ctx, route.path);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (route.kind === "batch") {
|
|
838
|
+
await bulkAddItems(ctx, route.items, "pasted text");
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
if (route.kind === "direct") {
|
|
842
|
+
addSingleItem(ctx, route.text);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
await startDrafting(ctx, "list", route.seed);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/** Append one objective to the list; activate immediately when idle. */
|
|
849
|
+
function addSingleItem(ctx: ExtensionContext, raw: string): void {
|
|
850
|
+
const { objective, verificationContract } = extractVerificationContract(raw);
|
|
851
|
+
const item = { id: newGoalId(), objective, verificationContract: verificationContract || undefined, addedAt: nowIso() };
|
|
852
|
+
state = { ...state, list: [...listQueue(), item] };
|
|
853
|
+
persistState(ctx);
|
|
854
|
+
appendLedger(ctx.cwd, "list_added", { id: item.id, objective: item.objective });
|
|
855
|
+
// Nothing active → activate immediately.
|
|
856
|
+
if (!state.goal || state.goal.status === "complete" || state.goal.status === "aborted") {
|
|
857
|
+
activateNextListItem(ctx);
|
|
858
|
+
} else {
|
|
859
|
+
ctx.ui.notify(`Queued (${listQueue().length} waiting): ${objective.slice(0, 80)}`, "info");
|
|
860
|
+
}
|
|
832
861
|
}
|
|
833
862
|
|
|
834
863
|
/**
|
|
835
864
|
* Config-gated push notification: if settings.notifyCmd is set, shell out
|
|
836
865
|
* with the message as $1. Fire-and-forget — a broken notify command never
|
|
837
|
-
* blocks the loop. /
|
|
866
|
+
* blocks the loop. /glla notify='<cmd>' to configure.
|
|
838
867
|
*/
|
|
839
868
|
function notifyExternal(ctx: ExtensionContext, message: string): void {
|
|
840
869
|
try {
|
|
@@ -1307,13 +1336,13 @@ function registerAgentTools(pi: any, ctx: ExtensionContext): void {
|
|
|
1307
1336
|
status: "active",
|
|
1308
1337
|
auditHistory: history,
|
|
1309
1338
|
pauseReason: `auditor infrastructure: ${result.error}`,
|
|
1310
|
-
pauseSuggestedAction: "Fix the auditor model (/
|
|
1339
|
+
pauseSuggestedAction: "Fix the auditor model (/glla model=provider/id) and call complete_goal again — your work was NOT judged",
|
|
1311
1340
|
}, ctx);
|
|
1312
1341
|
scheduleContinuation(ctx, true);
|
|
1313
1342
|
return {
|
|
1314
1343
|
content: [{
|
|
1315
1344
|
type: "text",
|
|
1316
|
-
text: `The auditor could not run (infrastructure, NOT a verdict): ${result.error}\nYour completion claim was not evaluated. Fix the auditor model with /
|
|
1345
|
+
text: `The auditor could not run (infrastructure, NOT a verdict): ${result.error}\nYour completion claim was not evaluated. Fix the auditor model with /glla model=provider/id and call complete_goal again — do not change your deliverable for this.`,
|
|
1317
1346
|
}],
|
|
1318
1347
|
details: {},
|
|
1319
1348
|
};
|
|
@@ -1823,7 +1852,7 @@ interface Settings {
|
|
|
1823
1852
|
const DEFAULT_SETTINGS: Settings = {
|
|
1824
1853
|
// Unset = follow the pi session thinking level (user selects thinking in
|
|
1825
1854
|
// pi, auditor follows), floor "high" — the auditor is the verification
|
|
1826
|
-
// gate, depth is worth more there than speed. /
|
|
1855
|
+
// gate, depth is worth more there than speed. /glla thinking= overrides.
|
|
1827
1856
|
auditorThinkingLevel: undefined,
|
|
1828
1857
|
};
|
|
1829
1858
|
|
|
@@ -1856,7 +1885,7 @@ function loadSettings(cwd: string): Settings {
|
|
|
1856
1885
|
) as unknown as Settings;
|
|
1857
1886
|
}
|
|
1858
1887
|
|
|
1859
|
-
/** Where each effective setting comes from (for the /
|
|
1888
|
+
/** Where each effective setting comes from (for the /glla display). */
|
|
1860
1889
|
function settingsProvenance(cwd: string): Record<keyof Settings, { value: unknown; source: "project" | "global" | "default" }> {
|
|
1861
1890
|
const proj = readSettingsFile(projectSettingsPath(cwd));
|
|
1862
1891
|
const glob = readSettingsFile(globalSettingsPath());
|
|
@@ -1905,7 +1934,7 @@ function getSessionThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high
|
|
|
1905
1934
|
* model in pi; the auditor uses it.** The plugin never picks a model itself.
|
|
1906
1935
|
*
|
|
1907
1936
|
* Chain:
|
|
1908
|
-
* 1. Explicit `/
|
|
1937
|
+
* 1. Explicit `/glla model=provider/id` override (rare).
|
|
1909
1938
|
* 2. The pi session model (ctx.model) — whatever the user selected.
|
|
1910
1939
|
*
|
|
1911
1940
|
* If the session model's provider is extension-registered, the auditor's
|
|
@@ -2169,7 +2198,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2169
2198
|
handler: settingsHandler,
|
|
2170
2199
|
});
|
|
2171
2200
|
pi.registerCommand("list", {
|
|
2172
|
-
description: "Loop 2: the list of audited goals — order is the default, not the law. /list add <
|
|
2201
|
+
description: "Loop 2: the list of audited goals — order is the default, not the law. /list <describe your tasks> (agent decomposes into items, one Confirm) | /list add <item or file> (direct, no interview) | /list show | /list next [n] | /list remove <n> | /list clear",
|
|
2173
2202
|
handler: (args: string, ctx: ExtensionContext) => { rememberCtx(ctx); return cmdList(args, ctx); },
|
|
2174
2203
|
});
|
|
2175
2204
|
pi.registerCommand("loop", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-list-loop-audit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|