skills/ascii-diagram/SKILL.md
Generate perfectly aligned ASCII diagrams — architecture, flow, sequence, box-and-arrow. Uses a programmatic character-grid approach so alignment is guaranteed by math, not token prediction. Includes post-render verification.
npx skillsauth add SethGammon/Citadel ascii-diagramInstall 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.
Use when:
Do NOT use when:
What this skill needs:
+--+, double-line ╔══╗, rounded ╭──╮, heavy ┏━━┓)Before writing ANY characters, plan the diagram structurally:
→)Write this plan out explicitly before proceeding. Example:
Elements:
A: "Client" → width=10, height=3
B: "Server" → width=10, height=3
C: "Database" → width=12, height=3
Layout: left-to-right
Connections: A→B (HTTP), B→C (SQL)
Total width: 10 + 6 + 10 + 6 + 12 = 44
Use this JavaScript approach mentally (or actually execute it via Bash if the diagram is complex):
// For complex diagrams, RUN this — don't try to hand-align
class Grid {
constructor(w, h) {
this.w = w; this.h = h;
this.cells = Array.from({length: h}, () => Array(w).fill(' '));
}
put(x, y, char) {
if (x >= 0 && x < this.w && y >= 0 && y < this.h) this.cells[y][x] = char;
}
text(x, y, str) {
for (let i = 0; i < str.length; i++) this.put(x + i, y, str[i]);
}
box(x, y, w, h, label) {
// Top border
this.put(x, y, '+');
for (let i = 1; i < w-1; i++) this.put(x+i, y, '-');
this.put(x+w-1, y, '+');
// Bottom border
this.put(x, y+h-1, '+');
for (let i = 1; i < w-1; i++) this.put(x+i, y+h-1, '-');
this.put(x+w-1, y+h-1, '+');
// Sides
for (let j = 1; j < h-1; j++) {
this.put(x, y+j, '|');
this.put(x+w-1, y+j, '|');
}
// Label (centered)
const lines = label.split('\n');
const startY = y + Math.floor((h - lines.length) / 2);
for (let li = 0; li < lines.length; li++) {
const line = lines[li];
const startX = x + Math.floor((w - line.length) / 2);
this.text(startX, startY + li, line);
}
}
hArrow(x1, x2, y, label) {
// Horizontal arrow from x1 to x2 at row y
const dir = x2 > x1 ? 1 : -1;
for (let x = x1; x !== x2; x += dir) this.put(x, y, '-');
this.put(x2, y, dir > 0 ? '>' : '<');
if (label) {
const lx = Math.min(x1, x2) + Math.floor((Math.abs(x2-x1) - label.length) / 2);
this.text(lx, y - 1, label);
}
}
vArrow(x, y1, y2, label) {
// Vertical arrow from y1 to y2 at column x
const dir = y2 > y1 ? 1 : -1;
for (let y = y1; y !== y2; y += dir) this.put(x, y, '|');
this.put(x, y2, dir > 0 ? 'v' : '^');
if (label) this.text(x + 2, Math.min(y1, y2) + Math.floor(Math.abs(y2-y1) / 2), label);
}
render() {
return this.cells.map(row => row.join('').trimEnd()).join('\n');
}
}
For any diagram with 4+ boxes or crossing connections, ACTUALLY RUN the script via Bash using Node. Do not attempt to mentally compute grid coordinates for complex diagrams. This is the entire point of the skill — let code handle alignment.
Pre-built grid engine: .citadel/scripts/grid.cjs provides
Grid and autoLayout(). For auto-layout, pass a JSON spec:
node .citadel/scripts/grid.cjs '{"direction":"horizontal","boxes":[{"id":"a","label":"Input"},{"id":"b","label":"Output"}],"arrows":[{"from":"a","to":"b","label":"data"}]}'
For complex/nested diagrams, use the Grid class directly via require():
node -e "
const {Grid} = require('./.citadel/scripts/grid.cjs');
const g = new Grid(60, 10);
g.box(0, 0, 20, 5, 'Box A');
g.box(30, 0, 20, 5, 'Box B');
g.hArrow(20, 29, 2, 'flow');
console.log(g.render());
"
Verification: .citadel/scripts/verify.cjs checks alignment:
echo "<diagram>" | node .citadel/scripts/verify.cjs --stdin
After generating the diagram, verify these properties:
+ corner has matching corners forming a rectangle-, |, or
diagonal characters ending in >, <, v, ^Verification method: Count characters. Pick any two | side borders that
should be in the same column — they MUST be at the same character offset from
the start of their respective lines.
If verification fails, fix by adjusting coordinates and re-rendering — do NOT try to patch individual characters.
Present the diagram in a fenced code block:
```
[diagram here]
```
If the diagram was generated by a script, also offer to save the generator script so the user can modify and re-run it.
Single: +--------+ Double: ╔════════╗ Rounded: ╭────────╮
| Label | ║ Label ║ │ Label │
+--------+ ╚════════╝ ╰────────╯
Horizontal: -----> <-----> ──────>
Vertical: | | │
| | │
v v ▼
Labeled: HTTP
------->
Pipeline (left-to-right):
+-------+ +-------+ +-------+
| Input |---->| Process|--->| Output|
+-------+ +-------+ +-------+
Layered (top-to-bottom):
+-------------------+
| Presentation |
+-------------------+
|
+-------------------+
| Business |
+-------------------+
|
+-------------------+
| Data |
+-------------------+
Nested (container with children):
+--[ Kubernetes cluster ]------------------+
| |
| +----------+ +----------+ +--------+ |
| | Service | | Service | |Registry| |
| +----------+ +----------+ +--------+ |
| |
+------------------------------------------+
| Symptom | Cause | Fix |
|---------|-------|-----|
| Boxes misaligned vertically | Computed wrong Y offset | Recalculate from top, re-render full grid |
| Arrow doesn't reach target | Off-by-one in x/y range | Use box.x + box.w for right edge, not box.x + box.w - 1 |
| Label overflows box | Box width too small | Recalculate: width = max(label.length + 4, minWidth) |
| Pipes don't line up across rows | Mixed tabs/spaces or variable-width chars | Use ONLY spaces, ONLY ASCII (unless explicitly using Unicode box-drawing) |
+--+ — pick one style.citadel/scripts/grid.cjs not present: The harness hasn't been initialized in this project yet. Either run /do setup to initialize, or use the inline Grid class from Step 2 directly — it's embedded in this skill's protocol as a copy-paste template.+--+ style) and note the switch.verify.cjs --stdinDisclosure: "Generating ASCII diagram. Output to screen (or file if requested)." Reversibility: green — outputs ASCII diagram to screen or a new file; no existing files modified Trust gates:
Present the diagram in a fenced code block. If a script was used to generate it, offer to save it so the user can tweak and re-run. If verification found issues, fix them before presenting.
tools
Bounded foreground repetition for the current session. Creates a loop contract, runs or coordinates an action plus verifier up to a declared attempt limit, and records evidence under .planning/loops/. Use for repeat-until-pass work that is too small for daemon and not time-based scheduling.
testing
Remove Citadel from a project. Exports valuable state (campaigns, postmortems, research, backlog, discoveries) to docs/citadel/ as human-readable markdown, then removes all harness files and hooks. The archive is detected by /do setup on re-install and offered for restore.
development
Research-driven multi-cycle improvement director. Forms causal hypotheses about why scores are low, validates them with scout agents before attacking, dispatches axis-parallel fleet attacks, extracts transferable patterns, and runs indefinitely within a budget envelope. Accumulates a persistent belief model and pattern library across sessions.
data-ai
Multi-repo campaign coordinator. Same lifecycle as fleet -- scope claims, discovery relay, wave-based execution -- but the unit of work is a repo, not a file. Coordinates campaigns across repositories with shared context.