@mandujs/mcp 0.38.12 → 0.39.0-beta.1

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 (183) hide show
  1. package/README.md +3 -3
  2. package/package.json +3 -5
  3. package/src/activity-adapter.ts +23 -23
  4. package/src/activity-monitor.ts +39 -10
  5. package/src/adapters/index.ts +20 -20
  6. package/src/adapters/monitor-adapter.ts +100 -100
  7. package/src/adapters/tool-adapter.ts +90 -90
  8. package/src/executor/index.ts +22 -22
  9. package/src/executor/tool-executor.ts +148 -148
  10. package/src/hooks/config-watcher.ts +173 -173
  11. package/src/hooks/index.ts +23 -23
  12. package/src/hooks/mcp-hooks.ts +227 -227
  13. package/src/index.ts +5 -5
  14. package/src/logging/index.ts +15 -15
  15. package/src/logging/mcp-transport.ts +134 -134
  16. package/src/new-resources.ts +2 -2
  17. package/src/profiles.ts +25 -54
  18. package/src/prompts.ts +4 -4
  19. package/src/registry/index.ts +13 -13
  20. package/src/registry/mcp-tool-registry.ts +298 -298
  21. package/src/resources/generated-skills/catalog.ts +36 -0
  22. package/src/resources/generated-skills/mandu-agent-workflow/SKILL.md +48 -0
  23. package/src/resources/generated-skills/mandu-contract/SKILL.md +20 -0
  24. package/src/resources/generated-skills/mandu-fs-routes/SKILL.md +19 -0
  25. package/src/resources/generated-skills/mandu-guard/SKILL.md +20 -0
  26. package/src/resources/generated-skills/mandu-hydration/SKILL.md +19 -0
  27. package/src/resources/generated-skills/mandu-testing/SKILL.md +20 -0
  28. package/src/resources/handlers.ts +3 -3
  29. package/src/resources/skills/guides.ts +49 -49
  30. package/src/resources/skills/index.ts +12 -12
  31. package/src/resources/skills/loader.ts +8 -35
  32. package/src/resources/skills/recipes.ts +28 -28
  33. package/src/server.ts +1 -1
  34. package/src/tools/agent.ts +479 -409
  35. package/src/tools/ate-exemplar.ts +92 -92
  36. package/src/tools/ate-flakes.ts +90 -90
  37. package/src/tools/ate-mutate.ts +103 -103
  38. package/src/tools/ate-mutation-report.ts +64 -64
  39. package/src/tools/ate-oracle-pending.ts +49 -49
  40. package/src/tools/ate-oracle-replay.ts +44 -44
  41. package/src/tools/ate-oracle-verdict.ts +70 -70
  42. package/src/tools/ate-prompt.ts +146 -146
  43. package/src/tools/ate-run.ts +1 -1
  44. package/src/tools/ate.ts +38 -38
  45. package/src/tools/brain.ts +6 -6
  46. package/src/tools/composite.ts +19 -61
  47. package/src/tools/contract.ts +11 -9
  48. package/src/tools/deploy-plan.ts +2 -2
  49. package/src/tools/deploy-preview.ts +316 -316
  50. package/src/tools/design.ts +825 -825
  51. package/src/tools/docs.ts +350 -350
  52. package/src/tools/generate.ts +4 -3
  53. package/src/tools/guard.ts +1 -1
  54. package/src/tools/history.ts +1 -1
  55. package/src/tools/hydration.ts +64 -64
  56. package/src/tools/index.ts +0 -118
  57. package/src/tools/kitchen.ts +72 -72
  58. package/src/tools/lint.ts +226 -226
  59. package/src/tools/loop-close.ts +175 -175
  60. package/src/tools/negotiate.ts +263 -263
  61. package/src/tools/resource.ts +1 -1
  62. package/src/tools/run-tests.ts +424 -424
  63. package/src/tools/runtime.ts +1 -1
  64. package/src/tools/seo.ts +1 -1
  65. package/src/tools/slot-validation.ts +19 -19
  66. package/src/tools/spec.ts +201 -201
  67. package/src/tools/transaction.ts +1 -1
  68. package/src/tx-lock.ts +73 -73
  69. package/src/utils/runtime-control.ts +52 -52
  70. package/src/utils/withWarnings.ts +1 -1
  71. package/src/resources/skills/mandu-agent-workflow/SKILL.md +0 -124
  72. package/src/resources/skills/mandu-agent-workflow/metadata.json +0 -7
  73. package/src/resources/skills/mandu-composition/SKILL.md +0 -131
  74. package/src/resources/skills/mandu-composition/metadata.json +0 -13
  75. package/src/resources/skills/mandu-composition/rules/_sections.md +0 -26
  76. package/src/resources/skills/mandu-composition/rules/_template.md +0 -77
  77. package/src/resources/skills/mandu-composition/rules/comp-arch-avoid-boolean-props.md +0 -146
  78. package/src/resources/skills/mandu-composition/rules/comp-arch-compound-components.md +0 -164
  79. package/src/resources/skills/mandu-composition/rules/comp-island-event.md +0 -161
  80. package/src/resources/skills/mandu-composition/rules/comp-island-slot-split.md +0 -167
  81. package/src/resources/skills/mandu-composition/rules/comp-pattern-children.md +0 -149
  82. package/src/resources/skills/mandu-composition/rules/comp-state-context-interface.md +0 -148
  83. package/src/resources/skills/mandu-composition/rules/comp-state-lift-state.md +0 -150
  84. package/src/resources/skills/mandu-deployment/SKILL.md +0 -135
  85. package/src/resources/skills/mandu-deployment/_sections.md +0 -41
  86. package/src/resources/skills/mandu-deployment/_template.md +0 -38
  87. package/src/resources/skills/mandu-deployment/metadata.json +0 -13
  88. package/src/resources/skills/mandu-deployment/rules/db-provider-supabase.md +0 -300
  89. package/src/resources/skills/mandu-deployment/rules/deploy-build-bun.md +0 -109
  90. package/src/resources/skills/mandu-deployment/rules/deploy-build-output.md +0 -115
  91. package/src/resources/skills/mandu-deployment/rules/deploy-cicd-github.md +0 -219
  92. package/src/resources/skills/mandu-deployment/rules/deploy-docker-bun.md +0 -150
  93. package/src/resources/skills/mandu-deployment/rules/deploy-docker-compose.md +0 -223
  94. package/src/resources/skills/mandu-deployment/rules/deploy-platform-fly.md +0 -152
  95. package/src/resources/skills/mandu-deployment/rules/deploy-platform-render.md +0 -179
  96. package/src/resources/skills/mandu-deployment/rules/deploy-platform-vercel.md +0 -140
  97. package/src/resources/skills/mandu-fs-routes/SKILL.md +0 -122
  98. package/src/resources/skills/mandu-fs-routes/metadata.json +0 -12
  99. package/src/resources/skills/mandu-fs-routes/rules/_sections.md +0 -36
  100. package/src/resources/skills/mandu-fs-routes/rules/_template.md +0 -69
  101. package/src/resources/skills/mandu-fs-routes/rules/routes-api-methods.md +0 -65
  102. package/src/resources/skills/mandu-fs-routes/rules/routes-dynamic-param.md +0 -93
  103. package/src/resources/skills/mandu-fs-routes/rules/routes-naming-page.md +0 -55
  104. package/src/resources/skills/mandu-guard/SKILL.md +0 -162
  105. package/src/resources/skills/mandu-guard/metadata.json +0 -12
  106. package/src/resources/skills/mandu-guard/rules/_sections.md +0 -36
  107. package/src/resources/skills/mandu-guard/rules/_template.md +0 -82
  108. package/src/resources/skills/mandu-guard/rules/guard-config-rules.md +0 -100
  109. package/src/resources/skills/mandu-guard/rules/guard-layer-direction.md +0 -76
  110. package/src/resources/skills/mandu-guard/rules/guard-preset-mandu.md +0 -81
  111. package/src/resources/skills/mandu-guard/rules/guard-validate-import.md +0 -80
  112. package/src/resources/skills/mandu-hydration/SKILL.md +0 -139
  113. package/src/resources/skills/mandu-hydration/metadata.json +0 -12
  114. package/src/resources/skills/mandu-hydration/rules/_sections.md +0 -31
  115. package/src/resources/skills/mandu-hydration/rules/_template.md +0 -72
  116. package/src/resources/skills/mandu-hydration/rules/hydration-data-event.md +0 -109
  117. package/src/resources/skills/mandu-hydration/rules/hydration-directive-use-client.md +0 -55
  118. package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +0 -160
  119. package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +0 -91
  120. package/src/resources/skills/mandu-performance/SKILL.md +0 -125
  121. package/src/resources/skills/mandu-performance/metadata.json +0 -14
  122. package/src/resources/skills/mandu-performance/rules/_sections.md +0 -31
  123. package/src/resources/skills/mandu-performance/rules/_template.md +0 -64
  124. package/src/resources/skills/mandu-performance/rules/perf-async-defer-await.md +0 -103
  125. package/src/resources/skills/mandu-performance/rules/perf-async-parallel.md +0 -95
  126. package/src/resources/skills/mandu-performance/rules/perf-bun-file.md +0 -124
  127. package/src/resources/skills/mandu-performance/rules/perf-bun-serve.md +0 -125
  128. package/src/resources/skills/mandu-performance/rules/perf-bundle-imports.md +0 -80
  129. package/src/resources/skills/mandu-performance/rules/perf-bundle-island-lazy.md +0 -145
  130. package/src/resources/skills/mandu-performance/rules/perf-cache-react.md +0 -98
  131. package/src/resources/skills/mandu-performance/rules/perf-render-transitions.md +0 -154
  132. package/src/resources/skills/mandu-security/SKILL.md +0 -127
  133. package/src/resources/skills/mandu-security/metadata.json +0 -13
  134. package/src/resources/skills/mandu-security/rules/_sections.md +0 -31
  135. package/src/resources/skills/mandu-security/rules/_template.md +0 -74
  136. package/src/resources/skills/mandu-security/rules/sec-auth-guard.md +0 -127
  137. package/src/resources/skills/mandu-security/rules/sec-env-management.md +0 -133
  138. package/src/resources/skills/mandu-security/rules/sec-input-validate.md +0 -148
  139. package/src/resources/skills/mandu-security/rules/sec-protect-csrf.md +0 -146
  140. package/src/resources/skills/mandu-security/rules/sec-protect-headers.md +0 -138
  141. package/src/resources/skills/mandu-slot/SKILL.md +0 -125
  142. package/src/resources/skills/mandu-slot/metadata.json +0 -12
  143. package/src/resources/skills/mandu-slot/rules/_sections.md +0 -36
  144. package/src/resources/skills/mandu-slot/rules/_template.md +0 -63
  145. package/src/resources/skills/mandu-slot/rules/slot-basic-structure.md +0 -38
  146. package/src/resources/skills/mandu-slot/rules/slot-ctx-response.md +0 -56
  147. package/src/resources/skills/mandu-slot/rules/slot-guard-auth.md +0 -59
  148. package/src/resources/skills/mandu-slot/rules/slot-http-methods.md +0 -64
  149. package/src/resources/skills/mandu-styling/SKILL.md +0 -196
  150. package/src/resources/skills/mandu-styling/_sections.md +0 -43
  151. package/src/resources/skills/mandu-styling/_template.md +0 -32
  152. package/src/resources/skills/mandu-styling/metadata.json +0 -15
  153. package/src/resources/skills/mandu-styling/rules/style-component-compound.md +0 -235
  154. package/src/resources/skills/mandu-styling/rules/style-component-slots.md +0 -255
  155. package/src/resources/skills/mandu-styling/rules/style-component-tokens.md +0 -205
  156. package/src/resources/skills/mandu-styling/rules/style-island-animations.md +0 -272
  157. package/src/resources/skills/mandu-styling/rules/style-island-scoping.md +0 -167
  158. package/src/resources/skills/mandu-styling/rules/style-island-variants.md +0 -221
  159. package/src/resources/skills/mandu-styling/rules/style-perf-critical.md +0 -209
  160. package/src/resources/skills/mandu-styling/rules/style-perf-purge.md +0 -192
  161. package/src/resources/skills/mandu-styling/rules/style-setup-modules.md +0 -162
  162. package/src/resources/skills/mandu-styling/rules/style-setup-panda.md +0 -164
  163. package/src/resources/skills/mandu-styling/rules/style-setup-tailwind.md +0 -170
  164. package/src/resources/skills/mandu-styling/rules/style-tailwind-v4-gotchas.md +0 -179
  165. package/src/resources/skills/mandu-styling/rules/style-theme-darkmode.md +0 -229
  166. package/src/resources/skills/mandu-testing/SKILL.md +0 -132
  167. package/src/resources/skills/mandu-testing/metadata.json +0 -13
  168. package/src/resources/skills/mandu-testing/rules/_sections.md +0 -26
  169. package/src/resources/skills/mandu-testing/rules/_template.md +0 -65
  170. package/src/resources/skills/mandu-testing/rules/test-component-island.md +0 -195
  171. package/src/resources/skills/mandu-testing/rules/test-e2e-playwright.md +0 -196
  172. package/src/resources/skills/mandu-testing/rules/test-mock-fetch.md +0 -219
  173. package/src/resources/skills/mandu-testing/rules/test-slot-unit.md +0 -192
  174. package/src/resources/skills/mandu-ui/SKILL.md +0 -159
  175. package/src/resources/skills/mandu-ui/_sections.md +0 -23
  176. package/src/resources/skills/mandu-ui/_template.md +0 -32
  177. package/src/resources/skills/mandu-ui/metadata.json +0 -13
  178. package/src/resources/skills/mandu-ui/rules/ui-accessibility-aria.md +0 -232
  179. package/src/resources/skills/mandu-ui/rules/ui-accessibility-focus.md +0 -238
  180. package/src/resources/skills/mandu-ui/rules/ui-composition-patterns.md +0 -259
  181. package/src/resources/skills/mandu-ui/rules/ui-island-integration.md +0 -258
  182. package/src/resources/skills/mandu-ui/rules/ui-radix-patterns.md +0 -213
  183. package/src/resources/skills/mandu-ui/rules/ui-shadcn-setup.md +0 -209
