@diagrammo/dgmo 0.8.3 → 0.8.4
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-diagram-this.md +60 -0
- package/.claude/commands/dgmo-document-project.md +128 -0
- package/.claude/commands/dgmo.md +185 -50
- package/.cursorrules +32 -37
- package/.github/copilot-instructions.md +35 -44
- package/.windsurfrules +32 -37
- package/README.md +4 -4
- package/dist/cli.cjs +153 -153
- package/dist/editor.cjs +336 -0
- package/dist/editor.cjs.map +1 -0
- package/dist/editor.d.cts +27 -0
- package/dist/editor.d.ts +27 -0
- package/dist/editor.js +305 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.cjs +3336 -1055
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3336 -1055
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +30 -29
- package/gallery/fixtures/arc.dgmo +18 -0
- package/gallery/fixtures/area.dgmo +19 -0
- package/gallery/fixtures/bar-stacked.dgmo +10 -0
- package/gallery/fixtures/bar.dgmo +10 -0
- package/gallery/fixtures/c4-full.dgmo +52 -0
- package/gallery/fixtures/c4.dgmo +17 -0
- package/gallery/fixtures/chord.dgmo +12 -0
- package/gallery/fixtures/class-basic.dgmo +14 -0
- package/gallery/fixtures/class-full.dgmo +43 -0
- package/gallery/fixtures/doughnut.dgmo +8 -0
- package/gallery/fixtures/flowchart-basic.dgmo +3 -0
- package/gallery/fixtures/flowchart-colors.dgmo +5 -0
- package/gallery/fixtures/flowchart-complex.dgmo +17 -0
- package/gallery/fixtures/flowchart-decision.dgmo +5 -0
- package/gallery/fixtures/flowchart-full.dgmo +13 -0
- package/gallery/fixtures/flowchart-groups.dgmo +10 -0
- package/gallery/fixtures/flowchart-loop.dgmo +7 -0
- package/gallery/fixtures/flowchart-nested.dgmo +7 -0
- package/gallery/fixtures/flowchart-shapes.dgmo +5 -0
- package/gallery/fixtures/function.dgmo +8 -0
- package/gallery/fixtures/funnel.dgmo +7 -0
- package/gallery/fixtures/gantt-full.dgmo +49 -0
- package/gallery/fixtures/gantt.dgmo +42 -0
- package/gallery/fixtures/heatmap.dgmo +8 -0
- package/gallery/fixtures/infra-full.dgmo +78 -0
- package/gallery/fixtures/infra-overload.dgmo +25 -0
- package/gallery/fixtures/infra.dgmo +47 -0
- package/gallery/fixtures/initiative-status-full.dgmo +46 -0
- package/gallery/fixtures/initiative-status-phases.dgmo +29 -0
- package/gallery/fixtures/initiative-status.dgmo +9 -0
- package/gallery/fixtures/line.dgmo +19 -0
- package/gallery/fixtures/multi-line.dgmo +11 -0
- package/gallery/fixtures/org-basic.dgmo +16 -0
- package/gallery/fixtures/org-full.dgmo +69 -0
- package/gallery/fixtures/org-teams.dgmo +25 -0
- package/gallery/fixtures/pie.dgmo +9 -0
- package/gallery/fixtures/polar-area.dgmo +8 -0
- package/gallery/fixtures/quadrant.dgmo +18 -0
- package/gallery/fixtures/radar.dgmo +8 -0
- package/gallery/fixtures/sankey.dgmo +31 -0
- package/gallery/fixtures/scatter.dgmo +21 -0
- package/gallery/fixtures/sequence-tags-protocols.dgmo +45 -0
- package/gallery/fixtures/sequence-tags.dgmo +41 -0
- package/gallery/fixtures/sequence.dgmo +35 -0
- package/gallery/fixtures/sitemap-basic.dgmo +12 -0
- package/gallery/fixtures/sitemap-full.dgmo +156 -0
- package/gallery/fixtures/slope.dgmo +8 -0
- package/gallery/fixtures/spr-eras.dgmo +62 -0
- package/gallery/fixtures/state.dgmo +30 -0
- package/gallery/fixtures/timeline-intraday.dgmo +14 -0
- package/gallery/fixtures/timeline.dgmo +32 -0
- package/gallery/fixtures/venn.dgmo +10 -0
- package/gallery/fixtures/wordcloud.dgmo +24 -0
- package/package.json +51 -2
- package/src/c4/layout.ts +372 -90
- package/src/c4/parser.ts +100 -55
- package/src/chart.ts +91 -28
- package/src/class/parser.ts +41 -12
- package/src/cli.ts +168 -61
- package/src/completion.ts +378 -183
- package/src/d3.ts +887 -288
- package/src/dgmo-mermaid.ts +16 -13
- package/src/dgmo-router.ts +69 -23
- package/src/echarts.ts +646 -153
- package/src/editor/dgmo.grammar +69 -0
- package/src/editor/dgmo.grammar.d.ts +2 -0
- package/src/editor/dgmo.grammar.js +18 -0
- package/src/editor/dgmo.grammar.terms.d.ts +5 -0
- package/src/editor/dgmo.grammar.terms.js +35 -0
- package/src/editor/highlight.ts +36 -0
- package/src/editor/index.ts +28 -0
- package/src/editor/keywords.ts +220 -0
- package/src/editor/tokens.ts +30 -0
- package/src/er/parser.ts +48 -14
- package/src/er/renderer.ts +112 -53
- package/src/gantt/calculator.ts +91 -29
- package/src/gantt/parser.ts +197 -71
- package/src/gantt/renderer.ts +1120 -350
- package/src/graph/flowchart-parser.ts +46 -25
- package/src/graph/state-parser.ts +47 -17
- package/src/infra/parser.ts +157 -53
- package/src/infra/renderer.ts +723 -271
- package/src/initiative-status/parser.ts +138 -44
- package/src/kanban/parser.ts +25 -14
- package/src/org/layout.ts +111 -44
- package/src/org/parser.ts +69 -22
- package/src/palettes/index.ts +3 -2
- package/src/sequence/parser.ts +193 -61
- package/src/sitemap/parser.ts +65 -29
- package/src/utils/arrows.ts +2 -22
- package/src/utils/duration.ts +39 -21
- package/src/utils/legend-constants.ts +0 -2
- package/src/utils/parsing.ts +75 -31
package/src/class/parser.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { resolveColor } from '../colors';
|
|
2
2
|
import type { PaletteColors } from '../palettes';
|
|
3
|
-
import { makeDgmoError, formatDgmoError
|
|
4
|
-
import {
|
|
3
|
+
import { makeDgmoError, formatDgmoError } from '../diagnostics';
|
|
4
|
+
import {
|
|
5
|
+
measureIndent,
|
|
6
|
+
parseFirstLine,
|
|
7
|
+
OPTION_NOCOLON_RE,
|
|
8
|
+
} from '../utils/parsing';
|
|
5
9
|
import type {
|
|
6
10
|
ParsedClassDiagram,
|
|
7
11
|
ClassNode,
|
|
@@ -35,11 +39,11 @@ const CLASS_DECL_RE =
|
|
|
35
39
|
// --|> TargetClass : label (colon-separated, kept for transition)
|
|
36
40
|
// Arrows: --|> ..|> *-- o-- ..> ->
|
|
37
41
|
const INDENT_REL_ARROW_RE =
|
|
38
|
-
/^(--\|>|\.\.\|>|\*--|o
|
|
42
|
+
/^(--\|>|\.\.\|>|\*--|o--|\.\.>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
|
|
39
43
|
|
|
40
44
|
// Legacy top-level relationship regex (used only for detection/rejection)
|
|
41
45
|
const REL_ARROW_RE =
|
|
42
|
-
/^([A-Z][A-Za-z0-9_]*)\s*(--\|>|\.\.\|>|\*--|o
|
|
46
|
+
/^([A-Z][A-Za-z0-9_]*)\s*(--\|>|\.\.\|>|\*--|o--|\.\.>|->)\s*([A-Z][A-Za-z0-9_]*)(?:\s+:?\s*(.+))?$/;
|
|
43
47
|
|
|
44
48
|
// Member line patterns
|
|
45
49
|
const VISIBILITY_RE = /^([+\-#])\s*/;
|
|
@@ -160,7 +164,8 @@ export function parseClassDiagram(
|
|
|
160
164
|
error: null,
|
|
161
165
|
};
|
|
162
166
|
|
|
163
|
-
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
168
|
+
const _fail = (line: number, message: string): ParsedClassDiagram => {
|
|
164
169
|
const diag = makeDgmoError(line, message);
|
|
165
170
|
result.diagnostics.push(diag);
|
|
166
171
|
result.error = formatDgmoError(diag);
|
|
@@ -281,8 +286,8 @@ export function parseClassDiagram(
|
|
|
281
286
|
makeDgmoError(
|
|
282
287
|
lineNumber,
|
|
283
288
|
`Relationship "${sourceName} ${arrow} ${targetName}" must be indented under the source class "${sourceName}"`,
|
|
284
|
-
'warning'
|
|
285
|
-
)
|
|
289
|
+
'warning'
|
|
290
|
+
)
|
|
286
291
|
);
|
|
287
292
|
continue;
|
|
288
293
|
}
|
|
@@ -319,17 +324,29 @@ export function parseClassDiagram(
|
|
|
319
324
|
currentClass = node;
|
|
320
325
|
continue;
|
|
321
326
|
}
|
|
327
|
+
|
|
328
|
+
// Catch-all: nothing matched this line
|
|
329
|
+
result.diagnostics.push(
|
|
330
|
+
makeDgmoError(lineNumber, `Unexpected line: '${trimmed}'.`, 'warning')
|
|
331
|
+
);
|
|
322
332
|
}
|
|
323
333
|
|
|
324
334
|
// Validation
|
|
325
335
|
if (result.classes.length === 0 && !result.error) {
|
|
326
|
-
const diag = makeDgmoError(
|
|
336
|
+
const diag = makeDgmoError(
|
|
337
|
+
1,
|
|
338
|
+
'No classes found. Add class declarations like "ClassName" or "ClassName [interface]".'
|
|
339
|
+
);
|
|
327
340
|
result.diagnostics.push(diag);
|
|
328
341
|
result.error = formatDgmoError(diag);
|
|
329
342
|
}
|
|
330
343
|
|
|
331
344
|
// Warn about isolated classes (not in any relationship)
|
|
332
|
-
if (
|
|
345
|
+
if (
|
|
346
|
+
result.classes.length >= 2 &&
|
|
347
|
+
result.relationships.length >= 1 &&
|
|
348
|
+
!result.error
|
|
349
|
+
) {
|
|
333
350
|
const connectedIds = new Set<string>();
|
|
334
351
|
for (const rel of result.relationships) {
|
|
335
352
|
connectedIds.add(rel.source);
|
|
@@ -337,7 +354,13 @@ export function parseClassDiagram(
|
|
|
337
354
|
}
|
|
338
355
|
for (const cls of result.classes) {
|
|
339
356
|
if (!connectedIds.has(cls.id)) {
|
|
340
|
-
result.diagnostics.push(
|
|
357
|
+
result.diagnostics.push(
|
|
358
|
+
makeDgmoError(
|
|
359
|
+
cls.lineNumber,
|
|
360
|
+
`Class "${cls.name}" is not connected to any other class`,
|
|
361
|
+
'warning'
|
|
362
|
+
)
|
|
363
|
+
);
|
|
341
364
|
}
|
|
342
365
|
}
|
|
343
366
|
}
|
|
@@ -380,7 +403,9 @@ export function looksLikeClassDiagram(content: string): boolean {
|
|
|
380
403
|
hasClassDecl = true;
|
|
381
404
|
}
|
|
382
405
|
// Check for old modifier pattern: ClassName [abstract|interface|enum]
|
|
383
|
-
if (
|
|
406
|
+
if (
|
|
407
|
+
/^[A-Z][A-Za-z0-9_]*\s+\[(abstract|interface|enum)\]/i.test(trimmed)
|
|
408
|
+
) {
|
|
384
409
|
hasModifier = true;
|
|
385
410
|
hasClassDecl = true;
|
|
386
411
|
}
|
|
@@ -435,7 +460,11 @@ export function extractSymbols(docText: string): DiagramSymbols {
|
|
|
435
460
|
for (const rawLine of docText.split('\n')) {
|
|
436
461
|
const line = rawLine.trim();
|
|
437
462
|
// Skip old-style colon metadata and new-style first line / space-separated options
|
|
438
|
-
if (
|
|
463
|
+
if (
|
|
464
|
+
inMetadata &&
|
|
465
|
+
(/^[a-z-]+\s*:/i.test(line) || /^class(\s|$)/i.test(line))
|
|
466
|
+
)
|
|
467
|
+
continue;
|
|
439
468
|
if (inMetadata && line.toLowerCase() === 'no-auto-color') continue;
|
|
440
469
|
if (inMetadata && /^[a-z]/.test(line) && OPTION_NOCOLON_RE.test(line)) {
|
|
441
470
|
const key = line.match(OPTION_NOCOLON_RE)![1].toLowerCase();
|
package/src/cli.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { execSync } from 'node:child_process';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
@@ -47,7 +48,8 @@ const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
|
47
48
|
org: 'Org chart — hierarchical tree structures',
|
|
48
49
|
kanban: 'Kanban board — task/workflow columns',
|
|
49
50
|
c4: 'C4 diagram — system architecture (context, container, component, deployment)',
|
|
50
|
-
'initiative-status':
|
|
51
|
+
'initiative-status':
|
|
52
|
+
'Initiative status — project roadmap with dependency tracking',
|
|
51
53
|
infra: 'Infra chart — infrastructure traffic flow with rps computation',
|
|
52
54
|
};
|
|
53
55
|
|
|
@@ -554,7 +556,11 @@ function parseArgs(argv: string[]): {
|
|
|
554
556
|
installClaudeSkill: false,
|
|
555
557
|
installClaudeCodeIntegration: false,
|
|
556
558
|
installCodexIntegration: false,
|
|
557
|
-
c4Level: 'context' as
|
|
559
|
+
c4Level: 'context' as
|
|
560
|
+
| 'context'
|
|
561
|
+
| 'containers'
|
|
562
|
+
| 'components'
|
|
563
|
+
| 'deployment',
|
|
558
564
|
c4System: undefined as string | undefined,
|
|
559
565
|
c4Container: undefined as string | undefined,
|
|
560
566
|
tagGroup: undefined as string | undefined,
|
|
@@ -597,7 +603,12 @@ function parseArgs(argv: string[]): {
|
|
|
597
603
|
i++;
|
|
598
604
|
} else if (arg === '--c4-level') {
|
|
599
605
|
const val = args[++i];
|
|
600
|
-
if (
|
|
606
|
+
if (
|
|
607
|
+
val !== 'context' &&
|
|
608
|
+
val !== 'containers' &&
|
|
609
|
+
val !== 'components' &&
|
|
610
|
+
val !== 'deployment'
|
|
611
|
+
) {
|
|
601
612
|
console.error(
|
|
602
613
|
`Error: Invalid C4 level "${val}". Valid levels: context, containers, components, deployment`
|
|
603
614
|
);
|
|
@@ -745,8 +756,14 @@ async function main(): Promise<void> {
|
|
|
745
756
|
|
|
746
757
|
function ask(prompt: string): Promise<string> {
|
|
747
758
|
return new Promise((resolve) => {
|
|
748
|
-
const rl = createInterface({
|
|
749
|
-
|
|
759
|
+
const rl = createInterface({
|
|
760
|
+
input: process.stdin,
|
|
761
|
+
output: process.stdout,
|
|
762
|
+
});
|
|
763
|
+
rl.question(prompt, (answer) => {
|
|
764
|
+
rl.close();
|
|
765
|
+
resolve(answer);
|
|
766
|
+
});
|
|
750
767
|
});
|
|
751
768
|
}
|
|
752
769
|
|
|
@@ -756,7 +773,9 @@ async function main(): Promise<void> {
|
|
|
756
773
|
const skillExists = existsSync(skillPath);
|
|
757
774
|
let installSkill = true;
|
|
758
775
|
if (skillExists) {
|
|
759
|
-
const ans = await ask(
|
|
776
|
+
const ans = await ask(
|
|
777
|
+
'~/.claude/commands/dgmo.md already exists. Overwrite? [y/N] '
|
|
778
|
+
);
|
|
760
779
|
installSkill = ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
761
780
|
}
|
|
762
781
|
if (installSkill) {
|
|
@@ -769,16 +788,26 @@ async function main(): Promise<void> {
|
|
|
769
788
|
|
|
770
789
|
// --- Step 2: Check / install dgmo-mcp binary ---
|
|
771
790
|
let dgmoMcpInstalled = false;
|
|
772
|
-
try {
|
|
791
|
+
try {
|
|
792
|
+
execSync('which dgmo-mcp', { stdio: 'pipe' });
|
|
793
|
+
dgmoMcpInstalled = true;
|
|
794
|
+
} catch {
|
|
795
|
+
/* not found */
|
|
796
|
+
}
|
|
773
797
|
if (!dgmoMcpInstalled) {
|
|
774
|
-
const ans = await ask(
|
|
775
|
-
|
|
798
|
+
const ans = await ask(
|
|
799
|
+
'\ndgmo-mcp not found. Install @diagrammo/dgmo-mcp globally now? [Y/n] '
|
|
800
|
+
);
|
|
801
|
+
const yes =
|
|
802
|
+
ans === '' || ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
776
803
|
if (yes) {
|
|
777
804
|
console.log('Installing @diagrammo/dgmo-mcp...');
|
|
778
805
|
execSync('npm install -g @diagrammo/dgmo-mcp', { stdio: 'inherit' });
|
|
779
806
|
console.log('✓ @diagrammo/dgmo-mcp installed');
|
|
780
807
|
} else {
|
|
781
|
-
console.log(
|
|
808
|
+
console.log(
|
|
809
|
+
' Skipped. Install later with: npm install -g @diagrammo/dgmo-mcp'
|
|
810
|
+
);
|
|
782
811
|
}
|
|
783
812
|
} else {
|
|
784
813
|
console.log('✓ dgmo-mcp already installed');
|
|
@@ -787,7 +816,9 @@ async function main(): Promise<void> {
|
|
|
787
816
|
// --- Step 3: Configure MCP server ---
|
|
788
817
|
console.log('\nWhere should the MCP server be configured?');
|
|
789
818
|
console.log(' 1) This project only — write .mcp.json here [default]');
|
|
790
|
-
console.log(
|
|
819
|
+
console.log(
|
|
820
|
+
' 2) Globally — add to ~/.claude/settings.json (works in all projects)'
|
|
821
|
+
);
|
|
791
822
|
const scopeAns = await ask('\nChoice [1]: ');
|
|
792
823
|
const useGlobal = scopeAns.trim() === '2';
|
|
793
824
|
const mcpEntry = { command: 'dgmo-mcp' };
|
|
@@ -796,24 +827,40 @@ async function main(): Promise<void> {
|
|
|
796
827
|
const settingsPath = join(claudeDir, 'settings.json');
|
|
797
828
|
let settings: Record<string, unknown> = {};
|
|
798
829
|
if (existsSync(settingsPath)) {
|
|
799
|
-
try {
|
|
830
|
+
try {
|
|
831
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
832
|
+
} catch {
|
|
833
|
+
/* use empty */
|
|
834
|
+
}
|
|
800
835
|
}
|
|
801
|
-
const mcpServers =
|
|
836
|
+
const mcpServers =
|
|
837
|
+
(settings.mcpServers as Record<string, unknown> | undefined) ?? {};
|
|
802
838
|
mcpServers['dgmo'] = mcpEntry;
|
|
803
839
|
settings.mcpServers = mcpServers;
|
|
804
|
-
writeFileSync(
|
|
840
|
+
writeFileSync(
|
|
841
|
+
settingsPath,
|
|
842
|
+
JSON.stringify(settings, null, 2) + '\n',
|
|
843
|
+
'utf-8'
|
|
844
|
+
);
|
|
805
845
|
console.log('✓ MCP server added to ~/.claude/settings.json');
|
|
806
846
|
} else {
|
|
807
847
|
const mcpPath = join(process.cwd(), '.mcp.json');
|
|
808
848
|
let mcp: Record<string, unknown> = {};
|
|
809
849
|
if (existsSync(mcpPath)) {
|
|
810
|
-
try {
|
|
850
|
+
try {
|
|
851
|
+
mcp = JSON.parse(readFileSync(mcpPath, 'utf-8'));
|
|
852
|
+
} catch {
|
|
853
|
+
/* use empty */
|
|
854
|
+
}
|
|
811
855
|
}
|
|
812
|
-
const mcpServers =
|
|
856
|
+
const mcpServers =
|
|
857
|
+
(mcp.mcpServers as Record<string, unknown> | undefined) ?? {};
|
|
813
858
|
mcpServers['dgmo'] = mcpEntry;
|
|
814
859
|
mcp.mcpServers = mcpServers;
|
|
815
860
|
writeFileSync(mcpPath, JSON.stringify(mcp, null, 2) + '\n', 'utf-8');
|
|
816
|
-
console.log(
|
|
861
|
+
console.log(
|
|
862
|
+
`✓ MCP server configured: ${join(process.cwd(), '.mcp.json')}`
|
|
863
|
+
);
|
|
817
864
|
}
|
|
818
865
|
|
|
819
866
|
console.log('\nRestart Claude Code to activate the MCP server.');
|
|
@@ -835,12 +882,17 @@ async function main(): Promise<void> {
|
|
|
835
882
|
? `~/.claude/commands/dgmo.md already exists. Overwrite? [y/N] `
|
|
836
883
|
: `Install dgmo Claude Code skill to ~/.claude/commands/dgmo.md? [Y/n] `;
|
|
837
884
|
await new Promise<void>((done) => {
|
|
838
|
-
const rl = createInterface({
|
|
885
|
+
const rl = createInterface({
|
|
886
|
+
input: process.stdin,
|
|
887
|
+
output: process.stdout,
|
|
888
|
+
});
|
|
839
889
|
rl.question(prompt, (answer) => {
|
|
840
890
|
rl.close();
|
|
841
891
|
const yes = alreadyExists
|
|
842
892
|
? answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'
|
|
843
|
-
: answer === '' ||
|
|
893
|
+
: answer === '' ||
|
|
894
|
+
answer.toLowerCase() === 'y' ||
|
|
895
|
+
answer.toLowerCase() === 'yes';
|
|
844
896
|
if (!yes) {
|
|
845
897
|
console.error('Aborted.');
|
|
846
898
|
process.exit(0);
|
|
@@ -859,23 +911,41 @@ async function main(): Promise<void> {
|
|
|
859
911
|
|
|
860
912
|
if (opts.installCodexIntegration) {
|
|
861
913
|
// Validate Codex CLI is installed
|
|
862
|
-
try {
|
|
863
|
-
|
|
914
|
+
try {
|
|
915
|
+
execSync('which codex', { stdio: 'pipe' });
|
|
916
|
+
} catch {
|
|
917
|
+
console.error(
|
|
918
|
+
'codex not found. Install Codex CLI first: https://openai.com/codex'
|
|
919
|
+
);
|
|
864
920
|
process.exit(1);
|
|
865
921
|
}
|
|
866
922
|
|
|
867
923
|
const ask = (prompt: string): Promise<string> =>
|
|
868
924
|
new Promise((resolve) => {
|
|
869
|
-
const rl = createInterface({
|
|
870
|
-
|
|
925
|
+
const rl = createInterface({
|
|
926
|
+
input: process.stdin,
|
|
927
|
+
output: process.stdout,
|
|
928
|
+
});
|
|
929
|
+
rl.question(prompt, (answer) => {
|
|
930
|
+
rl.close();
|
|
931
|
+
resolve(answer);
|
|
932
|
+
});
|
|
871
933
|
});
|
|
872
934
|
|
|
873
935
|
// Check / install dgmo-mcp binary
|
|
874
936
|
let dgmoMcpInstalled = false;
|
|
875
|
-
try {
|
|
937
|
+
try {
|
|
938
|
+
execSync('which dgmo-mcp', { stdio: 'pipe' });
|
|
939
|
+
dgmoMcpInstalled = true;
|
|
940
|
+
} catch {
|
|
941
|
+
/* not found */
|
|
942
|
+
}
|
|
876
943
|
if (!dgmoMcpInstalled) {
|
|
877
|
-
const ans = await ask(
|
|
878
|
-
|
|
944
|
+
const ans = await ask(
|
|
945
|
+
'\ndgmo-mcp not found. Install @diagrammo/dgmo-mcp globally now? [Y/n] '
|
|
946
|
+
);
|
|
947
|
+
const yes =
|
|
948
|
+
ans === '' || ans.toLowerCase() === 'y' || ans.toLowerCase() === 'yes';
|
|
879
949
|
if (yes) {
|
|
880
950
|
console.log('Installing @diagrammo/dgmo-mcp...');
|
|
881
951
|
try {
|
|
@@ -886,7 +956,9 @@ async function main(): Promise<void> {
|
|
|
886
956
|
console.error('Try manually: npm install -g @diagrammo/dgmo-mcp');
|
|
887
957
|
}
|
|
888
958
|
} else {
|
|
889
|
-
console.log(
|
|
959
|
+
console.log(
|
|
960
|
+
' Skipped. Install later with: npm install -g @diagrammo/dgmo-mcp'
|
|
961
|
+
);
|
|
890
962
|
}
|
|
891
963
|
} else {
|
|
892
964
|
console.log('✓ dgmo-mcp already installed');
|
|
@@ -894,11 +966,21 @@ async function main(): Promise<void> {
|
|
|
894
966
|
|
|
895
967
|
// Configure MCP server
|
|
896
968
|
console.log('\nWhere should the MCP server be configured?');
|
|
897
|
-
console.log(
|
|
898
|
-
|
|
969
|
+
console.log(
|
|
970
|
+
' 1) This project only — write .codex/config.toml here [default]'
|
|
971
|
+
);
|
|
972
|
+
console.log(
|
|
973
|
+
' 2) Globally — add to ~/.codex/config.toml (works in all projects)'
|
|
974
|
+
);
|
|
899
975
|
const scopeAns = await ask('\nChoice [1]: ');
|
|
900
|
-
if (
|
|
901
|
-
|
|
976
|
+
if (
|
|
977
|
+
scopeAns.trim() !== '' &&
|
|
978
|
+
scopeAns.trim() !== '1' &&
|
|
979
|
+
scopeAns.trim() !== '2'
|
|
980
|
+
) {
|
|
981
|
+
console.log(
|
|
982
|
+
` Unrecognized input "${scopeAns.trim()}", defaulting to option 1.`
|
|
983
|
+
);
|
|
902
984
|
}
|
|
903
985
|
const useGlobal = scopeAns.trim() === '2';
|
|
904
986
|
const tomlEntry = '[mcp_servers.dgmo]\ncommand = ["dgmo-mcp"]\n';
|
|
@@ -906,7 +988,9 @@ async function main(): Promise<void> {
|
|
|
906
988
|
if (useGlobal) {
|
|
907
989
|
const configPath = join(homedir(), '.codex', 'config.toml');
|
|
908
990
|
mkdirSync(join(homedir(), '.codex'), { recursive: true });
|
|
909
|
-
const existing = existsSync(configPath)
|
|
991
|
+
const existing = existsSync(configPath)
|
|
992
|
+
? readFileSync(configPath, 'utf-8')
|
|
993
|
+
: '';
|
|
910
994
|
if (existing.includes('[mcp_servers.dgmo]')) {
|
|
911
995
|
console.log('✓ MCP server already configured in ~/.codex/config.toml');
|
|
912
996
|
} else {
|
|
@@ -918,7 +1002,9 @@ async function main(): Promise<void> {
|
|
|
918
1002
|
const codexDir = join(process.cwd(), '.codex');
|
|
919
1003
|
const configPath = join(codexDir, 'config.toml');
|
|
920
1004
|
mkdirSync(codexDir, { recursive: true });
|
|
921
|
-
const existing = existsSync(configPath)
|
|
1005
|
+
const existing = existsSync(configPath)
|
|
1006
|
+
? readFileSync(configPath, 'utf-8')
|
|
1007
|
+
: '';
|
|
922
1008
|
if (existing.includes('[mcp_servers.dgmo]')) {
|
|
923
1009
|
console.log(`✓ MCP server already configured in .codex/config.toml`);
|
|
924
1010
|
} else {
|
|
@@ -983,10 +1069,8 @@ async function main(): Promise<void> {
|
|
|
983
1069
|
// Resolve org chart imports (tags and import directives)
|
|
984
1070
|
if (opts.input && parseDgmoChartType(content) === 'org') {
|
|
985
1071
|
const inputPath = resolve(opts.input);
|
|
986
|
-
const resolved = await resolveOrgImports(
|
|
987
|
-
|
|
988
|
-
inputPath,
|
|
989
|
-
(p) => readFileSync(p, 'utf-8'),
|
|
1072
|
+
const resolved = await resolveOrgImports(content, inputPath, (p) =>
|
|
1073
|
+
readFileSync(p, 'utf-8')
|
|
990
1074
|
);
|
|
991
1075
|
for (const diag of resolved.diagnostics) {
|
|
992
1076
|
console.error(formatDgmoError(diag));
|
|
@@ -1008,12 +1092,18 @@ async function main(): Promise<void> {
|
|
|
1008
1092
|
// Helper for JSON error output
|
|
1009
1093
|
function exitWithJsonError(error: string, line?: number): never {
|
|
1010
1094
|
if (opts.json) {
|
|
1011
|
-
process.stdout.write(
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1095
|
+
process.stdout.write(
|
|
1096
|
+
JSON.stringify(
|
|
1097
|
+
{
|
|
1098
|
+
success: false,
|
|
1099
|
+
error,
|
|
1100
|
+
...(line != null ? { line } : {}),
|
|
1101
|
+
...(chartType ? { chartType } : {}),
|
|
1102
|
+
},
|
|
1103
|
+
null,
|
|
1104
|
+
2
|
|
1105
|
+
) + '\n'
|
|
1106
|
+
);
|
|
1017
1107
|
} else {
|
|
1018
1108
|
console.error(error);
|
|
1019
1109
|
}
|
|
@@ -1046,18 +1136,26 @@ async function main(): Promise<void> {
|
|
|
1046
1136
|
}
|
|
1047
1137
|
|
|
1048
1138
|
if (opts.json) {
|
|
1049
|
-
process.stdout.write(
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1139
|
+
process.stdout.write(
|
|
1140
|
+
JSON.stringify(
|
|
1141
|
+
{
|
|
1142
|
+
success: true,
|
|
1143
|
+
url: result.url,
|
|
1144
|
+
...(chartType ? { chartType } : {}),
|
|
1145
|
+
},
|
|
1146
|
+
null,
|
|
1147
|
+
2
|
|
1148
|
+
) + '\n'
|
|
1149
|
+
);
|
|
1054
1150
|
} else {
|
|
1055
1151
|
process.stdout.write(result.url + '\n');
|
|
1056
1152
|
}
|
|
1057
1153
|
return;
|
|
1058
1154
|
}
|
|
1059
1155
|
|
|
1060
|
-
const paletteColors = getPalette(opts.palette)[
|
|
1156
|
+
const paletteColors = getPalette(opts.palette)[
|
|
1157
|
+
opts.theme === 'dark' ? 'dark' : 'light'
|
|
1158
|
+
];
|
|
1061
1159
|
|
|
1062
1160
|
// Word clouds require Canvas APIs (HTMLCanvasElement.getContext('2d'))
|
|
1063
1161
|
// which are unavailable in Node.js — check before attempting render.
|
|
@@ -1084,10 +1182,7 @@ async function main(): Promise<void> {
|
|
|
1084
1182
|
if (errors.length > 0) {
|
|
1085
1183
|
if (opts.json) {
|
|
1086
1184
|
const firstError = errors[0];
|
|
1087
|
-
exitWithJsonError(
|
|
1088
|
-
formatDgmoError(firstError),
|
|
1089
|
-
firstError.line,
|
|
1090
|
-
);
|
|
1185
|
+
exitWithJsonError(formatDgmoError(firstError), firstError.line);
|
|
1091
1186
|
}
|
|
1092
1187
|
for (const e of errors) {
|
|
1093
1188
|
console.error(`\u2716 ${formatDgmoError(e)}`);
|
|
@@ -1096,14 +1191,20 @@ async function main(): Promise<void> {
|
|
|
1096
1191
|
|
|
1097
1192
|
// Validate C4 options
|
|
1098
1193
|
if (opts.c4Level === 'containers' && !opts.c4System) {
|
|
1099
|
-
exitWithJsonError(
|
|
1194
|
+
exitWithJsonError(
|
|
1195
|
+
'Error: --c4-system is required when --c4-level is containers'
|
|
1196
|
+
);
|
|
1100
1197
|
}
|
|
1101
1198
|
if (opts.c4Level === 'components') {
|
|
1102
1199
|
if (!opts.c4System) {
|
|
1103
|
-
exitWithJsonError(
|
|
1200
|
+
exitWithJsonError(
|
|
1201
|
+
'Error: --c4-system is required when --c4-level is components'
|
|
1202
|
+
);
|
|
1104
1203
|
}
|
|
1105
1204
|
if (!opts.c4Container) {
|
|
1106
|
-
exitWithJsonError(
|
|
1205
|
+
exitWithJsonError(
|
|
1206
|
+
'Error: --c4-container is required when --c4-level is components'
|
|
1207
|
+
);
|
|
1107
1208
|
}
|
|
1108
1209
|
}
|
|
1109
1210
|
|
|
@@ -1143,11 +1244,17 @@ async function main(): Promise<void> {
|
|
|
1143
1244
|
outputPath = resolve(`${inputBasename}.png`);
|
|
1144
1245
|
writeFileSync(outputPath, svgToPng(svg, pngBg));
|
|
1145
1246
|
}
|
|
1146
|
-
process.stdout.write(
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1247
|
+
process.stdout.write(
|
|
1248
|
+
JSON.stringify(
|
|
1249
|
+
{
|
|
1250
|
+
success: true,
|
|
1251
|
+
...(outputPath ? { output: outputPath } : {}),
|
|
1252
|
+
...(chartType ? { chartType } : {}),
|
|
1253
|
+
},
|
|
1254
|
+
null,
|
|
1255
|
+
2
|
|
1256
|
+
) + '\n'
|
|
1257
|
+
);
|
|
1151
1258
|
} else if (opts.output) {
|
|
1152
1259
|
// Explicit output path
|
|
1153
1260
|
const outputPath = resolve(opts.output);
|