feat: enhance Mermaid export with subgraphs, edge labels, node shapes and styling#97
Conversation
Sets Xenova/bge-large-en-v1.5 (1024d) as the default embedding model for codegraph self-analysis, replacing the default minilm.
… and styling
- Change `graph LR` to `flowchart {direction}` with configurable --direction option
- Add directory subgraph clustering in file-level mode (ported from DOT export)
- Add edge labels from edge.kind (imports, calls) with imports-type collapsed to imports
- Add node shapes by kind: stadium for functions/methods, hexagon for classes/structs,
subroutine for modules
- Add role-based styling: entry=green, core=blue, utility=gray, dead=red, leaf=yellow
- Group function-level nodes by file into subgraphs
- Use stable node IDs (n0, n1, ...) for cleaner output
Impact: 4 functions changed, 2 affected
|
Claude finished @carlos-alm's task —— View job PR Review CompleteTodo List:
Phase 1: Root Cause Analysis ✅Problem Being Solved: The existing Mermaid export was limited compared to DOT export - missing subgraph clustering, edge labels, node shapes, and role-based styling. This creates a gap where DOT export had richer features. Why This Approach: The implementation logically ports successful features from DOT export to Mermaid, maintaining consistency. The changes add value without breaking existing functionality. Risk Assessment: Low risk - this is feature enhancement, not bug fixing. Phase 2: Configuration Change Detection ✅Configuration Changes Analysis:
Verdict: Configuration changes are appropriate and safe. Phase 3: Test Integrity Verification ✅Test Changes Analysis:
Verdict: Test integrity is excellent - tests were strengthened, not weakened. Phase 4: Code Quality Review ✅Implementation Analysis (src/export.js:128-314): ✅ Architecture & Design:
✅ Security:
✅ Performance:
✅ Maintainability:
Minor Observations:
No security, performance, or correctness issues identified. Final AssessmentRoot Cause AnalysisProblem Being Solved: Closing the feature gap between Mermaid and DOT exports by adding subgraphs, edge labels, node shapes, and styling - a legitimate enhancement request Why This Approach: Systematic port of proven DOT export features to Mermaid with appropriate adaptations for Mermaid syntax Risk Assessment: Very low risk - pure feature addition with comprehensive test coverage Critical ConcernsNone identified. This is a well-implemented feature enhancement. Final Recommendation
Recommended Actions Before Merge:
This PR significantly enhances the Mermaid export functionality and brings it to parity with the DOT export. Well done! 🎉 |
Greptile SummaryThis PR significantly enhances the Mermaid export functionality to match the feature parity of the DOT export. Key changes include:
All previous review issues have been properly addressed in commits 1c4ca34, b9d6ae4, 4f40eee, and 0c10e23. The implementation includes comprehensive test coverage and follows existing patterns in the codebase. Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
CLI[CLI export command] --> ExportOpts{Parse options}
ExportOpts --> Direction[direction flag]
ExportOpts --> FileLevel[fileLevel flag]
ExportOpts --> Format[format selection]
Format --> Mermaid[exportMermaid]
Format --> DOT[exportDOT]
Format --> JSON[exportJSON]
Mermaid --> FlowchartHeader[Generate flowchart header with direction]
FlowchartHeader --> NodeIDGen[Generate stable node IDs n0, n1...]
NodeIDGen --> FileLevelCheck{fileLevel?}
FileLevelCheck -->|Yes| FileEdges[Query file-level edges]
FileEdges --> DirSubgraphs[Build directory subgraphs]
DirSubgraphs --> EdgeLabels[Add edge labels from kind]
EdgeLabels --> CollapseTypes[Collapse imports-type to imports]
FileLevelCheck -->|No| FnEdges[Query function-level edges]
FnEdges --> FileSubgraphs[Group nodes by file into subgraphs]
FileSubgraphs --> NodeShapes[Apply shapes by kind]
NodeShapes --> CallLabels[Add call edge labels]
CallLabels --> RoleStyles[Apply role-based styling]
CollapseTypes --> Output[Generate Mermaid syntax]
RoleStyles --> Output
Last reviewed commit: 0c10e23 |
src/export.js
Outdated
| // Deduplicate edges per source-target pair, picking the most specific kind | ||
| const edgeMap = new Map(); | ||
| for (const { source, target, edge_kind } of edges) { | ||
| const key = `${source}|${target}`; | ||
| const label = edge_kind === 'imports-type' ? 'imports' : edge_kind; | ||
| if (!edgeMap.has(key)) edgeMap.set(key, { source, target, label }); |
There was a problem hiding this comment.
Comment says "picking the most specific kind" but implementation just takes first encountered edge (via !edgeMap.has(key) check). Either update comment to say "deduplicating edges" or implement actual specificity logic.
| function mermaidShape(kind, label) { | ||
| switch (kind) { | ||
| case 'function': | ||
| case 'method': | ||
| return `(["${label}"])`; | ||
| case 'class': | ||
| case 'interface': | ||
| case 'type': | ||
| case 'struct': | ||
| case 'enum': | ||
| case 'trait': | ||
| case 'record': | ||
| return `{{"${label}"}}`; | ||
| case 'module': | ||
| return `[["${label}"]]`; | ||
| default: | ||
| return `["${label}"]`; | ||
| } |
There was a problem hiding this comment.
Labels with special characters (quotes, brackets) could break Mermaid syntax. Consider escaping double quotes in labels:
| function mermaidShape(kind, label) { | |
| switch (kind) { | |
| case 'function': | |
| case 'method': | |
| return `(["${label}"])`; | |
| case 'class': | |
| case 'interface': | |
| case 'type': | |
| case 'struct': | |
| case 'enum': | |
| case 'trait': | |
| case 'record': | |
| return `{{"${label}"}}`; | |
| case 'module': | |
| return `[["${label}"]]`; | |
| default: | |
| return `["${label}"]`; | |
| } | |
| function mermaidShape(kind, label) { | |
| const escaped = label.replace(/"/g, '#quot;'); | |
| switch (kind) { | |
| case 'function': | |
| case 'method': | |
| return `(["${escaped}"])`; | |
| case 'class': | |
| case 'interface': | |
| case 'type': | |
| case 'struct': | |
| case 'enum': | |
| case 'trait': | |
| case 'record': | |
| return `{{"${escaped}"}}`; | |
| case 'module': | |
| return `[["${escaped}"]]`; | |
| default: | |
| return `["${escaped}"]`; | |
| } | |
| } |
There was a problem hiding this comment.
Fixed in 1c4ca34 — added escapeLabel() that replaces double quotes with #quot;, applied to all label sites.
src/export.js
Outdated
| lines.push(` subgraph ${sgId}["${dir}"]`); | ||
| for (const f of files) { | ||
| const nId = nodeId(f); | ||
| lines.push(` ${nId}["${path.basename(f)}"]`); |
There was a problem hiding this comment.
File basenames with quotes would break Mermaid syntax. Apply same escaping as suggested for mermaidShape.
There was a problem hiding this comment.
Fixed in 1c4ca34 — same escapeLabel() applied to directory names, file basenames, and subgraph titles.
- Add escapeLabel() to replace double quotes with #quot; in all Mermaid labels (node shapes, file basenames, directory names, subgraph titles) - Fix misleading comment: "picking the most specific kind" → "keeping the first encountered kind" Impact: 3 functions changed, 2 affected
|
Claude finished @carlos-alm's task —— View job PR Review CompleteTodo List:
Phase 1: Root Cause Analysis ✅Problem Being Solved: The existing Mermaid export was limited compared to DOT export - missing subgraph clustering, edge labels, node shapes, and role-based styling. This creates a gap where DOT export had richer features. Why This Approach: The implementation logically ports successful features from DOT export to Mermaid, maintaining consistency. The changes add value without breaking existing functionality. Risk Assessment: Low risk - this is feature enhancement, not bug fixing. Phase 2: Configuration Change Detection ✅Configuration Changes Analysis:
Verdict: Configuration changes are appropriate and safe. Phase 3: Test Integrity Verification ✅Test Changes Analysis:
Verdict: Test integrity is excellent - tests were strengthened, not weakened. Phase 4: Code Quality Review ✅Implementation Analysis (src/export.js:128-314): ✅ Architecture & Design:
✅ Security:
✅ Performance:
✅ Maintainability:
Minor Observations:
No security, performance, or correctness issues identified. Final AssessmentRoot Cause AnalysisProblem Being Solved: Closing the feature gap between Mermaid and DOT exports by adding subgraphs, edge labels, node shapes, and styling - a legitimate enhancement request Why This Approach: Systematic port of proven DOT export features to Mermaid with appropriate adaptations for Mermaid syntax Risk Assessment: Very low risk - pure feature addition with comprehensive test coverage Critical ConcernsNone identified. This is a well-implemented feature enhancement. Final Recommendation
Recommended Actions Before Merge:
This PR significantly enhances the Mermaid export functionality and brings it to parity with the DOT export. Well done! 🎉 |
Diff-impact Mermaid for this PRflowchart TB
subgraph sg0["src/builder.js **(modified)**"]
n0["buildGraph"]
end
style sg0 fill:#fff3e0,stroke:#ff9800
subgraph sg1["src/cli.js **(modified)**"]
n2["command:export"]
n3["command:prune"]
n4["command:search"]
n5["command:roles"]
end
style sg1 fill:#fff3e0,stroke:#ff9800
subgraph sg2["src/db.js **(modified)**"]
n6["initSchema"]
end
style sg2 fill:#fff3e0,stroke:#ff9800
subgraph sg3["src/embedder.js **(modified)**"]
n7["globMatch"]
n14["disposeModel"]
n15["loadModel"]
n8["_prepareSearch"]
n12["search"]
end
style sg3 fill:#fff3e0,stroke:#ff9800
subgraph sg4["src/export.js **(modified)**"]
n20["escapeLabel"]
n21["mermaidShape"]
n23["exportMermaid"]
n22["nodeId"]
end
style sg4 fill:#fff3e0,stroke:#ff9800
subgraph sg5["src/mcp.js **(modified)**"]
n13["startMCPServer"]
end
style sg5 fill:#fff3e0,stroke:#ff9800
subgraph sg6["src/queries.js **(modified)**"]
n24["listFunctionsData"]
n25["statsData"]
n26["stats"]
n28["contextData"]
n29["context"]
n31["explainFileImpl"]
n36["mapSymbol"]
n34["explainFunctionImpl"]
n37["explain"]
n39["printFunctionExplain"]
n40["whereSymbolImpl"]
n42["where"]
n44["rolesData"]
n45["roles"]
end
style sg6 fill:#fff3e0,stroke:#ff9800
subgraph sg7["src/registry.js **(modified)**"]
n46["pruneRegistry"]
end
style sg7 fill:#fff3e0,stroke:#ff9800
subgraph sg8["src/structure.js **(modified)**"]
n48["median"]
n49["classifyNodeRoles"]
end
style sg8 fill:#fff3e0,stroke:#ff9800
n9["searchData"]
n16["embed"]
n17["buildEmbeddings"]
n32["getFileLines"]
n41["whereData"]
subgraph sg9["Callers **(blast radius)**"]
n1["command:build"]
n10["multiSearchData"]
n11["benchmarkModel"]
n18["command:embed"]
n19["command:mcp"]
n27["command:stats"]
n30["command:context"]
n33["getFileLines"]
n35["explainCallees"]
n38["command:explain"]
n43["command:where"]
n47["command:list"]
n50["resolveBarrelExport"]
end
style sg9 fill:#f3e5f5,stroke:#9c27b0
n0 --> n1
n6 --> n0
n7 --> n8
n8 --> n9
n8 --> n10
n9 --> n11
n9 --> n12
n9 --> n13
n14 --> n11
n14 --> n15
n15 --> n16
n16 --> n17
n16 --> n9
n16 --> n10
n17 --> n11
n17 --> n18
n12 --> n4
n13 --> n19
n20 --> n21
n20 --> n22
n21 --> n22
n24 --> n13
n25 --> n26
n26 --> n27
n28 --> n13
n28 --> n29
n29 --> n30
n31 --> n32
n32 --> n33
n32 --> n34
n34 --> n35
n34 --> n32
n32 --> n31
n37 --> n38
n40 --> n41
n41 --> n13
n41 --> n42
n42 --> n43
n44 --> n13
n44 --> n45
n45 --> n5
n46 --> n13
n46 --> n47
n46 --> n3
n48 --> n49
n49 --> n50
|
src/export.js
Outdated
| const allKeys = [...nodeIdMap.keys()]; | ||
| const roleStyles = []; | ||
| for (const key of allKeys) { | ||
| const [file, name] = key.split('::'); |
There was a problem hiding this comment.
Splitting on :: will break if file paths or function names contain ::. Use split('::') with a limit or find the first occurrence and slice.
| const [file, name] = key.split('::'); | |
| const colonIdx = key.indexOf('::'); | |
| const file = colonIdx !== -1 ? key.slice(0, colonIdx) : key; | |
| const name = colonIdx !== -1 ? key.slice(colonIdx + 2) : ''; |
There was a problem hiding this comment.
Fixed in b9d6ae4 — now uses indexOf + slice instead of split('::').
Impact: 1 functions changed, 0 affected
|
Claude finished @carlos-alm's task —— View job Claude finished @carlos-alm's task —— View job PR Review CompleteTodo List:
Phase 1: Root Cause Analysis ✅Problem Being Solved: The existing Mermaid export was limited compared to DOT export - missing subgraph clustering, edge labels, node shapes, and role-based styling. This creates a gap where DOT export had richer features. Why This Approach: The implementation logically ports successful features from DOT export to Mermaid, maintaining consistency. The changes add value without breaking existing functionality. Risk Assessment: Low risk - this is feature enhancement, not bug fixing. Phase 2: Configuration Change Detection ✅Configuration Changes Analysis:
Verdict: Configuration changes are appropriate and safe. Phase 3: Test Integrity Verification ✅Test Changes Analysis:
Verdict: Test integrity is excellent - tests were strengthened, not weakened. Phase 4: Code Quality Review ✅Implementation Analysis (src/export.js:128-314): ✅ Architecture & Design:
✅ Security:
✅ Performance:
✅ Maintainability:
Minor Observations:
No security, performance, or correctness issues identified. Final AssessmentRoot Cause AnalysisProblem Being Solved: Closing the feature gap between Mermaid and DOT exports by adding subgraphs, edge labels, node shapes, and styling - a legitimate enhancement request Why This Approach: Systematic port of proven DOT export features to Mermaid with appropriate adaptations for Mermaid syntax Risk Assessment: Very low risk - pure feature addition with comprehensive test coverage Critical ConcernsNone identified. This is a well-implemented feature enhancement. Final Recommendation
Recommended Actions Before Merge:
This PR significantly enhances the Mermaid export functionality and brings it to parity with the DOT export. Well done! 🎉 |
… first Impact: 1 functions changed, 0 affected
|
Claude finished @carlos-alm's task —— View job PR Review CompleteTodo List:
Phase 1: Root Cause Analysis ✅Problem Being Solved: The existing Mermaid export was limited compared to DOT export - missing subgraph clustering, edge labels, node shapes, and role-based styling. This creates a gap where DOT export had richer features. Why This Approach: The implementation logically ports successful features from DOT export to Mermaid, maintaining consistency. The changes add value without breaking existing functionality. Risk Assessment: Low risk - this is feature enhancement, not bug fixing. Phase 2: Configuration Change Detection ✅Configuration Changes Analysis:
Verdict: Configuration changes are appropriate and safe. Phase 3: Test Integrity Verification ✅Test Changes Analysis:
Verdict: Test integrity is excellent - tests were strengthened, not weakened. Phase 4: Code Quality Review ✅Implementation Analysis (src/export.js:128-314): ✅ Architecture & Design:
✅ Security:
✅ Performance:
✅ Maintainability:
Minor Observations:
No security, performance, or correctness issues identified. Final AssessmentRoot Cause AnalysisProblem Being Solved: Closing the feature gap between Mermaid and DOT exports by adding subgraphs, edge labels, node shapes, and styling - a legitimate enhancement request Why This Approach: Systematic port of proven DOT export features to Mermaid with appropriate adaptations for Mermaid syntax Risk Assessment: Very low risk - pure feature addition with comprehensive test coverage Critical ConcernsNone identified. This is a well-implemented feature enhancement. Final Recommendation
Recommended Actions Before Merge:
This PR significantly enhances the Mermaid export functionality and brings it to parity with the DOT export. Well done! 🎉 |
| for (const key of allKeys) { | ||
| const colonIdx = key.indexOf('::'); | ||
| const file = colonIdx !== -1 ? key.slice(0, colonIdx) : key; | ||
| const name = colonIdx !== -1 ? key.slice(colonIdx + 2) : ''; | ||
| const row = db | ||
| .prepare('SELECT role FROM nodes WHERE file = ? AND name = ? AND role IS NOT NULL LIMIT 1') | ||
| .get(file, name); |
There was a problem hiding this comment.
role lookup logic assumes all keys in nodeIdMap for function-level mode will have :: separators, but file-level keys don't. if file nodes end up in the map during function mode (shouldn't happen based on current SQL, but defensive code would help), the query will search for file=<full-key> and name='' which will never match
| for (const key of allKeys) { | |
| const colonIdx = key.indexOf('::'); | |
| const file = colonIdx !== -1 ? key.slice(0, colonIdx) : key; | |
| const name = colonIdx !== -1 ? key.slice(colonIdx + 2) : ''; | |
| const row = db | |
| .prepare('SELECT role FROM nodes WHERE file = ? AND name = ? AND role IS NOT NULL LIMIT 1') | |
| .get(file, name); | |
| for (const key of allKeys) { | |
| const colonIdx = key.indexOf('::'); | |
| if (colonIdx === -1) continue; // Skip file-level keys | |
| const file = key.slice(0, colonIdx); | |
| const name = key.slice(colonIdx + 2); |
There was a problem hiding this comment.
Fixed in 0c10e23 — keys without :: are now skipped with continue.
Impact: 1 functions changed, 0 affected
|
Claude finished @carlos-alm's task —— View job PR Review CompleteTodo List:
Phase 1: Root Cause Analysis ✅Problem Being Solved: The existing Mermaid export was limited compared to DOT export - missing subgraph clustering, edge labels, node shapes, and role-based styling. This creates a gap where DOT export had richer features. Why This Approach: The implementation logically ports successful features from DOT export to Mermaid, maintaining consistency. The changes add value without breaking existing functionality. Risk Assessment: Low risk - this is feature enhancement, not bug fixing. Phase 2: Configuration Change Detection ✅Configuration Changes Analysis:
Verdict: Configuration changes are appropriate and safe. Phase 3: Test Integrity Verification ✅Test Changes Analysis:
Verdict: Test integrity is excellent - tests were strengthened, not weakened. Phase 4: Code Quality Review ✅Implementation Analysis (src/export.js:128-314): ✅ Architecture & Design:
✅ Security:
✅ Performance:
✅ Maintainability:
Minor Observations:
No security, performance, or correctness issues identified. Final AssessmentRoot Cause AnalysisProblem Being Solved: Closing the feature gap between Mermaid and DOT exports by adding subgraphs, edge labels, node shapes, and styling - a legitimate enhancement request Why This Approach: Systematic port of proven DOT export features to Mermaid with appropriate adaptations for Mermaid syntax Risk Assessment: Very low risk - pure feature addition with comprehensive test coverage Critical ConcernsNone identified. This is a well-implemented feature enhancement. Final Recommendation
Recommended Actions Before Merge:
This PR significantly enhances the Mermaid export functionality and brings it to parity with the DOT export. Well done! 🎉 |
Summary
graph LRtoflowchart {direction}with a new--directionCLI option (default:LR)edge.kind(imports,calls), collapsingimports-typetoimports([])for functions/methods, hexagon{{}}for classes/structs/enums/etc, subroutine[[]]for modulesn0,n1, ...) for cleaner outputCloses the gap between our Mermaid export and the DOT export, which already had subgraph clustering and edge metadata. Inspired by the rich Mermaid flowchart in PR #95's Greptile review.
Test plan
npm test)npm run lint)node src/cli.js export -f mermaid -Toutput into mermaid.live to confirm rendering--direction TBproducesflowchart TB--functionsmode shows shapes, subgraphs, edge labels, and role colors