@@ -11,7 +11,7 @@ import {
11
11
  loadManifest,
12
12
  needsHydration,
13
13
  } from "@mandujs/core";
14
- import type { BundleManifest } from "@mandujs/core/bundler";
14
+ import type { BundleManifest } from "@mandujs/core/compat/bundler/index";
15
15
  import { getDevServerState } from "./project.js";
16
16
  import { readRuntimeControl } from "../utils/runtime-control.js";
17
17
  import path from "path";
package/src/tools/seo.ts CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  type Sitemap,
19
19
  type RobotsFile,
20
20
  type JsonLd,
21
- } from "@mandujs/core";
21
+ } from "@mandujs/core/compat/seo/index";
22
22
  import { getProjectPaths } from "../utils/project.js";
23
23
  import * as fs from "fs";
24
24
  import * as path from "path";
@@ -1,13 +1,13 @@
1
- import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
- import {
3
- DEFAULT_SLOT_CONSTRAINTS,
4
- API_SLOT_CONSTRAINTS,
5
- READONLY_SLOT_CONSTRAINTS,
6
- } from "@mandujs/core";
7
-
8
- export const slotValidationToolDefinitions: Tool[] = [
9
- {
10
- name: "mandu.slot.constraints",
1
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ import {
3
+ DEFAULT_SLOT_CONSTRAINTS,
4
+ API_SLOT_CONSTRAINTS,
5
+ READONLY_SLOT_CONSTRAINTS,
6
+ } from "@mandujs/core";
7
+
8
+ export const slotValidationToolDefinitions: Tool[] = [
9
+ {
10
+ name: "mandu.slot.constraints",
11
11
  description:
12
12
  "Get recommended slot constraint presets (default, api, readonly).",
13
13
  annotations: {
@@ -27,10 +27,10 @@ export const slotValidationToolDefinitions: Tool[] = [
27
27
  },
28
28
  ];
29
29
 
30
- export function slotValidationTools(projectRoot: string) {
31
- void projectRoot;
32
- const handlers: Record<string, (args: Record<string, unknown>) => Promise<unknown>> = {
33
- "mandu.slot.constraints": async (args: Record<string, unknown>) => {
30
+ export function slotValidationTools(projectRoot: string) {
31
+ void projectRoot;
32
+ const handlers: Record<string, (args: Record<string, unknown>) => Promise<unknown>> = {
33
+ "mandu.slot.constraints": async (args: Record<string, unknown>) => {
34
34
  const { preset } = args as { preset?: "default" | "api" | "readonly" };
35
35
 
36
36
  const presets = {
@@ -84,10 +84,10 @@ Mandu.filling()
84
84
  `.trim(),
85
85
  };
86
86
  },
87
- };
88
-
89
- // Backward-compatible aliases
90
- handlers["mandu_get_slot_constraints"] = handlers["mandu.slot.constraints"];
91
-
87
+ };
88
+
89
+ // Backward-compatible aliases
90
+ handlers["mandu_get_slot_constraints"] = handlers["mandu.slot.constraints"];
91
+
92
92
  return handlers;
93
93
  }
package/src/tools/spec.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
- import {
3
- loadManifest,
4
- generateManifest,
5
- } from "@mandujs/core";
6
- import { getProjectPaths, isInsideProject, readJsonFile } from "../utils/project.js";
7
- import path from "path";
8
- import fs from "fs/promises";
2
+ import {
3
+ loadManifest,
4
+ generateManifest,
5
+ } from "@mandujs/core";
6
+ import { getProjectPaths, isInsideProject, readJsonFile } from "../utils/project.js";
7
+ import path from "path";
8
+ import fs from "fs/promises";
9
9
 
10
10
  export const specToolDefinitions: Tool[] = [
11
11
  {
@@ -21,8 +21,8 @@ export const specToolDefinitions: Tool[] = [
21
21
  required: [],
22
22
  },
23
23
  },
24
- {
25
- name: "mandu.route.get",
24
+ {
25
+ name: "mandu.route.get",
26
26
  description:
27
27
  "Get full details of a specific route by its ID. Use before modifying a route.",
28
28
  annotations: {
@@ -37,32 +37,32 @@ export const specToolDefinitions: Tool[] = [
37
37
  },
38
38
  },
39
39
  required: ["routeId"],
40
- },
41
- },
42
- {
43
- name: "mandu.route.boundaries",
44
- description:
45
- "Inspect compiler-discovered client boundary metadata for one route or all routes, including route manifest records and optional bundle manifest entries.",
46
- annotations: {
47
- readOnlyHint: true,
48
- },
49
- inputSchema: {
50
- type: "object",
51
- properties: {
52
- routeId: {
53
- type: "string",
54
- description: "Optional route ID to inspect. Omit to list boundaries for all routes.",
55
- },
56
- includeBundle: {
57
- type: "boolean",
58
- description: "When true, also correlate route boundaries with .mandu/manifest.json boundary bundle entries.",
59
- },
60
- },
61
- required: [],
62
- },
63
- },
64
- {
65
- name: "mandu.route.add",
40
+ },
41
+ },
42
+ {
43
+ name: "mandu.route.boundaries",
44
+ description:
45
+ "Inspect compiler-discovered client boundary metadata for one route or all routes, including route manifest records and optional bundle manifest entries.",
46
+ annotations: {
47
+ readOnlyHint: true,
48
+ },
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {
52
+ routeId: {
53
+ type: "string",
54
+ description: "Optional route ID to inspect. Omit to list boundaries for all routes.",
55
+ },
56
+ includeBundle: {
57
+ type: "boolean",
58
+ description: "When true, also correlate route boundaries with .mandu/manifest.json boundary bundle entries.",
59
+ },
60
+ },
61
+ required: [],
62
+ },
63
+ },
64
+ {
65
+ name: "mandu.route.add",
66
66
  description:
67
67
  "Scaffold a new route in app/ with optional slot and contract files, then regenerate the manifest.",
68
68
  annotations: {
@@ -151,7 +151,7 @@ export function specTools(projectRoot: string) {
151
151
  };
152
152
  },
153
153
 
154
- "mandu.route.get": async (args: Record<string, unknown>) => {
154
+ "mandu.route.get": async (args: Record<string, unknown>) => {
155
155
  const { routeId } = args as { routeId: string };
156
156
 
157
157
  const result = await loadManifest(paths.manifestPath);
@@ -164,158 +164,158 @@ export function specTools(projectRoot: string) {
164
164
  return { error: `Route not found: ${routeId}` };
165
165
  }
166
166
 
167
- return { route };
168
- },
169
-
170
- "mandu.route.boundaries": async (args: Record<string, unknown>) => {
171
- const { routeId, includeBundle = false } = args as { routeId?: string; includeBundle?: boolean };
172
-
173
- const result = await loadManifest(paths.manifestPath);
174
- if (!result.success || !result.data) {
175
- return { error: result.errors };
176
- }
177
-
178
- const bundleManifestPath = path.join(projectRoot, ".mandu", "manifest.json");
179
- const bundleManifest = includeBundle
180
- ? await readJsonFile<{
181
- boundaries?: Record<string, {
182
- route?: string;
183
- js?: string;
184
- module?: string;
185
- exportName?: string;
186
- priority?: string;
187
- hydrate?: string;
188
- }>;
189
- }>(bundleManifestPath)
190
- : null;
191
-
192
- const routes = routeId
193
- ? result.data.routes.filter((route) => route.id === routeId)
194
- : result.data.routes;
195
-
196
- if (routeId && routes.length === 0) {
197
- return { error: `Route not found: ${routeId}` };
198
- }
199
-
200
- const inspected = routes.map((route) => {
201
- const boundaries = route.boundaries ?? [];
202
- const diagnostics: Array<{
203
- code: string;
204
- severity: "warning";
205
- message: string;
206
- routeId: string;
207
- boundaryId?: string;
208
- }> = [];
209
-
210
- if (includeBundle && boundaries.length > 0 && !bundleManifest) {
211
- diagnostics.push({
212
- code: "MANDU_BOUNDARY_BUNDLE_MANIFEST_MISSING",
213
- severity: "warning",
214
- message: "Route has compiler-discovered client boundaries, but .mandu/manifest.json was not found. Run mandu build to inspect generated boundary chunks.",
215
- routeId: route.id,
216
- });
217
- }
218
-
219
- const boundaryRecords = boundaries.map((boundary) => {
220
- const bundle = bundleManifest?.boundaries?.[boundary.id];
221
- if (includeBundle && bundleManifest && !bundle) {
222
- diagnostics.push({
223
- code: "MANDU_BOUNDARY_BUNDLE_ENTRY_MISSING",
224
- severity: "warning",
225
- message: `Boundary '${boundary.id}' is present in routes manifest but missing from .mandu/manifest.json boundaries.`,
226
- routeId: route.id,
227
- boundaryId: boundary.id,
228
- });
229
- }
230
-
231
- return {
232
- id: boundary.id,
233
- routeId: boundary.routeId,
234
- module: boundary.module,
235
- importSpecifier: boundary.importSpecifier,
236
- exportName: boundary.exportName,
237
- localName: boundary.localName,
238
- hydrate: boundary.hydrate,
239
- ordinal: boundary.ordinal,
240
- propsSource: boundary.propsSource,
241
- propsKeys: boundary.propsKeys ?? [],
242
- hasSpreadProps: !!boundary.hasSpreadProps,
243
- source: boundary.source,
244
- bundle: bundle
245
- ? {
246
- js: bundle.js ?? null,
247
- priority: bundle.priority ?? null,
248
- hydrate: bundle.hydrate ?? null,
249
- }
250
- : null,
251
- };
252
- });
253
-
254
- return {
255
- routeId: route.id,
256
- pattern: route.pattern,
257
- module: route.module,
258
- boundaryCount: boundaryRecords.length,
259
- boundaries: boundaryRecords,
260
- diagnostics,
261
- };
262
- });
263
-
264
- return {
265
- source: {
266
- routesManifest: ".mandu/routes.manifest.json",
267
- bundleManifest: includeBundle && bundleManifest ? ".mandu/manifest.json" : null,
268
- },
269
- includeBundle,
270
- routeId: routeId ?? null,
271
- routeCount: inspected.length,
272
- boundaryCount: inspected.reduce((count, route) => count + route.boundaryCount, 0),
273
- routes: inspected,
274
- };
275
- },
276
-
277
- "mandu.route.add": async (args: Record<string, unknown>) => {
167
+ return { route };
168
+ },
169
+
170
+ "mandu.route.boundaries": async (args: Record<string, unknown>) => {
171
+ const { routeId, includeBundle = false } = args as { routeId?: string; includeBundle?: boolean };
172
+
173
+ const result = await loadManifest(paths.manifestPath);
174
+ if (!result.success || !result.data) {
175
+ return { error: result.errors };
176
+ }
177
+
178
+ const bundleManifestPath = path.join(projectRoot, ".mandu", "manifest.json");
179
+ const bundleManifest = includeBundle
180
+ ? await readJsonFile<{
181
+ boundaries?: Record<string, {
182
+ route?: string;
183
+ js?: string;
184
+ module?: string;
185
+ exportName?: string;
186
+ priority?: string;
187
+ hydrate?: string;
188
+ }>;
189
+ }>(bundleManifestPath)
190
+ : null;
191
+
192
+ const routes = routeId
193
+ ? result.data.routes.filter((route) => route.id === routeId)
194
+ : result.data.routes;
195
+
196
+ if (routeId && routes.length === 0) {
197
+ return { error: `Route not found: ${routeId}` };
198
+ }
199
+
200
+ const inspected = routes.map((route) => {
201
+ const boundaries = route.boundaries ?? [];
202
+ const diagnostics: Array<{
203
+ code: string;
204
+ severity: "warning";
205
+ message: string;
206
+ routeId: string;
207
+ boundaryId?: string;
208
+ }> = [];
209
+
210
+ if (includeBundle && boundaries.length > 0 && !bundleManifest) {
211
+ diagnostics.push({
212
+ code: "MANDU_BOUNDARY_BUNDLE_MANIFEST_MISSING",
213
+ severity: "warning",
214
+ message: "Route has compiler-discovered client boundaries, but .mandu/manifest.json was not found. Run mandu build to inspect generated boundary chunks.",
215
+ routeId: route.id,
216
+ });
217
+ }
218
+
219
+ const boundaryRecords = boundaries.map((boundary) => {
220
+ const bundle = bundleManifest?.boundaries?.[boundary.id];
221
+ if (includeBundle && bundleManifest && !bundle) {
222
+ diagnostics.push({
223
+ code: "MANDU_BOUNDARY_BUNDLE_ENTRY_MISSING",
224
+ severity: "warning",
225
+ message: `Boundary '${boundary.id}' is present in routes manifest but missing from .mandu/manifest.json boundaries.`,
226
+ routeId: route.id,
227
+ boundaryId: boundary.id,
228
+ });
229
+ }
230
+
231
+ return {
232
+ id: boundary.id,
233
+ routeId: boundary.routeId,
234
+ module: boundary.module,
235
+ importSpecifier: boundary.importSpecifier,
236
+ exportName: boundary.exportName,
237
+ localName: boundary.localName,
238
+ hydrate: boundary.hydrate,
239
+ ordinal: boundary.ordinal,
240
+ propsSource: boundary.propsSource,
241
+ propsKeys: boundary.propsKeys ?? [],
242
+ hasSpreadProps: !!boundary.hasSpreadProps,
243
+ source: boundary.source,
244
+ bundle: bundle
245
+ ? {
246
+ js: bundle.js ?? null,
247
+ priority: bundle.priority ?? null,
248
+ hydrate: bundle.hydrate ?? null,
249
+ }
250
+ : null,
251
+ };
252
+ });
253
+
254
+ return {
255
+ routeId: route.id,
256
+ pattern: route.pattern,
257
+ module: route.module,
258
+ boundaryCount: boundaryRecords.length,
259
+ boundaries: boundaryRecords,
260
+ diagnostics,
261
+ };
262
+ });
263
+
264
+ return {
265
+ source: {
266
+ routesManifest: ".mandu/routes.manifest.json",
267
+ bundleManifest: includeBundle && bundleManifest ? ".mandu/manifest.json" : null,
268
+ },
269
+ includeBundle,
270
+ routeId: routeId ?? null,
271
+ routeCount: inspected.length,
272
+ boundaryCount: inspected.reduce((count, route) => count + route.boundaryCount, 0),
273
+ routes: inspected,
274
+ };
275
+ },
276
+
277
+ "mandu.route.add": async (args: Record<string, unknown>) => {
278
278
  const { path: routePath, kind, withSlot = true, withContract = false } = args as {
279
279
  path: string;
280
280
  kind: "api" | "page";
281
281
  withSlot?: boolean;
282
282
  withContract?: boolean;
283
- };
284
-
285
- const createdFiles: string[] = [];
286
- const normalizedRoutePath = normalizeRoutePath(routePath);
287
- if (!normalizedRoutePath) {
288
- return {
289
- error: "Route path must be a non-empty relative path inside app/",
290
- };
291
- }
292
-
293
- // Scaffold app/ file
294
- const fileName = kind === "api" ? "route.ts" : "page.tsx";
295
- const appFilePath = path.resolve(paths.appDir, normalizedRoutePath, fileName);
296
- if (!isInsideProject(appFilePath, paths.appDir)) {
297
- return {
298
- error: "Route path resolves outside app/",
299
- };
300
- }
301
- if (await Bun.file(appFilePath).exists()) {
302
- return {
303
- error: `Route source already exists: app/${normalizedRoutePath}/${fileName}`,
304
- };
305
- }
306
- const appFileDir = path.dirname(appFilePath);
307
-
308
- await fs.mkdir(appFileDir, { recursive: true });
283
+ };
284
+
285
+ const createdFiles: string[] = [];
286
+ const normalizedRoutePath = normalizeRoutePath(routePath);
287
+ if (!normalizedRoutePath) {
288
+ return {
289
+ error: "Route path must be a non-empty relative path inside app/",
290
+ };
291
+ }
292
+
293
+ // Scaffold app/ file
294
+ const fileName = kind === "api" ? "route.ts" : "page.tsx";
295
+ const appFilePath = path.resolve(paths.appDir, normalizedRoutePath, fileName);
296
+ if (!isInsideProject(appFilePath, paths.appDir)) {
297
+ return {
298
+ error: "Route path resolves outside app/",
299
+ };
300
+ }
301
+ if (await Bun.file(appFilePath).exists()) {
302
+ return {
303
+ error: `Route source already exists: app/${normalizedRoutePath}/${fileName}`,
304
+ };
305
+ }
306
+ const appFileDir = path.dirname(appFilePath);
307
+
308
+ await fs.mkdir(appFileDir, { recursive: true });
309
309
 
310
310
  if (kind === "api") {
311
311
  await Bun.write(appFilePath, `export function GET(req: Request) {\n return Response.json({ message: "Hello" });\n}\n`);
312
- } else {
313
- await Bun.write(appFilePath, `export default function Page() {\n return <div>Page</div>;\n}\n`);
314
- }
315
- createdFiles.push(`app/${normalizedRoutePath}/${fileName}`);
316
-
317
- // Derive route ID from path
318
- const routeId = normalizedRoutePath.replace(/\//g, "-").replace(/[\[\]\.]/g, "");
312
+ } else {
313
+ await Bun.write(appFilePath, `export default function Page() {\n return <div>Page</div>;\n}\n`);
314
+ }
315
+ createdFiles.push(`app/${normalizedRoutePath}/${fileName}`);
316
+
317
+ // Derive route ID from path
318
+ const routeId = normalizedRoutePath.replace(/\//g, "-").replace(/[\[\]\.]/g, "");
319
319
 
320
320
  // Scaffold slot if requested
321
321
  if (withSlot) {
@@ -405,27 +405,27 @@ export function specTools(projectRoot: string) {
405
405
  };
406
406
 
407
407
  // Backward-compatible aliases (deprecated)
408
- handlers["mandu_list_routes"] = handlers["mandu.route.list"];
409
- handlers["mandu_get_route"] = handlers["mandu.route.get"];
408
+ handlers["mandu_list_routes"] = handlers["mandu.route.list"];
409
+ handlers["mandu_get_route"] = handlers["mandu.route.get"];
410
410
  handlers["mandu_add_route"] = handlers["mandu.route.add"];
411
411
  handlers["mandu_delete_route"] = handlers["mandu.route.delete"];
412
412
  handlers["mandu_validate_manifest"] = handlers["mandu.manifest.validate"];
413
-
414
- return handlers;
415
- }
416
-
417
- function normalizeRoutePath(value: unknown): string | null {
418
- if (typeof value !== "string") return null;
419
- const trimmed = value.trim().replace(/\\/g, "/");
420
- const withoutAppPrefix = trimmed.replace(/^app\//, "");
421
- const normalized = withoutAppPrefix.replace(/^\/+|\/+$/g, "");
422
- if (!normalized) return null;
423
-
424
- const segments = normalized.split("/").filter(Boolean);
425
- if (segments.length === 0) return null;
426
- if (segments.some((segment) => segment === "." || segment === ".." || segment.includes(":") || segment.includes("\0"))) {
427
- return null;
428
- }
429
-
430
- return segments.join("/");
431
- }
413
+
414
+ return handlers;
415
+ }
416
+
417
+ function normalizeRoutePath(value: unknown): string | null {
418
+ if (typeof value !== "string") return null;
419
+ const trimmed = value.trim().replace(/\\/g, "/");
420
+ const withoutAppPrefix = trimmed.replace(/^app\//, "");
421
+ const normalized = withoutAppPrefix.replace(/^\/+|\/+$/g, "");
422
+ if (!normalized) return null;
423
+
424
+ const segments = normalized.split("/").filter(Boolean);
425
+ if (segments.length === 0) return null;
426
+ if (segments.some((segment) => segment === "." || segment === ".." || segment.includes(":") || segment.includes("\0"))) {
427
+ return null;
428
+ }
429
+
430
+ return segments.join("/");
431
+ }
@@ -5,7 +5,7 @@ import {
5
5
  rollbackChange,
6
6
  getTransactionStatus,
7
7
  hasActiveTransaction,
8
- } from "@mandujs/core/change";
8
+ } from "@mandujs/core/compat/change/index";
9
9
  import { acquireLock, releaseLock, checkLock, requireLock } from "../tx-lock.js";
10
10
 
11
11
  export const transactionToolDefinitions: Tool[] = [