@gonrocca/zero-pi 0.1.16 → 0.1.18
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/extensions/sdd-agents.ts +128 -0
- package/package.json +3 -1
- package/prompts/forge.md +12 -29
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// zero-pi — SDD sub-agent provisioning.
|
|
2
|
+
//
|
|
3
|
+
// `/forge` delegates each phase to a dedicated sub-agent — `zero-explore`,
|
|
4
|
+
// `zero-plan`, `zero-build`, `zero-veredicto`. pi-subagents discovers agents
|
|
5
|
+
// from `~/.pi/agent/agents/**/*.md`, but a `pi install` of zero-pi ships only
|
|
6
|
+
// the phase *prompts* (`prompts/phases/*.md`), never the agent definitions —
|
|
7
|
+
// so `/forge` had nothing to delegate to and stalled.
|
|
8
|
+
//
|
|
9
|
+
// This extension closes that gap: at load it generates the four agent files
|
|
10
|
+
// under `~/.pi/agent/agents/zero/` from the package's own phase prompts and
|
|
11
|
+
// the per-phase models in `~/.pi/zero.json`. The files are regenerated every
|
|
12
|
+
// load, so they stay in sync with the prompts and with `/zero-models`.
|
|
13
|
+
|
|
14
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
/** The four SDD phases, each backed by a `prompts/phases/<phase>.md`. */
|
|
20
|
+
export const PHASES = ["explore", "plan", "build", "veredicto"] as const;
|
|
21
|
+
export type Phase = (typeof PHASES)[number];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Split a phase prompt into its `description` (from `---` frontmatter) and its
|
|
25
|
+
* body. A prompt with no frontmatter yields an empty description and the whole
|
|
26
|
+
* text as the body. Exported for tests.
|
|
27
|
+
*/
|
|
28
|
+
export function splitPhasePrompt(raw: string): { description: string; body: string } {
|
|
29
|
+
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
30
|
+
if (!match) return { description: "", body: raw.trim() };
|
|
31
|
+
const descLine = match[1].match(/^description:\s*(.+)$/m);
|
|
32
|
+
return {
|
|
33
|
+
description: descLine ? descLine[1].trim() : "",
|
|
34
|
+
body: match[2].trim(),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build the pi-subagents agent definition for one SDD phase: agent frontmatter
|
|
40
|
+
* (`name: zero-<phase>`, the phase model when known, a `replace` system
|
|
41
|
+
* prompt) followed by the phase prompt body. Exported for tests.
|
|
42
|
+
*/
|
|
43
|
+
export function buildAgentFile(
|
|
44
|
+
phase: Phase,
|
|
45
|
+
body: string,
|
|
46
|
+
description: string,
|
|
47
|
+
model: string | undefined,
|
|
48
|
+
): string {
|
|
49
|
+
const front = [
|
|
50
|
+
"---",
|
|
51
|
+
`name: zero-${phase}`,
|
|
52
|
+
`description: ${description || `zero SDD ${phase} phase`}`,
|
|
53
|
+
];
|
|
54
|
+
if (model) front.push(`model: ${model}`);
|
|
55
|
+
front.push(
|
|
56
|
+
"systemPromptMode: replace",
|
|
57
|
+
"inheritProjectContext: true",
|
|
58
|
+
"inheritSkills: false",
|
|
59
|
+
"---",
|
|
60
|
+
);
|
|
61
|
+
return `${front.join("\n")}\n\n${body}\n`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve the agent `model:` value for a phase from a parsed `zero.json`.
|
|
66
|
+
*
|
|
67
|
+
* zero.json stores a per-phase model as `"<model>"` or, in the installer's
|
|
68
|
+
* spec format, `"<model> <effort>"` — the agent `model:` field takes no effort
|
|
69
|
+
* suffix, so only the model token is kept. When `/zero-models` recorded the
|
|
70
|
+
* provider separately, it is folded back into a `provider/model` path. Returns
|
|
71
|
+
* `undefined` when no model is configured. Exported for tests.
|
|
72
|
+
*/
|
|
73
|
+
export function phaseModel(data: unknown, phase: Phase): string | undefined {
|
|
74
|
+
if (!data || typeof data !== "object") return undefined;
|
|
75
|
+
const d = data as {
|
|
76
|
+
models?: Record<string, unknown>;
|
|
77
|
+
providers?: Record<string, unknown>;
|
|
78
|
+
};
|
|
79
|
+
const raw = d.models?.[phase];
|
|
80
|
+
if (typeof raw !== "string" || raw.trim() === "") return undefined;
|
|
81
|
+
|
|
82
|
+
let model = raw.trim().split(/\s+/)[0];
|
|
83
|
+
const provider = d.providers?.[phase];
|
|
84
|
+
if (typeof provider === "string" && provider !== "" && !model.includes("/")) {
|
|
85
|
+
model = `${provider}/${model}`;
|
|
86
|
+
}
|
|
87
|
+
return model;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Read the per-phase model from `~/.pi/zero.json`; `undefined` when absent. */
|
|
91
|
+
function readPhaseModel(phase: Phase): string | undefined {
|
|
92
|
+
try {
|
|
93
|
+
return phaseModel(
|
|
94
|
+
JSON.parse(readFileSync(join(homedir(), ".pi", "zero.json"), "utf8")),
|
|
95
|
+
phase,
|
|
96
|
+
);
|
|
97
|
+
} catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The pi extension entry point. Generates the four `zero-<phase>` agent files
|
|
104
|
+
* so `/forge` has real sub-agents to delegate to. Every failure is swallowed —
|
|
105
|
+
* provisioning must never break a pi session — and one phase failing does not
|
|
106
|
+
* block the others.
|
|
107
|
+
*/
|
|
108
|
+
export default function register(_pi?: unknown): void {
|
|
109
|
+
try {
|
|
110
|
+
const here = dirname(fileURLToPath(import.meta.url)); // <pkg>/extensions
|
|
111
|
+
const phasesDir = join(here, "..", "prompts", "phases");
|
|
112
|
+
const agentsDir = join(homedir(), ".pi", "agent", "agents", "zero");
|
|
113
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
114
|
+
|
|
115
|
+
for (const phase of PHASES) {
|
|
116
|
+
try {
|
|
117
|
+
const raw = readFileSync(join(phasesDir, `${phase}.md`), "utf8");
|
|
118
|
+
const { description, body } = splitPhasePrompt(raw);
|
|
119
|
+
const file = buildAgentFile(phase, body, description, readPhaseModel(phase));
|
|
120
|
+
writeFileSync(join(agentsDir, `zero-${phase}.md`), file, "utf8");
|
|
121
|
+
} catch {
|
|
122
|
+
// A single phase failing must not block the other three.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
// Sub-agent provisioning must never break a pi session.
|
|
127
|
+
}
|
|
128
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonrocca/zero-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
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": [
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"./extensions/startup-banner.ts",
|
|
27
27
|
"./extensions/working-phrases.ts",
|
|
28
28
|
"./extensions/win-tree-kill.ts",
|
|
29
|
+
"./extensions/sdd-agents.ts",
|
|
29
30
|
"./extensions/conversation-resume.ts",
|
|
30
31
|
"./extensions/zero-models.ts",
|
|
31
32
|
"./extensions/autotune-extension.ts",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"extensions/startup-banner.ts",
|
|
41
42
|
"extensions/working-phrases.ts",
|
|
42
43
|
"extensions/win-tree-kill.ts",
|
|
44
|
+
"extensions/sdd-agents.ts",
|
|
43
45
|
"extensions/conversation-resume.ts",
|
|
44
46
|
"extensions/zero-models.ts",
|
|
45
47
|
"extensions/opencode-models.ts",
|
package/prompts/forge.md
CHANGED
|
@@ -1,34 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Run
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|