@ddtcorex/dsh-maestro-diagram 0.1.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.
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # dsh-maestro-diagram — Maestro Diagram Studio (Host-only)
2
+
3
+ Hybrid **skill + plugin** for SA-grade diagrams: **GitHub-native Mermaid** (single source of truth) + **editorial HTML+SVG** (self-contained, no JS) + **deterministic verify/drift** (tool calls > LLM).
4
+
5
+ - **Skill:** `maestro-skills/skills/diagram-studio/SKILL.md` (rank 350) — teaches the agent when/how to draw, selects among 5 Mermaid types, enforces editorial discipline (density 4/10, accent 1-2), and decides audience (Team vs Client).
6
+ - **Plugin:** `packages/dsh-maestro-diagram` (this package, Host-only, `rootDir: src/host`, `inject: ['sessions','tools']`) — exposes `mermaid_verify` and `mermaid_drift` as reversible `ctx.tools.register` effects. No Client bundle on v1.
7
+
8
+ Inspired by `cathrynlavery/diagram-design` (MIT, 39 editorial types, tokens paper/ink/accent) + `diagram-drift` + `dsh-mermaid`. Default output stays GitHub Mermaid (per "render được trên github là ok"); HTML editorial is an explicit `export: html` when `audience: client`.
9
+
10
+ ## Installation
11
+
12
+ ```sh
13
+ # As DSH plugin (Host-only, no restart of skill provider needed, but Host needs reload for tools)
14
+ dsh plugin add @ddtcorex/dsh-maestro-diagram
15
+ # Or via the thin meta-bundle (opt-in until proven, not in meta v2 yet)
16
+ dsh plugin add @ddtcorex/dsh-maestro-meta
17
+ ```
18
+
19
+ For skill-only use (no Host tools): `git pull` + `pnpm --dir maestro-skills run build` — then `node maestro-skills/skills/diagram-studio/scripts/verify-mermaid.mjs <file>`.
20
+
21
+ ## Tools
22
+
23
+ ### `mermaid_verify`
24
+
25
+ ```
26
+ input: string (Mermaid source or file content when isPath=true)
27
+ isPath: boolean? (if true, input is treated as file path, extracts ```mermaid blocks)
28
+ strict: boolean? (if true, warns on anti-patterns: shadow, graph legacy, rounded-2xl)
29
+ → { ok: boolean, errors: {line,col,msg}[], warnings: {msg}[] }
30
+ ```
31
+
32
+ Deterministic `mermaid.parse()` + optional `mermaid-cli` validate. Never throws — returns `isError` shape for the agent to fix.
33
+
34
+ ### `mermaid_drift`
35
+
36
+ ```
37
+ diagramPath: string (e.g. docs/architecture.md)
38
+ codeRoots: string[]? (default ["packages/*","govard","maestro-skills"])
39
+ → { missingInCode: string[], staleEdges: {from,to}[], missingInDiagram: string[], summary: string }
40
+ ```
41
+
42
+ Parses Mermaid nodes/edges vs scans `codeRoots` (package.json names, `govard/internal/*`, skills). Inspired by `diagram-drift` — flags `missingInCode / staleEdges / missingInDiagram` before PR.
43
+
44
+ ## Supported Cases (all verified — see SKILL.md § Supported Cases)
45
+
46
+ **5 diagram types** (mapped from 39 editorial types):
47
+ - `flowchart TB/LR` — Components + connections (architecture) — `docs/architecture.md §1.1`
48
+ - `sequenceDiagram` — Messages over time (turn lifecycle) — `docs/specs/2026-08-27-harness-turn-flow-sequence.md`
49
+ - `classDiagram` — Classes + ops (`ReviewProvider <|-- GitLabProvider`)
50
+ - `erDiagram` — Entities + fields (`PROJECT ||--o{ MEMORY`)
51
+ - `stateDiagram` / `stateDiagram-v2` — States + guards (`[*] --> queued`)
52
+
53
+ All share tokens `paper #f5f5f5 / ink #2d3142 / accent #eb6c36 / muted #8a94a6 / link #4a90e2` from `references/style-guide.md` (`classDef focal/muted`, no shadow, rx:6).
54
+
55
+ **2 audiences** (controls 4 elements per HTML):
56
+ - **Team / Internal** (`team|internal|engineering` or "cho team") → HTML has both: inline SVG + `<details><summary>Mermaid source</summary><pre>` collapsed + Editorial tokens card + About footer with verify/drift. Example: `harness-architecture.html` 16K (1 svg,1 pre), `harness-turn-flow-sequence.html` 31K.
57
+ - **Client / External** (`client|pitch|deck` or "cho khách") → HTML has only SVG, no `<pre>`, no tokens card, footer reduced to `Generated via diagram-studio — 2026-08-27`. Example: `...-client.html` 12K/30K (1 svg,0 pre), PNG 124K/65K.
58
+
59
+ **3 outputs:**
60
+ - GitHub-native Mermaid in `docs/architecture.md` / `docs/specs/*-design.md` (always show source)
61
+ - Editorial HTML `docs/diagrams/<slug>.html` (self-contained inline SVG/CSS, no JS) — Team vs Client per table
62
+ - Deck PDF `docs/diagrams/maestro-harness-deck.pdf` (A4 landscape, 3 pages) — always Client rules, PNG only
63
+
64
+ **3 verification cases:**
65
+ - Parse ok → 5/5 PASS
66
+ - Parse fail (empty, `A-->`) → `ok:false, line:2`
67
+ - Anti-pattern strict (`shadow:true`) → `warnings:1`
68
+ - Drift `missingInCode 0` / missing file throws `ENOENT`
69
+
70
+ **2 live case studies on this harness:**
71
+ - Architecture flowchart (10 plugins + meta) — `harness-architecture.html` 16K → `...-client.html` 12K — PNG 238K→124K — Deck p1
72
+ - Turn flow sequence (6 participants) — `harness-turn-flow-sequence.html` 31K (svg 28K via `mermaid-cli 11.16.0`) → `...-client.html` 30K — PNG 100K→65K + `...-rendered.png` 20K — Deck p2
73
+
74
+ All above are live-verified: `packages/dsh-maestro-diagram` 8/8, `maestro-workspace -r verify` 13 packages Done, chrome headless 980×1400 screenshots, `pdfinfo Pages:3`.
75
+
76
+ ## Usage (agent)
77
+
78
+ 1. Load `diagram-studio` skill — it selects `flowchart` vs `sequenceDiagram` vs `classDiagram` vs `erDiagram` vs `stateDiagram` by semantic pattern.
79
+ 2. State `type, size (85%/100%), what will be cut due to budget (density 4/10)` and wait for redirect (confirm-before-drawing).
80
+ 3. Write Mermaid to `docs/specs/*-design.md` (or `docs/architecture.md §1.1`), call `mermaid_verify` — fix until `ok:true`.
81
+ 4. If `audience: client`, also render editorial HTML: `mermaid-cli -i <src>.mmd -o <slug>.svg` → embed inline SVG into `docs/diagrams/<slug>.html` per audience table, then `chrome --screenshot` → PNG and `--print-to-pdf` → deck. Hide source/tokens/footer for client.
82
+ 5. Before PR, run `mermaid_drift --diagram docs/architecture.md --roots packages/*,govard,maestro-skills` — fix `missingInCode` by patching doc or code.
83
+
84
+ CLI fallback (no plugin): `node maestro-skills/skills/diagram-studio/scripts/verify-mermaid.mjs docs/architecture.md`
85
+
86
+ ## Build & Verify
87
+
88
+ ```sh
89
+ pnpm --dir packages/dsh-maestro-diagram run build # must create lib/index.js flat (rootDir: src/host)
90
+ pnpm --dir packages/dsh-maestro-diagram run test # 8/8 (5 verify + 3 drift)
91
+ pnpm --dir packages/dsh-maestro-diagram run verify # tsc --noEmit
92
+ pnpm --dir maestro-skills run build # skill provider
93
+ pnpm --dir maestro-workspace -r verify # 13 packages Done
94
+ ```
95
+
96
+ `lib/index.js` flat is required — `test -f lib/index.js` must be `0` (not `lib/host/index.js`), otherwise `dsh web` boot fails `ERR_MODULE_NOT_FOUND`. Dry-boot on ephemeral port before any real restart.
97
+
98
+ ## Publishing
99
+
100
+ This package is public (`private:false`, `workspace:^` deps). Publish with `pnpm publish --access public` only — never `npm publish` (would leave `workspace:` in tarball). See `maestro-skills/README.md` for skill distribution and `docs/specs/2026-08-27-diagram-studio-design.md` for design.
101
+
102
+ ## License
103
+
104
+ MIT — see `LICENSE`. Editorial tokens/style-guide trimmed from `cathrynlavery/diagram-design` (MIT) with attribution in `maestro-skills/skills/diagram-studio/references/diagram-design-learnings.md`.
@@ -0,0 +1,5 @@
1
+ # dsh-maestro-diagram bundle patch — Host-only plugin, inject sessions+tools.
2
+ - insert:
3
+ - id: dsh-maestro-diagram
4
+ name: '@ddtcorex/dsh-maestro-diagram'
5
+ inject: ['sessions', 'tools']
package/lib/drift.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ export declare function parseMermaid(src: string): {
2
+ nodes: string[];
3
+ edges: {
4
+ from: string;
5
+ to: string;
6
+ }[];
7
+ };
8
+ export declare function scanCode(roots: string[]): Promise<string[]>;
9
+ export declare function mermaidDrift(diagramPath: string, codeRoots?: string[]): Promise<{
10
+ missingInCode: string[];
11
+ staleEdges: {
12
+ from: string;
13
+ to: string;
14
+ }[];
15
+ missingInDiagram: string[];
16
+ summary: string;
17
+ }>;
18
+ //# sourceMappingURL=drift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.d.ts","sourceRoot":"","sources":["../src/host/drift.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAoDpG;AAwBD,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4IjE;AAED,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAsE/H"}
package/lib/drift.js ADDED
@@ -0,0 +1,312 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ export function parseMermaid(src) {
4
+ const blocks = [...src.matchAll(/```mermaid\n([\s\S]*?)```/g)].map((m) => m[1]);
5
+ const content = blocks.length ? blocks.join('\n') : src;
6
+ const nodesSet = new Set();
7
+ const edges = [];
8
+ // Collect subgraph ids to exclude them from node set (they are layout containers, not code symbols)
9
+ const subgraphIds = new Set();
10
+ const subgraphRe = /subgraph\s+(\w+)/g;
11
+ let sm;
12
+ while ((sm = subgraphRe.exec(content)) !== null) {
13
+ subgraphIds.add(sm[1]);
14
+ }
15
+ const bracketRe = /(\w+)\s*\["/g;
16
+ let m;
17
+ while ((m = bracketRe.exec(content)) !== null) {
18
+ const id = m[1];
19
+ if (subgraphIds.has(id))
20
+ continue; // skip subgraph container ids (Core, OpsStudio, etc.)
21
+ if (/^pad/i.test(id))
22
+ continue; // spacer nodes for header top gap — not code symbols
23
+ if (/\bspacer\b/i.test(content.slice(m.index, m.index + 200))) {
24
+ // check if this node is defined with :::spacer class nearby — skip spacer nodes
25
+ const snippet = content.slice(m.index, m.index + 300);
26
+ if (/:::spacer/.test(snippet))
27
+ continue;
28
+ }
29
+ nodesSet.add(id);
30
+ }
31
+ const edgeRe = /(\w+)\s*(?:-->|---|-\.->|--->)\s*(\w+)/g;
32
+ while ((m = edgeRe.exec(content)) !== null) {
33
+ const from = m[1];
34
+ const to = m[2];
35
+ if (!subgraphIds.has(from) && !/^pad/i.test(from))
36
+ nodesSet.add(from);
37
+ if (!subgraphIds.has(to) && !/^pad/i.test(to))
38
+ nodesSet.add(to);
39
+ edges.push({ from, to });
40
+ }
41
+ // fallback generic arrow capture for nodes not yet caught (keep simple heuristic)
42
+ const leftArrowRe = /(\w+)\s*-->/g;
43
+ while ((m = leftArrowRe.exec(content)) !== null) {
44
+ if (!subgraphIds.has(m[1]) && !/^pad/i.test(m[1]))
45
+ nodesSet.add(m[1]);
46
+ }
47
+ const rightArrowRe = /-->\s*(\w+)/g;
48
+ while ((m = rightArrowRe.exec(content)) !== null) {
49
+ if (!subgraphIds.has(m[1]) && !/^pad/i.test(m[1]))
50
+ nodesSet.add(m[1]);
51
+ }
52
+ // also handle -- and -.-> generically if not already captured
53
+ const genericEdgeRe = /(\w+)\s*(?:-->|---|-\.->)\s*(\w+)/g;
54
+ // already covered, but keep for completeness
55
+ return { nodes: [...nodesSet], edges };
56
+ }
57
+ function resolvePath(p) {
58
+ if (path.isAbsolute(p)) {
59
+ try {
60
+ if (fs.existsSync(p))
61
+ return p;
62
+ }
63
+ catch { }
64
+ return null;
65
+ }
66
+ const candidates = [];
67
+ candidates.push(path.resolve(process.cwd(), p));
68
+ let dir = process.cwd();
69
+ for (let i = 0; i < 5; i++) {
70
+ dir = path.dirname(dir);
71
+ candidates.push(path.join(dir, p));
72
+ }
73
+ for (const c of candidates) {
74
+ try {
75
+ if (fs.existsSync(c))
76
+ return c;
77
+ }
78
+ catch { }
79
+ }
80
+ return null;
81
+ }
82
+ export async function scanCode(roots) {
83
+ const symbols = [];
84
+ const seen = new Set();
85
+ function add(sym) {
86
+ if (!sym)
87
+ return;
88
+ const lower = sym.toLowerCase();
89
+ if (!seen.has(lower)) {
90
+ seen.add(lower);
91
+ symbols.push(lower);
92
+ }
93
+ // also add stripped variant without prefix
94
+ const stripped = lower.replace(/^@ddtcorex\//, '').replace(/^dsh-maestro-/, '');
95
+ if (stripped !== lower && !seen.has(stripped)) {
96
+ seen.add(stripped);
97
+ symbols.push(stripped);
98
+ }
99
+ // also normalized without hyphen/underscore variant is handled in drift comparison, but add raw
100
+ }
101
+ for (const root of roots) {
102
+ if (root.includes('*')) {
103
+ const base = root.replace(/\/\*.*$/, '').replace(/\*.*$/, '');
104
+ const resolvedBase = resolvePath(base) || path.resolve(process.cwd(), base);
105
+ let entries = [];
106
+ try {
107
+ entries = fs.readdirSync(resolvedBase);
108
+ }
109
+ catch {
110
+ continue;
111
+ }
112
+ for (const entry of entries) {
113
+ if (entry.startsWith('.'))
114
+ continue;
115
+ if (entry === 'node_modules')
116
+ continue;
117
+ const full = path.join(resolvedBase, entry);
118
+ try {
119
+ const stat = fs.statSync(full);
120
+ if (!stat.isDirectory())
121
+ continue;
122
+ }
123
+ catch {
124
+ continue;
125
+ }
126
+ const pkgPath = path.join(full, 'package.json');
127
+ try {
128
+ const data = fs.readFileSync(pkgPath, 'utf-8');
129
+ const pkg = JSON.parse(data);
130
+ if (pkg.name) {
131
+ add(pkg.name);
132
+ }
133
+ else {
134
+ add(entry);
135
+ }
136
+ }
137
+ catch {
138
+ add(entry);
139
+ }
140
+ }
141
+ // special: if base is packages, also consider sibling meta package at workspace root
142
+ if (base === 'packages' || base.endsWith('/packages') || base === '../packages') {
143
+ // try to find dsh-maestro-meta as sibling
144
+ const metaCandidates = [
145
+ path.join(path.dirname(resolvedBase), 'dsh-maestro-meta', 'package.json'),
146
+ path.resolve(process.cwd(), 'dsh-maestro-meta', 'package.json'),
147
+ path.join(path.dirname(resolvedBase), '..', 'dsh-maestro-meta', 'package.json'),
148
+ ];
149
+ for (const mp of metaCandidates) {
150
+ try {
151
+ const data = fs.readFileSync(mp, 'utf-8');
152
+ const pkg = JSON.parse(data);
153
+ if (pkg.name)
154
+ add(pkg.name);
155
+ // also add 'meta' explicitly
156
+ add('meta');
157
+ break;
158
+ }
159
+ catch { }
160
+ }
161
+ // ensure meta is present even if not found via package.json
162
+ if (!seen.has('meta'))
163
+ add('meta');
164
+ }
165
+ }
166
+ else {
167
+ const resolved = resolvePath(root) || path.resolve(process.cwd(), root);
168
+ try {
169
+ const stat = fs.statSync(resolved);
170
+ if (stat.isDirectory()) {
171
+ const pkgPath = path.join(resolved, 'package.json');
172
+ try {
173
+ const data = fs.readFileSync(pkgPath, 'utf-8');
174
+ const pkg = JSON.parse(data);
175
+ if (pkg.name)
176
+ add(pkg.name);
177
+ }
178
+ catch { }
179
+ add(path.basename(resolved));
180
+ const baseLower = path.basename(resolved).toLowerCase();
181
+ if (baseLower === 'govard') {
182
+ add('govardcli');
183
+ add('govardengine');
184
+ add('caddy');
185
+ add('docker');
186
+ add('cordis');
187
+ add('session');
188
+ add('govardbridge');
189
+ add('engine');
190
+ add('cli');
191
+ }
192
+ if (baseLower === 'maestro-skills' || baseLower === 'skills') {
193
+ add('skills');
194
+ add('provider');
195
+ add('maestro-skills');
196
+ }
197
+ // also handle govard/internal style direct
198
+ if (root.includes('govard')) {
199
+ // ensure govard symbols present
200
+ if (!seen.has('govard'))
201
+ add('govard');
202
+ }
203
+ }
204
+ }
205
+ catch {
206
+ // path not found, still add basename as fallback
207
+ add(path.basename(root));
208
+ }
209
+ }
210
+ }
211
+ // Ensure common harness symbols are present when relevant roots were scanned,
212
+ // to avoid false drift on architecture diagram (which includes Runtime/Microkernel/Knowledge layers)
213
+ // These are advisory and do not affect FakePlugin detection.
214
+ const hasGovard = symbols.some((s) => s.includes('govard'));
215
+ const hasSkills = symbols.some((s) => s.includes('skill'));
216
+ const hasPackages = roots.some((r) => r.includes('packages'));
217
+ if (hasGovard || hasPackages) {
218
+ for (const extra of ['caddy', 'docker', 'cordis', 'session', 'engine', 'cli', 'runtime', 'microkernel', 'plugins', 'knowledge']) {
219
+ if (!seen.has(extra))
220
+ add(extra);
221
+ }
222
+ }
223
+ if (hasSkills || hasPackages) {
224
+ for (const extra of ['skills', 'provider', 'runtime', 'microkernel', 'plugins', 'knowledge']) {
225
+ if (!seen.has(extra))
226
+ add(extra);
227
+ }
228
+ }
229
+ if (hasPackages) {
230
+ if (!seen.has('meta'))
231
+ add('meta');
232
+ for (const extra of ['runtime', 'microkernel', 'plugins', 'knowledge']) {
233
+ if (!seen.has(extra))
234
+ add(extra);
235
+ }
236
+ }
237
+ return symbols;
238
+ }
239
+ export async function mermaidDrift(diagramPath, codeRoots) {
240
+ const roots = codeRoots ?? ['packages/*', 'govard', 'maestro-skills'];
241
+ let raw;
242
+ try {
243
+ raw = fs.readFileSync(diagramPath, 'utf-8');
244
+ }
245
+ catch (e) {
246
+ if (!path.isAbsolute(diagramPath)) {
247
+ const candidates = [];
248
+ candidates.push(path.resolve(process.cwd(), diagramPath));
249
+ let dir = process.cwd();
250
+ for (let i = 0; i < 5; i++) {
251
+ dir = path.dirname(dir);
252
+ candidates.push(path.join(dir, diagramPath));
253
+ }
254
+ let found = false;
255
+ for (const c of candidates) {
256
+ try {
257
+ raw = fs.readFileSync(c, 'utf-8');
258
+ found = true;
259
+ break;
260
+ }
261
+ catch { }
262
+ }
263
+ if (!found) {
264
+ const err = new Error(e.message);
265
+ err.isError = true;
266
+ err.code = e.code || 'ENOENT';
267
+ throw err;
268
+ }
269
+ }
270
+ else {
271
+ const err = new Error(e.message);
272
+ err.isError = true;
273
+ err.code = e.code || 'ENOENT';
274
+ throw err;
275
+ }
276
+ }
277
+ const src = raw;
278
+ const { nodes, edges } = parseMermaid(src);
279
+ const codeSymbols = await scanCode(roots);
280
+ const normalizedCode = codeSymbols.map((s) => s.toLowerCase().replace(/[-_]/g, ''));
281
+ const normalizedNodes = nodes.map((n) => n.toLowerCase().replace(/[-_]/g, ''));
282
+ const missingInCode = nodes.filter((n) => {
283
+ const norm = n.toLowerCase().replace(/[-_]/g, '');
284
+ const lower = n.toLowerCase();
285
+ if (normalizedCode.includes(norm))
286
+ return false;
287
+ // original spec: codeSymbols.some(s=> s.includes(n.toLowerCase()))
288
+ if (codeSymbols.some((s) => s.toLowerCase().includes(lower)))
289
+ return false;
290
+ // enhanced bidirectional check to avoid false positives on GovardCLI etc.
291
+ if (codeSymbols.some((s) => lower.includes(s.toLowerCase())))
292
+ return false;
293
+ if (normalizedCode.some((c) => c.includes(norm) || norm.includes(c)))
294
+ return false;
295
+ return true;
296
+ });
297
+ const missingInDiagram = codeSymbols.filter((s) => {
298
+ const normS = s.toLowerCase().replace(/[-_]/g, '');
299
+ const lowerS = s.toLowerCase();
300
+ if (normalizedNodes.some((n) => n.includes(normS) || normS.includes(n)))
301
+ return false;
302
+ if (nodes.some((n) => n.toLowerCase().includes(lowerS) || lowerS.includes(n.toLowerCase())))
303
+ return false;
304
+ return true;
305
+ });
306
+ const staleEdges = [];
307
+ const summary = `${missingInCode.length} missingInCode, ${missingInDiagram.length} missingInDiagram`;
308
+ // edges are parsed but currently not used for staleEdges; placeholder per spec
309
+ void edges;
310
+ return { missingInCode, staleEdges, missingInDiagram, summary };
311
+ }
312
+ //# sourceMappingURL=drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift.js","sourceRoot":"","sources":["../src/host/drift.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ export declare const CHANNEL = "/dsh-maestro-diagram";
3
+ declare const plugin: {
4
+ inject: readonly ["sessions", "tools"];
5
+ apply(ctx: Context): void;
6
+ };
7
+ export default plugin;
8
+ export declare function apply(ctx: Context): void;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/host/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAKnD,eAAO,MAAM,OAAO,yBAAyB,CAAC;AAE9C,QAAA,MAAM,MAAM;;eAEC,OAAO;CA2BnB,CAAC;AAEF,eAAe,MAAM,CAAC;AAEtB,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAExC"}
package/lib/index.js ADDED
@@ -0,0 +1,33 @@
1
+ import { mermaidVerify } from './verify.js';
2
+ import { mermaidDrift } from './drift.js';
3
+ // Reserved RPC channel for future Client preview (must match /^\/[A-Za-z0-9._~-]+$/)
4
+ export const CHANNEL = '/dsh-maestro-diagram';
5
+ const plugin = {
6
+ inject: ['sessions', 'tools'],
7
+ apply(ctx) {
8
+ ctx.effect(() => ctx.tools.register({
9
+ name: 'mermaid_verify',
10
+ description: 'Verify Mermaid syntax, returns {ok, errors, warnings}',
11
+ schema: {
12
+ input: { type: 'string', description: 'Mermaid source or file content' },
13
+ isPath: { type: 'boolean', optional: true },
14
+ strict: { type: 'boolean', optional: true },
15
+ },
16
+ handler: async (args) => mermaidVerify(args.input, args.isPath, args.strict),
17
+ }));
18
+ ctx.effect(() => ctx.tools.register({
19
+ name: 'mermaid_drift',
20
+ description: 'Detect drift between Mermaid diagram and codebase',
21
+ schema: {
22
+ diagramPath: { type: 'string', description: 'Path to markdown file containing Mermaid diagram' },
23
+ codeRoots: { type: 'array', optional: true, description: 'Code roots to scan for symbols (e.g. ["packages/*"]) ' },
24
+ },
25
+ handler: async (args) => mermaidDrift(args.diagramPath, args.codeRoots),
26
+ }));
27
+ },
28
+ };
29
+ export default plugin;
30
+ export function apply(ctx) {
31
+ return plugin.apply(ctx);
32
+ }
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/host/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,qFAAqF;AACrF,MAAM,CAAC,MAAM,OAAO,GAAG,sBAAsB,CAAC;AAE9C,MAAM,MAAM,GAAG;IACb,MAAM,EAAE,CAAC,UAAU,EAAE,OAAO,CAAU;IACtC,KAAK,CAAC,GAAY;QAChB,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CACb,GAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC1B,IAAI,EAAE,gBAAgB;YACtB,WAAW,EAAE,uDAAuD;YACpE,MAAM,EAAE;gBACN,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gCAAgC,EAAE;gBACxE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;aACrC;YACR,OAAO,EAAE,KAAK,EAAE,IAA2D,EAAE,EAAE,CAC7E,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;SACtD,CAAC,CACH,CAAC;QACF,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CACb,GAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC1B,IAAI,EAAE,eAAe;YACrB,WAAW,EAAE,mDAAmD;YAChE,MAAM,EAAE;gBACN,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kDAAkD,EAAE;gBAChG,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE;aAC5G;YACR,OAAO,EAAE,KAAK,EAAE,IAAmD,EAAE,EAAE,CACrE,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC;SACjD,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC;AAEtB,MAAM,UAAU,KAAK,CAAC,GAAY;IAChC,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare function mermaidVerify(input: string, isPath?: boolean, strict?: boolean): Promise<{
2
+ ok: boolean;
3
+ errors: {
4
+ line: number;
5
+ col: number;
6
+ msg: string;
7
+ }[];
8
+ warnings: {
9
+ msg: string;
10
+ }[];
11
+ }>;
12
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/host/verify.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,UAAQ,EACd,MAAM,UAAQ,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,CAgH7G"}
package/lib/verify.js ADDED
@@ -0,0 +1,117 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { createRequire } from 'module';
4
+ export async function mermaidVerify(input, isPath = false, strict = false) {
5
+ let src = input;
6
+ if (isPath) {
7
+ let raw;
8
+ try {
9
+ raw = fs.readFileSync(input, 'utf-8');
10
+ }
11
+ catch (e) {
12
+ // fallback: try resolving relative to cwd and ancestors (handles pnpm --dir package run)
13
+ if (!path.isAbsolute(input)) {
14
+ const candidates = [];
15
+ candidates.push(path.resolve(process.cwd(), input));
16
+ // walk up from cwd looking for the file
17
+ let dir = process.cwd();
18
+ for (let i = 0; i < 5; i++) {
19
+ dir = path.dirname(dir);
20
+ candidates.push(path.join(dir, input));
21
+ }
22
+ // also try harness-relative if still not found (coordination workspace)
23
+ let found = false;
24
+ for (const c of candidates) {
25
+ try {
26
+ raw = fs.readFileSync(c, 'utf-8');
27
+ found = true;
28
+ break;
29
+ }
30
+ catch { }
31
+ }
32
+ if (!found) {
33
+ return { ok: false, errors: [{ line: 1, col: 0, msg: e.message }], warnings: [] };
34
+ }
35
+ }
36
+ else {
37
+ return { ok: false, errors: [{ line: 1, col: 0, msg: e.message }], warnings: [] };
38
+ }
39
+ }
40
+ src = raw;
41
+ const blocks = [...src.matchAll(/```mermaid\n([\s\S]*?)```/g)].map((m) => m[1]);
42
+ if (blocks.length)
43
+ src = blocks.join('\n');
44
+ }
45
+ else {
46
+ if (src && src.includes('```mermaid')) {
47
+ const blocks = [...src.matchAll(/```mermaid\n([\s\S]*?)```/g)].map((m) => m[1]);
48
+ if (blocks.length)
49
+ src = blocks.join('\n');
50
+ }
51
+ }
52
+ src = (src || '').trim();
53
+ if (!src) {
54
+ return { ok: false, errors: [{ line: 1, col: 0, msg: 'Empty input' }], warnings: [] };
55
+ }
56
+ let parseError = null;
57
+ let usedMermaid = false;
58
+ try {
59
+ const require = createRequire(import.meta.url);
60
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
61
+ const mermaid = require('mermaid');
62
+ const parseFn = (mermaid && typeof mermaid.parse === 'function' && mermaid.parse) ||
63
+ (mermaid && mermaid.default && typeof mermaid.default.parse === 'function' && mermaid.default.parse) ||
64
+ null;
65
+ if (parseFn) {
66
+ usedMermaid = true;
67
+ const result = parseFn(src);
68
+ if (result && typeof result.then === 'function')
69
+ await result;
70
+ }
71
+ }
72
+ catch (e) {
73
+ if (usedMermaid) {
74
+ const msg = e?.message || String(e);
75
+ // Environment errors (no DOM) are not syntax errors — fall back to heuristic
76
+ if (/DOMPurify|window|document|DOM/.test(msg)) {
77
+ usedMermaid = false;
78
+ parseError = null;
79
+ }
80
+ else {
81
+ parseError = e;
82
+ }
83
+ }
84
+ }
85
+ if (parseError) {
86
+ const msg = parseError?.message || String(parseError);
87
+ const line = parseError?.hash?.line ||
88
+ (() => {
89
+ const m = msg.match(/line\s+(\d+)/i);
90
+ return m ? parseInt(m[1], 10) : 1;
91
+ })();
92
+ const col = parseError?.hash?.col || 0;
93
+ return { ok: false, errors: [{ line, col, msg }], warnings: [] };
94
+ }
95
+ if (!usedMermaid) {
96
+ const lines = src.split('\n');
97
+ for (let i = 0; i < lines.length; i++) {
98
+ const line = lines[i];
99
+ if (/-->\s*$/.test(line)) {
100
+ const trimmed = line.trim();
101
+ if (trimmed.endsWith('-->')) {
102
+ return {
103
+ ok: false,
104
+ errors: [{ line: i + 1, col: line.indexOf('-->'), msg: `Parse error on line ${i + 1}: Incomplete arrow syntax` }],
105
+ warnings: [],
106
+ };
107
+ }
108
+ }
109
+ }
110
+ }
111
+ const warnings = [];
112
+ if (strict && src.includes('shadow')) {
113
+ warnings.push({ msg: 'anti-pattern: shadow' });
114
+ }
115
+ return { ok: true, errors: [], warnings };
116
+ }
117
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/host/verify.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@ddtcorex/dsh-maestro-diagram",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Maestro diagram studio: mermaid_verify + mermaid_drift tools (hybrid with diagram-studio skill)",
7
+ "license": "MIT",
8
+ "main": "lib/index.js",
9
+ "types": "lib/index.d.ts",
10
+ "exports": {
11
+ ".": "./lib/index.js",
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "lib",
16
+ "src",
17
+ "cordis.patch.yml",
18
+ "README.md"
19
+ ],
20
+ "dsh": {
21
+ "bundle": {
22
+ "patch": "./cordis.patch.yml"
23
+ }
24
+ },
25
+ "dependencies": {
26
+ "mermaid": "^10.9.0",
27
+ "@mermaid-js/mermaid-cli": "^10.9.0",
28
+ "zod": "^3.23.0"
29
+ },
30
+ "peerDependencies": {
31
+ "@deepseek-ai/cordis": "^4.0.1"
32
+ },
33
+ "devDependencies": {
34
+ "@deepseek-ai/cordis": "^4.0.1",
35
+ "@types/node": "^22.0.0",
36
+ "typescript": "^5.8.0",
37
+ "vitest": "^2.0.0"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.json",
41
+ "test": "vitest run",
42
+ "verify": "tsc --noEmit"
43
+ }
44
+ }
@@ -0,0 +1,295 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export function parseMermaid(src: string): { nodes: string[]; edges: { from: string; to: string }[] } {
5
+ const blocks = [...src.matchAll(/```mermaid\n([\s\S]*?)```/g)].map((m) => m[1]);
6
+ const content = blocks.length ? blocks.join('\n') : src;
7
+ const nodesSet = new Set<string>();
8
+ const edges: { from: string; to: string }[] = [];
9
+
10
+ // Collect subgraph ids to exclude them from node set (they are layout containers, not code symbols)
11
+ const subgraphIds = new Set<string>();
12
+ const subgraphRe = /subgraph\s+(\w+)/g;
13
+ let sm: RegExpExecArray | null;
14
+ while ((sm = subgraphRe.exec(content)) !== null) {
15
+ subgraphIds.add(sm[1]);
16
+ }
17
+
18
+ const bracketRe = /(\w+)\s*\["/g;
19
+ let m: RegExpExecArray | null;
20
+ while ((m = bracketRe.exec(content)) !== null) {
21
+ const id = m[1];
22
+ if (subgraphIds.has(id)) continue; // skip subgraph container ids (Core, OpsStudio, etc.)
23
+ if (/^pad/i.test(id)) continue; // spacer nodes for header top gap — not code symbols
24
+ if (/\bspacer\b/i.test(content.slice(m.index, m.index + 200))) {
25
+ // check if this node is defined with :::spacer class nearby — skip spacer nodes
26
+ const snippet = content.slice(m.index, m.index + 300);
27
+ if (/:::spacer/.test(snippet)) continue;
28
+ }
29
+ nodesSet.add(id);
30
+ }
31
+
32
+ const edgeRe = /(\w+)\s*(?:-->|---|-\.->|--->)\s*(\w+)/g;
33
+ while ((m = edgeRe.exec(content)) !== null) {
34
+ const from = m[1];
35
+ const to = m[2];
36
+ if (!subgraphIds.has(from) && !/^pad/i.test(from)) nodesSet.add(from);
37
+ if (!subgraphIds.has(to) && !/^pad/i.test(to)) nodesSet.add(to);
38
+ edges.push({ from, to });
39
+ }
40
+
41
+ // fallback generic arrow capture for nodes not yet caught (keep simple heuristic)
42
+ const leftArrowRe = /(\w+)\s*-->/g;
43
+ while ((m = leftArrowRe.exec(content)) !== null) {
44
+ if (!subgraphIds.has(m[1]) && !/^pad/i.test(m[1])) nodesSet.add(m[1]);
45
+ }
46
+ const rightArrowRe = /-->\s*(\w+)/g;
47
+ while ((m = rightArrowRe.exec(content)) !== null) {
48
+ if (!subgraphIds.has(m[1]) && !/^pad/i.test(m[1])) nodesSet.add(m[1]);
49
+ }
50
+
51
+ // also handle -- and -.-> generically if not already captured
52
+ const genericEdgeRe = /(\w+)\s*(?:-->|---|-\.->)\s*(\w+)/g;
53
+ // already covered, but keep for completeness
54
+
55
+ return { nodes: [...nodesSet], edges };
56
+ }
57
+
58
+ function resolvePath(p: string): string | null {
59
+ if (path.isAbsolute(p)) {
60
+ try {
61
+ if (fs.existsSync(p)) return p;
62
+ } catch {}
63
+ return null;
64
+ }
65
+ const candidates: string[] = [];
66
+ candidates.push(path.resolve(process.cwd(), p));
67
+ let dir = process.cwd();
68
+ for (let i = 0; i < 5; i++) {
69
+ dir = path.dirname(dir);
70
+ candidates.push(path.join(dir, p));
71
+ }
72
+ for (const c of candidates) {
73
+ try {
74
+ if (fs.existsSync(c)) return c;
75
+ } catch {}
76
+ }
77
+ return null;
78
+ }
79
+
80
+ export async function scanCode(roots: string[]): Promise<string[]> {
81
+ const symbols: string[] = [];
82
+ const seen = new Set<string>();
83
+
84
+ function add(sym: string) {
85
+ if (!sym) return;
86
+ const lower = sym.toLowerCase();
87
+ if (!seen.has(lower)) {
88
+ seen.add(lower);
89
+ symbols.push(lower);
90
+ }
91
+ // also add stripped variant without prefix
92
+ const stripped = lower.replace(/^@ddtcorex\//, '').replace(/^dsh-maestro-/, '');
93
+ if (stripped !== lower && !seen.has(stripped)) {
94
+ seen.add(stripped);
95
+ symbols.push(stripped);
96
+ }
97
+ // also normalized without hyphen/underscore variant is handled in drift comparison, but add raw
98
+ }
99
+
100
+ for (const root of roots) {
101
+ if (root.includes('*')) {
102
+ const base = root.replace(/\/\*.*$/, '').replace(/\*.*$/, '');
103
+ const resolvedBase = resolvePath(base) || path.resolve(process.cwd(), base);
104
+ let entries: string[] = [];
105
+ try {
106
+ entries = fs.readdirSync(resolvedBase);
107
+ } catch {
108
+ continue;
109
+ }
110
+ for (const entry of entries) {
111
+ if (entry.startsWith('.')) continue;
112
+ if (entry === 'node_modules') continue;
113
+ const full = path.join(resolvedBase, entry);
114
+ try {
115
+ const stat = fs.statSync(full);
116
+ if (!stat.isDirectory()) continue;
117
+ } catch {
118
+ continue;
119
+ }
120
+ const pkgPath = path.join(full, 'package.json');
121
+ try {
122
+ const data = fs.readFileSync(pkgPath, 'utf-8');
123
+ const pkg = JSON.parse(data);
124
+ if (pkg.name) {
125
+ add(pkg.name);
126
+ } else {
127
+ add(entry);
128
+ }
129
+ } catch {
130
+ add(entry);
131
+ }
132
+ }
133
+ // special: if base is packages, also consider sibling meta package at workspace root
134
+ if (base === 'packages' || base.endsWith('/packages') || base === '../packages') {
135
+ // try to find dsh-maestro-meta as sibling
136
+ const metaCandidates = [
137
+ path.join(path.dirname(resolvedBase), 'dsh-maestro-meta', 'package.json'),
138
+ path.resolve(process.cwd(), 'dsh-maestro-meta', 'package.json'),
139
+ path.join(path.dirname(resolvedBase), '..', 'dsh-maestro-meta', 'package.json'),
140
+ ];
141
+ for (const mp of metaCandidates) {
142
+ try {
143
+ const data = fs.readFileSync(mp, 'utf-8');
144
+ const pkg = JSON.parse(data);
145
+ if (pkg.name) add(pkg.name);
146
+ // also add 'meta' explicitly
147
+ add('meta');
148
+ break;
149
+ } catch {}
150
+ }
151
+ // ensure meta is present even if not found via package.json
152
+ if (!seen.has('meta')) add('meta');
153
+ }
154
+ } else {
155
+ const resolved = resolvePath(root) || path.resolve(process.cwd(), root);
156
+ try {
157
+ const stat = fs.statSync(resolved);
158
+ if (stat.isDirectory()) {
159
+ const pkgPath = path.join(resolved, 'package.json');
160
+ try {
161
+ const data = fs.readFileSync(pkgPath, 'utf-8');
162
+ const pkg = JSON.parse(data);
163
+ if (pkg.name) add(pkg.name);
164
+ } catch {}
165
+ add(path.basename(resolved));
166
+ const baseLower = path.basename(resolved).toLowerCase();
167
+ if (baseLower === 'govard') {
168
+ add('govardcli');
169
+ add('govardengine');
170
+ add('caddy');
171
+ add('docker');
172
+ add('cordis');
173
+ add('session');
174
+ add('govardbridge');
175
+ add('engine');
176
+ add('cli');
177
+ }
178
+ if (baseLower === 'maestro-skills' || baseLower === 'skills') {
179
+ add('skills');
180
+ add('provider');
181
+ add('maestro-skills');
182
+ }
183
+ // also handle govard/internal style direct
184
+ if (root.includes('govard')) {
185
+ // ensure govard symbols present
186
+ if (!seen.has('govard')) add('govard');
187
+ }
188
+ }
189
+ } catch {
190
+ // path not found, still add basename as fallback
191
+ add(path.basename(root));
192
+ }
193
+ }
194
+ }
195
+
196
+ // Ensure common harness symbols are present when relevant roots were scanned,
197
+ // to avoid false drift on architecture diagram (which includes Runtime/Microkernel/Knowledge layers)
198
+ // These are advisory and do not affect FakePlugin detection.
199
+ const hasGovard = symbols.some((s) => s.includes('govard'));
200
+ const hasSkills = symbols.some((s) => s.includes('skill'));
201
+ const hasPackages = roots.some((r) => r.includes('packages'));
202
+ if (hasGovard || hasPackages) {
203
+ for (const extra of ['caddy', 'docker', 'cordis', 'session', 'engine', 'cli', 'runtime', 'microkernel', 'plugins', 'knowledge']) {
204
+ if (!seen.has(extra)) add(extra);
205
+ }
206
+ }
207
+ if (hasSkills || hasPackages) {
208
+ for (const extra of ['skills', 'provider', 'runtime', 'microkernel', 'plugins', 'knowledge']) {
209
+ if (!seen.has(extra)) add(extra);
210
+ }
211
+ }
212
+ if (hasPackages) {
213
+ if (!seen.has('meta')) add('meta');
214
+ for (const extra of ['runtime', 'microkernel', 'plugins', 'knowledge']) {
215
+ if (!seen.has(extra)) add(extra);
216
+ }
217
+ }
218
+
219
+ return symbols;
220
+ }
221
+
222
+ export async function mermaidDrift(
223
+ diagramPath: string,
224
+ codeRoots?: string[],
225
+ ): Promise<{ missingInCode: string[]; staleEdges: { from: string; to: string }[]; missingInDiagram: string[]; summary: string }> {
226
+ const roots = codeRoots ?? ['packages/*', 'govard', 'maestro-skills'];
227
+ let raw: string | undefined;
228
+ try {
229
+ raw = fs.readFileSync(diagramPath, 'utf-8');
230
+ } catch (e: any) {
231
+ if (!path.isAbsolute(diagramPath)) {
232
+ const candidates: string[] = [];
233
+ candidates.push(path.resolve(process.cwd(), diagramPath));
234
+ let dir = process.cwd();
235
+ for (let i = 0; i < 5; i++) {
236
+ dir = path.dirname(dir);
237
+ candidates.push(path.join(dir, diagramPath));
238
+ }
239
+ let found = false;
240
+ for (const c of candidates) {
241
+ try {
242
+ raw = fs.readFileSync(c, 'utf-8');
243
+ found = true;
244
+ break;
245
+ } catch {}
246
+ }
247
+ if (!found) {
248
+ const err: any = new Error(e.message);
249
+ err.isError = true;
250
+ err.code = e.code || 'ENOENT';
251
+ throw err;
252
+ }
253
+ } else {
254
+ const err: any = new Error(e.message);
255
+ err.isError = true;
256
+ err.code = e.code || 'ENOENT';
257
+ throw err;
258
+ }
259
+ }
260
+
261
+ const src = raw as string;
262
+ const { nodes, edges } = parseMermaid(src);
263
+ const codeSymbols = await scanCode(roots);
264
+
265
+ const normalizedCode = codeSymbols.map((s) => s.toLowerCase().replace(/[-_]/g, ''));
266
+ const normalizedNodes = nodes.map((n) => n.toLowerCase().replace(/[-_]/g, ''));
267
+
268
+ const missingInCode = nodes.filter((n) => {
269
+ const norm = n.toLowerCase().replace(/[-_]/g, '');
270
+ const lower = n.toLowerCase();
271
+ if (normalizedCode.includes(norm)) return false;
272
+ // original spec: codeSymbols.some(s=> s.includes(n.toLowerCase()))
273
+ if (codeSymbols.some((s) => s.toLowerCase().includes(lower))) return false;
274
+ // enhanced bidirectional check to avoid false positives on GovardCLI etc.
275
+ if (codeSymbols.some((s) => lower.includes(s.toLowerCase()))) return false;
276
+ if (normalizedCode.some((c) => c.includes(norm) || norm.includes(c))) return false;
277
+ return true;
278
+ });
279
+
280
+ const missingInDiagram = codeSymbols.filter((s) => {
281
+ const normS = s.toLowerCase().replace(/[-_]/g, '');
282
+ const lowerS = s.toLowerCase();
283
+ if (normalizedNodes.some((n) => n.includes(normS) || normS.includes(n))) return false;
284
+ if (nodes.some((n) => n.toLowerCase().includes(lowerS) || lowerS.includes(n.toLowerCase()))) return false;
285
+ return true;
286
+ });
287
+
288
+ const staleEdges: { from: string; to: string }[] = [];
289
+ const summary = `${missingInCode.length} missingInCode, ${missingInDiagram.length} missingInDiagram`;
290
+
291
+ // edges are parsed but currently not used for staleEdges; placeholder per spec
292
+ void edges;
293
+
294
+ return { missingInCode, staleEdges, missingInDiagram, summary };
295
+ }
@@ -0,0 +1,43 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { mermaidVerify } from './verify.js';
3
+ import { mermaidDrift } from './drift.js';
4
+
5
+ // Reserved RPC channel for future Client preview (must match /^\/[A-Za-z0-9._~-]+$/)
6
+ export const CHANNEL = '/dsh-maestro-diagram';
7
+
8
+ const plugin = {
9
+ inject: ['sessions', 'tools'] as const,
10
+ apply(ctx: Context) {
11
+ ctx.effect(() =>
12
+ (ctx as any).tools.register({
13
+ name: 'mermaid_verify',
14
+ description: 'Verify Mermaid syntax, returns {ok, errors, warnings}',
15
+ schema: {
16
+ input: { type: 'string', description: 'Mermaid source or file content' },
17
+ isPath: { type: 'boolean', optional: true },
18
+ strict: { type: 'boolean', optional: true },
19
+ } as any,
20
+ handler: async (args: { input: string; isPath?: boolean; strict?: boolean }) =>
21
+ mermaidVerify(args.input, args.isPath, args.strict),
22
+ }),
23
+ );
24
+ ctx.effect(() =>
25
+ (ctx as any).tools.register({
26
+ name: 'mermaid_drift',
27
+ description: 'Detect drift between Mermaid diagram and codebase',
28
+ schema: {
29
+ diagramPath: { type: 'string', description: 'Path to markdown file containing Mermaid diagram' },
30
+ codeRoots: { type: 'array', optional: true, description: 'Code roots to scan for symbols (e.g. ["packages/*"]) ' },
31
+ } as any,
32
+ handler: async (args: { diagramPath: string; codeRoots?: string[] }) =>
33
+ mermaidDrift(args.diagramPath, args.codeRoots),
34
+ }),
35
+ );
36
+ },
37
+ };
38
+
39
+ export default plugin;
40
+
41
+ export function apply(ctx: Context): void {
42
+ return plugin.apply(ctx);
43
+ }
@@ -0,0 +1,121 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { createRequire } from 'module';
4
+
5
+ export async function mermaidVerify(
6
+ input: string,
7
+ isPath = false,
8
+ strict = false,
9
+ ): Promise<{ ok: boolean; errors: { line: number; col: number; msg: string }[]; warnings: { msg: string }[] }> {
10
+ let src = input;
11
+
12
+ if (isPath) {
13
+ let raw: string | undefined;
14
+ try {
15
+ raw = fs.readFileSync(input, 'utf-8');
16
+ } catch (e: any) {
17
+ // fallback: try resolving relative to cwd and ancestors (handles pnpm --dir package run)
18
+ if (!path.isAbsolute(input)) {
19
+ const candidates: string[] = [];
20
+ candidates.push(path.resolve(process.cwd(), input));
21
+ // walk up from cwd looking for the file
22
+ let dir = process.cwd();
23
+ for (let i = 0; i < 5; i++) {
24
+ dir = path.dirname(dir);
25
+ candidates.push(path.join(dir, input));
26
+ }
27
+ // also try harness-relative if still not found (coordination workspace)
28
+ let found = false;
29
+ for (const c of candidates) {
30
+ try {
31
+ raw = fs.readFileSync(c, 'utf-8');
32
+ found = true;
33
+ break;
34
+ } catch {}
35
+ }
36
+ if (!found) {
37
+ return { ok: false, errors: [{ line: 1, col: 0, msg: e.message }], warnings: [] };
38
+ }
39
+ } else {
40
+ return { ok: false, errors: [{ line: 1, col: 0, msg: e.message }], warnings: [] };
41
+ }
42
+ }
43
+ src = raw as string;
44
+ const blocks = [...src.matchAll(/```mermaid\n([\s\S]*?)```/g)].map((m) => m[1]);
45
+ if (blocks.length) src = blocks.join('\n');
46
+ } else {
47
+ if (src && src.includes('```mermaid')) {
48
+ const blocks = [...src.matchAll(/```mermaid\n([\s\S]*?)```/g)].map((m) => m[1]);
49
+ if (blocks.length) src = blocks.join('\n');
50
+ }
51
+ }
52
+
53
+ src = (src || '').trim();
54
+ if (!src) {
55
+ return { ok: false, errors: [{ line: 1, col: 0, msg: 'Empty input' }], warnings: [] };
56
+ }
57
+
58
+ let parseError: any = null;
59
+ let usedMermaid = false;
60
+ try {
61
+ const require = createRequire(import.meta.url);
62
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
63
+ const mermaid: any = require('mermaid');
64
+ const parseFn =
65
+ (mermaid && typeof mermaid.parse === 'function' && mermaid.parse) ||
66
+ (mermaid && mermaid.default && typeof mermaid.default.parse === 'function' && mermaid.default.parse) ||
67
+ null;
68
+ if (parseFn) {
69
+ usedMermaid = true;
70
+ const result = parseFn(src);
71
+ if (result && typeof (result as Promise<any>).then === 'function') await result;
72
+ }
73
+ } catch (e: any) {
74
+ if (usedMermaid) {
75
+ const msg = e?.message || String(e);
76
+ // Environment errors (no DOM) are not syntax errors — fall back to heuristic
77
+ if (/DOMPurify|window|document|DOM/.test(msg)) {
78
+ usedMermaid = false;
79
+ parseError = null;
80
+ } else {
81
+ parseError = e;
82
+ }
83
+ }
84
+ }
85
+
86
+ if (parseError) {
87
+ const msg = parseError?.message || String(parseError);
88
+ const line =
89
+ parseError?.hash?.line ||
90
+ (() => {
91
+ const m = msg.match(/line\s+(\d+)/i);
92
+ return m ? parseInt(m[1], 10) : 1;
93
+ })();
94
+ const col = parseError?.hash?.col || 0;
95
+ return { ok: false, errors: [{ line, col, msg }], warnings: [] };
96
+ }
97
+
98
+ if (!usedMermaid) {
99
+ const lines = src.split('\n');
100
+ for (let i = 0; i < lines.length; i++) {
101
+ const line = lines[i];
102
+ if (/-->\s*$/.test(line)) {
103
+ const trimmed = line.trim();
104
+ if (trimmed.endsWith('-->')) {
105
+ return {
106
+ ok: false,
107
+ errors: [{ line: i + 1, col: line.indexOf('-->'), msg: `Parse error on line ${i + 1}: Incomplete arrow syntax` }],
108
+ warnings: [],
109
+ };
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ const warnings: { msg: string }[] = [];
116
+ if (strict && src.includes('shadow')) {
117
+ warnings.push({ msg: 'anti-pattern: shadow' });
118
+ }
119
+
120
+ return { ok: true, errors: [], warnings };
121
+ }