agentleFS
yoganyogan/agent-skills/skills/visualize/SKILL.md

visualize

Skill2 starsChanged 2 months agoMITView on GitHub

Draws a diagram of something in the codebase — a database layout, a request or interaction flow, how a set of classes relate, a state machine, or an architecture — by exploring the code, deriving a spec, rendering it with D2, checking it against legibility and contrast gates, and opening the finished image. Use when the user asks to see, draw, diagram, visualize, or get an overview of structure or flow ("show me the DB layout", "visualize what happens when a user logs in", "give me a visual overview of the service classes", "diagram the socket lifecycle"), or invokes /visualize.

45 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use exp-pilot-eval, exp-pilot-run and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • skyllwtSkill

    exp-pilot-eval

    Pilot result evaluation — read pilot results, apply success criteria, update idea page (pilot_result, failure_reason if failed), generate PILOT_VERDICT_REPORT. Called by /ideate Phase 5 after /exp-pilot-run.

    Also in 4 projects that use this

  • skyllwtSkill

    exp-pilot-run

    Pilot experiment execution — read Pilot Spec YAML, write pilot code, run experiment(Confirm with the user before operation and require the applicant to conduct manual inspection), return results. Called by /ideate Phase 5. Does NOT modify wiki pages or judge pass/fail.

    Also in 4 projects that use this

  • LambenthanSkill

    daily-arxiv

    每日从 arXiv 拉取新论文,过滤相关性,auto-ingest 高优先级论文,检测 SOTA 更新

    Also in 4 projects that use this

  • LambenthanSkill

    exp-eval

    实验判决门:Review LLM 独立评判实验结果 → 4 种判决路径 → 自动更新 claims confidence、ideas status、graph edges

    Also in 4 projects that use this

  • phanghonghaoSkill

    poster

    HTML/CSS A4 海报生成。从沙龙/活动 Markdown 文件生成科技风 A4 海报 PDF/PNG。Use when user mentions: poster, 海报, 海报生成, make poster, generate poster, 活动海报, 沙龙海报.

    Also in 4 projects that use this

  • +1 more from skyllwt
  • +6 more from Lambenthan

The file

Show its rules and text

We split this skill into the rules it gives an agent

18 rules. 0 are common, 16 are distinctive, and 2 are very specific to this project.

  • 0

    Common

    Most of the 43 edited versions we read keep these.

  • 16

    Distinctive

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Read the user's words for the giveaways:

    line 33
  2. Distinctive:

    If the request genuinely spans two questions ("show me the schema and how a write flows through it"), draw two diagrams, each answering one.

    line 46
  3. Distinctive:

    If the request is ambiguous between two kinds, say which you picked and why in one sentence, then draw it.

    line 50
  4. Distinctive:

    Show only what you verified: If you could not work out whether an edge exists, leave it out rather than drawing a guess.

    line 72
  5. Very specific:

    Label with the code's own words: Use the real table, column, class and method names.

    line 120
  6. Distinctive:

    Do not reach for this on a diagram that already fits.

    line 180
  7. Distinctive:

    Do it only when the diagram is genuinely too wide to read.

    line 192
  8. Distinctive:

    When you do split:

    line 195
  9. Distinctive:

    Add a note only when something in the request or the material makes a specific thing worth singling out:

    line 275
  10. Distinctive:

    When you do add one, keep it to 2–4 words in the reader's own language.

    line 299
  11. Distinctive:

    Do not reach for styling to convey meaning instead.

    line 308
  12. Very specific:

    Only roles the diagram actually paints may appear — plus new, below — and at most four keys in all, or the spec is rejected: a colour in …

    line 343
  13. Distinctive:

    Keep the note for a box you have something specific to say about — "gains a revision column".

    line 371
  14. Distinctive:

    Every run without it opens the image, so three refinement passes leave the user with three windows to close.

    line 383
  15. Distinctive:

    Drop the note: if it was not one of the one or two things the reader most needs.

    line 449
  16. Distinctive:

    Shorten it: if it is worth keeping: a narrower box has more places it can go.

    line 451
  17. Distinctive:

    Draw fewer boxes: if two or three notes all report this — the diagram is too crowded to annotate, and thinning the picture is the real fix.

    line 452
  18. Distinctive:

    If you drew more than one diagram, say what question each one answers — and if you split one subject into parts, say that you split it and …

    line 464

“Common” is measured: kept by at least 6 in 10 of the 43 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Visualize

