pi-dev-flow 0.52.0 → 0.52.2
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/CHANGELOG.md +12 -0
- package/README.md +5 -1
- package/banner.png +0 -0
- package/banner.svg +52 -0
- package/extensions/dev-flow.ts +11 -0
- package/package.json +26 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ Format: newest first. `Unreleased` collects what's merged but not yet tagged.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.52.2] - 2026-10-21
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **The `[dev-flow]` stage line printed twice per prompt.** One package reaches one runtime twice as fast as you install it — a project `git:` pin beside the global `npm:` package, or a dogfood checkout whose `.pi/extensions` pointer sits next to the installed copy — and pi dedupes extensions by **file path**, not by package, so both copies register and both answers were appended: two `[dev-flow]` lines in every prompt, in the logs and to the model. The stage-context handler now claims the run: pi builds **one ctx object per `before_agent_start`** and hands it to every registered handler, so the first pass stamps it and a rival copy answers nothing. The claim is per run rather than per process on purpose — pi-web hosts many sessions inside one Node process (dev-flow's own session map is keyed by session id for that reason), and a process-wide "only the first copy registers" rule would leave every later session without guards, without the `flow` tool and without stage context. Each copy still registers its guards, tool and command: their verdicts are identical, and pi exposes no runtime identity during load to dedupe them against.
|
|
12
|
+
|
|
13
|
+
## [0.52.1] - 2026-10-21
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **The npm listing describes the flow instead of the pipeline.** The pi.dev package gallery builds its cards from name + description + keywords — its search box matches exactly that text, and the page renders no images — so the manifest now carries a description that says what the flow *is* (gated checkpoints, self-improvement reflections, one worktree per feature, changelog + publish as part of the release) plus thirteen keywords in the shape the other pi packages use. It also ships a banner (`banner.svg`, rasterized to `banner.png`, declared as `pi.image`): the five stages with the four checkpoint words over the gates, and the release stage pointing at "registry · image · release page" — deliberately generic, because where a release ships is the project's playbook, not dev-flow's. The package now names its resource directories explicitly under `pi` rather than relying on conventional discovery (the packed tarball was loaded against the real pi binary).
|
|
18
|
+
|
|
7
19
|
## [0.52.0] - 2026-10-21
|
|
8
20
|
|
|
9
21
|
### Added
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# dev-flow
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
An enforced feature workflow for [Pi](https://pi.dev), shipped as a pi package. You say what you
|
|
4
6
|
want; an agent builds it; **the harness — not the agent's good manners — decides whether a stage
|
|
5
7
|
may advance.** One feature at a time, five stages, and four questions asked of you —
|
|
@@ -48,7 +50,9 @@ pi install ./pi-dev-flow # or a local checkout
|
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
Later releases arrive with `pi update --extensions` (an npm install reconciles to the newest version);
|
|
51
|
-
then `/reload` — a running session keeps the code it loaded at startup.
|
|
53
|
+
then `/reload` — a running session keeps the code it loaded at startup. Two sources at once (a project
|
|
54
|
+
`git:` pin beside the global `npm:` package) load two copies — pi dedupes extensions by path, not by
|
|
55
|
+
package — so keep the one you actually update from.
|
|
52
56
|
|
|
53
57
|
Then, once per project repo, `/flow init`. It reads the repo (default branch and forge from git,
|
|
54
58
|
package manager, test/build/run conventions, version manifest) and writes **`.dev-flow/project.md`
|
package/banner.png
ADDED
|
Binary file
|
package/banner.svg
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">
|
|
2
|
+
<rect width="1280" height="640" rx="18" fill="#0f1216"/>
|
|
3
|
+
<text x="60" y="112" fill="#e6edf3" font-size="46" font-weight="700">dev-flow</text>
|
|
4
|
+
<text x="60" y="156" fill="#8b98a9" font-size="21">enforced feature workflow for pi — one flow, one branch, one worktree per feature</text>
|
|
5
|
+
|
|
6
|
+
<!-- gates (checkpoint words the operator answers in chat) -->
|
|
7
|
+
<g fill="none" stroke="#f0b429" stroke-width="2">
|
|
8
|
+
<path d="M 260 236 l 14 14 l -14 14 l -14 -14 z"/>
|
|
9
|
+
<path d="M 500 236 l 14 14 l -14 14 l -14 -14 z"/>
|
|
10
|
+
<path d="M 740 236 l 14 14 l -14 14 l -14 -14 z"/>
|
|
11
|
+
<path d="M 980 236 l 14 14 l -14 14 l -14 -14 z"/>
|
|
12
|
+
</g>
|
|
13
|
+
<g fill="#f0b429" font-size="17" text-anchor="middle">
|
|
14
|
+
<text x="260" y="212">Approve?</text>
|
|
15
|
+
<text x="500" y="212">Verified?</text>
|
|
16
|
+
<text x="740" y="212">Release?</text>
|
|
17
|
+
<text x="980" y="212">Finish?</text>
|
|
18
|
+
</g>
|
|
19
|
+
|
|
20
|
+
<!-- stages -->
|
|
21
|
+
<g font-size="19" text-anchor="middle">
|
|
22
|
+
<rect x="60" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
|
|
23
|
+
<text x="160" y="308" fill="#e6edf3">plan</text>
|
|
24
|
+
<rect x="300" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
|
|
25
|
+
<text x="400" y="308" fill="#e6edf3">implement</text>
|
|
26
|
+
<rect x="540" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
|
|
27
|
+
<text x="640" y="308" fill="#e6edf3">pre-release</text>
|
|
28
|
+
<rect x="780" y="270" width="200" height="62" rx="10" fill="#12261f" stroke="#3fb950"/>
|
|
29
|
+
<text x="880" y="308" fill="#7ee2a3">release</text>
|
|
30
|
+
<rect x="1020" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
|
|
31
|
+
<text x="1120" y="308" fill="#e6edf3">done</text>
|
|
32
|
+
</g>
|
|
33
|
+
<g stroke="#4b5563" stroke-width="2" fill="#4b5563">
|
|
34
|
+
<line x1="260" y1="301" x2="296" y2="301"/><path d="M 296 301 l -8 -5 v 10 z"/>
|
|
35
|
+
<line x1="500" y1="301" x2="536" y2="301"/><path d="M 536 301 l -8 -5 v 10 z"/>
|
|
36
|
+
<line x1="740" y1="301" x2="776" y2="301"/><path d="M 776 301 l -8 -5 v 10 z"/>
|
|
37
|
+
<line x1="980" y1="301" x2="1016" y2="301"/><path d="M 1016 301 l -8 -5 v 10 z"/>
|
|
38
|
+
</g>
|
|
39
|
+
|
|
40
|
+
<text x="60" y="404" fill="#8b98a9" font-size="18">each gate refuses until its exit criteria hold — plan, checks, deploy, changelog, manifest, tag</text>
|
|
41
|
+
|
|
42
|
+
<!-- publishing hangs off the release stage: where it goes is the project's playbook, not dev-flow's -->
|
|
43
|
+
<g font-size="17">
|
|
44
|
+
<line x1="880" y1="332" x2="880" y2="444" stroke="#3fb950" stroke-width="2"/>
|
|
45
|
+
<path d="M 880 444 l -5 -8 h 10 z" fill="#3fb950"/>
|
|
46
|
+
<rect x="620" y="452" width="600" height="82" rx="10" fill="#12261f" stroke="#3fb950"/>
|
|
47
|
+
<text x="920" y="484" fill="#7ee2a3" text-anchor="middle">the release ships: registry · image · release page</text>
|
|
48
|
+
<text x="920" y="512" fill="#8b98a9" text-anchor="middle" font-size="15">release-done refuses to close while the artifact is missing</text>
|
|
49
|
+
</g>
|
|
50
|
+
|
|
51
|
+
<text x="60" y="576" fill="#5b6672" font-size="16">.dev-flow/<slug>/ — plan, state, worktree · guards keep edits inside the feature</text>
|
|
52
|
+
</svg>
|
package/extensions/dev-flow.ts
CHANGED
|
@@ -194,6 +194,9 @@ const compacting = new Set<string>();
|
|
|
194
194
|
/** Seam for the takeover's summarize call (pi's own compact()); tests replace `.run` — mutable object stands in for ESM mocking. */
|
|
195
195
|
export const compactionTakeover: { run: typeof compact } = { run: compact };
|
|
196
196
|
|
|
197
|
+
/** Marks the before_agent_start pass that already wrote this run's [dev-flow] line — see the claim in that handler. */
|
|
198
|
+
const CONTEXT_EMITTED = Symbol.for("pi-dev-flow.context-emitted");
|
|
199
|
+
|
|
197
200
|
function unfinishedStates(root: string): FlowState[] {
|
|
198
201
|
if (!existsSync(dfDir(root))) return [];
|
|
199
202
|
return readdirSync(dfDir(root), { withFileTypes: true })
|
|
@@ -1464,6 +1467,14 @@ export default function devFlow(pi: ExtensionAPI) {
|
|
|
1464
1467
|
});
|
|
1465
1468
|
|
|
1466
1469
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
1470
|
+
// One [dev-flow] line per run, whatever number of copies of this package are installed.
|
|
1471
|
+
// The same package reaches one runtime twice all the time (a project pin beside the npm install,
|
|
1472
|
+
// or this checkout's .pi/extensions pointer beside it) and pi dedupes extensions by file path
|
|
1473
|
+
// only, so both copies answer and the line lands twice in the prompt. pi hands every handler of
|
|
1474
|
+
// a run the same ctx object, so the first pass claims the run and the rival stays silent.
|
|
1475
|
+
const run = ctx as unknown as Record<symbol, boolean | undefined>;
|
|
1476
|
+
if (run[CONTEXT_EMITTED]) return undefined;
|
|
1477
|
+
run[CONTEXT_EMITTED] = true;
|
|
1467
1478
|
if (!existsSync(dfDir(ctx.cwd))) return undefined;
|
|
1468
1479
|
const cfg = loadProject(dfDir(ctx.cwd)).cfg;
|
|
1469
1480
|
const state = boundFeature(ctx.cwd, sessionKey(ctx));
|
package/package.json
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-dev-flow",
|
|
3
|
-
"version": "0.52.
|
|
4
|
-
"description": "Enforced plan → implement → pre-release → release
|
|
3
|
+
"version": "0.52.2",
|
|
4
|
+
"description": "Enforced feature workflow for Pi: plan → implement → pre-release → release, gated checkpoints, self-improvement reflections, one git worktree per feature, changelog + publish as part of the release",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
|
-
"pi-package"
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"extension",
|
|
11
|
+
"workflow",
|
|
12
|
+
"dev-flow",
|
|
13
|
+
"spec",
|
|
14
|
+
"plan",
|
|
15
|
+
"release",
|
|
16
|
+
"semver",
|
|
17
|
+
"changelog",
|
|
18
|
+
"git-worktree",
|
|
19
|
+
"guardrails"
|
|
8
20
|
],
|
|
9
21
|
"license": "MIT",
|
|
10
22
|
"repository": "https://github.com/dmarchevsky/pi-dev-flow",
|
|
@@ -16,10 +28,21 @@
|
|
|
16
28
|
"src/",
|
|
17
29
|
"skills/",
|
|
18
30
|
"docs/",
|
|
31
|
+
"banner.svg",
|
|
32
|
+
"banner.png",
|
|
19
33
|
"README.md",
|
|
20
34
|
"CHANGELOG.md",
|
|
21
35
|
"LICENSE"
|
|
22
36
|
],
|
|
37
|
+
"pi": {
|
|
38
|
+
"extensions": [
|
|
39
|
+
"./extensions/dev-flow.ts"
|
|
40
|
+
],
|
|
41
|
+
"skills": [
|
|
42
|
+
"./skills"
|
|
43
|
+
],
|
|
44
|
+
"image": "./banner.png"
|
|
45
|
+
},
|
|
23
46
|
"peerDependencies": {
|
|
24
47
|
"@earendil-works/pi-ai": "*",
|
|
25
48
|
"@earendil-works/pi-coding-agent": "*",
|