
Mermaid Graph Writer
- 103 installs
- 178 repo stars
- Updated July 14, 2026
- erichowens/some_claude_skills
Helps with ai & agent building tasks.
About
mermaid-graph-writer is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- mermaid-graph-writer
- AI & Agent Building
- AI-coding skill
Mermaid Graph Writer by the numbers
- 103 all-time installs (skills.sh)
- Ranked #4,276 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill mermaid-graph-writerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 103 |
|---|---|
| repo stars | ★ 178 |
| Last updated | July 14, 2026 |
| Repository | erichowens/some_claude_skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Mermaid Graph Writer
Writes precise, well-structured Mermaid diagrams. Selects the optimal diagram type for the content, uses correct syntax, and produces diagrams that are readable by both humans (rendered) and agents (text DSL).
---
When to Use
✅ Use for:
- Creating any Mermaid diagram from a description or data
- Choosing the right diagram type for a visualization need
- Refactoring prose decision trees into Mermaid flowcharts
- Modeling system architectures, protocols, state machines, data models
- Encoding temporal knowledge as timeline diagrams
❌ NOT for:
- Rendering/exporting Mermaid to PNG/SVG/PDF (use
mermaid-graph-renderer) - ASCII art or Unicode box-drawing (use
diagramming-expert) - GUI-based design tools (Figma, etc.)
---
Diagram Type Selection
flowchart TD
A{What are you modeling?} -->|Branching logic| B[flowchart]
A -->|Request/response over time| C[sequenceDiagram]
A -->|States and transitions| D[stateDiagram-v2]
A -->|Entities and relationships| E[erDiagram]
A -->|Chronological evolution| F[timeline]
A -->|Concept hierarchy| G[mindmap]
A -->|Time-sequenced tasks| H[gantt]
A -->|Proportions| I[pie]
A -->|2-axis comparison| J[quadrantChart]
A -->|Branch/merge history| K[gitGraph]
A -->|Type hierarchy / OO| L[classDiagram]
A -->|User experience steps| M[journey]
A -->|Quantity flows| N[sankey-beta]
A -->|Numeric data| O[xychart-beta]
A -->|System components| P[block-beta]
A -->|Infrastructure| Q[architecture-beta]
A -->|Task board| R[kanban]
A -->|Requirements traceability| S[requirementDiagram]
A -->|System context / containers| T[C4Context]
A -->|Protocol packets / headers| U[packet-beta]
A -->|Multi-axis comparison| V[radar]
A -->|Hierarchical proportions| W[treemap]
A -->|Code-style sequences| X[zenuml]Quick Reference
| Content | Type | Direction |
|---|---|---|
| Decision tree / process | flowchart TD | Top-down for decisions, LR for processes |
| API protocol / agent comms | sequenceDiagram | Always vertical (implicit) |
| Lifecycle / status machine | stateDiagram-v2 | Automatic layout |
| Database / data model | erDiagram | Automatic layout |
| "What changed when" | timeline | Horizontal chronological |
| Taxonomy / brainstorm | mindmap | Radial from root |
| Project schedule | gantt | Horizontal timeline |
| Category proportions | pie | Circular |
| Effort vs. impact | quadrantChart | 2D scatter |
| Git branching strategy | gitGraph | Horizontal |
| Class/interface hierarchy | classDiagram | Automatic |
| User flow with satisfaction | journey | Horizontal sections |
| Flow quantities between categories | sankey-beta | Left-to-right flow |
| Bar/line charts | xychart-beta | Standard axes |
| System block layout | block-beta | Grid-based |
| Cloud/infra topology | architecture-beta | Grouped services |
| Task status columns | kanban | Column-based |
| Requirements traceability | requirementDiagram | Automatic layout |
| System context / containers | C4Context / C4Container | Layered (5 sub-types) |
| Protocol packets / headers | packet-beta | Horizontal bit layout |
| Multi-axis scoring | radar | Radial axes |
| Hierarchical proportions | treemap | Nested rectangles |
| Code-style sequences | zenuml | Vertical (plugin) |
---
Flowchart Deep Dive (Most Common)
Direction
TD/TB— top-down (best for decision trees)LR— left-right (best for processes, pipelines)BT— bottom-up (rare, for dependency graphs)RL— right-left (rare)
Node Shapes
[text] Rectangle (default action)
(text) Rounded rectangle (soft step)
{text} Diamond (decision/condition)
([text]) Stadium/pill (start/end)
[[text]] Subroutine (subprocess)
[(text)] Cylinder (database/storage)
((text)) Circle (event/trigger)
>text] Flag (async/signal)
{{text}} Hexagon (preparation)
[/text/] Parallelogram (input/output)
[\text\] Reverse parallelogram
[/text\] Trapezoid
[\text/] Reverse trapezoidEdge Styles
--> Solid arrow (main flow)
--- Solid line (association)
-.-> Dotted arrow (optional/async)
==> Thick arrow (emphasis/critical path)
--text--> Labeled edge
~~~ Invisible link (layout control only)Subgraphs
flowchart TD
subgraph Backend
A[API] --> B[DB]
end
subgraph Frontend
C[UI] --> D[State]
end
C -->|fetch| A---
Sequence Diagram Essentials
Messages
->> Solid arrow (sync request)
-->> Dotted arrow (async response)
-) Open arrow (async fire-and-forget)
-x Cross (failed/rejected)Blocks
activate / deactivate Lifeline activation
alt / else / end Conditional branching
loop / end Repetition
par / and / end Parallel execution
critical / end Critical section
break / end Break-out flow
rect rgb(...) / end Background highlightNumbering
Add autonumber after the first line to auto-number all messages.
---
State Diagram Essentials
[*] --> State1 Start transition
State1 --> State2 Named transition
State1 --> State2: event Labeled transition
State2 --> [*] End transition
state State1 { Nested states
[*] --> SubA
SubA --> SubB
}
state fork <<fork>> Fork pseudostate
state join <<join>> Join pseudostate
state choice <<choice>> Choice pseudostate---
ER Diagram Essentials
Cardinality
||--|| Exactly one to exactly one
||--o{ One to zero-or-many
}o--o{ Zero-or-many to zero-or-many
||--|{ One to one-or-manyAttributes
erDiagram
USER {
int id PK
string name
string email UK
}
ORDER {
int id PK
int user_id FK
date created_at
}
USER ||--o{ ORDER : places---
Writing Principles
1. Descriptive Labels, Not Codes
- ✅
A[Check if tests pass] - ❌
A[Step 2.3]
2. Consistent Direction
Pick one direction for the whole diagram. Don't mix TD and LR within the same flowchart.
3. Max 15 Nodes per Diagram
Beyond 15 nodes, split into multiple diagrams or use subgraphs. A crowded diagram is worse than no diagram.
4. Use Subgraphs for Grouping
When a diagram has natural clusters (frontend/backend, phases, teams), use subgraphs to group them visually.
5. Label All Decision Edges
Every edge leaving a diamond ({decision}) node must have a label:
- ✅
A{Ready?} -->|Yes| BandA -->|No| C - ❌
A{Ready?} --> BandA --> C(which is yes? which is no?)
6. Use Appropriate Edge Styles
- Solid arrows for main flow
- Dotted arrows for optional/async paths
- Thick arrows for critical paths or emphasis
- Invisible links (
~~~) only for layout tweaking
---
Anti-Patterns
Wrong Diagram Type
Novice: Using a flowchart for everything — even protocols, state machines, and data models. Expert: Match diagram type to content structure. Sequence diagrams for protocols. State diagrams for lifecycle. ER for data models. Each type exists because flowcharts can't express that structure well.
Overcrowded Diagram
Novice: One diagram with 30 nodes and crossing edges. Expert: Split into overview diagram + detail diagrams. Use subgraphs. Max ~15 nodes per diagram.
Unlabeled Decision Edges
Novice: {Decision} --> A and {Decision} --> B — which condition leads where? Expert: Always label edges from decision diamonds: -->|Yes| and -->|No| (or -->|Success| and -->|Failure|, etc.)
Prose That Should Be a Diagram
Novice: "First check if X. If X then do A, otherwise do B. Then if A succeeds, do C, otherwise retry A." Expert: That's a flowchart. Write it as one. The formal graph is more precise AND more readable.
---
References
references/diagram-types.md— Consult for comprehensive syntax, features, and examples for all 23 Mermaid diagram types: timeline, mindmap, quadrant, sankey, XY chart, block, architecture, kanban, pie, gitgraph, class, journey, requirementDiagram, C4 (5 sub-types), packet-beta, radar, treemap, and zenumlscripts/validate_mermaid.py— Validates Mermaid syntax in any file: checks diagram type declarations, matching fences, structural correctness
Changelog: mermaid-graph-writer
v1.0.0 (2026-02-05)
Created
- Diagram type selection decision tree (Mermaid flowchart)
- Quick reference table for all 16+ diagram types
- Deep dive on flowchart (node shapes, edge styles, subgraphs, direction)
- Sequence diagram essentials (messages, blocks, autonumber)
- State diagram essentials (transitions, nesting, pseudostates)
- ER diagram essentials (cardinality, attributes)
- 6 writing principles (descriptive labels, max 15 nodes, subgraphs, etc.)
- 4 anti-patterns (wrong type, overcrowded, unlabeled edges, prose-that-should-be-diagram)
Complete Mermaid Diagram Type Reference
Comprehensive syntax reference for all 23 Mermaid diagram types. Each section includes the declaration syntax, key features, gotchas, and a working example.
Last updated: 2026-03
---
1. Flowchart
Declaration: flowchart TD (or TB, LR, BT, RL)
Covered in detail in SKILL.md. See the Flowchart Deep Dive section.
---
2. Sequence Diagram
Declaration: sequenceDiagram
Covered in detail in SKILL.md. See the Sequence Diagram Essentials section.
---
3. Class Diagram
Declaration: classDiagram
classDiagram
class Animal {
+String name
+int age
+makeSound() void
}
class Dog {
+fetch() void
}
class Cat {
+purr() void
}
Animal <|-- Dog : extends
Animal <|-- Cat : extends
Animal "1" --> "*" Food : eatsKey Syntax
class ClassName {
+publicField type # + public
-privateField type # - private
#protectedField type # # protected
~packageField type # ~ package/internal
+methodName() returnType
+abstractMethod()* void # * = abstract
+staticMethod()$ void # $ = static
}Relationships
<|-- Inheritance (extends)
*-- Composition (has, owns lifecycle)
o-- Aggregation (has, separate lifecycle)
--> Association (uses)
-- Link (bidirectional)
..> Dependency (uses temporarily)
..|> Realization (implements)Cardinality
"1" --> "*" One to many
"1" --> "0..1" One to zero-or-one
"*" --> "*" Many to manyGotchas
- Generic types need HTML encoding:
List~String~notList<String> - Mermaid uses
~for generics, not angle brackets - Namespace support: wrap classes in
namespace MyNamespace { ... }
---
4. State Diagram
Declaration: stateDiagram-v2
Covered in detail in SKILL.md. See the State Diagram Essentials section.
---
5. Entity Relationship Diagram
Declaration: erDiagram
Covered in detail in SKILL.md. See the ER Diagram Essentials section.
---
6. User Journey
Declaration: journey
journey
title User Onboarding Flow
section Sign Up
Visit landing page: 5: User
Click sign up: 4: User
Fill form: 2: User
Verify email: 3: User
section First Use
Complete tutorial: 4: User
Create first project: 5: User
Invite team member: 3: UserSyntax
journey
title [Title text]
section [Section Name]
[Task description]: [score 1-5]: [Actor1, Actor2]- Score: 1 (frustrated) to 5 (delighted)
- Multiple actors: comma-separated
- Sections group related tasks visually
Gotchas
- No colons in task descriptions (use dashes instead)
- Actors must be consistent across the diagram
- Score is required for every task
---
7. Gantt Chart
Declaration: gantt
gantt
title Project Timeline
dateFormat YYYY-MM-DD
excludes weekends
section Planning
Requirements :done, req, 2026-01-01, 14d
Design :active, des, after req, 10d
section Development
Backend API :dev1, after des, 21d
Frontend :dev2, after des, 18d
Integration :int, after dev1, 7d
section Launch
Testing :test, after int, 14d
Deployment :milestone, deploy, after test, 0dTask Syntax
Task name :status, id, start, duration- Status:
done,active,crit(critical), or omit for default - Start:
YYYY-MM-DDorafter taskId - Duration:
Nd(days),Nw(weeks), or end date - Milestone: Duration of
0d
Date Formats
dateFormat YYYY-MM-DD # Default
dateFormat DD-MM-YYYY # European
dateFormat X # Unix timestamp
axisFormat %b %d # Axis display formatGotchas
excludes weekendsis separate fromexcludes 2026-12-25- Task IDs are optional but needed for
afterreferences - Commas in task names break parsing — avoid them
---
8. Pie Chart
Declaration: pie
pie title Skills by Category
"SWE" : 30
"Domain" : 80
"Creative" : 25
"WinDAGs" : 7
"Recovery" : 12Syntax
pie [showData] [title Title Text]
"Label" : valueshowData— optional, shows percentages on slices- Values are absolute — Mermaid calculates percentages
- Labels must be quoted
Gotchas
- No negative values
- Labels MUST be in quotes
showDatakeyword goes BEFOREtitle
---
9. Quadrant Chart
Declaration: quadrantChart
quadrantChart
title Skill Priority Matrix
x-axis Low Effort --> High Effort
y-axis Low Impact --> High Impact
quadrant-1 Do First
quadrant-2 Schedule
quadrant-3 Delegate
quadrant-4 Eliminate
Add NOT clauses: [0.2, 0.8]
Add Mermaid diagrams: [0.3, 0.9]
Rewrite descriptions: [0.5, 0.7]
Create reference files: [0.7, 0.6]
Add CHANGELOGs: [0.1, 0.3]Syntax
quadrantChart
title [Title]
x-axis [Low Label] --> [High Label]
y-axis [Low Label] --> [High Label]
quadrant-1 [Top-right label]
quadrant-2 [Top-left label]
quadrant-3 [Bottom-left label]
quadrant-4 [Bottom-right label]
[Point name]: [x, y] # x and y are 0.0 to 1.0Gotchas
- Quadrant numbering: 1=top-right, 2=top-left, 3=bottom-left, 4=bottom-right (counterclockwise from top-right)
- Coordinates are 0.0 to 1.0 (normalized)
- Point names cannot contain colons
---
10. Requirement Diagram
Declaration: requirementDiagram
requirementDiagram
requirement Auth System {
id: REQ-001
text: Users must authenticate before accessing protected resources
risk: high
verifymethod: test
}
requirement Token Expiry {
id: REQ-002
text: Auth tokens must expire within 24 hours
risk: medium
verifymethod: inspection
}
element Auth Service {
type: microservice
}
Auth Service - satisfies -> Auth System
Auth Service - satisfies -> Token ExpiryElement Types
requirement Standard requirement
functionalRequirement
performanceRequirement
interfaceRequirement
physicalRequirement
designConstraint
element Implementation elementRelationship Types
- contains -> Parent contains child
- copies -> Derived copy
- derives -> Derived requirement
- satisfies -> Element satisfies requirement
- verifies -> Element verifies requirement
- refines -> More specific version
- traces -> Traceability linkVerify Methods
verifymethod: analysis | demonstration | inspection | testRisk Levels
risk: low | medium | highGotchas
- All fields (
id,text,risk,verifymethod) are required - Relationship arrows use
- verb ->with spaces around the dash - Element
typeis freeform text
---
11. Git Graph
Declaration: gitGraph
gitGraph
commit id: "initial"
branch develop
checkout develop
commit id: "feature-start"
branch feature/auth
checkout feature/auth
commit id: "add-login"
commit id: "add-oauth"
checkout develop
merge feature/auth
checkout main
merge develop tag: "v1.0"Commands
commit [id: "label"] [tag: "tag"] [type: NORMAL|REVERSE|HIGHLIGHT]
branch branchName
checkout branchName
merge branchName [id: "label"] [tag: "tag"]
cherry-pick id: "commitId"Gotchas
- Branch names cannot contain spaces
checkoutis required before committing to a branchmergemerges INTO the currently checked-out branch- Cherry-pick requires the commit to have an explicit
id
---
12. Mindmap
Declaration: mindmap
mindmap
root((Claude Skills))
SWE
error-handling
caching
database-design
TypeScript patterns
Domain
Legal
Medical
Recovery
Creative
Pixel art
Design systems
Typography
WinDAGs
Architect
Evaluator
DecomposerNode Shapes
root((text)) Circle (root node)
text Rectangle (default)
(text) Rounded rectangle
[text] Square
{{text}} Bang/hexagon
)text( CloudStructure
- Indentation defines hierarchy (spaces, not tabs)
- Each deeper indent = child of the line above
- No explicit edge syntax — hierarchy IS the structure
Gotchas
- Indentation MUST be consistent (use spaces)
- No edge labels or styling
- Cannot add links between non-parent/child nodes
- Special characters in text need escaping
---
13. Timeline
Declaration: timeline
timeline
title Mermaid Version History
2019 : Mermaid 8.0
: Class diagrams added
2021 : Mermaid 9.0
: State diagrams v2
2023 : Mermaid 10.0
: Mindmaps, timelines
: Quadrant charts
2025 : Mermaid 11.0
: Kanban, radar, treemapSyntax
timeline
title [Title]
[Time Period] : [Event 1]
: [Event 2]- Time periods are left-aligned
- Events are indented with
:prefix - Multiple events per time period by repeating
:lines
Sections
timeline
section Phase 1
2024 : Event A
section Phase 2
2025 : Event BGotchas
- Time periods are strings (free text, not parsed as dates)
- No links or connections between events
- Keep events concise — long text wraps poorly
---
14. Sankey Diagram
Declaration: sankey-beta
sankey-beta
Source A,Target X,50
Source A,Target Y,30
Source B,Target X,20
Source B,Target Z,40
Target X,Final,70
Target Y,Final,30
Target Z,Final,40Syntax
sankey-beta
Source,Target,Value- CSV format: source node, target node, flow value
- Nodes are created implicitly from source/target names
- Flow width proportional to value
Gotchas
- Header line
sankey-betamust be followed by a blank line - No spaces around commas in data rows
- Node names cannot contain commas
- Values must be positive numbers
- Flows go left-to-right (cannot reverse)
---
15. XY Chart
Declaration: xychart-beta
xychart-beta
title "Skills by Month"
x-axis [Jan, Feb, Mar, Apr, May, Jun]
y-axis "Count" 0 --> 50
bar [10, 15, 22, 30, 35, 42]
line [10, 15, 22, 30, 35, 42]Syntax
xychart-beta [horizontal]
title "Chart Title"
x-axis "Label" [val1, val2, ...] # Categorical
x-axis "Label" min --> max # Numeric range
y-axis "Label" min --> max
bar [data1, data2, ...]
line [data1, data2, ...]horizontalkeyword rotates the chart 90 degrees- Multiple
barandlineseries supported - Data arrays must match x-axis category count
Gotchas
- Category labels and data arrays use square brackets
- Numeric ranges use
-->(not..or-) - Title and axis labels should be quoted
horizontalgoes on the declaration line, not inside
---
16. Block Diagram
Declaration: block-beta
block-beta
columns 3
A["Frontend"] B["API Gateway"] C["Auth Service"]
D["Database"]:3
A --> B
B --> C
B --> DSyntax
block-beta
columns N # Grid columns
A["Label"] # Block with label
A["Label"]:N # Block spanning N columns
space # Empty grid cell
space:N # N empty cells
block:groupId
E["Child"]
end
A --> B # ConnectionBlock Shapes
A["text"] Rectangle (default)
A("text") Rounded
A(("text")) Circle
A{"text"} Diamond
A{{"text"}} Hexagon
A[/"text"/] Parallelogram
A>"text"] FlagGotchas
columnsmust be declared before any blocks- Blocks fill left-to-right, top-to-bottom in the grid
:Nspan syntax MUST be adjacent to the block (no space)- Connections must reference block IDs, not labels
---
17. Architecture Diagram
Declaration: architecture-beta
architecture-beta
group cloud(cloud)[Cloud Infrastructure]
group api(server)[API Layer] in cloud
service web(internet)[Web App] in cloud
service gateway(server)[API Gateway] in api
service auth(lock)[Auth] in api
service db(database)[PostgreSQL] in cloud
web:R --> L:gateway
gateway:R --> L:auth
gateway:B --> T:dbSyntax
architecture-beta
group groupId(icon)[Label]
group groupId(icon)[Label] in parentGroup
service serviceId(icon)[Label]
service serviceId(icon)[Label] in groupId
serviceA:edge --> edge:serviceBEdge Positions
T = Top, B = Bottom, L = Left, R = RightIcons
Built-in: cloud, database, disk, internet, server, lock
Custom icons via iconify (requires configuration).
Gotchas
- Edge syntax uses
:positionon BOTH sides:A:R --> L:B - Groups can nest (use
in parentGroup) - Only
-->edges (no dotted, no labels) - Icon names are from a limited built-in set
---
18. Kanban
Declaration: kanban
kanban
Todo
task1[Add NOT clauses]
task2[Create reference files]
In Progress
task3[Update Mermaid coverage]
Done
task4[Grade all skills]
task5[Fix bottom 8 skills]Syntax
kanban
Column Name
taskId[Task Label]
taskId[Task Label]@{ priority: high }
Another Column
taskId[Task Label]Metadata (Optional)
taskId[Label]@{ assignee: "name", priority: "high", ticket: "PROJ-123" }Gotchas
- Column names are unindented, tasks are indented
- Task IDs must be unique across all columns
- No connections between tasks
- Column order = display order (left to right)
- Metadata support varies by renderer
---
19. C4 Diagram (5 Sub-Types)
C4 Context
Declaration: C4Context
C4Context
title System Context Diagram
Person(user, "End User", "Uses the web app")
System(webapp, "Web Application", "Main product")
System_Ext(email, "Email Service", "Sends notifications")
System_Ext(payment, "Payment Gateway", "Processes payments")
Rel(user, webapp, "Uses", "HTTPS")
Rel(webapp, email, "Sends emails via", "SMTP")
Rel(webapp, payment, "Processes payments", "API")C4 Container
Declaration: C4Container
C4Container
title Container Diagram
Person(user, "User")
System_Boundary(webapp, "Web Application") {
Container(spa, "SPA", "React", "User interface")
Container(api, "API", "Node.js", "Business logic")
ContainerDb(db, "Database", "PostgreSQL", "Stores data")
}
Rel(user, spa, "Uses")
Rel(spa, api, "Calls", "JSON/HTTPS")
Rel(api, db, "Reads/Writes", "SQL")All 5 C4 Sub-Types
| Type | Declaration | Scope |
|---|---|---|
| Context | C4Context | System landscape — people, systems, external dependencies |
| Container | C4Container | Inside one system — apps, databases, APIs |
| Component | C4Component | Inside one container — modules, classes, services |
| Dynamic | C4Dynamic | Runtime interactions — numbered sequence of calls |
| Deployment | C4Deployment | Infrastructure — nodes, containers, deployment targets |
Elements
Person(id, "Label", "Description")
Person_Ext(id, "Label", "Description")
System(id, "Label", "Description")
System_Ext(id, "Label", "Description")
System_Boundary(id, "Label") { ... }
Container(id, "Label", "Technology", "Description")
ContainerDb(id, "Label", "Technology", "Description")
ContainerQueue(id, "Label", "Technology", "Description")
Component(id, "Label", "Technology", "Description")Relationships
Rel(from, to, "Label")
Rel(from, to, "Label", "Technology")
Rel_D(from, to, "Label") # Down
Rel_U(from, to, "Label") # Up
Rel_L(from, to, "Label") # Left
Rel_R(from, to, "Label") # RightGotchas
- C4 diagrams use FUNCTION CALL syntax, not Mermaid's usual
A --> B System_Boundaryuses curly braces{ }, notend- Description and technology fields are optional but recommended
_Extsuffix marks external systems/people (different styling)
---
20. Packet Diagram
Declaration: packet-beta
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
100-103: "Reserved"
104-104: "CWR"
105-105: "ECE"
106-106: "URG"
107-107: "ACK"
108-108: "PSH"
109-109: "RST"
110-110: "SYN"
111-111: "FIN"
112-127: "Window Size"Syntax
packet-beta
start-end: "Label"- Bit ranges define field positions
- Single-bit fields:
N-N: "Label" - Multi-bit fields:
start-end: "Label" - Fields render as a grid showing bit positions
Gotchas
- Bit ranges must be non-overlapping and sequential
- Labels must be quoted
- Row width defaults to 32 bits (configurable via
bitWidthin config) - Primarily used for network protocol documentation
---
21. Radar Chart
Declaration: radar
radar
title Skill Quality Assessment
axis Description, Scope, Disclosure, Anti-Patterns, Tools, Activation, Visual, Output, Temporal, Docs
curve skill-architect [95, 97, 90, 92, 88, 96, 93, 85, 82, 88]
curve code-architecture [87, 90, 80, 82, 70, 85, 88, 75, 60, 72]
curve mermaid-graph-writer [90, 92, 85, 88, 70, 90, 95, 80, 65, 78]Syntax
radar
title [Title]
axis Label1, Label2, Label3, ...
curve SeriesName [val1, val2, val3, ...]
curve AnotherSeries [val1, val2, val3, ...]- Multiple curves overlay on the same radar
- Values should be on the same scale (e.g., 0-100)
- Axis count must match value count per curve
Gotchas
- Axis labels are comma-separated on ONE line
- Curve values use square brackets
- Series name cannot contain spaces (use hyphens)
- Minimum 3 axes for a meaningful radar
---
22. Treemap
Declaration: treemap
treemap
root[Skill Library]
SWE[SWE Skills]
error-handling[Error Handling]
caching[Caching]
database[Database Design]
typescript[TypeScript]
Domain[Domain Skills]
legal[Legal]
medical[Medical]
recovery[Recovery]
Creative[Creative Skills]
pixel-art[Pixel Art]
design[Design Systems]Syntax
treemap
root[Root Label]
child1[Child Label]
grandchild1[Label]
child2[Child Label]- Indentation defines hierarchy (like mindmap)
- Rectangle sizes proportional to leaf count or values
- Labels use square brackets
Gotchas
- Indentation must be consistent
- No explicit size values in basic syntax (proportional to children)
- No connections or links
- Best for showing hierarchical composition
---
23. ZenUML (Plugin)
Declaration: zenuml
zenuml
@Actor User
@Boundary WebApp
@Service AuthService
@Database UserDB
User->WebApp.login(username, password) {
WebApp->AuthService.validate(username, password) {
AuthService->UserDB.findUser(username)
if (valid) {
return token
} else {
throw AuthError
}
}
}Syntax
ZenUML uses a code-like syntax instead of Mermaid's usual DSL:
@Actor ParticipantName
@Boundary ParticipantName
@Service ParticipantName
@Database ParticipantName
ParticipantA->ParticipantB.methodName(args) {
// Nested calls
if (condition) {
return value
}
}Participant Stereotypes
@Actor Person/user
@Boundary System boundary (UI, API gateway)
@Control Controller/coordinator
@Entity Domain entity
@Service Service component
@Database Data storeControl Flow
if (condition) { ... } else { ... }
while (condition) { ... }
try { ... } catch (error) { ... }
par { ... } # ParallelGotchas
- Plugin: ZenUML is an external Mermaid plugin — not available in all renderers
- Uses curly braces
{ }for nesting (code-like), notactivate/deactivate - Method call syntax:
A->B.method(args)notA->>B: method(args) - Check renderer compatibility before using
---
Summary Table
| # | Type | Declaration | Best For |
|---|---|---|---|
| 1 | Flowchart | flowchart TD/LR | Decision trees, processes, pipelines |
| 2 | Sequence | sequenceDiagram | API calls, protocols, agent communication |
| 3 | Class | classDiagram | OO hierarchies, interfaces, type systems |
| 4 | State | stateDiagram-v2 | Lifecycles, status machines, FSMs |
| 5 | ER | erDiagram | Database schemas, data models |
| 6 | Journey | journey | User experience, satisfaction mapping |
| 7 | Gantt | gantt | Project timelines, schedules |
| 8 | Pie | pie | Category proportions, distributions |
| 9 | Quadrant | quadrantChart | Priority matrices, 2-axis comparison |
| 10 | Requirement | requirementDiagram | Requirements traceability |
| 11 | Git Graph | gitGraph | Branching strategies, release flows |
| 12 | Mindmap | mindmap | Taxonomies, brainstorms, concept maps |
| 13 | Timeline | timeline | Chronological events, version history |
| 14 | Sankey | sankey-beta | Flow quantities, budget allocation |
| 15 | XY Chart | xychart-beta | Bar/line charts, numeric data |
| 16 | Block | block-beta | System layouts, grid-based architectures |
| 17 | Architecture | architecture-beta | Cloud/infra topology, service maps |
| 18 | Kanban | kanban | Task boards, workflow columns |
| 19 | C4 | C4Context + 4 | System context, containers, components |
| 20 | Packet | packet-beta | Network protocols, binary layouts |
| 21 | Radar | radar | Multi-axis scoring, skill comparisons |
| 22 | Treemap | treemap | Hierarchical proportions |
| 23 | ZenUML | zenuml | Code-style sequence diagrams (plugin) |
---
Stability Tiers
| Tier | Types | Notes |
|---|---|---|
| Stable | flowchart, sequenceDiagram, classDiagram, stateDiagram-v2, erDiagram, journey, gantt, pie, gitGraph, mindmap, timeline, requirementDiagram | Production-safe, syntax frozen |
| Beta | quadrantChart, sankey-beta, xychart-beta, block-beta, architecture-beta, packet-beta, kanban, radar, treemap | Syntax may change. The -beta suffix is literal. |
| Plugin | zenuml, C4Context/Container/Component/Dynamic/Deployment | Require external plugins or specific renderer support |
#!/usr/bin/env python3
"""
Mermaid Syntax Validator — Structural Validation Without Rendering
Validates Mermaid diagram blocks in markdown files for common structural errors
that cause render failures. Does NOT require a Mermaid renderer — works purely
on syntax analysis.
Usage:
python scripts/validate_mermaid.py <file.md>
python scripts/validate_mermaid.py <directory> --recursive
python scripts/validate_mermaid.py --skill <skill-path>
python scripts/validate_mermaid.py --all-skills
Examples:
python validate_mermaid.py SKILL.md
python validate_mermaid.py references/
python validate_mermaid.py --skill .claude/skills/mermaid-graph-writer
python validate_mermaid.py --all-skills --errors-only
"""
import argparse
import json
import re
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional, Tuple
# ──────────────────────────────────────────────────────────────────────
# All 23 Mermaid Diagram Types — Valid Declaration Keywords
# ──────────────────────────────────────────────────────────────────────
VALID_DIAGRAM_TYPES = {
# Stable
"flowchart",
"graph", # Legacy alias for flowchart
"sequenceDiagram",
"classDiagram",
"stateDiagram",
"stateDiagram-v2",
"erDiagram",
"journey",
"gantt",
"pie",
"requirementDiagram",
"gitGraph",
"mindmap",
"timeline",
# Beta
"quadrantChart",
"sankey-beta",
"xychart-beta",
"block-beta",
"architecture-beta",
"packet-beta",
"kanban",
"radar",
"treemap",
# Plugin / C4
"zenuml",
"C4Context",
"C4Container",
"C4Component",
"C4Dynamic",
"C4Deployment",
}
# Diagram types that use directional suffixes
DIRECTIONAL_TYPES = {"flowchart", "graph"}
VALID_DIRECTIONS = {"TD", "TB", "LR", "RL", "BT"}
# Diagram types that use curly-brace blocks
CURLY_BRACE_TYPES = {"zenuml"}
# Diagram types that use indentation-based structure
INDENT_TYPES = {"mindmap", "timeline", "kanban", "treemap"}
# Types using subgraph/end blocks
SUBGRAPH_TYPES = {"flowchart", "graph", "block-beta"}
# Types using state blocks
STATE_BLOCK_TYPES = {"stateDiagram", "stateDiagram-v2"}
# ──────────────────────────────────────────────────────────────────────
# Data Structures
# ──────────────────────────────────────────────────────────────────────
@dataclass
class DiagramIssue:
file: str
line: int # Line in the original file
diagram_line: int # Line within the diagram block
severity: str # 'error', 'warning'
message: str
diagram_type: str
@dataclass
class DiagramBlock:
file: str
start_line: int # Line number of ```mermaid
end_line: int # Line number of closing ```
content: str # Raw content between fences
diagram_type: str # Detected type
@dataclass
class ValidationReport:
files_scanned: int = 0
diagrams_found: int = 0
issues: List[DiagramIssue] = field(default_factory=list)
@property
def errors(self) -> List[DiagramIssue]:
return [i for i in self.issues if i.severity == "error"]
@property
def warnings(self) -> List[DiagramIssue]:
return [i for i in self.issues if i.severity == "warning"]
@property
def passed(self) -> bool:
return len(self.errors) == 0
# ──────────────────────────────────────────────────────────────────────
# Extraction: Find Mermaid blocks in markdown
# ──────────────────────────────────────────────────────────────────────
def extract_mermaid_blocks(file_path: Path) -> List[DiagramBlock]:
"""Extract all ```mermaid ... ``` blocks from a markdown file."""
content = file_path.read_text(encoding="utf-8")
lines = content.split("\n")
blocks = []
i = 0
while i < len(lines):
line = lines[i].strip()
if line.startswith("```mermaid"):
start = i + 1
# Find closing fence
j = i + 1
while j < len(lines) and not lines[j].strip().startswith("```"):
j += 1
if j < len(lines):
block_content = "\n".join(lines[start:j])
# Detect diagram type from first non-empty line
dtype = _detect_diagram_type(block_content)
blocks.append(DiagramBlock(
file=str(file_path),
start_line=i + 1, # 1-indexed
end_line=j + 1,
content=block_content,
diagram_type=dtype,
))
i = j + 1
continue
i += 1
return blocks
def _detect_diagram_type(content: str) -> str:
"""Detect the diagram type from the first non-empty, non-frontmatter line."""
lines = content.strip().split("\n")
in_frontmatter = False
for line in lines:
stripped = line.strip()
if not stripped:
continue
if stripped == "---":
in_frontmatter = not in_frontmatter
continue
if in_frontmatter:
continue
# First real line is the declaration
first_word = stripped.split()[0] if stripped.split() else ""
# Check for directional suffix: "flowchart TD"
if first_word in DIRECTIONAL_TYPES:
return first_word
# Check exact match
if first_word in VALID_DIAGRAM_TYPES:
return first_word
# Check if it starts with a valid type (e.g., "pie showData")
for dtype in VALID_DIAGRAM_TYPES:
if stripped.startswith(dtype):
return dtype
return first_word # Unknown type — will be flagged
return "unknown"
# ──────────────────────────────────────────────────────────────────────
# Validation Checks
# ──────────────────────────────────────────────────────────────────────
def validate_block(block: DiagramBlock) -> List[DiagramIssue]:
"""Run all structural checks on a single Mermaid diagram block."""
issues = []
# Check 1: Valid diagram type
issues.extend(_check_diagram_type(block))
# Check 2: Direction suffix for flowcharts
issues.extend(_check_direction(block))
# Check 3: Balanced subgraph/end blocks
issues.extend(_check_balanced_blocks(block))
# Check 4: Balanced curly braces (ZenUML, C4)
issues.extend(_check_balanced_braces(block))
# Check 5: Empty diagram
issues.extend(_check_empty_diagram(block))
# Check 6: Node ID conflicts
issues.extend(_check_node_ids(block))
# Check 7: Sequence diagram specific checks
if block.diagram_type == "sequenceDiagram":
issues.extend(_check_sequence_diagram(block))
# Check 8: ER diagram specific checks
if block.diagram_type == "erDiagram":
issues.extend(_check_er_diagram(block))
# Check 9: Sankey CSV format
if block.diagram_type == "sankey-beta":
issues.extend(_check_sankey(block))
# Check 10: Node count warning
issues.extend(_check_node_count(block))
return issues
def _make_issue(block: DiagramBlock, diagram_line: int, severity: str, message: str) -> DiagramIssue:
return DiagramIssue(
file=block.file,
line=block.start_line + diagram_line,
diagram_line=diagram_line,
severity=severity,
message=message,
diagram_type=block.diagram_type,
)
def _check_diagram_type(block: DiagramBlock) -> List[DiagramIssue]:
"""Verify the diagram type is recognized."""
dtype = block.diagram_type
if dtype == "unknown":
return [_make_issue(block, 0, "error", "Could not detect diagram type — first line must be a valid declaration")]
if dtype not in VALID_DIAGRAM_TYPES and dtype not in DIRECTIONAL_TYPES:
# Check for common misspellings
suggestions = {
"sequencediagram": "sequenceDiagram",
"classdiagram": "classDiagram",
"statediagram": "stateDiagram-v2",
"erdiagram": "erDiagram",
"gitgraph": "gitGraph",
"quadrantchart": "quadrantChart",
"requirementdiagram": "requirementDiagram",
"c4context": "C4Context",
"c4container": "C4Container",
}
lower_dtype = dtype.lower()
if lower_dtype in suggestions:
return [_make_issue(block, 0, "error",
f"Invalid diagram type '{dtype}' — did you mean '{suggestions[lower_dtype]}'? (case-sensitive)")]
return [_make_issue(block, 0, "error",
f"Unknown diagram type '{dtype}' — see all 23 valid types in references/diagram-types.md")]
return []
def _check_direction(block: DiagramBlock) -> List[DiagramIssue]:
"""Check flowchart/graph has a valid direction suffix."""
if block.diagram_type not in DIRECTIONAL_TYPES:
return []
first_line = block.content.strip().split("\n")[0].strip()
parts = first_line.split()
if len(parts) < 2:
return [_make_issue(block, 0, "warning",
f"'{block.diagram_type}' without direction — defaults to TD. Consider adding: TD, LR, BT, or RL")]
direction = parts[1]
if direction not in VALID_DIRECTIONS:
return [_make_issue(block, 0, "error",
f"Invalid direction '{direction}' — valid: TD, TB, LR, RL, BT")]
return []
def _check_balanced_blocks(block: DiagramBlock) -> List[DiagramIssue]:
"""Check for matched block-open/close pairs.
- flowchart/graph: subgraph/end
- stateDiagram: state Name { ... } (curly braces)
- block-beta: block:name/end AND subgraph/end
"""
if block.diagram_type not in SUBGRAPH_TYPES | STATE_BLOCK_TYPES:
return []
issues = []
lines = block.content.split("\n")
if block.diagram_type in STATE_BLOCK_TYPES:
# State diagrams use curly braces for nested states
open_count = 0
openers = []
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.endswith("{") and "state" in stripped:
open_count += 1
openers.append(i)
elif stripped == "}":
open_count -= 1
if open_count > 0:
issues.append(_make_issue(block, openers[-1], "error",
f"{open_count} unclosed 'state' block(s) — add '}}' to close"))
elif open_count < 0:
issues.append(_make_issue(block, 0, "error",
f"{abs(open_count)} extra '}}' without matching 'state ... {{'"))
else:
# Flowchart uses subgraph/end, block-beta uses block:/end
open_count = 0
openers = []
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith("subgraph ") or stripped == "subgraph":
open_count += 1
openers.append(i)
elif stripped.startswith("block:") or stripped.startswith("block "):
# block-beta nested blocks: block:groupId:span
if block.diagram_type == "block-beta":
open_count += 1
openers.append(i)
elif stripped == "end" or stripped.startswith("end "):
open_count -= 1
keyword = "subgraph" if block.diagram_type in {"flowchart", "graph"} else "block"
if open_count > 0:
issues.append(_make_issue(block, openers[-1], "error",
f"{open_count} unclosed '{keyword}' block(s) — add 'end' to close"))
elif open_count < 0:
issues.append(_make_issue(block, 0, "error",
f"{abs(open_count)} extra 'end' keyword(s) without matching '{keyword}'"))
return issues
def _check_balanced_braces(block: DiagramBlock) -> List[DiagramIssue]:
"""Check for matched curly braces (ZenUML, C4 System_Boundary).
Skips ER diagrams — they use { } in both attribute blocks AND relationship syntax."""
if block.diagram_type == "erDiagram":
return [] # ER uses { in relationships like ||--o{ — not block delimiters
content = block.content
opens = content.count("{")
closes = content.count("}")
if opens != closes:
return [_make_issue(block, 0, "error",
f"Unbalanced curly braces: {opens} opening vs {closes} closing")]
return []
def _check_empty_diagram(block: DiagramBlock) -> List[DiagramIssue]:
"""Check for diagrams with just a type declaration and nothing else."""
lines = [l.strip() for l in block.content.strip().split("\n") if l.strip()]
# Remove frontmatter
clean_lines = []
in_fm = False
for line in lines:
if line == "---":
in_fm = not in_fm
continue
if not in_fm:
clean_lines.append(line)
if len(clean_lines) <= 1:
return [_make_issue(block, 0, "warning", "Diagram appears empty — only has type declaration")]
return []
def _check_node_ids(block: DiagramBlock) -> List[DiagramIssue]:
"""Check for duplicate node IDs in flowcharts."""
if block.diagram_type not in {"flowchart", "graph"}:
return []
# Extract node definitions: A[text], B(text), C{text}, etc.
node_defs = re.findall(r"^\s*(\w+)\s*[\[\({]", block.content, re.MULTILINE)
# Also from edges: A --> B[text]
edge_node_defs = re.findall(r"--[>|].*?(\w+)\s*[\[\({]", block.content)
all_defs = node_defs + edge_node_defs
# Check for redefinitions with different labels (not just repeated references)
seen = {}
issues = []
for node_id in all_defs:
if node_id in {"subgraph", "end", "style", "class", "click", "linkStyle"}:
continue
if node_id in seen:
seen[node_id] += 1
else:
seen[node_id] = 1
# Multiple definitions of same ID with labels = potential conflict
for node_id, count in seen.items():
if count > 3: # Threshold: re-using an ID many times suggests a problem
issues.append(_make_issue(block, 0, "warning",
f"Node '{node_id}' defined {count} times — if labels differ, only the last one renders"))
return issues
def _check_sequence_diagram(block: DiagramBlock) -> List[DiagramIssue]:
"""Sequence-diagram-specific checks."""
issues = []
lines = block.content.split("\n")
for i, line in enumerate(lines):
stripped = line.strip()
# Check for missing colon in messages
if re.match(r"^\s*\w+\s*->>?\+?\s*\w+\s*$", stripped):
if ":" not in stripped and stripped != "sequenceDiagram":
issues.append(_make_issue(block, i, "warning",
f"Message without label: '{stripped}' — add ': description' after target"))
# Check for unmatched activate/deactivate
# (Simplified: just count them)
activates = sum(1 for l in lines if l.strip().startswith("activate "))
deactivates = sum(1 for l in lines if l.strip().startswith("deactivate "))
if activates != deactivates:
issues.append(_make_issue(block, 0, "warning",
f"{activates} activate(s) vs {deactivates} deactivate(s) — imbalanced lifelines"))
return issues
def _check_er_diagram(block: DiagramBlock) -> List[DiagramIssue]:
"""ER-diagram-specific checks."""
issues = []
lines = block.content.split("\n")
for i, line in enumerate(lines):
stripped = line.strip()
# Check relationship syntax
if re.search(r"[|}o{]-{1,2}[|}o{]", stripped):
# Has a relationship line
if ":" not in stripped:
issues.append(_make_issue(block, i, "warning",
f"ER relationship without label: '{stripped}' — add ': verb' at end"))
return issues
def _check_sankey(block: DiagramBlock) -> List[DiagramIssue]:
"""Sankey-specific checks: CSV format validation."""
issues = []
lines = block.content.split("\n")
for i, line in enumerate(lines):
stripped = line.strip()
if not stripped or stripped == "sankey-beta":
continue
parts = stripped.split(",")
if len(parts) != 3:
issues.append(_make_issue(block, i, "error",
f"Sankey data must be 'Source,Target,Value' — got {len(parts)} fields"))
elif parts[2].strip():
try:
float(parts[2].strip())
except ValueError:
issues.append(_make_issue(block, i, "error",
f"Sankey value must be numeric — got '{parts[2].strip()}'"))
return issues
def _check_node_count(block: DiagramBlock) -> List[DiagramIssue]:
"""Warn about overcrowded diagrams."""
if block.diagram_type not in {"flowchart", "graph"}:
return []
# Count unique node IDs
node_ids = set(re.findall(r"(?:^|\s)(\w+)\s*[\[\({>]", block.content, re.MULTILINE))
node_ids -= {"subgraph", "end", "style", "class", "click", "linkStyle", "flowchart", "graph"}
if len(node_ids) > 20:
return [_make_issue(block, 0, "warning",
f"Diagram has ~{len(node_ids)} nodes — consider splitting into multiple diagrams (max ~15 recommended)")]
return []
# ──────────────────────────────────────────────────────────────────────
# File/Directory Scanning
# ──────────────────────────────────────────────────────────────────────
def scan_file(file_path: Path, report: ValidationReport):
"""Scan a single markdown file for Mermaid blocks and validate them."""
if not file_path.suffix in (".md", ".mdx", ".markdown"):
return
report.files_scanned += 1
blocks = extract_mermaid_blocks(file_path)
report.diagrams_found += len(blocks)
for block in blocks:
issues = validate_block(block)
report.issues.extend(issues)
def scan_directory(dir_path: Path, report: ValidationReport, recursive: bool = True):
"""Scan all markdown files in a directory."""
pattern = "**/*.md" if recursive else "*.md"
for md_file in sorted(dir_path.glob(pattern)):
scan_file(md_file, report)
# Also .mdx
for mdx_file in sorted(dir_path.glob(pattern.replace(".md", ".mdx"))):
scan_file(mdx_file, report)
def scan_skill(skill_path: Path, report: ValidationReport):
"""Scan a skill directory (SKILL.md + references)."""
skill_md = skill_path / "SKILL.md"
if skill_md.exists():
scan_file(skill_md, report)
refs_dir = skill_path / "references"
if refs_dir.exists():
scan_directory(refs_dir, report)
# ──────────────────────────────────────────────────────────────────────
# Output
# ──────────────────────────────────────────────────────────────────────
def print_report(report: ValidationReport, errors_only: bool = False):
"""Print human-readable report."""
if not report.issues:
print(f"\n Scanned {report.files_scanned} files, {report.diagrams_found} diagrams — all valid")
return
current_file = None
for issue in sorted(report.issues, key=lambda i: (i.file, i.line)):
if errors_only and issue.severity != "error":
continue
if issue.file != current_file:
current_file = issue.file
print(f"\n {current_file}")
icon = " " if issue.severity == "error" else " "
print(f" {icon} L{issue.line} [{issue.diagram_type}] {issue.message}")
print(f"\n Summary: {report.files_scanned} files, {report.diagrams_found} diagrams")
print(f" {len(report.errors)} errors, {len(report.warnings)} warnings")
if report.passed:
print(" Mermaid syntax validation passed")
else:
print(" Mermaid syntax validation FAILED")
# ──────────────────────────────────────────────────────────────────────
# CLI
# ──────────────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="Validate Mermaid diagram syntax in markdown files",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("path", nargs="?", help="File or directory to scan")
parser.add_argument("--skill", help="Scan a skill directory (SKILL.md + references)")
parser.add_argument("--all-skills", action="store_true", help="Scan all skills under .claude/skills/")
parser.add_argument("--recursive", action="store_true", default=True, help="Recurse into subdirectories")
parser.add_argument("--errors-only", action="store_true", help="Only show errors, not warnings")
parser.add_argument("--json", action="store_true", help="JSON output")
parser.add_argument("--base", default=".", help="Base project directory")
args = parser.parse_args()
report = ValidationReport()
if args.all_skills:
skills_dir = Path(args.base).resolve() / ".claude" / "skills"
if not skills_dir.exists():
print(f"No skills directory at {skills_dir}")
return 1
for skill_dir in sorted(skills_dir.iterdir()):
if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
scan_skill(skill_dir, report)
elif args.skill:
skill_path = Path(args.skill).resolve()
scan_skill(skill_path, report)
elif args.path:
target = Path(args.path).resolve()
if target.is_file():
scan_file(target, report)
elif target.is_dir():
scan_directory(target, report, recursive=args.recursive)
else:
print(f"Path not found: {target}")
return 1
else:
parser.error("Provide a path, --skill, or --all-skills")
if args.json:
output = {
"files_scanned": report.files_scanned,
"diagrams_found": report.diagrams_found,
"passed": report.passed,
"errors": len(report.errors),
"warnings": len(report.warnings),
"issues": [
{
"file": i.file,
"line": i.line,
"severity": i.severity,
"diagram_type": i.diagram_type,
"message": i.message,
}
for i in report.issues
],
}
print(json.dumps(output, indent=2))
else:
print_report(report, errors_only=args.errors_only)
return 0 if report.passed else 1
if __name__ == "__main__":
sys.exit(main())