From d2
Converts Mermaid flowcharts, sequence, ER, and class diagrams to D2 format. Auto-detects type from code or .md/.mmd files and applies specialist syntax rules.
npx claudepluginhub diegomarino/claude-toolshed --plugin d2This skill is limited to using the following tools:
Input: "$ARGUMENTS"
Generate architecture diagrams, flowcharts, decision trees, workflows, sequence flows, ERDs from declarative D2 text with automatic layouts, themes, and styling.
Generates D2 diagrams from text descriptions by classifying type (sequence, architecture, ER, class) and planning structure with nodes, groups, edges.
Generates D2 diagrams from textual descriptions for system architectures, flowcharts, network topologies, data flows, and component relationships.
Share bugs, ideas, or general feedback.
Input: "$ARGUMENTS"
Detect the Mermaid diagram type from the input, apply the appropriate translation rules, and produce valid D2 output using the matching specialist's syntax patterns.
PLUGIN_DIR=$(find "$HOME/.claude/plugins/cache" -type d -name "d2" -path "*/skills/d2" 2>/dev/null | head -1)
Fallback (dev/repo):
PLUGIN_DIR=$(find "$HOME" -maxdepth 8 -type d -name "d2" -path "*/skills/d2" 2>/dev/null | head -1)
If still empty: stop — "Plugin not found. Is it installed?"
bash "$PLUGIN_DIR/scripts/ensure-deps.sh"
Read .claude/d2.json if present. Apply:
theme_id (default: 0)layout (default: "dagre")sketch (default: false)output_directory (default: "./diagrams")auto_validate (default: true)auto_render (default: false)If $ARGUMENTS is a file path ending in .md or .mmd: read that file and extract the first fenced code block (```mermaid).
If $ARGUMENTS is a raw code block or inline text: use as-is.
If no input: ask the user to paste the Mermaid code.
Read the first non-blank, non-comment line of the Mermaid code:
| First line starts with | Mermaid type | D2 specialist |
|---|---|---|
flowchart, graph | Flowchart | d2-architecture |
sequenceDiagram | Sequence | d2-sequence |
erDiagram | Entity-Relationship | d2-er |
classDiagram | Class | d2-class |
stateDiagram, stateDiagram-v2 | State | Unsupported |
gantt | Gantt | Unsupported |
pie | Pie | Unsupported |
journey | User Journey | Unsupported |
gitgraph, gitGraph | Git Graph | Unsupported |
mindmap | Mind Map | Unsupported |
timeline | Timeline | Unsupported |
Unsupported: Respond with:
"D2 has no native equivalent for
{type}diagrams. Supported conversions: flowchart, sequenceDiagram, erDiagram, classDiagram."
Apply the per-type rules below. Read the matching specialist file AFTER translating — it provides the canonical D2 syntax to validate your output against.
Specialist paths:
$PLUGIN_DIR/specialists/d2-architecture.md$PLUGIN_DIR/specialists/d2-sequence.md$PLUGIN_DIR/specialists/d2-er.md$PLUGIN_DIR/specialists/d2-class.mdNode shapes:
| Mermaid | D2 |
|---|---|
A[Text] | A: Text |
A(Rounded) | A: Rounded { shape: oval } |
A{Decision} | A: Decision { shape: diamond } |
A((Circle)) | A: Circle { shape: circle } |
A[(Database)] | A: Database { shape: cylinder } |
A([Stadium]) | A: Stadium { shape: oval } |
A[/Parallelogram/] | A: Parallelogram (no direct equivalent, use default) |
Connections:
| Mermaid | D2 |
|---|---|
A --> B | A -> B |
A --> B: label | A -> B: label |
A --- B | A -- B |
A -.-> B | A -> B { style.stroke-dash: 4 } |
A -.-> B: label | A -> B: label { style.stroke-dash: 4 } |
A ==> B | A -> B { style.stroke-width: 4 } |
A --o B | A -> B: o (label the relationship type) |
A --x B | A -> B: x |
A <--> B | A <-> B |
Subgraphs:
# Mermaid
subgraph MyGroup
A --> B
end
# D2
MyGroup: {
A -> B
}
If a subgraph has a title different from its ID:
# Mermaid
subgraph sg1["My Title"]
# D2
sg1: My Title {
Label quoting — REQUIRED:
D2 interprets { } in edge labels as edge map syntax. Any label containing {, }, (, ), :, ", or ; MUST be wrapped in double quotes:
# WRONG — D2 parses {id, name} as an edge map key
A -> B: POST /users {id, name}
# CORRECT
A -> B: "POST /users {id, name}"
Apply this to every label during conversion. When in doubt, quote it.
Styles: Drop style, classDef, and class directives — translate visual intent using D2 shape or style properties only where semantically meaningful.
Direction:
graph TD / flowchart TD → layout-engine: dagre (default, top-down)graph LR / flowchart LR → add direction: right inside each container, or use layout-engine: elkAll content wraps in a top-level shape: sequence_diagram container:
# Mermaid
sequenceDiagram
participant A
participant B
A->>B: Hello
B-->>A: Hi
# D2
shape: sequence_diagram
A -> B: Hello
B -> A: Hi { style.stroke-dash: 4 }
Message translations:
| Mermaid arrow | D2 |
|---|---|
A->>B: msg | A -> B: msg |
A->B: msg | A -> B: msg |
A-->>B: msg | A -> B: msg { style.stroke-dash: 4 } |
A-xB: msg | A -> B: msg (note: Mermaid "destroyed" semantic is lost) |
A-)B: msg | A -> B: msg (async arrow, use plain) |
Label quoting in sequences:
Message labels containing {, }, (, ), :, or ; MUST be double-quoted:
# WRONG
Client -> Server: POST /login {email, pass}
# CORRECT
Client -> Server: "POST /login {email, pass}"
Participants:
participant X as Label → use Label as the node identifier in D2 (or alias map manually)actor X → same as participant in D2Activation bars: No D2 equivalent. Drop activate/deactivate lines.
Notes:
# Mermaid
Note over A: Some note
Note over A,B: Shared note
# D2 — use dot-notation on the actor, NO arrow needed
A."Some note"
Notes spanning two actors can only be attached to one in D2 — attach to the more relevant actor.
Alt / opt / loop blocks → D2 Groups:
Mermaid's alt/else/opt/loop blocks map to D2 named groups. Actors used inside groups must be pre-declared at the top level:
# Mermaid
sequenceDiagram
participant A
participant B
alt valid credentials
A->>B: success
else invalid
A->>B: failure
end
# D2
shape: sequence_diagram
# Pre-declare actors used in groups
A
B
valid credentials: {
A -> B: success
}
invalid: {
A -> B: failure
}
Each entity becomes a sql_table shape. Fields map from Mermaid type name to D2 name: type.
# Mermaid
erDiagram
USER {
int id PK
string email UK
string name
}
ORDER {
int id PK
int user_id FK
decimal total
}
USER ||--o{ ORDER : "places"
# D2
USER: {
shape: sql_table
id: int { constraint: primary_key }
email: string { constraint: unique }
name: string
}
ORDER: {
shape: sql_table
id: int { constraint: primary_key }
user_id: int { constraint: foreign_key }
total: decimal
}
USER.id -> ORDER.user_id: places
Constraint mapping:
| Mermaid | D2 |
|---|---|
PK | { constraint: primary_key } |
FK | { constraint: foreign_key } |
UK | { constraint: unique } |
PK, FK | { constraint: [primary_key; foreign_key] } |
| (none) | (no constraint clause) |
Relationship lines: Map to -> between the PK field of the parent and FK field of the child when identifiable. If fields are unclear, connect entity-to-entity with the relationship label:
USER -> ORDER: places
Cardinality annotations — D2 supports crow's foot notation natively via source-arrowhead and target-arrowhead. Map Mermaid cardinality to D2 arrowhead shapes:
| Mermaid | Meaning | D2 arrowhead |
|---|---|---|
|o | Zero or one | cf-one |
|| | Exactly one | cf-one-required |
o{ | Zero or more | cf-many |
|{ | One or more | cf-many-required |
# Mermaid: USER ||--o{ ORDER : "places"
# D2:
USER.id -> ORDER.user_id: places {
source-arrowhead.shape: cf-one-required
target-arrowhead.shape: cf-many
}
Each class becomes a labeled container. Methods and fields become labeled entries.
# Mermaid
classDiagram
class Animal {
+String name
+speak() void
}
class Dog {
+String breed
+fetch() void
}
Animal <|-- Dog
# D2
Animal: {
name: String
speak(): void
}
Dog: {
breed: String
fetch(): void
}
Animal -> Dog: extends
Relationship arrows:
| Mermaid | D2 label |
|---|---|
| `A < | -- B` (inheritance) |
A *-- B (composition) | A -> B: composition |
A o-- B (aggregation) | A -> B: aggregation |
A --> B (association) | A -> B |
A ..> B (dependency) | A -> B: uses { style.stroke-dash: 4 } |
| `A .. | > B` (realization) |
A -- B (link) | A -- B |
Visibility modifiers: Drop +, -, #, ~ prefixes — D2 has no visibility concept. Keep the name and type only.
Stereotypes: <<interface>>, <<abstract>>, <<service>> — append to the container label:
# Mermaid: class Flyable <<interface>>
# D2:
Flyable (interface): {
Namespaces: namespace MyNS { class A } → MyNS: { A: { ... } }
vars: {
d2-config: {
theme-id: {theme_id}
layout-engine: {layout}
sketch: {sketch}
}
}
Place this at the TOP of the output file (before all diagram content).
Exception: ER diagrams — force sketch: false regardless of config (sketch mode breaks sql_table shapes).
d2 validate {output_file}
If validation fails, inspect the error and apply the fix from $PLUGIN_DIR/references/guides/troubleshooting.md. Fix silently and re-validate before reporting to the user.
Output filename: {original-name}-d2-{YYYYMMDD}.d2
If the input was inline text (no filename): converted-{mermaid-type}-{YYYYMMDD}.d2
Save to OUTPUT_DIR.
If auto_render == true:
d2 {output_file} {output_file_without_ext}.svg
Converted {mermaid-type} diagram to D2.
Input: {source}
Output: {output_file}
Render: {svg_path or "not rendered (auto_render=false)"}
Translation notes:
- {any semantic losses or approximations made}
Always list translation notes — things that could not be converted exactly (activation bars, cardinality notation, style directives, unsupported constructs).
Input:
sequenceDiagram
participant C as Client
participant S as Server
C->>S: POST /login
S-->>C: 200 JWT
Output:
vars: {
d2-config: {
theme-id: 0
layout-engine: dagre
sketch: false
}
}
shape: sequence_diagram
Client -> Server: POST /login
Server -> Client: 200 JWT { style.stroke-dash: 4 }
Translation notes:
participant X as Label aliases resolved: C → Client, S → Server-->> (dashed reply arrow) mapped to { style.stroke-dash: 4 }