pi-bmad-flow 0.1.1 → 0.1.3
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/bmad-orchestrator/commands.ts +16 -1
- package/extensions/bmad-orchestrator/detector.ts +28 -4
- package/extensions/bmad-orchestrator/router.ts +8 -0
- package/extensions/bmad-orchestrator/sprint.ts +22 -3
- package/extensions/bmad-orchestrator/types.ts +2 -0
- package/package.json +1 -1
- package/scripts/audit-project.mjs +89 -66
|
@@ -82,6 +82,15 @@ export function registerBmadCommands(pi: ExtensionAPI, cwd: string): void {
|
|
|
82
82
|
ctx.ui.notify(`Delivery: ${deliverySummary}`, "info");
|
|
83
83
|
ctx.ui.notify(`Design: ${designSummary}`, "info");
|
|
84
84
|
ctx.ui.notify(`TEA: ${teaSummary}`, "info");
|
|
85
|
+
if (state.activeSprintStatusPath) {
|
|
86
|
+
ctx.ui.notify(`Status file: ${state.activeSprintStatusPath}`, "info");
|
|
87
|
+
}
|
|
88
|
+
if (state.activeStoryLocation) {
|
|
89
|
+
ctx.ui.notify(`Story location: ${state.activeStoryLocation}`, "info");
|
|
90
|
+
}
|
|
91
|
+
if (state.inventory.epicFiles.length > 0) {
|
|
92
|
+
ctx.ui.notify(`Epic file: ${state.inventory.epicFiles[0]}`, "info");
|
|
93
|
+
}
|
|
85
94
|
ctx.ui.notify(`Next: ${next.command} - ${next.summary}`, "info");
|
|
86
95
|
|
|
87
96
|
refreshBmadUi(ctx, state, next);
|
|
@@ -96,9 +105,15 @@ export function registerBmadCommands(pi: ExtensionAPI, cwd: string): void {
|
|
|
96
105
|
const next = decideNextAction(state);
|
|
97
106
|
ctx.ui.notify(`Current phase: ${state.phase}`, "info");
|
|
98
107
|
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}`,
|
|
108
|
+
`Artifacts: PRD ${state.inventory.prdFiles.length}, UX ${state.inventory.uxFiles.length}, Architecture ${state.inventory.architectureFiles.length}, Epics ${state.inventory.epicFiles.length}, Stories ${state.inventory.storyFiles.length}`,
|
|
100
109
|
"info",
|
|
101
110
|
);
|
|
111
|
+
if (state.activeSprintStatusPath) {
|
|
112
|
+
ctx.ui.notify(`Status file: ${state.activeSprintStatusPath}`, "info");
|
|
113
|
+
}
|
|
114
|
+
if (state.activeStoryLocation) {
|
|
115
|
+
ctx.ui.notify(`Story location: ${state.activeStoryLocation}`, "info");
|
|
116
|
+
}
|
|
102
117
|
ctx.ui.notify(`Recommended: ${next.command} (${next.reason})`, "info");
|
|
103
118
|
refreshBmadUi(ctx, state, next);
|
|
104
119
|
appendBmadState(pi, { event: "status", nextCommand: next.command });
|
|
@@ -16,14 +16,35 @@ function collectStructuredFiles(dirPath: string): string[] {
|
|
|
16
16
|
return unique([...collectMarkdownFiles(dirPath), ...collectYamlFiles(dirPath)]);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function detectStoryFiles(storyLocationFiles: string[]): string[] {
|
|
20
|
+
return storyLocationFiles.filter((file) => {
|
|
21
|
+
const lower = file.toLowerCase();
|
|
22
|
+
const base = lower.split("/").pop() ?? lower;
|
|
23
|
+
if (!base.endsWith(".md")) return false;
|
|
24
|
+
if (["readme.md", "index.md", "deferred-work.md"].includes(base)) return false;
|
|
25
|
+
if (/(checklist|traceability|report|spec-|specification|validation)/.test(base)) return false;
|
|
26
|
+
return /^(story-.*|\d+-\d+-.*|rb\d+-\d+-.*)\.md$/.test(base);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function detectEpicFiles(planningFiles: string[]): string[] {
|
|
31
|
+
const canonical = planningFiles.filter((file) => file.toLowerCase().endsWith("/epics.md"));
|
|
32
|
+
if (canonical.length > 0) return canonical;
|
|
33
|
+
|
|
34
|
+
return planningFiles.filter((file) => {
|
|
35
|
+
const lower = file.toLowerCase();
|
|
36
|
+
const base = lower.split("/").pop() ?? lower;
|
|
37
|
+
return /(^|.*-)epics\.md$/.test(base) || /^epic-.*\.md$/.test(base);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
19
41
|
function detectInventory(paths: BmadPaths): ArtifactInventory {
|
|
20
42
|
const resolvedSprintStatusPath = resolveSprintStatusPath(paths.sprintStatusPath);
|
|
21
43
|
const sprint = loadSprintStatus(resolvedSprintStatusPath);
|
|
22
44
|
const storyLocationDir = resolveStoryLocation(resolvedSprintStatusPath, sprint);
|
|
23
45
|
const planningFiles = collectMarkdownFiles(paths.planningArtifactsDir);
|
|
24
46
|
const implementationFiles = collectMarkdownFiles(paths.implementationArtifactsDir);
|
|
25
|
-
const storyLocationFiles =
|
|
26
|
-
storyLocationDir !== paths.implementationArtifactsDir ? collectMarkdownFiles(storyLocationDir) : implementationFiles;
|
|
47
|
+
const storyLocationFiles = collectMarkdownFiles(storyLocationDir);
|
|
27
48
|
const designFiles = collectStructuredFiles(paths.designArtifactsDir);
|
|
28
49
|
const docsFiles = collectStructuredFiles(paths.docsDir);
|
|
29
50
|
const teaFiles = unique([
|
|
@@ -65,8 +86,8 @@ function detectInventory(paths: BmadPaths): ArtifactInventory {
|
|
|
65
86
|
/(ux|wds-|wireframe|storyboard|page-spec|page-specification|design-delivery|scenario)/,
|
|
66
87
|
),
|
|
67
88
|
architectureFiles: matches(allContextFiles, /architecture|adr/),
|
|
68
|
-
epicFiles:
|
|
69
|
-
storyFiles:
|
|
89
|
+
epicFiles: detectEpicFiles(planningFiles),
|
|
90
|
+
storyFiles: detectStoryFiles(storyLocationFiles),
|
|
70
91
|
reviewFiles: matches(allContextFiles, /review|retrospective|readiness-report|validation-report/),
|
|
71
92
|
projectContextFiles: matches(allContextFiles, /project-context/),
|
|
72
93
|
wdsScenarioFiles,
|
|
@@ -154,6 +175,7 @@ export function detectBmadProjectState(cwd: string): BmadProjectState {
|
|
|
154
175
|
const nextBacklogStory = sprint?.storyOrder.find((story) => sprint.developmentStatus[story] === "backlog");
|
|
155
176
|
const nextReadyStory = sprint?.storyOrder.find((story) => sprint.developmentStatus[story] === "ready-for-dev");
|
|
156
177
|
const activeStory = sprint?.storyOrder.find((story) => sprint.developmentStatus[story] === "in-progress");
|
|
178
|
+
const activeStoryLocation = sprint ? resolveStoryLocation(resolvedSprintStatusPath, sprint) : undefined;
|
|
157
179
|
|
|
158
180
|
return {
|
|
159
181
|
phase,
|
|
@@ -163,6 +185,8 @@ export function detectBmadProjectState(cwd: string): BmadProjectState {
|
|
|
163
185
|
hasWdsArtifacts,
|
|
164
186
|
hasTeaArtifacts,
|
|
165
187
|
inventory,
|
|
188
|
+
activeSprintStatusPath: hasSprintStatus ? resolvedSprintStatusPath : undefined,
|
|
189
|
+
activeStoryLocation,
|
|
166
190
|
nextBacklogStory,
|
|
167
191
|
nextReadyStory,
|
|
168
192
|
activeStory,
|
|
@@ -47,6 +47,14 @@ export function decideNextAction(state: BmadProjectState): NextAction {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
if (!state.activeStoryLocation) {
|
|
51
|
+
return {
|
|
52
|
+
summary: "Sprint tracking exists, but story_location is missing or could not be resolved.",
|
|
53
|
+
command: "bmad-sprint-planning",
|
|
54
|
+
reason: "The active story directory should come from sprint-status.yaml before story execution starts.",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
if (state.activeStory) {
|
|
51
59
|
return {
|
|
52
60
|
summary: `Story ${state.activeStory} is already in progress.`,
|
|
@@ -3,8 +3,7 @@ import { dirname, resolve } from "node:path";
|
|
|
3
3
|
import type { SprintStatus } from "./types.js";
|
|
4
4
|
|
|
5
5
|
const STATUS_FILE_CANDIDATES = ["sprint-status.yaml", "story-status.yaml"];
|
|
6
|
-
|
|
7
|
-
const STORY_KEY_PATTERN = /^\d+-\d+-/;
|
|
6
|
+
const STORY_KEY_PATTERN = /^(?:\d+-\d+-|rb\d+-\d+-)/;
|
|
8
7
|
|
|
9
8
|
function parseStatusLine(line: string): { key: string; value: string } | undefined {
|
|
10
9
|
const match = line.match(/^\s{2}([a-zA-Z0-9._-]+):\s*([a-zA-Z0-9._-]+)\s*$/);
|
|
@@ -57,6 +56,18 @@ export function loadSprintStatus(statusPath: string): SprintStatus | undefined {
|
|
|
57
56
|
if (STORY_KEY_PATTERN.test(parsed.key)) storyOrder.push(parsed.key);
|
|
58
57
|
}
|
|
59
58
|
|
|
59
|
+
if (storyOrder.length === 0) {
|
|
60
|
+
for (const key of Object.keys(developmentStatus)) {
|
|
61
|
+
if (STORY_KEY_PATTERN.test(key)) storyOrder.push(key);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (storyOrder.length === 0) {
|
|
66
|
+
for (const key of Object.keys(developmentStatus)) {
|
|
67
|
+
if (!key.startsWith("epic-") && !key.endsWith("-retrospective")) storyOrder.push(key);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
return { developmentStatus, storyOrder, storyLocation };
|
|
61
72
|
}
|
|
62
73
|
|
|
@@ -94,5 +105,13 @@ export function resolveStoryLocation(statusPath: string, sprint?: SprintStatus):
|
|
|
94
105
|
if (!location) return fallbackDir;
|
|
95
106
|
if (location.startsWith("{")) return fallbackDir;
|
|
96
107
|
if (location.startsWith("/")) return location;
|
|
97
|
-
|
|
108
|
+
|
|
109
|
+
const relativeToStatusDir = resolve(dirname(statusPath), location);
|
|
110
|
+
if (existsSync(relativeToStatusDir)) return relativeToStatusDir;
|
|
111
|
+
|
|
112
|
+
const projectRoot = resolve(dirname(statusPath), "..", "..");
|
|
113
|
+
const relativeToProjectRoot = resolve(projectRoot, location);
|
|
114
|
+
if (existsSync(relativeToProjectRoot)) return relativeToProjectRoot;
|
|
115
|
+
|
|
116
|
+
return relativeToStatusDir;
|
|
98
117
|
}
|
|
@@ -65,6 +65,8 @@ export interface BmadProjectState {
|
|
|
65
65
|
hasWdsArtifacts: boolean;
|
|
66
66
|
hasTeaArtifacts: boolean;
|
|
67
67
|
inventory: ArtifactInventory;
|
|
68
|
+
activeSprintStatusPath?: string;
|
|
69
|
+
activeStoryLocation?: string;
|
|
68
70
|
nextBacklogStory?: string;
|
|
69
71
|
nextReadyStory?: string;
|
|
70
72
|
activeStory?: string;
|
package/package.json
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { resolve, join, relative } from "node:path";
|
|
2
|
+
import { resolve, join, relative, dirname } from "node:path";
|
|
3
|
+
|
|
4
|
+
const STORY_KEY_PATTERN = /^(?:\d+-\d+-|rb\d+-\d+-)/;
|
|
3
5
|
|
|
4
6
|
function parseArgs(argv) {
|
|
5
7
|
const options = {
|
|
@@ -48,13 +50,10 @@ function readJson(path) {
|
|
|
48
50
|
function resolveSprintStatusPath(path) {
|
|
49
51
|
if (existsSync(path)) return path;
|
|
50
52
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
for (const candidate of candidates) {
|
|
57
|
-
if (candidate !== path && existsSync(candidate)) return candidate;
|
|
53
|
+
const directory = dirname(path);
|
|
54
|
+
for (const candidate of ["sprint-status.yaml", "story-status.yaml"]) {
|
|
55
|
+
const candidatePath = resolve(directory, candidate);
|
|
56
|
+
if (existsSync(candidatePath)) return candidatePath;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
return path;
|
|
@@ -73,76 +72,93 @@ function parseSprintStatus(path) {
|
|
|
73
72
|
storyLocation: undefined,
|
|
74
73
|
};
|
|
75
74
|
|
|
76
|
-
let
|
|
75
|
+
let inDevelopmentStatus = false;
|
|
77
76
|
for (const rawLine of lines) {
|
|
78
|
-
const line = rawLine.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (/^story_order:\s*$/.test(trimmed)) {
|
|
83
|
-
section = "story_order";
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (/^development_status:\s*$/.test(trimmed)) {
|
|
87
|
-
section = "development_status";
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (/^story_location:\s*/.test(trimmed)) {
|
|
91
|
-
state.storyLocation = trimmed.split(":").slice(1).join(":").trim().replace(/^['"]|['"]$/g, "");
|
|
92
|
-
section = "";
|
|
93
|
-
continue;
|
|
77
|
+
const line = rawLine.trimEnd();
|
|
78
|
+
if (!inDevelopmentStatus && !state.storyLocation) {
|
|
79
|
+
const storyLocationMatch = line.match(/^story_location:\s*["']?(.+?)["']?\s*$/);
|
|
80
|
+
if (storyLocationMatch) state.storyLocation = storyLocationMatch[1];
|
|
94
81
|
}
|
|
95
82
|
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
if (match) state.storyOrder.push(match[1].trim().replace(/^['"]|['"]$/g, ""));
|
|
83
|
+
if (!inDevelopmentStatus) {
|
|
84
|
+
if (line === "development_status:") inDevelopmentStatus = true;
|
|
99
85
|
continue;
|
|
100
86
|
}
|
|
101
87
|
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
88
|
+
if (!line.startsWith(" ") && line.length > 0 && !line.startsWith("#")) break;
|
|
89
|
+
const match = line.match(/^\s{2}([a-zA-Z0-9._-]+):\s*([a-zA-Z0-9._-]+)\s*$/);
|
|
90
|
+
if (!match) continue;
|
|
91
|
+
|
|
92
|
+
const key = match[1];
|
|
93
|
+
const value = match[2];
|
|
94
|
+
state.developmentStatus[key] = value;
|
|
95
|
+
if (STORY_KEY_PATTERN.test(key)) state.storyOrder.push(key);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (state.storyOrder.length === 0) {
|
|
99
|
+
for (const key of Object.keys(state.developmentStatus)) {
|
|
100
|
+
if (STORY_KEY_PATTERN.test(key)) state.storyOrder.push(key);
|
|
109
101
|
}
|
|
110
102
|
}
|
|
111
103
|
|
|
112
104
|
return state;
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
107
|
+
function resolveStoryLocation(statusPath, sprint) {
|
|
108
|
+
const fallbackDir = dirname(statusPath);
|
|
109
|
+
const location = sprint.storyLocation?.trim();
|
|
110
|
+
if (!location) return fallbackDir;
|
|
111
|
+
if (location.startsWith("{")) return fallbackDir;
|
|
112
|
+
if (location.startsWith("/")) return location;
|
|
113
|
+
|
|
114
|
+
const relativeToStatusDir = resolve(dirname(statusPath), location);
|
|
115
|
+
if (existsSync(relativeToStatusDir)) return relativeToStatusDir;
|
|
116
|
+
|
|
117
|
+
const projectRoot = resolve(dirname(statusPath), "..", "..");
|
|
118
|
+
const relativeToProjectRoot = resolve(projectRoot, location);
|
|
119
|
+
if (existsSync(relativeToProjectRoot)) return relativeToProjectRoot;
|
|
120
|
+
|
|
121
|
+
return relativeToStatusDir;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function detectStoryFiles(storyLocationFiles) {
|
|
125
|
+
return storyLocationFiles.filter((file) => {
|
|
126
|
+
const lower = file.toLowerCase();
|
|
127
|
+
const base = lower.split("/").pop() ?? lower;
|
|
128
|
+
if (!base.endsWith(".md")) return false;
|
|
129
|
+
if (["readme.md", "index.md", "deferred-work.md"].includes(base)) return false;
|
|
130
|
+
if (/(checklist|traceability|report|spec-|specification|validation)/.test(base)) return false;
|
|
131
|
+
return /^(story-.*|\d+-\d+-.*|rb\d+-\d+-.*)\.md$/.test(base);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function detectEpicFiles(planningFiles) {
|
|
136
|
+
const canonical = planningFiles.filter((file) => file.toLowerCase().endsWith("/epics.md"));
|
|
137
|
+
if (canonical.length > 0) return canonical;
|
|
138
|
+
|
|
139
|
+
return planningFiles.filter((file) => {
|
|
140
|
+
const lower = file.toLowerCase();
|
|
141
|
+
const base = lower.split("/").pop() ?? lower;
|
|
142
|
+
return /(^|.*-)epics\.md$/.test(base) || /^epic-.*\.md$/.test(base);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function decideNextAction(inventory, sprintStatusExists, sprint, activeStoryLocation) {
|
|
147
|
+
if (inventory.prdFiles.length === 0) return "bmad-create-prd";
|
|
148
|
+
if (inventory.uxFiles.length === 0) return "bmad-create-ux-design";
|
|
149
|
+
if (inventory.architectureFiles.length === 0) return "bmad-create-architecture";
|
|
150
|
+
if (inventory.epicFiles.length === 0) return "bmad-create-epics-and-stories";
|
|
151
|
+
if (!sprintStatusExists) return "bmad-sprint-planning";
|
|
152
|
+
if (!activeStoryLocation) return "bmad-sprint-planning";
|
|
131
153
|
|
|
132
154
|
const activeStory = sprint.storyOrder.find((story) => sprint.developmentStatus[story] === "in-progress");
|
|
133
|
-
if (activeStory) {
|
|
134
|
-
return `bmad-dev-story ${activeStory}`;
|
|
135
|
-
}
|
|
155
|
+
if (activeStory) return `bmad-dev-story ${activeStory}`;
|
|
136
156
|
|
|
137
157
|
const nextReadyStory = sprint.storyOrder.find((story) => sprint.developmentStatus[story] === "ready-for-dev");
|
|
138
|
-
if (nextReadyStory) {
|
|
139
|
-
return `/bmad-start (${nextReadyStory})`;
|
|
140
|
-
}
|
|
158
|
+
if (nextReadyStory) return `/bmad-start (${nextReadyStory})`;
|
|
141
159
|
|
|
142
160
|
const nextBacklogStory = sprint.storyOrder.find((story) => sprint.developmentStatus[story] === "backlog");
|
|
143
|
-
if (nextBacklogStory) {
|
|
144
|
-
return `bmad-create-story (${nextBacklogStory})`;
|
|
145
|
-
}
|
|
161
|
+
if (nextBacklogStory) return `bmad-create-story (${nextBacklogStory})`;
|
|
146
162
|
|
|
147
163
|
return "bmad-correct-course";
|
|
148
164
|
}
|
|
@@ -205,12 +221,18 @@ function main() {
|
|
|
205
221
|
const testFiles = collectStructuredFiles(paths.testArtifactsDir);
|
|
206
222
|
const allContextFiles = [...planningFiles, ...implementationFiles, ...docsFiles, ...designFiles, ...testFiles];
|
|
207
223
|
|
|
224
|
+
const resolvedSprintStatusPath = resolveSprintStatusPath(paths.sprintStatusPath);
|
|
225
|
+
const sprint = parseSprintStatus(resolvedSprintStatusPath);
|
|
226
|
+
const sprintStatusExists = existsSync(resolvedSprintStatusPath);
|
|
227
|
+
const activeStoryLocation = sprintStatusExists ? resolveStoryLocation(resolvedSprintStatusPath, sprint) : undefined;
|
|
228
|
+
const activeStoryFiles = activeStoryLocation ? collectStructuredFiles(activeStoryLocation) : [];
|
|
229
|
+
|
|
208
230
|
const inventory = {
|
|
209
231
|
prdFiles: matches(allContextFiles, /(^|\/)(prd|.*prd.*)\.md$/),
|
|
210
232
|
uxFiles: matches(allContextFiles, /(ux|wds-|wireframe|storyboard|page-spec|page-specification|design-delivery|scenario)/),
|
|
211
233
|
architectureFiles: matches(allContextFiles, /architecture|adr/),
|
|
212
|
-
epicFiles:
|
|
213
|
-
storyFiles:
|
|
234
|
+
epicFiles: detectEpicFiles(planningFiles),
|
|
235
|
+
storyFiles: detectStoryFiles(activeStoryFiles),
|
|
214
236
|
projectContextFiles: matches(allContextFiles, /project-context/),
|
|
215
237
|
wdsDeliveryFiles: matches(allContextFiles, /(design-deliver(y|ies)|\/deliveries\/|dd-\d+)/),
|
|
216
238
|
wdsPageSpecFiles: matches(allContextFiles, /\/c-ux-scenarios\/.*(specifications\.md|page-spec|page-specification|\/frontend\/.*\.md)/),
|
|
@@ -219,10 +241,7 @@ function main() {
|
|
|
219
241
|
teaNfrFiles: matches(allContextFiles, /nfr-assessment|nfr-report/),
|
|
220
242
|
};
|
|
221
243
|
|
|
222
|
-
const
|
|
223
|
-
const sprint = parseSprintStatus(resolvedSprintStatusPath);
|
|
224
|
-
const sprintStatusExists = existsSync(resolvedSprintStatusPath);
|
|
225
|
-
const nextAction = decideNextAction(inventory, sprintStatusExists, sprint);
|
|
244
|
+
const nextAction = decideNextAction(inventory, sprintStatusExists, sprint, activeStoryLocation);
|
|
226
245
|
|
|
227
246
|
console.log("pi-bmad-flow project audit");
|
|
228
247
|
console.log(`project: ${projectDir}`);
|
|
@@ -259,6 +278,7 @@ function main() {
|
|
|
259
278
|
console.log("");
|
|
260
279
|
console.log("sprint:");
|
|
261
280
|
console.log(`sprint status: ${sprintStatusExists ? "present" : "missing"}`);
|
|
281
|
+
console.log(`status file: ${resolvedSprintStatusPath}`);
|
|
262
282
|
console.log(`story order entries: ${sprint.storyOrder.length}`);
|
|
263
283
|
console.log(`ready stories: ${sprint.storyOrder.filter((story) => sprint.developmentStatus[story] === "ready-for-dev").length}`);
|
|
264
284
|
console.log(`active stories: ${sprint.storyOrder.filter((story) => sprint.developmentStatus[story] === "in-progress").length}`);
|
|
@@ -266,6 +286,9 @@ function main() {
|
|
|
266
286
|
if (sprint.storyLocation) {
|
|
267
287
|
console.log(`story location: ${sprint.storyLocation}`);
|
|
268
288
|
}
|
|
289
|
+
if (activeStoryLocation) {
|
|
290
|
+
console.log(`resolved story location: ${activeStoryLocation}`);
|
|
291
|
+
}
|
|
269
292
|
|
|
270
293
|
console.log("");
|
|
271
294
|
console.log(`next action: ${nextAction}`);
|