@nowline/mcp 0.8.6 → 0.9.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 (58) hide show
  1. package/dist/capabilities.d.ts.map +1 -1
  2. package/dist/capabilities.js +2 -9
  3. package/dist/capabilities.js.map +1 -1
  4. package/dist/diagnostics.d.ts +43 -15
  5. package/dist/diagnostics.d.ts.map +1 -1
  6. package/dist/diagnostics.js +77 -25
  7. package/dist/diagnostics.js.map +1 -1
  8. package/dist/generated/resources.d.ts.map +1 -1
  9. package/dist/generated/resources.js +15 -3
  10. package/dist/generated/resources.js.map +1 -1
  11. package/dist/generated/ui-bundle.d.ts.map +1 -1
  12. package/dist/generated/ui-bundle.js +2 -2
  13. package/dist/generated/ui-bundle.js.map +1 -1
  14. package/dist/http.d.ts +4 -0
  15. package/dist/http.d.ts.map +1 -0
  16. package/dist/http.js +23 -0
  17. package/dist/http.js.map +1 -0
  18. package/dist/index.js +6 -12
  19. package/dist/index.js.map +1 -1
  20. package/dist/options.d.ts +26 -0
  21. package/dist/options.d.ts.map +1 -0
  22. package/dist/options.js +55 -0
  23. package/dist/options.js.map +1 -0
  24. package/dist/reference-cheatsheet.d.ts +1 -1
  25. package/dist/reference-cheatsheet.d.ts.map +1 -1
  26. package/dist/reference-cheatsheet.js +27 -2
  27. package/dist/reference-cheatsheet.js.map +1 -1
  28. package/dist/schema-vocab.d.ts +4 -2
  29. package/dist/schema-vocab.d.ts.map +1 -1
  30. package/dist/schema-vocab.js +9 -0
  31. package/dist/schema-vocab.js.map +1 -1
  32. package/dist/schemas.d.ts +2 -0
  33. package/dist/schemas.d.ts.map +1 -1
  34. package/dist/schemas.js +2 -0
  35. package/dist/schemas.js.map +1 -1
  36. package/dist/server.d.ts.map +1 -1
  37. package/dist/server.js +146 -56
  38. package/dist/server.js.map +1 -1
  39. package/dist/ui/entry.js +5 -12
  40. package/dist/ui/entry.js.map +1 -1
  41. package/dist/ui/payload.d.ts +14 -0
  42. package/dist/ui/payload.d.ts.map +1 -1
  43. package/dist/ui/payload.js +23 -0
  44. package/dist/ui/payload.js.map +1 -1
  45. package/package.json +21 -16
  46. package/src/capabilities.ts +4 -2
  47. package/src/diagnostics.ts +99 -39
  48. package/src/generated/resources.ts +15 -3
  49. package/src/generated/ui-bundle.ts +2 -2
  50. package/src/http.ts +28 -0
  51. package/src/index.ts +6 -13
  52. package/src/options.ts +79 -0
  53. package/src/reference-cheatsheet.ts +27 -2
  54. package/src/schema-vocab.ts +9 -0
  55. package/src/schemas.ts +2 -0
  56. package/src/server.ts +195 -57
  57. package/src/ui/entry.ts +6 -11
  58. package/src/ui/payload.ts +26 -0