Turn a question about the codebase into one diagram that answers it.

The rendering is solved and gated — you do not need to think about colour, size, or layout. Your job is the two things a renderer cannot do: choose the right kind of diagram for the question, and find out what is actually true in the code. Everything else is lib/diagram.

Output is descriptive, not evaluative: a picture of how things are, not an opinion about how they should be. No findings, no recommendations.

Step 1 — Turn the request into a question, then pick the kind

Diagram kinds are not interchangeable, and the most common failure here is drawing a boxes- and-arrows picture for a question about order or state. Work out what the user actually wants to know, then read it off this table:

The reader's questionkind
What talks to what, and where does this live?architecture
What happens, in what order, across components?sequence
What does the data look like?er
How do these types relate?class
What states can this be in, and how does it move between them?state
How did we get from the old design to the new one?two diagrams, before and after

Read the user's words for the giveaways:

  • "when X happens", "the flow when", "what happens if" → sequence. Anything with an order in it. Boxes and arrows cannot express "first, then".
  • "layout", "schema", "tables", "columns" → er.
  • "lifecycle", "can it be in", "transitions", "retry", "states" → state.
  • "classes", "interfaces", "types", "who implements" → class.
  • "architecture", "overview", "how it fits together", "where does X run" → architecture.
  • "migration", "cutover", "before and after" → two diagrams of the same kind, one per state of the world, rendered together and opened in one viewer window. There is no animated kind: boards that replace each other on screen make the reader hold the previous one in their head, which is exactly the comparison they wanted to see side by side.

If the request genuinely spans two questions ("show me the schema and how a write flows through it"), draw two diagrams, each answering one. Do not merge them: a diagram that answers two questions answers neither clearly, and both will be too big to read.

If the request is ambiguous between two kinds, say which you picked and why in one sentence, then draw it. Do not stop to ask unless the two readings would send you exploring completely different parts of the codebase.

Step 2 — Explore, and get the facts right

This is the step with no shortcuts, and the one that decides whether the diagram is worth anything. A beautiful diagram of a structure that does not exist is worse than no diagram. Never infer structure from names alone — open the file.

Where to look, by kind:

kindread
ermigrations (the latest state, not the first), schema dumps, ORM model classes, CREATE TABLE. Prefer a migration directory's end state over a stale schema.sql.
sequencethe entry point (route, handler, controller, event listener) and then follow the calls. The order is the content, so trace it; do not guess it. Decide explicitly whether the failure path belongs — for "what happens when X" it usually does, and if you leave it out, say so.
classthe type/class declarations themselves, plus what they implement or extend. Method signatures, not bodies.
statethe enum or union of states, and every place it is assigned or transitioned. Look for the error and retry paths — they are the ones people forget, and usually the interesting part.
architecturedeployment manifests, service definitions, client config, and the calls that cross a process boundary.

