Skip to content

Repository files navigation

pi-diagram

CI

Diagram extension demo

An extension for the Pi and Oh My Pi terminal coding agents. It makes agents actively use visual explanations instead of defaulting to walls of text. The model writes compact declarative D2 source through one diagram tool. The extension renders that source as Unicode in the terminal and as reusable SVG and PNG output.

If any of those names are new to you:

  • D2 is a declarative diagram language. Text such as client -> gateway -> api compiles to a laid-out diagram, and the same language covers containers, sequence diagrams, and SQL tables.
  • Pi and Oh My Pi are terminal coding agents. An extension is an npm package they load at startup to add tools the model can call.

Why

Coding agents explain spatial things in prose: "A calls B, B queues C, C writes D, D emits an event back to A." The user rebuilds a graph in their head that the agent could have drawn. A fenced Mermaid block moves the problem rather than solving it, because a terminal shows the source instead of the picture.

The goal is to change how agents explain, not just add a renderer. When structure, flow, sequence, data, or relationships are easier to see than to read, the agent leads with a diagram and limits prose to details the picture cannot show.

D2 keeps generation token-efficient: the model describes nodes, edges, groups, and labels instead of spending tokens on layout or SVG coordinates. The tool owns the layout, theme, spacing, and fonts, so the same compact source produces consistent Unicode, SVG, and PNG output.

Requirements

  • Node.js 22 or newer
  • The D2 CLI, version 0.8.0 or newer, on PATH or named by D2_BIN
brew install d2                          # ships 0.8.1
go install github.com/d2lang/d2@v0.8.1   # or a pinned build from source

The prebuilt binaries on D2's GitHub releases page stop at 0.7.1, which draws SQL tables as empty boxes, so they are below the supported floor.

D2 is an external dependency on purpose. The extension never downloads a renderer during a tool call. Without it, the extension still loads and a call explains how to install it.

Install

Pi:

pi install npm:@mcuste/pi-diagram

Oh My Pi:

omp plugin install @mcuste/pi-diagram

Or through the Oh My Pi marketplace:

/marketplace add mcuste/pi-diagram
/marketplace install pi-diagram@pi-diagram

From a local checkout:

pnpm install
pi install /absolute/path/to/pi-diagram
omp plugin link /absolute/path/to/pi-diagram

What the tool does

One tool named diagram. Give it D2 source and it draws the diagram in the terminal:

edge: Edge { gateway }
core: Core { api; worker }
edge.gateway -> core.api
core.api -> core.worker: enqueue
┌───────────────────┐
│       Edge        │
│                   │
│    ┌──────────┐   │
│    │ gateway  │   │
│    └──────────┘   │
│          │        │
└──────────│────────┘
           │
 ┌─────────│─────────┐
 │       Core        │
 │         ▼         │
 │     ┌──────┐      │
 │     │ api  │      │
 │     └──────┘      │
 │         │         │
 │      enqueue      │
 │         ▼         │
 │    ┌─────────┐    │
 │    │ worker  │    │
 │    └─────────┘    │
 └───────────────────┘

Containers, sequence diagrams, SQL tables, class shapes, and state flows all work from the same language.

Field Purpose
source The diagram, in D2
title Label shown above the diagram
render auto prepares Unicode and PNG; image, unicode, and source choose a view
profile What the diagram is for. It sets the layout engine, theme, and spacing
formats Files to produce: source, svg, png, txt. Written outside the repository
save Also copy them into the repository. dir is required

When the model draws

A tool description alone gets called when the user asks for a diagram, not whenever a diagram is the clearer answer. To make visual explanation active, the extension adds a short block to the host system prompt: draw when structure, flow, or relationships are easier to see than to read, call the tool before explaining, keep the prose to what the picture does not show, and never hand-write ASCII art or Mermaid. The block tells the model to check C4, sequence, class, data, dependency, or tree before drawing a generic flow. The tool description contains the detailed profile and shape rules.

The result is fewer walls of text: an answer about how parts connect arrives as a picture with a few lines around it. The block is appended to whatever the host built, is added once, and is left out when the diagram tool is not active.

What a profile changes

The model says what a diagram is for, and the tool decides how it looks:

Profile For Drawn as
explain States, decisions, transformations, or process flow inside an answer Hand drawn, neutral theme, small margins
architecture Runtime services and infrastructure without C4 levels Neutral theme, more room between rows so edges stay separable
data Stored records and relations, or types and public APIs Neutral theme, tight spacing: tables and classes are tall already
docs A saved diagram when no more specific profile fits Grey theme that prints in greyscale, page-sized margins
tree Folders, call trees, or other parent-child structures Dagre, which fans children out under their parent
c4 System scope, users, external systems, runnable units, or module responsibilities Architecture spacing under the C4 palette
dependency Imports, packages, build order, coupling, impact, or cycles The tightest spacing, cutting the room edges take past nodes

