@gonrocca/zero-pi 0.1.17 → 0.1.19

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.
@@ -21,7 +21,6 @@ import { join } from "node:path";
21
21
 
22
22
  import { readAutotuneMode, type AutotuneMode } from "./autotune.ts";
23
23
  import type { AutotunePending } from "./autotune-extension.ts";
24
- import { discoverModels } from "./opencode-models.ts";
25
24
 
26
25
  /** The SDD phases, in pipeline order. */
27
26
  export const PHASES = ["explore", "plan", "build", "veredicto"] as const;
@@ -214,45 +213,30 @@ const CUSTOM_PROVIDER = "— otro provider (escribir) —";
214
213
  /**
215
214
  * Group models by provider for the picker.
216
215
  *
217
- * The primary source is OpenCode's model catalogthe same one the `zero`
218
- * installer's picker uses — so every provider the user authenticated there
219
- * (opencode-go, openai/codex, anthropic, …) is offered, with only tool-call-
220
- * capable models (the SDD phases need tool calls). When OpenCode is not
221
- * present it falls back to pi's own model registry, then to an empty map.
216
+ * The source is pi's own model registry`getAll()` returns every model from
217
+ * every provider the user is authenticated for (`anthropic`, `openai-codex`,
218
+ * `opencode-go`, …), each with the exact provider id and model id pi resolves
219
+ * at runtime. Using pi's registry rather than a foreign catalog like
220
+ * OpenCode's guarantees the names written to `~/.pi/zero.json` are names pi
221
+ * actually understands, so a configured phase model never fails to resolve.
222
222
  */
223
223
  function providerGroups(registry: PiModelRegistry | undefined): Map<string, string[]> {
224
- try {
225
- const discovered = discoverModels().filter((m) => m.toolCall);
226
- if (discovered.length > 0) return groupByProvider(discovered);
227
- } catch {
228
- /* fall through to pi's registry */
229
- }
230
224
  if (registry && typeof registry.getAll === "function") {
231
225
  try {
232
226
  const all = registry.getAll();
233
227
  if (all && all.length > 0) return groupByProvider(all);
234
228
  } catch {
235
- /* fall through */
229
+ /* fall through to an empty map */
236
230
  }
237
231
  }
238
232
  return new Map();
239
233
  }
240
234
 
