From 1d4b9cf29953092f3cd49180acec55dc9a9dd1b0 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 23 Feb 2026 11:56:53 +0100 Subject: [PATCH] chore(skills): add d2-diagram skill for text-to-diagram generation Wraps d2 CLI (v0.7.1) with project defaults: theme 200 (Dark Mauve), dagre layout, PNG output. Includes render/validate/batch scripts, syntax guide, and five category templates (architecture, entity, data-flow, state, UI flow). Co-Authored-By: Claude Opus 4.6 --- .claude/skills/d2-diagram/SKILL.md | 151 +++++++++++ .../d2-diagram/references/d2-syntax-guide.md | 212 +++++++++++++++ .../references/diagram-templates.md | 251 ++++++++++++++++++ .claude/skills/d2-diagram/scripts/d2-batch.sh | 84 ++++++ .../skills/d2-diagram/scripts/d2-render.sh | 92 +++++++ 5 files changed, 790 insertions(+) create mode 100644 .claude/skills/d2-diagram/SKILL.md create mode 100644 .claude/skills/d2-diagram/references/d2-syntax-guide.md create mode 100644 .claude/skills/d2-diagram/references/diagram-templates.md create mode 100755 .claude/skills/d2-diagram/scripts/d2-batch.sh create mode 100755 .claude/skills/d2-diagram/scripts/d2-render.sh diff --git a/.claude/skills/d2-diagram/SKILL.md b/.claude/skills/d2-diagram/SKILL.md new file mode 100644 index 000000000..c39ef4c98 --- /dev/null +++ b/.claude/skills/d2-diagram/SKILL.md @@ -0,0 +1,151 @@ +--- +name: d2-diagram +description: > + Generate technical diagrams using d2 (text-to-diagram CLI). Use when the + user says "create a diagram", "draw architecture", "make a flowchart", + "diagram this", "render d2", "d2", "data flow diagram", "entity relationship", + "state machine", "sequence diagram", "UI flow", or invokes /d2-diagram. + Produces .d2 source files and renders them to PNG. Also use when asked + to update, re-render, or batch render existing diagrams. +--- + +# d2 Diagram Generation + +Generate technical diagrams from text using d2 (v0.7.1). Pure CLI, no +external dependencies beyond the d2 binary. + +**Binary:** `/home/linuxbrew/.linuxbrew/bin/d2` + +## Project Defaults + +| Setting | Value | Override | +|---------|-------|----------| +| Theme | 200 (Dark Mauve) | `--theme N` | +| Layout | dagre | `--layout elk` | +| Padding | 100px | — | +| Format | PNG | `--svg` | + +## Output Convention + +``` +docs/diagrams/ + architecture/ # System architecture, IPC, component layout + data-flow/ # Sequence diagrams, data pipelines + entity/ # ER diagrams, ECS component schemas + state/ # State machines, behavior trees + ui/ # UI navigation flow, screen transitions +``` + +Both `.d2` source and `.png` output are tracked in git. + +## Single Diagram Workflow + +1. **Determine category** — architecture, data-flow, entity, state, or ui +2. **Read template** — `references/diagram-templates.md` for the matching category +3. **Read syntax** — `references/d2-syntax-guide.md` if unfamiliar with d2 syntax +4. **Write .d2 source** — to `docs/diagrams/{category}/{name}.d2` +5. **Validate** — `.claude/skills/d2-diagram/scripts/d2-render.sh validate {file}` +6. **Render** — `.claude/skills/d2-diagram/scripts/d2-render.sh {file}` +7. **Read SVG** — verify the output, present to user + +### Script Usage + +```bash +# Render with project defaults +.claude/skills/d2-diagram/scripts/d2-render.sh docs/diagrams/architecture/ipc-bridge.d2 + +# Validate syntax only +.claude/skills/d2-diagram/scripts/d2-render.sh validate docs/diagrams/architecture/ipc-bridge.d2 + +# Auto-format source +.claude/skills/d2-diagram/scripts/d2-render.sh fmt docs/diagrams/architecture/ipc-bridge.d2 + +# Sketch mode (hand-drawn look for drafts) +.claude/skills/d2-diagram/scripts/d2-render.sh docs/diagrams/ui/flow.d2 --sketch + +# Light theme (for printable docs) +.claude/skills/d2-diagram/scripts/d2-render.sh docs/diagrams/entity/schema.d2 --theme 0 + +# SVG output (if specifically needed) +.claude/skills/d2-diagram/scripts/d2-render.sh docs/diagrams/architecture/overview.d2 --svg +``` + +## Batch Render + +Re-render all diagrams after theme or style changes: + +```bash +# All diagrams +.claude/skills/d2-diagram/scripts/d2-batch.sh + +# One category +.claude/skills/d2-diagram/scripts/d2-batch.sh docs/diagrams/architecture/ + +# Preview what would render +.claude/skills/d2-diagram/scripts/d2-batch.sh --dry-run + +# Force re-render everything +.claude/skills/d2-diagram/scripts/d2-batch.sh --force +``` + +Batch skips files whose PNG is newer than the `.d2` source unless `--force`. + +## Advanced Patterns + +### Variables for consistent styling + +```d2 +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" +} +``` + +### Multi-board (layers) + +```d2 +# Base diagram here + +layers: { + detailed: { + # More detailed view + } +} +``` + +### Sequence diagrams + +```d2 +shape: sequence_diagram +client: Godot Client +server: Rust Server + +client -> server: TickRequest(delta) +server -> client: WorldState(entities) +``` + +### Imports + +Split shared definitions into a separate file and import: + +```d2 +...@shared-defs.d2 +``` + +## Agent Guidance + +- **Qatux** — Architecture decision records, system overview diagrams, data + schemas. Prefer architecture and entity templates. +- **Tyre** — IPC bridge, ECS system flow, chunk loading pipeline, perception + system data flow. Prefer architecture and data-flow templates. +- **Araminta** — UI navigation flow, screen transitions, component hierarchy. + Prefer UI flow template. + +## References + +- `references/d2-syntax-guide.md` — Language quick reference (shapes, edges, + containers, styling, variables). Read when unfamiliar with d2 syntax. +- `references/diagram-templates.md` — Five category templates with complete + d2 source examples. Read when starting a new diagram. diff --git a/.claude/skills/d2-diagram/references/d2-syntax-guide.md b/.claude/skills/d2-diagram/references/d2-syntax-guide.md new file mode 100644 index 000000000..e1c05de50 --- /dev/null +++ b/.claude/skills/d2-diagram/references/d2-syntax-guide.md @@ -0,0 +1,212 @@ +# D2 Language Quick Reference + +## Nodes + +```d2 +server # Implicit label from key +server: Simulation Server # Explicit label +server: Simulation Server { # With properties + shape: hexagon + style.fill: "#2d3436" +} +``` + +## Edges + +```d2 +a -> b # Directed +a <- b # Reverse directed +a <-> b # Bidirectional +a -- b # Undirected +a -> b: "label" # Labeled edge +a -> b -> c # Chained +``` + +## Containers (nesting) + +```d2 +infrastructure: { + server: Simulation Server + database: State Store { + shape: cylinder + } +} +``` + +## Shapes + +| Shape | Use for | +|-------|---------| +| `rectangle` | Default. Components, modules, generic. | +| `hexagon` | Systems, services, major components. | +| `cylinder` | Databases, storage, persistent state. | +| `diamond` | Decisions, conditions, branch points. | +| `oval` / `circle` | Start/end states, events. | +| `cloud` | External systems, networks. | +| `person` | Actors, users, NPCs. | +| `queue` | Message queues, buffers. | +| `page` | Documents, files. | +| `package` | Packages, modules, crates. | +| `sql_table` | Database tables, ECS component schemas. | +| `class` | Class diagrams, ECS system definitions. | +| `code` | Code blocks (set `language` property). | +| `markdown` | Rich text blocks. | + +## SQL Tables + +```d2 +entity: { + shape: sql_table + id: u64 {constraint: primary_key} + name: String + position: Vec2 + faction_id: u64 {constraint: foreign_key} +} +``` + +## Class Diagrams + +```d2 +perception_system: { + shape: class + +run(world: &mut World) + -calculate_los(entity: Entity): HashSet + #update_knowledge(entity: Entity, seen: HashSet) +} +``` + +## Sequence Diagrams + +```d2 +shape: sequence_diagram +client: Godot Client +server: Rust Server + +client -> server: TickRequest(delta) +server -> server: run ECS systems +server -> client: WorldState(entities) +``` + +## Styling + +```d2 +node: Label { + style: { + fill: "#2d3436" + stroke: "#333340" + stroke-width: 2 + stroke-dash: 5 # Dashed line + opacity: 0.8 + font-size: 14 + font-color: "#c8d0e0" + bold: true + italic: false + border-radius: 4 + shadow: true + 3d: true # Rectangles only + multiple: true # Stacked appearance + double-border: true # Rectangles/ovals only + } +} +``` + +### Edge styling + +```d2 +a -> b: { + style: { + stroke: "#c8d8f0" + stroke-width: 2 + stroke-dash: 5 + opacity: 0.8 + animated: true # Animated flow + } +} +``` + +## Variables + +```d2 +vars: { + color-bg: "#1a1e24" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" +} + +node: { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} +``` + +## Direction + +```d2 +direction: right # left-to-right (default for dagre) +direction: down # top-to-bottom +direction: up +direction: left +``` + +## Imports + +```d2 +...@shared-defs.d2 # Spread import (inline all definitions) +``` + +## Icons + +```d2 +node: Label { + icon: https://icons.terrastruct.com/essentials/time.svg +} +``` + +## Layers (multi-board) + +```d2 +# Base diagram content here + +layers: { + detailed: { + # More detailed view + } + simplified: { + # Simplified overview + } +} +``` + +## Scenarios (animated transitions) + +```d2 +# Base state + +scenarios: { + alert: { + # Changes from base for alert state + } + combat: { + # Changes from base for combat state + } +} +``` + +## Comments + +```d2 +# This is a comment +node: Label # Inline comment +``` + +## Project Colors (from visual-grammar-v01.md) + +| Constant | Hex | Usage | +|----------|-----|-------| +| Zone 1 floor | `#1a1e24` | Dark backgrounds | +| Zone 1 wall | `#2a3040` | Component fill | +| Outline standard | `#333340` | Borders, strokes | +| Insert chrome | `#c8d0e0` | Text, labels | +| Zone 1 fixture | `#c8d8f0` | Accents, highlights | diff --git a/.claude/skills/d2-diagram/references/diagram-templates.md b/.claude/skills/d2-diagram/references/diagram-templates.md new file mode 100644 index 000000000..d134c9733 --- /dev/null +++ b/.claude/skills/d2-diagram/references/diagram-templates.md @@ -0,0 +1,251 @@ +# Diagram Templates + +Copy, adapt, and render. Each template uses project colors from visual-grammar-v01.md. + +--- + +## 1. Architecture Diagram + +System components, relationships, communication channels. + +**When to use:** IPC bridge, perception pipeline, chunk loading, ECS system layout, client-server architecture. + +**Agents:** Tyre (system architecture), Qatux (architecture decision records). + +```d2 +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" +} + +direction: right + +client: Godot Client { + shape: hexagon + style.fill: ${color-bg} + style.font-color: ${color-text} + + rendering: Rendering { + style.fill: ${color-bg} + style.font-color: ${color-text} + } + ui: UI Layer { + style.fill: ${color-bg} + style.font-color: ${color-text} + } + bridge: IPC Bridge { + style.fill: ${color-bg} + style.font-color: ${color-text} + style.stroke: ${color-accent} + } +} + +server: Rust Server { + shape: hexagon + style.fill: ${color-bg} + style.font-color: ${color-text} + + ecs: bevy_ecs { + style.fill: ${color-bg} + style.font-color: ${color-text} + } + perception: Perception { + style.fill: ${color-bg} + style.font-color: ${color-text} + } + bridge: IPC Bridge { + style.fill: ${color-bg} + style.font-color: ${color-text} + style.stroke: ${color-accent} + } +} + +client.bridge -> server.bridge: "stdin/stdout" { + style.stroke: ${color-accent} + style.stroke-dash: 5 +} +``` + +--- + +## 2. Entity Relationship + +Data schemas, ECS components, knowledge graph structure. + +**When to use:** Database tables, component definitions, entity relationships, knowledge store schema. + +**Agents:** Tyre (ECS component design), Qatux (schema documentation). + +```d2 +entity: Entity { + shape: sql_table + id: u64 {constraint: primary_key} + name: String + faction_id: u64 {constraint: foreign_key} +} + +position: Position { + shape: sql_table + entity_id: u64 {constraint: foreign_key} + x: f32 + y: f32 + chunk_id: u32 +} + +knowledge: KnowledgeEntry { + shape: sql_table + observer_id: u64 {constraint: foreign_key} + subject_id: u64 {constraint: foreign_key} + fact_type: FactType + confidence: f32 + last_seen_tick: u64 +} + +entity.id -> position.entity_id +entity.id -> knowledge.observer_id +entity.id -> knowledge.subject_id +``` + +--- + +## 3. Sequence / Data Flow + +Ordered interactions between systems over time. + +**When to use:** IPC message flow, tick processing, perception update cycle, dialogue system exchanges. + +**Agents:** Tyre (system interaction design), Qatux (protocol documentation). + +```d2 +shape: sequence_diagram + +client: Godot Client +bridge: IPC Bridge +server: Rust Server +ecs: ECS Systems + +client -> bridge: TickRequest(delta, input) +bridge -> server: deserialize + dispatch +server -> ecs: run_systems(delta) +ecs -> ecs: perception, AI, physics +ecs -> server: collect WorldState +server -> bridge: serialize WorldState +bridge -> client: WorldState(entities, events) +client -> client: update rendering +``` + +--- + +## 4. State Machine + +Entity states, transitions, conditions. + +**When to use:** NPC behavior states, game mode transitions, dialogue state, investigation phases. + +**Agents:** Tyre (behavior system design), Qatux (state documentation). + +```d2 +vars: { + color-state: "#2a3040" + color-text: "#c8d0e0" + color-edge: "#c8d8f0" + color-decision: "#333340" +} + +idle: Idle { + style.fill: ${color-state} + style.font-color: ${color-text} +} + +alert: Alert { + style.fill: ${color-state} + style.font-color: ${color-text} +} + +investigate: Investigate { + style.fill: ${color-state} + style.font-color: ${color-text} +} + +combat: Combat { + style.fill: ${color-state} + style.font-color: ${color-text} + style.stroke: "#f0b840" +} + +flee: Flee { + style.fill: ${color-state} + style.font-color: ${color-text} +} + +idle -> alert: "stimulus detected" { style.stroke: ${color-edge} } +alert -> investigate: "stimulus confirmed" { style.stroke: ${color-edge} } +alert -> idle: "timeout / stimulus lost" { style.stroke: ${color-edge}; style.stroke-dash: 5 } +investigate -> combat: "threat confirmed" { style.stroke: "#f0b840" } +investigate -> idle: "nothing found" { style.stroke: ${color-edge}; style.stroke-dash: 5 } +combat -> flee: "health < threshold" { style.stroke: "#f0b840" } +combat -> idle: "threat eliminated" { style.stroke: ${color-edge}; style.stroke-dash: 5 } +flee -> idle: "safe distance reached" { style.stroke: ${color-edge}; style.stroke-dash: 5 } +``` + +--- + +## 5. UI Flow + +Screen navigation, component hierarchy, interaction paths. + +**When to use:** HUD layout relationships, menu navigation, dialogue flow, insert mode transitions. + +**Agents:** Araminta (UI/visual design), Tyre (interface architecture), Qatux (UI documentation). + +```d2 +vars: { + color-screen: "#1a1e24" + color-panel: "#2a3040" + color-text: "#c8d0e0" + color-nav: "#c8d8f0" +} + +gameplay: Gameplay { + style.fill: ${color-screen} + style.font-color: ${color-text} + + hud: HUD { + style.fill: ${color-panel} + style.font-color: ${color-text} + + minimap: Minimap + monologue: Monologue Panel + insert_display: Insert Display + } + + world: World View { + style.fill: ${color-panel} + style.font-color: ${color-text} + } +} + +pause: Pause Menu { + style.fill: ${color-screen} + style.font-color: ${color-text} + + inventory: Inventory + journal: Journal + settings: Settings +} + +dialogue: Dialogue Mode { + style.fill: ${color-screen} + style.font-color: ${color-text} + + speaker: Speaker Panel + responses: Response List +} + +gameplay -> pause: "ESC" { style.stroke: ${color-nav} } +pause -> gameplay: "ESC / Resume" { style.stroke: ${color-nav}; style.stroke-dash: 5 } +gameplay -> dialogue: "interact with NPC" { style.stroke: ${color-nav} } +dialogue -> gameplay: "end conversation" { style.stroke: ${color-nav}; style.stroke-dash: 5 } +``` diff --git a/.claude/skills/d2-diagram/scripts/d2-batch.sh b/.claude/skills/d2-diagram/scripts/d2-batch.sh new file mode 100755 index 000000000..fe456ad58 --- /dev/null +++ b/.claude/skills/d2-diagram/scripts/d2-batch.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR" && git rev-parse --show-toplevel)" +RENDER="$SCRIPT_DIR/d2-render.sh" + +usage() { + cat <&2; exit 1 + fi + shift + ;; + esac +done + +[[ ! -d "$DIR" ]] && { echo "ERROR: Directory not found: $DIR" >&2; exit 1; } + +RENDERED=0 +SKIPPED=0 +FAILED=0 + +while IFS= read -r -d '' d2_file; do + png_file="${d2_file%.d2}.png" + + # Skip if PNG is newer than source (unless --force) + if [[ "$FORCE" != true ]] && [[ -f "$png_file" ]] && [[ "$png_file" -nt "$d2_file" ]]; then + SKIPPED=$((SKIPPED + 1)) + continue + fi + + rel_path="${d2_file#"$REPO_ROOT/"}" + + if [[ "$DRY_RUN" == true ]]; then + echo "Would render: $rel_path" + RENDERED=$((RENDERED + 1)) + continue + fi + + if "$RENDER" "$d2_file" "${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}"; then + RENDERED=$((RENDERED + 1)) + else + echo "FAILED: $rel_path" >&2 + FAILED=$((FAILED + 1)) + fi +done < <(find "$DIR" -name '*.d2' -print0 | sort -z) + +echo "" +echo "Batch complete: $RENDERED rendered, $SKIPPED skipped (up to date), $FAILED failed" diff --git a/.claude/skills/d2-diagram/scripts/d2-render.sh b/.claude/skills/d2-diagram/scripts/d2-render.sh new file mode 100755 index 000000000..fc561d928 --- /dev/null +++ b/.claude/skills/d2-diagram/scripts/d2-render.sh @@ -0,0 +1,92 @@ +#!/bin/bash +set -euo pipefail + +D2="/home/linuxbrew/.linuxbrew/bin/d2" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR" && git rev-parse --show-toplevel)" + +DEFAULT_THEME=200 +DEFAULT_LAYOUT="dagre" +DEFAULT_PAD=100 + +usage() { + cat < [options] + +Render a .d2 file to PNG with project defaults (theme $DEFAULT_THEME, $DEFAULT_LAYOUT layout). + +Commands: + validate Check syntax without rendering + fmt Auto-format in place + +Options: + --theme N Override theme (default: $DEFAULT_THEME) + --layout NAME Override layout engine (default: $DEFAULT_LAYOUT) + --sketch Enable hand-drawn sketch mode + --output PATH Override output path (default: input with .png extension) + --svg Render to SVG instead of PNG + +Examples: + $(basename "$0") docs/diagrams/architecture/ipc-bridge.d2 + $(basename "$0") validate docs/diagrams/architecture/ipc-bridge.d2 + $(basename "$0") docs/diagrams/architecture/ipc-bridge.d2 --sketch --theme 0 +EOF + exit 1 +} + +[[ $# -lt 1 ]] && usage + +# Parse subcommand +SUBCMD="" +case "$1" in + validate|fmt) + SUBCMD="$1" + shift + ;; +esac + +[[ $# -lt 1 ]] && usage + +INPUT="$1" +shift + +# Resolve to absolute path +[[ "$INPUT" != /* ]] && INPUT="$REPO_ROOT/$INPUT" + +[[ ! -f "$INPUT" ]] && { echo "ERROR: File not found: $INPUT" >&2; exit 1; } + +# Handle subcommands +if [[ -n "$SUBCMD" ]]; then + "$D2" "$SUBCMD" "$INPUT" + echo "OK: $SUBCMD $INPUT" + exit 0 +fi + +# Parse render options +THEME="$DEFAULT_THEME" +LAYOUT="$DEFAULT_LAYOUT" +SKETCH="" +OUTPUT="" +FORMAT="png" + +while [[ $# -gt 0 ]]; do + case "$1" in + --theme) THEME="$2"; shift 2 ;; + --layout) LAYOUT="$2"; shift 2 ;; + --sketch) SKETCH="-s"; shift ;; + --output) OUTPUT="$2"; shift 2 ;; + --svg) FORMAT="svg"; shift ;; + *) echo "Unknown option: $1" >&2; exit 1 ;; + esac +done + +# Derive output path +if [[ -z "$OUTPUT" ]]; then + OUTPUT="${INPUT%.d2}.$FORMAT" +fi + +# Render +"$D2" -t "$THEME" -l "$LAYOUT" --pad "$DEFAULT_PAD" $SKETCH "$INPUT" "$OUTPUT" + +SIZE=$(stat --printf="%s" "$OUTPUT" 2>/dev/null || stat -f%z "$OUTPUT" 2>/dev/null) +echo "Rendered: $OUTPUT ($(( SIZE / 1024 ))KB)"