pi-bmad-flow 0.1.0
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 +111 -0
- package/docs/install-model.md +64 -0
- package/docs/workflow-wireframe.md +122 -0
- package/extensions/bmad-orchestrator/commands.ts +238 -0
- package/extensions/bmad-orchestrator/detector.ts +168 -0
- package/extensions/bmad-orchestrator/files.ts +49 -0
- package/extensions/bmad-orchestrator/gates.ts +70 -0
- package/extensions/bmad-orchestrator/index.ts +131 -0
- package/extensions/bmad-orchestrator/packets.ts +172 -0
- package/extensions/bmad-orchestrator/router.ts +79 -0
- package/extensions/bmad-orchestrator/sprint.ts +82 -0
- package/extensions/bmad-orchestrator/tool.ts +78 -0
- package/extensions/bmad-orchestrator/types.ts +96 -0
- package/extensions/bmad-orchestrator/ui.ts +19 -0
- package/fixtures/sample-bmad-project/_bmad/_config/manifest.yaml +5 -0
- package/fixtures/sample-bmad-project/_bmad/bmm/README.md +1 -0
- package/fixtures/sample-bmad-project/_bmad/core/README.md +1 -0
- package/fixtures/sample-bmad-project/_bmad/tea/README.md +1 -0
- package/fixtures/sample-bmad-project/_bmad/wds/README.md +1 -0
- package/fixtures/sample-bmad-project/_bmad-output/implementation-artifacts/sprint-status.yaml +12 -0
- package/fixtures/sample-bmad-project/_bmad-output/implementation-artifacts/stories/1-2-user-authentication-dashboard.md +26 -0
- package/fixtures/sample-bmad-project/_bmad-output/planning-artifacts/architecture.md +7 -0
- package/fixtures/sample-bmad-project/_bmad-output/planning-artifacts/epic-1-auth-dashboard.md +7 -0
- package/fixtures/sample-bmad-project/_bmad-output/planning-artifacts/prd.md +6 -0
- package/fixtures/sample-bmad-project/_bmad-output/test-artifacts/nfr-assessment.md +7 -0
- package/fixtures/sample-bmad-project/_bmad-output/test-artifacts/test-review.md +6 -0
- package/fixtures/sample-bmad-project/_bmad-output/test-artifacts/traceability-matrix.md +5 -0
- package/fixtures/sample-bmad-project/design-artifacts/C-UX-Scenarios/01-user-onboarding/00-scenario-overview.md +5 -0
- package/fixtures/sample-bmad-project/design-artifacts/C-UX-Scenarios/01-user-onboarding/1.2-authentication-dashboard/Frontend/specifications.md +12 -0
- package/fixtures/sample-bmad-project/design-artifacts/D-Design-System/components/auth-form.md +3 -0
- package/fixtures/sample-bmad-project/design-artifacts/D-Design-System/components/dashboard-primary-button.md +3 -0
- package/fixtures/sample-bmad-project/design-artifacts/D-Design-System/design-tokens.md +5 -0
- package/fixtures/sample-bmad-project/design-artifacts/E-PRD/Design-Deliveries/DD-001-auth-dashboard.yaml +17 -0
- package/fixtures/sample-bmad-project/design-artifacts/_progress/00-design-log.md +5 -0
- package/fixtures/sample-bmad-project/docs/project-context.md +5 -0
- package/package.json +36 -0
- package/scripts/audit-project.mjs +266 -0
- package/scripts/bootstrap.mjs +108 -0
- package/scripts/check.mjs +64 -0
- package/scripts/fixture-smoke.mjs +55 -0
- package/skills/bmad-phase-handoff/SKILL.md +13 -0
- package/skills/bmad-story-full/SKILL.md +20 -0
- package/skills/bmad-story-lean/SKILL.md +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# pi-bmad-flow
|
|
2
|
+
|
|
3
|
+
Pi-native orchestration overlay for BMAD.
|
|
4
|
+
|
|
5
|
+
This package is designed to run on top of:
|
|
6
|
+
|
|
7
|
+
1. upstream `pi`
|
|
8
|
+
2. official BMAD install targeting `pi`
|
|
9
|
+
|
|
10
|
+
It adds a thin control plane for phase routing and story-loop automation.
|
|
11
|
+
|
|
12
|
+
## Install in a BMAD project
|
|
13
|
+
|
|
14
|
+
Install Pi and BMAD first, then install this package into the project:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @mariozechner/pi-coding-agent
|
|
18
|
+
cd /path/to/project
|
|
19
|
+
npx bmad-method install
|
|
20
|
+
pi install -l npm:pi-bmad-flow```
|
|
21
|
+
|
|
22
|
+
For early access builds before npm publish, install from git or a local path:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pi install -l git:github.com/<your-org>/pi-bmad-flow
|
|
26
|
+
pi install -l /absolute/path/to/pi-bmad-flow
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
After install, open `pi` in the project and run:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
/bmad-status
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Recommended BMAD modules for this overlay:
|
|
36
|
+
|
|
37
|
+
- core
|
|
38
|
+
- bmm
|
|
39
|
+
- cis
|
|
40
|
+
- wds
|
|
41
|
+
- tea
|
|
42
|
+
- bmb
|
|
43
|
+
|
|
44
|
+
Package docs:
|
|
45
|
+
|
|
46
|
+
- [`docs/install-model.md`](./docs/install-model.md)
|
|
47
|
+
- [`docs/workflow-wireframe.md`](./docs/workflow-wireframe.md)
|
|
48
|
+
|
|
49
|
+
## Publish
|
|
50
|
+
|
|
51
|
+
From `pi-bmad-flow/`:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm run check
|
|
55
|
+
npm run smoke:fixture
|
|
56
|
+
npm publish --access public
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`prepublishOnly` also runs package validation automatically before publish.
|
|
60
|
+
|
|
61
|
+
If `pi-bmad-flow` is already taken on npm, choose another unscoped name in `package.json`, run `npm view <name>` to confirm availability, and publish that name instead.
|
|
62
|
+
|
|
63
|
+
## Validate against a project
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm run audit:project -- --project-dir /absolute/path/to/project
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Expected project layout
|
|
70
|
+
|
|
71
|
+
The extension expects standard BMAD folders:
|
|
72
|
+
|
|
73
|
+
- `_bmad/`
|
|
74
|
+
- `_bmad-output/planning-artifacts/`
|
|
75
|
+
- `_bmad-output/implementation-artifacts/`
|
|
76
|
+
|
|
77
|
+
Optional:
|
|
78
|
+
|
|
79
|
+
- `design-artifacts/`
|
|
80
|
+
- `docs/`
|
|
81
|
+
|
|
82
|
+
## Commands
|
|
83
|
+
|
|
84
|
+
- `/bmad-next`
|
|
85
|
+
- `/bmad-start`
|
|
86
|
+
- `/bmad-review`
|
|
87
|
+
- `/bmad-gate`
|
|
88
|
+
- `/bmad-status`
|
|
89
|
+
- `/bmad-phase`
|
|
90
|
+
|
|
91
|
+
## Current scope
|
|
92
|
+
|
|
93
|
+
This overlay keeps BMAD adaptation inside Pi-native runtime features instead of prompt wrappers.
|
|
94
|
+
|
|
95
|
+
Current behavior:
|
|
96
|
+
|
|
97
|
+
- BMAD detection
|
|
98
|
+
- module detection
|
|
99
|
+
- artifact-aware phase detection
|
|
100
|
+
- sprint status parsing
|
|
101
|
+
- next-step routing
|
|
102
|
+
- plain-English BMAD intent routing through Pi input interception
|
|
103
|
+
- dedicated story sessions created by `/bmad-start`
|
|
104
|
+
- lean versus full packet selection
|
|
105
|
+
- WDS-aware packet assembly for UI stories
|
|
106
|
+
- TEA-aware review and gate routing
|
|
107
|
+
- BMAD-specific compaction summaries
|
|
108
|
+
- lightweight BMAD status widget and footer state
|
|
109
|
+
- optional LLM access to deterministic BMAD state through the `bmad_orchestrator` tool
|
|
110
|
+
|
|
111
|
+
This package does not replace official BMAD workflows. It reduces routing overhead around them.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Install Model
|
|
2
|
+
|
|
3
|
+
`pi-bmad-flow` is a thin overlay. It should not replace either `pi` or BMAD.
|
|
4
|
+
|
|
5
|
+
## Recommended install order
|
|
6
|
+
|
|
7
|
+
1. Install upstream `pi`
|
|
8
|
+
2. Install official BMAD into the project
|
|
9
|
+
3. Install `pi-bmad-flow` project-locally
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @mariozechner/pi-coding-agent
|
|
13
|
+
cd /path/to/project
|
|
14
|
+
npx bmad-method install
|
|
15
|
+
pi install -l /absolute/path/to/pi-bmad-flow
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## BMAD installer choices
|
|
19
|
+
|
|
20
|
+
Use the official BMAD installer and keep your existing module selection:
|
|
21
|
+
|
|
22
|
+
- `BMad Core Module`
|
|
23
|
+
- `BMad Method Agile-AI Driven-Development`
|
|
24
|
+
- `BMad Builder`
|
|
25
|
+
- `BMad Creative Intelligence Suite`
|
|
26
|
+
- `Test Architect`
|
|
27
|
+
- `Whiteport Design Studio`
|
|
28
|
+
|
|
29
|
+
That gives you the official artifact model under `_bmad/` and `_bmad-output/`. `pi-bmad-flow` reads that structure and adds orchestration on top.
|
|
30
|
+
|
|
31
|
+
## Why this install shape is correct
|
|
32
|
+
|
|
33
|
+
- Upstream `pi` stays updateable without maintaining a fork.
|
|
34
|
+
- Official BMAD stays updateable without maintaining a fork.
|
|
35
|
+
- Your custom behavior lives in one overlay package that can evolve independently.
|
|
36
|
+
- If BMAD changes, the overlay can adapt at the routing layer instead of forcing a reinstall strategy rewrite.
|
|
37
|
+
|
|
38
|
+
## Day-to-day usage
|
|
39
|
+
|
|
40
|
+
Inside a BMAD project:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pi
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then use:
|
|
47
|
+
|
|
48
|
+
- `/bmad-status`
|
|
49
|
+
- `/bmad-next`
|
|
50
|
+
- `/bmad-start`
|
|
51
|
+
- `/bmad-review`
|
|
52
|
+
- `/bmad-gate`
|
|
53
|
+
|
|
54
|
+
`/bmad-start` should be the normal entry into implementation. It creates a dedicated Pi story session, attaches lean or full packet guidance, and keeps the planning session clean.
|
|
55
|
+
|
|
56
|
+
## Distribution model for your version
|
|
57
|
+
|
|
58
|
+
Your install story should become:
|
|
59
|
+
|
|
60
|
+
1. User installs upstream `pi`
|
|
61
|
+
2. User runs official BMAD installer with the module set you standardize
|
|
62
|
+
3. User installs your overlay package
|
|
63
|
+
|
|
64
|
+
Later, you can wrap this with a bootstrap script, but the bootstrap should still execute the same three layers under the hood. Do not hard-fork BMAD unless upstream compatibility stops mattering.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Workflow Wireframe
|
|
2
|
+
|
|
3
|
+
This is the Pi-native BMAD operating loop optimized for low token usage, high reliability, and minimal manual routing.
|
|
4
|
+
|
|
5
|
+
## Operating model
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Install Project
|
|
9
|
+
-> official BMAD install
|
|
10
|
+
-> install pi-bmad-flow overlay
|
|
11
|
+
-> start pi
|
|
12
|
+
|
|
13
|
+
Project Start
|
|
14
|
+
-> /bmad-status
|
|
15
|
+
-> /bmad-next
|
|
16
|
+
-> overlay identifies missing phase artifact
|
|
17
|
+
|
|
18
|
+
Planning Loop
|
|
19
|
+
-> create PRD
|
|
20
|
+
-> create UX/WDS artifacts
|
|
21
|
+
-> create architecture
|
|
22
|
+
-> create epics and stories
|
|
23
|
+
-> run implementation-readiness gate
|
|
24
|
+
|
|
25
|
+
Delivery Loop
|
|
26
|
+
-> /bmad-start
|
|
27
|
+
-> overlay selects next ready story
|
|
28
|
+
-> overlay opens a dedicated Pi story session
|
|
29
|
+
-> overlay chooses lean or full packet
|
|
30
|
+
-> BMAD dev workflow executes story inside that story session
|
|
31
|
+
-> /bmad-review
|
|
32
|
+
-> /bmad-gate
|
|
33
|
+
-> story moves forward in sprint-status.yaml
|
|
34
|
+
|
|
35
|
+
Epic Completion
|
|
36
|
+
-> retrospective
|
|
37
|
+
-> next epic
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Human role
|
|
41
|
+
|
|
42
|
+
You should make fewer routing decisions and more approval decisions.
|
|
43
|
+
|
|
44
|
+
Your job becomes:
|
|
45
|
+
|
|
46
|
+
- approve planning outputs
|
|
47
|
+
- approve story boundaries
|
|
48
|
+
- intervene on ambiguous tradeoffs
|
|
49
|
+
- inspect review findings when risk is high
|
|
50
|
+
|
|
51
|
+
Pi should take over:
|
|
52
|
+
|
|
53
|
+
- phase detection
|
|
54
|
+
- next-step routing
|
|
55
|
+
- story pickup
|
|
56
|
+
- lean versus full context selection
|
|
57
|
+
- story-session setup
|
|
58
|
+
- gate selection
|
|
59
|
+
|
|
60
|
+
## Recommended lane split
|
|
61
|
+
|
|
62
|
+
Use two planning lanes, but keep one delivery loop.
|
|
63
|
+
|
|
64
|
+
### Lane A: BMAD core
|
|
65
|
+
|
|
66
|
+
- `bmad-create-prd`
|
|
67
|
+
- `bmad-create-architecture`
|
|
68
|
+
- `bmad-create-epics-and-stories`
|
|
69
|
+
- `bmad-check-implementation-readiness`
|
|
70
|
+
|
|
71
|
+
### Lane B: WDS depth
|
|
72
|
+
|
|
73
|
+
- UX and scenario work
|
|
74
|
+
- page specs
|
|
75
|
+
- design delivery
|
|
76
|
+
- design system artifacts when reusable UI matters
|
|
77
|
+
|
|
78
|
+
The overlay should treat WDS artifacts as implementation inputs, not parallel decoration. That means UI stories should pull WDS page specs into the packet automatically when present.
|
|
79
|
+
|
|
80
|
+
## Token minimization strategy
|
|
81
|
+
|
|
82
|
+
Default to lean packets.
|
|
83
|
+
|
|
84
|
+
Use full packets only when:
|
|
85
|
+
|
|
86
|
+
- story is security, auth, billing, migration, infra, or public API related
|
|
87
|
+
- project-context is missing or weak
|
|
88
|
+
- architecture is still unstable
|
|
89
|
+
- story is UI-heavy and depends on multiple WDS artifacts
|
|
90
|
+
|
|
91
|
+
Lean packet should usually include:
|
|
92
|
+
|
|
93
|
+
- current story file
|
|
94
|
+
- project-context if present
|
|
95
|
+
- relevant architecture section
|
|
96
|
+
- relevant WDS or UX file only if the story touches UI
|
|
97
|
+
|
|
98
|
+
Full packet adds:
|
|
99
|
+
|
|
100
|
+
- PRD context
|
|
101
|
+
- prior review or retrospective notes
|
|
102
|
+
- additional architecture and WDS support files
|
|
103
|
+
|
|
104
|
+
## Reliability rules
|
|
105
|
+
|
|
106
|
+
- Never begin dev without a story file.
|
|
107
|
+
- Never start a new story while another is `in-progress`.
|
|
108
|
+
- Never skip architecture for medium or large work.
|
|
109
|
+
- Never treat WDS outputs as optional for UI-heavy stories.
|
|
110
|
+
- Never let operational session metadata become the source of truth over BMAD artifacts.
|
|
111
|
+
|
|
112
|
+
## Best install experience for your eventual distribution
|
|
113
|
+
|
|
114
|
+
The cleanest user flow is:
|
|
115
|
+
|
|
116
|
+
1. `npx bmad-method install`
|
|
117
|
+
2. choose your standard BMAD module set
|
|
118
|
+
3. `pi install -l your-overlay-package`
|
|
119
|
+
4. open `pi`
|
|
120
|
+
5. run `/bmad-status`
|
|
121
|
+
|
|
122
|
+
That keeps installation simple and preserves upstream compatibility.
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { detectBmadInstall, detectBmadProjectState } from "./detector.js";
|
|
3
|
+
import { decideGate } from "./gates.js";
|
|
4
|
+
import { buildStoryPacket, packetHint, selectPacketMode } from "./packets.js";
|
|
5
|
+
import { decideNextAction } from "./router.js";
|
|
6
|
+
import { updateStoryStatus } from "./sprint.js";
|
|
7
|
+
import type { GatePlan, StoryPacket } from "./types.js";
|
|
8
|
+
import { refreshBmadUi } from "./ui.js";
|
|
9
|
+
|
|
10
|
+
function now(): string {
|
|
11
|
+
return new Date().toISOString();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function appendBmadState(
|
|
15
|
+
pi: ExtensionAPI,
|
|
16
|
+
payload: {
|
|
17
|
+
event: "status" | "next" | "story-start" | "review" | "gate";
|
|
18
|
+
storyKey?: string;
|
|
19
|
+
packetMode?: "lean" | "full";
|
|
20
|
+
nextCommand?: string;
|
|
21
|
+
gateLevel?: "lightweight" | "strong";
|
|
22
|
+
files?: string[];
|
|
23
|
+
},
|
|
24
|
+
): void {
|
|
25
|
+
pi.appendEntry("bmad-state", {
|
|
26
|
+
...payload,
|
|
27
|
+
generatedAt: now(),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatPacketLaunchMessage(story: string, packet: StoryPacket): string {
|
|
32
|
+
const lines = [
|
|
33
|
+
`You are now in the dedicated story session for ${story}.`,
|
|
34
|
+
"",
|
|
35
|
+
`Start implementation with packet mode: ${packet.mode}`,
|
|
36
|
+
...packet.notes.map((note) => `- ${note}`),
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
if (packet.files.length > 0) {
|
|
40
|
+
lines.push("", "Load these files first:");
|
|
41
|
+
lines.push(...packet.files.map((file) => `- ${file}`));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
lines.push("", `Then run the BMAD delivery workflow for ${story} and keep this session focused on that story only.`);
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatGateMessage(plan: GatePlan): string {
|
|
49
|
+
const lines = [
|
|
50
|
+
`Gate level: ${plan.level}`,
|
|
51
|
+
`Reason: ${plan.reason}`,
|
|
52
|
+
"",
|
|
53
|
+
"Run these workflows in order:",
|
|
54
|
+
...plan.commands.map((command, index) => `${index + 1}. ${command}`),
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
if (plan.evidenceFiles.length > 0) {
|
|
58
|
+
lines.push("", "Available TEA evidence:");
|
|
59
|
+
lines.push(...plan.evidenceFiles.map((file) => `- ${file}`));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return lines.join("\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function registerBmadCommands(pi: ExtensionAPI, cwd: string): void {
|
|
66
|
+
pi.registerCommand("bmad-status", {
|
|
67
|
+
description: "Show BMAD install, phase, and story queue status",
|
|
68
|
+
handler: async (_args, ctx) => {
|
|
69
|
+
const install = detectBmadInstall(cwd);
|
|
70
|
+
const state = detectBmadProjectState(cwd);
|
|
71
|
+
const next = decideNextAction(state);
|
|
72
|
+
const moduleText = install.modules.length > 0 ? install.modules.join(", ") : "none";
|
|
73
|
+
const planningSummary = `PRD ${state.inventory.prdFiles.length}, UX ${state.inventory.uxFiles.length}, Architecture ${state.inventory.architectureFiles.length}, Epics ${state.inventory.epicFiles.length}`;
|
|
74
|
+
const deliverySummary = `Stories ${state.inventory.storyFiles.length}, Reviews ${state.inventory.reviewFiles.length}, Sprint ${state.hasSprintStatus ? "yes" : "no"}`;
|
|
75
|
+
const designSummary = `WDS scenarios ${state.inventory.wdsScenarioFiles.length}, page specs ${state.inventory.wdsPageSpecFiles.length}, deliveries ${state.inventory.wdsDeliveryFiles.length}`;
|
|
76
|
+
const teaSummary = `TEA reviews ${state.inventory.teaReviewFiles.length}, trace ${state.inventory.teaTraceFiles.length}, NFR ${state.inventory.teaNfrFiles.length}`;
|
|
77
|
+
|
|
78
|
+
ctx.ui.notify(`BMAD installed: ${install.installed ? "yes" : "no"}`, "info");
|
|
79
|
+
ctx.ui.notify(`Modules: ${moduleText}`, "info");
|
|
80
|
+
ctx.ui.notify(`Phase: ${state.phase}`, "info");
|
|
81
|
+
ctx.ui.notify(`Planning: ${planningSummary}`, "info");
|
|
82
|
+
ctx.ui.notify(`Delivery: ${deliverySummary}`, "info");
|
|
83
|
+
ctx.ui.notify(`Design: ${designSummary}`, "info");
|
|
84
|
+
ctx.ui.notify(`TEA: ${teaSummary}`, "info");
|
|
85
|
+
ctx.ui.notify(`Next: ${next.command} - ${next.summary}`, "info");
|
|
86
|
+
|
|
87
|
+
refreshBmadUi(ctx, state, next);
|
|
88
|
+
appendBmadState(pi, { event: "status", nextCommand: next.command });
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
pi.registerCommand("bmad-phase", {
|
|
93
|
+
description: "Show current workflow phase and recommended route",
|
|
94
|
+
handler: async (_args, ctx) => {
|
|
95
|
+
const state = detectBmadProjectState(cwd);
|
|
96
|
+
const next = decideNextAction(state);
|
|
97
|
+
ctx.ui.notify(`Current phase: ${state.phase}`, "info");
|
|
98
|
+
ctx.ui.notify(
|
|
99
|
+
`Artifacts: PRD ${state.inventory.prdFiles.length}, UX ${state.inventory.uxFiles.length}, Architecture ${state.inventory.architectureFiles.length}, Stories ${state.inventory.storyFiles.length}`,
|
|
100
|
+
"info",
|
|
101
|
+
);
|
|
102
|
+
ctx.ui.notify(`Recommended: ${next.command} (${next.reason})`, "info");
|
|
103
|
+
refreshBmadUi(ctx, state, next);
|
|
104
|
+
appendBmadState(pi, { event: "status", nextCommand: next.command });
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
pi.registerCommand("bmad-next", {
|
|
109
|
+
description: "Determine next action from BMAD artifacts",
|
|
110
|
+
handler: async (_args, ctx) => {
|
|
111
|
+
const install = detectBmadInstall(cwd);
|
|
112
|
+
if (!install.installed) {
|
|
113
|
+
ctx.ui.notify("No BMAD installation detected at _bmad/_config/manifest.yaml", "warning");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const state = detectBmadProjectState(cwd);
|
|
118
|
+
const next = decideNextAction(state);
|
|
119
|
+
ctx.ui.notify(next.summary, "info");
|
|
120
|
+
ctx.ui.notify(`Recommended command: ${next.command}`, "info");
|
|
121
|
+
refreshBmadUi(ctx, state, next);
|
|
122
|
+
appendBmadState(pi, { event: "next", nextCommand: next.command });
|
|
123
|
+
|
|
124
|
+
if (!ctx.hasUI) return;
|
|
125
|
+
const shouldLaunch = await ctx.ui.confirm("Launch recommended workflow?", next.command);
|
|
126
|
+
if (shouldLaunch) {
|
|
127
|
+
pi.sendUserMessage(next.command, { source: "extension" });
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
pi.registerCommand("bmad-start", {
|
|
133
|
+
description: "Start the next ready story in a dedicated story session with lean/full packet selection",
|
|
134
|
+
handler: async (_args, ctx) => {
|
|
135
|
+
const install = detectBmadInstall(cwd);
|
|
136
|
+
const state = detectBmadProjectState(cwd);
|
|
137
|
+
|
|
138
|
+
if (!install.installed || !state.hasSprintStatus) {
|
|
139
|
+
ctx.ui.notify("BMAD sprint status not found. Run bmad-sprint-planning first.", "warning");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const story = state.nextReadyStory;
|
|
144
|
+
if (!story) {
|
|
145
|
+
ctx.ui.notify("No ready-for-dev story found. Run bmad-create-story first.", "warning");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const mode = selectPacketMode(state);
|
|
150
|
+
const packet = buildStoryPacket(state, story, mode);
|
|
151
|
+
const statusUpdated = updateStoryStatus(install.paths.sprintStatusPath, story, "in-progress");
|
|
152
|
+
if (!statusUpdated) {
|
|
153
|
+
ctx.ui.notify(`Could not update sprint status for ${story}.`, "warning");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const originLeafId = ctx.sessionManager.getLeafId();
|
|
157
|
+
if (originLeafId) {
|
|
158
|
+
pi.setLabel(originLeafId, `handoff:${story}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await ctx.waitForIdle();
|
|
162
|
+
const sessionResult = await ctx.newSession({
|
|
163
|
+
parentSession: ctx.sessionManager.getSessionFile(),
|
|
164
|
+
});
|
|
165
|
+
if (sessionResult.cancelled) {
|
|
166
|
+
ctx.ui.notify("Story session creation was cancelled.", "warning");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
pi.setSessionName(`Story ${story}`);
|
|
171
|
+
appendBmadState(pi, {
|
|
172
|
+
event: "story-start",
|
|
173
|
+
storyKey: story,
|
|
174
|
+
packetMode: mode,
|
|
175
|
+
files: packet.files,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
ctx.ui.notify(`Opened story session for ${story}`, "info");
|
|
179
|
+
ctx.ui.notify(packetHint(mode), "info");
|
|
180
|
+
if (packet.uiRelevant) {
|
|
181
|
+
ctx.ui.notify("WDS artifacts were added because this appears to be a UI-facing story.", "info");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const refreshedState = detectBmadProjectState(cwd);
|
|
185
|
+
const refreshedNext = decideNextAction(refreshedState);
|
|
186
|
+
refreshBmadUi(ctx, refreshedState, refreshedNext);
|
|
187
|
+
pi.sendUserMessage(formatPacketLaunchMessage(story, packet), { source: "extension" });
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
pi.registerCommand("bmad-review", {
|
|
192
|
+
description: "Run review workflow for active story",
|
|
193
|
+
handler: async (_args, ctx) => {
|
|
194
|
+
const state = detectBmadProjectState(cwd);
|
|
195
|
+
const story = state.activeStory ?? state.nextReadyStory;
|
|
196
|
+
if (!story) {
|
|
197
|
+
ctx.ui.notify("No active or ready story found to review.", "warning");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
ctx.ui.notify(`Running review for ${story}`, "info");
|
|
202
|
+
appendBmadState(pi, { event: "review", storyKey: story });
|
|
203
|
+
refreshBmadUi(ctx, state, decideNextAction(state));
|
|
204
|
+
pi.sendUserMessage(`bmad-code-review ${story}`, { source: "extension" });
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
pi.registerCommand("bmad-gate", {
|
|
209
|
+
description: "Run lightweight or TEA quality gate based on risk",
|
|
210
|
+
handler: async (_args, ctx) => {
|
|
211
|
+
const install = detectBmadInstall(cwd);
|
|
212
|
+
const state = detectBmadProjectState(cwd);
|
|
213
|
+
const story = state.activeStory ?? state.nextReadyStory;
|
|
214
|
+
if (!story) {
|
|
215
|
+
ctx.ui.notify("No active or ready story found to gate.", "warning");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const decision = decideGate(state, install.modules.includes("tea"));
|
|
219
|
+
const plan: GatePlan = {
|
|
220
|
+
storyKey: story,
|
|
221
|
+
level: decision.level,
|
|
222
|
+
reason: decision.reason,
|
|
223
|
+
commands: decision.commands,
|
|
224
|
+
evidenceFiles: decision.evidenceFiles,
|
|
225
|
+
generatedAt: now(),
|
|
226
|
+
};
|
|
227
|
+
ctx.ui.notify(`Gate: ${decision.level}`, "info");
|
|
228
|
+
ctx.ui.notify(decision.reason, "info");
|
|
229
|
+
ctx.ui.notify(`Commands: ${decision.commands.length}`, "info");
|
|
230
|
+
if (decision.evidenceFiles.length > 0) {
|
|
231
|
+
ctx.ui.notify(`Evidence files: ${decision.evidenceFiles.length}`, "info");
|
|
232
|
+
}
|
|
233
|
+
appendBmadState(pi, { event: "gate", storyKey: story, gateLevel: decision.level });
|
|
234
|
+
refreshBmadUi(ctx, state, decideNextAction(state));
|
|
235
|
+
pi.sendUserMessage(formatGateMessage(plan), { source: "extension" });
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { collectMarkdownFiles, collectYamlFiles } from "./files.js";
|
|
4
|
+
import { loadSprintStatus, resolveStoryLocation } from "./sprint.js";
|
|
5
|
+
import type { ArtifactInventory, BmadInstallState, BmadPaths, BmadPhase, BmadProjectState } from "./types.js";
|
|
6
|
+
|
|
7
|
+
function matches(files: string[], pattern: RegExp): string[] {
|
|
8
|
+
return files.filter((file) => pattern.test(file.toLowerCase()));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function unique(files: string[]): string[] {
|
|
12
|
+
return Array.from(new Set(files)).sort();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function collectStructuredFiles(dirPath: string): string[] {
|
|
16
|
+
return unique([...collectMarkdownFiles(dirPath), ...collectYamlFiles(dirPath)]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function detectInventory(paths: BmadPaths): ArtifactInventory {
|
|
20
|
+
const sprint = loadSprintStatus(paths.sprintStatusPath);
|
|
21
|
+
const storyLocationDir = resolveStoryLocation(paths.sprintStatusPath, sprint);
|
|
22
|
+
const planningFiles = collectMarkdownFiles(paths.planningArtifactsDir);
|
|
23
|
+
const implementationFiles = collectMarkdownFiles(paths.implementationArtifactsDir);
|
|
24
|
+
const storyLocationFiles =
|
|
25
|
+
storyLocationDir !== paths.implementationArtifactsDir ? collectMarkdownFiles(storyLocationDir) : implementationFiles;
|
|
26
|
+
const designFiles = collectStructuredFiles(paths.designArtifactsDir);
|
|
27
|
+
const docsFiles = collectStructuredFiles(paths.docsDir);
|
|
28
|
+
const teaFiles = unique([
|
|
29
|
+
...collectStructuredFiles(paths.teaArtifactsDir),
|
|
30
|
+
...collectStructuredFiles(join(paths.root, "test-artifacts")),
|
|
31
|
+
]);
|
|
32
|
+
const allContextFiles = unique([
|
|
33
|
+
...planningFiles,
|
|
34
|
+
...implementationFiles,
|
|
35
|
+
...storyLocationFiles,
|
|
36
|
+
...designFiles,
|
|
37
|
+
...docsFiles,
|
|
38
|
+
...teaFiles,
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const wdsScenarioFiles = matches(allContextFiles, /\/c-ux-scenarios\//);
|
|
42
|
+
const wdsPageSpecFiles = matches(
|
|
43
|
+
allContextFiles,
|
|
44
|
+
/\/c-ux-scenarios\/.*(specifications\.md|page-spec|page-specification|\/frontend\/.*\.md)/,
|
|
45
|
+
);
|
|
46
|
+
const wdsStoryboardFiles = matches(allContextFiles, /storyboard/);
|
|
47
|
+
const wdsDeliveryFiles = matches(allContextFiles, /(design-deliver(y|ies)|\/deliveries\/|dd-\d+)/);
|
|
48
|
+
const wdsDesignSystemFiles = matches(allContextFiles, /\/d-design-system\//);
|
|
49
|
+
const wdsProgressFiles = matches(allContextFiles, /\/_progress\/00-design-log\.md$|scenario-tracking\.ya?ml|validation-report\.md/);
|
|
50
|
+
const teaTraceFiles = matches(allContextFiles, /traceability|gate-decision/);
|
|
51
|
+
const teaReviewFiles = matches(allContextFiles, /test-review/);
|
|
52
|
+
const teaNfrFiles = matches(allContextFiles, /nfr-assessment|nfr-report/);
|
|
53
|
+
const teaAutomationFiles = matches(allContextFiles, /automation-summary|atdd-checklist/);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
planningFiles,
|
|
57
|
+
implementationFiles,
|
|
58
|
+
designFiles,
|
|
59
|
+
docsFiles,
|
|
60
|
+
teaFiles,
|
|
61
|
+
prdFiles: matches(allContextFiles, /(^|\/)(prd|.*prd.*)\.md$/),
|
|
62
|
+
uxFiles: matches(
|
|
63
|
+
allContextFiles,
|
|
64
|
+
/(ux|wds-|wireframe|storyboard|page-spec|page-specification|design-delivery|scenario)/,
|
|
65
|
+
),
|
|
66
|
+
architectureFiles: matches(allContextFiles, /architecture|adr/),
|
|
67
|
+
epicFiles: matches(allContextFiles, /(^|\/)epics?\/|epic-/),
|
|
68
|
+
storyFiles: matches(allContextFiles, /(^|\/)story-.*\.md$/),
|
|
69
|
+
reviewFiles: matches(allContextFiles, /review|retrospective|readiness-report|validation-report/),
|
|
70
|
+
projectContextFiles: matches(allContextFiles, /project-context/),
|
|
71
|
+
wdsScenarioFiles,
|
|
72
|
+
wdsPageSpecFiles,
|
|
73
|
+
wdsStoryboardFiles,
|
|
74
|
+
wdsDeliveryFiles,
|
|
75
|
+
wdsDesignSystemFiles,
|
|
76
|
+
wdsProgressFiles,
|
|
77
|
+
teaTraceFiles,
|
|
78
|
+
teaReviewFiles,
|
|
79
|
+
teaNfrFiles,
|
|
80
|
+
teaAutomationFiles,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function detectPhase(inventory: ArtifactInventory, hasSprintStatus: boolean): BmadPhase {
|
|
85
|
+
if (inventory.prdFiles.length === 0 && inventory.uxFiles.length === 0 && inventory.architectureFiles.length === 0) {
|
|
86
|
+
return "discovery";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if ((inventory.prdFiles.length > 0 || inventory.uxFiles.length > 0) && inventory.architectureFiles.length === 0) {
|
|
90
|
+
return "planning";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if ((inventory.architectureFiles.length > 0 || inventory.epicFiles.length > 0) && !hasSprintStatus) {
|
|
94
|
+
return "solutioning";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (hasSprintStatus && inventory.storyFiles.length === 0) {
|
|
98
|
+
return "implementation-setup";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return "delivery";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getBmadPaths(cwd: string): BmadPaths {
|
|
105
|
+
const root = cwd;
|
|
106
|
+
const bmadDir = join(root, "_bmad");
|
|
107
|
+
const planningArtifactsDir = join(root, "_bmad-output", "planning-artifacts");
|
|
108
|
+
const implementationArtifactsDir = join(root, "_bmad-output", "implementation-artifacts");
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
root,
|
|
112
|
+
bmadDir,
|
|
113
|
+
manifestPath: join(bmadDir, "_config", "manifest.yaml"),
|
|
114
|
+
planningArtifactsDir,
|
|
115
|
+
implementationArtifactsDir,
|
|
116
|
+
teaArtifactsDir: join(root, "_bmad-output", "test-artifacts"),
|
|
117
|
+
designArtifactsDir: join(root, "design-artifacts"),
|
|
118
|
+
docsDir: join(root, "docs"),
|
|
119
|
+
sprintStatusPath: join(implementationArtifactsDir, "sprint-status.yaml"),
|
|
120
|
+
extensionStateDir: join(root, ".pi", "bmad"),
|
|
121
|
+
extensionStatePath: join(root, ".pi", "bmad", "state.json"),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function detectBmadInstall(cwd: string): BmadInstallState {
|
|
126
|
+
const paths = getBmadPaths(cwd);
|
|
127
|
+
const installed = existsSync(paths.manifestPath);
|
|
128
|
+
const knownModules = ["core", "bmm", "cis", "wds", "tea", "bmb"];
|
|
129
|
+
const modules = knownModules.filter((moduleId) => existsSync(join(paths.bmadDir, moduleId)));
|
|
130
|
+
return { installed, modules, paths };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function detectBmadProjectState(cwd: string): BmadProjectState {
|
|
134
|
+
const install = detectBmadInstall(cwd);
|
|
135
|
+
const inventory = detectInventory(install.paths);
|
|
136
|
+
const hasPlanningArtifacts = inventory.planningFiles.length > 0;
|
|
137
|
+
const hasImplementationArtifacts = inventory.implementationFiles.length > 0;
|
|
138
|
+
const hasSprintStatus = existsSync(install.paths.sprintStatusPath);
|
|
139
|
+
const phase = detectPhase(inventory, hasSprintStatus);
|
|
140
|
+
const hasWdsArtifacts =
|
|
141
|
+
inventory.wdsScenarioFiles.length > 0 ||
|
|
142
|
+
inventory.wdsPageSpecFiles.length > 0 ||
|
|
143
|
+
inventory.wdsDeliveryFiles.length > 0 ||
|
|
144
|
+
inventory.wdsDesignSystemFiles.length > 0;
|
|
145
|
+
const hasTeaArtifacts =
|
|
146
|
+
inventory.teaFiles.length > 0 ||
|
|
147
|
+
inventory.teaTraceFiles.length > 0 ||
|
|
148
|
+
inventory.teaReviewFiles.length > 0 ||
|
|
149
|
+
inventory.teaNfrFiles.length > 0;
|
|
150
|
+
|
|
151
|
+
const sprint = hasSprintStatus ? loadSprintStatus(install.paths.sprintStatusPath) : undefined;
|
|
152
|
+
const nextBacklogStory = sprint?.storyOrder.find((story) => sprint.developmentStatus[story] === "backlog");
|
|
153
|
+
const nextReadyStory = sprint?.storyOrder.find((story) => sprint.developmentStatus[story] === "ready-for-dev");
|
|
154
|
+
const activeStory = sprint?.storyOrder.find((story) => sprint.developmentStatus[story] === "in-progress");
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
phase,
|
|
158
|
+
hasPlanningArtifacts,
|
|
159
|
+
hasImplementationArtifacts,
|
|
160
|
+
hasSprintStatus,
|
|
161
|
+
hasWdsArtifacts,
|
|
162
|
+
hasTeaArtifacts,
|
|
163
|
+
inventory,
|
|
164
|
+
nextBacklogStory,
|
|
165
|
+
nextReadyStory,
|
|
166
|
+
activeStory,
|
|
167
|
+
};
|
|
168
|
+
}
|