@ikunin/sprintpilot 2.3.5 → 2.3.6
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.
|
@@ -640,6 +640,50 @@ function hasGraphvizBinary() {
|
|
|
640
640
|
}
|
|
641
641
|
}
|
|
642
642
|
|
|
643
|
+
// Find the Mermaid CLI binary on PATH. Cross-platform: npm installs
|
|
644
|
+
// CLI tools as `.cmd` shims on Windows, and Node's spawn family does
|
|
645
|
+
// not auto-resolve PATHEXT extensions without `shell: true`. We probe
|
|
646
|
+
// platform-appropriate candidates explicitly. Returns the resolved
|
|
647
|
+
// binary name (suitable for spawnSync) or null if none found.
|
|
648
|
+
function findMermaidCliBinary() {
|
|
649
|
+
const candidates =
|
|
650
|
+
process.platform === 'win32'
|
|
651
|
+
? ['mmdc.cmd', 'mmdc.ps1', 'mmdc.bat', 'mmdc.exe', 'mmdc']
|
|
652
|
+
: ['mmdc'];
|
|
653
|
+
for (const name of candidates) {
|
|
654
|
+
try {
|
|
655
|
+
const r = spawnSync(name, ['--version'], { stdio: 'ignore' });
|
|
656
|
+
if (r.status === 0) return name;
|
|
657
|
+
} catch {
|
|
658
|
+
// ENOENT etc — try next candidate
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return null;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Try to render `<mmd>` to a sibling `<mmd>.png` via mmdc. Returns
|
|
665
|
+
// { written: true, file: <png-path> } — success
|
|
666
|
+
// { written: false, reason: 'mmdc-missing' } — toolchain absent
|
|
667
|
+
// { written: false, reason: 'render-failed', message: <stderr> } — toolchain errored
|
|
668
|
+
// Never throws; the caller threads the result into the envelope.
|
|
669
|
+
function tryRenderMermaidPng(mmdPath) {
|
|
670
|
+
const bin = findMermaidCliBinary();
|
|
671
|
+
if (!bin) {
|
|
672
|
+
return { written: false, reason: 'mmdc-missing' };
|
|
673
|
+
}
|
|
674
|
+
const pngPath = mmdPath.endsWith('.mmd') ? mmdPath.slice(0, -4) + '.png' : mmdPath + '.png';
|
|
675
|
+
const r = spawnSync(bin, ['-i', mmdPath, '-o', pngPath, '-b', 'transparent', '--quiet'], {
|
|
676
|
+
encoding: 'utf8',
|
|
677
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
678
|
+
windowsHide: true,
|
|
679
|
+
});
|
|
680
|
+
if (r.status !== 0) {
|
|
681
|
+
const message = (r.stderr || r.stdout || 'mmdc exited non-zero').trim();
|
|
682
|
+
return { written: false, reason: 'render-failed', message };
|
|
683
|
+
}
|
|
684
|
+
return { written: true, file: pngPath };
|
|
685
|
+
}
|
|
686
|
+
|
|
643
687
|
function defaultRenderOutputPath(projectRoot, format) {
|
|
644
688
|
const ext = format === 'graphviz' ? 'dot' : 'mmd';
|
|
645
689
|
return path.join(projectRoot, '_bmad-output', 'implementation-artifacts', `sprint-plan-dag.${ext}`);
|
|
@@ -694,12 +738,29 @@ function runRender({ projectRoot, epic, format, output }) {
|
|
|
694
738
|
};
|
|
695
739
|
}
|
|
696
740
|
|
|
741
|
+
// Sibling-PNG render (mermaid only). Non-fatal: caller surfaces the
|
|
742
|
+
// reason if mmdc is missing or errored. graphviz users render via
|
|
743
|
+
// `dot -Tpng …` themselves; no auto-PNG for that format.
|
|
744
|
+
let pngEnvelope = null;
|
|
745
|
+
if (effectiveFormat === 'mermaid') {
|
|
746
|
+
const pngResult = tryRenderMermaidPng(outputPath);
|
|
747
|
+
if (pngResult.written) {
|
|
748
|
+
pngEnvelope = { png_file: pngResult.file };
|
|
749
|
+
} else {
|
|
750
|
+
pngEnvelope = {
|
|
751
|
+
png_reason: pngResult.reason,
|
|
752
|
+
...(pngResult.message ? { png_message: pngResult.message } : {}),
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
697
757
|
return {
|
|
698
758
|
wrote: true,
|
|
699
759
|
file: outputPath,
|
|
700
760
|
format: effectiveFormat,
|
|
701
761
|
requested_format: requestedFormat,
|
|
702
762
|
...(fallbackReason ? { fallback: fallbackReason } : {}),
|
|
763
|
+
...(pngEnvelope || {}),
|
|
703
764
|
nodes: dag.nodes.length,
|
|
704
765
|
edges: dag.edges.length,
|
|
705
766
|
};
|
|
@@ -113,8 +113,8 @@ node _Sprintpilot/scripts/resolve-dag.js render --format mermaid \
|
|
|
113
113
|
[--epic <id>] [--output <path>] --project-root <root>
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
Returns JSON `{wrote, file, format, nodes, edges, fallback?}`.
|
|
117
|
-
read the rendered file and inline its contents in a ```mermaid fenced
|
|
116
|
+
Returns JSON `{wrote, file, format, nodes, edges, fallback?, png_file?, png_reason?, png_message?}`.
|
|
117
|
+
Then read the rendered file and inline its contents in a ```mermaid fenced
|
|
118
118
|
code block so the chat client (Claude Code, etc.) renders the diagram
|
|
119
119
|
inline. Also report the file path so the user can preview elsewhere:
|
|
120
120
|
|
|
@@ -134,6 +134,21 @@ cross-references back to the user's tracker (Jira / Linear / GitHub /
|
|
|
134
134
|
GitLab). Stories/epics without an issue_id render with the bare key —
|
|
135
135
|
silence communicates "not tracked" rather than spamming `[no issue]`.
|
|
136
136
|
|
|
137
|
+
**PNG sibling render.** As a side-effect of the mermaid render, the
|
|
138
|
+
script tries to produce a `<file>.png` next to the `.mmd` via the
|
|
139
|
+
official Mermaid CLI (`mmdc`). Three outcomes to surface:
|
|
140
|
+
|
|
141
|
+
- `png_file` set → PNG produced. Report:
|
|
142
|
+
> Also wrote PNG: `<png_file>` (rendered via Mermaid CLI).
|
|
143
|
+
- `png_reason: "mmdc-missing"` → Mermaid CLI not installed. Report:
|
|
144
|
+
> PNG render skipped — install Mermaid CLI to get a `.png` next to
|
|
145
|
+
> the `.mmd`:
|
|
146
|
+
> `npm install -g @mermaid-js/mermaid-cli`
|
|
147
|
+
> (Requires Node 18+. Then re-run this skill.)
|
|
148
|
+
- `png_reason: "render-failed"` → mmdc errored. Report the `png_message`
|
|
149
|
+
verbatim and suggest re-running with `mmdc -i <mmd> -o <png>` to see
|
|
150
|
+
the full error.
|
|
151
|
+
|
|
137
152
|
### Graphviz
|
|
138
153
|
|
|
139
154
|
```
|
package/package.json
CHANGED