241
- /**
242
- * Find the provider that owns a model id — checked against OpenCode's catalog
243
- * first, then pi's own registry.
244
- */
235
+ /** Find the provider that owns a model id, per pi's own model registry. */
245
236
  function resolveProvider(
246
237
  registry: PiModelRegistry | undefined,
247
238
  modelId: string,
248
239
  ): string | undefined {
249
- try {
250
- for (const m of discoverModels()) {
251
- if (m.id === modelId) return m.provider;
252
- }
253
- } catch {
254
- /* ignore */
255
- }
256
240
  if (registry && typeof registry.getAll === "function") {
257
241
  try {
258
242
  for (const m of registry.getAll()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonrocca/zero-pi",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "zero-pi — an installable layer for pi (pi.dev): the zero spec-driven development workflow, skill auto-learning, and an animated ZERO startup banner. Adds capability to pi without modifying pi.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -44,7 +44,6 @@
44
44
  "extensions/sdd-agents.ts",
45
45
  "extensions/conversation-resume.ts",
46
46
  "extensions/zero-models.ts",
47
- "extensions/opencode-models.ts",
48
47
  "extensions/autotune.ts",
49
48
  "extensions/autotune-extension.ts",
50
49
  "extensions/spec-merge.ts",
package/prompts/forge.md CHANGED
@@ -1,34 +1,17 @@
1
1
  ---
2
- description: Run an automatic spec-driven development pipeline for a feature request
2
+ description: Run the zero spec-driven development pipeline for a feature request
3
3
  ---
4
4
 
5
- Run the zero SDD pipeline for the feature request in the arguments.
6
-
7
- **Parse the arguments first.** If the arguments start with `--continue`, this is
8
- a **resume** run, not a fresh one:
9
-
10
- - `--continue` with no slug resume mode: hand control to the orchestrator's
11
- `## Resuming a run` section, which scans `.sdd/*/` for an unfinished run.
12
- - `--continue <slug>` resume mode targeting `.sdd/<slug>/` directly. If that
13
- directory does not exist, report "no such run: <slug>" and stop — do **not**
14
- start a fresh run under that slug.
15
- - Anything else (a feature request, or no arguments) → a fresh run, exactly
16
- today's behaviour: the arguments are the feature request.
17
-
18
- Follow the zero SDD orchestrator instructions: drive the run through the
19
- explore → plan → build → veredicto phases, honour the build/veredicto iteration
20
- cap, and use the execution mode (interactive or automatic) the user chose.
21
-
22
- Delegate each phase to its dedicated sub-agent so the phase runs on the model
23
- it is configured for: `zero-explore`, `zero-plan`, `zero-build`, and
24
- `zero-veredicto`. You stay the orchestrator — you decide phase order and count
25
- the rounds; the sub-agents only execute their phase.
26
-
27
- In interactive mode, pause after each phase with a summary and ask before
28
- continuing. Never report success unless the veredicto phase returned a `pasa`
29
- verdict; if the cap is reached first, report that the result is not verified.
30
-
31
- To change the per-phase models, the user runs the `/zero-models` command — do
32
- not handle model configuration here.
5
+ Run the zero SDD pipeline for the feature request below — you are the
6
+ orchestrator. Drive it through four phases in order: **explore → plan → build →
7
+ veredicto**, delegating each to its sub-agent (`zero-explore`, `zero-plan`,
8
+ `zero-build`, `zero-veredicto`). A `corregir` verdict re-runs `build`; a
9
+ `replantear` verdict re-runs `plan`; after a few rounds with no `pasa`, stop and
10
+ report the result as not verified. Ask the user for interactive or automatic
11
+ mode up front; in interactive mode pause after each phase for approval. Never
12
+ claim success unless veredicto returned `pasa`.
13
+
14
+ If the request begins with `--continue [slug]`, resume that unfinished run from
15
+ its `.sdd/<slug>/` artifacts instead of starting fresh.
33
16
 
34
17
  Feature request: $ARGUMENTS
@@ -1,111 +0,0 @@
1
- // zero-pi — OpenCode model-catalog discovery.
2
- //
3
- // Surfaces the model providers a user can run by cross-referencing OpenCode's
4
- // model catalog cache with the user's OpenCode auth file. This is the same
5
- // approach the `zero` installer's model picker uses, so `/zero-models` offers
6
- // the same providers — opencode-go, openai (codex/gpt), anthropic, … — as the
7
- // installer, instead of a hardcoded Claude list.
8
- //
9
- // Sources, under the user's home (ZERO_HOME-overridable for tests):
10
- // ~/.cache/opencode/models.json — the full provider/model catalog
11
- // ~/.local/share/opencode/auth.json — the providers the user authenticated
12
- //
13
- // Discovery is total: a missing or corrupt file yields an empty list, so a
14
- // user without OpenCode simply falls back to pi's own model registry.
15
-
16
- import { readFileSync } from "node:fs";
17
- import { homedir } from "node:os";
18
- import { join } from "node:path";
19
-
20
- /** A model discovered from OpenCode's catalog. */
21
- export interface DiscoveredModel {
22
- /** The provider id, e.g. `opencode-go`, `openai`, `anthropic`. */
23
- provider: string;
24
- /** The model id, e.g. `gpt-5-codex`. */
25
- id: string;
26
- /** The human-readable model name. */
27
- name: string;
28
- /** Whether the model supports tool calls — required for the SDD phases. */
29
- toolCall: boolean;
30
- /** Whether the model exposes a reasoning-effort setting. */
31
- reasoning: boolean;
32
- }
33
-
34
- /** The home directory discovery resolves OpenCode paths against. */
35
- function discoveryHome(): string {
36
- const override = process.env.ZERO_HOME;
37
- return typeof override === "string" && override.trim() !== "" ? override : homedir();
38
- }
39
-
40
- /** True for a plain (non-array) object. */
41
- function isObject(value: unknown): value is Record<string, unknown> {
42
- return typeof value === "object" && value !== null && !Array.isArray(value);
43
- }
44
-
45
- /** Read and parse a JSON file; returns null on any failure. */
46
- function readJson(path: string): unknown {
47
- try {
48
- return JSON.parse(readFileSync(path, "utf8")) as unknown;
49
- } catch {
50
- return null;
51
- }
52
- }
53
-
54
- /**
55
- * Extract a provider's models from its OpenCode catalog entry.
56
- *
57
- * Exported for tests.
58
- */
59
- export function extractModels(
60
- providerId: string,
61
- entry: Record<string, unknown>,
62
- ): DiscoveredModel[] {
63
- const raw = entry.models;
64
- if (!isObject(raw)) return [];
65
-
66
- const models: DiscoveredModel[] = [];
67
- for (const [id, model] of Object.entries(raw)) {
68
- if (!isObject(model)) continue;
69
- models.push({
70
- provider: providerId,
71
- id,
72
- name: typeof model.name === "string" ? model.name : id,
73
- toolCall: model.tool_call === true,
74
- reasoning: model.reasoning === true,
75
- });
76
- }
77
- return models;
78
- }
79
-
80
- /**
81
- * Discover every model the user can run, flattened across all OpenCode-
82
- * authenticated providers.
83
- *
84
- * `catalogJson` and `authJson` are injectable for tests; in normal use they
85
- * are read from OpenCode's cache and auth files on disk. The result is empty
86
- * when either source is absent or malformed.
87
- */
88
- export function discoverModels(
89
- catalogJson?: unknown,
90
- authJson?: unknown,
91
- ): DiscoveredModel[] {
92
- // Only touch disk when an argument is genuinely absent — an explicit value
93
- // (including `null`) is used as given, so tests never read real files.
94
- const auth =
95
- authJson === undefined
96
- ? readJson(join(discoveryHome(), ".local", "share", "opencode", "auth.json"))
97
- : authJson;
98
- const catalog =
99
- catalogJson === undefined
100
- ? readJson(join(discoveryHome(), ".cache", "opencode", "models.json"))
101
- : catalogJson;
102
- if (!isObject(auth) || !isObject(catalog)) return [];
103
-
104
- const models: DiscoveredModel[] = [];
105
- for (const providerId of Object.keys(auth)) {
106
- const entry = catalog[providerId];
107
- if (!isObject(entry)) continue;
108
- models.push(...extractModels(providerId, entry));
109
- }
110
- return models;
111
- }