callman-mcp 0.1.0 → 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 (204) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +45 -16
  3. package/dist/client/api.d.ts +64 -3
  4. package/dist/client/api.d.ts.map +1 -1
  5. package/dist/client/api.js +67 -2
  6. package/dist/client/api.js.map +1 -1
  7. package/dist/client/errors.d.ts.map +1 -1
  8. package/dist/client/errors.js +33 -7
  9. package/dist/client/errors.js.map +1 -1
  10. package/dist/client/types.d.ts +91 -2
  11. package/dist/client/types.d.ts.map +1 -1
  12. package/dist/context.d.ts +2 -0
  13. package/dist/context.d.ts.map +1 -1
  14. package/dist/context.js +7 -2
  15. package/dist/context.js.map +1 -1
  16. package/dist/domain/contracts.d.ts +893 -0
  17. package/dist/domain/contracts.d.ts.map +1 -0
  18. package/dist/domain/contracts.js +90 -0
  19. package/dist/domain/contracts.js.map +1 -0
  20. package/dist/domain/diagram/build.d.ts +520 -18
  21. package/dist/domain/diagram/build.d.ts.map +1 -1
  22. package/dist/domain/diagram/build.js +65 -19
  23. package/dist/domain/diagram/build.js.map +1 -1
  24. package/dist/domain/diagram/patch.d.ts +624 -51
  25. package/dist/domain/diagram/patch.d.ts.map +1 -1
  26. package/dist/domain/diagram/patch.js +180 -40
  27. package/dist/domain/diagram/patch.js.map +1 -1
  28. package/dist/domain/diagram/style.d.ts +315 -0
  29. package/dist/domain/diagram/style.d.ts.map +1 -0
  30. package/dist/domain/diagram/style.js +124 -0
  31. package/dist/domain/diagram/style.js.map +1 -0
  32. package/dist/domain/diagram/summarize.d.ts +8 -0
  33. package/dist/domain/diagram/summarize.d.ts.map +1 -1
  34. package/dist/domain/diagram/summarize.js +28 -13
  35. package/dist/domain/diagram/summarize.js.map +1 -1
  36. package/dist/domain/requestIntegrations.d.ts +719 -0
  37. package/dist/domain/requestIntegrations.d.ts.map +1 -0
  38. package/dist/domain/requestIntegrations.js +222 -0
  39. package/dist/domain/requestIntegrations.js.map +1 -0
  40. package/dist/domain/requestShape.d.ts +1350 -152
  41. package/dist/domain/requestShape.d.ts.map +1 -1
  42. package/dist/domain/requestShape.js +336 -121
  43. package/dist/domain/requestShape.js.map +1 -1
  44. package/dist/domain/resolve.d.ts +2 -1
  45. package/dist/domain/resolve.d.ts.map +1 -1
  46. package/dist/domain/resolve.js +5 -1
  47. package/dist/domain/resolve.js.map +1 -1
  48. package/dist/domain/scenario/build.d.ts +82 -26
  49. package/dist/domain/scenario/build.d.ts.map +1 -1
  50. package/dist/domain/scenario/build.js +196 -65
  51. package/dist/domain/scenario/build.js.map +1 -1
  52. package/dist/domain/scenario/nodeConfig.d.ts +11 -0
  53. package/dist/domain/scenario/nodeConfig.d.ts.map +1 -0
  54. package/dist/domain/scenario/nodeConfig.js +251 -0
  55. package/dist/domain/scenario/nodeConfig.js.map +1 -0
  56. package/dist/domain/scenario/summarize.d.ts +145 -25
  57. package/dist/domain/scenario/summarize.d.ts.map +1 -1
  58. package/dist/domain/scenario/summarize.js +84 -29
  59. package/dist/domain/scenario/summarize.js.map +1 -1
  60. package/dist/lib/mask.d.ts +0 -18
  61. package/dist/lib/mask.d.ts.map +1 -1
  62. package/dist/lib/mask.js +2 -44
  63. package/dist/lib/mask.js.map +1 -1
  64. package/dist/lib/pagination.d.ts +3 -2
  65. package/dist/lib/pagination.d.ts.map +1 -1
  66. package/dist/lib/pagination.js +13 -8
  67. package/dist/lib/pagination.js.map +1 -1
  68. package/dist/lib/redact.d.ts +12 -0
  69. package/dist/lib/redact.d.ts.map +1 -0
  70. package/dist/lib/redact.js +50 -0
  71. package/dist/lib/redact.js.map +1 -0
  72. package/dist/lib/strict.d.ts +4 -0
  73. package/dist/lib/strict.d.ts.map +1 -0
  74. package/dist/lib/strict.js +14 -0
  75. package/dist/lib/strict.js.map +1 -0
  76. package/dist/prompts/index.js +7 -7
  77. package/dist/prompts/index.js.map +1 -1
  78. package/dist/resources/docs/contracts.d.ts +2 -0
  79. package/dist/resources/docs/contracts.d.ts.map +1 -0
  80. package/dist/resources/docs/contracts.js +74 -0
  81. package/dist/resources/docs/contracts.js.map +1 -0
  82. package/dist/resources/docs/diagramSchema.d.ts +1 -1
  83. package/dist/resources/docs/diagramSchema.d.ts.map +1 -1
  84. package/dist/resources/docs/diagramSchema.js +95 -28
  85. package/dist/resources/docs/diagramSchema.js.map +1 -1
  86. package/dist/resources/docs/overview.d.ts +1 -1
  87. package/dist/resources/docs/overview.d.ts.map +1 -1
  88. package/dist/resources/docs/overview.js +29 -7
  89. package/dist/resources/docs/overview.js.map +1 -1
  90. package/dist/resources/docs/requestSchema.d.ts +1 -1
  91. package/dist/resources/docs/requestSchema.d.ts.map +1 -1
  92. package/dist/resources/docs/requestSchema.js +85 -16
  93. package/dist/resources/docs/requestSchema.js.map +1 -1
  94. package/dist/resources/generated/aiPacks.d.ts +1 -1
  95. package/dist/resources/generated/aiPacks.d.ts.map +1 -1
  96. package/dist/resources/generated/aiPacks.js +3 -2
  97. package/dist/resources/generated/aiPacks.js.map +1 -1
  98. package/dist/resources/index.d.ts.map +1 -1
  99. package/dist/resources/index.js +19 -19
  100. package/dist/resources/index.js.map +1 -1
  101. package/dist/run/executeCollection.d.ts +3 -1
  102. package/dist/run/executeCollection.d.ts.map +1 -1
  103. package/dist/run/executeCollection.js +16 -4
  104. package/dist/run/executeCollection.js.map +1 -1
  105. package/dist/run/summarize.d.ts +17 -6
  106. package/dist/run/summarize.d.ts.map +1 -1
  107. package/dist/run/summarize.js +48 -41
  108. package/dist/run/summarize.js.map +1 -1
  109. package/dist/run/types.d.ts +1 -0
  110. package/dist/run/types.d.ts.map +1 -1
  111. package/dist/server.d.ts +1 -1
  112. package/dist/server.d.ts.map +1 -1
  113. package/dist/server.js +1 -1
  114. package/dist/server.js.map +1 -1
  115. package/dist/tools/catalog.d.ts +1 -1
  116. package/dist/tools/catalog.d.ts.map +1 -1
  117. package/dist/tools/catalog.js +18 -0
  118. package/dist/tools/catalog.js.map +1 -1
  119. package/dist/tools/collections.d.ts.map +1 -1
  120. package/dist/tools/collections.js +53 -41
  121. package/dist/tools/collections.js.map +1 -1
  122. package/dist/tools/contracts.d.ts +4 -0
  123. package/dist/tools/contracts.d.ts.map +1 -0
  124. package/dist/tools/contracts.js +201 -0
  125. package/dist/tools/contracts.js.map +1 -0
  126. package/dist/tools/diagrams.d.ts.map +1 -1
  127. package/dist/tools/diagrams.js +19 -10
  128. package/dist/tools/diagrams.js.map +1 -1
  129. package/dist/tools/environments.d.ts.map +1 -1
  130. package/dist/tools/environments.js +184 -64
  131. package/dist/tools/environments.js.map +1 -1
  132. package/dist/tools/import.d.ts.map +1 -1
  133. package/dist/tools/import.js +111 -20
  134. package/dist/tools/import.js.map +1 -1
  135. package/dist/tools/index.d.ts +2 -1
  136. package/dist/tools/index.d.ts.map +1 -1
  137. package/dist/tools/index.js +19 -1
  138. package/dist/tools/index.js.map +1 -1
  139. package/dist/tools/lookups.d.ts +38 -7
  140. package/dist/tools/lookups.d.ts.map +1 -1
  141. package/dist/tools/lookups.js +113 -10
  142. package/dist/tools/lookups.js.map +1 -1
  143. package/dist/tools/meta.d.ts.map +1 -1
  144. package/dist/tools/meta.js +9 -4
  145. package/dist/tools/meta.js.map +1 -1
  146. package/dist/tools/requests.d.ts.map +1 -1
  147. package/dist/tools/requests.js +138 -50
  148. package/dist/tools/requests.js.map +1 -1
  149. package/dist/tools/run.d.ts.map +1 -1
  150. package/dist/tools/run.js +84 -28
  151. package/dist/tools/run.js.map +1 -1
  152. package/dist/tools/scenarios.d.ts.map +1 -1
  153. package/dist/tools/scenarios.js +238 -75
  154. package/dist/tools/scenarios.js.map +1 -1
  155. package/dist/tools/shared.d.ts +1 -0
  156. package/dist/tools/shared.d.ts.map +1 -1
  157. package/dist/tools/shared.js +9 -1
  158. package/dist/tools/shared.js.map +1 -1
  159. package/dist/tools/workspaces.d.ts.map +1 -1
  160. package/dist/tools/workspaces.js +44 -7
  161. package/dist/tools/workspaces.js.map +1 -1
  162. package/dist-cjs/client/api.js +67 -2
  163. package/dist-cjs/client/errors.js +33 -7
  164. package/dist-cjs/context.js +7 -2
  165. package/dist-cjs/domain/contracts.js +93 -0
  166. package/dist-cjs/domain/diagram/build.js +71 -20
  167. package/dist-cjs/domain/diagram/patch.js +179 -39
  168. package/dist-cjs/domain/diagram/style.js +130 -0
  169. package/dist-cjs/domain/diagram/summarize.js +28 -13
  170. package/dist-cjs/domain/requestIntegrations.js +228 -0
  171. package/dist-cjs/domain/requestShape.js +342 -121
  172. package/dist-cjs/domain/resolve.js +5 -1
  173. package/dist-cjs/domain/scenario/build.js +197 -65
  174. package/dist-cjs/domain/scenario/nodeConfig.js +260 -0
  175. package/dist-cjs/domain/scenario/summarize.js +86 -29
  176. package/dist-cjs/lib/mask.js +3 -48
  177. package/dist-cjs/lib/pagination.js +13 -8
  178. package/dist-cjs/lib/redact.js +53 -0
  179. package/dist-cjs/lib/strict.js +18 -0
  180. package/dist-cjs/prompts/index.js +7 -7
  181. package/dist-cjs/resources/docs/contracts.js +76 -0
  182. package/dist-cjs/resources/docs/diagramSchema.js +95 -28
  183. package/dist-cjs/resources/docs/overview.js +29 -7
  184. package/dist-cjs/resources/docs/requestSchema.js +85 -16
  185. package/dist-cjs/resources/generated/aiPacks.js +3 -2
  186. package/dist-cjs/resources/index.js +17 -17
  187. package/dist-cjs/run/executeCollection.js +16 -4
  188. package/dist-cjs/run/summarize.js +48 -41
  189. package/dist-cjs/server.js +1 -1
  190. package/dist-cjs/tools/catalog.js +18 -0
  191. package/dist-cjs/tools/collections.js +52 -40
  192. package/dist-cjs/tools/contracts.js +204 -0
  193. package/dist-cjs/tools/diagrams.js +19 -10
  194. package/dist-cjs/tools/environments.js +182 -62
  195. package/dist-cjs/tools/import.js +107 -16
  196. package/dist-cjs/tools/index.js +21 -2
  197. package/dist-cjs/tools/lookups.js +123 -11
  198. package/dist-cjs/tools/meta.js +9 -4
  199. package/dist-cjs/tools/requests.js +134 -46
  200. package/dist-cjs/tools/run.js +82 -26
  201. package/dist-cjs/tools/scenarios.js +235 -72
  202. package/dist-cjs/tools/shared.js +11 -2
  203. package/dist-cjs/tools/workspaces.js +43 -6
  204. package/package.json +2 -2
@@ -5,25 +5,45 @@ const callman_core_1 = require("callman-core");
5
5
  const zod_1 = require("zod");
6
6
  const errors_js_1 = require("../../client/errors.js");
7
7
  const ids_js_1 = require("../../lib/ids.js");
8
+ const strict_js_1 = require("../../lib/strict.js");
8
9
  const build_js_1 = require("./build.js");
10
+ const style_js_1 = require("./style.js");
9
11
  // Incremental edits to a stored diagram — the model sends operations, not a
10
12
  // 200-node document. Removing a node drops the edges attached to it.
13
+ const nodeShape = build_js_1.diagramNodeInputSchema.shape;
14
+ const edgeShape = build_js_1.diagramEdgeInputSchema.shape;
15
+ const nodeChangesSchema = (0, strict_js_1.strictObject)({
16
+ type: nodeShape.type.optional(),
17
+ label: nodeShape.label,
18
+ parent: zod_1.z.string().max(64).nullable().optional().describe('New container id; null or "" moves the node to the top level'),
19
+ position: nodeShape.position,
20
+ size: nodeShape.size,
21
+ style: style_js_1.nodeStylePatchSchema.optional(),
22
+ rotation: nodeShape.rotation.unwrap().nullable().optional().describe("Degrees, clockwise; null resets"),
23
+ locked: nodeShape.locked,
24
+ hidden: nodeShape.hidden,
25
+ data: zod_1.z.record(zod_1.z.unknown()).nullable().optional().describe("MERGED shallowly into the node's data; a key set to null is removed, data: null clears it"),
26
+ }, "field", "updateNode changes");
27
+ const edgeChangesSchema = (0, strict_js_1.strictObject)({
28
+ source: edgeShape.source.optional(),
29
+ target: edgeShape.target.optional(),
30
+ label: edgeShape.label.describe('"" removes the label'),
31
+ semantic: edgeShape.semantic,
32
+ color: style_js_1.colorInputSchema.nullable().optional().describe("Line CSS color; null restores the default"),
33
+ routing: edgeShape.routing,
34
+ markerEnd: edgeShape.markerEnd,
35
+ markerStart: edgeShape.markerStart,
36
+ }, "field", "updateEdge changes");
37
+ // Every op is strict: a field at the wrong level ({ op: "updateNode", id, label })
38
+ // is an error, not a silent no-op that still counts as "applied".
11
39
  exports.diagramPatchOpSchema = zod_1.z.discriminatedUnion("op", [
12
- zod_1.z.object({ op: zod_1.z.literal("addNode"), node: build_js_1.diagramNodeInputSchema }),
13
- zod_1.z.object({
14
- op: zod_1.z.literal("updateNode"),
15
- id: zod_1.z.string().min(1),
16
- changes: build_js_1.diagramNodeInputSchema.partial().omit({ id: true }),
17
- }),
18
- zod_1.z.object({ op: zod_1.z.literal("removeNode"), id: zod_1.z.string().min(1) }),
19
- zod_1.z.object({ op: zod_1.z.literal("addEdge"), edge: build_js_1.diagramEdgeInputSchema }),
20
- zod_1.z.object({
21
- op: zod_1.z.literal("updateEdge"),
22
- id: zod_1.z.string().min(1),
23
- changes: build_js_1.diagramEdgeInputSchema.partial().omit({ id: true }),
24
- }),
25
- zod_1.z.object({ op: zod_1.z.literal("removeEdge"), id: zod_1.z.string().min(1) }),
26
- zod_1.z.object({ op: zod_1.z.literal("setTitle"), title: zod_1.z.string().min(1).max(200) }),
40
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("addNode"), node: build_js_1.diagramNodeInputSchema }, "field", "addNode ops"),
41
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("updateNode"), id: zod_1.z.string().min(1), changes: nodeChangesSchema }, "field", "updateNode ops"),
42
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("removeNode"), id: zod_1.z.string().min(1) }, "field", "removeNode ops"),
43
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("addEdge"), edge: build_js_1.diagramEdgeInputSchema }, "field", "addEdge ops"),
44
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("updateEdge"), id: zod_1.z.string().min(1), changes: edgeChangesSchema }, "field", "updateEdge ops"),
45
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("removeEdge"), id: zod_1.z.string().min(1) }, "field", "removeEdge ops"),
46
+ (0, strict_js_1.strictObject)({ op: zod_1.z.literal("setTitle"), title: zod_1.z.string().min(1).max(200) }, "field", "setTitle ops"),
27
47
  ]);