Four rules that keep a diagram honest:

  • Show only what you verified. If you could not work out whether an edge exists, leave it out rather than drawing a guess. An absent edge is a gap; a wrong edge is a lie.
  • Never draw an edge because the tool "usually works that way". An edge comes from the config, not from your model of the system. The trap is that the invented edge is plausible, so nothing looks wrong — and it also hides the advice you would otherwise get, because the "consider splitting this" warning is about how connected the picture is. One speculative arrow can silence it.

    The case that produced this rule: a GitLab CI diagram drew lint → test → .post: on success, because stages do run in order — except every job in that repo declares needs: [], which opts out of stage ordering entirely and starts the job immediately. The config even said so in a comment. Two edges that the YAML contradicts, and four separate stage graphs merged into one 3000px-wide picture. Check for needs: before drawing stage order; a DAG pipeline's real edges are the needs, and the stages are only grouping.

  • Say what you read. Config and a live run answer different questions: a sampled pipeline shows what ran that time, so tag-only and conditional jobs are simply absent — one run of this skill lost an entire .pre stage that way, having read two live pipelines instead of the configuration. Either is a legitimate source. Which one you used is part of the answer, and belongs in your summary.
  • In a sequence, an arrow means "A calls B" — so if a component reacts to state changing somewhere else, draw the state. This is the failure that looks most like success: the interesting part of a flow is often "and now the app knows", nothing calls anything to make that happen, and the tempting fix is an arrow between two things that never speak. Put the medium on the canvas instead — the cache entry, the table row, the queue topic, the file, the flag — and both halves become real calls: whoever wrote it called the write, and the reactor calls the read. A participant with nothing but a self-call is the symptom of having skipped this.

    What is left over after that is a genuine push: the receiver never asked, and there is no medium to point at, because the sender simply sends. Mark it "push": true (dashed, open arrowhead) and label it with what triggered it — on peer join, on row change. Do not use it for an ordinary call you happen to be waiting on.

  • A lane is a role in the interaction, not necessarily one module. Drawing the medium adds participants, and the ≤7 message budget pushes back — when they collide, raise the altitude of the lanes before you drop an edge or split the diagram. Two collaborators that are one subsystem at the level the question is asked belong in one lane, and then the handoff between them is internal: there is no arrow to omit and nothing to invent. Put the real names in detail, a smaller second line, so the reader can still grep for something.

    Two things not to merge, because both turn the abstraction into a way of hiding what you did not check:

    • Never merge across a boundary the question is about. For "what happens when a user logs in", the internals of the routing layer are not the question but the API boundary is.
    • Never merge a medium with its reader or its writer. The medium earns its own lane precisely because two different parties touch it; fold it in and the reaction stops being a call and goes invisible again — which is the problem you were solving.
  • Label with the code's own words. Use the real table, column, class and method names. A reader has to be able to grep for what they see. One exception, for punctuation only: write an arrow inside a label as →, not ->. Everything around it is typeset, so ASCII reads as source code that escaped — and the single glyph is narrower. The renderer warns you.
  • Label every relationship. An unlabelled arrow says two things are connected and nothing about how, which is the least interesting half of the answer. In particular:
    • er edges carry a cardinality that names its entities, in the direction the arrow goes — n edits : 1 doc, 0..1 claim : 1 doc, n : m via email_documents. Not a bare ratio: n : 1 is technically correct and hard to read, because it makes the reader work out which end is which and then map it back to the table names. And not backwards: the table the arrow LEAVES belongs on the left of the colon, the one it points at on the right, so the label and the picture agree instead of having to be reconciled. Point the edge at the specific column (documents.owner_id -> users.id) so it is obvious which key it is.
    • state transitions carry their trigger — what causes the move.
    • class edges carry the relationship — owns, implements, emits (dashed for implements/uses, solid for ownership).

Step 3 — Write the spec, small

The spec is JSON. Full field reference: REFERENCE.md.

The output is a standalone image, so it has no width to fit into. A reader opens it and zooms; a nine-table schema at full size is fine. Do not split a diagram because it looks wide, and do not split one to satisfy a size warning — the warnings are advisory, and the standalone gates deliberately do not enforce a page width or a viewport height. The renderer leans into that width: a standalone er, class or state diagram is laid out wide, because it is opened full-screen on a landscape monitor. direction is not yours to set — a spec carrying one is rejected, because pinning it also switches off the escalation that keeps text readable.

Split only for an editorial reason: the diagram is answering two different questions and each deserves its own. "All the tables" is one question, even with nine tables in it.

Too much to draw: go up an altitude before you give up detail

"How do the backend services work together" on a real repo means 25 boxes and 38 labelled edges, and past roughly a dozen boxes that is not a diagram any more — the labels detach from the lines they belong to, and no direction, colour or compaction step recovers it. Correctness is not the problem: every edge can be right and the picture still unreadable.

But that question was asked about areas, not about individual classes. Collapse the containers and the same content is 11 boxes and 19 edges, on one screen, with every label on its own edge:

python3 .../visualize.py spec.json --overview                    # one box per area
python3 .../visualize.py spec.json --overview --drop api --drop tasks
python3 .../visualize.py spec.json                              # and the detailed one

--overview is mechanical, not a summary you have to write: each container becomes one box that lists its members underneath — most-connected first, then +N more — and the edges between areas are merged, keeping the verb when there is one and counting them when there are several.

  • Write the detailed spec anyway. The overview is derived from it, so you lose nothing by authoring the full thing and rendering it both ways. Offer the detail; lead with the overview.
  • --drop the entry point that touches everything. In that repo, API routers and Procrastinate tasks were half of all 38 edge ends and neither is a service — dropping them took the overview from 19 edges to 10, and "every service is reachable from the API; lifecycle work runs as tasks" is a sentence you say in your summary. A node adjacent to most areas is costing you the whole layout to tell the reader one thing.
  • Say which view they are looking at, and that the other exists.

