@diagrammo/dgmo 0.6.2 → 0.7.0

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.
Files changed (61) hide show
  1. package/.claude/commands/dgmo.md +231 -13
  2. package/AGENTS.md +148 -0
  3. package/dist/cli.cjs +341 -165
  4. package/dist/index.cjs +4900 -1685
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +259 -18
  7. package/dist/index.d.ts +259 -18
  8. package/dist/index.js +4642 -1436
  9. package/dist/index.js.map +1 -1
  10. package/package.json +5 -3
  11. package/src/c4/layout.ts +0 -5
  12. package/src/c4/parser.ts +0 -16
  13. package/src/c4/renderer.ts +7 -11
  14. package/src/class/layout.ts +0 -1
  15. package/src/class/parser.ts +28 -0
  16. package/src/class/renderer.ts +189 -34
  17. package/src/cli.ts +566 -25
  18. package/src/colors.ts +3 -3
  19. package/src/completion.ts +58 -0
  20. package/src/d3.ts +179 -122
  21. package/src/dgmo-router.ts +3 -58
  22. package/src/echarts.ts +96 -55
  23. package/src/er/parser.ts +30 -1
  24. package/src/er/renderer.ts +12 -7
  25. package/src/gantt/calculator.ts +677 -0
  26. package/src/gantt/parser.ts +761 -0
  27. package/src/gantt/renderer.ts +2125 -0
  28. package/src/gantt/resolver.ts +144 -0
  29. package/src/gantt/types.ts +168 -0
  30. package/src/graph/flowchart-parser.ts +27 -4
  31. package/src/graph/flowchart-renderer.ts +1 -2
  32. package/src/graph/state-parser.ts +0 -1
  33. package/src/graph/state-renderer.ts +1 -3
  34. package/src/index.ts +37 -0
  35. package/src/infra/compute.ts +0 -7
  36. package/src/infra/layout.ts +0 -2
  37. package/src/infra/parser.ts +46 -4
  38. package/src/infra/renderer.ts +49 -27
  39. package/src/initiative-status/filter.ts +63 -0
  40. package/src/initiative-status/layout.ts +319 -67
  41. package/src/initiative-status/parser.ts +200 -25
  42. package/src/initiative-status/renderer.ts +298 -35
  43. package/src/initiative-status/types.ts +6 -0
  44. package/src/kanban/parser.ts +0 -2
  45. package/src/org/layout.ts +22 -59
  46. package/src/org/renderer.ts +11 -36
  47. package/src/palettes/dracula.ts +60 -0
  48. package/src/palettes/index.ts +8 -6
  49. package/src/palettes/monokai.ts +60 -0
  50. package/src/palettes/registry.ts +4 -2
  51. package/src/sequence/parser.ts +14 -11
  52. package/src/sequence/renderer.ts +5 -6
  53. package/src/sequence/tag-resolution.ts +0 -1
  54. package/src/sharing.ts +8 -0
  55. package/src/sitemap/layout.ts +1 -14
  56. package/src/sitemap/parser.ts +1 -2
  57. package/src/sitemap/renderer.ts +4 -7
  58. package/src/utils/arrows.ts +7 -7
  59. package/src/utils/duration.ts +212 -0
  60. package/src/utils/export-container.ts +40 -0
  61. package/src/utils/legend-constants.ts +1 -0
package/src/cli.ts CHANGED
@@ -8,21 +8,13 @@ import { render } from './render';
8
8
  import { parseDgmo, getAllChartTypes } from './dgmo-router';
9
9
  import { parseDgmoChartType } from './dgmo-router';
10
10
  import { formatDgmoError } from './diagnostics';
11
- import { getPalette } from './palettes/registry';
11
+ import { getPalette, getAvailablePalettes } from './palettes';
12
12
  import { DEFAULT_FONT_NAME } from './fonts';
13
13
  import { encodeDiagramUrl } from './sharing';
14
14
  import { resolveOrgImports } from './org/resolver';
15
15
 
16
- const PALETTES = [
17
- 'nord',
18
- 'solarized',
19
- 'catppuccin',
20
- 'rose-pine',
21
- 'gruvbox',
22
- 'tokyo-night',
23
- 'one-dark',
24
- 'bold',
25
- ];
16
+ // Derived from the palette registry so new palettes are auto-included.
17
+ const PALETTES = getAvailablePalettes().map((p) => p.id);
26
18
 