explain is the default and is drawn by hand: an answer in a conversation is a rough model, and a crisp diagram claims more precision than it has. Every other profile is crisp.

Every profile also sets a dark theme, so a saved SVG adapts to dark mode.

Only the picture changes. D2 draws text in character cells, so every profile gives the same box drawing.

Unicode and PNG views

Every call prepares Unicode, SVG, and PNG from the same D2 source.

Pi shows Unicode in its live tool row. Press Ctrl+O to replace it with the PNG and press it again to return to Unicode. If the terminal or harness has no image protocol, expansion keeps the Unicode view and reports This terminal cannot display inline images. A multiplexer between the terminal and agent must forward the protocol. tmux needs allow-passthrough, and herdr needs experimental.kitty_graphics.

OMP keeps complete Unicode in the chronological tool result because settled transcript rows are immutable. Ctrl+O opens the latest diagram's PNG in a viewport-fitted fullscreen overlay. Press Ctrl+O or Esc to close it and return to the transcript. The Open PNG link remains available independently.

OMP owns the shared terminal image budget. Closing an overlay does not purge unrelated terminal images; the host evicts cached graphics under its normal budget policy.

Per-call overrides remain available when the user requests another method:

  • render: "image" shows a compact inline PNG. Pi can zoom it with Ctrl+O.
  • render: "unicode" keeps Unicode in both Pi views.
  • render: "source" shows the D2 source.

The PNG never enters the model's context. It is written to a private temporary directory and read only by the display renderer.

Where the terminal supports OSC 8 hyperlinks, OMP shows the linked PNG file name below the Unicode tool result. Pi links the title above the PNG, or the file name below an untitled PNG. Open that link to pan or zoom beyond the terminal view. Ghostty, Kitty, WezTerm, and iTerm2 support these links, some of them on a modified click.

D2 exports PNG by driving a headless browser it downloads on first use, which this tool will not do during a call. Instead the SVG is rasterized locally by resvg, which needs no browser and no network. Labels use the fonts in the SVG. Characters those fonts do not cover, such as CJK, fall back to fonts on the machine and produce a note.

What the model gets back

In a terminal this extension draws the row, so the model reads one line: Drew "Request path". It is on the user's screen, so it is not repeated here. Saved paths and generation notes come with it, so a conversation full of diagrams costs about what a conversation of text costs. Print, RPC, and JSON modes have no row to draw, so there the Unicode diagram is the result text.

Expanding a result replaces Unicode with PNG where supported and adds the display mode, profile, D2 version, file paths, diagnostics, and source.

Where files go

Most diagrams explain something in passing and should leave nothing behind. So nothing is written unless it is asked for, and the repository is never the default:

Call Files Approval
{ source } none, the diagram is only in the transcript read
{ source, formats: ["svg"] } an SVG in a private temp directory, path returned read
{ source, title, save: { dir: "docs/diagrams" } } editable D2 plus an embeddable SVG write, prompts with the exact files

save.dir has no default. There is no directory convention that holds across repositories, so the destination has to be named. Only pass save when the user asked to keep the diagram. A saved .d2 is put through d2 fmt first, because people edit it later.

A saved pair is editable source plus a viewable rendering:

docs/diagrams/request-lifecycle.d2
docs/diagrams/request-lifecycle.svg

Markdown then needs no D2 or Mermaid runtime:

![Request lifecycle](diagrams/request-lifecycle.svg)

For documentation prefer svg: it scales, and Markdown renders it anywhere. png exists for places where SVG support is weak.

Repository paths stay inside the workspace. Absolute paths, .., and symlinks pointing outside are all refused.

What D2 drew is also kept in a temporary store, so the same diagram drawn again is about ten times faster, including in a later session. A new D2 version, or any change to a profile, is a different entry. The store holds 64 MB for a week.

Layout engine, theme, padding, and font are deliberately not in the schema. They are policy here, and a model given those knobs spends tokens on styling and produces a different look every call.

What is deliberately missing

  • Model-drawn images. The model never emits SVG coordinates or ASCII art directly. It writes source, and a renderer lays it out.
  • Imports and external assets. Diagram source cannot pull in files, local images, or remote icons. Those turn a rendering request into file and network access.
  • Interactive editing. The tool renders; it is not a diagram editor in the terminal.
  • Every D2 feature. A safe subset is supported, and anything outside it is reported as an error the model can correct rather than being silently dropped.

Documentation

License

MIT

About

Makes Pi and Oh My Pi agents actively explain complex topics with token-efficient D2 diagrams

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages