From arcforge
Creates Excalidraw diagrams in Obsidian for architecture visualizations, flowcharts, mind maps, and concept relationships using design-build-validate-save pipeline with Playwright rendering.
npx claudepluginhub gregoryho/arcforge --plugin arcforgeThis skill uses the workspace's default tool permissions.
Diagrams should ARGUE, not DISPLAY. A diagram is a visual argument — structure mirrors concept, shape carries meaning. If removing all text leaves a meaningless grid of boxes, the diagram has failed.
agents/diagram-builder.mdagents/diagram-saver.mdagents/diagram-validator.mdreferences/check_overlaps.pyreferences/color-palette.mdreferences/depth-enhancements.mdreferences/element-templates.mdreferences/layout-heuristics.mdreferences/painters-toolkit.mdreferences/plan_layout.pyreferences/pyproject.tomlreferences/render_excalidraw.pyreferences/render_template.htmlreferences/save-format.mdreferences/uv.lockreferences/verify_saved_diagram.pyreferences/visual-patterns.mdMandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Diagrams should ARGUE, not DISPLAY. A diagram is a visual argument — structure mirrors concept, shape carries meaning. If removing all text leaves a meaningless grid of boxes, the diagram has failed.
DESIGN → BUILD → VALIDATE → SAVE
You own the full workflow. For complex diagrams, delegate mechanical phases (Build, Validate, Save) to subagents that read from agents/ — this keeps your context clean. For simple diagrams or when subagents aren't available, execute each phase yourself. The instructions below are self-contained either way.
The rest of this skill is split into two layers of guidance:
Both layers apply across all four phases.
Detect theme before building. Choosing the wrong palette silently produces an unreadable diagram (dark palette on light background = pale washout; light palette on dark = near-invisible text). The Playwright renderer happily renders whatever colors you picked — no feedback loop to catch this.
obsidian eval code="document.body.classList.contains('theme-dark') ? 'dark' : 'light'"
If no response within 5 seconds, pick a palette from prompt signals ("dark mode" in the request, time of day) and state the assumption in your completion output. Never silently assume.
Every Validate iteration must render AND view PNG. You cannot judge composition, readability, or hierarchy from JSON alone. The overlap checker catches bounding-box overlaps but not visual problems — arrows crossing unrelated elements at acute angles, text too small to read, hierarchy failing because the hero doesn't dominate. View /tmp/diagram.png with the Read tool every iteration.
Save must be verified. Both ea.create() and the manual-fallback write path can silently produce format corruption — the canvas renders, markdown text bleeds through, and the Playwright renderer cannot detect this. Always run verify_saved_diagram.py after save.
ea.reset() at the start of every EA invocation — without it, elements accumulate from previous calls and appear in the output invisiblyid — this orphans connected arrows (they lose the shape they were bound to)addText with box returns the BOX id, not the text id — use the returned value for connectObjectsviewBackgroundColor matches the detected theme — #1e1e1e for dark, #ffffff for lightreferences/save-format.mdFour arrow-path collisions that recur across diagrams. Each is a rendered-pixel overlap, detectable by check_overlaps.py after build, but cheaper to prevent at design time by tracing arrow paths mentally.
Mentally trace every planned arrow from source to target before writing EA code. If the path crosses an unrelated element, reposition before building. For fix strategies (arrow routing, anchor distribution, waypoint planning), read references/layout-heuristics.md Part 2.
HARD keeps the diagram technically valid. SOFT is where you make it good. These are concept judgments — they depend on what you're drawing.
For each major concept in the diagram, answer before reaching for shapes:
Every element should serve the concept. Before adding anything, ask: what does this communicate that labels alone don't?
If the answer is "nothing" — the element is noise. If the answer is "this is where the flow starts" / "this is state the user should sense" / "this is a zone boundary" / "this shows the concept is symmetric" — the element is doing work.
Two registers to match:
Input → Process → Output), give peers the same size. Hero sizing implies real importance — don't fabricate one where the concept doesn't claim it.Decoration is not the enemy. Unjustified decoration is.
When one element is genuinely more important than peers (a convergence point that everything flows into, the hero concept the diagram is about), size it larger. When peers are genuinely equal, size them equally. Concrete size ranges are in references/painters-toolkit.md — they're suggestions for reference, not mandated tiers.
Before building, ask: if I removed all text from this design, would the structure communicate anything about the concept? A fan-out says "one source, many outputs" without any labels. A convergence says "many inputs, one result." A cycle says "feedback."
If your planned structure is just "labeled boxes connected by arrows" with no isomorphism to the concept — the structure isn't doing work. Revisit.
This is a check, not a gate. Sometimes "A connects to B" is genuinely what the concept says, and that's fine. But the check prompts verification before committing.
Shape variety, subtitles, zone labels, decorative accents, separators, containers, footer annotations — these are your brushes. Read references/painters-toolkit.md during design to see the full vocabulary, then pick what serves the current concept.
Not a menu to copy from. A vocabulary to pick from.
For pattern inspiration — fan-out, convergence, tree, timeline, spiral/cycle, cloud, assembly line, side-by-side, gap — see references/visual-patterns.md. Each pattern has ASCII sketches, shape-meaning tables, and "when NOT to use."
Each major concept in a diagram typically uses a different pattern. If two adjacent sections look structurally identical, consider redesigning one — visual monotony kills comprehension.
references/depth-enhancements.md.references/plan_layout.py to compute coordinates automatically — it enforces systematic spacing and two-column separation that prevents the most common overlap defects.Create elements using the EA API via obsidian eval. EA handles text sizing and arrow binding automatically. The build ends by exporting all elements as a .excalidraw JSON file to /tmp/diagram.excalidraw.
Before writing EA code, read references/layout-heuristics.md Part 1 for grid-based coordinate planning.
(async () => {
const ea = window.ExcalidrawAutomate;
ea.reset();
const s = ea.style;
s.roughness = 0; s.opacity = 100; s.fillStyle = 'solid';
s.fontFamily = 3; s.roundness = {type: 3};
// --- Style BEFORE each element ---
s.strokeColor = '#e2e8f0'; s.backgroundColor = '#1e40af'; s.fontSize = 16;
const boxA = ea.addText(200, 50, 'Label', {
box: 'rectangle', boxPadding: 20, boxStrokeColor: '#60a5fa'
});
// --- Arrows ---
s.strokeColor = '#475569';
ea.connectObjects(boxA, 'bottom', boxB, 'top', { endArrowHead: 'arrow' });
// --- Free text + structural lines ---
s.strokeColor = '#93c5fd'; s.fontSize = 20;
ea.addText(40, 210, 'SECTION TITLE');
// --- Export ---
const els = ea.getElements();
const json = {
type: 'excalidraw', version: 2, source: 'https://excalidraw.com',
elements: els,
appState: { viewBackgroundColor: '#1e1e1e', gridSize: 20 },
files: {}
};
require('fs').writeFileSync('/tmp/diagram.excalidraw', JSON.stringify(json, null, 2));
return els.length + ' elements exported';
})()
Key build rules (these restate the mechanical invariants above in context):
ea.style.* applies to the NEXT element createdaddText with box returns the BOX id — use this for connectObjects, not the text idviewBackgroundColor — #1e1e1e (dark) or #ffffff (light), per Step 4Read references/element-templates.md for the full EA API reference, raw JSON templates for Phase 2 fixes, and the binding checklist.
Mermaid shortcut: For simple flowcharts under 10 elements, use ea.addMermaid() instead. Only flowchart type produces native editable elements — other Mermaid types fall back to SVG images.
After building, render to PNG, view it, fix defects. Up to 3 iterations — then save and report remaining issues.
ITERATION (repeat up to 3×):
1. CHECK — cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv run python check_overlaps.py /tmp/diagram.excalidraw
2. RENDER — cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv run python render_excalidraw.py /tmp/diagram.excalidraw \
--output /tmp/diagram.png --scale 2
View /tmp/diagram.png with the Read tool — HARD, every iteration.
3. JUDGE — Apply SOFT judgment. Design intent: correct patterns? Hero
dominant (if concept claims one)? Brushes from Painter's Toolkit
serving the concept? Defects: overlaps, crossings, uneven
spacing, text too small?
4. FIX — Edit .excalidraw JSON directly (Read → find element → Edit x/y).
Moving shapes does NOT break arrow binding (Excalidraw recalculates
from binding data, not coordinates).
Never change element IDs — this orphans connected arrows.
→ Next iteration, or proceed to Save if clean.
Read references/layout-heuristics.md Part 2 for fix strategies.
First-time setup (if renderer fails with missing deps):
cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv sync && uv run playwright install chromium
When to rebuild vs. JSON-edit: Positional fixes (move, resize, spacing) → edit JSON. Structural changes (add/remove elements, change connections) → rebuild from Phase 1 with ea.reset().
Two paths — prefer ea.create(), fall back to manual write only if EA is unreachable.
ea.create() via obsidian eval(async () => {
const ea = window.ExcalidrawAutomate;
ea.reset();
const json = JSON.parse(require('fs').readFileSync('/tmp/diagram.excalidraw', 'utf8'));
json.elements.forEach(el => { ea.elementsDict[el.id] = el; });
ea.setView('new');
await ea.create({
filename: '<name>', foldername: '<folder>',
onNewPane: false, silent: true
});
return 'Saved to vault';
})()
ea.elementsDict is a documented public property used in official Excalidraw scripts.
Only use when obsidian eval code="typeof window.ExcalidrawAutomate" returns empty (EA plugin unavailable). Obsidian checks format heuristics — any deviation causes silent corruption. Read references/save-format.md for the byte-exact template.
cd ${ARCFORGE_ROOT}/skills/arc-diagramming-obsidian/references && \
uv run python verify_saved_diagram.py <vault-path>/<name>.excalidraw.md
Exits non-zero on format corruption or render mismatch. For the manual-fallback path, also compares the re-rendered PNG against /tmp/diagram.png to catch JSON corruption. If verification fails, regenerate using the canonical template from references/save-format.md — not the file you just wrote.
![[diagram-name]]
Place outside bilingual callouts (diagrams are language-neutral).
For complex diagrams, spawn a subagent for each mechanical phase to keep context clean. For simple diagrams or when subagents aren't available, follow the phases above directly.
When delegating, pass the agent file and relevant context. Each subagent reads its instructions and the reference files it needs:
agents/diagram-builder.md. Output: /tmp/diagram.excalidraw + element count.agents/diagram-validator.md. Output: validated .excalidraw + PNG at /tmp/diagram.png + issues report.agents/diagram-saver.md. Output: vault path of saved file.Quality gate: After validation returns, view the PNG yourself before proceeding to Save. If it doesn't match your design intent, revise the spec and re-run Build, or give specific fix instructions to a new Validate pass.
references/color-palette.md — Semantic colors for light + dark modereferences/visual-patterns.md — 9 patterns with layout guidancereferences/painters-toolkit.md — Shape variety, subtitles, zone labels, containers, accents, separators, footers, size suggestionsreferences/element-templates.md — Full EA API reference + raw JSON templatesreferences/layout-heuristics.md — Grid planning (Part 1) + fix strategies (Part 2)references/depth-enhancements.md — Research, Multi-Zoom, Evidence (comprehensive only)references/save-format.md — Manual .excalidraw.md canonical template (fallback save path)references/plan_layout.py — Automatic coordinate computation for 20+ elementsreferences/verify_saved_diagram.py — Post-save verification (format markers + render check)Keep in-scope tasks yourself; route out-of-scope tasks to the dedicated skill.
| Task | Do yourself | Delegate to |
|---|---|---|
| Generate elements (EA API or raw JSON) | ✓ | — |
| Render to PNG (Playwright) | ✓ | — |
Check overlaps (check_overlaps.py) | ✓ | — |
Layout planning (plan_layout.py) | ✓ | — |
| Detect Obsidian theme | — | obsidian:obsidian-cli (via obsidian eval) |
| Find vault path / search existing notes | — | obsidian:obsidian-cli |
Write .excalidraw.md to vault | ✓ (direct filesystem write) — OR ea.create() | — |
| Embed diagram in a wiki note | — | obsidian:obsidian-cli + obsidian:obsidian-markdown |
| Reload a changed diagram in Obsidian | — | obsidian:obsidian-cli |
✅ Created diagram → [vault-path/filename.excalidraw]
Pattern: [visual patterns used]
Elements: [count]
Validated: [iterations completed]
⚠️ Diagramming blocked
Issue: [what went wrong]
To resolve: [specific action needed]