@diagrammo/dgmo 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/dgmo.md +294 -0
- package/AGENTS.md +148 -0
- package/dist/cli.cjs +338 -163
- package/dist/index.cjs +1080 -319
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -4
- package/dist/index.d.ts +28 -4
- package/dist/index.js +1078 -319
- package/dist/index.js.map +1 -1
- package/docs/ai-integration.md +33 -50
- package/package.json +8 -5
- package/src/c4/layout.ts +68 -10
- package/src/c4/parser.ts +0 -16
- package/src/c4/renderer.ts +1 -5
- package/src/class/layout.ts +0 -1
- package/src/class/parser.ts +28 -0
- package/src/class/renderer.ts +5 -26
- package/src/cli.ts +673 -2
- package/src/completion.ts +58 -0
- package/src/d3.ts +58 -106
- package/src/dgmo-router.ts +0 -57
- package/src/echarts.ts +96 -55
- package/src/er/classify.ts +206 -0
- package/src/er/layout.ts +259 -94
- package/src/er/parser.ts +30 -1
- package/src/er/renderer.ts +231 -18
- package/src/graph/flowchart-parser.ts +27 -4
- package/src/graph/flowchart-renderer.ts +1 -2
- package/src/graph/state-parser.ts +0 -1
- package/src/graph/state-renderer.ts +1 -3
- package/src/index.ts +10 -0
- package/src/infra/compute.ts +0 -7
- package/src/infra/layout.ts +60 -15
- package/src/infra/parser.ts +46 -4
- package/src/infra/renderer.ts +376 -47
- package/src/initiative-status/layout.ts +46 -30
- package/src/initiative-status/renderer.ts +5 -25
- package/src/kanban/parser.ts +0 -2
- package/src/org/layout.ts +0 -4
- package/src/org/renderer.ts +7 -28
- package/src/sequence/parser.ts +14 -11
- package/src/sequence/renderer.ts +0 -2
- package/src/sequence/tag-resolution.ts +0 -1
- package/src/sitemap/layout.ts +1 -14
- package/src/sitemap/parser.ts +1 -2
- package/src/sitemap/renderer.ts +0 -3
- package/src/utils/arrows.ts +7 -7
- package/src/utils/export-container.ts +40 -0
- package/.claude/skills/dgmo-chart/SKILL.md +0 -141
- package/.claude/skills/dgmo-flowchart/SKILL.md +0 -61
- package/.claude/skills/dgmo-generate/SKILL.md +0 -59
- package/.claude/skills/dgmo-sequence/SKILL.md +0 -104
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
import {
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { resolve, join, basename, extname } from 'node:path';
|
|
5
|
+
import { createInterface } from 'node:readline';
|
|
4
6
|
import { Resvg } from '@resvg/resvg-js';
|
|
5
7
|
import { render } from './render';
|
|
6
8
|
import { parseDgmo, getAllChartTypes } from './dgmo-router';
|
|
@@ -57,6 +59,441 @@ const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
|
57
59
|
infra: 'Infra chart — infrastructure traffic flow with rps computation',
|
|
58
60
|
};
|
|
59
61
|
|
|
62
|
+
const CLAUDE_SKILL_CONTENT = `# dgmo — Diagrammo Diagram Assistant
|
|
63
|
+
|
|
64
|
+
You are helping the user author, render, and share diagrams using the \`dgmo\` CLI and \`.dgmo\` file format.
|
|
65
|
+
|
|
66
|
+
## What is dgmo?
|
|
67
|
+
|
|
68
|
+
\`dgmo\` is a CLI tool and library that renders \`.dgmo\` diagram files to PNG, SVG, or shareable URLs. Diagrams are written in a plain-text DSL.
|
|
69
|
+
|
|
70
|
+
## Setup Check — Run This First
|
|
71
|
+
|
|
72
|
+
**Before doing anything else**, check whether the MCP tools are available in this session by attempting to call \`mcp__dgmo__list_chart_types\`. If that tool exists and succeeds, skip this section entirely.
|
|
73
|
+
|
|
74
|
+
If the MCP tools are **not** available, run the setup flow below — do not ask the user, just do it:
|
|
75
|
+
|
|
76
|
+
### Step 1 — Install the CLI (if missing)
|
|
77
|
+
|
|
78
|
+
\`\`\`bash
|
|
79
|
+
which dgmo || npm install -g @diagrammo/dgmo
|
|
80
|
+
\`\`\`
|
|
81
|
+
|
|
82
|
+
### Step 2 — Install the MCP server (if missing)
|
|
83
|
+
|
|
84
|
+
\`\`\`bash
|
|
85
|
+
which dgmo-mcp || npm install -g @diagrammo/dgmo-mcp
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
### Step 3 — Configure the MCP server
|
|
89
|
+
|
|
90
|
+
Ask the user:
|
|
91
|
+
|
|
92
|
+
> "Where should I configure the MCP server?
|
|
93
|
+
> 1) This project only — write \`.mcp.json\` here [default]
|
|
94
|
+
> 2) Globally — add to \`~/.claude/settings.json\` (works in all projects)"
|
|
95
|
+
|
|
96
|
+
**Option 1 (default):** Create or update \`.mcp.json\` in the current working directory:
|
|
97
|
+
|
|
98
|
+
\`\`\`json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"dgmo": {
|
|
102
|
+
"command": "dgmo-mcp"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
If \`.mcp.json\` already exists and has other servers, merge the \`dgmo\` entry in — do not overwrite the file.
|
|
109
|
+
|
|
110
|
+
**Option 2 (global):** Add the \`dgmo\` entry to the \`mcpServers\` object in \`~/.claude/settings.json\`. Read the file first and merge — do not overwrite other keys.
|
|
111
|
+
|
|
112
|
+
### Step 4 — Prompt restart
|
|
113
|
+
|
|
114
|
+
Tell the user:
|
|
115
|
+
|
|
116
|
+
> "Done. **Restart Claude Code** to activate the MCP server — diagram preview and rendering will be available in the next session."
|
|
117
|
+
|
|
118
|
+
Then proceed with the user's original request using CLI fallback (see "Other output options" below).
|
|
119
|
+
|
|
120
|
+
> **Note for future users:** To set up in one step from the terminal before starting a Claude Code session, run \`dgmo --install-claude-code-integration\`. It handles everything: installs \`@diagrammo/dgmo-mcp\`, writes the skill, and configures the MCP server.
|
|
121
|
+
|
|
122
|
+
## Getting Syntax Help
|
|
123
|
+
|
|
124
|
+
**Always use the MCP tool first** if it's available in this session:
|
|
125
|
+
|
|
126
|
+
\`\`\`
|
|
127
|
+
mcp__dgmo__get_language_reference // full reference
|
|
128
|
+
mcp__dgmo__get_language_reference("sequence") // specific chart type
|
|
129
|
+
\`\`\`
|
|
130
|
+
|
|
131
|
+
This is the authoritative, always-up-to-date syntax reference. Use it before guessing syntax.
|
|
132
|
+
|
|
133
|
+
## Your Workflow
|
|
134
|
+
|
|
135
|
+
When the user asks you to create or edit a diagram:
|
|
136
|
+
|
|
137
|
+
1. **Get syntax** — call \`mcp__dgmo__get_language_reference("<type>")\` if you're unsure of the syntax.
|
|
138
|
+
2. **Write the \`.dgmo\` content** — compose the markup.
|
|
139
|
+
3. **Save the source file** (if working in a project) — write it to \`<name>.dgmo\` so the user has an editable file.
|
|
140
|
+
4. **Render and show** — pick the right output based on what the user wants (see below).
|
|
141
|
+
|
|
142
|
+
### Output options — always offer these proactively after creating a diagram
|
|
143
|
+
|
|
144
|
+
| What the user wants | How to do it |
|
|
145
|
+
|---|---|
|
|
146
|
+
| **Quick look in the desktop app** | \`mcp__dgmo__open_in_app(dgmo)\` — opens directly in Diagrammo (macOS) |
|
|
147
|
+
| **Browser preview with theme toggle** | \`mcp__dgmo__preview_diagram([{dgmo, title}])\` — opens HTML in browser |
|
|
148
|
+
| **View in macOS Preview (or default image viewer)** | \`mcp__dgmo__render_diagram(dgmo, format:"png")\` → get temp path → \`open <path>\` |
|
|
149
|
+
| **View SVG in browser** | \`mcp__dgmo__render_diagram(dgmo, format:"svg")\` → write SVG to a temp \`.svg\` file → \`open <path>\` |
|
|
150
|
+
| **Save as PNG** | \`mcp__dgmo__render_diagram(dgmo, format:"png")\` → returns temp path; offer to copy to their preferred location. Or CLI: \`dgmo file.dgmo -o out.png\` |
|
|
151
|
+
| **Save as SVG** | \`mcp__dgmo__render_diagram(dgmo, format:"svg")\` returns SVG text — write it to the desired path. Or CLI: \`dgmo file.dgmo -o out.svg\` |
|
|
152
|
+
| **Shareable URL** | \`mcp__dgmo__share_diagram(dgmo)\` or CLI: \`dgmo file.dgmo -o url --copy\` |
|
|
153
|
+
|
|
154
|
+
**After creating a diagram, always present these options to the user** — don't just render silently and stop. A good response ends with something like: *"I've saved the file as \`diagram.dgmo\`. Want me to open it in the app, export it as a PNG, or generate a shareable link?"*
|
|
155
|
+
|
|
156
|
+
## CLI Reference
|
|
157
|
+
|
|
158
|
+
\`\`\`
|
|
159
|
+
dgmo <input.dgmo> [options]
|
|
160
|
+
cat input.dgmo | dgmo [options]
|
|
161
|
+
\`\`\`
|
|
162
|
+
|
|
163
|
+
Key options:
|
|
164
|
+
- \`-o <file>\` — output file; format inferred from extension (\`.svg\` → SVG, else PNG)
|
|
165
|
+
- \`-o url\` — output a shareable diagrammo.app URL
|
|
166
|
+
- \`--theme <theme>\` — \`light\` (default), \`dark\`, \`transparent\`
|
|
167
|
+
- \`--palette <name>\` — \`nord\` (default), \`solarized\`, \`catppuccin\`, \`rose-pine\`, \`gruvbox\`, \`tokyo-night\`, \`one-dark\`, \`bold\`
|
|
168
|
+
- \`--copy\` — copy the URL to clipboard (use with \`-o url\`)
|
|
169
|
+
- \`--no-branding\` — omit diagrammo.app branding from exports
|
|
170
|
+
- \`--chart-types\` — list all supported chart types
|
|
171
|
+
|
|
172
|
+
## Supported Chart Types
|
|
173
|
+
|
|
174
|
+
| Type | Use case |
|
|
175
|
+
|------|----------|
|
|
176
|
+
| \`bar\` | Categorical comparisons |
|
|
177
|
+
| \`line\` / \`multi-line\` / \`area\` | Trends over time |
|
|
178
|
+
| \`pie\` / \`doughnut\` | Part-to-whole |
|
|
179
|
+
| \`radar\` / \`polar-area\` | Multi-dimensional metrics |
|
|
180
|
+
| \`bar-stacked\` | Multi-series categorical |
|
|
181
|
+
| \`scatter\` | 2D data points or bubble chart |
|
|
182
|
+
| \`sankey\` | Flow / allocation |
|
|
183
|
+
| \`chord\` | Circular flow relationships |
|
|
184
|
+
| \`function\` | Mathematical expressions |
|
|
185
|
+
| \`heatmap\` | Matrix intensity |
|
|
186
|
+
| \`funnel\` | Conversion pipeline |
|
|
187
|
+
| \`slope\` | Change between two periods |
|
|
188
|
+
| \`wordcloud\` | Term frequency |
|
|
189
|
+
| \`arc\` | Network relationships |
|
|
190
|
+
| \`timeline\` | Events, eras, date ranges |
|
|
191
|
+
| \`venn\` | Set overlaps |
|
|
192
|
+
| \`quadrant\` | 2x2 positioning matrix |
|
|
193
|
+
| \`sequence\` | Message / interaction flows |
|
|
194
|
+
| \`flowchart\` | Decision trees, process flows |
|
|
195
|
+
| \`state\` | State machine / lifecycle |
|
|
196
|
+
| \`class\` | UML class hierarchies |
|
|
197
|
+
| \`er\` | Database schemas |
|
|
198
|
+
| \`org\` | Hierarchical tree structures |
|
|
199
|
+
| \`kanban\` | Task / workflow columns |
|
|
200
|
+
| \`c4\` | System architecture (context → container → component → deployment) |
|
|
201
|
+
| \`initiative-status\` | Project roadmap with dependency tracking |
|
|
202
|
+
| \`sitemap\` | Website / app navigation structure |
|
|
203
|
+
| \`infra\` | Infrastructure traffic flow with rps computation |
|
|
204
|
+
|
|
205
|
+
## Key Syntax Patterns
|
|
206
|
+
|
|
207
|
+
### Common to all diagrams
|
|
208
|
+
|
|
209
|
+
\`\`\`
|
|
210
|
+
chart: sequence // explicit type (optional — auto-detected)
|
|
211
|
+
title: My Diagram
|
|
212
|
+
palette: catppuccin // override palette
|
|
213
|
+
|
|
214
|
+
// This is a comment (only // syntax — not #)
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
Inline colors on most elements: append \`(colorname)\` — e.g. \`North(red): 850\`, \`[Process(blue)]\`.
|
|
218
|
+
Named colors: \`red\`, \`orange\`, \`yellow\`, \`green\`, \`blue\`, \`purple\`, \`teal\`, \`cyan\`, \`gray\`.
|
|
219
|
+
|
|
220
|
+
### sequence (most commonly used)
|
|
221
|
+
|
|
222
|
+
\`\`\`
|
|
223
|
+
chart: sequence
|
|
224
|
+
title: Auth Flow
|
|
225
|
+
|
|
226
|
+
// Participants auto-inferred, or declare explicitly:
|
|
227
|
+
User is an actor
|
|
228
|
+
API is a service
|
|
229
|
+
DB is a database
|
|
230
|
+
|
|
231
|
+
User -Login-> API
|
|
232
|
+
API -Find user-> DB
|
|
233
|
+
DB -user record-> API
|
|
234
|
+
|
|
235
|
+
if credentials valid
|
|
236
|
+
API -200 OK + token-> User
|
|
237
|
+
else
|
|
238
|
+
API -401 Unauthorized-> User
|
|
239
|
+
|
|
240
|
+
== Logout ==
|
|
241
|
+
|
|
242
|
+
User -Logout-> API
|
|
243
|
+
API -Delete session-> DB
|
|
244
|
+
\`\`\`
|
|
245
|
+
|
|
246
|
+
- Sync: \`A -label-> B\` · Async: \`A ~label~> B\` · Unlabeled: \`A -> B\`
|
|
247
|
+
- Blocks: \`if\` / \`else\`, \`loop\`, \`parallel\` — closed by indentation (no \`end\` keyword)
|
|
248
|
+
- Notes: \`note on API: text\` or \`note: text\`
|
|
249
|
+
- Sections: \`== Title ==\`
|
|
250
|
+
- Groups: \`[Group Name]\` with indented participants
|
|
251
|
+
|
|
252
|
+
### flowchart
|
|
253
|
+
|
|
254
|
+
\`\`\`
|
|
255
|
+
(Start) -> <Valid Input?>
|
|
256
|
+
-yes-> [Process Data] -> (Done)
|
|
257
|
+
-no-> /Get Input/ -> <Valid Input?>
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
Shapes: \`(oval)\` \`[rect]\` \`<diamond>\` \`/parallelogram/\` \`[[subroutine]]\` \`[document~]\`
|
|
261
|
+
|
|
262
|
+
### bar / line / pie (data charts)
|
|
263
|
+
|
|
264
|
+
\`\`\`
|
|
265
|
+
// bar
|
|
266
|
+
title: Revenue by Region
|
|
267
|
+
series: Revenue
|
|
268
|
+
North: 850
|
|
269
|
+
South: 620
|
|
270
|
+
|
|
271
|
+
// line (multi-series)
|
|
272
|
+
series: Sales(red), Costs(blue)
|
|
273
|
+
Q1: 100, 50
|
|
274
|
+
Q2: 120, 55
|
|
275
|
+
|
|
276
|
+
// pie
|
|
277
|
+
chart: pie
|
|
278
|
+
labels: percent
|
|
279
|
+
Company A: 40
|
|
280
|
+
Company B: 35
|
|
281
|
+
\`\`\`
|
|
282
|
+
|
|
283
|
+
### er
|
|
284
|
+
|
|
285
|
+
\`\`\`
|
|
286
|
+
users
|
|
287
|
+
id: int [pk]
|
|
288
|
+
email: varchar [unique]
|
|
289
|
+
1-writes-* posts
|
|
290
|
+
|
|
291
|
+
posts
|
|
292
|
+
id: int [pk]
|
|
293
|
+
author_id: int [fk]
|
|
294
|
+
\`\`\`
|
|
295
|
+
|
|
296
|
+
### org
|
|
297
|
+
|
|
298
|
+
\`\`\`
|
|
299
|
+
CEO
|
|
300
|
+
VP Engineering
|
|
301
|
+
[Platform Team]
|
|
302
|
+
Lead
|
|
303
|
+
Dev 1
|
|
304
|
+
Dev 2
|
|
305
|
+
VP Marketing
|
|
306
|
+
\`\`\`
|
|
307
|
+
|
|
308
|
+
### infra
|
|
309
|
+
|
|
310
|
+
\`\`\`
|
|
311
|
+
chart: infra
|
|
312
|
+
edge
|
|
313
|
+
rps: 10000
|
|
314
|
+
-> CDN
|
|
315
|
+
|
|
316
|
+
CDN
|
|
317
|
+
cache-hit: 80%
|
|
318
|
+
-> API
|
|
319
|
+
|
|
320
|
+
API
|
|
321
|
+
instances: 3
|
|
322
|
+
max-rps: 500
|
|
323
|
+
latency-ms: 45
|
|
324
|
+
\`\`\`
|
|
325
|
+
|
|
326
|
+
## Anti-Patterns
|
|
327
|
+
|
|
328
|
+
\`\`\`
|
|
329
|
+
# comment ❌ use // comment
|
|
330
|
+
async A -> B: msg ❌ use A ~msg~> B
|
|
331
|
+
A <- B ❌ left-pointing arrows removed — use B -> A
|
|
332
|
+
parallel else ❌ not supported — use separate parallel blocks
|
|
333
|
+
== Foo(#ff0000) == ❌ hex colors not supported — use named colors: == Foo(red) ==
|
|
334
|
+
A -routes to /api-> B ❌ -> inside a label is ambiguous — rephrase the label
|
|
335
|
+
end ❌ not needed — indentation closes blocks in sequence diagrams
|
|
336
|
+
\`\`\`
|
|
337
|
+
|
|
338
|
+
## Tips
|
|
339
|
+
|
|
340
|
+
- Default theme: \`light\`, default palette: \`nord\` — ask the user their preference before a final export.
|
|
341
|
+
- Stdin mode for quick renders: \`echo "..." | dgmo -o out.png\`
|
|
342
|
+
- For C4, \`--c4-level\` drills from context → containers → components → deployment.
|
|
343
|
+
- When auto-detection picks the wrong chart type, add an explicit \`chart:\` directive.
|
|
344
|
+
- \`mcp__dgmo__preview_diagram\` accepts multiple diagrams at once — useful for showing variants side by side.
|
|
345
|
+
`;
|
|
346
|
+
|
|
347
|
+
const CODEX_AGENTS_CONTENT = `# DGMO Diagram Language — Codex Integration
|
|
348
|
+
|
|
349
|
+
Use dgmo tools to create, render, and share diagrams. dgmo is a text-based diagram markup language that renders to SVG/PNG.
|
|
350
|
+
|
|
351
|
+
## Quick setup
|
|
352
|
+
|
|
353
|
+
If the MCP server is not yet configured:
|
|
354
|
+
|
|
355
|
+
\`\`\`bash
|
|
356
|
+
dgmo --install-codex-integration
|
|
357
|
+
\`\`\`
|
|
358
|
+
|
|
359
|
+
This installs the MCP server and writes the dgmo config to \`.codex/config.toml\`. Restart Codex to activate.
|
|
360
|
+
|
|
361
|
+
## MCP Tools
|
|
362
|
+
|
|
363
|
+
When the \`dgmo\` MCP server is configured, use these tools directly:
|
|
364
|
+
- \`preview_diagram\` — renders diagram(s) and opens a live HTML preview in the browser (default for showing diagrams)
|
|
365
|
+
- \`render_diagram\` — renders to PNG or SVG, returns file path
|
|
366
|
+
- \`share_diagram\` — creates a shareable diagrammo.app URL
|
|
367
|
+
- \`open_in_app\` — opens diagram in Diagrammo desktop app (macOS)
|
|
368
|
+
- \`list_chart_types\` — lists all 32 supported chart types with descriptions
|
|
369
|
+
- \`get_language_reference\` — fetches full syntax for any chart type (call this before generating an unfamiliar chart type)
|
|
370
|
+
- \`generate_report\` — renders multiple diagrams into an HTML report with table of contents
|
|
371
|
+
|
|
372
|
+
## When to use dgmo
|
|
373
|
+
|
|
374
|
+
- Architecture diagrams, sequence diagrams, flowcharts
|
|
375
|
+
- Data charts (bar, line, pie, scatter, heatmap, etc.)
|
|
376
|
+
- ER diagrams, class diagrams, org charts
|
|
377
|
+
- Project roadmaps, kanban boards, timelines
|
|
378
|
+
|
|
379
|
+
## Quick syntax reference
|
|
380
|
+
|
|
381
|
+
### Sequence diagram
|
|
382
|
+
\`\`\`
|
|
383
|
+
chart: sequence
|
|
384
|
+
title: Auth Flow
|
|
385
|
+
|
|
386
|
+
User -Login-> API
|
|
387
|
+
API -Find user-> DB
|
|
388
|
+
DB -user-> API
|
|
389
|
+
if valid
|
|
390
|
+
API -200 OK-> User
|
|
391
|
+
else
|
|
392
|
+
API -401-> User
|
|
393
|
+
\`\`\`
|
|
394
|
+
|
|
395
|
+
### Flowchart
|
|
396
|
+
\`\`\`
|
|
397
|
+
chart: flowchart
|
|
398
|
+
title: Process
|
|
399
|
+
|
|
400
|
+
(Start) -> <Valid?>
|
|
401
|
+
-yes-> [Process] -> (Done)
|
|
402
|
+
-no-> /Get Input/ -> <Valid?>
|
|
403
|
+
\`\`\`
|
|
404
|
+
|
|
405
|
+
### Bar chart
|
|
406
|
+
\`\`\`
|
|
407
|
+
chart: bar
|
|
408
|
+
title: Revenue
|
|
409
|
+
series: USD
|
|
410
|
+
|
|
411
|
+
North: 850
|
|
412
|
+
South: 620
|
|
413
|
+
East: 1100
|
|
414
|
+
\`\`\`
|
|
415
|
+
|
|
416
|
+
### ER diagram
|
|
417
|
+
\`\`\`
|
|
418
|
+
chart: er
|
|
419
|
+
title: Schema
|
|
420
|
+
|
|
421
|
+
users
|
|
422
|
+
id: int [pk]
|
|
423
|
+
email: varchar [unique]
|
|
424
|
+
|
|
425
|
+
posts
|
|
426
|
+
id: int [pk]
|
|
427
|
+
user_id: int [fk]
|
|
428
|
+
|
|
429
|
+
users 1--* posts : writes
|
|
430
|
+
\`\`\`
|
|
431
|
+
|
|
432
|
+
### Org chart
|
|
433
|
+
\`\`\`
|
|
434
|
+
chart: org
|
|
435
|
+
|
|
436
|
+
CEO
|
|
437
|
+
VP Engineering
|
|
438
|
+
Team Lead A
|
|
439
|
+
Team Lead B
|
|
440
|
+
VP Marketing
|
|
441
|
+
\`\`\`
|
|
442
|
+
|
|
443
|
+
### Infra chart
|
|
444
|
+
\`\`\`
|
|
445
|
+
chart: infra
|
|
446
|
+
direction: LR
|
|
447
|
+
|
|
448
|
+
edge
|
|
449
|
+
rps: 10000
|
|
450
|
+
-> CDN
|
|
451
|
+
|
|
452
|
+
CDN
|
|
453
|
+
cache-hit: 80%
|
|
454
|
+
-> LB
|
|
455
|
+
|
|
456
|
+
LB
|
|
457
|
+
-> API | split: 70%
|
|
458
|
+
-> Web | split: 30%
|
|
459
|
+
|
|
460
|
+
API
|
|
461
|
+
instances: 3
|
|
462
|
+
max-rps: 500
|
|
463
|
+
latency-ms: 45
|
|
464
|
+
\`\`\`
|
|
465
|
+
|
|
466
|
+
## All 32 chart types
|
|
467
|
+
|
|
468
|
+
bar, line, multi-line, area, pie, doughnut, radar, polar-area, bar-stacked, scatter, sankey, chord, function, heatmap, funnel, slope, wordcloud, arc, timeline, venn, quadrant, sequence, flowchart, state, class, er, org, kanban, c4, initiative-status, sitemap, infra
|
|
469
|
+
|
|
470
|
+
## Common patterns
|
|
471
|
+
|
|
472
|
+
- \`chart: type\` — explicit chart type (auto-detected if unambiguous)
|
|
473
|
+
- \`title: text\` — diagram title
|
|
474
|
+
- \`// comment\` — only \`//\` comments (not \`#\`)
|
|
475
|
+
- \`(colorname)\` — inline colors: \`Label(red): 100\`
|
|
476
|
+
- \`series: A(red), B(blue)\` — multi-series with colors
|
|
477
|
+
|
|
478
|
+
## Rendering via CLI
|
|
479
|
+
|
|
480
|
+
\`\`\`bash
|
|
481
|
+
dgmo file.dgmo -o output.svg # SVG
|
|
482
|
+
dgmo file.dgmo -o url # shareable link
|
|
483
|
+
dgmo file.dgmo --json # structured JSON output
|
|
484
|
+
\`\`\`
|
|
485
|
+
|
|
486
|
+
## Mistakes to avoid
|
|
487
|
+
|
|
488
|
+
- Don't use \`#\` for comments — use \`//\`
|
|
489
|
+
- Don't use \`end\` to close sequence blocks — indentation closes them
|
|
490
|
+
- Don't use hex colors in section headers — use named colors
|
|
491
|
+
- Don't forget \`chart:\` directive when content is ambiguous
|
|
492
|
+
- Sequence arrows: \`->\` (sync), \`~>\` (async) — always left-to-right
|
|
493
|
+
|
|
494
|
+
Full reference: call \`get_language_reference\` MCP tool or visit diagrammo.app/docs
|
|
495
|
+
`;
|
|
496
|
+
|
|
60
497
|
function printHelp(): void {
|
|
61
498
|
console.log(`Usage: dgmo <input> [options]
|
|
62
499
|
cat input.dgmo | dgmo [options]
|
|
@@ -78,6 +515,14 @@ Options:
|
|
|
78
515
|
--copy Copy URL to clipboard (only with -o url)
|
|
79
516
|
--json Output structured JSON to stdout
|
|
80
517
|
--chart-types List all supported chart types
|
|
518
|
+
--install-claude-code-integration
|
|
519
|
+
Full Claude Code setup: install the /dgmo skill and configure
|
|
520
|
+
the dgmo MCP server — installs @diagrammo/dgmo-mcp if needed,
|
|
521
|
+
then writes .mcp.json (project) or ~/.claude/settings.json (global)
|
|
522
|
+
--install-claude-skill Install only the /dgmo skill to ~/.claude/commands/dgmo.md
|
|
523
|
+
--install-codex-integration
|
|
524
|
+
Full Codex CLI setup: write AGENTS.md to the project and configure
|
|
525
|
+
the dgmo MCP server in .codex/config.toml (project) or ~/.codex/config.toml (global)
|
|
81
526
|
--help Show this help
|
|
82
527
|
--version Show version`);
|
|
83
528
|
}
|
|
@@ -100,6 +545,9 @@ function parseArgs(argv: string[]): {
|
|
|
100
545
|
copy: boolean;
|
|
101
546
|
json: boolean;
|
|
102
547
|
chartTypes: boolean;
|
|
548
|
+
installClaudeSkill: boolean;
|
|
549
|
+
installClaudeCodeIntegration: boolean;
|
|
550
|
+
installCodexIntegration: boolean;
|
|
103
551
|
c4Level: 'context' | 'containers' | 'components' | 'deployment';
|
|
104
552
|
c4System: string | undefined;
|
|
105
553
|
c4Container: string | undefined;
|
|
@@ -116,6 +564,9 @@ function parseArgs(argv: string[]): {
|
|
|
116
564
|
copy: false,
|
|
117
565
|
json: false,
|
|
118
566
|
chartTypes: false,
|
|
567
|
+
installClaudeSkill: false,
|
|
568
|
+
installClaudeCodeIntegration: false,
|
|
569
|
+
installCodexIntegration: false,
|
|
119
570
|
c4Level: 'context' as 'context' | 'containers' | 'components' | 'deployment',
|
|
120
571
|
c4System: undefined as string | undefined,
|
|
121
572
|
c4Container: undefined as string | undefined,
|
|
@@ -185,6 +636,15 @@ function parseArgs(argv: string[]): {
|
|
|
185
636
|
} else if (arg === '--chart-types') {
|
|
186
637
|
result.chartTypes = true;
|
|
187
638
|
i++;
|
|
639
|
+
} else if (arg === '--install-claude-code-integration') {
|
|
640
|
+
result.installClaudeCodeIntegration = true;
|
|
641
|
+
i++;
|
|
642
|
+
} else if (arg === '--install-claude-skill') {
|
|
643
|
+
result.installClaudeSkill = true;
|
|
644
|
+
i++;
|
|
645
|
+
} else if (arg === '--install-codex-integration') {
|
|
646
|
+
result.installCodexIntegration = true;
|
|
647
|
+
i++;
|
|
188
648
|
} else if (arg === '--copy') {
|
|
189
649
|
result.copy = true;
|
|
190
650
|
i++;
|
|
@@ -288,6 +748,217 @@ async function main(): Promise<void> {
|
|
|
288
748
|
return;
|
|
289
749
|
}
|
|
290
750
|
|
|
751
|
+
if (opts.installClaudeCodeIntegration) {
|
|
752
|
+
const claudeDir = join(homedir(), '.claude');
|
|
753
|
+
if (!existsSync(claudeDir)) {
|
|
754
|
+
console.error('~/.claude directory not found.');
|
|
755
|
+
console.error('Install Claude Code first: https://claude.ai/code');
|
|
756
|
+
process.exit(1);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function ask(prompt: string): Promise<string> {
|
|
760
|
+
return new Promise((resolve) => {
|
|
761
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
762
|
+
rl.question(prompt, (answer) => { rl.close(); resolve(answer); });
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// --- Step 1: Install skill ---
|
|
767
|
+
const commandsDir = join(claudeDir, 'commands');
|
|
768
|
+
const skillPath = join(commandsDir, 'dgmo.md');
|
|
769
|
+
const skillExists = existsSync(skillPath);
|
|
770
|
+
let installSkill = true;
|
|
771
|
+
if (skillExists) {
|
|
772
|
+
const ans = await ask('~/.claude/commands/dgmo.md already exists. Overwrite? [y/N] ');
|
|
773
|
+
installSkill = ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
774
|
+
}
|
|
775
|
+
if (installSkill) {
|
|
776
|
+
if (!existsSync(commandsDir)) mkdirSync(commandsDir, { recursive: true });
|
|
777
|
+
writeFileSync(skillPath, CLAUDE_SKILL_CONTENT, 'utf-8');
|
|
778
|
+
console.log('✓ Skill installed: ~/.claude/commands/dgmo.md');
|
|
779
|
+
} else {
|
|
780
|
+
console.log(' Skipped skill install.');
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// --- Step 2: Check / install dgmo-mcp binary ---
|
|
784
|
+
let dgmoMcpInstalled = false;
|
|
785
|
+
try { execSync('which dgmo-mcp', { stdio: 'pipe' }); dgmoMcpInstalled = true; } catch { /* not found */ }
|
|
786
|
+
if (!dgmoMcpInstalled) {
|
|
787
|
+
const ans = await ask('\ndgmo-mcp not found. Install @diagrammo/dgmo-mcp globally now? [Y/n] ');
|
|
788
|
+
const yes = ans === '' || ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
789
|
+
if (yes) {
|
|
790
|
+
console.log('Installing @diagrammo/dgmo-mcp...');
|
|
791
|
+
execSync('npm install -g @diagrammo/dgmo-mcp', { stdio: 'inherit' });
|
|
792
|
+
console.log('✓ @diagrammo/dgmo-mcp installed');
|
|
793
|
+
} else {
|
|
794
|
+
console.log(' Skipped. Install later with: npm install -g @diagrammo/dgmo-mcp');
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
console.log('✓ dgmo-mcp already installed');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// --- Step 3: Configure MCP server ---
|
|
801
|
+
console.log('\nWhere should the MCP server be configured?');
|
|
802
|
+
console.log(' 1) This project only — write .mcp.json here [default]');
|
|
803
|
+
console.log(' 2) Globally — add to ~/.claude/settings.json (works in all projects)');
|
|
804
|
+
const scopeAns = await ask('\nChoice [1]: ');
|
|
805
|
+
const useGlobal = scopeAns.trim() === '2';
|
|
806
|
+
const mcpEntry = { command: 'dgmo-mcp' };
|
|
807
|
+
|
|
808
|
+
if (useGlobal) {
|
|
809
|
+
const settingsPath = join(claudeDir, 'settings.json');
|
|
810
|
+
let settings: Record<string, unknown> = {};
|
|
811
|
+
if (existsSync(settingsPath)) {
|
|
812
|
+
try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch { /* use empty */ }
|
|
813
|
+
}
|
|
814
|
+
const mcpServers = (settings.mcpServers as Record<string, unknown> | undefined) ?? {};
|
|
815
|
+
mcpServers['dgmo'] = mcpEntry;
|
|
816
|
+
settings.mcpServers = mcpServers;
|
|
817
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
818
|
+
console.log('✓ MCP server added to ~/.claude/settings.json');
|
|
819
|
+
} else {
|
|
820
|
+
const mcpPath = join(process.cwd(), '.mcp.json');
|
|
821
|
+
let mcp: Record<string, unknown> = {};
|
|
822
|
+
if (existsSync(mcpPath)) {
|
|
823
|
+
try { mcp = JSON.parse(readFileSync(mcpPath, 'utf-8')); } catch { /* use empty */ }
|
|
824
|
+
}
|
|
825
|
+
const mcpServers = (mcp.mcpServers as Record<string, unknown> | undefined) ?? {};
|
|
826
|
+
mcpServers['dgmo'] = mcpEntry;
|
|
827
|
+
mcp.mcpServers = mcpServers;
|
|
828
|
+
writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n', 'utf-8');
|
|
829
|
+
console.log(`✓ MCP server configured: ${join(process.cwd(), '.mcp.json')}`);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
console.log('\nRestart Claude Code to activate the MCP server.');
|
|
833
|
+
console.log('Then type /dgmo in any session to start creating diagrams.');
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (opts.installClaudeSkill) {
|
|
838
|
+
const claudeDir = join(homedir(), '.claude');
|
|
839
|
+
if (!existsSync(claudeDir)) {
|
|
840
|
+
console.error('~/.claude directory not found.');
|
|
841
|
+
console.error('Install Claude Code first: https://claude.ai/code');
|
|
842
|
+
process.exit(1);
|
|
843
|
+
}
|
|
844
|
+
const commandsDir = join(claudeDir, 'commands');
|
|
845
|
+
const destPath = join(commandsDir, 'dgmo.md');
|
|
846
|
+
const alreadyExists = existsSync(destPath);
|
|
847
|
+
const prompt = alreadyExists
|
|
848
|
+
? `~/.claude/commands/dgmo.md already exists. Overwrite? [y/N] `
|
|
849
|
+
: `Install dgmo Claude Code skill to ~/.claude/commands/dgmo.md? [Y/n] `;
|
|
850
|
+
await new Promise<void>((done) => {
|
|
851
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
852
|
+
rl.question(prompt, (answer) => {
|
|
853
|
+
rl.close();
|
|
854
|
+
const yes = alreadyExists
|
|
855
|
+
? answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'
|
|
856
|
+
: answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
857
|
+
if (!yes) {
|
|
858
|
+
console.error('Aborted.');
|
|
859
|
+
process.exit(0);
|
|
860
|
+
}
|
|
861
|
+
done();
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
if (!existsSync(commandsDir)) {
|
|
865
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
866
|
+
}
|
|
867
|
+
writeFileSync(destPath, CLAUDE_SKILL_CONTENT, 'utf-8');
|
|
868
|
+
console.log(`Installed: ${destPath}`);
|
|
869
|
+
console.log('Use /dgmo in Claude Code to activate the skill.');
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (opts.installCodexIntegration) {
|
|
874
|
+
// Validate Codex CLI is installed
|
|
875
|
+
try { execSync('which codex', { stdio: 'pipe' }); } catch {
|
|
876
|
+
console.error('codex not found. Install Codex CLI first: https://openai.com/codex');
|
|
877
|
+
process.exit(1);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const ask = (prompt: string): Promise<string> =>
|
|
881
|
+
new Promise((resolve) => {
|
|
882
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
883
|
+
rl.question(prompt, (answer) => { rl.close(); resolve(answer); });
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// Check / install dgmo-mcp binary
|
|
887
|
+
let dgmoMcpInstalled = false;
|
|
888
|
+
try { execSync('which dgmo-mcp', { stdio: 'pipe' }); dgmoMcpInstalled = true; } catch { /* not found */ }
|
|
889
|
+
if (!dgmoMcpInstalled) {
|
|
890
|
+
const ans = await ask('\ndgmo-mcp not found. Install @diagrammo/dgmo-mcp globally now? [Y/n] ');
|
|
891
|
+
const yes = ans === '' || ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
892
|
+
if (yes) {
|
|
893
|
+
console.log('Installing @diagrammo/dgmo-mcp...');
|
|
894
|
+
try {
|
|
895
|
+
execSync('npm install -g @diagrammo/dgmo-mcp', { stdio: 'inherit' });
|
|
896
|
+
console.log('✓ @diagrammo/dgmo-mcp installed');
|
|
897
|
+
} catch {
|
|
898
|
+
console.error('Error: Failed to install @diagrammo/dgmo-mcp.');
|
|
899
|
+
console.error('Try manually: npm install -g @diagrammo/dgmo-mcp');
|
|
900
|
+
}
|
|
901
|
+
} else {
|
|
902
|
+
console.log(' Skipped. Install later with: npm install -g @diagrammo/dgmo-mcp');
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
905
|
+
console.log('✓ dgmo-mcp already installed');
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// Configure MCP server
|
|
909
|
+
console.log('\nWhere should the MCP server be configured?');
|
|
910
|
+
console.log(' 1) This project only — write .codex/config.toml here [default]');
|
|
911
|
+
console.log(' 2) Globally — add to ~/.codex/config.toml (works in all projects)');
|
|
912
|
+
const scopeAns = await ask('\nChoice [1]: ');
|
|
913
|
+
if (scopeAns.trim() !== '' && scopeAns.trim() !== '1' && scopeAns.trim() !== '2') {
|
|
914
|
+
console.log(` Unrecognized input "${scopeAns.trim()}", defaulting to option 1.`);
|
|
915
|
+
}
|
|
916
|
+
const useGlobal = scopeAns.trim() === '2';
|
|
917
|
+
const tomlEntry = '[mcp_servers.dgmo]\ncommand = ["dgmo-mcp"]\n';
|
|
918
|
+
|
|
919
|
+
if (useGlobal) {
|
|
920
|
+
const configPath = join(homedir(), '.codex', 'config.toml');
|
|
921
|
+
mkdirSync(join(homedir(), '.codex'), { recursive: true });
|
|
922
|
+
const existing = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
|
|
923
|
+
if (existing.includes('[mcp_servers.dgmo]')) {
|
|
924
|
+
console.log('✓ MCP server already configured in ~/.codex/config.toml');
|
|
925
|
+
} else {
|
|
926
|
+
const separator = existing.length > 0 ? '\n' : '';
|
|
927
|
+
writeFileSync(configPath, existing + separator + tomlEntry, 'utf-8');
|
|
928
|
+
console.log('✓ MCP server added to ~/.codex/config.toml');
|
|
929
|
+
}
|
|
930
|
+
} else {
|
|
931
|
+
const codexDir = join(process.cwd(), '.codex');
|
|
932
|
+
const configPath = join(codexDir, 'config.toml');
|
|
933
|
+
mkdirSync(codexDir, { recursive: true });
|
|
934
|
+
const existing = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
|
|
935
|
+
if (existing.includes('[mcp_servers.dgmo]')) {
|
|
936
|
+
console.log(`✓ MCP server already configured in .codex/config.toml`);
|
|
937
|
+
} else {
|
|
938
|
+
const separator = existing.length > 0 ? '\n' : '';
|
|
939
|
+
writeFileSync(configPath, existing + separator + tomlEntry, 'utf-8');
|
|
940
|
+
console.log(`✓ MCP server configured: ${configPath}`);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Write AGENTS.md
|
|
945
|
+
const agentsPath = join(process.cwd(), 'AGENTS.md');
|
|
946
|
+
let writeAgents = true;
|
|
947
|
+
if (existsSync(agentsPath)) {
|
|
948
|
+
const ans = await ask('\nAGENTS.md already exists. Overwrite? [y/N] ');
|
|
949
|
+
writeAgents = ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
950
|
+
}
|
|
951
|
+
if (writeAgents) {
|
|
952
|
+
writeFileSync(agentsPath, CODEX_AGENTS_CONTENT, 'utf-8');
|
|
953
|
+
console.log(`✓ AGENTS.md written to: ${agentsPath}`);
|
|
954
|
+
} else {
|
|
955
|
+
console.log(' Skipped AGENTS.md.');
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
console.log('\nRestart Codex to activate the MCP server.');
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
|
|
291
962
|
// Determine input source
|
|
292
963
|
let content: string;
|
|
293
964
|
let inputBasename: string | undefined;
|