@pascal-app/mcp 0.2.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 (249) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +330 -0
  3. package/dist/bin/pascal-mcp.d.ts +3 -0
  4. package/dist/bin/pascal-mcp.d.ts.map +1 -0
  5. package/dist/bin/pascal-mcp.js +91 -0
  6. package/dist/bridge/index.d.ts +3 -0
  7. package/dist/bridge/index.d.ts.map +1 -0
  8. package/dist/bridge/index.js +1 -0
  9. package/dist/bridge/node-shims.d.ts +16 -0
  10. package/dist/bridge/node-shims.d.ts.map +1 -0
  11. package/dist/bridge/node-shims.js +25 -0
  12. package/dist/bridge/scene-bridge.d.ts +158 -0
  13. package/dist/bridge/scene-bridge.d.ts.map +1 -0
  14. package/dist/bridge/scene-bridge.js +482 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +4 -0
  18. package/dist/lib/rehydrate-site-children.d.ts +12 -0
  19. package/dist/lib/rehydrate-site-children.d.ts.map +1 -0
  20. package/dist/lib/rehydrate-site-children.js +40 -0
  21. package/dist/lib/safe-fetch.d.ts +19 -0
  22. package/dist/lib/safe-fetch.d.ts.map +1 -0
  23. package/dist/lib/safe-fetch.js +221 -0
  24. package/dist/operations/index.d.ts +2 -0
  25. package/dist/operations/index.d.ts.map +1 -0
  26. package/dist/operations/index.js +1 -0
  27. package/dist/operations/scene-operations.d.ts +67 -0
  28. package/dist/operations/scene-operations.d.ts.map +1 -0
  29. package/dist/operations/scene-operations.js +192 -0
  30. package/dist/prompts/from-brief.d.ts +11 -0
  31. package/dist/prompts/from-brief.d.ts.map +1 -0
  32. package/dist/prompts/from-brief.js +48 -0
  33. package/dist/prompts/index.d.ts +10 -0
  34. package/dist/prompts/index.d.ts.map +1 -0
  35. package/dist/prompts/index.js +14 -0
  36. package/dist/prompts/iterate-on-feedback.d.ts +11 -0
  37. package/dist/prompts/iterate-on-feedback.d.ts.map +1 -0
  38. package/dist/prompts/iterate-on-feedback.js +41 -0
  39. package/dist/prompts/renovation-from-photos.d.ts +24 -0
  40. package/dist/prompts/renovation-from-photos.d.ts.map +1 -0
  41. package/dist/prompts/renovation-from-photos.js +135 -0
  42. package/dist/prompts/scene-guidance.d.ts +2 -0
  43. package/dist/prompts/scene-guidance.d.ts.map +1 -0
  44. package/dist/prompts/scene-guidance.js +24 -0
  45. package/dist/resources/agent-guide.d.ts +5 -0
  46. package/dist/resources/agent-guide.d.ts.map +1 -0
  47. package/dist/resources/agent-guide.js +85 -0
  48. package/dist/resources/catalog-items.d.ts +10 -0
  49. package/dist/resources/catalog-items.d.ts.map +1 -0
  50. package/dist/resources/catalog-items.js +29 -0
  51. package/dist/resources/constraints.d.ts +9 -0
  52. package/dist/resources/constraints.d.ts.map +1 -0
  53. package/dist/resources/constraints.js +69 -0
  54. package/dist/resources/index.d.ts +15 -0
  55. package/dist/resources/index.d.ts.map +1 -0
  56. package/dist/resources/index.js +23 -0
  57. package/dist/resources/scene-current.d.ts +9 -0
  58. package/dist/resources/scene-current.d.ts.map +1 -0
  59. package/dist/resources/scene-current.js +20 -0
  60. package/dist/resources/scene-summary.d.ts +10 -0
  61. package/dist/resources/scene-summary.d.ts.map +1 -0
  62. package/dist/resources/scene-summary.js +196 -0
  63. package/dist/server.d.ts +14 -0
  64. package/dist/server.d.ts.map +1 -0
  65. package/dist/server.js +18 -0
  66. package/dist/storage/index.d.ts +13 -0
  67. package/dist/storage/index.d.ts.map +1 -0
  68. package/dist/storage/index.js +14 -0
  69. package/dist/storage/slug.d.ts +21 -0
  70. package/dist/storage/slug.d.ts.map +1 -0
  71. package/dist/storage/slug.js +62 -0
  72. package/dist/storage/sqlite-driver.d.ts +18 -0
  73. package/dist/storage/sqlite-driver.d.ts.map +1 -0
  74. package/dist/storage/sqlite-driver.js +32 -0
  75. package/dist/storage/sqlite-scene-store.d.ts +51 -0
  76. package/dist/storage/sqlite-scene-store.d.ts.map +1 -0
  77. package/dist/storage/sqlite-scene-store.js +526 -0
  78. package/dist/storage/types.d.ts +136 -0
  79. package/dist/storage/types.d.ts.map +1 -0
  80. package/dist/storage/types.js +28 -0
  81. package/dist/templates/empty-studio.d.ts +8 -0
  82. package/dist/templates/empty-studio.d.ts.map +1 -0
  83. package/dist/templates/empty-studio.js +230 -0
  84. package/dist/templates/garden-house.d.ts +8 -0
  85. package/dist/templates/garden-house.d.ts.map +1 -0
  86. package/dist/templates/garden-house.js +242 -0
  87. package/dist/templates/index.d.ts +23 -0
  88. package/dist/templates/index.d.ts.map +1 -0
  89. package/dist/templates/index.js +20 -0
  90. package/dist/templates/two-bedroom.d.ts +8 -0
  91. package/dist/templates/two-bedroom.d.ts.map +1 -0
  92. package/dist/templates/two-bedroom.js +270 -0
  93. package/dist/tools/apply-patch.d.ts +25 -0
  94. package/dist/tools/apply-patch.d.ts.map +1 -0
  95. package/dist/tools/apply-patch.js +59 -0
  96. package/dist/tools/asset-catalog.d.ts +16 -0
  97. package/dist/tools/asset-catalog.d.ts.map +1 -0
  98. package/dist/tools/asset-catalog.js +308 -0
  99. package/dist/tools/check-collisions.d.ts +15 -0
  100. package/dist/tools/check-collisions.d.ts.map +1 -0
  101. package/dist/tools/check-collisions.js +61 -0
  102. package/dist/tools/construction-tools.d.ts +93 -0
  103. package/dist/tools/construction-tools.d.ts.map +1 -0
  104. package/dist/tools/construction-tools.js +369 -0
  105. package/dist/tools/create-level.d.ts +14 -0
  106. package/dist/tools/create-level.d.ts.map +1 -0
  107. package/dist/tools/create-level.js +48 -0
  108. package/dist/tools/create-wall.d.ts +15 -0
  109. package/dist/tools/create-wall.d.ts.map +1 -0
  110. package/dist/tools/create-wall.js +50 -0
  111. package/dist/tools/cut-opening.d.ts +18 -0
  112. package/dist/tools/cut-opening.d.ts.map +1 -0
  113. package/dist/tools/cut-opening.js +57 -0
  114. package/dist/tools/delete-node.d.ts +12 -0
  115. package/dist/tools/delete-node.d.ts.map +1 -0
  116. package/dist/tools/delete-node.js +37 -0
  117. package/dist/tools/describe-node.d.ts +17 -0
  118. package/dist/tools/describe-node.d.ts.map +1 -0
  119. package/dist/tools/describe-node.js +86 -0
  120. package/dist/tools/duplicate-level.d.ts +12 -0
  121. package/dist/tools/duplicate-level.d.ts.map +1 -0
  122. package/dist/tools/duplicate-level.js +56 -0
  123. package/dist/tools/errors.d.ts +20 -0
  124. package/dist/tools/errors.d.ts.map +1 -0
  125. package/dist/tools/errors.js +25 -0
  126. package/dist/tools/export-glb.d.ts +10 -0
  127. package/dist/tools/export-glb.d.ts.map +1 -0
  128. package/dist/tools/export-glb.js +24 -0
  129. package/dist/tools/export-json.d.ts +11 -0
  130. package/dist/tools/export-json.d.ts.map +1 -0
  131. package/dist/tools/export-json.js +23 -0
  132. package/dist/tools/find-nodes.d.ts +32 -0
  133. package/dist/tools/find-nodes.d.ts.map +1 -0
  134. package/dist/tools/find-nodes.js +105 -0
  135. package/dist/tools/geometry.d.ts +24 -0
  136. package/dist/tools/geometry.d.ts.map +1 -0
  137. package/dist/tools/geometry.js +94 -0
  138. package/dist/tools/get-node.d.ts +11 -0
  139. package/dist/tools/get-node.d.ts.map +1 -0
  140. package/dist/tools/get-node.js +27 -0
  141. package/dist/tools/get-scene.d.ts +11 -0
  142. package/dist/tools/get-scene.d.ts.map +1 -0
  143. package/dist/tools/get-scene.js +26 -0
  144. package/dist/tools/index.d.ts +12 -0
  145. package/dist/tools/index.d.ts.map +1 -0
  146. package/dist/tools/index.js +64 -0
  147. package/dist/tools/live-sync.d.ts +11 -0
  148. package/dist/tools/live-sync.d.ts.map +1 -0
  149. package/dist/tools/live-sync.js +62 -0
  150. package/dist/tools/measure.d.ts +14 -0
  151. package/dist/tools/measure.d.ts.map +1 -0
  152. package/dist/tools/measure.js +137 -0
  153. package/dist/tools/photo-to-scene/index.d.ts +11 -0
  154. package/dist/tools/photo-to-scene/index.d.ts.map +1 -0
  155. package/dist/tools/photo-to-scene/index.js +11 -0
  156. package/dist/tools/photo-to-scene/photo-to-scene.d.ts +26 -0
  157. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -0
  158. package/dist/tools/photo-to-scene/photo-to-scene.js +323 -0
  159. package/dist/tools/place-item.d.ts +15 -0
  160. package/dist/tools/place-item.d.ts.map +1 -0
  161. package/dist/tools/place-item.js +84 -0
  162. package/dist/tools/redo.d.ts +11 -0
  163. package/dist/tools/redo.d.ts.map +1 -0
  164. package/dist/tools/redo.js +25 -0
  165. package/dist/tools/room-tools.d.ts +97 -0
  166. package/dist/tools/room-tools.d.ts.map +1 -0
  167. package/dist/tools/room-tools.js +510 -0
  168. package/dist/tools/scene-lifecycle/create-project.d.ts +33 -0
  169. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -0
  170. package/dist/tools/scene-lifecycle/create-project.js +70 -0
  171. package/dist/tools/scene-lifecycle/delete-scene.d.ts +12 -0
  172. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -0
  173. package/dist/tools/scene-lifecycle/delete-scene.js +42 -0
  174. package/dist/tools/scene-lifecycle/get-project-status.d.ts +31 -0
  175. package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -0
  176. package/dist/tools/scene-lifecycle/get-project-status.js +69 -0
  177. package/dist/tools/scene-lifecycle/index.d.ts +17 -0
  178. package/dist/tools/scene-lifecycle/index.d.ts.map +1 -0
  179. package/dist/tools/scene-lifecycle/index.js +29 -0
  180. package/dist/tools/scene-lifecycle/list-scenes.d.ts +27 -0
  181. package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -0
  182. package/dist/tools/scene-lifecycle/list-scenes.js +49 -0
  183. package/dist/tools/scene-lifecycle/load-scene.d.ts +31 -0
  184. package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -0
  185. package/dist/tools/scene-lifecycle/load-scene.js +55 -0
  186. package/dist/tools/scene-lifecycle/metadata.d.ts +49 -0
  187. package/dist/tools/scene-lifecycle/metadata.d.ts.map +1 -0
  188. package/dist/tools/scene-lifecycle/metadata.js +68 -0
  189. package/dist/tools/scene-lifecycle/rename-scene.d.ts +22 -0
  190. package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -0
  191. package/dist/tools/scene-lifecycle/rename-scene.js +63 -0
  192. package/dist/tools/scene-lifecycle/save-scene.d.ts +42 -0
  193. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -0
  194. package/dist/tools/scene-lifecycle/save-scene.js +130 -0
  195. package/dist/tools/scene-lifecycle/test-utils.d.ts +39 -0
  196. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -0
  197. package/dist/tools/scene-lifecycle/test-utils.js +211 -0
  198. package/dist/tools/scene-query.d.ts +57 -0
  199. package/dist/tools/scene-query.d.ts.map +1 -0
  200. package/dist/tools/scene-query.js +645 -0
  201. package/dist/tools/schemas.d.ts +52 -0
  202. package/dist/tools/schemas.d.ts.map +1 -0
  203. package/dist/tools/schemas.js +42 -0
  204. package/dist/tools/set-zone.d.ts +14 -0
  205. package/dist/tools/set-zone.d.ts.map +1 -0
  206. package/dist/tools/set-zone.js +48 -0
  207. package/dist/tools/templates/create-from-template.d.ts +54 -0
  208. package/dist/tools/templates/create-from-template.d.ts.map +1 -0
  209. package/dist/tools/templates/create-from-template.js +145 -0
  210. package/dist/tools/templates/create-house-from-brief.d.ts +35 -0
  211. package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -0
  212. package/dist/tools/templates/create-house-from-brief.js +170 -0
  213. package/dist/tools/templates/index.d.ts +14 -0
  214. package/dist/tools/templates/index.d.ts.map +1 -0
  215. package/dist/tools/templates/index.js +18 -0
  216. package/dist/tools/templates/list-templates.d.ts +18 -0
  217. package/dist/tools/templates/list-templates.d.ts.map +1 -0
  218. package/dist/tools/templates/list-templates.js +36 -0
  219. package/dist/tools/undo.d.ts +11 -0
  220. package/dist/tools/undo.d.ts.map +1 -0
  221. package/dist/tools/undo.js +25 -0
  222. package/dist/tools/validate-scene.d.ts +14 -0
  223. package/dist/tools/validate-scene.d.ts.map +1 -0
  224. package/dist/tools/validate-scene.js +24 -0
  225. package/dist/tools/variants/generate-variants.d.ts +30 -0
  226. package/dist/tools/variants/generate-variants.d.ts.map +1 -0
  227. package/dist/tools/variants/generate-variants.js +160 -0
  228. package/dist/tools/variants/index.d.ts +9 -0
  229. package/dist/tools/variants/index.d.ts.map +1 -0
  230. package/dist/tools/variants/index.js +9 -0
  231. package/dist/tools/variants/mutations.d.ts +18 -0
  232. package/dist/tools/variants/mutations.d.ts.map +1 -0
  233. package/dist/tools/variants/mutations.js +324 -0
  234. package/dist/tools/vision/analyze-floorplan-image.d.ts +33 -0
  235. package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -0
  236. package/dist/tools/vision/analyze-floorplan-image.js +146 -0
  237. package/dist/tools/vision/analyze-room-photo.d.ts +29 -0
  238. package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -0
  239. package/dist/tools/vision/analyze-room-photo.js +130 -0
  240. package/dist/tools/vision/index.d.ts +12 -0
  241. package/dist/tools/vision/index.d.ts.map +1 -0
  242. package/dist/tools/vision/index.js +14 -0
  243. package/dist/transports/http.d.ts +36 -0
  244. package/dist/transports/http.d.ts.map +1 -0
  245. package/dist/transports/http.js +203 -0
  246. package/dist/transports/stdio.d.ts +14 -0
  247. package/dist/transports/stdio.d.ts.map +1 -0
  248. package/dist/transports/stdio.js +16 -0
  249. package/package.json +86 -0
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ import { publishLiveSceneSnapshot } from './live-sync';
3
+ export const undoInput = {
4
+ steps: z.number().int().positive().optional(),
5
+ };
6
+ export const undoOutput = {
7
+ undone: z.number(),
8
+ };
9
+ export function registerUndo(server, bridge) {
10
+ server.registerTool('undo', {
11
+ title: 'Undo',
12
+ description: 'Undo the most recent N steps in the scene history (default 1). Returns the number of steps actually undone.',
13
+ inputSchema: undoInput,
14
+ outputSchema: undoOutput,
15
+ }, async ({ steps }) => {
16
+ const undone = bridge.undo(steps ?? 1);
17
+ if (undone > 0)
18
+ await publishLiveSceneSnapshot(bridge, 'undo');
19
+ const payload = { undone };
20
+ return {
21
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
22
+ structuredContent: payload,
23
+ };
24
+ });
25
+ }
@@ -0,0 +1,14 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import type { SceneOperations } from '../operations';
4
+ export declare const validateSceneInput: {};
5
+ export declare const validateSceneOutput: {
6
+ valid: z.ZodBoolean;
7
+ errors: z.ZodArray<z.ZodObject<{
8
+ nodeId: z.ZodString;
9
+ path: z.ZodString;
10
+ message: z.ZodString;
11
+ }, z.core.$strip>>;
12
+ };
13
+ export declare function registerValidateScene(server: McpServer, bridge: SceneOperations): void;
14
+ //# sourceMappingURL=validate-scene.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-scene.d.ts","sourceRoot":"","sources":["../../src/tools/validate-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,eAAO,MAAM,kBAAkB,IAAK,CAAA;AAEpC,eAAO,MAAM,mBAAmB;;;;;;;CAS/B,CAAA;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAkBtF"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ export const validateSceneInput = {};
3
+ export const validateSceneOutput = {
4
+ valid: z.boolean(),
5
+ errors: z.array(z.object({
6
+ nodeId: z.string(),
7
+ path: z.string(),
8
+ message: z.string(),
9
+ })),
10
+ };
11
+ export function registerValidateScene(server, bridge) {
12
+ server.registerTool('validate_scene', {
13
+ title: 'Validate scene',
14
+ description: 'Run Zod validation against every node in the scene. Returns `{ valid, errors }` where each error has `{ nodeId, path, message }`.',
15
+ inputSchema: validateSceneInput,
16
+ outputSchema: validateSceneOutput,
17
+ }, async () => {
18
+ const result = bridge.validateScene();
19
+ return {
20
+ content: [{ type: 'text', text: JSON.stringify(result) }],
21
+ structuredContent: result,
22
+ };
23
+ });
24
+ }
@@ -0,0 +1,30 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import type { SceneOperations } from '../../operations';
4
+ export declare const generateVariantsInput: {
5
+ baseSceneId: z.ZodOptional<z.ZodString>;
6
+ count: z.ZodDefault<z.ZodNumber>;
7
+ vary: z.ZodDefault<z.ZodArray<z.ZodEnum<{
8
+ "wall-thickness": "wall-thickness";
9
+ "wall-height": "wall-height";
10
+ "zone-labels": "zone-labels";
11
+ "room-proportions": "room-proportions";
12
+ "open-plan": "open-plan";
13
+ "door-positions": "door-positions";
14
+ "fence-style": "fence-style";
15
+ }>>>;
16
+ seed: z.ZodOptional<z.ZodNumber>;
17
+ save: z.ZodDefault<z.ZodBoolean>;
18
+ };
19
+ export declare const generateVariantsOutput: {
20
+ variants: z.ZodArray<z.ZodObject<{
21
+ index: z.ZodNumber;
22
+ description: z.ZodString;
23
+ nodeCount: z.ZodNumber;
24
+ sceneId: z.ZodOptional<z.ZodString>;
25
+ url: z.ZodOptional<z.ZodString>;
26
+ graph: z.ZodOptional<z.ZodAny>;
27
+ }, z.core.$strip>>;
28
+ };
29
+ export declare function registerGenerateVariants(server: McpServer, bridge: SceneOperations): void;
30
+ //# sourceMappingURL=generate-variants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-variants.d.ts","sourceRoot":"","sources":["../../../src/tools/variants/generate-variants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAcvD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;CAYjC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;CAWlC,CAAA;AAmDD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA4FzF"}
@@ -0,0 +1,160 @@
1
+ import { forkSceneGraph } from '@pascal-app/core/clone-scene-graph';
2
+ import { AnyNode as AnyNodeSchema } from '@pascal-app/core/schema';
3
+ import { z } from 'zod';
4
+ import { ErrorCode, throwMcpError } from '../errors';
5
+ import { applyMutation, describeVariant, mulberry32 } from './mutations';
6
+ const MUTATION_KINDS = [
7
+ 'wall-thickness',
8
+ 'wall-height',
9
+ 'zone-labels',
10
+ 'room-proportions',
11
+ 'open-plan',
12
+ 'door-positions',
13
+ 'fence-style',
14
+ ];
15
+ export const generateVariantsInput = {
16
+ baseSceneId: z
17
+ .string()
18
+ .optional()
19
+ .describe('If set, fork from this saved scene; else fork from current bridge state.'),
20
+ count: z.number().int().min(1).max(10).default(3),
21
+ vary: z.array(z.enum(MUTATION_KINDS)).min(1).default(['wall-thickness', 'wall-height']),
22
+ seed: z.number().int().optional().describe('Deterministic RNG seed.'),
23
+ save: z
24
+ .boolean()
25
+ .default(false)
26
+ .describe('If true, also save each variant via SceneStore and return ids.'),
27
+ };
28
+ export const generateVariantsOutput = {
29
+ variants: z.array(z.object({
30
+ index: z.number(),
31
+ description: z.string(),
32
+ nodeCount: z.number(),
33
+ sceneId: z.string().optional(),
34
+ url: z.string().optional(),
35
+ graph: z.any().optional(),
36
+ })),
37
+ };
38
+ /**
39
+ * `forkSceneGraph` normalises `SiteNode.children` to string IDs, but the
40
+ * `SiteNode` schema declares that field as an array of full `BuildingNode` /
41
+ * `ItemNode` objects (see CROSS_CUTTING §2). To keep variants validating
42
+ * against `AnyNode`, re-embed the site children from the flat dict.
43
+ *
44
+ * Pure: returns a new graph without mutating the input.
45
+ */
46
+ function rehydrateSiteChildren(graph) {
47
+ const out = {
48
+ nodes: { ...graph.nodes },
49
+ rootNodeIds: [...graph.rootNodeIds],
50
+ ...(graph.collections ? { collections: graph.collections } : {}),
51
+ };
52
+ for (const [id, node] of Object.entries(out.nodes)) {
53
+ if (node.type !== 'site')
54
+ continue;
55
+ const childrenField = node.children;
56
+ if (!Array.isArray(childrenField))
57
+ continue;
58
+ const rehydrated = [];
59
+ for (const child of childrenField) {
60
+ if (typeof child === 'string') {
61
+ const target = out.nodes[child];
62
+ if (target && (target.type === 'building' || target.type === 'item')) {
63
+ rehydrated.push(target);
64
+ }
65
+ }
66
+ else if (child && typeof child === 'object' && 'id' in child) {
67
+ rehydrated.push(child);
68
+ }
69
+ }
70
+ out.nodes[id] = {
71
+ ...node,
72
+ children: rehydrated,
73
+ };
74
+ }
75
+ return out;
76
+ }
77
+ /**
78
+ * Count how many nodes in a graph fail `AnyNode` validation. Used to keep the
79
+ * tool from returning silently corrupt variants.
80
+ */
81
+ function countInvalidNodes(graph) {
82
+ let invalid = 0;
83
+ for (const node of Object.values(graph.nodes)) {
84
+ if (!AnyNodeSchema.safeParse(node).success)
85
+ invalid++;
86
+ }
87
+ return invalid;
88
+ }
89
+ export function registerGenerateVariants(server, bridge) {
90
+ server.registerTool('generate_variants', {
91
+ title: 'Generate variants',
92
+ description: 'Generate N variations of a base scene by forking and applying seeded mutations. Example: "give me 5 variations of this kitchen". If `save=true`, each variant is persisted via scene operations and returned with an id + URL; otherwise the graph is returned inline.',
93
+ inputSchema: generateVariantsInput,
94
+ outputSchema: generateVariantsOutput,
95
+ }, async ({ baseSceneId, count, vary, seed, save }) => {
96
+ // 1. Obtain the base SceneGraph.
97
+ let base;
98
+ let baseName = 'scene';
99
+ if (baseSceneId) {
100
+ const loaded = await bridge.loadStoredScene(baseSceneId);
101
+ if (!loaded) {
102
+ throwMcpError(ErrorCode.InvalidParams, 'scene_not_found', { id: baseSceneId });
103
+ }
104
+ base = loaded.graph;
105
+ baseName = loaded.name;
106
+ }
107
+ else {
108
+ base = bridge.exportSceneGraph();
109
+ }
110
+ // 2. Seed the RNG. Default seed is a time-ish number so runs vary, but
111
+ // tests always pass a fixed seed for determinism.
112
+ const initialSeed = seed ?? Math.floor(Math.random() * 0xff_ff_ff_ff);
113
+ const mutations = vary;
114
+ const variants = [];
115
+ for (let i = 0; i < count; i++) {
116
+ // Each variant gets its own RNG stream derived from (seed + i) so
117
+ // results are deterministic per-index.
118
+ const rng = mulberry32(initialSeed + i);
119
+ let forked = forkSceneGraph(base);
120
+ for (const kind of mutations) {
121
+ forked = applyMutation(forked, rng, kind);
122
+ }
123
+ // Re-embed site children so variants match the SiteNode schema.
124
+ forked = rehydrateSiteChildren(forked);
125
+ const invalidCount = countInvalidNodes(forked);
126
+ if (invalidCount > 0) {
127
+ throwMcpError(ErrorCode.InternalError, `variant_invalid: variant ${i} produced ${invalidCount} invalid node(s)`, { index: i });
128
+ }
129
+ const nodeCount = Object.keys(forked.nodes).length;
130
+ const description = describeVariant(forked, mutations);
131
+ if (save) {
132
+ try {
133
+ const meta = await bridge.saveScene({
134
+ name: `${baseName}-variant-${i + 1}`,
135
+ graph: forked,
136
+ });
137
+ variants.push({
138
+ index: i,
139
+ description,
140
+ nodeCount,
141
+ sceneId: meta.id,
142
+ url: `/scene/${meta.id}`,
143
+ });
144
+ }
145
+ catch (err) {
146
+ const msg = err instanceof Error ? err.message : String(err);
147
+ throwMcpError(ErrorCode.InternalError, `save_failed: ${msg}`, { index: i });
148
+ }
149
+ }
150
+ else {
151
+ variants.push({ index: i, description, nodeCount, graph: forked });
152
+ }
153
+ }
154
+ const payload = { variants };
155
+ return {
156
+ content: [{ type: 'text', text: JSON.stringify(payload) }],
157
+ structuredContent: payload,
158
+ };
159
+ });
160
+ }
@@ -0,0 +1,9 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { SceneOperations } from '../../operations';
3
+ /**
4
+ * Register the variant-generation MCP tools against shared scene operations.
5
+ */
6
+ export declare function registerVariantTools(server: McpServer, bridge: SceneOperations): void;
7
+ export { generateVariantsInput, generateVariantsOutput, registerGenerateVariants, } from './generate-variants';
8
+ export { applyMutation, describeVariant, type MutationKind, mulberry32, type Rng, } from './mutations';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/variants/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAGvD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAErF;AAED,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,aAAa,EACb,eAAe,EACf,KAAK,YAAY,EACjB,UAAU,EACV,KAAK,GAAG,GACT,MAAM,aAAa,CAAA"}
@@ -0,0 +1,9 @@
1
+ import { registerGenerateVariants } from './generate-variants';
2
+ /**
3
+ * Register the variant-generation MCP tools against shared scene operations.
4
+ */
5
+ export function registerVariantTools(server, bridge) {
6
+ registerGenerateVariants(server, bridge);
7
+ }
8
+ export { generateVariantsInput, generateVariantsOutput, registerGenerateVariants, } from './generate-variants';
9
+ export { applyMutation, describeVariant, mulberry32, } from './mutations';
@@ -0,0 +1,18 @@
1
+ import type { SceneGraph } from '@pascal-app/core/clone-scene-graph';
2
+ /** Mutation kinds handled by `applyMutation`. */
3
+ export type MutationKind = 'wall-thickness' | 'wall-height' | 'zone-labels' | 'room-proportions' | 'open-plan' | 'door-positions' | 'fence-style';
4
+ /** Deterministic seeded RNG. */
5
+ export type Rng = () => number;
6
+ /**
7
+ * Tiny Park-Miller PRNG. Returns a function that produces uniformly
8
+ * distributed floats in [0, 1) without bitwise operators.
9
+ */
10
+ export declare function mulberry32(seed: number): Rng;
11
+ /** Pure: apply a single mutation and return a fresh graph. */
12
+ export declare function applyMutation(graph: SceneGraph, rng: Rng, kind: MutationKind): SceneGraph;
13
+ /**
14
+ * Human-readable summary of the mutations applied to a variant. Reads the
15
+ * interesting fields from the graph (e.g. first wall's thickness/height).
16
+ */
17
+ export declare function describeVariant(graph: SceneGraph, mutations: readonly MutationKind[]): string;
18
+ //# sourceMappingURL=mutations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../../src/tools/variants/mutations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAGpE,iDAAiD;AACjD,MAAM,MAAM,YAAY,GACpB,gBAAgB,GAChB,aAAa,GACb,aAAa,GACb,kBAAkB,GAClB,WAAW,GACX,gBAAgB,GAChB,aAAa,CAAA;AAEjB,gCAAgC;AAChC,MAAM,MAAM,GAAG,GAAG,MAAM,MAAM,CAAA;AAE9B;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAO5C;AA0OD,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY,GAAG,UAAU,CAiBzF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAyB7F"}
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Tiny Park-Miller PRNG. Returns a function that produces uniformly
3
+ * distributed floats in [0, 1) without bitwise operators.
4
+ */
5
+ export function mulberry32(seed) {
6
+ let state = Math.trunc(Math.abs(seed)) % 2_147_483_647;
7
+ if (state === 0)
8
+ state = 1;
9
+ return () => {
10
+ state = (state * 16_807) % 2_147_483_647;
11
+ return (state - 1) / 2_147_483_646;
12
+ };
13
+ }
14
+ /** Pick a random element from a non-empty array. */
15
+ function pickFrom(rng, values) {
16
+ const idx = Math.floor(rng() * values.length);
17
+ return values[Math.min(idx, values.length - 1)];
18
+ }
19
+ /** Shallow clone a scene graph: nodes are copied one level deep, node dict is fresh. */
20
+ function cloneGraph(graph) {
21
+ const clonedNodes = {};
22
+ for (const [id, node] of Object.entries(graph.nodes)) {
23
+ // structuredClone so sub-objects (arrays, tuples, metadata) are independent.
24
+ clonedNodes[id] = structuredClone(node);
25
+ }
26
+ return {
27
+ nodes: clonedNodes,
28
+ rootNodeIds: [...graph.rootNodeIds],
29
+ ...(graph.collections ? { collections: structuredClone(graph.collections) } : {}),
30
+ };
31
+ }
32
+ const WALL_THICKNESS_OPTIONS = [0.1, 0.15, 0.2, 0.25];
33
+ const WALL_HEIGHT_OPTIONS = [2.4, 2.6, 2.7, 3.0];
34
+ const FENCE_STYLES = ['privacy', 'slat', 'rail'];
35
+ /** Fisher–Yates shuffle in place using the provided RNG. */
36
+ function shuffleInPlace(arr, rng) {
37
+ for (let i = arr.length - 1; i > 0; i--) {
38
+ const j = Math.floor(rng() * (i + 1));
39
+ const tmp = arr[i];
40
+ arr[i] = arr[j];
41
+ arr[j] = tmp;
42
+ }
43
+ }
44
+ /**
45
+ * Compute 2D bounds (min/max x/z) of the first `site` node's polygon points,
46
+ * or `null` if no site is present.
47
+ */
48
+ function siteBounds(graph) {
49
+ for (const node of Object.values(graph.nodes)) {
50
+ if (node.type !== 'site')
51
+ continue;
52
+ const pts = node.polygon?.points;
53
+ if (!pts || pts.length === 0)
54
+ continue;
55
+ let minX = Number.POSITIVE_INFINITY;
56
+ let maxX = Number.NEGATIVE_INFINITY;
57
+ let minZ = Number.POSITIVE_INFINITY;
58
+ let maxZ = Number.NEGATIVE_INFINITY;
59
+ for (const [x, z] of pts) {
60
+ if (x < minX)
61
+ minX = x;
62
+ if (x > maxX)
63
+ maxX = x;
64
+ if (z < minZ)
65
+ minZ = z;
66
+ if (z > maxZ)
67
+ maxZ = z;
68
+ }
69
+ if (!Number.isFinite(minX))
70
+ continue;
71
+ return { minX, maxX, minZ, maxZ };
72
+ }
73
+ return null;
74
+ }
75
+ /**
76
+ * Heuristic: a wall is a perimeter wall if either of its endpoints sits close
77
+ * to the site polygon's bounding rectangle (within `epsilon`). Returns `false`
78
+ * if there is no site polygon (treat everything as interior so the mutations
79
+ * still exercise something on partial scenes).
80
+ */
81
+ function isPerimeterWall(wall, bounds, epsilon = 0.01) {
82
+ if (!(bounds && wall.start && wall.end))
83
+ return false;
84
+ const onBound = (x, z) => Math.abs(x - bounds.minX) <= epsilon ||
85
+ Math.abs(x - bounds.maxX) <= epsilon ||
86
+ Math.abs(z - bounds.minZ) <= epsilon ||
87
+ Math.abs(z - bounds.maxZ) <= epsilon;
88
+ const [sx, sz] = wall.start;
89
+ const [ex, ez] = wall.end;
90
+ return onBound(sx, sz) || onBound(ex, ez);
91
+ }
92
+ function applyWallThickness(graph, rng) {
93
+ const out = cloneGraph(graph);
94
+ for (const node of Object.values(out.nodes)) {
95
+ if (node.type !== 'wall')
96
+ continue;
97
+ node.thickness = pickFrom(rng, WALL_THICKNESS_OPTIONS);
98
+ }
99
+ return out;
100
+ }
101
+ function applyWallHeight(graph, rng) {
102
+ const out = cloneGraph(graph);
103
+ for (const node of Object.values(out.nodes)) {
104
+ if (node.type !== 'wall')
105
+ continue;
106
+ node.height = pickFrom(rng, WALL_HEIGHT_OPTIONS);
107
+ }
108
+ return out;
109
+ }
110
+ function applyZoneLabels(graph, rng) {
111
+ const out = cloneGraph(graph);
112
+ const zoneNodes = [];
113
+ for (const node of Object.values(out.nodes)) {
114
+ if (node.type === 'zone')
115
+ zoneNodes.push(node);
116
+ }
117
+ if (zoneNodes.length < 2)
118
+ return out;
119
+ const labels = zoneNodes.map((z) => z.name ?? '');
120
+ shuffleInPlace(labels, rng);
121
+ for (let i = 0; i < zoneNodes.length; i++) {
122
+ ;
123
+ zoneNodes[i].name = labels[i];
124
+ }
125
+ return out;
126
+ }
127
+ function applyRoomProportions(graph, rng) {
128
+ const out = cloneGraph(graph);
129
+ const bounds = siteBounds(out);
130
+ for (const node of Object.values(out.nodes)) {
131
+ if (node.type !== 'wall')
132
+ continue;
133
+ const wall = node;
134
+ if (!(wall.start && wall.end))
135
+ continue;
136
+ if (isPerimeterWall(wall, bounds))
137
+ continue;
138
+ // Nudge each endpoint by ±10% of its current value.
139
+ const nudge = (v) => v * (1 + (rng() * 2 - 1) * 0.1);
140
+ const clampX = (v) => bounds ? Math.min(bounds.maxX, Math.max(bounds.minX, v)) : v;
141
+ const clampZ = (v) => bounds ? Math.min(bounds.maxZ, Math.max(bounds.minZ, v)) : v;
142
+ const [sx, sz] = wall.start;
143
+ const [ex, ez] = wall.end;
144
+ wall.start = [clampX(nudge(sx)), clampZ(nudge(sz))];
145
+ wall.end = [clampX(nudge(ex)), clampZ(nudge(ez))];
146
+ }
147
+ return out;
148
+ }
149
+ function applyOpenPlan(graph, rng) {
150
+ const out = cloneGraph(graph);
151
+ const bounds = siteBounds(out);
152
+ const interiorWallIds = [];
153
+ for (const [id, node] of Object.entries(out.nodes)) {
154
+ if (node.type !== 'wall')
155
+ continue;
156
+ if (isPerimeterWall(node, bounds))
157
+ continue;
158
+ interiorWallIds.push(id);
159
+ }
160
+ if (interiorWallIds.length === 0)
161
+ return out;
162
+ const targetId = interiorWallIds[Math.floor(rng() * interiorWallIds.length)];
163
+ // Collect any openings attached to this wall so we can drop them too.
164
+ const attached = [];
165
+ for (const [attId, node] of Object.entries(out.nodes)) {
166
+ if (node.wallId === targetId)
167
+ attached.push(attId);
168
+ }
169
+ const removal = new Set([targetId, ...attached]);
170
+ // Drop from nodes.
171
+ for (const id of removal)
172
+ delete out.nodes[id];
173
+ // Drop from rootNodeIds (unlikely for walls, but consistent).
174
+ out.rootNodeIds = out.rootNodeIds.filter((id) => !removal.has(id));
175
+ // Drop references from any parent's `children` array.
176
+ for (const parent of Object.values(out.nodes)) {
177
+ if (!('children' in parent && Array.isArray(parent.children))) {
178
+ continue;
179
+ }
180
+ const children = parent.children;
181
+ parent.children = children.filter((child) => {
182
+ if (typeof child === 'string')
183
+ return !removal.has(child);
184
+ if (child && typeof child === 'object' && 'id' in child) {
185
+ return !removal.has(child.id);
186
+ }
187
+ return true;
188
+ });
189
+ }
190
+ return out;
191
+ }
192
+ function applyDoorPositions(graph, rng) {
193
+ const out = cloneGraph(graph);
194
+ // Group doors by their parent wall so we can space them out and skip collisions.
195
+ const doorsByWall = new Map();
196
+ for (const node of Object.values(out.nodes)) {
197
+ if (node.type !== 'door')
198
+ continue;
199
+ const wallId = node.wallId;
200
+ if (!wallId)
201
+ continue;
202
+ let list = doorsByWall.get(wallId);
203
+ if (!list) {
204
+ list = [];
205
+ doorsByWall.set(wallId, list);
206
+ }
207
+ list.push(node);
208
+ }
209
+ for (const [, doors] of doorsByWall) {
210
+ // Minimum separation along the parametric wall axis — rough keep-away to
211
+ // avoid obvious overlaps.
212
+ const minGap = 0.15;
213
+ const usedTs = [];
214
+ for (const door of doors) {
215
+ let attempts = 0;
216
+ let t = 0.5;
217
+ while (attempts < 8) {
218
+ t = 0.2 + rng() * 0.6; // [0.2, 0.8]
219
+ const collides = usedTs.some((u) => Math.abs(u - t) < minGap);
220
+ if (!collides)
221
+ break;
222
+ attempts++;
223
+ }
224
+ // If we still collide after 8 attempts, skip this door (leave it alone).
225
+ if (usedTs.some((u) => Math.abs(u - t) < minGap))
226
+ continue;
227
+ usedTs.push(t);
228
+ door.wallT = t;
229
+ }
230
+ }
231
+ return out;
232
+ }
233
+ function applyFenceStyle(graph, rng) {
234
+ const out = cloneGraph(graph);
235
+ let i = 0;
236
+ for (const node of Object.values(out.nodes)) {
237
+ if (node.type !== 'fence')
238
+ continue;
239
+ // Use rng to choose a rotation offset so each call can produce a different
240
+ // starting point even when called multiple times with the same base.
241
+ const offset = Math.floor(rng() * FENCE_STYLES.length);
242
+ const style = FENCE_STYLES[(i + offset) % FENCE_STYLES.length];
243
+ node.style = style;
244
+ i++;
245
+ }
246
+ return out;
247
+ }
248
+ /** Pure: apply a single mutation and return a fresh graph. */
249
+ export function applyMutation(graph, rng, kind) {
250
+ switch (kind) {
251
+ case 'wall-thickness':
252
+ return applyWallThickness(graph, rng);
253
+ case 'wall-height':
254
+ return applyWallHeight(graph, rng);
255
+ case 'zone-labels':
256
+ return applyZoneLabels(graph, rng);
257
+ case 'room-proportions':
258
+ return applyRoomProportions(graph, rng);
259
+ case 'open-plan':
260
+ return applyOpenPlan(graph, rng);
261
+ case 'door-positions':
262
+ return applyDoorPositions(graph, rng);
263
+ case 'fence-style':
264
+ return applyFenceStyle(graph, rng);
265
+ }
266
+ }
267
+ /**
268
+ * Human-readable summary of the mutations applied to a variant. Reads the
269
+ * interesting fields from the graph (e.g. first wall's thickness/height).
270
+ */
271
+ export function describeVariant(graph, mutations) {
272
+ const parts = [];
273
+ if (mutations.includes('wall-thickness')) {
274
+ const t = firstWallField(graph, 'thickness');
275
+ if (t !== null)
276
+ parts.push(`wall thickness ${t}m`);
277
+ }
278
+ if (mutations.includes('wall-height')) {
279
+ const h = firstWallField(graph, 'height');
280
+ if (h !== null)
281
+ parts.push(`wall height ${h}m`);
282
+ }
283
+ if (mutations.includes('zone-labels')) {
284
+ const names = [];
285
+ for (const node of Object.values(graph.nodes)) {
286
+ if (node.type === 'zone')
287
+ names.push(node.name ?? '');
288
+ }
289
+ if (names.length > 0)
290
+ parts.push(`zones [${names.join(', ')}]`);
291
+ }
292
+ if (mutations.includes('room-proportions'))
293
+ parts.push('room proportions nudged');
294
+ if (mutations.includes('open-plan'))
295
+ parts.push('open-plan');
296
+ if (mutations.includes('door-positions'))
297
+ parts.push('doors repositioned');
298
+ if (mutations.includes('fence-style')) {
299
+ const s = firstFenceField(graph, 'style');
300
+ if (s !== null)
301
+ parts.push(`fence style ${s}`);
302
+ }
303
+ return parts.length > 0 ? parts.join(', ') : 'no-op';
304
+ }
305
+ function firstWallField(graph, field) {
306
+ for (const node of Object.values(graph.nodes)) {
307
+ if (node.type !== 'wall')
308
+ continue;
309
+ const v = node[field];
310
+ if (typeof v === 'number')
311
+ return v;
312
+ }
313
+ return null;
314
+ }
315
+ function firstFenceField(graph, field) {
316
+ for (const node of Object.values(graph.nodes)) {
317
+ if (node.type !== 'fence')
318
+ continue;
319
+ const v = node[field];
320
+ if (typeof v === 'string')
321
+ return v;
322
+ }
323
+ return null;
324
+ }
@@ -0,0 +1,33 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import type { SceneOperations } from '../../operations';
4
+ /**
5
+ * Input shape for `analyze_floorplan_image`.
6
+ *
7
+ * `image` is either a base64-encoded payload (optionally prefixed with a
8
+ * `data:image/<mime>;base64,` URL) or an `http(s)` URL which we fetch and
9
+ * inline as base64 before forwarding to the MCP host via sampling.
10
+ */
11
+ export declare const analyzeFloorplanImageInput: {
12
+ image: z.ZodString;
13
+ scaleHint: z.ZodOptional<z.ZodString>;
14
+ };
15
+ export declare const analyzeFloorplanImageOutput: {
16
+ walls: z.ZodArray<z.ZodObject<{
17
+ start: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
18
+ end: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
19
+ thickness: z.ZodOptional<z.ZodNumber>;
20
+ }, z.core.$strip>>;
21
+ rooms: z.ZodArray<z.ZodObject<{
22
+ name: z.ZodString;
23
+ polygon: z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
24
+ approximateAreaSqM: z.ZodOptional<z.ZodNumber>;
25
+ }, z.core.$strip>>;
26
+ approximateDimensions: z.ZodObject<{
27
+ widthM: z.ZodNumber;
28
+ depthM: z.ZodNumber;
29
+ }, z.core.$strip>;
30
+ confidence: z.ZodNumber;
31
+ };
32
+ export declare function registerAnalyzeFloorplanImage(server: McpServer, _bridge: SceneOperations): void;
33
+ //# sourceMappingURL=analyze-floorplan-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze-floorplan-image.d.ts","sourceRoot":"","sources":["../../../src/tools/vision/analyze-floorplan-image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEvD;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B;;;CAMtC,CAAA;AAED,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;CAoBvC,CAAA;AAgFD,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAiE/F"}