@genart-dev/mcp-server 0.1.2 → 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.
- package/dist/index.cjs +387 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +383 -14
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +398 -34
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.js +388 -19
- package/dist/lib.js.map +1 -1
- package/package.json +10 -9
package/dist/lib.js
CHANGED
|
@@ -345,6 +345,7 @@ import { writeFile as writeFile2, stat as stat2, unlink } from "fs/promises";
|
|
|
345
345
|
import { basename as basename2, dirname as dirname2, resolve } from "path";
|
|
346
346
|
import {
|
|
347
347
|
createDefaultRegistry,
|
|
348
|
+
resolveComponents,
|
|
348
349
|
resolvePreset,
|
|
349
350
|
serializeGenart,
|
|
350
351
|
serializeWorkspace as serializeWorkspace2
|
|
@@ -441,10 +442,40 @@ async function createSketch(state, input) {
|
|
|
441
442
|
const adapter = registry4.resolve(rendererType);
|
|
442
443
|
algorithm = adapter.getAlgorithmTemplate();
|
|
443
444
|
}
|
|
445
|
+
let resolvedComponents;
|
|
446
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
447
|
+
const shorthand = {};
|
|
448
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
449
|
+
if (typeof value === "string") {
|
|
450
|
+
shorthand[name] = value;
|
|
451
|
+
} else if (value.version) {
|
|
452
|
+
shorthand[name] = value.version;
|
|
453
|
+
} else if (value.code) {
|
|
454
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
455
|
+
resolvedComponents[name] = {
|
|
456
|
+
...value.version ? { version: value.version } : {},
|
|
457
|
+
code: value.code,
|
|
458
|
+
...value.exports ? { exports: value.exports } : {}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (Object.keys(shorthand).length > 0) {
|
|
463
|
+
const resolved = resolveComponents(shorthand, rendererType);
|
|
464
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
465
|
+
for (const rc of resolved) {
|
|
466
|
+
resolvedComponents[rc.name] = {
|
|
467
|
+
version: rc.version,
|
|
468
|
+
code: rc.code,
|
|
469
|
+
exports: [...rc.exports]
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
444
474
|
const seed = input.seed ?? Math.floor(Math.random() * 1e5);
|
|
445
475
|
const ts = now2();
|
|
476
|
+
const hasComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
446
477
|
const sketch = {
|
|
447
|
-
genart: "1.1",
|
|
478
|
+
genart: hasComponents ? "1.2" : "1.1",
|
|
448
479
|
id: input.id,
|
|
449
480
|
title: input.title,
|
|
450
481
|
created: ts,
|
|
@@ -458,6 +489,7 @@ async function createSketch(state, input) {
|
|
|
458
489
|
...input.philosophy ? { philosophy: input.philosophy } : {},
|
|
459
490
|
...input.themes && input.themes.length > 0 ? { themes: input.themes } : {},
|
|
460
491
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
492
|
+
...hasComponents ? { components: resolvedComponents } : {},
|
|
461
493
|
...input.agent ? { agent: input.agent } : {},
|
|
462
494
|
...input.model ? { model: input.model } : {}
|
|
463
495
|
};
|
|
@@ -641,10 +673,44 @@ async function updateAlgorithm(state, input) {
|
|
|
641
673
|
);
|
|
642
674
|
}
|
|
643
675
|
}
|
|
676
|
+
let resolvedComponents;
|
|
677
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
678
|
+
const renderer = def.renderer.type;
|
|
679
|
+
const shorthand = {};
|
|
680
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
681
|
+
if (typeof value === "string") {
|
|
682
|
+
shorthand[name] = value;
|
|
683
|
+
} else if (value.version) {
|
|
684
|
+
shorthand[name] = value.version;
|
|
685
|
+
} else if (value.code) {
|
|
686
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
687
|
+
resolvedComponents[name] = {
|
|
688
|
+
...value.version ? { version: value.version } : {},
|
|
689
|
+
code: value.code,
|
|
690
|
+
...value.exports ? { exports: value.exports } : {}
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (Object.keys(shorthand).length > 0) {
|
|
695
|
+
const resolved = resolveComponents(shorthand, renderer);
|
|
696
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
697
|
+
for (const rc of resolved) {
|
|
698
|
+
resolvedComponents[rc.name] = {
|
|
699
|
+
version: rc.version,
|
|
700
|
+
code: rc.code,
|
|
701
|
+
exports: [...rc.exports]
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
const updated = ["algorithm"];
|
|
707
|
+
const hasNewComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
708
|
+
if (hasNewComponents) updated.push("components");
|
|
644
709
|
const newDef = {
|
|
645
710
|
...def,
|
|
646
711
|
modified: now2(),
|
|
647
712
|
algorithm: input.algorithm,
|
|
713
|
+
...hasNewComponents ? { genart: "1.2", components: resolvedComponents } : {},
|
|
648
714
|
...input.agent ? { agent: input.agent } : {},
|
|
649
715
|
...input.model ? { model: input.model } : {}
|
|
650
716
|
};
|
|
@@ -658,7 +724,7 @@ async function updateAlgorithm(state, input) {
|
|
|
658
724
|
}
|
|
659
725
|
state.emitMutation("sketch:updated", {
|
|
660
726
|
id: input.sketchId,
|
|
661
|
-
updated
|
|
727
|
+
updated
|
|
662
728
|
});
|
|
663
729
|
return {
|
|
664
730
|
success: true,
|
|
@@ -666,6 +732,7 @@ async function updateAlgorithm(state, input) {
|
|
|
666
732
|
renderer: def.renderer.type,
|
|
667
733
|
algorithmLength: input.algorithm.length,
|
|
668
734
|
validationPassed,
|
|
735
|
+
...hasNewComponents ? { componentsUpdated: true } : {},
|
|
669
736
|
fileContent: json
|
|
670
737
|
};
|
|
671
738
|
}
|
|
@@ -1967,8 +2034,219 @@ ${guidelines}`,
|
|
|
1967
2034
|
};
|
|
1968
2035
|
}
|
|
1969
2036
|
|
|
1970
|
-
// src/tools/
|
|
2037
|
+
// src/tools/components.ts
|
|
1971
2038
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
2039
|
+
import {
|
|
2040
|
+
COMPONENT_REGISTRY,
|
|
2041
|
+
resolveComponents as resolveComponents2,
|
|
2042
|
+
serializeGenart as serializeGenart3
|
|
2043
|
+
} from "@genart-dev/core";
|
|
2044
|
+
var VALID_RENDERERS2 = [
|
|
2045
|
+
"p5",
|
|
2046
|
+
"three",
|
|
2047
|
+
"glsl",
|
|
2048
|
+
"canvas2d",
|
|
2049
|
+
"svg"
|
|
2050
|
+
];
|
|
2051
|
+
var RENDERER_TARGET = {
|
|
2052
|
+
p5: "js",
|
|
2053
|
+
three: "js",
|
|
2054
|
+
canvas2d: "js",
|
|
2055
|
+
svg: "js",
|
|
2056
|
+
glsl: "glsl"
|
|
2057
|
+
};
|
|
2058
|
+
async function listComponents(_state, input) {
|
|
2059
|
+
let entries = Object.values(COMPONENT_REGISTRY);
|
|
2060
|
+
if (input.renderer) {
|
|
2061
|
+
const renderer = input.renderer;
|
|
2062
|
+
const target = RENDERER_TARGET[renderer];
|
|
2063
|
+
if (!target) {
|
|
2064
|
+
throw new Error(
|
|
2065
|
+
`Unknown renderer type: '${input.renderer}'. Valid types: ${VALID_RENDERERS2.join(", ")}`
|
|
2066
|
+
);
|
|
2067
|
+
}
|
|
2068
|
+
entries = entries.filter(
|
|
2069
|
+
(e) => e.target === target && (e.renderers.length === 0 || e.renderers.includes(renderer))
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
if (input.category) {
|
|
2073
|
+
const cat = input.category;
|
|
2074
|
+
entries = entries.filter((e) => e.category === cat);
|
|
2075
|
+
}
|
|
2076
|
+
entries.sort((a, b) => {
|
|
2077
|
+
const catCmp = a.category.localeCompare(b.category);
|
|
2078
|
+
if (catCmp !== 0) return catCmp;
|
|
2079
|
+
return a.name.localeCompare(b.name);
|
|
2080
|
+
});
|
|
2081
|
+
const components = entries.map((e) => ({
|
|
2082
|
+
name: e.name,
|
|
2083
|
+
version: e.version,
|
|
2084
|
+
category: e.category,
|
|
2085
|
+
target: e.target,
|
|
2086
|
+
exports: [...e.exports],
|
|
2087
|
+
dependencies: [...e.dependencies],
|
|
2088
|
+
description: e.description
|
|
2089
|
+
}));
|
|
2090
|
+
return {
|
|
2091
|
+
count: components.length,
|
|
2092
|
+
components
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
async function addComponent(state, input) {
|
|
2096
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2097
|
+
const def = loaded.definition;
|
|
2098
|
+
const renderer = def.renderer.type;
|
|
2099
|
+
const entry = COMPONENT_REGISTRY[input.component];
|
|
2100
|
+
if (!entry) {
|
|
2101
|
+
throw new Error(`Unknown component: "${input.component}"`);
|
|
2102
|
+
}
|
|
2103
|
+
const target = RENDERER_TARGET[renderer];
|
|
2104
|
+
if (entry.target !== target) {
|
|
2105
|
+
throw new Error(
|
|
2106
|
+
`Component "${input.component}" has target "${entry.target}" but renderer "${renderer}" requires target "${target}"`
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
if (entry.renderers.length > 0 && !entry.renderers.includes(renderer)) {
|
|
2110
|
+
throw new Error(
|
|
2111
|
+
`Component "${input.component}" is not compatible with renderer "${renderer}". Compatible: ${entry.renderers.join(", ")}`
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
const existingComponents = {};
|
|
2115
|
+
if (def.components) {
|
|
2116
|
+
for (const [name, value] of Object.entries(def.components)) {
|
|
2117
|
+
if (typeof value === "string") {
|
|
2118
|
+
existingComponents[name] = value;
|
|
2119
|
+
} else if (value.version) {
|
|
2120
|
+
existingComponents[name] = value.version;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
if (existingComponents[input.component]) {
|
|
2125
|
+
throw new Error(
|
|
2126
|
+
`Component "${input.component}" is already present in sketch "${input.sketchId}"`
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
existingComponents[input.component] = input.version ?? "^1.0.0";
|
|
2130
|
+
const resolved = resolveComponents2(existingComponents, renderer);
|
|
2131
|
+
const resolvedRecord = {};
|
|
2132
|
+
for (const rc of resolved) {
|
|
2133
|
+
resolvedRecord[rc.name] = {
|
|
2134
|
+
version: rc.version,
|
|
2135
|
+
code: rc.code,
|
|
2136
|
+
exports: [...rc.exports]
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
const previousNames = new Set(
|
|
2140
|
+
def.components ? Object.keys(def.components) : []
|
|
2141
|
+
);
|
|
2142
|
+
const added = resolved.map((rc) => rc.name).filter((name) => !previousNames.has(name));
|
|
2143
|
+
const newDef = {
|
|
2144
|
+
...def,
|
|
2145
|
+
genart: "1.2",
|
|
2146
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2147
|
+
components: resolvedRecord
|
|
2148
|
+
};
|
|
2149
|
+
state.sketches.set(input.sketchId, {
|
|
2150
|
+
definition: newDef,
|
|
2151
|
+
path: loaded.path
|
|
2152
|
+
});
|
|
2153
|
+
const json = serializeGenart3(newDef);
|
|
2154
|
+
if (!state.remoteMode) {
|
|
2155
|
+
await writeFile4(loaded.path, json, "utf-8");
|
|
2156
|
+
}
|
|
2157
|
+
state.emitMutation("sketch:updated", {
|
|
2158
|
+
id: input.sketchId,
|
|
2159
|
+
updated: ["components"]
|
|
2160
|
+
});
|
|
2161
|
+
return {
|
|
2162
|
+
success: true,
|
|
2163
|
+
sketchId: input.sketchId,
|
|
2164
|
+
components: resolvedRecord,
|
|
2165
|
+
added,
|
|
2166
|
+
fileContent: json
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
async function removeComponent(state, input) {
|
|
2170
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2171
|
+
const def = loaded.definition;
|
|
2172
|
+
if (!def.components || !def.components[input.component]) {
|
|
2173
|
+
throw new Error(
|
|
2174
|
+
`Component "${input.component}" is not present in sketch "${input.sketchId}"`
|
|
2175
|
+
);
|
|
2176
|
+
}
|
|
2177
|
+
const remaining = { ...def.components };
|
|
2178
|
+
delete remaining[input.component];
|
|
2179
|
+
for (const [name, value] of Object.entries(remaining)) {
|
|
2180
|
+
const entry = COMPONENT_REGISTRY[name];
|
|
2181
|
+
if (entry && entry.dependencies.includes(input.component)) {
|
|
2182
|
+
throw new Error(
|
|
2183
|
+
`Cannot remove "${input.component}": component "${name}" depends on it`
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
let warning;
|
|
2188
|
+
const removedValue = def.components[input.component];
|
|
2189
|
+
const exports = typeof removedValue === "string" ? COMPONENT_REGISTRY[input.component]?.exports ?? [] : removedValue.exports ?? [];
|
|
2190
|
+
const usedExports = exports.filter((exp) => def.algorithm.includes(exp));
|
|
2191
|
+
if (usedExports.length > 0) {
|
|
2192
|
+
warning = `Algorithm may reference these exports from "${input.component}": ${usedExports.join(", ")}. Review your algorithm after removal.`;
|
|
2193
|
+
}
|
|
2194
|
+
const removed = [input.component];
|
|
2195
|
+
const neededDeps = /* @__PURE__ */ new Set();
|
|
2196
|
+
for (const name of Object.keys(remaining)) {
|
|
2197
|
+
const entry = COMPONENT_REGISTRY[name];
|
|
2198
|
+
if (entry) {
|
|
2199
|
+
collectTransitiveDeps(name, neededDeps);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
for (const name of Object.keys(remaining)) {
|
|
2203
|
+
if (!neededDeps.has(name) && !isDirectComponent(name, remaining)) {
|
|
2204
|
+
delete remaining[name];
|
|
2205
|
+
removed.push(name);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
const hasRemaining = Object.keys(remaining).length > 0;
|
|
2209
|
+
const newDef = {
|
|
2210
|
+
...def,
|
|
2211
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2212
|
+
...hasRemaining ? { components: remaining } : { components: void 0 }
|
|
2213
|
+
};
|
|
2214
|
+
state.sketches.set(input.sketchId, {
|
|
2215
|
+
definition: newDef,
|
|
2216
|
+
path: loaded.path
|
|
2217
|
+
});
|
|
2218
|
+
const json = serializeGenart3(newDef);
|
|
2219
|
+
if (!state.remoteMode) {
|
|
2220
|
+
await writeFile4(loaded.path, json, "utf-8");
|
|
2221
|
+
}
|
|
2222
|
+
state.emitMutation("sketch:updated", {
|
|
2223
|
+
id: input.sketchId,
|
|
2224
|
+
updated: ["components"]
|
|
2225
|
+
});
|
|
2226
|
+
return {
|
|
2227
|
+
success: true,
|
|
2228
|
+
sketchId: input.sketchId,
|
|
2229
|
+
removed,
|
|
2230
|
+
...warning ? { warning } : {},
|
|
2231
|
+
fileContent: json
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
function collectTransitiveDeps(name, deps) {
|
|
2235
|
+
const entry = COMPONENT_REGISTRY[name];
|
|
2236
|
+
if (!entry) return;
|
|
2237
|
+
deps.add(name);
|
|
2238
|
+
for (const dep of entry.dependencies) {
|
|
2239
|
+
if (!deps.has(dep)) {
|
|
2240
|
+
collectTransitiveDeps(dep, deps);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
function isDirectComponent(name, components) {
|
|
2245
|
+
return name in components;
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
// src/tools/capture.ts
|
|
2249
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
1972
2250
|
import {
|
|
1973
2251
|
createDefaultRegistry as createDefaultRegistry2
|
|
1974
2252
|
} from "@genart-dev/core";
|
|
@@ -2146,7 +2424,7 @@ async function buildScreenshotMetadata(state, multi, info) {
|
|
|
2146
2424
|
previewPath: info.previewPath
|
|
2147
2425
|
};
|
|
2148
2426
|
if (!state.remoteMode) {
|
|
2149
|
-
await
|
|
2427
|
+
await writeFile5(info.previewPath, multi.previewPng);
|
|
2150
2428
|
metadata.savedPreviewTo = info.previewPath;
|
|
2151
2429
|
}
|
|
2152
2430
|
return metadata;
|
|
@@ -2207,12 +2485,12 @@ async function captureBatch(state, input) {
|
|
|
2207
2485
|
|
|
2208
2486
|
// src/tools/export.ts
|
|
2209
2487
|
import { createWriteStream } from "fs";
|
|
2210
|
-
import { stat as stat4, writeFile as
|
|
2488
|
+
import { stat as stat4, writeFile as writeFile6 } from "fs/promises";
|
|
2211
2489
|
import { dirname as dirname5 } from "path";
|
|
2212
2490
|
import archiver from "archiver";
|
|
2213
2491
|
import {
|
|
2214
2492
|
createDefaultRegistry as createDefaultRegistry3,
|
|
2215
|
-
serializeGenart as
|
|
2493
|
+
serializeGenart as serializeGenart4
|
|
2216
2494
|
} from "@genart-dev/core";
|
|
2217
2495
|
var registry3 = createDefaultRegistry3();
|
|
2218
2496
|
async function validateOutputPath(outputPath) {
|
|
@@ -2282,7 +2560,7 @@ async function exportHtml(sketch, outputPath) {
|
|
|
2282
2560
|
const adapter = registry3.resolve(sketch.renderer.type);
|
|
2283
2561
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2284
2562
|
const content = Buffer.from(html, "utf-8");
|
|
2285
|
-
await
|
|
2563
|
+
await writeFile6(outputPath, content);
|
|
2286
2564
|
return {
|
|
2287
2565
|
success: true,
|
|
2288
2566
|
sketchId: sketch.id,
|
|
@@ -2298,7 +2576,7 @@ async function exportPng(sketch, input) {
|
|
|
2298
2576
|
const width = input.width ?? sketch.canvas.width;
|
|
2299
2577
|
const height = input.height ?? sketch.canvas.height;
|
|
2300
2578
|
const result = await captureHtml({ html, width, height });
|
|
2301
|
-
await
|
|
2579
|
+
await writeFile6(input.outputPath, result.bytes);
|
|
2302
2580
|
return {
|
|
2303
2581
|
success: true,
|
|
2304
2582
|
sketchId: sketch.id,
|
|
@@ -2313,7 +2591,7 @@ async function exportSvg(sketch, input) {
|
|
|
2313
2591
|
const height = input.height ?? sketch.canvas.height;
|
|
2314
2592
|
if (sketch.renderer.type === "svg") {
|
|
2315
2593
|
const content2 = Buffer.from(sketch.algorithm, "utf-8");
|
|
2316
|
-
await
|
|
2594
|
+
await writeFile6(input.outputPath, content2);
|
|
2317
2595
|
return {
|
|
2318
2596
|
success: true,
|
|
2319
2597
|
sketchId: sketch.id,
|
|
@@ -2335,7 +2613,7 @@ async function exportSvg(sketch, input) {
|
|
|
2335
2613
|
href="data:image/png;base64,${b64}"/>
|
|
2336
2614
|
</svg>`;
|
|
2337
2615
|
const content = Buffer.from(svg, "utf-8");
|
|
2338
|
-
await
|
|
2616
|
+
await writeFile6(input.outputPath, content);
|
|
2339
2617
|
return {
|
|
2340
2618
|
success: true,
|
|
2341
2619
|
sketchId: sketch.id,
|
|
@@ -2348,7 +2626,7 @@ async function exportSvg(sketch, input) {
|
|
|
2348
2626
|
}
|
|
2349
2627
|
async function exportAlgorithm(sketch, outputPath) {
|
|
2350
2628
|
const content = Buffer.from(sketch.algorithm, "utf-8");
|
|
2351
|
-
await
|
|
2629
|
+
await writeFile6(outputPath, content);
|
|
2352
2630
|
return {
|
|
2353
2631
|
success: true,
|
|
2354
2632
|
sketchId: sketch.id,
|
|
@@ -2363,7 +2641,7 @@ async function exportZip(sketch, input) {
|
|
|
2363
2641
|
const width = input.width ?? sketch.canvas.width;
|
|
2364
2642
|
const height = input.height ?? sketch.canvas.height;
|
|
2365
2643
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2366
|
-
const genartJson =
|
|
2644
|
+
const genartJson = serializeGenart4(sketch);
|
|
2367
2645
|
const algorithm = sketch.algorithm;
|
|
2368
2646
|
const algExt = algorithmExtension(sketch.renderer.type);
|
|
2369
2647
|
const captureResult = await captureHtml({ html, width, height });
|
|
@@ -2900,6 +3178,7 @@ function createServer(state) {
|
|
|
2900
3178
|
);
|
|
2901
3179
|
registerWorkspaceTools(server, state);
|
|
2902
3180
|
registerSketchTools(server, state);
|
|
3181
|
+
registerComponentTools(server, state);
|
|
2903
3182
|
registerSelectionTools(server, state);
|
|
2904
3183
|
registerParameterTools(server, state);
|
|
2905
3184
|
registerArrangementTools(server, state);
|
|
@@ -3003,7 +3282,7 @@ function registerWorkspaceTools(server, state) {
|
|
|
3003
3282
|
function registerSketchTools(server, state) {
|
|
3004
3283
|
server.tool(
|
|
3005
3284
|
"create_sketch",
|
|
3006
|
-
|
|
3285
|
+
'Create a new .genart sketch file from metadata, parameters, and algorithm. IMPORTANT: Do not embed common utilities (PRNG, noise, easing, color math, vector ops) inline in the algorithm. Instead, declare them as components: { "prng": "^1.0.0", "noise-2d": "^1.0.0" }. Then use the exported functions directly in your algorithm (e.g., mulberry32, fbm2D). Use list_components to see all available components for the current renderer.',
|
|
3007
3286
|
{
|
|
3008
3287
|
id: z2.string().describe("URL-safe kebab-case identifier"),
|
|
3009
3288
|
title: z2.string().describe("Human-readable title"),
|
|
@@ -3041,6 +3320,16 @@ function registerSketchTools(server, state) {
|
|
|
3041
3320
|
algorithm: z2.string().optional().describe("Algorithm source code (default: renderer template). For p5: must be `function sketch(p, state) { ... }` in instance mode. State provides: state.WIDTH, state.HEIGHT, state.SEED (number), state.PARAMS (keyed by param key), state.COLORS (keyed by color key, hex strings). Use p5 instance methods (p.createCanvas, p.background, etc)."),
|
|
3042
3321
|
seed: z2.number().optional().describe("Initial random seed (default: random)"),
|
|
3043
3322
|
skills: z2.array(z2.string()).optional().describe("Design skill references"),
|
|
3323
|
+
components: z2.record(
|
|
3324
|
+
z2.union([
|
|
3325
|
+
z2.string(),
|
|
3326
|
+
z2.object({
|
|
3327
|
+
version: z2.string().optional(),
|
|
3328
|
+
code: z2.string().optional(),
|
|
3329
|
+
exports: z2.array(z2.string()).optional()
|
|
3330
|
+
})
|
|
3331
|
+
])
|
|
3332
|
+
).optional().describe('Component dependencies. Use list_components to see available. Keys are component names, values are semver ranges (e.g. "^1.0.0") or objects with version/code/exports.'),
|
|
3044
3333
|
addToWorkspace: z2.string().optional().describe("Path to workspace to add sketch to after creation"),
|
|
3045
3334
|
agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3046
3335
|
model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
@@ -3120,11 +3409,21 @@ function registerSketchTools(server, state) {
|
|
|
3120
3409
|
);
|
|
3121
3410
|
server.tool(
|
|
3122
3411
|
"update_algorithm",
|
|
3123
|
-
"Replace the algorithm source code of a sketch",
|
|
3412
|
+
"Replace the algorithm source code of a sketch. If adding/changing components, pass them in the components field alongside the algorithm.",
|
|
3124
3413
|
{
|
|
3125
3414
|
sketchId: z2.string().describe("ID of the sketch to update"),
|
|
3126
3415
|
algorithm: z2.string().describe("New algorithm source code. For p5: must be `function sketch(p, state) { ... }` in instance mode. State provides: state.WIDTH, state.HEIGHT, state.SEED, state.PARAMS (keyed by param key), state.COLORS (keyed by color key)."),
|
|
3127
3416
|
validate: z2.boolean().optional().describe("Run renderer-specific validation before saving (default: true)"),
|
|
3417
|
+
components: z2.record(
|
|
3418
|
+
z2.union([
|
|
3419
|
+
z2.string(),
|
|
3420
|
+
z2.object({
|
|
3421
|
+
version: z2.string().optional(),
|
|
3422
|
+
code: z2.string().optional(),
|
|
3423
|
+
exports: z2.array(z2.string()).optional()
|
|
3424
|
+
})
|
|
3425
|
+
])
|
|
3426
|
+
).optional().describe("Component dependencies to resolve alongside the algorithm update. Use list_components to see available."),
|
|
3128
3427
|
agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3129
3428
|
model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
3130
3429
|
},
|
|
@@ -3220,6 +3519,76 @@ function registerSketchTools(server, state) {
|
|
|
3220
3519
|
}
|
|
3221
3520
|
);
|
|
3222
3521
|
}
|
|
3522
|
+
function registerComponentTools(server, state) {
|
|
3523
|
+
server.tool(
|
|
3524
|
+
"list_components",
|
|
3525
|
+
"List available reusable components from the registry, filtered by renderer and/or category. Components provide common utilities (PRNG, noise, easing, color math, etc.) that can be declared as dependencies instead of inlining code in the algorithm.",
|
|
3526
|
+
{
|
|
3527
|
+
renderer: z2.enum(["p5", "three", "glsl", "canvas2d", "svg"]).optional().describe("Filter by renderer compatibility"),
|
|
3528
|
+
category: z2.enum([
|
|
3529
|
+
"randomness",
|
|
3530
|
+
"noise",
|
|
3531
|
+
"math",
|
|
3532
|
+
"easing",
|
|
3533
|
+
"color",
|
|
3534
|
+
"vector",
|
|
3535
|
+
"geometry",
|
|
3536
|
+
"grid",
|
|
3537
|
+
"particle",
|
|
3538
|
+
"physics",
|
|
3539
|
+
"distribution",
|
|
3540
|
+
"pattern",
|
|
3541
|
+
"sdf",
|
|
3542
|
+
"transform",
|
|
3543
|
+
"animation",
|
|
3544
|
+
"string",
|
|
3545
|
+
"data-structure",
|
|
3546
|
+
"imaging"
|
|
3547
|
+
]).optional().describe("Filter by component category")
|
|
3548
|
+
},
|
|
3549
|
+
async (args) => {
|
|
3550
|
+
try {
|
|
3551
|
+
const result = await listComponents(state, args);
|
|
3552
|
+
return jsonResult(result);
|
|
3553
|
+
} catch (e) {
|
|
3554
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
);
|
|
3558
|
+
server.tool(
|
|
3559
|
+
"add_component",
|
|
3560
|
+
"Add a component dependency to an existing sketch. Resolves the component and any transitive dependencies from the registry, validates renderer compatibility, and writes the resolved form to the sketch file.",
|
|
3561
|
+
{
|
|
3562
|
+
sketchId: z2.string().describe("ID of the sketch to add the component to"),
|
|
3563
|
+
component: z2.string().describe("Component name (e.g. 'prng', 'noise-2d', 'glsl-noise')"),
|
|
3564
|
+
version: z2.string().optional().describe("Version range (default: '^1.0.0')")
|
|
3565
|
+
},
|
|
3566
|
+
async (args) => {
|
|
3567
|
+
try {
|
|
3568
|
+
const result = await addComponent(state, args);
|
|
3569
|
+
return jsonResult(result);
|
|
3570
|
+
} catch (e) {
|
|
3571
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
);
|
|
3575
|
+
server.tool(
|
|
3576
|
+
"remove_component",
|
|
3577
|
+
"Remove a component dependency from a sketch. Checks for dependent components and warns if the algorithm references the component's exports.",
|
|
3578
|
+
{
|
|
3579
|
+
sketchId: z2.string().describe("ID of the sketch to remove the component from"),
|
|
3580
|
+
component: z2.string().describe("Component name to remove")
|
|
3581
|
+
},
|
|
3582
|
+
async (args) => {
|
|
3583
|
+
try {
|
|
3584
|
+
const result = await removeComponent(state, args);
|
|
3585
|
+
return jsonResult(result);
|
|
3586
|
+
} catch (e) {
|
|
3587
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3223
3592
|
function registerSelectionTools(server, state) {
|
|
3224
3593
|
server.tool(
|
|
3225
3594
|
"get_selection",
|
|
@@ -3676,10 +4045,10 @@ function notifyMutation(type, payload) {
|
|
|
3676
4045
|
import {
|
|
3677
4046
|
parseGenart as parseGenart4,
|
|
3678
4047
|
parseWorkspace as parseWorkspace2,
|
|
3679
|
-
serializeGenart as
|
|
4048
|
+
serializeGenart as serializeGenart5,
|
|
3680
4049
|
serializeWorkspace as serializeWorkspace3
|
|
3681
4050
|
} from "@genart-dev/core";
|
|
3682
|
-
import { writeFile as
|
|
4051
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
3683
4052
|
var EditorState = class extends EventEmitter {
|
|
3684
4053
|
/** Absolute path to the active .genart-workspace file, or null. */
|
|
3685
4054
|
workspacePath = null;
|
|
@@ -3815,16 +4184,16 @@ var EditorState = class extends EventEmitter {
|
|
|
3815
4184
|
}
|
|
3816
4185
|
const json = serializeWorkspace3(this.workspace);
|
|
3817
4186
|
if (!this.remoteMode) {
|
|
3818
|
-
await
|
|
4187
|
+
await writeFile7(this.workspacePath, json, "utf-8");
|
|
3819
4188
|
}
|
|
3820
4189
|
this.emitMutation("workspace:saved", { path: this.workspacePath });
|
|
3821
4190
|
}
|
|
3822
4191
|
/** Save a sketch to disk. */
|
|
3823
4192
|
async saveSketch(id) {
|
|
3824
4193
|
const loaded = this.requireSketch(id);
|
|
3825
|
-
const json =
|
|
4194
|
+
const json = serializeGenart5(loaded.definition);
|
|
3826
4195
|
if (!this.remoteMode) {
|
|
3827
|
-
await
|
|
4196
|
+
await writeFile7(loaded.path, json, "utf-8");
|
|
3828
4197
|
}
|
|
3829
4198
|
this.emitMutation("sketch:saved", { id, path: loaded.path });
|
|
3830
4199
|
}
|