@@ -1 +1 @@
1
- {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/ui/payload.ts"],"names":[],"mappings":"AAQA,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACnC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,GAC5D,cAAc,GAAG,SAAS,CAc5B;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC1C,cAAc,GAAG,SAAS,CAS5B"}
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/ui/payload.ts"],"names":[],"mappings":"AAQA,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACnC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,GAC5D,cAAc,GAAG,SAAS,CAc5B;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC1C,cAAc,GAAG,SAAS,CAc5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAElF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAGlD"}
@@ -38,12 +38,35 @@ export function parsePreviewFromContent(content) {
38
38
  export function parsePreviewFromArguments(args) {
39
39
  if (!args || typeof args.source !== 'string')
40
40
  return undefined;
41
+ // A `timezone` without a `now` names a date only the server can resolve;
42
+ // wait for the result payload, which carries it.
43
+ if (typeof args.timezone === 'string' && typeof args.now !== 'string')
44
+ return undefined;
41
45
  return {
42
46
  source: args.source,
43
47
  theme: typeof args.theme === 'string' ? args.theme : undefined,
44
48
  now: typeof args.now === 'string' ? args.now : undefined,
45
49
  width: typeof args.width === 'number' ? args.width : undefined,
50
+ nonWorking: args.nonWorking === 'hide' || args.nonWorking === 'show' ? args.nonWorking : undefined,
46
51
  locale: 'en-US',
47
52
  };
48
53
  }
54
+ /**
55
+ * The shell's non-working dropdown value for a payload. A payload with no
56
+ * `nonWorking` maps to `'file'` (no override), so the file's own key applies;
57
+ * anything other than `hide` or `show` is treated as unset.
58
+ */
59
+ export function toNonWorkingOverride(nonWorking) {
60
+ return nonWorking === 'hide' || nonWorking === 'show' ? nonWorking : 'file';
61
+ }
62
+ /**
63
+ * The shell's now-line value for a payload's `now`. Omitted means today; `-`
64
+ * (the CLI's `--now -` sentinel, also accepted by the `render` tool) hides the
65
+ * now-line; a date passes through.
66
+ */
67
+ export function toNowOverride(now) {
68
+ if (now === '-')
69
+ return 'hide';
70
+ return typeof now === 'string' && now.length > 0 ? now : 'today';
71
+ }
49
72
  //# sourceMappingURL=payload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/ui/payload.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,iFAAiF;AACjF,+EAA+E;AAC/E,gFAAgF;AAChF,sEAAsE;AACtE,gDAAgD;AAehD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACnC,OAA2D;IAE3D,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI;YAAE,SAAS;QACnD,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;YACxD,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACzE,OAAO,MAAM,CAAC;YAClB,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,qBAAqB;QACzB,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CACrC,IAAyC;IAEzC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC/D,OAAO;QACH,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC9D,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QACxD,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC9D,MAAM,EAAE,OAAO;KAClB,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/ui/payload.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,iFAAiF;AACjF,+EAA+E;AAC/E,gFAAgF;AAChF,sEAAsE;AACtE,gDAAgD;AAiBhD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACnC,OAA2D;IAE3D,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI;YAAE,SAAS;QACnD,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;YACxD,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACzE,OAAO,MAAM,CAAC;YAClB,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,qBAAqB;QACzB,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CACrC,IAAyC;IAEzC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC/D,yEAAyE;IACzE,iDAAiD;IACjD,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxF,OAAO;QACH,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC9D,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QACxD,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC9D,UAAU,EACN,IAAI,CAAC,UAAU,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAC1F,MAAM,EAAE,OAAO;KAClB,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAmB;IACpD,OAAO,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACtC,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IAC/B,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;AACrE,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nowline/mcp",
3
3
  "mcpName": "io.nowline/nowline",
4
- "version": "0.8.6",
4
+ "version": "0.9.0",
5
5
  "description": "Nowline MCP server — lets AI agents create, edit, and render your roadmaps.",
6
6
  "license": "Apache-2.0",
7
7
  "engines": {
@@ -22,6 +22,10 @@
22
22
  "./server": {
23
23
  "types": "./dist/server.d.ts",
24
24
  "import": "./dist/server.js"
25
+ },
26
+ "./http": {
27
+ "types": "./dist/http.d.ts",
28
+ "import": "./dist/http.js"
25
29
  }
26
30
  },
27
31
  "files": [
@@ -30,26 +34,27 @@
30
34
  ],
31
35
  "dependencies": {
32
36
  "@modelcontextprotocol/ext-apps": "^2.0.3",
33
- "@modelcontextprotocol/node": "^2.1.0",
34
- "@modelcontextprotocol/server": "^2.2.0",
35
- "@nowline/browser": "0.8.6",
36
- "@nowline/core": "0.8.6",
37
- "@nowline/export": "0.8.6",
38
- "@nowline/export-core": "0.8.6",
39
- "@nowline/layout": "0.8.6",
40
- "@nowline/preview": "0.8.6",
41
- "@nowline/preview-shell": "0.8.6",
42
- "@nowline/share-link": "0.8.6",
37
+ "@modelcontextprotocol/node": "^2.1.1",
38
+ "@modelcontextprotocol/server": "^2.3.1",
39
+ "@nowline/browser": "0.9.0",
40
+ "@nowline/config": "0.9.0",
41
+ "@nowline/core": "0.9.0",
42
+ "@nowline/export": "0.9.0",
43
+ "@nowline/export-core": "0.9.0",
44
+ "@nowline/layout": "0.9.0",
45
+ "@nowline/preview": "0.9.0",
46
+ "@nowline/preview-shell": "0.9.0",
47
+ "@nowline/share-link": "0.9.0",
43
48
  "@resvg/resvg-wasm": "^2.6.2",
44
49
  "langium": "~4.4.0",
45
- "zod": "^4.6.5"
50
+ "zod": "^4.6.7"
46
51
  },
47
52
  "devDependencies": {
48
- "@modelcontextprotocol/client": "^2.2.0",
49
- "@modelcontextprotocol/inspector": "2.8.0",
50
- "@types/node": "^26.6.3",
53
+ "@modelcontextprotocol/client": "^2.3.1",
54
+ "@modelcontextprotocol/inspector": "2.12.0",
55
+ "@types/node": "^26.6.5",
51
56
  "esbuild": "^0.28.2",
52
- "vitest": "^5.0.2"
57
+ "vitest": "^5.0.5"
53
58
  },
54
59
  "scripts": {
55
60
  "prebuild": "node scripts/bundle-resources.mjs && node scripts/bundle-ui.mjs",
@@ -1,7 +1,9 @@
1
+ import { THEME_NAMES } from '@nowline/layout';
2
+
1
3
  // Aggregated server capabilities. Used by the `capabilities` tool and the
2
4
  // individual `list-*` projection tools. Values are hardcoded to their stable
3
5
  // registry sources and must be kept in sync with:
4
- // - themes: @nowline/layout/src/themes/index.ts (ThemeName)
6
+ // - themes: THEME_NAMES, imported from @nowline/layout
5
7
  // - icons: @nowline/renderer/src/svg/icons.ts (CAPACITY_ICON_SVG keys)
6
8
  // - locales: @nowline/core/src/i18n (message bundle files)
7
9
  // - formats: @nowline/export ExportFormat union
@@ -16,7 +18,7 @@ export interface ServerCapabilities {
16
18
  }
17
19
 
18
20
  export const CAPABILITIES: ServerCapabilities = {
19
- themes: ['light', 'dark', 'grayscale'],
21
+ themes: THEME_NAMES,
20
22
  // Built-in `capacity-icon:` vocabulary (CAPACITY_ICON_SVG keys).
21
23
  icons: ['person', 'people', 'points', 'time'],
22
24
  locales: ['en', 'fr', 'fr-CA', 'fr-FR'],
@@ -4,11 +4,20 @@ import {
4
4
  collectDocumentDiagnostics,
5
5
  createNowlineServices,
6
6
  extractSuggestion,
7
+ inferCodeFromMessage,
8
+ isRoutedResolveDiagnostic,
7
9
  type NowlineFile,
10
+ type ResolveDiagnostic,
8
11
  resolveDiagnosticCode,
9
- resolveIncludes,
10
12
  } from '@nowline/core';
11
- import { collectLayoutInsights, type LayoutInsight, layoutRoadmap } from '@nowline/layout';
13
+ import {
14
+ type HostEnv,
15
+ type RenderInputs,
16
+ type StagedRoadmap,
17
+ type StageResult,
18
+ stageForExport,
19
+ } from '@nowline/export';
20
+ import { collectLayoutInsights, type LayoutInsight } from '@nowline/layout';
12
21
  import { URI } from 'langium';
13
22
 
14
23
  export interface McpDiagnostic {
@@ -91,6 +100,50 @@ export function collectMcpDiagnostics(
91
100
  return out;
92
101
  }
93
102
 
103
+ /**
104
+ * A routed include-resolver diagnostic (a wave rule, specs/waves.md §6.1, or
105
+ * an included file's NL.E0302) as an MCP diagnostic: its stable code, the
106
+ * file it points into and a 1-based line and column, the validator's for the
107
+ * same finding when the diagnostic carries a range (column 1 otherwise).
108
+ * WV8, the one uncoded wave rule, is labelled like the validator's copy. An
109
+ * included file's syntax error takes its position from the lexer or parser.
110
+ */
111
+ export function resolveDiagnosticToMcp(diag: ResolveDiagnostic): McpDiagnostic {
112
+ // An included file's syntax error, labelled like the root file's own.
113
+ if (diag.syntax) {
114
+ const column =
115
+ diag.syntax.origin === 'lexer'
116
+ ? diag.syntax.error.column
117
+ : diag.syntax.error.token?.startColumn;
118
+ return {
119
+ file: diag.sourcePath,
120
+ line: (diag.line ?? 0) + 1,
121
+ // An error at end of input has no position (NaN).
122
+ column: column !== undefined && Number.isFinite(column) ? column : 1,
123
+ severity: 'error',
124
+ code: diag.syntax.origin === 'lexer' ? 'lexing-error' : 'parsing-error',
125
+ message: diag.message,
126
+ };
127
+ }
128
+ const suggestion = extractSuggestion(diag.message);
129
+ return {
130
+ file: diag.sourcePath,
131
+ line: (diag.range?.start.line ?? diag.line ?? 0) + 1,
132
+ column: (diag.range?.start.character ?? 0) + 1,
133
+ severity: diag.severity,
134
+ code: diag.code ?? inferCodeFromMessage(diag.message),
135
+ message: diag.message,
136
+ ...(suggestion === undefined ? {} : { suggestion }),
137
+ };
138
+ }
139
+
140
+ /** The routed diagnostics among `diagnostics`, as MCP diagnostics. */
141
+ export function routedResolveDiagnosticsToMcp(
142
+ diagnostics: readonly ResolveDiagnostic[],
143
+ ): McpDiagnostic[] {
144
+ return diagnostics.filter(isRoutedResolveDiagnostic).map(resolveDiagnosticToMcp);
145
+ }
146
+
94
147
  export function diagnosticsErrorResponse(filePath: string, diagnostics: McpDiagnostic[]) {
95
148
  return {
96
149
  content: [
@@ -103,6 +156,19 @@ export function diagnosticsErrorResponse(filePath: string, diagnostics: McpDiagn
103
156
  };
104
157
  }
105
158
 
159
+ /**
160
+ * The error response when include resolution reports a routed (wave-rule)
161
+ * error: the validator's diagnostics (warnings only, since `doc` validated)
162
+ * followed by `routed`.
163
+ */
164
+ export function includeDiagnosticsResponse(
165
+ doc: Awaited<ReturnType<typeof buildDocument>>,
166
+ filePath: string,
167
+ routed: McpDiagnostic[],
168
+ ) {
169
+ return diagnosticsErrorResponse(filePath, [...collectMcpDiagnostics(doc, filePath), ...routed]);
170
+ }
171
+
106
172
  export async function diagnosticsErrorBlock(
107
173
  source: string,
108
174
  filePath: string,
@@ -118,47 +184,41 @@ export async function diagnosticsErrorBlock(
118
184
  return { ok: true, doc };
119
185
  }
120
186
 
121
- export interface LayoutInsightInputs {
122
- source: string;
123
- filePath: string;
124
- today?: Date;
125
- theme?: 'light' | 'dark' | 'grayscale';
126
- width?: number;
127
- locale?: string;
128
- readFile?: (absPath: string) => Promise<string>;
129
- /** Pre-built document to reuse instead of re-parsing `source`. The
130
- * caller is responsible for passing a doc parsed from the same
131
- * `source` (e.g. the one from `diagnosticsErrorBlock`). */
132
- doc?: Awaited<ReturnType<typeof buildDocument>>;
187
+ /**
188
+ * Stage a validated document once for a tool call: the render, the export,
189
+ * the insights and the routed include diagnostics all read this one result.
190
+ * `doc` is the document `diagnosticsErrorBlock` (or `validate`) already built
191
+ * from `source`, so nothing is parsed again.
192
+ */
193
+ export function stageMcpRoadmap(
194
+ doc: Awaited<ReturnType<typeof buildDocument>>,
195
+ source: string,
196
+ inputs: RenderInputs,
197
+ host: HostEnv,
198
+ ): Promise<StageResult> {
199
+ return stageForExport(source, inputs, host, { document: doc, services: getMcpServices() });
133
200
  }
134
201
 
135
- export async function collectMcpLayoutInsights(inputs: LayoutInsightInputs): Promise<McpInsight[]> {
136
- const doc = inputs.doc ?? (await buildDocument(inputs.source));
137
- const diagnostics = collectMcpDiagnostics(doc, inputs.filePath);
138
- if (diagnostics.some((d) => d.severity === 'error')) {
139
- return [];
140
- }
141
-
142
- const services = getMcpServices();
143
- const file = doc.parseResult.value;
144
- const resolved = await resolveIncludes(file, inputs.filePath, {
145
- services: services.Nowline,
146
- readFile: inputs.readFile,
147
- });
148
- if (resolved.diagnostics.some((d) => d.severity === 'error')) {
149
- return [];
202
+ /**
203
+ * The routed (wave-rule) include diagnostics a stage reports, as MCP
204
+ * diagnostics, errors included. An uncoded include error takes precedence and
205
+ * hides them, as does a validator error (specs/waves.md §6.1).
206
+ */
207
+ export function stageRoutedDiagnostics(stage: StageResult): McpDiagnostic[] {
208
+ if (stage.kind === 'staged' || (stage.kind === 'include-error' && !stage.legacy)) {
209
+ return stage.routed.map(resolveDiagnosticToMcp);
150
210
  }
211
+ return [];
212
+ }
151
213
 
152
- const layout = layoutRoadmap(file, resolved, {
153
- today: inputs.today,
154
- theme: inputs.theme ?? 'light',
155
- width: inputs.width,
156
- locale: inputs.locale ?? 'en-US',
157
- });
158
-
159
- return collectLayoutInsights(layout, {
160
- today: inputs.today,
161
- locale: inputs.locale ?? 'en-US',
214
+ /**
215
+ * Layout insights read from a staged roadmap's model: the layout the render
216
+ * draws, logo included, so the insights describe the same header and chart.
217
+ */
218
+ export function layoutInsightsFromStage(stage: StagedRoadmap): McpInsight[] {
219
+ return collectLayoutInsights(stage.model, {
220
+ today: stage.layout.today,
221
+ locale: stage.layout.locale ?? 'en-US',
162
222
  }).map(layoutInsightToMcp);
163
223
  }
164
224