Notes → Diagram
Convert rough points, in any format, into correct and readable diagrams — delivered as Mermaid source, an editable draw.io file, a Lucidchart import bundle, rendered images, and a combined PDF.
The hard part is not diagram syntax. It is reading someone's half-formed notes and deciding what is a node, what is a relationship, what is an attribute, what is noise, and what is simply missing. This skill is mostly about that decision-making. Syntax lives in references/syntax-reference.md; platform specifics live in references/platforms.md.
Workflow
Seven steps. Do not skip step 3 — jumping straight from raw notes to diagram syntax is what produces diagrams that look plausible and are quietly wrong.
1. Get the text out of the source
If the points were pasted into the conversation, you already have them — move on.
If they're in a file, extract first:
python3 scripts/extract_points.py <file> [more files...]
It handles .docx, .pdf, .xlsx/.xlsm, .csv/.tsv, .txt, .md, .json, .html, .pptx and prints clean text to stdout. Spreadsheets come out as a row-per-line table, which is usually exactly the structure the diagram needs. Format-specific pitfalls are covered in references/input-handling.md — read it when extraction comes back messy or empty.
2. Lock in the diagram type
Usually the user names it. Honor that choice even if you'd pick differently.
If they didn't name one, infer from the shape of the notes and state your pick in one line as you deliver ("These read as an ordered API exchange, so I built a sequence diagram"). Only ask if two types are genuinely equally defensible.
| Notes look like… | Type | Mermaid header |
|---|---|---|
| Nouns with properties and "has a / is a" links | Class diagram | classDiagram |
| Ordered exchange between named parties | Sequence diagram | sequenceDiagram |
| Steps with decisions, branches, outcomes | Flowchart | flowchart TD |
| Tables, fields, keys, cardinality | ERD | erDiagram |
| Systems, users, services, deployment layers | C4 | C4Context / C4Container / C4Component |
| One thing moving between named statuses | State diagram | stateDiagram-v2 |
| Branches, commits, merges, release flow | Git graph | gitGraph |
| Tasks with dates, durations, dependencies | Gantt | gantt |
| Categories with numbers that sum to a whole | Pie / bar | pie / xychart-beta |
If the requested type genuinely can't hold the material, still produce the best version you can, then say plainly what was missing and which type would fit better. Don't refuse, and don't silently switch types.
3. Build an extraction inventory
Before writing any diagram syntax, restate the notes as a structured inventory. This is where the thinking happens — it forces every diagram element to trace back to something the user actually wrote, and it doubles as the input to the draw.io generator in step 5.
Read the playbook for your diagram type in references/extraction-playbooks.md. Each playbook gives you the elements that type requires, the phrases in rough notes that signal each element, default rules for what notes always leave out, and a worked example.
Keep the inventory terse:
ENTITIES: Order, Customer, LineItem, Product
RELATIONS: Customer places Order (1:many, from "each customer can have several orders")
Order contains LineItem (1:many, "min one item")
ATTRIBUTES: Order → id PK, total, created_at
UNCLEAR: "product catalog sync" — mentioned once, no relation stated
MISSING: no key marked on Product
Three rules govern it:
- Never invent an element. If
Paymentis not in the notes, it is not in the diagram — however obviously the domain implies it. - Inference is allowed; silence is not. You may infer cardinality, direction, or ordering when the notes strongly imply it, but every inference goes in the assumptions list you deliver in step 7.
- Noise gets dropped, not diagrammed. Attendee names, action items, "TODO ask Priya", timestamps — leave them out unless they're part of the subject matter.
4. Ask what they want produced
Platforms and export formats are cheap to generate but not free, and the right answer varies. Ask once, up front, with the ask_user_input tool if available — one compact question set rather than a stream of follow-ups:
- Platforms: Mermaid, draw.io, Lucidchart (multi-select; default to all three if they've already said "all platforms")
- Image formats: SVG, PNG, JPG, none (multi-select)
- Combined PDF: yes / no
Skip the question and proceed with sensible defaults when the user has already specified them, or when they clearly want speed ("just give me a quick flowchart"). Defaults if unstated: Mermaid only, PNG, no PDF. Never ask twice in one task.
5. Generate the platform files
Everything lands under an output folder — one subfolder per platform, so nothing collides:
<output>/
├── mermaid/ <name>.mmd
├── drawio/ <name>.drawio
├── lucidchart/ <name>.mmd, <name>.drawio, IMPORT-<name>.md
├── images/ <name>.svg / .png / .jpg
└── diagrams.pdf
Mermaid — write the .mmd by hand from the inventory, using references/syntax-reference.md. This is the source of truth; the other platforms derive from it.
draw.io — write the inventory out as a graph model JSON (nodes, edges, shapes) and convert it:
python3 scripts/build_drawio.py graph.json -o <output>/drawio/<name>.drawio
This produces real, editable mxGraph shapes with an automatic layered layout — not a picture. The model format and shape vocabulary are documented at the top of the script and in references/platforms.md. Node-and-edge diagrams (flowchart, state, class, ERD, C4) convert cleanly; sequence, gantt, gitGraph and pie have no faithful node-edge form, so for those follow the fallback guidance in references/platforms.md rather than forcing a bad conversion.
Lucidchart — Lucidchart has no open authoring format, so there is no file to write directly. It has two real import routes, and this builds both plus the instructions:
python3 scripts/build_lucid.py <output>/mermaid/<name>.mmd \
--drawio <output>/drawio/<name>.drawio --outdir <output>/lucidchart
It checks the diagram type against what Lucid's "Diagram as code" panel actually supports, picks the right route, and writes IMPORT-<name>.md with the steps. Don't promise a "Lucidchart file" — say what it is: a Mermaid paste-in plus a draw.io import, with instructions.
6. Lint, then render
Lint every Mermaid file. This catches errors that render as a blank box instead of a message:
python3 scripts/lint_mermaid.py <output>/mermaid/<name>.mmd
Fix everything it flags and re-run until clean. It's static, so a clean pass means no known breakers — also eyeball the diagram against the inventory: is every entity present, every relation drawn, every arrow pointing the right way?
Then render whatever formats the user chose, and build the PDF in the same command:
python3 scripts/render_export.py <output>/mermaid \
--formats png,svg --outdir <output>/images --pdf <output>/diagrams.pdf
Rendering drives a headless Chrome via mmdc. Neither is guaranteed to exist, so render_export.py first calls scripts/ensure_renderer.py, which locates them and installs whatever is missing (npm i -g @mermaid-js/mermaid-cli, npx puppeteer browsers install chrome). The outcome is cached, so a blocked network costs one attempt rather than one per run.
If the toolchain can't be assembled, rendering is skipped and the reason is reported. That is not a failure of the task: deliver the sources and say plainly that images couldn't be produced here, because the .mmd files still render in GitHub, GitLab, VS Code, Notion, Obsidian and mermaid.live. Don't retry by hand — the bootstrap already tried.
--pdf-only <imagedir> bundles existing images without re-rendering.
7. Deliver
Give the user, in this order:
- The diagram inline as a ```mermaid fenced block, so they can see it immediately.
- The files, presented with
present_files— sources first, then images, then the PDF. A file that is written but never presented is unreachable on mobile. - Assumptions — every inference you made, one line each, phrased so they can correct you fast:
Assumed Customer→Order is 1-to-many (notes said "several orders per customer"). - Gaps — what the notes didn't cover, with the question that would fill each:
No primary key given for Product — is it product_id?
Keep 3 and 4 short and skip either if genuinely empty. A diagram delivered with an honest three-line gap list is far more useful than one that hides its guesses.
Then offer the natural next move: splitting an oversized diagram, adding a second view, or regenerating after they correct an assumption.
Sizing and readability
Rough notes often contain more than one diagram's worth of material. A diagram past roughly 20 nodes stops communicating.
When the material is too big, prefer splitting by concern over shrinking: one diagram per subsystem, per user journey, or per C4 level. Say what you split and why. Multiple diagrams flow naturally through the pipeline — each gets its own .mmd, its own image, and its own page in the PDF.
If the user explicitly wants it all in one, use subgraph (flowchart) or box (sequence) grouping to keep it legible, and put the direction on the first line (flowchart LR reads better than TD for wide, shallow processes).
Quality bar
Before delivering, check:
- Every diagram element traces to something in the notes, or appears in the assumptions list.
- Every substantive point in the notes is either in the diagram, in the gaps list, or was noise.
- Labels use the user's own vocabulary — if the notes say "rider", the node says Rider, not User.
- The linter passes on every
.mmd. - Node/participant names are consistent throughout (a typo'd second mention creates a phantom node, the most common silent failure).
- The draw.io file opens as shapes, and its node count matches the Mermaid version.
- Every file the user asked for exists and was presented.
Reference files
references/extraction-playbooks.md— per-type: required elements, signal phrases in rough notes, default rules, worked examples. Read the section for your type in step 3.references/syntax-reference.md— compact Mermaid syntax for all supported types plus escaping and pitfalls. Read in step 5.references/platforms.md— draw.io graph model and shape vocabulary, Lucidchart import routes and limits, rendering and PDF options, per-type conversion fidelity. Read in steps 4–6.references/input-handling.md— per-format extraction recipes and recovery. Read in step 1 only if extraction is messy.
Scripts
scripts/extract_points.py— text out of docx/pdf/xlsx/csv/txt/md/json/html/pptx.--list-sheets,--sheet,--pages.scripts/lint_mermaid.py— static checks for silent-failure syntax errors.--strictadds style warnings.scripts/build_drawio.py— graph model JSON → editable.drawiowith layered auto-layout.scripts/build_lucid.py— Mermaid + draw.io → Lucidchart import bundle with compatibility check.scripts/render_export.py—.mmd→ SVG/PNG/JPG and a combined PDF.--pdf-onlybundles existing images.scripts/ensure_renderer.py— finds or installs mermaid-cli + Chrome, caching the result. Called automatically byrender_export.py; run--statusto check without installing.