/SKILL.md
Use when user requests diagrams, flowcharts, architecture charts, or visualizations. Also use proactively when explaining systems with 3+ components, complex data flows, or relationships that benefit from visual representation. Generates .excalidraw files and exports to PNG/SVG via Kroki API or locally using excalidraw-brute-export-cli.
npx skillsauth add agents365-ai/excalidraw-skill excalidrawInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate .excalidraw JSON files and export to PNG/SVG.
Two export options:
curl) — zero install, SVG output onlySupported formats: PNG (local CLI only), SVG (both options). PDF is NOT supported.
Explicit triggers: user says "画图", "diagram", "visualize", "flowchart", "draw", "架构图", "流程图"
Proactive triggers:
Skip when: a simple list or table suffices, or user is in a quick Q&A flow
# Just needs curl (pre-installed on macOS/Linux/Windows Git Bash)
curl --version
No additional setup. SVG rendered via https://kroki.io.
The CLI uses Firefox (not Chromium). Check and install:
npm install -g excalidraw-brute-export-cli
npx playwright install firefox
macOS patch (one-time, required):
CLI_MAIN=$(npm root -g)/excalidraw-brute-export-cli/src/main.js
sed -i '' 's/keyboard.press("Control+O")/keyboard.press("Meta+O")/' "$CLI_MAIN"
sed -i '' 's/keyboard.press("Control+Shift+E")/keyboard.press("Meta+Shift+E")/' "$CLI_MAIN"
Windows/Linux: No patch needed.
Update check (notify, don't pull) — first use per conversation. Throttle to once per 24 h via <this-skill-dir>/.last_update; never mutate the skill directory without explicit user consent.
If .last_update exists and is <24 h old, skip this step entirely.
Otherwise, fetch the latest tag from upstream:
git -C <this-skill-dir> ls-remote --tags origin 'v*' 2>/dev/null \
| awk '{print $2}' | sed 's|refs/tags/||' | sort -V | tail -1
Compare with this skill's metadata.version from the frontmatter. If the upstream tag is strictly newer (semver), tell the user one line and ask:
"A newer version of this skill is available: vX.Y.Z → vA.B.C. Want me to
git pull?"
If they say yes, run git -C <this-skill-dir> pull --ff-only. Refresh .last_update either way so the prompt doesn't repeat for 24 hours.
If upstream is the same or older, refresh .last_update silently and continue.
On any failure (offline, not a git checkout — e.g. ClawHub-installed copy, read-only path, no permission), swallow the error silently and continue with the user's task. Do not mention the failure.
Check deps — use Kroki (curl) for SVG; use local CLI for PNG
Plan — identify diagram type, pick a visual pattern, choose color palette
Generate — write .excalidraw JSON file (section-by-section for large diagrams)
Export — run Kroki or CLI command
Report — tell user the output file path
roughness: 0 — clean, modern look for all technical diagrams (use 1 only when user requests hand-drawn/casual style)fontFamily: 2 (Helvetica) — professional look; use 1 (Virgil) only for casual/sketch style, 3 (Cascadia) for code snippetsfillStyle: "solid" — default fill| Level | Size | Use for | |-------|------|---------| | Title | 28px | Diagram title | | Header | 24px | Section/group headers | | Label | 20px | Primary element labels | | Description | 16px | Secondary text, descriptions | | Note | 14px | Annotations, fine print |
Follow the 60-30-10 rule: 60% whitespace/neutral, 30% primary accent, 10% highlight.
Semantic fill colors (use with strokeColor one shade darker):
| Category | Fill | Stroke | Use for |
|----------|------|--------|---------|
| Primary / Input | #dbeafe | #1e40af | Entry points, APIs, user-facing |
| Success / Data | #dcfce7 | #166534 | Data stores, success states |
| Warning / Decision | #fef9c3 | #854d0e | Decision points, conditions |
| Error / Critical | #fee2e2 | #991b1b | Errors, alerts, critical paths |
| External / Storage | #f3e8ff | #6b21a8 | External services, databases, AI/ML |
| Process / Default | #e0f2fe | #0369a1 | Standard process steps |
| Trigger / Start | #fed7aa | #c2410c | Start nodes, triggers, events |
| Neutral / Container | #f1f5f9 | #475569 | Groups, swimlanes, backgrounds |
Text colors:
| Level | Color |
|-------|-------|
| Title | #1e293b |
| Label | #334155 |
| Description | #64748b |
Rule: Do not invent new colors. Pick from this palette.
| Style | Meaning |
|-------|---------|
| Solid (strokeStyle: null) | Primary flow, main path |
| Dashed ("dashed") | Response, async, callback |
| Dotted ("dotted") | Optional, reference, weak dependency |
{
"type": "excalidraw",
"version": 2,
"source": "claude-code",
"elements": [],
"appState": { "viewBackgroundColor": "#ffffff" }
}
| type | use for | |-----------|----------------------------------| | rectangle | boxes, components, modules | | ellipse | start/end nodes, databases | | diamond | decision points | | arrow | directed connections | | line | undirected connections | | text | standalone labels |
Calculate element width from label text to prevent truncation:
Latin text: width = max(160, charCount * 9)
CJK text: width = max(160, charCount * 18)
Mixed text: estimate each character individually, sum up
Height: use 60 for single-line labels, add 24 per additional line.
{
"id": "auth_service",
"type": "rectangle",
"x": 100, "y": 100,
"width": 160, "height": 60,
"angle": 0,
"strokeColor": "#1e40af",
"backgroundColor": "#dbeafe",
"fillStyle": "solid",
"strokeWidth": 2,
"roughness": 0,
"opacity": 100,
"seed": 100001,
"boundElements": [
{ "id": "arrow_to_db", "type": "arrow" },
{ "id": "label_auth", "type": "text" }
]
}
Use descriptive string IDs (e.g., "api_gateway", "arrow_gw_to_auth") instead of random strings.
Give each element a unique seed (integer). Namespace by section: 100xxx, 200xxx, 300xxx.
boundElements: use null when empty, never []updated: always use 1, never timestampsframeId, index, versionNonce, rawTextpoints in arrows: always start at [0, 0]seed: must be a positive integer, unique per elementWhen text belongs inside a shape, bind them bidirectionally:
{
"id": "label_auth",
"type": "text",
"text": "Auth Service",
"fontSize": 20,
"fontFamily": 2,
"textAlign": "center",
"verticalAlign": "middle",
"strokeColor": "#1e293b",
"containerId": "auth_service"
}
CRITICAL: Text strokeColor is the text color. Always set it explicitly to a dark color from the text color palette. Never omit it — omitting strokeColor on text can cause invisible text that blends with the shape background.
The parent shape must list the text in its boundElements:
"boundElements": [{ "id": "label_auth", "type": "text" }]
Arrows must bind to shapes, and shapes must reference bound arrows:
{
"id": "arrow_gw_to_auth",
"type": "arrow",
"points": [[0, 0], [200, 0]],
"startBinding": { "elementId": "api_gateway", "gap": 5, "focus": 0 },
"endBinding": { "elementId": "auth_service", "gap": 5, "focus": 0 }
}
Both api_gateway and auth_service must include in their boundElements:
"boundElements": [{ "id": "arrow_gw_to_auth", "type": "arrow" }]
L-shaped (elbow) arrows — orthogonal routing with 3+ points:
"points": [[0, 0], [100, 0], [100, 150]]
Elbowed arrows — automatic right-angle routing:
{
"type": "arrow",
"points": [[0, 0], [0, -50], [200, -50], [200, 0]],
"elbowed": true
}
Curved arrows — smooth routing with waypoints:
{
"type": "arrow",
"points": [[0, 0], [50, -40], [200, 0]],
"roundness": { "type": 2 }
}
Related elements share groupIds. Nested groups list IDs innermost-first:
"groupIds": ["inner_group", "outer_group"]
Choose the right visual pattern for each diagram type.
| Scenario | Spacing | |----------|---------| | Labeled arrow gap (between shapes) | 150–200px | | Unlabeled arrow gap | 100–120px | | Column spacing (labeled arrows) | 400px (220px box + 180px gap) | | Column spacing (unlabeled arrows) | 340px (220px box + 120px gap) | | Row spacing | 280–350px (150px box + 130–200px gap) | | Zone/container padding | 50–60px around children | | Zone/container opacity | 25–40 | | Minimum gap between any elements | 40px |
Neutral containers (opacity: 30, padding: 50px)Trigger colorPrimary color, radial around centerProcess colorNeutral colorNeutral fill, "dashed" stroke, opacity: 30) as lane boundariesFor diagrams with 10+ elements, do NOT generate the entire JSON at once. Build in sections:
elements arrayboundElements and startBinding/endBinding references are consistentNamespace element seeds by section (100xxx, 200xxx, 300xxx) to avoid collisions.
# SVG via Kroki API
curl -s -X POST https://kroki.io/excalidraw/svg \
-H "Content-Type: application/json" \
--data-binary "@diagram.excalidraw" \
-o diagram.svg
# Via local Kroki Docker (offline)
curl -s -X POST http://localhost:8000/excalidraw/svg \
-H "Content-Type: application/json" \
--data-binary "@diagram.excalidraw" \
-o diagram.svg
# PNG at 2x scale (recommended)
excalidraw-brute-export-cli -i diagram.excalidraw -o diagram.png -f png -s 2
# PNG at 1x scale
excalidraw-brute-export-cli -i diagram.excalidraw -o diagram.png -f png -s 1
# SVG
excalidraw-brute-export-cli -i diagram.excalidraw -o diagram.svg -f svg -s 1
Required flags: -f (format: png or svg) and -s (scale: 1, 2, or 3).
Never put text on large background/zone rectangles. Excalidraw centers text in the middle of the shape, overlapping contained elements. Instead, use a free-standing text element positioned at the top of the zone.
Avoid cross-zone arrows. Long diagonal arrows create visual spaghetti. Route arrows within zones or along zone edges. If a cross-zone connection is unavoidable, route it along the perimeter.
Use arrow labels sparingly. Labels placed at the arrow midpoint overlap on short arrows. Keep labels to ≤12 characters and ensure ≥120px clear space between connected shapes. Omit labels when the connection meaning is obvious from context.
Don't use filled backgrounds on containers that hold other elements. Use opacity: 30 (or 25-40 range) for zone/container rectangles so contained elements remain visible.
Always set explicit strokeColor on text elements. Text strokeColor is the rendered text color. If omitted, text may inherit the parent shape's background color and become invisible. Use #1e293b (title), #334155 (label), or #64748b (description) from the text color palette.
| Mistake | Fix |
|---------|-----|
| Kroki returns error | Ensure file is valid JSON with "type": "excalidraw" and "elements" array |
| Kroki only outputs SVG | Use local CLI (excalidraw-brute-export-cli) for PNG |
| Export fails with "Missing required flag" | Always pass -f png and -s 2 |
| Export fails with "Executable doesn't exist" | Run npx playwright install firefox |
| macOS: timeout waiting for file chooser | Apply the macOS Meta patch above |
| Arrow points not relative to origin | points always start at [0,0] |
| Missing id on elements | Use descriptive string IDs per element |
| Overlapping elements | Use spacing reference table; minimum 40px gap |
| Arrows not interactive in excalidraw.com | Add boundElements to shapes referencing all bound arrows/text |
| Text not centered in shape | Set containerId on text AND add text to shape's boundElements |
| All text same size | Use font size hierarchy: 28 → 24 → 20 → 16 → 14 |
| Diagram looks monotone | Apply semantic colors from the palette, follow 60-30-10 rule |
| Text invisible / same color as background | Always set strokeColor on text elements to a dark color (#1e293b, #334155, or #64748b) |
| Text overlaps inside zone/container | Don't bind text to zone rectangles; use free-standing text at top |
| Text truncated in shapes | Use width formula: max(160, charCount * 9), double for CJK |
| boundElements: [] causes issues | Use null for empty boundElements, never [] |
tools
Use when user requests diagrams, flowcharts, architecture charts, or visualizations. Also use proactively when explaining systems with 3+ components, complex data flows, or relationships that benefit from visual representation. Generates .excalidraw files and exports to PNG/SVG via Kroki API or locally using excalidraw-brute-export-cli.
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------