28
48
  const applyDiagramPatch = (doc, ops, options = {}) => {
29
49
  const strict = options.strictTypes !== false;
@@ -34,11 +54,14 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
34
54
  const removedNodeIds = [];
35
55
  const removedEdgeIds = [];
36
56
  const warnings = [];
57
+ const moved = new Set();
37
58
  const nextNodeId = (0, ids_js_1.sequentialIds)("n", nodes.keys());
38
59
  const nextEdgeId = (0, ids_js_1.sequentialIds)("e", edges.keys());
39
60
  const resolveType = (type, id) => {
40
- if ((0, callman_core_1.getDiagramComponent)(type))
61
+ if ((0, callman_core_1.getDiagramComponent)(type)) {
62
+ (0, build_js_1.assertNotConnector)(type, id);
41
63
  return type;
64
+ }
42
65
  const suggestions = (0, build_js_1.suggestComponentTypes)(type);
43
66
  if (strict) {
44
67
  throw new errors_js_1.ToolInputError("DIAGRAM_UNKNOWN_COMPONENT_TYPE", `Unknown component type "${type}" on ${id}.`, {
@@ -69,9 +92,17 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
69
92
  size: op.node.size ?? def?.defaultSize ?? { width: 140, height: 60 },
70
93
  label: op.node.label ?? (type === "rect" && op.node.type !== "rect" ? op.node.type : def?.defaultLabel),
71
94
  ...(op.node.parent ? { parentId: op.node.parent } : {}),
72
- ...(op.node.style ? { style: op.node.style } : {}),
95
+ ...(op.node.style ? { style: (0, style_js_1.applyStyleInput)(undefined, op.node.style) } : {}),
96
+ ...(op.node.rotation ? { rotation: op.node.rotation } : {}),
97
+ ...(op.node.locked ? { locked: true } : {}),
98
+ ...(op.node.hidden ? { hidden: true } : {}),
73
99
  ...(op.node.data ? { data: op.node.data } : {}),
74
100
  });
101
+ if (op.node.parent) {
102
+ const warning = (0, build_js_1.parentWarning)(id, op.node.parent, nodes.get(op.node.parent)?.type);
103
+ if (warning)
104
+ warnings.push(warning);
105
+ }
75
106
  if (!op.node.position)
76
107
  addedNodeIds.push(id);
77
108
  break;
@@ -81,19 +112,72 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
81
112
  if (!node)
82
113
  throw new errors_js_1.ToolInputError("DIAGRAM_NODE_NOT_FOUND", `Node "${op.id}" does not exist.`);
83
114
  const changes = op.changes;
84
- if (changes.parent !== undefined && changes.parent && !nodes.has(changes.parent)) {
115
+ if (changes.parent && !nodes.has(changes.parent)) {
85
116
  throw new errors_js_1.ToolInputError("DIAGRAM_PARENT_NOT_FOUND", `Parent "${changes.parent}" does not exist.`);
86
117
  }
87
- nodes.set(op.id, {
88
- ...node,
89
- ...(changes.type !== undefined ? { type: resolveType(changes.type, op.id) } : {}),
90
- ...(changes.label !== undefined ? { label: changes.label } : {}),
91
- ...(changes.position !== undefined ? { position: changes.position } : {}),
92
- ...(changes.size !== undefined ? { size: changes.size } : {}),
93
- ...(changes.parent !== undefined ? { parentId: changes.parent || undefined } : {}),
94
- ...(changes.style !== undefined ? { style: changes.style } : {}),
95
- ...(changes.data !== undefined ? { data: changes.data } : {}),
96
- });
118
+ if (changes.parent && changes.parent === op.id)
119
+ throw new errors_js_1.ToolInputError("DIAGRAM_PARENT_CYCLE", `Node "${op.id}" cannot contain itself.`);
120
+ const next = { ...node };
121
+ if (changes.type !== undefined)
122
+ next.type = resolveType(changes.type, op.id);
123
+ if (changes.label !== undefined)
124
+ next.label = changes.label;
125
+ if (changes.position !== undefined)
126
+ next.position = changes.position;
127
+ if (changes.size !== undefined)
128
+ next.size = changes.size;
129
+ if (changes.parent !== undefined) {
130
+ if (changes.parent)
131
+ next.parentId = changes.parent;
132
+ else
133
+ delete next.parentId;
134
+ }
135
+ if (changes.style !== undefined) {
136
+ const style = changes.style === null ? undefined : (0, style_js_1.applyStyleInput)(node.style, changes.style);
137
+ if (style)
138
+ next.style = style;
139
+ else
140
+ delete next.style;
141
+ }
142
+ if (changes.rotation !== undefined) {
143
+ if (changes.rotation)
144
+ next.rotation = changes.rotation;
145
+ else
146
+ delete next.rotation;
147
+ }
148
+ if (changes.locked !== undefined) {
149
+ if (changes.locked)
150
+ next.locked = true;
151
+ else
152
+ delete next.locked;
153
+ }
154
+ if (changes.hidden !== undefined) {
155
+ if (changes.hidden)
156
+ next.hidden = true;
157
+ else
158
+ delete next.hidden;
159
+ }
160
+ if (changes.data !== undefined) {
161
+ const data = changes.data === null ? {} : { ...(node.data ?? {}) };
162
+ for (const [key, value] of Object.entries(changes.data ?? {})) {
163
+ if (value === null)
164
+ delete data[key];
165
+ else
166
+ data[key] = value;
167
+ }
168
+ if (Object.keys(data).length > 0)
169
+ next.data = data;
170
+ else
171
+ delete next.data;
172
+ }
173
+ if (changes.position !== undefined || changes.size !== undefined || changes.parent !== undefined)
174
+ moved.add(op.id);
175
+ if (changes.parent) {
176
+ const warning = (0, build_js_1.parentWarning)(op.id, changes.parent, nodes.get(changes.parent)?.type);
177
+ if (warning)
178
+ warnings.push(warning);
179
+ }
180
+ nodes.set(op.id, next);
97
181
  break;
98
182
  }
99
183
  case "removeNode": {
@@ -130,7 +214,8 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
130
214
  ...(op.edge.label ? { label: op.edge.label } : {}),
131
215
  routing: op.edge.routing ?? "orthogonal",
132
216
  markerEnd: op.edge.markerEnd ?? "classicThin",
133
- ...(semantic ? { style: (0, callman_core_1.semanticStyle)(semantic) } : {}),
217
+ ...(op.edge.markerStart && op.edge.markerStart !== "none" ? { markerStart: op.edge.markerStart } : {}),
218
+ ...(semantic || op.edge.color ? { style: (0, build_js_1.edgeStyleFor)(semantic, op.edge.color) } : {}),
134
219
  });
135
220
  break;
136
221
  }
@@ -139,20 +224,46 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
139
224
  if (!edge)
140
225
  throw new errors_js_1.ToolInputError("DIAGRAM_EDGE_NOT_FOUND", `Edge "${op.id}" does not exist.`);
141
226
  const changes = op.changes;
142
- const next = {
143
- ...edge,
144
- ...(changes.source !== undefined ? { source: changes.source } : {}),
145
- ...(changes.target !== undefined ? { target: changes.target } : {}),
146
- ...(changes.label !== undefined ? { label: changes.label } : {}),
147
- ...(changes.routing !== undefined ? { routing: changes.routing } : {}),
148
- ...(changes.markerEnd !== undefined ? { markerEnd: changes.markerEnd } : {}),
149
- ...(changes.semantic !== undefined ? { style: (0, callman_core_1.semanticStyle)(changes.semantic) } : {}),
150
- };
227
+ const next = { ...edge };
228
+ if (changes.source !== undefined)
229
+ next.source = changes.source;
230
+ if (changes.target !== undefined)
231
+ next.target = changes.target;
232
+ if (changes.label !== undefined) {
233
+ if (changes.label)
234
+ next.label = changes.label;
235
+ else
236
+ delete next.label;
237
+ }
238
+ if (changes.routing !== undefined)
239
+ next.routing = changes.routing;
240
+ if (changes.markerEnd !== undefined)
241
+ next.markerEnd = changes.markerEnd;
242
+ if (changes.markerStart !== undefined) {
243
+ if (changes.markerStart === "none")
244
+ delete next.markerStart;
245
+ else
246
+ next.markerStart = changes.markerStart;
247
+ }
248
+ // The semantic owns the line tuple (dash + width); the color is kept
249
+ // unless it is changed explicitly.
250
+ if (changes.semantic !== undefined || changes.color !== undefined) {
251
+ const stroke = changes.color === null ? undefined : changes.color !== undefined ? (0, style_js_1.toTokenRef)(changes.color) : edge.style?.stroke;
252
+ const line = changes.semantic !== undefined ? (0, callman_core_1.semanticStyle)(changes.semantic) : { ...(edge.style ?? {}) };
253
+ delete line.stroke;
254
+ const style = { ...line, ...(stroke ? { stroke } : {}) };
255
+ if (Object.keys(style).length > 0)
256
+ next.style = style;
257
+ else
258
+ delete next.style;
259
+ }
151
260
  for (const endpoint of [next.source, next.target]) {
152
261
  if (endpoint && !nodes.has(endpoint) && !edges.has(endpoint)) {
153
262
  throw new errors_js_1.ToolInputError("DIAGRAM_EDGE_ENDPOINT_NOT_FOUND", `Edge endpoint "${endpoint}" does not exist.`);
154
263
  }
155
264
  }
265
+ if (changes.source !== undefined || changes.target !== undefined)
266
+ clearRoute(next);
156
267
  edges.set(op.id, next);
157
268
  break;
158
269
  }
@@ -164,6 +275,28 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
164
275
  }
165
276
  }
166
277
  }
278
+ const reroutedEdgeIds = [];
279
+ if (moved.size > 0) {
280
+ // A moved container carries its children: their edges are stale too.
281
+ const affected = new Set(moved);
282
+ let grew = true;
283
+ while (grew) {
284
+ grew = false;
285
+ for (const node of nodes.values()) {
286
+ if (node.parentId && affected.has(node.parentId) && !affected.has(node.id)) {
287
+ affected.add(node.id);
288
+ grew = true;
289
+ }
290
+ }
291
+ }
292
+ for (const edge of edges.values()) {
293
+ const touches = (edge.source && affected.has(edge.source)) || (edge.target && affected.has(edge.target));
294
+ if (touches && (edge.waypoints || edge.sourceAnchor || edge.targetAnchor || edge.routing === "straight")) {
295
+ clearRoute(edge);
296
+ reroutedEdgeIds.push(edge.id);
297
+ }
298
+ }
299
+ }
167
300
  const document = (0, callman_core_1.normalizeDiagramStyles)({
168
301
  ...doc,
169
302
  meta: { ...doc.meta, title, updatedAt: new Date().toISOString() },
@@ -174,6 +307,13 @@ const applyDiagramPatch = (doc, ops, options = {}) => {
174
307
  if (!parsed.ok) {
175
308
  throw new errors_js_1.ToolInputError("DIAGRAM_DOCUMENT_INVALID", `The patched document is invalid: ${parsed.errors.join("; ")}`, { errors: parsed.errors });
176
309
  }
177
- return { document: parsed.document, addedNodeIds, removedNodeIds, removedEdgeIds, warnings };
310
+ return { document: parsed.document, addedNodeIds, removedNodeIds, removedEdgeIds, reroutedEdgeIds, warnings };
178
311
  };
179
312
  exports.applyDiagramPatch = applyDiagramPatch;
313
+ // Drop a saved route so the studio re-routes the edge orthogonally.
314
+ const clearRoute = (edge) => {
315
+ delete edge.waypoints;
316
+ delete edge.sourceAnchor;
317
+ delete edge.targetAnchor;
318
+ edge.routing = "orthogonal";
319
+ };
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.styleView = exports.applyStyleInput = exports.fromTokenRef = exports.toTokenRef = exports.nodeStylePatchSchema = exports.nodeStyleInputSchema = exports.STYLE_KEYS = exports.colorInputSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const strict_js_1 = require("../../lib/strict.js");
6
+ // The visual style a model may set on a node — exactly the DiagramStyle
7
+ // fields the Diagram Studio renders, typed, so a misspelled key
8
+ // ("strokeColor", "borderColor", "fontWeight") is REJECTED with the list of
9
+ // valid keys instead of being dropped by core's normalizer while the tool
10
+ // reports success. Colors are plain CSS strings on the way in and on the way
11
+ // out; the stored form is core's TokenRef (`{ raw: "#3B82F6" }`).
12
+ exports.colorInputSchema = zod_1.z
13
+ .union([
14
+ zod_1.z.string().trim().min(1).max(64),
15
+ zod_1.z.object({ raw: zod_1.z.string().trim().min(1).max(64) }).strict(),
16
+ zod_1.z.object({ token: zod_1.z.string().trim().min(1).max(64) }).strict(),
17
+ ])
18
+ .describe('CSS color: "#3B82F6", "rgb(59,130,246)", "rgba(59,130,246,0.08)" or a name like "tomato"');
19
+ const FONT_BOLD = 1;
20
+ const FONT_ITALIC = 2;
21
+ const FONT_UNDERLINE = 4;
22
+ const styleFields = {
23
+ fill: exports.colorInputSchema.describe('Background CSS color, e.g. "#EFF6FF". A translucent "rgba(59,130,246,0.08)" tints a container without hiding its children'),
24
+ stroke: exports.colorInputSchema.describe('Border CSS color, e.g. "#3B82F6"'),
25
+ textColor: exports.colorInputSchema.describe('Label CSS color, e.g. "#1E3A8A"'),
26
+ strokeWidth: zod_1.z.number().min(0).max(20).describe("Border width in px (default ~1.5)"),
27
+ dashed: zod_1.z.boolean().describe("Dashed border (containers are dashed by default)"),
28
+ dashPattern: zod_1.z.string().regex(/^\d+(\.\d+)?( \d+(\.\d+)?)+$/, 'SVG dash array like "6 4" or "1 3"').describe('Dash array when dashed: "6 4" dashed, "1 3" dotted'),
29
+ radius: zod_1.z.number().min(0).max(200).describe("Corner radius in px"),
30
+ rounded: zod_1.z.boolean().describe("Rounded corners with the default radius"),
31
+ fontSize: zod_1.z.number().min(6).max(96).describe("Label font size in px"),
32
+ fontFamily: zod_1.z.string().min(1).max(100),
33
+ bold: zod_1.z.boolean().describe("Bold label"),
34
+ italic: zod_1.z.boolean().describe("Italic label"),
35
+ underline: zod_1.z.boolean().describe("Underlined label"),
36
+ align: zod_1.z.enum(["left", "center", "right"]).describe("Horizontal label alignment"),
37
+ verticalAlign: zod_1.z.enum(["top", "middle", "bottom"]).describe("Vertical label alignment"),
38
+ shadow: zod_1.z.boolean(),
39
+ opacity: zod_1.z.number().min(0).max(100).describe("0 (invisible) – 100 (opaque)"),
40
+ };
41
+ exports.STYLE_KEYS = Object.keys(styleFields);
42
+ exports.nodeStyleInputSchema = (0, strict_js_1.strictObject)(Object.fromEntries(Object.entries(styleFields).map(([key, schema]) => [key, schema.optional()])), "style key").describe(`Visual style. Keys: ${exports.STYLE_KEYS.join(", ")}. Colors are CSS strings.`);
43
+ // On updateNode a key set to null removes it; `style: null` clears the style.
44
+ exports.nodeStylePatchSchema = (0, strict_js_1.strictObject)(Object.fromEntries(Object.entries(styleFields).map(([key, schema]) => [key, schema.nullable().optional()])), "style key")
45
+ .nullable()
46
+ .describe(`Style changes, MERGED into the node's current style: only the keys you pass change, a key set to null is removed, style: null clears it. Keys: ${exports.STYLE_KEYS.join(", ")}.`);
47
+ const toTokenRef = (color) => (typeof color === "string" ? { raw: color.trim() } : color);
48
+ exports.toTokenRef = toTokenRef;
49
+ const fromTokenRef = (ref) => {
50
+ if (!ref)
51
+ return undefined;
52
+ return "raw" in ref ? ref.raw : { token: ref.token };
53
+ };
54
+ exports.fromTokenRef = fromTokenRef;
55
+ // Input (plain colors, bold/italic/underline flags) → stored DiagramStyle.
56
+ // `base` is the node's current style for a merge; null values delete.
57
+ const applyStyleInput = (base, input) => {
58
+ const out = { ...(base ?? {}) };
59
+ const assign = (key, value) => {
60
+ if (value === undefined)
61
+ return;
62
+ if (value === null)
63
+ delete out[key];
64
+ else
65
+ out[key] = value;
66
+ };
67
+ for (const key of ["fill", "stroke", "textColor"]) {
68
+ const value = input[key];
69
+ assign(key, value === undefined || value === null ? value : (0, exports.toTokenRef)(value));
70
+ }
71
+ assign("strokeWidth", input.strokeWidth);
72
+ assign("dashed", input.dashed);
73
+ assign("dashPattern", input.dashPattern);
74
+ assign("radius", input.radius);
75
+ assign("rounded", input.rounded);
76
+ assign("fontSize", input.fontSize);
77
+ assign("fontFamily", input.fontFamily);
78
+ assign("align", input.align);
79
+ assign("verticalAlign", input.verticalAlign);
80
+ assign("shadow", input.shadow);
81
+ assign("opacity", input.opacity);
82
+ // The studio stores bold/italic/underline as ONE maxGraph bitmask.
83
+ const flags = [
84
+ [input.bold, FONT_BOLD],
85
+ [input.italic, FONT_ITALIC],
86
+ [input.underline, FONT_UNDERLINE],
87
+ ];
88
+ if (flags.some(([value]) => value !== undefined)) {
89
+ let mask = out.fontStyle ?? 0;
90
+ for (const [value, bit] of flags) {
91
+ if (value === true)
92
+ mask |= bit;
93
+ else if (value === false || value === null)
94
+ mask &= ~bit;
95
+ }
96
+ if (mask === 0)
97
+ delete out.fontStyle;
98
+ else
99
+ out.fontStyle = mask;
100
+ }
101
+ // fontWeight is not rendered by the studio (bold lives in fontStyle).
102
+ delete out.fontWeight;
103
+ return Object.keys(out).length > 0 ? out : undefined;
104
+ };
105
+ exports.applyStyleInput = applyStyleInput;
106
+ // Stored DiagramStyle → the same compact shape a model writes, so what
107
+ // get_diagram returns can be sent straight back in a patch.
108
+ const styleView = (style) => {
109
+ if (!style)
110
+ return undefined;
111
+ const out = {};
112
+ for (const key of ["fill", "stroke", "textColor"]) {
113
+ const value = (0, exports.fromTokenRef)(style[key]);
114
+ if (value !== undefined)
115
+ out[key] = value;
116
+ }
117
+ for (const key of ["strokeWidth", "dashed", "dashPattern", "radius", "rounded", "fontSize", "fontFamily", "align", "verticalAlign", "shadow", "opacity"]) {
118
+ if (style[key] !== undefined)
119
+ out[key] = style[key];
120
+ }
121
+ const mask = style.fontStyle ?? 0;
122
+ if (mask & FONT_BOLD)
123
+ out.bold = true;
124
+ if (mask & FONT_ITALIC)
125
+ out.italic = true;
126
+ if (mask & FONT_UNDERLINE)
127
+ out.underline = true;
128
+ return Object.keys(out).length > 0 ? out : undefined;
129
+ };
130
+ exports.styleView = styleView;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.diagramFullView = exports.diagramGraphView = exports.diagramListView = void 0;
4
4
  const callman_core_1 = require("callman-core");
5
5
  const truncate_js_1 = require("../../lib/truncate.js");
6
+ const style_js_1 = require("./style.js");
6
7
  // Compact views of a stored diagram: the list row, the graph (what is
7
8
  // connected to what, with the edge meaning read back from its style) and
8
9
  // the full document behind a size gate.
@@ -21,21 +22,35 @@ const diagramListView = (diagram) => {
21
22
  };
22
23
  };
23
24
  exports.diagramListView = diagramListView;
25
+ // Style and flags come back in the same compact shape the tools accept, so a
26
+ // model can read a node's look and send a patch without the full document.
24
27
  const diagramGraphView = (doc) => ({
25
28
  title: doc.meta.title,
26
- nodes: doc.nodes.map((node) => ({
27
- id: node.id,
28
- type: node.type,
29
- label: node.label ?? null,
30
- ...(node.parentId ? { parent: node.parentId } : {}),
31
- })),
32
- edges: doc.edges.map((edge) => ({
33
- id: edge.id,
34
- source: edge.source ?? null,
35
- target: edge.target ?? null,
36
- label: edge.label ?? null,
37
- semantic: (0, callman_core_1.semanticFromStyle)(edge.style),
38
- })),
29
+ nodes: doc.nodes.map((node) => {
30
+ const style = (0, style_js_1.styleView)(node.style);
31
+ return {
32
+ id: node.id,
33
+ type: node.type,
34
+ label: node.label ?? null,
35
+ ...(node.parentId ? { parent: node.parentId } : {}),
36
+ ...(style ? { style } : {}),
37
+ ...(node.rotation ? { rotation: node.rotation } : {}),
38
+ ...(node.locked ? { locked: true } : {}),
39
+ ...(node.hidden ? { hidden: true } : {}),
40
+ };
41
+ }),
42
+ edges: doc.edges.map((edge) => {
43
+ const color = (0, style_js_1.fromTokenRef)(edge.style?.stroke);
44
+ return {
45
+ id: edge.id,
46
+ source: edge.source ?? null,
47
+ target: edge.target ?? null,
48
+ label: edge.label ?? null,
49
+ semantic: (0, callman_core_1.semanticFromStyle)(edge.style),
50
+ ...(color ? { color } : {}),
51
+ ...(edge.markerStart ? { markerStart: edge.markerStart } : {}),
52
+ };
53
+ }),
39
54
  });
40
55
  exports.diagramGraphView = diagramGraphView;
41
56
  const diagramFullView = (doc) => {