Skip to content
Back to skills

Gen Diagram

CSecurity

Generate a d2 architecture diagram (https://d2lang.com/) using icons from icons.terrastruct.com. Renders SVG when the d2 binary is installed.

  • 16 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 25, 2026
developmentrustgojavashellbashsqlnodedockerkubernetesaws

Works with

  • claude code
  • cli
  • api

Security analysis

C71/100
  • criticalPipes output to a shell interpreter
  • mediumUses curl or wget to download content
  • criticalDownloads and executes remote scripts — classic supply chain attack

Pro shows the line behind each finding and how to fix it

Scanned September 25, 2026

npx -y skills add sgaunet/claude-plugins --skill gen-diagram --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Gen Diagram?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Gen Diagram
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/sgaunet-gen-diagram/badge)](https://www.skillsdirectory.com/skills/sgaunet-gen-diagram)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: gen-diagram
description: Generate a d2 architecture diagram (https://d2lang.com/) using icons from icons.terrastruct.com. Renders SVG when the d2 binary is installed.
argument-hint: "[--type=arch|sequence|er|deployment|c4|pipeline] [--no-render] [--no-mise] [--output=<path>] [--dry-run] [<description>]"
user-invocable: true
disable-model-invocation: true
allowed-tools: Read, Write, Edit, Grep, Glob, Bash(d2:*), Bash(mise:*), Bash(git:*), Bash(test:*), Bash(grep:*), Bash(mkdir:*), Bash(command:*), Task, AskUserQuestion
---

# Generate d2 Diagram

Produce a `.d2` source file (and a rendered `.svg` when the `d2` binary is installed) that captures a software or infrastructure architecture. Default engine is **d2** (https://d2lang.com/), default icons come from **https://icons.terrastruct.com/**.

## Why This Skill Exists

**Problem**: Architecture diagrams in design docs and READMEs are usually either missing, drawn in a closed-source tool that no one can edit, or pasted as PNG screenshots that drift from reality. Running through d2 syntax and the icon catalog by hand takes 10-20 minutes per diagram.

**Solution**: One invocation that:
- Synthesizes a `.d2` source file from a free-text description or repo analysis
- Embeds vendor icons from `icons.terrastruct.com` only when the URL is known to resolve
- Renders to SVG automatically if `d2` is installed
- Stores both files in `docs/diagrams/` for diff-able, PR-reviewable diagrams

## Content Generation Rules

**CRITICAL**: When generating any content (.d2 source, file headers, descriptions, or any other output):
- **NEVER** add Claude Code attribution or co-authorship
- **NEVER** include phrases like "Generated by Claude", "Created with Claude Code", or similar
- **NEVER** add "Co-Authored-By: Claude" in commit messages or documentation
- All generated content should appear as if written by a human developer
- Focus on technical accuracy and clear, readable d2 source

## Process (6 Phases)

### Phase 1: Prerequisites Validation

1. **Validate Git repository:**
   ```bash
   git rev-parse --git-dir
   ```
   - If fails: Exit with error "Not a git repository. Run 'git init' first."

2. **Detect d2 and the mise toolchain** (non-blocking, and **no writes in this phase**):
   ```bash
   command -v d2
   command -v mise
   test -f mise.toml && grep -qE '^[[:space:]]*"?d2"?[[:space:]]*=' mise.toml
   ```
   - `d2` on PATH: store `d2Installed = true`. Capture version: `d2 --version`.
   - `d2` absent: store `d2Installed = false` (will warn later, but proceed — the `.d2`
     source is produced either way).
   - `mise` on PATH: store `miseAvailable = true`.
   - `mise.toml` already pins d2: store `d2Pinned = true`.

   When `miseAvailable && !d2Pinned && !--no-mise`, the pin is added in **Phase 5** and
   disclosed in the Phase 4 preview first. `mise use` edits `mise.toml`, which is a project
   file change and must pass through the confirmation gate like any other write.

3. **Verify skill assets:**
   - Use Read to confirm `${CLAUDE_PLUGIN_ROOT}/assets/gen-diagram/d2-cheatsheet.md` exists.
   - If missing: Exit with error "Cheatsheet not found at ${CLAUDE_PLUGIN_ROOT}/assets/gen-diagram/d2-cheatsheet.md. Plugin may be corrupted. Reinstall the software-engineering plugin."

4. **Resolve repository root** for output path defaults:
   ```bash
   git rev-parse --show-toplevel
   ```
   Store as `repoRoot`.

### Phase 2: Argument Parsing

Parse `$ARGUMENTS` (or `$argument`, depending on host syntax). Tokenize on spaces, then partition into flags and a free-text remainder.

**Supported flags:**
- `--type=<arch|sequence|er|deployment|c4|pipeline>` — diagram style. Default: inferred from description, falls back to `arch`.
- `--no-render` — skip the SVG render step even if d2 is installed.
- `--output=<path>` — explicit target path for the `.d2` file. Default: `docs/diagrams/<slug>.d2`.
- `--dry-run` — produce the preview only; do not write any files.
- `--no-mise` — never touch `mise.toml`; rely on whatever `d2` is already on PATH.

**Everything else** is joined back with spaces as `description`.

**Slugification:**
- If `description` is non-empty: lowercase, replace non-alphanumerics with `-`, collapse repeats, trim leading/trailing `-`, cap at 60 chars.
- If `description` is empty: slug = `architecture`.

**Type inference** (when `--type` is omitted):
- Description matches `/sequence|flow|step.*step|request.*response/i` → `sequence`
- Matches `/er|entity|schema|table/i` → `er`
- Matches `/deploy|infra|vpc|cluster|kubernetes|aws|gcp|azure/i` → `deployment`
- Matches `/c4|context|container|component/i` → `c4`
- Matches `/pipeline|ci\/cd|workflow|jobs/i` → `pipeline`
- Otherwise → `arch`

**Default output path:**
- If `--output` provided: use it verbatim (resolve relative to cwd).
- Else: `<repoRoot>/docs/diagrams/<slug>.d2`.

The accompanying SVG path is the same path with `.d2` replaced by `.svg`.

### Phase 3: Context Gathering

1. **Read the cheatsheet** to ground the LLM in real d2 syntax and verified icon URLs:
   ```
   Read: ${CLAUDE_PLUGIN_ROOT}/assets/gen-diagram/d2-cheatsheet.md
   ```

2. **If `description` is empty**, launch one Explore subagent to summarize the repo's architecture:
   ```
   Task(
     subagent_type: "Explore",
     description: "Repo architecture summary",
     prompt: "
       Summarize this repository's architecture for a system diagram. Return:
       {
         entryPoints: [list of binaries / servers / endpoints discovered],
         services: [list of internal services or modules],
         datastores: [databases, caches, queues with vendor names],
         externalDeps: [third-party APIs, SaaS],
         deploymentTargets: [k8s, AWS service, etc., if obvious from IaC],
         language: dominant language,
         shape: 'monolith' | 'modular' | 'microservices' | 'library' | 'cli'
       }

       Detection strategy:
       - Languages: glob for *.go, *.py, *.js, *.ts, *.rs, *.java
       - Entry points: cmd/*/main.go, main.py, src/index.{ts,js}, bin/*
       - Datastores: search go.mod / package.json / requirements.txt for postgres, mysql, redis, kafka, mongo, sqlite, elasticsearch
       - Deployment: presence of Dockerfile, k8s/, helm/, *.tf, .github/workflows/

       Return concise JSON, no prose.
     "
   )
   ```
   Use the returned summary as the modeling input. Otherwise, skip this step.

3. **If `description` is non-empty**, treat it as the modeling input directly. Use the cheatsheet patterns to expand abbreviations (e.g. "auth flow with JWT and Redis" → sequence diagram with user, SPA, API, Redis cache).

### Phase 4: Confirmation Preview

Build the preview message:

```
📐 d2 Diagram Preview

Type:        <type>
Description: <description or "(auto-detected from repo)">
Output:      <relative path>.d2
Render:      <"yes (d2 → SVG)" if d2Installed && !--no-render else "no (source only)">
d2 tooling:  <"mise.toml → pin d2 0.7.1 (new)" | "mise.toml already pins d2" | "PATH d2 <version>" | "none — source only">

Planned content:
  • Layout: <direction> / <layout-engine>
  • Nodes (~N): <comma-separated short list>
  • Icons: <count of icons.terrastruct.com URLs that will be embedded>

<CONDITIONAL WARNINGS>
```

**Conditional warnings:**

If `d2Installed = false`:
```
ℹ️  d2 binary not found
    .d2 source will be created; rendering depends on provisioning d2.
    Preferred — pins the version for this repo, local dev and CI alike:
      • mise use d2@0.7.1     # adds d2 to mise.toml [tools] and installs it
    Without mise:
      • macOS:  brew install d2
      • Linux:  curl -fsSL https://d2lang.com/install.sh | sh -s --
      • Verify: d2 --version
```

If `miseAvailable && !d2Pinned && !--no-mise`:
```
ℹ️  mise.toml will gain a d2 pin
    Adding:  d2 = "0.7.1"   (under [tools])
    <"mise.toml will be created" | "existing pins are preserved">
    This keeps local renders and CI on the same d2 version.
    Skip with --no-mise.
```

If target `.d2` file already exists:
```
⚠️  WARNING: <path>.d2 already exists
    This will OVERWRITE the existing diagram source.
    Consider committing or backing up the current version first.
```

**Confirmation logic:**

If `--dry-run`:
```
[DRY RUN MODE - No files will be written]
```
Proceed straight to Phase 6 to print the preview and the would-be `.d2` content. Do **not** write or render.

If target `.d2` exists and not `--dry-run`:
```
Use AskUserQuestion:
  Question: "Overwrite existing diagram at <path>.d2?"
  Options:
    - "Yes, overwrite" (proceed to Phase 5)
    - "No, cancel" (exit with "Operation cancelled by user")
```

If target does not exist and not `--dry-run`:
```
Use AskUserQuestion:
  Question: "Create diagram at <path>.d2?"
  Options:
    - "Yes, create" (proceed to Phase 5)
    - "No, cancel" (exit with "Operation cancelled by user")
```

### Phase 5: Generation

1. **Synthesize the `.d2` source** following the cheatsheet:
   - Begin with a 1-3 line comment header: `# <Title>` and a sentence on scope/audience.
   - For `sequence`: start the body with `shape: sequence_diagram`.
   - For `er`: each table is a `shape: sql_table` block.
   - For `deployment`: nest containers by trust boundary (region → VPC → subnet).
   - For `c4`: use `shape: c4-person` for actors and `shape: package` for containers.
   - For `arch` / `pipeline`: pick `direction: right` and label every edge.
   - **Only include `icon:` URLs that appear in the cheatsheet's verified list.** If the design needs an icon not in the list, omit it (the user can add it manually) rather than guessing.
   - Apply layout engine `dagre` by default. Switch to `elk` if node count > 10.
   - Keep node count ≤ 12 per top-level view.

2. **Ensure parent directory exists:**
   ```bash
   mkdir -p "$(dirname <output>)"
   ```

3. **Write the file** with Write to the resolved output path.

4. **Pin d2 in `mise.toml`** when `miseAvailable && !d2Pinned && !--no-mise`:
   ```bash
   mise use d2@0.7.1
   ```
   Run it from `repoRoot`. `mise use` is idempotent: it records `d2 = "0.7.1"` under
   `[tools]`, creates `mise.toml` when absent, leaves every other pin untouched, and
   installs the binary — so the Phase 6 render can proceed even if d2 was missing.
   On success set `d2Installed = true` and `misePinWritten = true`.
   - On failure (no network, unknown version, untrusted config) report the stderr as a
     **soft failure**, leave `d2Installed` unchanged, and continue — the `.d2` source is
     written regardless. A first run may need `mise trust` before `mise use` succeeds.

### Phase 6: Render & Success Report

1. **Render to SVG** when `d2Installed && !--no-render && !--dry-run`:
   ```bash
   d2 <output>.d2 <output>.svg
   ```
   If d2 was just provisioned by the Phase 5 `mise use` it may not be on PATH in this
   shell yet — route through mise instead:
   ```bash
   mise exec -- d2 <output>.d2 <output>.svg
   ```
   Capture exit code. If non-zero, report the d2 error to the user but treat as a soft failure (the `.d2` source was still produced).

2. **Verify written files:**
   ```bash
   test -f <output>.d2 && wc -l <output>.d2
   test -f <output>.svg  # only if render attempted
   ```

3. **Success report (normal execution):**
   ```
   ✅ Diagram created

   Files:
     ✓ <output>.d2 (<N> lines)
     ✓ <output>.svg              # only if render succeeded
     ✓ mise.toml (d2 = "0.7.1")  # only if misePinWritten

   Type:        <type>
   Layout:      <direction> / <layout-engine>
   Icons used:  <count>

   Next steps:

     1. Preview the SVG:
        open <output>.svg                 # macOS
        xdg-open <output>.svg             # Linux

     2. Edit the source:
        ${EDITOR:-vi} <output>.d2

     3. Re-render after edits:
        d2 <output>.d2 <output>.svg

     4. Try a different layout:
        d2 --layout=elk <output>.d2 <output>.svg

     5. Embed in docs:
        ![Architecture](<relative-path>.svg)

     6. Browse the icon catalog:
        https://icons.terrastruct.com/

     7. d2 reference:
        https://d2lang.com/tour/intro
   ```

4. **Success report (dry-run):**
   ```
   ✅ [DRY RUN] Preview only — no files written

   Would create:
     • <output>.d2 (~<N> lines)
     • <output>.svg               # only if d2Installed && !--no-render

   --- Preview of <output>.d2 ---
   <full d2 source>
   --- end preview ---

   Run again without --dry-run to write.
   ```

5. **If `d2Installed = false`**, append:
   ```

   ⚠️  Install d2 to render automatically next time:
        mise use d2@0.7.1      # preferred: pins it in mise.toml for dev + CI
        # or: brew install d2
        # or: curl -fsSL https://d2lang.com/install.sh | sh -s --
   ```

6. **If `d2` render failed**, append:
   ```

   ⚠️  d2 render failed (.d2 source was written successfully):
        <stderr from d2>
        Try manually: d2 <output>.d2 <output>.svg
   ```

## Tool Provisioning (mise)

d2 is pinned through [mise](https://mise.jdx.dev) rather than installed ad hoc, so the
version that renders a diagram locally is the version CI renders it with.

**Pinned version: `d2@0.7.1`.** Bump it in one place — this section — and in the Phase 4/5/6
strings above.

```bash
mise use d2@0.7.1
```

What that does:
- Records `d2 = "0.7.1"` under `[tools]` in the repo-root `mise.toml`
- Creates `mise.toml` if the repo has none; preserves every existing pin if it does
- Installs the binary, so a render can follow immediately in the same run
- Is idempotent — re-running it is a no-op once the pin matches

Rules this skill follows:
- **Detect in Phase 1, write in Phase 5.** `mise use` mutates a tracked project file, so it
  sits behind the same confirmation gate as the `.d2` output and never runs under `--dry-run`.
- **Never rewrite an existing pin.** If `mise.toml` already lists d2 at any version, leave it
  alone and use it — the project's choice wins over this skill's default.
- **Opt out with `--no-mise`** for repos that manage tooling another way (Nix, Homebrew,
  devcontainer, distro packages).
- **Soft-fail only.** A failed `mise use` never blocks the `.d2` source from being written.

Once pinned, collaborators and CI get d2 from:
```bash
mise install          # provision every pinned tool
mise exec -- d2 --version
```

CI needs no extra step when the pipeline already uses `jdx/mise-action` (GitHub/Forgejo) or
the `jdx/mise` image (GitLab) — the same pattern the go-specialist CI generators emit.

## Integration with Other Skills

- **Before `/gen-diagram`**: none required.
- **After `/gen-diagram`**:
  - `/gen-claude` will pick up the new `docs/diagrams/` directory in subsequent runs.
  - Reference the SVG from generated docs or PR descriptions.
- **Companion agent**: invoking `/gen-diagram` proactively pulls in the `diagram-architect` agent for ongoing edits to `.d2` files in the repo.

## Examples

```bash
# Architecture from a description, render SVG if d2 is installed
/gen-diagram simple web app with frontend, API, postgres, redis

# Sequence diagram for an auth flow, custom output path
/gen-diagram --type=sequence --output=docs/diagrams/login.d2 login flow with JWT

# ER diagram from existing schema description
/gen-diagram --type=er users have many orders, orders have many line items

# Auto-discover from the repo (no description)
/gen-diagram

# Preview only, don't write
/gen-diagram --dry-run kafka-fronted ingestion service

# Skip rendering even when d2 is installed
/gen-diagram --no-render deployment topology on AWS
```

## Limitations & Caveats

- **d2 binary required for SVG**: only the source file is produced when `d2` is missing and cannot be provisioned. `mise use d2@0.7.1` fixes that permanently for the repo; `--no-mise` opts out.
- **Icon catalog drift**: only icons listed in the cheatsheet are guaranteed to resolve. Add more by browsing https://icons.terrastruct.com/ and updating the cheatsheet.
- **Repo analysis is best-effort**: when `description` is empty, the Explore subagent summarizes what it can find — clean up the resulting diagram by hand for unusual layouts.
- **No PNG/PDF**: only SVG is rendered by default. Use `d2 --target=png` manually for raster output.
- **Node count cap**: diagrams with more than ~12 top-level nodes get crowded. Split into multiple files (`-context.d2`, `-containers.d2`) rather than fighting the layout.

## Error Recovery

**If `mise use d2@0.7.1` fails:**
1. Untrusted config is the usual cause: `mise trust` in the repo root, then retry.
2. Unknown version — check what the registry offers: `mise ls-remote d2`.
3. No network or a locked-down environment: fall back to `brew install d2`, or re-run with
   `--no-mise` to skip the pin entirely. The `.d2` source is written either way.

**If `d2 --version` errors or `d2` rendering fails:**
1. Check version: `d2 --version` (require ≥ 0.6), or `mise exec -- d2 --version` when pinned.
2. Try a different layout: `d2 --layout=elk <file>.d2`.
3. Validate syntax by reading the d2 stderr — it usually points at the offending line.

**If an icon URL 404s:**
1. Open https://icons.terrastruct.com/ and confirm the exact path (spaces become `%20`).
2. Either fix the URL in the `.d2` file or remove the `icon:` attribute and the surrounding `shape: image`.

**If the diagram is too dense:**
1. Re-run with `--type=c4` to layer it.
2. Split into separate files per concern (auth, billing, ingestion, …).
3. Increase grouping depth so siblings share a parent container.

## Additional Resources

- d2 documentation: https://d2lang.com/
- mise (tool version manager): https://mise.jdx.dev/
- mise registry (d2 backend): https://mise.jdx.dev/registry.html
- d2 tour: https://d2lang.com/tour/intro
- d2 themes: https://d2lang.com/tour/themes
- Icon catalog: https://icons.terrastruct.com/
- d2 playground: https://play.d2lang.com/
- Companion agent: `diagram-architect` (auto-activates on `.d2` files)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.