Do not reach for this on a diagram that already fits. It is for the case where the honest answer to a broad question is genuinely too big for one picture.

The exception: several unrelated graphs in one answer

Sometimes one honest question has an answer made of pieces that do not touch — a CI pipeline whose stages share no dependencies, a service with four independent workers. The renderer tells you when this happens: "N disconnected groups of nodes". This is the one oversize you can always fix, because they are already separate pictures. A real pipeline came out 3433×693 — aspect 5:1, most of the canvas holding jobs with no edges at all — where the same content as four diagrams is 1405×543 at the largest.

Do it only when the diagram is genuinely too wide to read. Three groups in a small diagram is fine; leave it alone.

When you do split:

  • One diagram per group, and drop nothing. Correctness first — a split that loses a node or an edge is worse than a wide picture. Every node ends up in exactly one part.
  • Number and name every part in its title — pipeline 2/4 — test stage. The title is drawn into the image, so a part is not anonymous when it is the only one on screen.
  • Where the split cuts a link, leave a pointer on both sides. This is one of the few places a note is clearly earned: the reader would otherwise have no way to know the connection exists. Two to four words, naming the other part, with the direction in it — → then test stage on the upstream node, ← after lint stage on the downstream one. Cut as few links as possible: split between groups that are disjoint, or joined by a single edge.
  • A group with no edges at all is still a legitimate diagram — a stage's five independent jobs is a picture of that stage.

Say in your summary that you split it and why, and name the parts.

What is still worth keeping tight, because it is about legibility rather than fitting:

  • Only the columns and members the question is about. A table has 30 columns; the question is about 4. Show 4. This is the single biggest lever you have, and it is about signal, not size.
  • ≤6 states, ≤7 sequence messages. Both are editorial, not arithmetic: the renderer compacts a sequence diagram's rows for you, so a long one is not tall — it is just a program the reader has to execute in their head. A state machine past six states is usually two questions wearing one coat.
  • Short edge labels when the diagram also has callouts. They compete for the same whitespace, and D2 reserves no room for a callout, so a long edge label beside one is the likeliest thing to end up half-covered.

The renderer prints advisory warnings before it draws. Read them, but weigh them — for a standalone image, "wide" is not a problem.

Use the roles as a vocabulary

Pick a role for what a thing is, never for the colour you want:

client · svc (logic) · store (persistent) · cache (transient) · ext (outside our control) · neutral

A sequence colours by group, not by kind. Give each lane a group — browser and server, cli and daemon, producer / broker / consumer — and every lane in it shares one colour. A per-lane role turns seven lanes into six colours that each mean a different thing, none of which is what a reader of a flow wants to know: they want to see which side of the wire a lane is on, and where the crossing happens. Two or three groups is one colour per side, which is the same paint doing useful work. Groups take no legend — "these sit together and match" needs no key — and a lane may not carry both a group and a role, because then the colour would be claiming two things.

Order the lanes so a group's lanes sit together, usually. It is a preference, not a rule, and it competes with keeping arrows short: a flow where one lane talks to both sides cannot have both. Judge it per diagram — on a login flow, putting the server at the far edge is right even though it lengthens two arrows, because those two arrows are the network crossing and seeing them span the canvas is the point. The renderer warns about a split group so you notice it; ignoring the warning is a legitimate answer.

A state diagram uses a different set, for the same reason — a state is not a datastore: working (in progress) · steady (settled, the happy resting place) · transient (retrying, about to move) · stuck (somewhere it should not be, and not the end) · terminal (the end of the line) · neutral. The first three and terminal land on the traffic-light reading a reader already has, which is the one place colour carries meaning here without a legend. stuck is purple and lands on nothing — it says "a different kind of state from the ones around it" and leaves the label to say which, so give a stuck state a label that names the problem.

One role means one thing per diagram. Two states the reader must tell apart cannot share a role: a file-journey machine drew "deferred" (waiting out a grace window) and "imported, still there" (the delete failed) both transient, and the first reader's question was why two states that mean such different things were the same colour. If two states collide, one of them is mis-roled — that is what stuck exists for.

The point is consistency: if store means Postgres in one diagram, it must not mean "the important one" in the next. When you draw several diagrams for one request, reuse the roles identically across them — that consistency is most of what makes a set of figures feel like one system rather than several unrelated pictures.

Notes are the exception. Default to none

