@gonrocca/zero-pi 0.1.62 โ 0.1.64
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 +5 -3
- package/extensions/zero-cost-extension.ts +92 -26
- package/extensions/zero-cost.ts +3 -1
- package/extensions/zero-models-picker.ts +43 -0
- package/extensions/zero-models.ts +10 -15
- package/package.json +1 -1
- package/prompts/forge.md +4 -2
- package/prompts/orchestrator.md +24 -0
package/README.md
CHANGED
|
@@ -47,7 +47,9 @@ the `zero-*` phase agents.
|
|
|
47
47
|
2. Run `/zero-models` (or set direct assignments) when you want a different
|
|
48
48
|
model/thinking profile per phase; otherwise the package defaults are used.
|
|
49
49
|
3. Run `/forge <feature>` and let the automatic gates drive the flow.
|
|
50
|
-
4.
|
|
50
|
+
4. Read the final cost table: `/forge` invokes `/zero-cost <slug>` automatically
|
|
51
|
+
at the end. Re-run `/zero-cost [slug]` manually only when you want to inspect
|
|
52
|
+
an older run or debug missing metadata.
|
|
51
53
|
|
|
52
54
|
## ๐ `/forge` โ the SDD pipeline
|
|
53
55
|
|
|
@@ -104,7 +106,7 @@ into `/forge` for you.
|
|
|
104
106
|
| **Dependency-aware tasks** | `tasks.md` is validated as a task graph with mandatory `depends:` edges, topological ordering, and review workload totals. |
|
|
105
107
|
| **Autotune** | Learns which model fits each phase from your run history and re-tunes itself; optional `autotuneBudget.maxPhaseCostUsd` suppresses costly step-ups. |
|
|
106
108
|
| **`/zero-doctor`** | Preflight diagnostics for zero-pi: package install, Node version, pi-subagents, generated phase agents, model config, `.sdd/config`, run history, git, and `gh` auth. |
|
|
107
|
-
| **`/zero-cost`** | Aggregates a run's sub-agent `meta.json` files into a per-phase token/cost/duration report โ
|
|
109
|
+
| **`/zero-cost`** | Aggregates a run's sub-agent `meta.json` files into a per-phase token/cost/duration report โ `/forge` invokes it automatically at the end; manual re-run stays available for older runs/debug. |
|
|
108
110
|
| **`/zero-checkpoint`** | Writes patch-based worktree checkpoints under `.sdd/<slug>/checkpoints/` before risky build batches. |
|
|
109
111
|
| **`/zero-sync` / `/zero-archive`** | Folds each run's spec delta into a canonical, project-wide spec store and archives approved runs. |
|
|
110
112
|
| **Git / PR / Issues** | `/zero-branch`, `/zero-git-validate`, `/zero-pr`, and `/zero-issue` keep branches and GitHub links audit-ready. |
|
|
@@ -129,7 +131,7 @@ into `/forge` for you.
|
|
|
129
131
|
| `/zero-archive <slug>` | Merge an approved run into `.sdd/specs/`, move it to `.sdd/archive/YYYY-MM-DD-<slug>/`, and persist `archivePath`. |
|
|
130
132
|
| `/zero-validate <slug>` | Validate proposal/spec/design/tasks artifacts, including task schema and per-domain specs. |
|
|
131
133
|
| `/zero-status` | Show each `.sdd/` run's artifact, sync, latest-verdict, and GitHub-link status. |
|
|
132
|
-
| `/zero-cost [<slug>]` | Report tokens (in/out/cache), USD cost, duration, and tool-count per SDD phase for a run โ `<slug>` for a specific run, no argument for the most recent. |
|
|
134
|
+
| `/zero-cost [<slug>]` | Report tokens (in/out/cache), USD cost, duration, and tool-count per SDD phase for a run โ reads native pi session metas and project-local `.pi-subagents/artifacts`; `<slug>` for a specific run, no argument for the most recent. |
|
|
133
135
|
| `/zero-checkpoint [<slug>] [--json]` | Save `diff.patch`, `status.txt`, `head.txt`, `meta.json`, and a review-before-running `restore.sh` under `.sdd/<slug>/checkpoints/<id>/`. |
|
|
134
136
|
| `/zero-branch <slug>` | Create/reuse the configured SDD Git branch and persist `branch`/`baseBranch`. |
|
|
135
137
|
| `/zero-git-validate <slug>` | Check worktree, branch, remote, `gh auth`, and verdict gating before PR/archive. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { parseMeta, selectRunMetas, aggregateRun, formatReport, type PhaseMeta } from "./zero-cost.ts";
|
|
6
6
|
|
|
@@ -8,40 +8,106 @@ type NotifyType = "info" | "warning" | "error";
|
|
|
8
8
|
interface PiCommandContext { ui: { notify(message: string, type?: NotifyType): void } }
|
|
9
9
|
interface PiExtensionAPI { registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: PiCommandContext) => void | Promise<void> }): void }
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
export interface ReadAllPhaseMetasOptions {
|
|
12
|
+
/** Override for tests; defaults to ~/.pi/agent/sessions. */
|
|
13
|
+
sessionsRoot?: string;
|
|
14
|
+
/** Project cwd used to discover local .pi-subagents/artifacts; defaults to process.cwd(). */
|
|
15
|
+
cwd?: string;
|
|
16
|
+
/** Disable project-local .pi-subagents scanning when needed. */
|
|
17
|
+
includeProjectArtifacts?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function addMeta(out: PhaseMeta[], seen: Set<string>, meta: PhaseMeta): void {
|
|
21
|
+
const key = `${meta.runId}\0${meta.phase}\0${meta.slug ?? ""}\0${meta.timestamp}\0${meta.model}`;
|
|
22
|
+
if (seen.has(key)) return;
|
|
23
|
+
seen.add(key);
|
|
24
|
+
out.push(meta);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readArtifactDir(artifacts: string, out: PhaseMeta[], seen: Set<string>): void {
|
|
28
|
+
if (!existsSync(artifacts)) return;
|
|
29
|
+
let files: string[];
|
|
30
|
+
try { files = readdirSync(artifacts); }
|
|
31
|
+
catch { return; }
|
|
32
|
+
for (const f of files) {
|
|
33
|
+
if (!f.endsWith("_meta.json")) continue;
|
|
34
|
+
try {
|
|
35
|
+
const meta = parseMeta(JSON.parse(readFileSync(join(artifacts, f), "utf8")));
|
|
36
|
+
if (meta) addMeta(out, seen, meta);
|
|
37
|
+
} catch { /* skip unreadable / malformed meta */ }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function localArtifactDirs(startCwd: string): string[] {
|
|
42
|
+
const out: string[] = [];
|
|
43
|
+
const home = resolve(homedir());
|
|
44
|
+
let dir = resolve(startCwd);
|
|
45
|
+
let hops = 0;
|
|
46
|
+
for (;;) {
|
|
47
|
+
out.push(join(dir, ".pi-subagents", "artifacts"));
|
|
48
|
+
const parent = dirname(dir);
|
|
49
|
+
if (dir === parent || dir === home || hops >= 8) break;
|
|
50
|
+
dir = parent;
|
|
51
|
+
hops += 1;
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Read every sub-agent `meta.json` that zero-cost knows about:
|
|
57
|
+
* - pi-native `/forge` artifacts under `~/.pi/agent/sessions/<session>/subagent-artifacts/`.
|
|
58
|
+
* - project-local pi-subagents artifacts under `.pi-subagents/artifacts/` from
|
|
59
|
+
* the current cwd and its parents. This covers manually delegated zero phase
|
|
60
|
+
* runs without making `/zero-cost` scan unrelated projects. */
|
|
61
|
+
export function readAllPhaseMetas(options: ReadAllPhaseMetasOptions = {}): PhaseMeta[] {
|
|
62
|
+
const root = options.sessionsRoot ?? join(homedir(), ".pi", "agent", "sessions");
|
|
17
63
|
const out: PhaseMeta[] = [];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
64
|
+
const seen = new Set<string>();
|
|
65
|
+
|
|
66
|
+
if (existsSync(root)) {
|
|
67
|
+
try {
|
|
68
|
+
for (const session of readdirSync(root, { withFileTypes: true })) {
|
|
69
|
+
if (!session.isDirectory()) continue;
|
|
70
|
+
readArtifactDir(join(root, session.name, "subagent-artifacts"), out, seen);
|
|
71
|
+
}
|
|
72
|
+
} catch { /* ignore inaccessible session root */ }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options.includeProjectArtifacts !== false) {
|
|
76
|
+
for (const artifacts of localArtifactDirs(options.cwd ?? process.cwd())) {
|
|
77
|
+
readArtifactDir(artifacts, out, seen);
|
|
28
78
|
}
|
|
29
79
|
}
|
|
80
|
+
|
|
30
81
|
return out;
|
|
31
82
|
}
|
|
32
83
|
|
|
33
|
-
function
|
|
84
|
+
export function formatNoCostMessage(slug: string | null, metaCount: number): string {
|
|
85
|
+
const searched = "Busquรฉ en ~/.pi/agent/sessions/*/subagent-artifacts/ y en .pi-subagents/artifacts/ del proyecto actual (y padres).";
|
|
86
|
+
if (slug) {
|
|
87
|
+
const hint = metaCount > 0
|
|
88
|
+
? "Encontrรฉ metadata de otros runs, pero ningรบn meta matchea ese slug; probรก /zero-cost sin argumento o revisรก el slug exacto."
|
|
89
|
+
: "No encontrรฉ ningรบn *_meta.json de zero-* todavรญa.";
|
|
90
|
+
return [
|
|
91
|
+
`zero-cost: no encontrรฉ datos de costo para "${slug}".`,
|
|
92
|
+
searched,
|
|
93
|
+
hint,
|
|
94
|
+
"Si el run fue manual con subagent(...) en otro proyecto, corrรฉ /zero-cost desde ese cwd; si no hay *_meta.json, no hay costo recuperable.",
|
|
95
|
+
].join("\n");
|
|
96
|
+
}
|
|
97
|
+
return [
|
|
98
|
+
"zero-cost: no encontrรฉ runs con datos de costo todavรญa.",
|
|
99
|
+
searched,
|
|
100
|
+
"/zero-cost solo suma metadata real (*_meta.json). Ejecutรก /forge nativo y volvรฉ a probar; si el run fue manual, corrรฉ el comando desde el proyecto donde quedรณ .pi-subagents/.",
|
|
101
|
+
].join("\n");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function runZeroCost(args: string, ctx: PiCommandContext, options: ReadAllPhaseMetasOptions = {}): void {
|
|
34
105
|
const notify = (m: string, t?: NotifyType) => { try { ctx.ui.notify(m, t); } catch {} };
|
|
35
106
|
const slug = args.trim() || null;
|
|
36
|
-
const metas = readAllPhaseMetas();
|
|
107
|
+
const metas = readAllPhaseMetas(options);
|
|
37
108
|
const selected = selectRunMetas(metas, slug);
|
|
38
109
|
if (selected.length === 0) {
|
|
39
|
-
notify(
|
|
40
|
-
slug
|
|
41
|
-
? `zero-cost: no encontrรฉ datos de costo para "${slug}".`
|
|
42
|
-
: "zero-cost: no encontrรฉ runs con datos de costo todavรญa.",
|
|
43
|
-
"info",
|
|
44
|
-
);
|
|
110
|
+
notify(formatNoCostMessage(slug, metas.length), "info");
|
|
45
111
|
return;
|
|
46
112
|
}
|
|
47
113
|
notify(formatReport(aggregateRun(selected, slug ?? selected[0]?.slug ?? null)), "info");
|
|
@@ -52,7 +118,7 @@ export default function register(pi?: PiExtensionAPI): void {
|
|
|
52
118
|
pi.registerCommand("zero-cost", {
|
|
53
119
|
description: "Reporta tokens, costo y duraciรณn por fase de un run SDD (slug opcional; sin slug = el mรกs reciente)",
|
|
54
120
|
handler: (args: string, ctx: PiCommandContext): void => {
|
|
55
|
-
try { if (ctx?.ui?.notify)
|
|
121
|
+
try { if (ctx?.ui?.notify) runZeroCost(args ?? "", ctx); }
|
|
56
122
|
catch (err) { try { ctx.ui.notify(`zero-cost: ${err instanceof Error ? err.message : String(err)}`, "error"); } catch {} }
|
|
57
123
|
},
|
|
58
124
|
});
|
package/extensions/zero-cost.ts
CHANGED
|
@@ -77,7 +77,9 @@ export function extractSlug(task: unknown): string | null {
|
|
|
77
77
|
const slug = m[1];
|
|
78
78
|
if (slug && slug !== "specs" && slug !== "archive") return slug;
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
const label = /(?:^|\n)\s*Slug:\s*([A-Za-z0-9._-]+)/i.exec(task);
|
|
81
|
+
const slug = label?.[1];
|
|
82
|
+
return slug && slug !== "specs" && slug !== "archive" ? slug : null;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
function num(v: unknown): number {
|
|
@@ -346,6 +346,49 @@ export function navigate(state: PickerState, dir: -1 | 1): PickerState {
|
|
|
346
346
|
return state;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
// decodeKey โ keystroke decoding
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
/** The picker's five meaningful keys, as decoded from a raw stdin sequence. */
|
|
354
|
+
export type PickerKey = "up" | "down" | "enter" | "esc" | "backspace";
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Kitty-keyboard-protocol matcher for one functional key: `CSI <code>` +
|
|
358
|
+
* optional `;1` (no modifiers) + optional `:1` (press) / `:2` (repeat) +
|
|
359
|
+
* `<final>`. A `:3` (release) or any real modifier does NOT match โ releases
|
|
360
|
+
* must be ignored and modified keys are not picker keys.
|
|
361
|
+
*/
|
|
362
|
+
function kittyPressOrRepeat(code: string, final: string): RegExp {
|
|
363
|
+
return new RegExp(`^\\x1b\\[${code}(?:;1(?::[12])?)?${final}$`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const KITTY_UP = kittyPressOrRepeat("1", "A");
|
|
367
|
+
const KITTY_DOWN = kittyPressOrRepeat("1", "B");
|
|
368
|
+
const KITTY_ENTER = kittyPressOrRepeat("13", "u");
|
|
369
|
+
const KITTY_ESC = kittyPressOrRepeat("27", "u");
|
|
370
|
+
const KITTY_BACKSPACE = kittyPressOrRepeat("127", "u");
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Decode one raw stdin sequence into a picker key, or `null` when it is not
|
|
374
|
+
* one (printable characters, releases, modified keys, unknown sequences).
|
|
375
|
+
*
|
|
376
|
+
* pi-tui negotiates kitty keyboard protocol flags 7 with the terminal, and a
|
|
377
|
+
* granting terminal (Ghostty, kitty, โฆ) then encodes arrows as
|
|
378
|
+
* `CSI 1;1:1 A/B` and Esc as `CSI 27 u` โ never the legacy `\x1b[A` / bare
|
|
379
|
+
* `\x1b` forms. A non-granting terminal keeps the legacy (or SS3
|
|
380
|
+
* application-cursor-mode) forms. Both worlds are accepted here; kitty
|
|
381
|
+
* repeats (`:2`) navigate too so holding an arrow scrolls the list.
|
|
382
|
+
*/
|
|
383
|
+
export function decodeKey(data: string): PickerKey | null {
|
|
384
|
+
if (data === "\x1b[A" || data === "\x1bOA" || KITTY_UP.test(data)) return "up";
|
|
385
|
+
if (data === "\x1b[B" || data === "\x1bOB" || KITTY_DOWN.test(data)) return "down";
|
|
386
|
+
if (data === "\r" || data === "\n" || data === "\r\n" || KITTY_ENTER.test(data)) return "enter";
|
|
387
|
+
if (data === "\x1b" || KITTY_ESC.test(data)) return "esc";
|
|
388
|
+
if (data === "\x7f" || data === "\x08" || KITTY_BACKSPACE.test(data)) return "backspace";
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
349
392
|
// ---------------------------------------------------------------------------
|
|
350
393
|
// enter / back โ Enter/Esc dispatch
|
|
351
394
|
// ---------------------------------------------------------------------------
|
|
@@ -44,6 +44,7 @@ import type { AutotunePending } from "./autotune-extension.ts";
|
|
|
44
44
|
import {
|
|
45
45
|
back,
|
|
46
46
|
createPickerState,
|
|
47
|
+
decodeKey,
|
|
47
48
|
enter,
|
|
48
49
|
navigate,
|
|
49
50
|
submitText,
|
|
@@ -521,14 +522,6 @@ const PICKER_TITLE = "zero ยท modelos SDD";
|
|
|
521
522
|
/** The dim help line shown at the foot of the boxed panel. */
|
|
522
523
|
const PICKER_HELP = "โโ navegar ยท enter elegir ยท esc volver";
|
|
523
524
|
|
|
524
|
-
/** ANSI arrow-key escape sequences. */
|
|
525
|
-
const KEY_UP = "\x1b[A";
|
|
526
|
-
const KEY_DOWN = "\x1b[B";
|
|
527
|
-
const KEY_ESC = "\x1b";
|
|
528
|
-
/** Enter โ CR or LF, depending on the terminal. */
|
|
529
|
-
const KEY_ENTER = new Set(["\r", "\n", "\r\n"]);
|
|
530
|
-
/** Backspace โ DEL or BS. */
|
|
531
|
-
const KEY_BACKSPACE = new Set(["\x7f", "\x08"]);
|
|
532
525
|
|
|
533
526
|
/**
|
|
534
527
|
* Clamp a rendered line to `width` columns so it never overflows the box
|
|
@@ -678,7 +671,8 @@ function createPickerComponent(
|
|
|
678
671
|
|
|
679
672
|
/** Route a keystroke while the inline text buffer is open. */
|
|
680
673
|
function handleTextInput(data: string): void {
|
|
681
|
-
|
|
674
|
+
const key = decodeKey(data);
|
|
675
|
+
if (key === "esc") {
|
|
682
676
|
// Esc abandons the typed value and returns to the current list screen
|
|
683
677
|
// unchanged. `submitText` with an empty string is exactly that no-op:
|
|
684
678
|
// it clears `textPrompt` and rebuilds the list without committing.
|
|
@@ -687,13 +681,13 @@ function createPickerComponent(
|
|
|
687
681
|
tui.requestRender();
|
|
688
682
|
return;
|
|
689
683
|
}
|
|
690
|
-
if (
|
|
684
|
+
if (key === "enter") {
|
|
691
685
|
state = submitText(state, buffer ?? "");
|
|
692
686
|
buffer = null;
|
|
693
687
|
tui.requestRender();
|
|
694
688
|
return;
|
|
695
689
|
}
|
|
696
|
-
if (
|
|
690
|
+
if (key === "backspace") {
|
|
697
691
|
buffer = (buffer ?? "").slice(0, -1);
|
|
698
692
|
tui.requestRender();
|
|
699
693
|
return;
|
|
@@ -714,24 +708,25 @@ function createPickerComponent(
|
|
|
714
708
|
return;
|
|
715
709
|
}
|
|
716
710
|
|
|
717
|
-
|
|
711
|
+
const key = decodeKey(data);
|
|
712
|
+
if (key === "up") {
|
|
718
713
|
state = navigate(state, -1);
|
|
719
714
|
tui.requestRender();
|
|
720
715
|
return;
|
|
721
716
|
}
|
|
722
|
-
if (
|
|
717
|
+
if (key === "down") {
|
|
723
718
|
state = navigate(state, 1);
|
|
724
719
|
tui.requestRender();
|
|
725
720
|
return;
|
|
726
721
|
}
|
|
727
|
-
if (
|
|
722
|
+
if (key === "enter") {
|
|
728
723
|
const result = enter(state);
|
|
729
724
|
// `enter` on a custom-* row opens `textPrompt`; arm the buffer.
|
|
730
725
|
if (result.type === "state" && result.state.textPrompt) buffer = "";
|
|
731
726
|
applyResult(result);
|
|
732
727
|
return;
|
|
733
728
|
}
|
|
734
|
-
if (
|
|
729
|
+
if (key === "esc") {
|
|
735
730
|
applyResult(back(state));
|
|
736
731
|
return;
|
|
737
732
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/zero-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.64",
|
|
4
4
|
"description": "zero-pi โ an installable layer for pi (pi.dev): the zero spec-driven development workflow (clarify โ explore โ plan โ analyze โ build โ veredicto) with automatic clarify/analyze gates, per-phase model autotune, and token-efficient batched builds. Adds capability to pi without modifying pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/prompts/forge.md
CHANGED
|
@@ -15,8 +15,10 @@ returns `continue` (proceed to build) or `replan` (re-run plan with its
|
|
|
15
15
|
defects). A `corregir` verdict re-runs `build`; a `replantear` verdict re-runs
|
|
16
16
|
`plan`; after a few rounds with no `pasa`, stop and report the result as not
|
|
17
17
|
verified. Ask the user for interactive or automatic mode up front; in
|
|
18
|
-
interactive mode pause after each phase for approval.
|
|
19
|
-
|
|
18
|
+
interactive mode pause after each phase for approval. At terminal run end,
|
|
19
|
+
invoke `/zero-cost <slug>` automatically and include the best-effort cost table
|
|
20
|
+
in the final summary; the user should not have to run a second command. Never
|
|
21
|
+
claim success unless veredicto returned `pasa`.
|
|
20
22
|
|
|
21
23
|
**Parse the arguments first.** If the request begins with `--continue`, this is
|
|
22
24
|
a **resume** run, not a fresh one:
|
package/prompts/orchestrator.md
CHANGED
|
@@ -476,6 +476,30 @@ verdict" above). If the push fails for any reason, or zero runs with `--no-mcp`
|
|
|
476
476
|
or Cortex is down โ emit a non-blocking warning and continue, never block the
|
|
477
477
|
run. The local line already stands.
|
|
478
478
|
|
|
479
|
+
## Run cost report
|
|
480
|
+
|
|
481
|
+
At the end of every run that reached a terminal verdict (`pasa` or
|
|
482
|
+
`cap-reached`), invoke `/zero-cost <slug>` automatically and include its result
|
|
483
|
+
in the final summary under `Costo:`. The user should not have to remember a
|
|
484
|
+
second command after `/forge`; `/zero-cost` remains available only as a manual
|
|
485
|
+
re-run/debug command.
|
|
486
|
+
|
|
487
|
+
Rules:
|
|
488
|
+
|
|
489
|
+
- Run it **after** the final veredicto/iteration-cap outcome is known and after
|
|
490
|
+
the `~/.pi/zero-runs.jsonl` metric append above. Do not run it for an aborted
|
|
491
|
+
invocation that never reached veredicto.
|
|
492
|
+
- Pass the feature slug explicitly: `/zero-cost <slug>`. Never rely on the
|
|
493
|
+
command's "latest run" default from inside `/forge`.
|
|
494
|
+
- Treat cost reporting as best-effort and non-blocking. If the command is
|
|
495
|
+
missing, errors, or reports no metadata, keep the verdict intact and summarize
|
|
496
|
+
the reason plainly: the run may not have been executed by native `/forge`, pi
|
|
497
|
+
may not have written `*_meta.json`, or project-local `.pi-subagents/` artifacts
|
|
498
|
+
may live under a different cwd.
|
|
499
|
+
- If the command returns a cost table, relay the table compactly in the final
|
|
500
|
+
summary. Do not invent costs from `~/.pi/zero-runs.jsonl`; that file records
|
|
501
|
+
outcomes/models only, not usage.
|
|
502
|
+
|
|
479
503
|
## Git/PR/archive commands
|
|
480
504
|
|
|
481
505
|
Recommended command order for an audit-ready SDD change is: `/zero-branch <slug>` โ `/zero-issue <slug>` โ `/forge <slug>` (plan/build/veredicto) โ `/zero-git-validate <slug> --for=pr` โ `/zero-pr <slug>` โ `/zero-archive <slug>` after `pasa`.
|