27
19
  const THEMES = ['light', 'dark', 'transparent'] as const;
28
20
 
@@ -65,7 +57,93 @@ You are helping the user author, render, and share diagrams using the \`dgmo\` C
65
57
 
66
58
  ## What is dgmo?
67
59
 
68
- \`dgmo\` is a CLI tool that renders \`.dgmo\` diagram files to PNG, SVG, or shareable URLs. Diagrams are written in a plain-text DSL.
60
+ \`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.
61
+
62
+ ## Setup Check — Run This First
63
+
64
+ **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.
65
+
66
+ If the MCP tools are **not** available, run the setup flow below — do not ask the user, just do it:
67
+
68
+ ### Step 1 — Install the CLI (if missing)
69
+
70
+ \`\`\`bash
71
+ which dgmo || npm install -g @diagrammo/dgmo
72
+ \`\`\`
73
+
74
+ ### Step 2 — Install the MCP server (if missing)
75
+
76
+ \`\`\`bash
77
+ which dgmo-mcp || npm install -g @diagrammo/dgmo-mcp
78
+ \`\`\`
79
+
80
+ ### Step 3 — Configure the MCP server
81
+
82
+ Ask the user:
83
+
84
+ > "Where should I configure the MCP server?
85
+ > 1) This project only — write \`.mcp.json\` here [default]
86
+ > 2) Globally — add to \`~/.claude/settings.json\` (works in all projects)"
87
+
88
+ **Option 1 (default):** Create or update \`.mcp.json\` in the current working directory:
89
+
90
+ \`\`\`json
91
+ {
92
+ "mcpServers": {
93
+ "dgmo": {
94
+ "command": "dgmo-mcp"
95
+ }
96
+ }
97
+ }
98
+ \`\`\`
99
+
100
+ If \`.mcp.json\` already exists and has other servers, merge the \`dgmo\` entry in — do not overwrite the file.
101
+
102
+ **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.
103
+
104
+ ### Step 4 — Prompt restart
105
+
106
+ Tell the user:
107
+
108
+ > "Done. **Restart Claude Code** to activate the MCP server — diagram preview and rendering will be available in the next session."
109
+
110
+ Then proceed with the user's original request using CLI fallback (see "Other output options" below).
111
+
112
+ > **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.
113
+
114
+ ## Getting Syntax Help
115
+
116
+ **Always use the MCP tool first** if it's available in this session:
117
+
118
+ \`\`\`
119
+ mcp__dgmo__get_language_reference // full reference
120
+ mcp__dgmo__get_language_reference("sequence") // specific chart type
121
+ \`\`\`
122
+
123
+ This is the authoritative, always-up-to-date syntax reference. Use it before guessing syntax.
124
+
125
+ ## Your Workflow
126
+
127
+ When the user asks you to create or edit a diagram:
128
+
129
+ 1. **Get syntax** — call \`mcp__dgmo__get_language_reference("<type>")\` if you're unsure of the syntax.
130
+ 2. **Write the \`.dgmo\` content** — compose the markup.
131
+ 3. **Save the source file** (if working in a project) — write it to \`<name>.dgmo\` so the user has an editable file.
132
+ 4. **Render and show** — pick the right output based on what the user wants (see below).
133
+
134
+ ### Output options — always offer these proactively after creating a diagram
135
+
136
+ | What the user wants | How to do it |
137
+ |---|---|
138
+ | **Quick look in the desktop app** | \`mcp__dgmo__open_in_app(dgmo)\` — opens directly in Diagrammo (macOS) |
139
+ | **Browser preview with theme toggle** | \`mcp__dgmo__preview_diagram([{dgmo, title}])\` — opens HTML in browser |
140
+ | **View in macOS Preview (or default image viewer)** | \`mcp__dgmo__render_diagram(dgmo, format:"png")\` → get temp path → \`open <path>\` |
141
+ | **View SVG in browser** | \`mcp__dgmo__render_diagram(dgmo, format:"svg")\` → write SVG to a temp \`.svg\` file → \`open <path>\` |
142
+ | **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\` |
143
+ | **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\` |
144
+ | **Shareable URL** | \`mcp__dgmo__share_diagram(dgmo)\` or CLI: \`dgmo file.dgmo -o url --copy\` |
145
+
146
+ **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?"*
69
147
 
70
148
  ## CLI Reference
71
149
 
@@ -106,35 +184,306 @@ Key options:
106
184
  | \`quadrant\` | 2x2 positioning matrix |
107
185
  | \`sequence\` | Message / interaction flows |
108
186
  | \`flowchart\` | Decision trees, process flows |
187
+ | \`state\` | State machine / lifecycle |
109
188
  | \`class\` | UML class hierarchies |
110
189
  | \`er\` | Database schemas |
111
190
  | \`org\` | Hierarchical tree structures |
112
191
  | \`kanban\` | Task / workflow columns |
113
192
  | \`c4\` | System architecture (context → container → component → deployment) |
114
193
  | \`initiative-status\` | Project roadmap with dependency tracking |
194
+ | \`sitemap\` | Website / app navigation structure |
195
+ | \`infra\` | Infrastructure traffic flow with rps computation |
115
196
 
116
- ## Your Workflow
197
+ ## Key Syntax Patterns
117
198
 
118
- When the user asks you to create or edit a diagram:
199
+ ### Common to all diagrams
119
200
 
120
- 1. **Write or edit the \`.dgmo\` file** with the appropriate chart type and data.
121
- 2. **Render it** with \`dgmo <file>.dgmo -o <file>.png\` to verify it produces output without errors.
122
- 3. **Show the user** what was created and suggest a shareable URL with \`dgmo <file>.dgmo -o url --copy\` if they want to share it.
201
+ \`\`\`
202
+ chart: sequence // explicit type (optional auto-detected)
203
+ title: My Diagram
204
+ palette: catppuccin // override palette
123
205
 
124
- When the user asks for a **shareable link**, run:
206
+ // This is a comment (only // syntax — not #)
125
207
  \`\`\`
126
- dgmo <file>.dgmo -o url --copy
208
+
209
+ Inline colors on most elements: append \`(colorname)\` — e.g. \`North(red): 850\`, \`[Process(blue)]\`.
210
+ Named colors: \`red\`, \`orange\`, \`yellow\`, \`green\`, \`blue\`, \`purple\`, \`teal\`, \`cyan\`, \`gray\`.
211
+
212
+ ### sequence (most commonly used)
213
+
127
214
  \`\`\`
215
+ chart: sequence
216
+ title: Auth Flow
128
217
 
129
- ## Getting Syntax Help
218
+ // Participants auto-inferred, or declare explicitly:
219
+ User is an actor
220
+ API is a service
221
+ DB is a database
222
+
223
+ User -Login-> API
224
+ API -Find user-> DB
225
+ DB -user record-> API
130
226
 
131
- Run \`dgmo --chart-types\` to list types. For detailed syntax of a specific chart type, the best reference is the diagrammo.app documentation or existing \`.dgmo\` files in the project.
227
+ if credentials valid
228
+ API -200 OK + token-> User
229
+ else
230
+ API -401 Unauthorized-> User
231
+
232
+ == Logout ==
233
+
234
+ User -Logout-> API
235
+ API -Delete session-> DB
236
+ \`\`\`
237
+
238
+ - Sync: \`A -label-> B\` · Async: \`A ~label~> B\` · Unlabeled: \`A -> B\`
239
+ - Blocks: \`if\` / \`else\`, \`loop\`, \`parallel\` — closed by indentation (no \`end\` keyword)
240
+ - Notes: \`note on API: text\` or \`note: text\`
241
+ - Sections: \`== Title ==\`
242
+ - Groups: \`[Group Name]\` with indented participants
243
+
244
+ ### flowchart
245
+
246
+ \`\`\`
247
+ (Start) -> <Valid Input?>
248
+ -yes-> [Process Data] -> (Done)
249
+ -no-> /Get Input/ -> <Valid Input?>
250
+ \`\`\`
251
+
252
+ Shapes: \`(oval)\` \`[rect]\` \`<diamond>\` \`/parallelogram/\` \`[[subroutine]]\` \`[document~]\`
253
+
254
+ ### bar / line / pie (data charts)
255
+
256
+ \`\`\`
257
+ // bar
258
+ title: Revenue by Region
259
+ series: Revenue
260
+ North: 850
261
+ South: 620
262
+
263
+ // line (multi-series)
264
+ series: Sales(red), Costs(blue)
265
+ Q1: 100, 50
266
+ Q2: 120, 55
267
+
268
+ // pie
269
+ chart: pie
270
+ labels: percent
271
+ Company A: 40
272
+ Company B: 35
273
+ \`\`\`
274
+
275
+ ### er
276
+
277
+ \`\`\`
278
+ users
279
+ id: int [pk]
280
+ email: varchar [unique]
281
+ 1-writes-* posts
282
+
283
+ posts
284
+ id: int [pk]
285
+ author_id: int [fk]
286
+ \`\`\`
287
+
288
+ ### org
289
+
290
+ \`\`\`
291
+ CEO
292
+ VP Engineering
293
+ [Platform Team]
294
+ Lead
295
+ Dev 1
296
+ Dev 2
297
+ VP Marketing
298
+ \`\`\`
299
+
300
+ ### infra
301
+
302
+ \`\`\`
303
+ chart: infra
304
+ edge
305
+ rps: 10000
306
+ -> CDN
307
+
308
+ CDN
309
+ cache-hit: 80%
310
+ -> API
311
+
312
+ API
313
+ instances: 3
314
+ max-rps: 500
315
+ latency-ms: 45
316
+ \`\`\`
317
+
318
+ ## Anti-Patterns
319
+
320
+ \`\`\`
321
+ # comment ❌ use // comment
322
+ async A -> B: msg ❌ use A ~msg~> B
323
+ A <- B ❌ left-pointing arrows removed — use B -> A
324
+ parallel else ❌ not supported — use separate parallel blocks
325
+ == Foo(#ff0000) == ❌ hex colors not supported — use named colors: == Foo(red) ==
326
+ A -routes to /api-> B ❌ -> inside a label is ambiguous — rephrase the label
327
+ end ❌ not needed — indentation closes blocks in sequence diagrams
328
+ \`\`\`
132
329
 
133
330
  ## Tips
134
331
 
135
- - Default theme is \`light\` and default palette is \`nord\` — ask the user if they have a preference before rendering a final export.
136
- - For C4 diagrams, use \`--c4-level\` to drill from context → containers → components → deployment.
137
- - Stdin mode is useful for quick one-off renders: \`echo "..." | dgmo -o out.png\`
332
+ - Default theme: \`light\`, default palette: \`nord\` — ask the user their preference before a final export.
333
+ - Stdin mode for quick renders: \`echo "..." | dgmo -o out.png\`
334
+ - For C4, \`--c4-level\` drills from context containers components deployment.
335
+ - When auto-detection picks the wrong chart type, add an explicit \`chart:\` directive.
336
+ - \`mcp__dgmo__preview_diagram\` accepts multiple diagrams at once — useful for showing variants side by side.
337
+ `;
338
+
339
+ const CODEX_AGENTS_CONTENT = `# DGMO Diagram Language — Codex Integration
340
+
341
+ Use dgmo tools to create, render, and share diagrams. dgmo is a text-based diagram markup language that renders to SVG/PNG.
342
+
343
+ ## Quick setup
344
+
345
+ If the MCP server is not yet configured:
346
+
347
+ \`\`\`bash
348
+ dgmo --install-codex-integration
349
+ \`\`\`
350
+
351
+ This installs the MCP server and writes the dgmo config to \`.codex/config.toml\`. Restart Codex to activate.
352
+
353
+ ## MCP Tools
354
+
355
+ When the \`dgmo\` MCP server is configured, use these tools directly:
356
+ - \`preview_diagram\` — renders diagram(s) and opens a live HTML preview in the browser (default for showing diagrams)
357
+ - \`render_diagram\` — renders to PNG or SVG, returns file path
358
+ - \`share_diagram\` — creates a shareable diagrammo.app URL
359
+ - \`open_in_app\` — opens diagram in Diagrammo desktop app (macOS)
360
+ - \`list_chart_types\` — lists all 32 supported chart types with descriptions
361
+ - \`get_language_reference\` — fetches full syntax for any chart type (call this before generating an unfamiliar chart type)
362
+ - \`generate_report\` — renders multiple diagrams into an HTML report with table of contents
363
+
364
+ ## When to use dgmo
365
+
366
+ - Architecture diagrams, sequence diagrams, flowcharts
367
+ - Data charts (bar, line, pie, scatter, heatmap, etc.)
368
+ - ER diagrams, class diagrams, org charts
369
+ - Project roadmaps, kanban boards, timelines
370
+
371
+ ## Quick syntax reference
372
+
373
+ ### Sequence diagram
374
+ \`\`\`
375
+ chart: sequence
376
+ title: Auth Flow
377
+
378
+ User -Login-> API
379
+ API -Find user-> DB
380
+ DB -user-> API
381
+ if valid
382
+ API -200 OK-> User
383
+ else
384
+ API -401-> User
385
+ \`\`\`
386
+
387
+ ### Flowchart
388
+ \`\`\`
389
+ chart: flowchart
390
+ title: Process
391
+
392
+ (Start) -> <Valid?>
393
+ -yes-> [Process] -> (Done)
394
+ -no-> /Get Input/ -> <Valid?>
395
+ \`\`\`
396
+
397
+ ### Bar chart
398
+ \`\`\`
399
+ chart: bar
400
+ title: Revenue
401
+ series: USD
402
+
403
+ North: 850
404
+ South: 620
405
+ East: 1100
406
+ \`\`\`
407
+
408
+ ### ER diagram
409
+ \`\`\`
410
+ chart: er
411
+ title: Schema
412
+
413
+ users
414
+ id: int [pk]
415
+ email: varchar [unique]
416
+
417
+ posts
418
+ id: int [pk]
419
+ user_id: int [fk]
420
+
421
+ users 1--* posts : writes
422
+ \`\`\`
423
+
424
+ ### Org chart
425
+ \`\`\`
426
+ chart: org
427
+
428
+ CEO
429
+ VP Engineering
430
+ Team Lead A
431
+ Team Lead B
432
+ VP Marketing
433
+ \`\`\`
434
+
435
+ ### Infra chart
436
+ \`\`\`
437
+ chart: infra
438
+ direction: LR
439
+
440
+ edge
441
+ rps: 10000
442
+ -> CDN
443
+
444
+ CDN
445
+ cache-hit: 80%
446
+ -> LB
447
+
448
+ LB
449
+ -> API | split: 70%
450
+ -> Web | split: 30%
451
+
452
+ API
453
+ instances: 3
454
+ max-rps: 500
455
+ latency-ms: 45
456
+ \`\`\`
457
+
458
+ ## All 32 chart types
459
+
460
+ 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
461
+
462
+ ## Common patterns
463
+
464
+ - \`chart: type\` — explicit chart type (auto-detected if unambiguous)
465
+ - \`title: text\` — diagram title
466
+ - \`// comment\` — only \`//\` comments (not \`#\`)
467
+ - \`(colorname)\` — inline colors: \`Label(red): 100\`
468
+ - \`series: A(red), B(blue)\` — multi-series with colors
469
+
470
+ ## Rendering via CLI
471
+
472
+ \`\`\`bash
473
+ dgmo file.dgmo -o output.svg # SVG
474
+ dgmo file.dgmo -o url # shareable link
475
+ dgmo file.dgmo --json # structured JSON output
476
+ \`\`\`
477
+
478
+ ## Mistakes to avoid
479
+
480
+ - Don't use \`#\` for comments — use \`//\`
481
+ - Don't use \`end\` to close sequence blocks — indentation closes them
482
+ - Don't use hex colors in section headers — use named colors
483
+ - Don't forget \`chart:\` directive when content is ambiguous
484
+ - Sequence arrows: \`->\` (sync), \`~>\` (async) — always left-to-right
485
+
486
+ Full reference: call \`get_language_reference\` MCP tool or visit diagrammo.app/docs
138
487
  `;
