@gonrocca/zero-pi 0.1.61 → 0.1.63

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
@@ -37,7 +37,19 @@ pi install npm:@gonrocca/zero-pi
37
37
  pi install npm:pi-subagents # required — the pipeline delegates to sub-agents
38
38
  ```
39
39
 
40
- Needs Node ≥ 20.6. Restart pi after an upgrade.
40
+ Needs Node ≥ 20.6. Restart pi after an install or upgrade so pi regenerates
41
+ the `zero-*` phase agents.
42
+
43
+ ### Quick start
44
+
45
+ 1. Run `/zero-doctor` to catch missing `pi-subagents`, stale generated agents,
46
+ invalid model config, or GitHub CLI auth before spending tokens.
47
+ 2. Run `/zero-models` (or set direct assignments) when you want a different
48
+ model/thinking profile per phase; otherwise the package defaults are used.
49
+ 3. Run `/forge <feature>` and let the automatic gates drive the flow.
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.
41
53
 
42
54
  ## 🛠 `/forge` — the SDD pipeline
43
55
 
@@ -55,6 +67,10 @@ to its own sub-agent:
55
67
  | **build** | Implement the plan. |
56
68
  | **veredicto** | Review it adversarially and record a verdict. |
57
69
 
70
+ Each run keeps its paper trail under `.sdd/<slug>/`: `clarifications.md`,
71
+ `findings.md`, `proposal.md`, `spec.md`, `design.md`, `tasks.md`,
72
+ `checklist.md`, and (when Strict TDD engages) `tdd-evidence.md`.
73
+
58
74
  The **clarify** and **analyze** gates are automatic inside `/forge` — no extra
59
75
  slash command in the normal flow. `clarify` writes `.sdd/<slug>/clarifications.md`
60
76
  and asks only when proceeding would risk the wrong product; `analyze` writes
@@ -90,13 +106,13 @@ into `/forge` for you.
90
106
  | **Dependency-aware tasks** | `tasks.md` is validated as a task graph with mandatory `depends:` edges, topological ordering, and review workload totals. |
91
107
  | **Autotune** | Learns which model fits each phase from your run history and re-tunes itself; optional `autotuneBudget.maxPhaseCostUsd` suppresses costly step-ups. |
92
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. |
93
- | **`/zero-cost`** | Aggregates a run's sub-agent `meta.json` files into a per-phase token/cost/duration report — no schema change, reads what pi already writes. |
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. |
94
110
  | **`/zero-checkpoint`** | Writes patch-based worktree checkpoints under `.sdd/<slug>/checkpoints/` before risky build batches. |
95
111
  | **`/zero-sync` / `/zero-archive`** | Folds each run's spec delta into a canonical, project-wide spec store and archives approved runs. |
96
112
  | **Git / PR / Issues** | `/zero-branch`, `/zero-git-validate`, `/zero-pr`, and `/zero-issue` keep branches and GitHub links audit-ready. |
97
113
  | **Run memory** | Every run recalls and saves traces to Cortex, so runs learn from each other. |
98
114
  | **Provider guard** | Warns when the `anthropic` provider runs on a metered API key instead of your subscription. |
99
- | **Startup banner** | The violet ANSI-Shadow `ZERO` wordmark, drawn once at pi startup — `ZERO_HEADER=off` to disable. |
115
+ | **Startup banner** | The sunset ANSI-Shadow `ZERO` wordmark, drawn once at pi startup — `ZERO_HEADER=off` to disable. |
100
116
  | **Working-phrase ticker** | Swaps pi's `Working...` for a context-aware Spanish phrase + spinner. |
101
117
  | **Conversation resume** | Writes `.pi/zero-resume.md` on exit — the restore command + a conversation tail. |
102
118
  | **Windows tree-kill** | Aborting a turn kills the whole process tree — no orphaned `claude`. |
@@ -115,7 +131,7 @@ into `/forge` for you.
115
131
  | `/zero-archive <slug>` | Merge an approved run into `.sdd/specs/`, move it to `.sdd/archive/YYYY-MM-DD-<slug>/`, and persist `archivePath`. |
116
132
  | `/zero-validate <slug>` | Validate proposal/spec/design/tasks artifacts, including task schema and per-domain specs. |
117
133
  | `/zero-status` | Show each `.sdd/` run's artifact, sync, latest-verdict, and GitHub-link status. |
118
- | `/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. |
119
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>/`. |
120
136
  | `/zero-branch <slug>` | Create/reuse the configured SDD Git branch and persist `branch`/`baseBranch`. |
121
137
  | `/zero-git-validate <slug>` | Check worktree, branch, remote, `gh auth`, and verdict gating before PR/archive. |
@@ -248,11 +264,12 @@ tool-call budget with a mid-budget stop check. The heavy lifting happens inside
248
264
  the sub-agents on their own models — leave the session that runs `/forge` on
249
265
  your cheap default model.
250
266
 
251
- ## Continuous integration
267
+ ## Release checks
252
268
 
253
- The `.github/workflows/zero-pi-ci.yml` workflow runs on every push to `main` and
254
- on every pull request that touches `packages/zero-pi/**`. It enforces `npm test`
255
- and `npm run pack-check` for the package.
269
+ Before publishing, run `npm test` and `npm run pack-check` from
270
+ `packages/zero-pi`. `prepublishOnly` also runs the test suite, but `pack-check`
271
+ confirms the npm tarball still contains the prompts, skills, themes, assets, and
272
+ extension support modules pi needs.
256
273
 
257
274
  ## 🔗 Relationship to `zero`
258
275
 
@@ -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
- /** Read every sub-agent `meta.json` across all pi session dirs and parse the
12
- * zero-<phase> ones. Scanning every session keeps us decoupled from pi's
13
- * cwd-encoding scheme; selection by slug/timestamp narrows to one run. */
14
- function readAllPhaseMetas(): PhaseMeta[] {
15
- const root = join(homedir(), ".pi", "agent", "sessions");
16
- if (!existsSync(root)) return [];
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
- for (const session of readdirSync(root, { withFileTypes: true })) {
19
- if (!session.isDirectory()) continue;
20
- const artifacts = join(root, session.name, "subagent-artifacts");
21
- if (!existsSync(artifacts)) continue;
22
- for (const f of readdirSync(artifacts)) {
23
- if (!f.endsWith("_meta.json")) continue;
24
- try {
25
- const meta = parseMeta(JSON.parse(readFileSync(join(artifacts, f), "utf8")));
26
- if (meta) out.push(meta);
27
- } catch { /* skip unreadable / malformed meta */ }
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 runCost(args: string, ctx: PiCommandContext): void {
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) runCost(args ?? "", ctx); }
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
  });
@@ -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
- return null;
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonrocca/zero-pi",
3
- "version": "0.1.61",
3
+ "version": "0.1.63",
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. Never claim success unless
19
- veredicto returned `pasa`.
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:
@@ -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`.