@ikunin/sprintpilot 2.3.4 → 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.
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.3.4
3
+ version: 2.3.6
4
4
  description: Sprintpilot — autopilot and multi-agent addon for BMad Method (git workflow, parallel agents, autonomous story execution)
5
5
  bmad_compatibility: ">=6.2.0"
6
6
  modules:
@@ -508,11 +508,16 @@ function renderMermaid(dag, plan) {
508
508
  const epicIssueIds = issueIdByEpicId(plan);
509
509
  const { intra, cross } = bucketEdges(dag.edges);
510
510
  const lines = [];
511
+ // The diagram-type declaration MUST be the first non-blank line.
512
+ // Strict markdown→mermaid renderers (Claude Code's chat renderer
513
+ // included) sniff for `flowchart`/`graph`/etc. as the first content
514
+ // line; leading `%%` comments cause them to silently skip rendering.
515
+ // Comments after the type declaration are fine.
516
+ lines.push('flowchart LR');
511
517
  lines.push(`%% plan-id: ${plan?.plan_id ?? 'unknown'}`);
512
518
  lines.push(`%% generated: ${plan?.generated ?? new Date().toISOString()}`);
513
519
  lines.push('%% Sprint plan DAG — node fill encodes plan_status; cross-epic edges are dashed.');
514
520
  lines.push('%% Story labels are prefixed with their issue_id when set in plan.stories.');
515
- lines.push('flowchart LR');
516
521
 
517
522
  // Group nodes by epic (if any). When sprint-wide, emit subgraphs.
518
523
  const epicGroups = new Map();
@@ -635,6 +640,50 @@ function hasGraphvizBinary() {
635
640
  }
636
641
  }
637
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
+
638
687
  function defaultRenderOutputPath(projectRoot, format) {
639
688
  const ext = format === 'graphviz' ? 'dot' : 'mmd';
640
689
  return path.join(projectRoot, '_bmad-output', 'implementation-artifacts', `sprint-plan-dag.${ext}`);
@@ -689,12 +738,29 @@ function runRender({ projectRoot, epic, format, output }) {
689
738
  };
690
739
  }
691
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
+
692
757
  return {
693
758
  wrote: true,
694
759
  file: outputPath,
695
760
  format: effectiveFormat,
696
761
  requested_format: requestedFormat,
697
762
  ...(fallbackReason ? { fallback: fallbackReason } : {}),
763
+ ...(pngEnvelope || {}),
698
764
  nodes: dag.nodes.length,
699
765
  edges: dag.edges.length,
700
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?}`. Then
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "2.3.4",
3
+ "version": "2.3.6",
4
4
  "description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {