@pascal-app/mcp 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -26
- package/dist/bin/pascal-mcp.js +8 -2
- package/dist/bridge/scene-bridge.d.ts +41 -6
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +230 -45
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/operations/scene-operations.d.ts +23 -3
- package/dist/operations/scene-operations.d.ts.map +1 -1
- package/dist/operations/scene-operations.js +31 -1
- package/dist/prompts/from-brief.js +1 -1
- package/dist/prompts/iterate-on-feedback.js +1 -1
- package/dist/prompts/scene-guidance.js +3 -3
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +22 -1
- package/dist/resources/catalog-items.d.ts +4 -5
- package/dist/resources/catalog-items.d.ts.map +1 -1
- package/dist/resources/catalog-items.js +7 -9
- package/dist/resources/design-schema.d.ts +9 -0
- package/dist/resources/design-schema.d.ts.map +1 -0
- package/dist/resources/design-schema.js +21 -0
- package/dist/resources/index.d.ts +3 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +5 -2
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +14 -6
- package/dist/storage/disk-artifact-store.d.ts +9 -0
- package/dist/storage/disk-artifact-store.d.ts.map +1 -0
- package/dist/storage/disk-artifact-store.js +46 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/sqlite-scene-store.d.ts +2 -2
- package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
- package/dist/storage/sqlite-scene-store.js +4 -3
- package/dist/storage/types.d.ts +32 -3
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -0
- package/dist/tools/add-column.d.ts +5 -0
- package/dist/tools/add-column.d.ts.map +1 -0
- package/dist/tools/add-column.js +58 -0
- package/dist/tools/add-object.d.ts +60 -0
- package/dist/tools/add-object.d.ts.map +1 -0
- package/dist/tools/add-object.js +156 -0
- package/dist/tools/apply-patch.d.ts +3 -0
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +28 -2
- package/dist/tools/asset-catalog.d.ts +9 -5
- package/dist/tools/asset-catalog.d.ts.map +1 -1
- package/dist/tools/asset-catalog.js +10 -12
- package/dist/tools/check-collisions.d.ts +1 -1
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +1 -2
- package/dist/tools/clear-scene.d.ts +9 -0
- package/dist/tools/clear-scene.d.ts.map +1 -0
- package/dist/tools/clear-scene.js +59 -0
- package/dist/tools/collections.d.ts +4 -0
- package/dist/tools/collections.d.ts.map +1 -0
- package/dist/tools/collections.js +52 -0
- package/dist/tools/construction-tools.d.ts +8 -41
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +120 -230
- package/dist/tools/create-unit.d.ts +2 -0
- package/dist/tools/create-unit.d.ts.map +1 -1
- package/dist/tools/describe-node.d.ts.map +1 -1
- package/dist/tools/describe-node.js +8 -6
- package/dist/tools/errors.d.ts +11 -0
- package/dist/tools/errors.d.ts.map +1 -1
- package/dist/tools/errors.js +10 -0
- package/dist/tools/find-nodes.d.ts +3 -19
- package/dist/tools/find-nodes.d.ts.map +1 -1
- package/dist/tools/find-nodes.js +150 -53
- package/dist/tools/geometry-script-worker.d.ts +2 -0
- package/dist/tools/geometry-script-worker.d.ts.map +1 -0
- package/dist/tools/geometry-script-worker.js +21 -0
- package/dist/tools/get-scene.d.ts +1 -0
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +3 -1
- package/dist/tools/honest-patch-guard.d.ts +2 -0
- package/dist/tools/honest-patch-guard.d.ts.map +1 -0
- package/dist/tools/honest-patch-guard.js +32 -0
- package/dist/tools/hosted-services.d.ts +4 -0
- package/dist/tools/hosted-services.d.ts.map +1 -0
- package/dist/tools/hosted-services.js +30 -0
- package/dist/tools/index.d.ts +10 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -26
- package/dist/tools/live-sync.d.ts +24 -5
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +47 -26
- package/dist/tools/local-geometry-scripts.d.ts +8 -0
- package/dist/tools/local-geometry-scripts.d.ts.map +1 -0
- package/dist/tools/local-geometry-scripts.js +67 -0
- package/dist/tools/measure.d.ts +6 -0
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +33 -65
- package/dist/tools/measurement.d.ts +1 -36
- package/dist/tools/measurement.d.ts.map +1 -1
- package/dist/tools/measurement.js +2 -94
- package/dist/tools/node-world-point.d.ts +37 -0
- package/dist/tools/node-world-point.d.ts.map +1 -0
- package/dist/tools/node-world-point.js +457 -0
- package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
- package/dist/tools/normalize-schema-dialect.js +20 -0
- package/dist/tools/patch-guards.d.ts +58 -0
- package/dist/tools/patch-guards.d.ts.map +1 -0
- package/dist/tools/patch-guards.js +253 -0
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +3 -2
- package/dist/tools/place-design.d.ts +34 -0
- package/dist/tools/place-design.d.ts.map +1 -0
- package/dist/tools/place-design.js +91 -0
- package/dist/tools/redo.d.ts +2 -0
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +2 -2
- package/dist/tools/room-tools.d.ts +50 -113
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +129 -565
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +26 -1
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +2 -0
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +4 -3
- package/dist/tools/scene-lifecycle/save-scene.d.ts +1 -0
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +46 -6
- package/dist/tools/scene-lifecycle/test-utils.d.ts +2 -2
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/test-utils.js +2 -1
- package/dist/tools/set-unit-members.d.ts +2 -0
- package/dist/tools/set-unit-members.d.ts.map +1 -1
- package/dist/tools/set-zone.d.ts +2 -0
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +2 -2
- package/dist/tools/shared-tools.d.ts +30 -0
- package/dist/tools/shared-tools.d.ts.map +1 -0
- package/dist/tools/shared-tools.js +361 -0
- package/dist/tools/structure-tools.d.ts +57 -0
- package/dist/tools/structure-tools.d.ts.map +1 -0
- package/dist/tools/structure-tools.js +57 -0
- package/dist/tools/undo.d.ts +2 -0
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +2 -2
- package/dist/tools/validate-design.d.ts +136 -0
- package/dist/tools/validate-design.d.ts.map +1 -0
- package/dist/tools/validate-design.js +28 -0
- package/dist/tools/validate-scene.d.ts +5 -0
- package/dist/tools/validate-scene.d.ts.map +1 -1
- package/dist/tools/validate-scene.js +8 -6
- package/dist/tools/variants/mutations.js +1 -1
- package/dist/tools/view-scene.d.ts +38 -0
- package/dist/tools/view-scene.d.ts.map +1 -0
- package/dist/tools/view-scene.js +63 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +17 -3
- package/examples/embed-in-agent.ts +1 -1
- package/examples/generate-apartment.md +9 -11
- package/examples/photo-to-scene.md +1 -1
- package/examples/renovate-from-photos.md +3 -4
- package/package.json +12 -6
- package/dist/tools/create-level.d.ts +0 -21
- package/dist/tools/create-level.d.ts.map +0 -1
- package/dist/tools/create-level.js +0 -63
- package/dist/tools/create-wall.d.ts +0 -22
- package/dist/tools/create-wall.d.ts.map +0 -1
- package/dist/tools/create-wall.js +0 -57
- package/dist/tools/cut-opening.d.ts +0 -25
- package/dist/tools/cut-opening.d.ts.map +0 -1
- package/dist/tools/cut-opening.js +0 -61
- package/dist/tools/delete-node.d.ts +0 -19
- package/dist/tools/delete-node.d.ts.map +0 -1
- package/dist/tools/delete-node.js +0 -40
- package/dist/tools/door-clearance.d.ts +0 -135
- package/dist/tools/door-clearance.d.ts.map +0 -1
- package/dist/tools/door-clearance.js +0 -334
- package/dist/tools/duplicate-level.d.ts +0 -19
- package/dist/tools/duplicate-level.d.ts.map +0 -1
- package/dist/tools/duplicate-level.js +0 -60
- package/dist/tools/geometry.d.ts +0 -24
- package/dist/tools/geometry.d.ts.map +0 -1
- package/dist/tools/geometry.js +0 -94
- package/dist/tools/get-node.d.ts +0 -11
- package/dist/tools/get-node.d.ts.map +0 -1
- package/dist/tools/get-node.js +0 -29
- package/dist/tools/layout-clearance.d.ts +0 -105
- package/dist/tools/layout-clearance.d.ts.map +0 -1
- package/dist/tools/layout-clearance.js +0 -190
- package/dist/tools/place-item.d.ts +0 -22
- package/dist/tools/place-item.d.ts.map +0 -1
- package/dist/tools/place-item.js +0 -89
- package/dist/tools/scene-query.d.ts +0 -62
- package/dist/tools/scene-query.d.ts.map +0 -1
- package/dist/tools/scene-query.js +0 -670
package/dist/tools/geometry.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
export function distance2D(a, b) {
|
|
2
|
-
const dx = b[0] - a[0];
|
|
3
|
-
const dz = b[1] - a[1];
|
|
4
|
-
return Math.sqrt(dx * dx + dz * dz);
|
|
5
|
-
}
|
|
6
|
-
export function wallLength(wall) {
|
|
7
|
-
return distance2D(wall.start, wall.end);
|
|
8
|
-
}
|
|
9
|
-
export function clamp(value, min, max) {
|
|
10
|
-
if (max < min)
|
|
11
|
-
return (min + max) / 2;
|
|
12
|
-
return Math.max(min, Math.min(max, value));
|
|
13
|
-
}
|
|
14
|
-
export function wallLocalXFromT(wall, t, width) {
|
|
15
|
-
const length = wallLength(wall);
|
|
16
|
-
return clamp(t * length, width / 2, length - width / 2);
|
|
17
|
-
}
|
|
18
|
-
export function projectWorldPointToWallLocalX(wall, position) {
|
|
19
|
-
const [sx, sz] = wall.start;
|
|
20
|
-
const [ex, ez] = wall.end;
|
|
21
|
-
const dx = ex - sx;
|
|
22
|
-
const dz = ez - sz;
|
|
23
|
-
const len = Math.sqrt(dx * dx + dz * dz);
|
|
24
|
-
if (len === 0)
|
|
25
|
-
return 0;
|
|
26
|
-
const px = position[0] - sx;
|
|
27
|
-
const pz = position[2] - sz;
|
|
28
|
-
const distance = px * (dx / len) + pz * (dz / len);
|
|
29
|
-
return clamp(distance, 0, len);
|
|
30
|
-
}
|
|
31
|
-
export function polygonArea(points) {
|
|
32
|
-
if (points.length < 3)
|
|
33
|
-
return 0;
|
|
34
|
-
let area = 0;
|
|
35
|
-
for (let i = 0; i < points.length; i++) {
|
|
36
|
-
const current = points[i];
|
|
37
|
-
const next = points[(i + 1) % points.length];
|
|
38
|
-
area += current[0] * next[1] - next[0] * current[1];
|
|
39
|
-
}
|
|
40
|
-
return Math.abs(area) / 2;
|
|
41
|
-
}
|
|
42
|
-
export function polygonBounds(points) {
|
|
43
|
-
const xs = points.map((p) => p[0]);
|
|
44
|
-
const zs = points.map((p) => p[1]);
|
|
45
|
-
const minX = Math.min(...xs);
|
|
46
|
-
const maxX = Math.max(...xs);
|
|
47
|
-
const minZ = Math.min(...zs);
|
|
48
|
-
const maxZ = Math.max(...zs);
|
|
49
|
-
return {
|
|
50
|
-
minX,
|
|
51
|
-
maxX,
|
|
52
|
-
minZ,
|
|
53
|
-
maxZ,
|
|
54
|
-
width: maxX - minX,
|
|
55
|
-
depth: maxZ - minZ,
|
|
56
|
-
centerX: (minX + maxX) / 2,
|
|
57
|
-
centerZ: (minZ + maxZ) / 2,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
export function pointInBoundsWithPadding(x, z, bounds, padding) {
|
|
61
|
-
return (x >= bounds.minX + padding &&
|
|
62
|
-
x <= bounds.maxX - padding &&
|
|
63
|
-
z >= bounds.minZ + padding &&
|
|
64
|
-
z <= bounds.maxZ - padding);
|
|
65
|
-
}
|
|
66
|
-
export function pointOnSegment(point, a, b, tolerance = 1e-6) {
|
|
67
|
-
const cross = (point[1] - a[1]) * (b[0] - a[0]) - (point[0] - a[0]) * (b[1] - a[1]);
|
|
68
|
-
if (Math.abs(cross) > tolerance)
|
|
69
|
-
return false;
|
|
70
|
-
const dot = (point[0] - a[0]) * (b[0] - a[0]) + (point[1] - a[1]) * (b[1] - a[1]);
|
|
71
|
-
if (dot < -tolerance)
|
|
72
|
-
return false;
|
|
73
|
-
const lenSq = (b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2;
|
|
74
|
-
return dot <= lenSq + tolerance;
|
|
75
|
-
}
|
|
76
|
-
export function pointInPolygon(point, polygon, includeBoundary = true) {
|
|
77
|
-
if (polygon.length < 3)
|
|
78
|
-
return false;
|
|
79
|
-
let inside = false;
|
|
80
|
-
const [x, z] = point;
|
|
81
|
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
82
|
-
const a = polygon[i];
|
|
83
|
-
const b = polygon[j];
|
|
84
|
-
if (pointOnSegment(point, a, b))
|
|
85
|
-
return includeBoundary;
|
|
86
|
-
const intersects = a[1] > z !== b[1] > z && x < ((b[0] - a[0]) * (z - a[1])) / (b[1] - a[1]) + a[0];
|
|
87
|
-
if (intersects)
|
|
88
|
-
inside = !inside;
|
|
89
|
-
}
|
|
90
|
-
return inside;
|
|
91
|
-
}
|
|
92
|
-
export function polygonContainsPolygon(outer, inner) {
|
|
93
|
-
return inner.every((point) => pointInPolygon(point, outer, true));
|
|
94
|
-
}
|
package/dist/tools/get-node.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import type { SceneOperations } from '../operations/index.js';
|
|
4
|
-
export declare const getNodeInput: {
|
|
5
|
-
id: z.ZodString;
|
|
6
|
-
};
|
|
7
|
-
export declare const getNodeOutput: {
|
|
8
|
-
node: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
9
|
-
};
|
|
10
|
-
export declare function registerGetNode(server: McpServer, bridge: SceneOperations): void;
|
|
11
|
-
//# sourceMappingURL=get-node.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-node.d.ts","sourceRoot":"","sources":["../../src/tools/get-node.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,eAAe,CAAA;AAKpD,eAAO,MAAM,YAAY;;CAExB,CAAA;AAED,eAAO,MAAM,aAAa;;CAEzB,CAAA;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsBhF"}
|
package/dist/tools/get-node.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
|
|
3
|
-
import { ErrorCode, throwMcpError } from './errors.js';
|
|
4
|
-
import { NodeIdSchema } from './schemas.js';
|
|
5
|
-
export const getNodeInput = {
|
|
6
|
-
id: NodeIdSchema,
|
|
7
|
-
};
|
|
8
|
-
export const getNodeOutput = {
|
|
9
|
-
node: z.record(z.string(), z.unknown()),
|
|
10
|
-
};
|
|
11
|
-
export function registerGetNode(server, bridge) {
|
|
12
|
-
server.registerTool('get_node', {
|
|
13
|
-
title: 'Get node',
|
|
14
|
-
description: 'Return the full node payload for the given ID.',
|
|
15
|
-
inputSchema: getNodeInput,
|
|
16
|
-
outputSchema: getNodeOutput,
|
|
17
|
-
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
18
|
-
}, async ({ id }) => {
|
|
19
|
-
const node = bridge.getNode(id);
|
|
20
|
-
if (!node) {
|
|
21
|
-
throwMcpError(ErrorCode.InvalidParams, `Node not found: ${id}`);
|
|
22
|
-
}
|
|
23
|
-
const payload = { node: node };
|
|
24
|
-
return {
|
|
25
|
-
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
26
|
-
structuredContent: payload,
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared plan-layout clearance for MCP tools.
|
|
3
|
-
*
|
|
4
|
-
* - Door keep-outs (level-scoped)
|
|
5
|
-
* - Item–item AABB overlap (rotation + scale aware)
|
|
6
|
-
* - Placement candidate search when primary pose is blocked
|
|
7
|
-
*
|
|
8
|
-
* See docs/layout-clearance-error-log.md for regression checklist.
|
|
9
|
-
*/
|
|
10
|
-
import type { AnyNode } from '@pascal-app/core/schema';
|
|
11
|
-
import { type PlanAabb } from './door-clearance.js';
|
|
12
|
-
export { aabbsOverlap, collectDoorKeepouts, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, type PlanAabb, resolveNodeLevelId, } from './door-clearance.js';
|
|
13
|
-
/** Minimum free space (m) required between item footprints. */
|
|
14
|
-
export declare const DEFAULT_ITEM_GAP = 0.08;
|
|
15
|
-
export type OccupiedFootprint = {
|
|
16
|
-
id: string;
|
|
17
|
-
name?: string;
|
|
18
|
-
levelId?: string | null;
|
|
19
|
-
aabb: PlanAabb;
|
|
20
|
-
};
|
|
21
|
-
export type ItemCollision = {
|
|
22
|
-
aId: string;
|
|
23
|
-
bId: string;
|
|
24
|
-
aName?: string;
|
|
25
|
-
bName?: string;
|
|
26
|
-
levelId?: string | null;
|
|
27
|
-
kind: 'item-aabb';
|
|
28
|
-
violation: 'overlap' | 'clearance';
|
|
29
|
-
minimumClearanceMeters: number;
|
|
30
|
-
message: string;
|
|
31
|
-
};
|
|
32
|
-
export declare function nodeItemAabb(node: AnyNode): PlanAabb | null;
|
|
33
|
-
export declare function collectOccupiedFootprints(nodes: Iterable<AnyNode>, options?: {
|
|
34
|
-
levelId?: string;
|
|
35
|
-
excludeIds?: Set<string>;
|
|
36
|
-
floorOnly?: boolean;
|
|
37
|
-
}): OccupiedFootprint[];
|
|
38
|
-
export declare function findItemItemCollisions(args: {
|
|
39
|
-
nodes: Iterable<AnyNode>;
|
|
40
|
-
levelId?: string;
|
|
41
|
-
gap?: number;
|
|
42
|
-
}): ItemCollision[];
|
|
43
|
-
export type PlacementCandidate = {
|
|
44
|
-
x: number;
|
|
45
|
-
z: number;
|
|
46
|
-
rotationDeg: number;
|
|
47
|
-
};
|
|
48
|
-
export type PlacementRejectReason = 'outside_bounds' | 'blocks_door_clearance' | 'overlaps_item' | 'ok';
|
|
49
|
-
export declare function classifyPlacement(args: {
|
|
50
|
-
aabb: PlanAabb;
|
|
51
|
-
doorKeepouts: PlanAabb[];
|
|
52
|
-
occupied: PlanAabb[];
|
|
53
|
-
roomBounds?: {
|
|
54
|
-
minX: number;
|
|
55
|
-
maxX: number;
|
|
56
|
-
minZ: number;
|
|
57
|
-
maxZ: number;
|
|
58
|
-
};
|
|
59
|
-
padding?: number;
|
|
60
|
-
itemGap?: number;
|
|
61
|
-
doorGap?: number;
|
|
62
|
-
}): PlacementRejectReason;
|
|
63
|
-
export declare function generatePlacementCandidates(primary: PlacementCandidate, options?: {
|
|
64
|
-
lateralsM?: number[];
|
|
65
|
-
insetsM?: number[];
|
|
66
|
-
inward?: {
|
|
67
|
-
x: number;
|
|
68
|
-
z: number;
|
|
69
|
-
};
|
|
70
|
-
along?: {
|
|
71
|
-
x: number;
|
|
72
|
-
z: number;
|
|
73
|
-
};
|
|
74
|
-
}): PlacementCandidate[];
|
|
75
|
-
export declare function findValidPlacement(args: {
|
|
76
|
-
primary: PlacementCandidate;
|
|
77
|
-
dimensions: [number, number, number] | number[] | undefined;
|
|
78
|
-
doorKeepouts: PlanAabb[];
|
|
79
|
-
occupied: PlanAabb[];
|
|
80
|
-
roomBounds?: {
|
|
81
|
-
minX: number;
|
|
82
|
-
maxX: number;
|
|
83
|
-
minZ: number;
|
|
84
|
-
maxZ: number;
|
|
85
|
-
};
|
|
86
|
-
along?: {
|
|
87
|
-
x: number;
|
|
88
|
-
z: number;
|
|
89
|
-
};
|
|
90
|
-
inward?: {
|
|
91
|
-
x: number;
|
|
92
|
-
z: number;
|
|
93
|
-
};
|
|
94
|
-
}): {
|
|
95
|
-
candidate: PlacementCandidate;
|
|
96
|
-
reason: PlacementRejectReason;
|
|
97
|
-
} | {
|
|
98
|
-
candidate: null;
|
|
99
|
-
reason: PlacementRejectReason;
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Collect layout issues, scoped per level so stacked floors do not false-positive.
|
|
103
|
-
*/
|
|
104
|
-
export declare function layoutIssuesFromScene(nodes: Iterable<AnyNode>): string[];
|
|
105
|
-
//# sourceMappingURL=layout-clearance.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"layout-clearance.d.ts","sourceRoot":"","sources":["../../src/tools/layout-clearance.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared plan-layout clearance for MCP tools.
|
|
3
|
-
*
|
|
4
|
-
* - Door keep-outs (level-scoped)
|
|
5
|
-
* - Item–item AABB overlap (rotation + scale aware)
|
|
6
|
-
* - Placement candidate search when primary pose is blocked
|
|
7
|
-
*
|
|
8
|
-
* See docs/layout-clearance-error-log.md for regression checklist.
|
|
9
|
-
*/
|
|
10
|
-
import { aabbsOverlap, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, resolveNodeLevelId, } from './door-clearance.js';
|
|
11
|
-
export { aabbsOverlap, collectDoorKeepouts, findBlockedDoors, itemNodePlanAabb, itemPlanAabb, resolveNodeLevelId, } from './door-clearance.js';
|
|
12
|
-
/** Minimum free space (m) required between item footprints. */
|
|
13
|
-
export const DEFAULT_ITEM_GAP = 0.08;
|
|
14
|
-
export function nodeItemAabb(node) {
|
|
15
|
-
return itemNodePlanAabb(node);
|
|
16
|
-
}
|
|
17
|
-
export function collectOccupiedFootprints(nodes, options) {
|
|
18
|
-
const list = [...nodes];
|
|
19
|
-
const byId = new Map(list.map((n) => [n.id, n]));
|
|
20
|
-
const out = [];
|
|
21
|
-
for (const node of list) {
|
|
22
|
-
if (node.type !== 'item')
|
|
23
|
-
continue;
|
|
24
|
-
if (options?.excludeIds?.has(node.id))
|
|
25
|
-
continue;
|
|
26
|
-
const attach = node.asset?.attachTo;
|
|
27
|
-
if (options?.floorOnly &&
|
|
28
|
-
(attach === 'wall' || attach === 'wall-side' || attach === 'ceiling')) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const levelId = resolveNodeLevelId(node.id, byId);
|
|
32
|
-
if (options?.levelId && levelId !== options.levelId)
|
|
33
|
-
continue;
|
|
34
|
-
// Floor packing: prefer level-parented items (not wall-hosted children).
|
|
35
|
-
if (options?.floorOnly && node.parentId && levelId && node.parentId !== levelId) {
|
|
36
|
-
if (attach === 'wall' || attach === 'wall-side' || attach === 'ceiling')
|
|
37
|
-
continue;
|
|
38
|
-
// Wall-parented items without attachTo still skipped for floor packing.
|
|
39
|
-
if (byId.get(node.parentId)?.type === 'wall')
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const aabb = nodeItemAabb(node);
|
|
43
|
-
if (!aabb)
|
|
44
|
-
continue;
|
|
45
|
-
const name = node.name ?? node.asset?.name;
|
|
46
|
-
out.push({
|
|
47
|
-
id: node.id,
|
|
48
|
-
name: typeof name === 'string' ? name : undefined,
|
|
49
|
-
levelId,
|
|
50
|
-
aabb,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return out;
|
|
54
|
-
}
|
|
55
|
-
export function findItemItemCollisions(args) {
|
|
56
|
-
const gap = args.gap ?? DEFAULT_ITEM_GAP;
|
|
57
|
-
const footprints = collectOccupiedFootprints(args.nodes, { levelId: args.levelId });
|
|
58
|
-
const collisions = [];
|
|
59
|
-
for (let i = 0; i < footprints.length; i++) {
|
|
60
|
-
for (let j = i + 1; j < footprints.length; j++) {
|
|
61
|
-
const a = footprints[i];
|
|
62
|
-
const b = footprints[j];
|
|
63
|
-
if (a.levelId && b.levelId && a.levelId !== b.levelId)
|
|
64
|
-
continue;
|
|
65
|
-
if (!aabbsOverlap(a.aabb, b.aabb, gap))
|
|
66
|
-
continue;
|
|
67
|
-
collisions.push({
|
|
68
|
-
aId: a.id,
|
|
69
|
-
bId: b.id,
|
|
70
|
-
aName: a.name,
|
|
71
|
-
bName: b.name,
|
|
72
|
-
levelId: a.levelId ?? b.levelId,
|
|
73
|
-
kind: 'item-aabb',
|
|
74
|
-
violation: aabbsOverlap(a.aabb, b.aabb, 0) ? 'overlap' : 'clearance',
|
|
75
|
-
minimumClearanceMeters: gap,
|
|
76
|
-
message: aabbsOverlap(a.aabb, b.aabb, 0)
|
|
77
|
-
? `Items overlap: ${a.name ?? a.id} (${a.id}) and ${b.name ?? b.id} (${b.id})`
|
|
78
|
-
: `Items are closer than ${gap} m: ${a.name ?? a.id} (${a.id}) and ${b.name ?? b.id} (${b.id})`,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return collisions;
|
|
83
|
-
}
|
|
84
|
-
export function classifyPlacement(args) {
|
|
85
|
-
const padding = args.padding ?? 0.05;
|
|
86
|
-
const itemGap = args.itemGap ?? DEFAULT_ITEM_GAP;
|
|
87
|
-
const doorGap = args.doorGap ?? 0.02;
|
|
88
|
-
if (args.roomBounds) {
|
|
89
|
-
const b = args.roomBounds;
|
|
90
|
-
if (args.aabb.minX < b.minX + padding ||
|
|
91
|
-
args.aabb.maxX > b.maxX - padding ||
|
|
92
|
-
args.aabb.minZ < b.minZ + padding ||
|
|
93
|
-
args.aabb.maxZ > b.maxZ - padding) {
|
|
94
|
-
return 'outside_bounds';
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (args.doorKeepouts.some((k) => aabbsOverlap(args.aabb, k, doorGap))) {
|
|
98
|
-
return 'blocks_door_clearance';
|
|
99
|
-
}
|
|
100
|
-
if (args.occupied.some((o) => aabbsOverlap(args.aabb, o, itemGap))) {
|
|
101
|
-
return 'overlaps_item';
|
|
102
|
-
}
|
|
103
|
-
return 'ok';
|
|
104
|
-
}
|
|
105
|
-
export function generatePlacementCandidates(primary, options) {
|
|
106
|
-
const laterals = options?.lateralsM ?? [0, -0.4, 0.4, -0.8, 0.8, -1.2, 1.2];
|
|
107
|
-
const insets = options?.insetsM ?? [0, 0.25, 0.5, 0.75];
|
|
108
|
-
const along = options?.along ?? { x: 1, z: 0 };
|
|
109
|
-
const inward = options?.inward ?? { x: 0, z: 1 };
|
|
110
|
-
const out = [];
|
|
111
|
-
const seen = new Set();
|
|
112
|
-
for (const lat of laterals) {
|
|
113
|
-
for (const inset of insets) {
|
|
114
|
-
const x = primary.x + along.x * lat + inward.x * inset;
|
|
115
|
-
const z = primary.z + along.z * lat + inward.z * inset;
|
|
116
|
-
const key = `${x.toFixed(3)},${z.toFixed(3)},${primary.rotationDeg}`;
|
|
117
|
-
if (seen.has(key))
|
|
118
|
-
continue;
|
|
119
|
-
seen.add(key);
|
|
120
|
-
out.push({ x, z, rotationDeg: primary.rotationDeg });
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return out;
|
|
124
|
-
}
|
|
125
|
-
export function findValidPlacement(args) {
|
|
126
|
-
const candidates = generatePlacementCandidates(args.primary, {
|
|
127
|
-
along: args.along,
|
|
128
|
-
inward: args.inward,
|
|
129
|
-
});
|
|
130
|
-
// Prefer reporting why the **primary** pose failed (door/overlap), not the
|
|
131
|
-
// last lateral/inset candidate (often outside_bounds after large nudges).
|
|
132
|
-
let primaryReason = null;
|
|
133
|
-
const reasonPriority = [
|
|
134
|
-
'blocks_door_clearance',
|
|
135
|
-
'overlaps_item',
|
|
136
|
-
'outside_bounds',
|
|
137
|
-
];
|
|
138
|
-
let bestFailReason = 'overlaps_item';
|
|
139
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
140
|
-
const c = candidates[i];
|
|
141
|
-
const rot = (c.rotationDeg * Math.PI) / 180;
|
|
142
|
-
const aabb = itemPlanAabb([c.x, 0, c.z], args.dimensions, rot);
|
|
143
|
-
const reason = classifyPlacement({
|
|
144
|
-
aabb,
|
|
145
|
-
doorKeepouts: args.doorKeepouts,
|
|
146
|
-
occupied: args.occupied,
|
|
147
|
-
roomBounds: args.roomBounds,
|
|
148
|
-
});
|
|
149
|
-
if (reason === 'ok')
|
|
150
|
-
return { candidate: c, reason };
|
|
151
|
-
if (i === 0)
|
|
152
|
-
primaryReason = reason;
|
|
153
|
-
const prevRank = reasonPriority.indexOf(bestFailReason);
|
|
154
|
-
const nextRank = reasonPriority.indexOf(reason);
|
|
155
|
-
if (nextRank >= 0 && (prevRank < 0 || nextRank < prevRank)) {
|
|
156
|
-
bestFailReason = reason;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return { candidate: null, reason: primaryReason ?? bestFailReason };
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Collect layout issues, scoped per level so stacked floors do not false-positive.
|
|
163
|
-
*/
|
|
164
|
-
export function layoutIssuesFromScene(nodes) {
|
|
165
|
-
const list = [...nodes];
|
|
166
|
-
const byId = new Map(list.map((n) => [n.id, n]));
|
|
167
|
-
const levelIds = new Set();
|
|
168
|
-
for (const n of list) {
|
|
169
|
-
if (n.type === 'level')
|
|
170
|
-
levelIds.add(n.id);
|
|
171
|
-
}
|
|
172
|
-
// Also collect levels referenced by walls/items (in case filter missed)
|
|
173
|
-
for (const n of list) {
|
|
174
|
-
const lid = resolveNodeLevelId(n.id, byId);
|
|
175
|
-
if (lid)
|
|
176
|
-
levelIds.add(lid);
|
|
177
|
-
}
|
|
178
|
-
const issues = [];
|
|
179
|
-
const levels = levelIds.size > 0 ? [...levelIds] : [undefined];
|
|
180
|
-
for (const levelId of levels) {
|
|
181
|
-
for (const b of findBlockedDoors({ nodes: list, levelId })) {
|
|
182
|
-
issues.push(b.message);
|
|
183
|
-
}
|
|
184
|
-
for (const c of findItemItemCollisions({ nodes: list, levelId })) {
|
|
185
|
-
issues.push(c.message);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// Deduplicate (node may appear under multiple walks)
|
|
189
|
-
return [...new Set(issues)];
|
|
190
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import type { SceneOperations } from '../operations/index.js';
|
|
4
|
-
export declare const placeItemInput: {
|
|
5
|
-
catalogItemId: z.ZodString;
|
|
6
|
-
targetNodeId: z.ZodString;
|
|
7
|
-
position: z.ZodArray<z.ZodNumber>;
|
|
8
|
-
rotation: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
9
|
-
};
|
|
10
|
-
export declare const placeItemOutput: {
|
|
11
|
-
persistence: z.ZodOptional<z.ZodObject<{
|
|
12
|
-
status: z.ZodEnum<{
|
|
13
|
-
unbound: "unbound";
|
|
14
|
-
events_unsupported: "events_unsupported";
|
|
15
|
-
}>;
|
|
16
|
-
warning: z.ZodString;
|
|
17
|
-
}, z.core.$strip>>;
|
|
18
|
-
itemId: z.ZodString;
|
|
19
|
-
status: z.ZodOptional<z.ZodString>;
|
|
20
|
-
};
|
|
21
|
-
export declare function registerPlaceItem(server: McpServer, bridge: SceneOperations): void;
|
|
22
|
-
//# sourceMappingURL=place-item.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"place-item.d.ts","sourceRoot":"","sources":["../../src/tools/place-item.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,eAAe,CAAA;AASpD,eAAO,MAAM,cAAc;;;;;CAK1B,CAAA;AAED,eAAO,MAAM,eAAe;;;;;;;;;;CAI3B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAwFlF"}
|
package/dist/tools/place-item.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { ItemNode } from '@pascal-app/core/schema';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
|
-
import { findCatalogItem } from './asset-catalog.js';
|
|
5
|
-
import { ErrorCode, throwMcpError } from './errors.js';
|
|
6
|
-
import { projectWorldPointToWallLocalX, wallLength } from './geometry.js';
|
|
7
|
-
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
8
|
-
import { measurement } from './measurement.js';
|
|
9
|
-
import { NodeIdSchema, Vec3Schema } from './schemas.js';
|
|
10
|
-
export const placeItemInput = {
|
|
11
|
-
catalogItemId: z.string().min(1),
|
|
12
|
-
targetNodeId: NodeIdSchema,
|
|
13
|
-
position: Vec3Schema,
|
|
14
|
-
rotation: measurement('angle', 'rad', { description: 'Y-axis rotation.' }).optional(),
|
|
15
|
-
};
|
|
16
|
-
export const placeItemOutput = {
|
|
17
|
-
itemId: z.string(),
|
|
18
|
-
status: z.string().optional(),
|
|
19
|
-
...liveSyncOutput,
|
|
20
|
-
};
|
|
21
|
-
export function registerPlaceItem(server, bridge) {
|
|
22
|
-
server.registerTool('place_item', {
|
|
23
|
-
title: 'Place item',
|
|
24
|
-
description: 'Place a catalog item into the scene. Target a level/slab/zone for floor items, a wall for wall-attached items, or a ceiling for ceiling-attached items. Do not target the site node directly.',
|
|
25
|
-
inputSchema: placeItemInput,
|
|
26
|
-
outputSchema: placeItemOutput,
|
|
27
|
-
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
28
|
-
}, async ({ catalogItemId, targetNodeId, position, rotation }) => {
|
|
29
|
-
const target = bridge.getNode(targetNodeId);
|
|
30
|
-
if (!target) {
|
|
31
|
-
throwMcpError(ErrorCode.InvalidParams, `Target node not found: ${targetNodeId}`);
|
|
32
|
-
}
|
|
33
|
-
const targetType = target.type;
|
|
34
|
-
if (targetType !== 'level' &&
|
|
35
|
-
targetType !== 'slab' &&
|
|
36
|
-
targetType !== 'zone' &&
|
|
37
|
-
targetType !== 'wall' &&
|
|
38
|
-
targetType !== 'ceiling') {
|
|
39
|
-
throwMcpError(ErrorCode.InvalidRequest, `Cannot place item on ${targetType}; target must be a level, slab, zone, wall, or ceiling. Site-level placement is not supported yet because site.children is reserved for buildings.`);
|
|
40
|
-
}
|
|
41
|
-
const catalogAsset = findCatalogItem(catalogItemId);
|
|
42
|
-
const baseAsset = catalogAsset ?? {
|
|
43
|
-
id: catalogItemId,
|
|
44
|
-
name: catalogItemId,
|
|
45
|
-
category: 'unknown',
|
|
46
|
-
thumbnail: '',
|
|
47
|
-
src: 'asset://placeholder',
|
|
48
|
-
dimensions: [0.5, 0.5, 0.5],
|
|
49
|
-
offset: [0, 0, 0],
|
|
50
|
-
rotation: [0, 0, 0],
|
|
51
|
-
scale: [1, 1, 1],
|
|
52
|
-
};
|
|
53
|
-
const requestedPosition = position;
|
|
54
|
-
const parentId = targetType === 'slab' || targetType === 'zone'
|
|
55
|
-
? bridge.resolveLevelId(targetNodeId)
|
|
56
|
-
: targetNodeId;
|
|
57
|
-
if (!parentId) {
|
|
58
|
-
throwMcpError(ErrorCode.InvalidParams, `Could not resolve a level parent for target ${targetNodeId}`);
|
|
59
|
-
}
|
|
60
|
-
const wallExtras = {};
|
|
61
|
-
let itemPosition = requestedPosition;
|
|
62
|
-
if (targetType === 'wall') {
|
|
63
|
-
const localX = projectWorldPointToWallLocalX(target, requestedPosition);
|
|
64
|
-
const length = wallLength(target);
|
|
65
|
-
itemPosition = [localX, requestedPosition[1], 0];
|
|
66
|
-
Object.assign(wallExtras, {
|
|
67
|
-
wallId: targetNodeId,
|
|
68
|
-
wallT: length === 0 ? 0 : localX / length,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
const item = ItemNode.parse({
|
|
72
|
-
position: itemPosition,
|
|
73
|
-
rotation: [0, rotation ?? 0, 0],
|
|
74
|
-
asset: baseAsset,
|
|
75
|
-
...wallExtras,
|
|
76
|
-
});
|
|
77
|
-
const id = bridge.createNode(item, parentId);
|
|
78
|
-
const persistence = await publishLiveSceneSnapshot(bridge, 'place_item');
|
|
79
|
-
const payload = {
|
|
80
|
-
itemId: id,
|
|
81
|
-
status: catalogAsset ? 'ok' : 'catalog_unavailable',
|
|
82
|
-
...persistencePayload(persistence),
|
|
83
|
-
};
|
|
84
|
-
return {
|
|
85
|
-
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
86
|
-
structuredContent: payload,
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type { AnyNode } from '@pascal-app/core/schema';
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
import type { SceneOperations } from '../operations/index.js';
|
|
5
|
-
export declare const levelScopedInput: {
|
|
6
|
-
levelId: z.ZodOptional<z.ZodString>;
|
|
7
|
-
};
|
|
8
|
-
export declare const listLevelsOutput: {
|
|
9
|
-
activeSceneId: z.ZodNullable<z.ZodString>;
|
|
10
|
-
levelCount: z.ZodNumber;
|
|
11
|
-
occupiedStoryCount: z.ZodNumber;
|
|
12
|
-
supportLevelCount: z.ZodNumber;
|
|
13
|
-
roofLevelIds: z.ZodArray<z.ZodString>;
|
|
14
|
-
levels: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
15
|
-
};
|
|
16
|
-
export declare const getLevelSummaryOutput: {
|
|
17
|
-
levelId: z.ZodString;
|
|
18
|
-
levelName: z.ZodOptional<z.ZodString>;
|
|
19
|
-
floorIndex: z.ZodNumber;
|
|
20
|
-
role: z.ZodString;
|
|
21
|
-
metadataRole: z.ZodNullable<z.ZodString>;
|
|
22
|
-
isOccupiedStory: z.ZodBoolean;
|
|
23
|
-
isSupportLevel: z.ZodBoolean;
|
|
24
|
-
referenceLevelId: z.ZodNullable<z.ZodString>;
|
|
25
|
-
counts: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
26
|
-
walls: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
27
|
-
zones: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
28
|
-
items: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
29
|
-
slabs: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
30
|
-
ceilings: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
31
|
-
};
|
|
32
|
-
export declare const getWallsOutput: {
|
|
33
|
-
levelId: z.ZodString;
|
|
34
|
-
walls: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
35
|
-
};
|
|
36
|
-
export declare const getZonesOutput: {
|
|
37
|
-
levelId: z.ZodString;
|
|
38
|
-
zones: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
39
|
-
};
|
|
40
|
-
export declare const verifySceneOutput: {
|
|
41
|
-
ok: z.ZodBoolean;
|
|
42
|
-
valid: z.ZodBoolean;
|
|
43
|
-
levelCount: z.ZodNumber;
|
|
44
|
-
occupiedStoryCount: z.ZodNumber;
|
|
45
|
-
supportLevelCount: z.ZodNumber;
|
|
46
|
-
roofLevelIds: z.ZodArray<z.ZodString>;
|
|
47
|
-
activeSceneId: z.ZodNullable<z.ZodString>;
|
|
48
|
-
levels: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
49
|
-
emptyLevelIds: z.ZodArray<z.ZodString>;
|
|
50
|
-
issues: z.ZodArray<z.ZodString>;
|
|
51
|
-
hasIssues: z.ZodBoolean;
|
|
52
|
-
};
|
|
53
|
-
export declare function resolveReportedWallHeight(bridge: SceneOperations, wall: Extract<AnyNode, {
|
|
54
|
-
type: 'wall';
|
|
55
|
-
}>): number;
|
|
56
|
-
export declare function registerListLevels(server: McpServer, bridge: SceneOperations): void;
|
|
57
|
-
export declare function registerGetLevelSummary(server: McpServer, bridge: SceneOperations): void;
|
|
58
|
-
export declare function registerGetWalls(server: McpServer, bridge: SceneOperations): void;
|
|
59
|
-
export declare function registerGetZones(server: McpServer, bridge: SceneOperations): void;
|
|
60
|
-
export declare function registerVerifyScene(server: McpServer, bridge: SceneOperations): void;
|
|
61
|
-
export declare function registerSceneQueryTools(server: McpServer, bridge: SceneOperations): void;
|
|
62
|
-
//# sourceMappingURL=scene-query.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scene-query.d.ts","sourceRoot":"","sources":["../../src/tools/scene-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAQxE,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAapD,eAAO,MAAM,gBAAgB;;CAE5B,CAAA;AAID,eAAO,MAAM,gBAAgB;;;;;;;CAO5B,CAAA;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;CAejC,CAAA;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAA;AA8CD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACvC,MAAM,CAgBR;AAkWD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA0CnF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiBxF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAoBjF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAoBjF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAkQpF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAMxF"}
|