bmad-viewer 0.3.0 → 0.3.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/package.json +1 -1
- package/src/data/data-model.js +42 -1
- package/src/parsers/parse-yaml.js +4 -2
package/package.json
CHANGED
package/src/data/data-model.js
CHANGED
|
@@ -369,10 +369,21 @@ function buildProjectData(outputPath, aggregator) {
|
|
|
369
369
|
...content,
|
|
370
370
|
});
|
|
371
371
|
|
|
372
|
-
// Parse stories from epics.md
|
|
372
|
+
// Parse stories and epics from epics.md
|
|
373
373
|
if (name === 'epics' && ext === '.md' && content.raw) {
|
|
374
374
|
const storyContents = parseStoriesFromEpics(content.raw, aggregator);
|
|
375
375
|
project.storyContents = storyContents;
|
|
376
|
+
|
|
377
|
+
// Build epics from markdown when no sprint-status.yaml was found
|
|
378
|
+
if (project.epics.length === 0) {
|
|
379
|
+
project.epics = parseEpicsFromMarkdown(content.raw, storyContents);
|
|
380
|
+
// Build story stats from epics.md stories
|
|
381
|
+
for (const epic of project.epics) {
|
|
382
|
+
project.stories.total += epic.stories.length;
|
|
383
|
+
project.stories.pending += epic.stories.length; // all backlog by default
|
|
384
|
+
project.storyList.push(...epic.stories);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
376
387
|
}
|
|
377
388
|
}
|
|
378
389
|
}
|
|
@@ -539,6 +550,36 @@ function parseStoriesFromEpics(raw, aggregator) {
|
|
|
539
550
|
return storyMap;
|
|
540
551
|
}
|
|
541
552
|
|
|
553
|
+
/**
|
|
554
|
+
* Parse epics and their stories from epics.md markdown.
|
|
555
|
+
* Epics follow the pattern: ## Epic N: Title
|
|
556
|
+
* Stories follow the pattern: ### Story N.M: Title
|
|
557
|
+
*/
|
|
558
|
+
function parseEpicsFromMarkdown(raw, storyContents) {
|
|
559
|
+
const epicRegex = /^## Epic (\d+):\s*(.+)$/gm;
|
|
560
|
+
const epicMap = {};
|
|
561
|
+
let match;
|
|
562
|
+
|
|
563
|
+
while ((match = epicRegex.exec(raw)) !== null) {
|
|
564
|
+
const num = match[1];
|
|
565
|
+
const name = match[2].trim();
|
|
566
|
+
epicMap[num] = { id: `epic-${num}`, num, name, status: 'backlog', stories: [] };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Assign stories to their epics
|
|
570
|
+
for (const [key, storyData] of Object.entries(storyContents)) {
|
|
571
|
+
const epicNum = key.split('-')[0];
|
|
572
|
+
const story = { id: key, title: storyData.title, status: 'backlog', epic: epicNum };
|
|
573
|
+
if (epicMap[epicNum]) {
|
|
574
|
+
epicMap[epicNum].stories.push(story);
|
|
575
|
+
} else {
|
|
576
|
+
epicMap[epicNum] = { id: `epic-${epicNum}`, num: epicNum, name: `Epic ${epicNum}`, status: 'backlog', stories: [story] };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return Object.values(epicMap).sort((a, b) => Number(a.num) - Number(b.num));
|
|
581
|
+
}
|
|
582
|
+
|
|
542
583
|
/**
|
|
543
584
|
* Parse epic names from YAML comments like "# Epic 1: Fundación del Proyecto"
|
|
544
585
|
*/
|
|
@@ -26,12 +26,14 @@ export function parseYaml(filePath) {
|
|
|
26
26
|
*/
|
|
27
27
|
export function parseYamlContent(content, source = 'unknown') {
|
|
28
28
|
try {
|
|
29
|
-
const
|
|
29
|
+
const docs = yaml.loadAll(content);
|
|
30
|
+
const nonNull = docs.filter(d => d !== undefined && d !== null);
|
|
30
31
|
|
|
31
|
-
if (
|
|
32
|
+
if (nonNull.length === 0) {
|
|
32
33
|
return createResult(null, [], [`Empty YAML content in ${source}`]);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
const data = nonNull.length === 1 ? nonNull[0] : Object.assign({}, ...nonNull);
|
|
35
37
|
return createResult(data, [], []);
|
|
36
38
|
} catch (error) {
|
|
37
39
|
const warnings = [`Failed to parse ${source}`];
|