139
488
 
140
489
  function printHelp(): void {
@@ -158,7 +507,14 @@ Options:
158
507
  --copy Copy URL to clipboard (only with -o url)
159
508
  --json Output structured JSON to stdout
160
509
  --chart-types List all supported chart types
161
- --install-claude-skill Install the dgmo Claude Code skill to ~/.claude/commands/
510
+ --install-claude-code-integration
511
+ Full Claude Code setup: install the /dgmo skill and configure
512
+ the dgmo MCP server — installs @diagrammo/dgmo-mcp if needed,
513
+ then writes .mcp.json (project) or ~/.claude/settings.json (global)
514
+ --install-claude-skill Install only the /dgmo skill to ~/.claude/commands/dgmo.md
515
+ --install-codex-integration
516
+ Full Codex CLI setup: write AGENTS.md to the project and configure
517
+ the dgmo MCP server in .codex/config.toml (project) or ~/.codex/config.toml (global)
162
518
  --help Show this help
163
519
  --version Show version`);
164
520
  }
@@ -182,6 +538,8 @@ function parseArgs(argv: string[]): {
182
538
  json: boolean;
183
539
  chartTypes: boolean;
184
540
  installClaudeSkill: boolean;
541
+ installClaudeCodeIntegration: boolean;
542
+ installCodexIntegration: boolean;
185
543
  c4Level: 'context' | 'containers' | 'components' | 'deployment';
186
544
  c4System: string | undefined;
187
545
  c4Container: string | undefined;
@@ -199,6 +557,8 @@ function parseArgs(argv: string[]): {
199
557
  json: false,
200
558
  chartTypes: false,
201
559
  installClaudeSkill: false,
560
+ installClaudeCodeIntegration: false,
561
+ installCodexIntegration: false,
202
562
  c4Level: 'context' as 'context' | 'containers' | 'components' | 'deployment',
203
563
  c4System: undefined as string | undefined,
204
564
  c4Container: undefined as string | undefined,
@@ -268,9 +628,15 @@ function parseArgs(argv: string[]): {
268
628
  } else if (arg === '--chart-types') {
269
629
  result.chartTypes = true;
270
630
  i++;
631
+ } else if (arg === '--install-claude-code-integration') {
632
+ result.installClaudeCodeIntegration = true;
633
+ i++;
271
634
  } else if (arg === '--install-claude-skill') {
272
635
  result.installClaudeSkill = true;
273
636
  i++;
637
+ } else if (arg === '--install-codex-integration') {
638
+ result.installCodexIntegration = true;
639
+ i++;
274
640
  } else if (arg === '--copy') {
275
641
  result.copy = true;
276
642
  i++;
@@ -374,6 +740,92 @@ async function main(): Promise<void> {
374
740
  return;
375
741
  }
376
742
 
743
+ if (opts.installClaudeCodeIntegration) {
744
+ const claudeDir = join(homedir(), '.claude');
745
+ if (!existsSync(claudeDir)) {
746
+ console.error('~/.claude directory not found.');
747
+ console.error('Install Claude Code first: https://claude.ai/code');
748
+ process.exit(1);
749
+ }
750
+
751
+ function ask(prompt: string): Promise<string> {
752
+ return new Promise((resolve) => {
753
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
754
+ rl.question(prompt, (answer) => { rl.close(); resolve(answer); });
755
+ });
756
+ }
757
+
758
+ // --- Step 1: Install skill ---
759
+ const commandsDir = join(claudeDir, 'commands');
760
+ const skillPath = join(commandsDir, 'dgmo.md');
761
+ const skillExists = existsSync(skillPath);
762
+ let installSkill = true;
763
+ if (skillExists) {
764
+ const ans = await ask('~/.claude/commands/dgmo.md already exists. Overwrite? [y/N] ');
765
+ installSkill = ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
766
+ }
767
+ if (installSkill) {
768
+ if (!existsSync(commandsDir)) mkdirSync(commandsDir, { recursive: true });
769
+ writeFileSync(skillPath, CLAUDE_SKILL_CONTENT, 'utf-8');
770
+ console.log('✓ Skill installed: ~/.claude/commands/dgmo.md');
771
+ } else {
772
+ console.log(' Skipped skill install.');
773
+ }
774
+
775
+ // --- Step 2: Check / install dgmo-mcp binary ---
776
+ let dgmoMcpInstalled = false;
777
+ try { execSync('which dgmo-mcp', { stdio: 'pipe' }); dgmoMcpInstalled = true; } catch { /* not found */ }
778
+ if (!dgmoMcpInstalled) {
779
+ const ans = await ask('\ndgmo-mcp not found. Install @diagrammo/dgmo-mcp globally now? [Y/n] ');
780
+ const yes = ans === '' || ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
781
+ if (yes) {
782
+ console.log('Installing @diagrammo/dgmo-mcp...');
783
+ execSync('npm install -g @diagrammo/dgmo-mcp', { stdio: 'inherit' });
784
+ console.log('✓ @diagrammo/dgmo-mcp installed');
785
+ } else {
786
+ console.log(' Skipped. Install later with: npm install -g @diagrammo/dgmo-mcp');
787
+ }
788
+ } else {
789
+ console.log('✓ dgmo-mcp already installed');
790
+ }
791
+
792
+ // --- Step 3: Configure MCP server ---
793
+ console.log('\nWhere should the MCP server be configured?');
794
+ console.log(' 1) This project only — write .mcp.json here [default]');
795
+ console.log(' 2) Globally — add to ~/.claude/settings.json (works in all projects)');
796
+ const scopeAns = await ask('\nChoice [1]: ');
797
+ const useGlobal = scopeAns.trim() === '2';
798
+ const mcpEntry = { command: 'dgmo-mcp' };
799
+
800
+ if (useGlobal) {
801
+ const settingsPath = join(claudeDir, 'settings.json');
802
+ let settings: Record<string, unknown> = {};
803
+ if (existsSync(settingsPath)) {
804
+ try { settings = JSON.parse(readFileSync(settingsPath, 'utf-8')); } catch { /* use empty */ }
805
+ }
806
+ const mcpServers = (settings.mcpServers as Record<string, unknown> | undefined) ?? {};
807
+ mcpServers['dgmo'] = mcpEntry;
808
+ settings.mcpServers = mcpServers;
809
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
810
+ console.log('✓ MCP server added to ~/.claude/settings.json');
811
+ } else {
812
+ const mcpPath = join(process.cwd(), '.mcp.json');
813
+ let mcp: Record<string, unknown> = {};
814
+ if (existsSync(mcpPath)) {
815
+ try { mcp = JSON.parse(readFileSync(mcpPath, 'utf-8')); } catch { /* use empty */ }
816
+ }
817
+ const mcpServers = (mcp.mcpServers as Record<string, unknown> | undefined) ?? {};
818
+ mcpServers['dgmo'] = mcpEntry;
819
+ mcp.mcpServers = mcpServers;
820
+ writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n', 'utf-8');
821
+ console.log(`✓ MCP server configured: ${join(process.cwd(), '.mcp.json')}`);
822
+ }
823
+
824
+ console.log('\nRestart Claude Code to activate the MCP server.');
825
+ console.log('Then type /dgmo in any session to start creating diagrams.');
826
+ return;
827
+ }
828
+
377
829
  if (opts.installClaudeSkill) {
378
830
  const claudeDir = join(homedir(), '.claude');
379
831
  if (!existsSync(claudeDir)) {
@@ -410,6 +862,95 @@ async function main(): Promise<void> {
410
862
  return;
411
863
  }
412
864
 
865
+ if (opts.installCodexIntegration) {
866
+ // Validate Codex CLI is installed
867
+ try { execSync('which codex', { stdio: 'pipe' }); } catch {
868
+ console.error('codex not found. Install Codex CLI first: https://openai.com/codex');
869
+ process.exit(1);
870
+ }
871
+
872
+ const ask = (prompt: string): Promise<string> =>
873
+ new Promise((resolve) => {
874
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
875
+ rl.question(prompt, (answer) => { rl.close(); resolve(answer); });
876
+ });
877
+
878
+ // Check / install dgmo-mcp binary
879
+ let dgmoMcpInstalled = false;
880
+ try { execSync('which dgmo-mcp', { stdio: 'pipe' }); dgmoMcpInstalled = true; } catch { /* not found */ }
881
+ if (!dgmoMcpInstalled) {
882
+ const ans = await ask('\ndgmo-mcp not found. Install @diagrammo/dgmo-mcp globally now? [Y/n] ');
883
+ const yes = ans === '' || ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
884
+ if (yes) {
885
+ console.log('Installing @diagrammo/dgmo-mcp...');
886
+ try {
887
+ execSync('npm install -g @diagrammo/dgmo-mcp', { stdio: 'inherit' });
888
+ console.log('✓ @diagrammo/dgmo-mcp installed');
889
+ } catch {
890
+ console.error('Error: Failed to install @diagrammo/dgmo-mcp.');
891
+ console.error('Try manually: npm install -g @diagrammo/dgmo-mcp');
892
+ }
893
+ } else {
894
+ console.log(' Skipped. Install later with: npm install -g @diagrammo/dgmo-mcp');
895
+ }
896
+ } else {
897
+ console.log('✓ dgmo-mcp already installed');
898
+ }
899
+
900
+ // Configure MCP server
901
+ console.log('\nWhere should the MCP server be configured?');
902
+ console.log(' 1) This project only — write .codex/config.toml here [default]');
903
+ console.log(' 2) Globally — add to ~/.codex/config.toml (works in all projects)');
904
+ const scopeAns = await ask('\nChoice [1]: ');
905
+ if (scopeAns.trim() !== '' && scopeAns.trim() !== '1' && scopeAns.trim() !== '2') {
906
+ console.log(` Unrecognized input "${scopeAns.trim()}", defaulting to option 1.`);
907
+ }
908
+ const useGlobal = scopeAns.trim() === '2';
909
+ const tomlEntry = '[mcp_servers.dgmo]\ncommand = ["dgmo-mcp"]\n';
910
+
911
+ if (useGlobal) {
912
+ const configPath = join(homedir(), '.codex', 'config.toml');
913
+ mkdirSync(join(homedir(), '.codex'), { recursive: true });
914
+ const existing = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
915
+ if (existing.includes('[mcp_servers.dgmo]')) {
916
+ console.log('✓ MCP server already configured in ~/.codex/config.toml');
917
+ } else {
918
+ const separator = existing.length > 0 ? '\n' : '';
919
+ writeFileSync(configPath, existing + separator + tomlEntry, 'utf-8');
920
+ console.log('✓ MCP server added to ~/.codex/config.toml');
921
+ }
922
+ } else {
923
+ const codexDir = join(process.cwd(), '.codex');
924
+ const configPath = join(codexDir, 'config.toml');
925
+ mkdirSync(codexDir, { recursive: true });
926
+ const existing = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
927
+ if (existing.includes('[mcp_servers.dgmo]')) {
928
+ console.log(`✓ MCP server already configured in .codex/config.toml`);
929
+ } else {
930
+ const separator = existing.length > 0 ? '\n' : '';
931
+ writeFileSync(configPath, existing + separator + tomlEntry, 'utf-8');
932
+ console.log(`✓ MCP server configured: ${configPath}`);
933
+ }
934
+ }
935
+
936
+ // Write AGENTS.md
937
+ const agentsPath = join(process.cwd(), 'AGENTS.md');
938
+ let writeAgents = true;
939
+ if (existsSync(agentsPath)) {
940
+ const ans = await ask('\nAGENTS.md already exists. Overwrite? [y/N] ');
941
+ writeAgents = ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
942
+ }
943
+ if (writeAgents) {
944
+ writeFileSync(agentsPath, CODEX_AGENTS_CONTENT, 'utf-8');
945
+ console.log(`✓ AGENTS.md written to: ${agentsPath}`);
946
+ } else {
947
+ console.log(' Skipped AGENTS.md.');
948
+ }
949
+
950
+ console.log('\nRestart Codex to activate the MCP server.');
951
+ return;
952
+ }
953
+
413
954
  // Determine input source
414
955
  let content: string;
415
956
  let inputBasename: string | undefined;
package/src/colors.ts CHANGED
@@ -41,9 +41,9 @@ export const colorNames: Record<string, string> = {
41
41
  };
42
42
 
43
43
  /**
44
- * Resolves a color name or hex code to a valid CSS color.
44
+ * Resolves a color name to a valid CSS color.
45
45
  * When a palette is provided, named colors resolve against its color map first.
46
- * Hex codes (e.g. "#ff0000") are passed through regardless of palette (FR8).
46
+ * Hex codes are NOT supported use named colors only.
47
47
  * Unknown names are returned as-is.
48
48
  */
49
49
  export function resolveColor(
@@ -51,7 +51,6 @@ export function resolveColor(
51
51
  palette?: { colors: Record<string, string> }
52
52
  ): string {
53
53
  const lower = color.toLowerCase();
54
- if (lower.startsWith('#')) return lower;
55
54
 
56
55
  if (palette) {
57
56
  const named = palette.colors[lower];
@@ -59,6 +58,7 @@ export function resolveColor(
59
58
  }
60
59
 
61
60
  if (colorNames[lower]) return colorNames[lower];
61
+ // Unknown color name — return as-is so callers can detect + warn
62
62
  return color;
63
63
  }
64
64