Most diagrams should carry no note at all. A callout points at something; if the user asked for a plain overview — "show me the DB layout" — there is nothing to point at, and one callout per table is noise that covers the edge labels underneath it.

Add a note only when something in the request or the material makes a specific thing worth singling out:

the requestnotes?
"show me the DB layout", "overview of the classes"none. The diagram is the answer.
"what changed in this MR"yes — on the changed things, sparingly. For a box that was simply ADDED, prefer new and a legend entry over a note; see below.
"where is the flaw in this schema", "what looks wrong here"yes — on the flaw
"why is this slow"yes — on the hot path or the missing index

A note must not describe what a box already is. That is the failure mode to watch for, and it is easy to slip into, because glossing each table feels helpful:

document_claims → "leased, expiring"no. A caption. It says what the table is for, which its own name and columns already say.
email_documents → "join table"no. Two foreign keys and no other columns already say that.
documents → "gains a revision column"yes. A change the reader cannot see.
sessions → "no index on user_id"yes. A flaw, and the answer to what was asked.

The test: would the reader miss or misread something without it? If the fact is intrinsic to the thing, it belongs in the label, or it is already visible in the columns. If it is a change, a flaw, or the specific answer to the question, it is a note.

When you do add one, keep it to 2–4 words in the reader's own language. Two or three across a whole diagram is plenty; annotating most of the boxes makes the annotations mean nothing.

Notes are also, by a distance, the most expensive thing you can put in a spec: D2 reserves no canvas for a callout, so the renderer finds a position for each by drawing the whole diagram again at every candidate position and measuring it in a browser. That is seconds per note on a large diagram, and a spec with no notes skips it entirely. The editorial reason above is the one that decides; the clock only points the same way.

Do not reach for styling to convey meaning instead. A thick border or an odd colour says "something here is special" without ever saying what.

The one exception: colour that carries a meaning of its own

role normally says what a thing IS — a store, a service, an external system — and a reader needs no help with that. Occasionally the honest way to answer the question is to repurpose it into a distinction of your own: green for the tables we can query today against salmon for the ones whose grant is still pending, and suddenly the picture says "the whole supplier chain works, the whole order chain is dark" at a glance.

When you do that, and only then, add a legend — a word or three per colour, which the renderer draws as a swatch and its words under the drawing:

"legend": {"store": "queryable today", "ext": "grant still pending"}

Four rules, because a legend is easy to reach for and mostly wrong:

  • No legend for the default meanings. "store = a database" restates the shape. If someone reading the picture would guess the colour right, the legend is noise.
  • A colour has to GROUP something. One colour on one box groups nothing — it is decoration with a lookup attached, and a legend row spends the reader's attention to tell them a fact the box could have carried in words. If most of your colours would each sit on a single box, the colouring is not earning its legend: use one role for every box and no legend at all. Fewer colours is the normal answer and a legend is the exception; the renderer says so on a spec that gets this wrong, and lib/diagram/examples.py's class figure is kept as the worked example of the mistake.
  • A legend is not a substitute for words on the thing. It explains a COLOUR that groups several boxes; a fact about one box is still a note.
  • Say it in your summary too. The legend makes the image stand on its own once it is pasted somewhere else, which the prose cannot do — but the reader in front of you is reading your message, so tell them there as well.

Only roles the diagram actually paints may appear — plus new, below — and at most four keys in all, or the spec is rejected: a colour in the legend that is not on the canvas sends a reader hunting for it.

The one legend entry that is not a role: new

A box the change ADDED can be marked with "new": true — an accent border, or an accent fill on a table — and the accent named once for the whole drawing:

"legend": {"new": "added by this change"}

Reach for this rather than a note whenever being new is the whole of what there is to say. It reads better, because one row of words covers however many boxes are marked instead of the same word repeated around the picture; and it is much cheaper, because every callout is positioned by measuring eight anchors in a real browser and this is positioned not at all.

Only where colour is free, though. The accent is one more colour meaning, so on a drawing that needed a role legend it lands beside meanings the reader is already holding — and the legend can explain only one of them. A class diagram shipped exactly that: four repurposed role colours, none explained, and the drawing's only legend row pointing at a fifth box. The question it left a reader with was not "what is the accent" but "then what do the others mean?". A spec that explains a role in its legend and also sets new is rejected for it.

So: colours self-evident → accent and one legend row. Colours carrying a meaning of your own → give them the legend they need, and mark the change with a note.

Keep the note for a box you have something specific to say about — "gains a revision column". Then the two do different jobs: the accent finds the box, the words say what happened to it. Putting both on one box, with a note that reads "new", says the same thing twice.

Step 4 — Render

While you are still iterating, always pass --no-open:

python3 ~/.claude/skills/visualize/scripts/visualize.py spec.json --no-open

Every run without it opens the image, so three refinement passes leave the user with three windows to close. Open exactly once, at the end, when you are satisfied:

python3 ~/.claude/skills/visualize/scripts/visualize.py spec.json

That writes a standalone SVG — colours baked (light by default, since the image is viewed inside a frame it cannot paint; --theme dark for the other), the background painted, and the CSS its callout text needs carried inside the file — places every callout by measuring the candidate positions in a real browser, runs the gates, and opens it. Full size, so the reader can zoom; there is no page and no width limit.

Expect a few seconds per callout, and more on a large diagram, since every candidate position is a redraw of the whole thing: the placement search is the cost, and it is why you never position anything by hand. A diagram with no notes skips it entirely and is fast.

Several diagrams: render them all with --no-open, then open them in ONE command.

open /tmp/…-part1.png /tmp/…-part2.png /tmp/…-part3.png     # macOS; xdg-open elsewhere

One open with several files gives a single viewer window with a thumbnail sidebar you can scroll through; one call per file gives you a window per file, stacked on top of each other. Measured: two files in one call is +1 window, two separate calls is +2. Pass the PNG paths — the second line each run prints — in the order the reader should see them.

The other modes are for feeding a page rather than a person:

python3 .../visualize.py spec.json --format embed  # themeable SVG, follows a page's toggle
python3 .../visualize.py --format css             # the CSS that page must then ship

--format embed is not a preview: its colours are var() references and its callout CSS lives in the page, so opened on its own it renders unpainted shapes and clipped labels.

Step 5 — If a gate fails

The script prints a table and exits non-zero. It still opens the page, so you can see what it is complaining about. Fix the spec, not the styling:

complaintwhat it meansfix
TINY … < 11pxsomething renders too small to readshorten labels; fewer columns
CLIPPEDa callout is cut off at the canvas edgeshorten its note text — a narrower callout fits where a wide one cannot
contrast … FAILa colour is unreadable on the baked backgroundyou should not be able to cause this from a spec; report it rather than working around it
no palette mappingD2 emitted a colour the theme cannot bakeditto — report it
GATE COULD NOT RUNthe clipping gate has no browsernot a pass. Say so; it is the only gate that can see a callout cut off

Note what is not in that table: nothing about width or height. A standalone image has no page to fit, so "too wide" and "too tall" are not failures and splitting to fix them is wrong.

One warning is not a failure and does not affect the exit code: the note '…' covers the corner where an arrow turns / the head of an arrow means no position for that note kept it off one of those, so the drawing shipped with the least bad one. The picture is fine to look at and the fix is yours. Do not go looking for a styling problem — there isn't one, and never position the note by hand: all eight positions were measured and this was the best of them.

It says this about a corner or a head and about nothing else, so it is not a coverage report: a note lying across the middle of a straight run says nothing, because line showing on both sides reads straight through. Measured, that is the difference between 22px in the wrong place and 89px that costs a reader nothing. What you are being told is that part of an arrow which cannot be guessed from the rest of the picture is behind a box, and the decision is yours:

  • Drop the note if it was not one of the one or two things the reader most needs. That is the usual answer, and the fastest.
  • Shorten it if it is worth keeping: a narrower box has more places it can go.
  • Draw fewer boxes if two or three notes all report this — the diagram is too crowded to annotate, and thinning the picture is the real fix.

Then render again, with --no-open. One retry, not a loop: if the second attempt still says it, keep that drawing and tell the user in your summary — a crowded diagram can have no clear position for a note at all, and shortening the text again will not conjure one.

Step 6 — Tell the user what they are looking at

One or two sentences: what the diagram shows, and the one thing worth noticing in it. If you left something out to keep it legible, say so — that is information, not an apology.

If you drew more than one diagram, say what question each one answers — and if you split one subject into parts, say that you split it and why.

Name the file that is actually on screen, which is the PNG: the run prints opened: <path> on stderr for exactly this reason. Reporting the SVG path while the viewer shows the PNG has already happened once, and it sends anyone who follows the path to a file their viewer may crop.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.