@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/index.js
CHANGED
|
@@ -551,6 +551,7 @@ import { writeFile as writeFile3, stat as stat2, unlink } from "fs/promises";
|
|
|
551
551
|
import { basename as basename2, dirname as dirname3, resolve as resolve2 } from "path";
|
|
552
552
|
import {
|
|
553
553
|
createDefaultRegistry,
|
|
554
|
+
resolveComponents,
|
|
554
555
|
resolvePreset,
|
|
555
556
|
serializeGenart as serializeGenart2,
|
|
556
557
|
serializeWorkspace as serializeWorkspace3
|
|
@@ -647,10 +648,40 @@ async function createSketch(state, input) {
|
|
|
647
648
|
const adapter = registry4.resolve(rendererType);
|
|
648
649
|
algorithm = adapter.getAlgorithmTemplate();
|
|
649
650
|
}
|
|
651
|
+
let resolvedComponents;
|
|
652
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
653
|
+
const shorthand = {};
|
|
654
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
655
|
+
if (typeof value === "string") {
|
|
656
|
+
shorthand[name] = value;
|
|
657
|
+
} else if (value.version) {
|
|
658
|
+
shorthand[name] = value.version;
|
|
659
|
+
} else if (value.code) {
|
|
660
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
661
|
+
resolvedComponents[name] = {
|
|
662
|
+
...value.version ? { version: value.version } : {},
|
|
663
|
+
code: value.code,
|
|
664
|
+
...value.exports ? { exports: value.exports } : {}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (Object.keys(shorthand).length > 0) {
|
|
669
|
+
const resolved = resolveComponents(shorthand, rendererType);
|
|
670
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
671
|
+
for (const rc of resolved) {
|
|
672
|
+
resolvedComponents[rc.name] = {
|
|
673
|
+
version: rc.version,
|
|
674
|
+
code: rc.code,
|
|
675
|
+
exports: [...rc.exports]
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
650
680
|
const seed = input.seed ?? Math.floor(Math.random() * 1e5);
|
|
651
681
|
const ts = now2();
|
|
682
|
+
const hasComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
652
683
|
const sketch = {
|
|
653
|
-
genart: "1.1",
|
|
684
|
+
genart: hasComponents ? "1.2" : "1.1",
|
|
654
685
|
id: input.id,
|
|
655
686
|
title: input.title,
|
|
656
687
|
created: ts,
|
|
@@ -664,6 +695,7 @@ async function createSketch(state, input) {
|
|
|
664
695
|
...input.philosophy ? { philosophy: input.philosophy } : {},
|
|
665
696
|
...input.themes && input.themes.length > 0 ? { themes: input.themes } : {},
|
|
666
697
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
698
|
+
...hasComponents ? { components: resolvedComponents } : {},
|
|
667
699
|
...input.agent ? { agent: input.agent } : {},
|
|
668
700
|
...input.model ? { model: input.model } : {}
|
|
669
701
|
};
|
|
@@ -847,10 +879,44 @@ async function updateAlgorithm(state, input) {
|
|
|
847
879
|
);
|
|
848
880
|
}
|
|
849
881
|
}
|
|
882
|
+
let resolvedComponents;
|
|
883
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
884
|
+
const renderer = def.renderer.type;
|
|
885
|
+
const shorthand = {};
|
|
886
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
887
|
+
if (typeof value === "string") {
|
|
888
|
+
shorthand[name] = value;
|
|
889
|
+
} else if (value.version) {
|
|
890
|
+
shorthand[name] = value.version;
|
|
891
|
+
} else if (value.code) {
|
|
892
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
893
|
+
resolvedComponents[name] = {
|
|
894
|
+
...value.version ? { version: value.version } : {},
|
|
895
|
+
code: value.code,
|
|
896
|
+
...value.exports ? { exports: value.exports } : {}
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (Object.keys(shorthand).length > 0) {
|
|
901
|
+
const resolved = resolveComponents(shorthand, renderer);
|
|
902
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
903
|
+
for (const rc of resolved) {
|
|
904
|
+
resolvedComponents[rc.name] = {
|
|
905
|
+
version: rc.version,
|
|
906
|
+
code: rc.code,
|
|
907
|
+
exports: [...rc.exports]
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const updated = ["algorithm"];
|
|
913
|
+
const hasNewComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
914
|
+
if (hasNewComponents) updated.push("components");
|
|
850
915
|
const newDef = {
|
|
851
916
|
...def,
|
|
852
917
|
modified: now2(),
|
|
853
918
|
algorithm: input.algorithm,
|
|
919
|
+
...hasNewComponents ? { genart: "1.2", components: resolvedComponents } : {},
|
|
854
920
|
...input.agent ? { agent: input.agent } : {},
|
|
855
921
|
...input.model ? { model: input.model } : {}
|
|
856
922
|
};
|
|
@@ -864,7 +930,7 @@ async function updateAlgorithm(state, input) {
|
|
|
864
930
|
}
|
|
865
931
|
state.emitMutation("sketch:updated", {
|
|
866
932
|
id: input.sketchId,
|
|
867
|
-
updated
|
|
933
|
+
updated
|
|
868
934
|
});
|
|
869
935
|
return {
|
|
870
936
|
success: true,
|
|
@@ -872,6 +938,7 @@ async function updateAlgorithm(state, input) {
|
|
|
872
938
|
renderer: def.renderer.type,
|
|
873
939
|
algorithmLength: input.algorithm.length,
|
|
874
940
|
validationPassed,
|
|
941
|
+
...hasNewComponents ? { componentsUpdated: true } : {},
|
|
875
942
|
fileContent: json
|
|
876
943
|
};
|
|
877
944
|
}
|
|
@@ -2173,8 +2240,219 @@ ${guidelines}`,
|
|
|
2173
2240
|
};
|
|
2174
2241
|
}
|
|
2175
2242
|
|
|
2176
|
-
// src/tools/
|
|
2243
|
+
// src/tools/components.ts
|
|
2177
2244
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
2245
|
+
import {
|
|
2246
|
+
COMPONENT_REGISTRY,
|
|
2247
|
+
resolveComponents as resolveComponents2,
|
|
2248
|
+
serializeGenart as serializeGenart4
|
|
2249
|
+
} from "@genart-dev/core";
|
|
2250
|
+
var VALID_RENDERERS2 = [
|
|
2251
|
+
"p5",
|
|
2252
|
+
"three",
|
|
2253
|
+
"glsl",
|
|
2254
|
+
"canvas2d",
|
|
2255
|
+
"svg"
|
|
2256
|
+
];
|
|
2257
|
+
var RENDERER_TARGET = {
|
|
2258
|
+
p5: "js",
|
|
2259
|
+
three: "js",
|
|
2260
|
+
canvas2d: "js",
|
|
2261
|
+
svg: "js",
|
|
2262
|
+
glsl: "glsl"
|
|
2263
|
+
};
|
|
2264
|
+
async function listComponents(_state, input) {
|
|
2265
|
+
let entries = Object.values(COMPONENT_REGISTRY);
|
|
2266
|
+
if (input.renderer) {
|
|
2267
|
+
const renderer = input.renderer;
|
|
2268
|
+
const target = RENDERER_TARGET[renderer];
|
|
2269
|
+
if (!target) {
|
|
2270
|
+
throw new Error(
|
|
2271
|
+
`Unknown renderer type: '${input.renderer}'. Valid types: ${VALID_RENDERERS2.join(", ")}`
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
entries = entries.filter(
|
|
2275
|
+
(e) => e.target === target && (e.renderers.length === 0 || e.renderers.includes(renderer))
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
if (input.category) {
|
|
2279
|
+
const cat = input.category;
|
|
2280
|
+
entries = entries.filter((e) => e.category === cat);
|
|
2281
|
+
}
|
|
2282
|
+
entries.sort((a, b) => {
|
|
2283
|
+
const catCmp = a.category.localeCompare(b.category);
|
|
2284
|
+
if (catCmp !== 0) return catCmp;
|
|
2285
|
+
return a.name.localeCompare(b.name);
|
|
2286
|
+
});
|
|
2287
|
+
const components = entries.map((e) => ({
|
|
2288
|
+
name: e.name,
|
|
2289
|
+
version: e.version,
|
|
2290
|
+
category: e.category,
|
|
2291
|
+
target: e.target,
|
|
2292
|
+
exports: [...e.exports],
|
|
2293
|
+
dependencies: [...e.dependencies],
|
|
2294
|
+
description: e.description
|
|
2295
|
+
}));
|
|
2296
|
+
return {
|
|
2297
|
+
count: components.length,
|
|
2298
|
+
components
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
async function addComponent(state, input) {
|
|
2302
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2303
|
+
const def = loaded.definition;
|
|
2304
|
+
const renderer = def.renderer.type;
|
|
2305
|
+
const entry = COMPONENT_REGISTRY[input.component];
|
|
2306
|
+
if (!entry) {
|
|
2307
|
+
throw new Error(`Unknown component: "${input.component}"`);
|
|
2308
|
+
}
|
|
2309
|
+
const target = RENDERER_TARGET[renderer];
|
|
2310
|
+
if (entry.target !== target) {
|
|
2311
|
+
throw new Error(
|
|
2312
|
+
`Component "${input.component}" has target "${entry.target}" but renderer "${renderer}" requires target "${target}"`
|
|
2313
|
+
);
|
|
2314
|
+
}
|
|
2315
|
+
if (entry.renderers.length > 0 && !entry.renderers.includes(renderer)) {
|
|
2316
|
+
throw new Error(
|
|
2317
|
+
`Component "${input.component}" is not compatible with renderer "${renderer}". Compatible: ${entry.renderers.join(", ")}`
|
|
2318
|
+
);
|
|
2319
|
+
}
|
|
2320
|
+
const existingComponents = {};
|
|
2321
|
+
if (def.components) {
|
|
2322
|
+
for (const [name, value] of Object.entries(def.components)) {
|
|
2323
|
+
if (typeof value === "string") {
|
|
2324
|
+
existingComponents[name] = value;
|
|
2325
|
+
} else if (value.version) {
|
|
2326
|
+
existingComponents[name] = value.version;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
if (existingComponents[input.component]) {
|
|
2331
|
+
throw new Error(
|
|
2332
|
+
`Component "${input.component}" is already present in sketch "${input.sketchId}"`
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
existingComponents[input.component] = input.version ?? "^1.0.0";
|
|
2336
|
+
const resolved = resolveComponents2(existingComponents, renderer);
|
|
2337
|
+
const resolvedRecord = {};
|
|
2338
|
+
for (const rc of resolved) {
|
|
2339
|
+
resolvedRecord[rc.name] = {
|
|
2340
|
+
version: rc.version,
|
|
2341
|
+
code: rc.code,
|
|
2342
|
+
exports: [...rc.exports]
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
const previousNames = new Set(
|
|
2346
|
+
def.components ? Object.keys(def.components) : []
|
|
2347
|
+
);
|
|
2348
|
+
const added = resolved.map((rc) => rc.name).filter((name) => !previousNames.has(name));
|
|
2349
|
+
const newDef = {
|
|
2350
|
+
...def,
|
|
2351
|
+
genart: "1.2",
|
|
2352
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2353
|
+
components: resolvedRecord
|
|
2354
|
+
};
|
|
2355
|
+
state.sketches.set(input.sketchId, {
|
|
2356
|
+
definition: newDef,
|
|
2357
|
+
path: loaded.path
|
|
2358
|
+
});
|
|
2359
|
+
const json = serializeGenart4(newDef);
|
|
2360
|
+
if (!state.remoteMode) {
|
|
2361
|
+
await writeFile5(loaded.path, json, "utf-8");
|
|
2362
|
+
}
|
|
2363
|
+
state.emitMutation("sketch:updated", {
|
|
2364
|
+
id: input.sketchId,
|
|
2365
|
+
updated: ["components"]
|
|
2366
|
+
});
|
|
2367
|
+
return {
|
|
2368
|
+
success: true,
|
|
2369
|
+
sketchId: input.sketchId,
|
|
2370
|
+
components: resolvedRecord,
|
|
2371
|
+
added,
|
|
2372
|
+
fileContent: json
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
async function removeComponent(state, input) {
|
|
2376
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2377
|
+
const def = loaded.definition;
|
|
2378
|
+
if (!def.components || !def.components[input.component]) {
|
|
2379
|
+
throw new Error(
|
|
2380
|
+
`Component "${input.component}" is not present in sketch "${input.sketchId}"`
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
const remaining = { ...def.components };
|
|
2384
|
+
delete remaining[input.component];
|
|
2385
|
+
for (const [name, value] of Object.entries(remaining)) {
|
|
2386
|
+
const entry = COMPONENT_REGISTRY[name];
|
|
2387
|
+
if (entry && entry.dependencies.includes(input.component)) {
|
|
2388
|
+
throw new Error(
|
|
2389
|
+
`Cannot remove "${input.component}": component "${name}" depends on it`
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
let warning;
|
|
2394
|
+
const removedValue = def.components[input.component];
|
|
2395
|
+
const exports = typeof removedValue === "string" ? COMPONENT_REGISTRY[input.component]?.exports ?? [] : removedValue.exports ?? [];
|
|
2396
|
+
const usedExports = exports.filter((exp) => def.algorithm.includes(exp));
|
|
2397
|
+
if (usedExports.length > 0) {
|
|
2398
|
+
warning = `Algorithm may reference these exports from "${input.component}": ${usedExports.join(", ")}. Review your algorithm after removal.`;
|
|
2399
|
+
}
|
|
2400
|
+
const removed = [input.component];
|
|
2401
|
+
const neededDeps = /* @__PURE__ */ new Set();
|
|
2402
|
+
for (const name of Object.keys(remaining)) {
|
|
2403
|
+
const entry = COMPONENT_REGISTRY[name];
|
|
2404
|
+
if (entry) {
|
|
2405
|
+
collectTransitiveDeps(name, neededDeps);
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
for (const name of Object.keys(remaining)) {
|
|
2409
|
+
if (!neededDeps.has(name) && !isDirectComponent(name, remaining)) {
|
|
2410
|
+
delete remaining[name];
|
|
2411
|
+
removed.push(name);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
const hasRemaining = Object.keys(remaining).length > 0;
|
|
2415
|
+
const newDef = {
|
|
2416
|
+
...def,
|
|
2417
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2418
|
+
...hasRemaining ? { components: remaining } : { components: void 0 }
|
|
2419
|
+
};
|
|
2420
|
+
state.sketches.set(input.sketchId, {
|
|
2421
|
+
definition: newDef,
|
|
2422
|
+
path: loaded.path
|
|
2423
|
+
});
|
|
2424
|
+
const json = serializeGenart4(newDef);
|
|
2425
|
+
if (!state.remoteMode) {
|
|
2426
|
+
await writeFile5(loaded.path, json, "utf-8");
|
|
2427
|
+
}
|
|
2428
|
+
state.emitMutation("sketch:updated", {
|
|
2429
|
+
id: input.sketchId,
|
|
2430
|
+
updated: ["components"]
|
|
2431
|
+
});
|
|
2432
|
+
return {
|
|
2433
|
+
success: true,
|
|
2434
|
+
sketchId: input.sketchId,
|
|
2435
|
+
removed,
|
|
2436
|
+
...warning ? { warning } : {},
|
|
2437
|
+
fileContent: json
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
function collectTransitiveDeps(name, deps) {
|
|
2441
|
+
const entry = COMPONENT_REGISTRY[name];
|
|
2442
|
+
if (!entry) return;
|
|
2443
|
+
deps.add(name);
|
|
2444
|
+
for (const dep of entry.dependencies) {
|
|
2445
|
+
if (!deps.has(dep)) {
|
|
2446
|
+
collectTransitiveDeps(dep, deps);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
function isDirectComponent(name, components) {
|
|
2451
|
+
return name in components;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// src/tools/capture.ts
|
|
2455
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
2178
2456
|
import {
|
|
2179
2457
|
createDefaultRegistry as createDefaultRegistry2
|
|
2180
2458
|
} from "@genart-dev/core";
|
|
@@ -2352,7 +2630,7 @@ async function buildScreenshotMetadata(state, multi, info) {
|
|
|
2352
2630
|
previewPath: info.previewPath
|
|
2353
2631
|
};
|
|
2354
2632
|
if (!state.remoteMode) {
|
|
2355
|
-
await
|
|
2633
|
+
await writeFile6(info.previewPath, multi.previewPng);
|
|
2356
2634
|
metadata.savedPreviewTo = info.previewPath;
|
|
2357
2635
|
}
|
|
2358
2636
|
return metadata;
|
|
@@ -2413,12 +2691,12 @@ async function captureBatch(state, input) {
|
|
|
2413
2691
|
|
|
2414
2692
|
// src/tools/export.ts
|
|
2415
2693
|
import { createWriteStream } from "fs";
|
|
2416
|
-
import { stat as stat4, writeFile as
|
|
2694
|
+
import { stat as stat4, writeFile as writeFile7 } from "fs/promises";
|
|
2417
2695
|
import { dirname as dirname6 } from "path";
|
|
2418
2696
|
import archiver from "archiver";
|
|
2419
2697
|
import {
|
|
2420
2698
|
createDefaultRegistry as createDefaultRegistry3,
|
|
2421
|
-
serializeGenart as
|
|
2699
|
+
serializeGenart as serializeGenart5
|
|
2422
2700
|
} from "@genart-dev/core";
|
|
2423
2701
|
var registry3 = createDefaultRegistry3();
|
|
2424
2702
|
async function validateOutputPath(outputPath) {
|
|
@@ -2488,7 +2766,7 @@ async function exportHtml(sketch, outputPath) {
|
|
|
2488
2766
|
const adapter = registry3.resolve(sketch.renderer.type);
|
|
2489
2767
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2490
2768
|
const content = Buffer.from(html, "utf-8");
|
|
2491
|
-
await
|
|
2769
|
+
await writeFile7(outputPath, content);
|
|
2492
2770
|
return {
|
|
2493
2771
|
success: true,
|
|
2494
2772
|
sketchId: sketch.id,
|
|
@@ -2504,7 +2782,7 @@ async function exportPng(sketch, input) {
|
|
|
2504
2782
|
const width = input.width ?? sketch.canvas.width;
|
|
2505
2783
|
const height = input.height ?? sketch.canvas.height;
|
|
2506
2784
|
const result = await captureHtml({ html, width, height });
|
|
2507
|
-
await
|
|
2785
|
+
await writeFile7(input.outputPath, result.bytes);
|
|
2508
2786
|
return {
|
|
2509
2787
|
success: true,
|
|
2510
2788
|
sketchId: sketch.id,
|
|
@@ -2519,7 +2797,7 @@ async function exportSvg(sketch, input) {
|
|
|
2519
2797
|
const height = input.height ?? sketch.canvas.height;
|
|
2520
2798
|
if (sketch.renderer.type === "svg") {
|
|
2521
2799
|
const content2 = Buffer.from(sketch.algorithm, "utf-8");
|
|
2522
|
-
await
|
|
2800
|
+
await writeFile7(input.outputPath, content2);
|
|
2523
2801
|
return {
|
|
2524
2802
|
success: true,
|
|
2525
2803
|
sketchId: sketch.id,
|
|
@@ -2541,7 +2819,7 @@ async function exportSvg(sketch, input) {
|
|
|
2541
2819
|
href="data:image/png;base64,${b64}"/>
|
|
2542
2820
|
</svg>`;
|
|
2543
2821
|
const content = Buffer.from(svg, "utf-8");
|
|
2544
|
-
await
|
|
2822
|
+
await writeFile7(input.outputPath, content);
|
|
2545
2823
|
return {
|
|
2546
2824
|
success: true,
|
|
2547
2825
|
sketchId: sketch.id,
|
|
@@ -2554,7 +2832,7 @@ async function exportSvg(sketch, input) {
|
|
|
2554
2832
|
}
|
|
2555
2833
|
async function exportAlgorithm(sketch, outputPath) {
|
|
2556
2834
|
const content = Buffer.from(sketch.algorithm, "utf-8");
|
|
2557
|
-
await
|
|
2835
|
+
await writeFile7(outputPath, content);
|
|
2558
2836
|
return {
|
|
2559
2837
|
success: true,
|
|
2560
2838
|
sketchId: sketch.id,
|
|
@@ -2569,7 +2847,7 @@ async function exportZip(sketch, input) {
|
|
|
2569
2847
|
const width = input.width ?? sketch.canvas.width;
|
|
2570
2848
|
const height = input.height ?? sketch.canvas.height;
|
|
2571
2849
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2572
|
-
const genartJson =
|
|
2850
|
+
const genartJson = serializeGenart5(sketch);
|
|
2573
2851
|
const algorithm = sketch.algorithm;
|
|
2574
2852
|
const algExt = algorithmExtension(sketch.renderer.type);
|
|
2575
2853
|
const captureResult = await captureHtml({ html, width, height });
|
|
@@ -3106,6 +3384,7 @@ function createServer(state) {
|
|
|
3106
3384
|
);
|
|
3107
3385
|
registerWorkspaceTools(server, state);
|
|
3108
3386
|
registerSketchTools(server, state);
|
|
3387
|
+
registerComponentTools(server, state);
|
|
3109
3388
|
registerSelectionTools(server, state);
|
|
3110
3389
|
registerParameterTools(server, state);
|
|
3111
3390
|
registerArrangementTools(server, state);
|
|
@@ -3209,7 +3488,7 @@ function registerWorkspaceTools(server, state) {
|
|
|
3209
3488
|
function registerSketchTools(server, state) {
|
|
3210
3489
|
server.tool(
|
|
3211
3490
|
"create_sketch",
|
|
3212
|
-
|
|
3491
|
+
'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.',
|
|
3213
3492
|
{
|
|
3214
3493
|
id: z2.string().describe("URL-safe kebab-case identifier"),
|
|
3215
3494
|
title: z2.string().describe("Human-readable title"),
|
|
@@ -3247,6 +3526,16 @@ function registerSketchTools(server, state) {
|
|
|
3247
3526
|
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)."),
|
|
3248
3527
|
seed: z2.number().optional().describe("Initial random seed (default: random)"),
|
|
3249
3528
|
skills: z2.array(z2.string()).optional().describe("Design skill references"),
|
|
3529
|
+
components: z2.record(
|
|
3530
|
+
z2.union([
|
|
3531
|
+
z2.string(),
|
|
3532
|
+
z2.object({
|
|
3533
|
+
version: z2.string().optional(),
|
|
3534
|
+
code: z2.string().optional(),
|
|
3535
|
+
exports: z2.array(z2.string()).optional()
|
|
3536
|
+
})
|
|
3537
|
+
])
|
|
3538
|
+
).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.'),
|
|
3250
3539
|
addToWorkspace: z2.string().optional().describe("Path to workspace to add sketch to after creation"),
|
|
3251
3540
|
agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3252
3541
|
model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
@@ -3326,11 +3615,21 @@ function registerSketchTools(server, state) {
|
|
|
3326
3615
|
);
|
|
3327
3616
|
server.tool(
|
|
3328
3617
|
"update_algorithm",
|
|
3329
|
-
"Replace the algorithm source code of a sketch",
|
|
3618
|
+
"Replace the algorithm source code of a sketch. If adding/changing components, pass them in the components field alongside the algorithm.",
|
|
3330
3619
|
{
|
|
3331
3620
|
sketchId: z2.string().describe("ID of the sketch to update"),
|
|
3332
3621
|
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)."),
|
|
3333
3622
|
validate: z2.boolean().optional().describe("Run renderer-specific validation before saving (default: true)"),
|
|
3623
|
+
components: z2.record(
|
|
3624
|
+
z2.union([
|
|
3625
|
+
z2.string(),
|
|
3626
|
+
z2.object({
|
|
3627
|
+
version: z2.string().optional(),
|
|
3628
|
+
code: z2.string().optional(),
|
|
3629
|
+
exports: z2.array(z2.string()).optional()
|
|
3630
|
+
})
|
|
3631
|
+
])
|
|
3632
|
+
).optional().describe("Component dependencies to resolve alongside the algorithm update. Use list_components to see available."),
|
|
3334
3633
|
agent: z2.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3335
3634
|
model: z2.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
3336
3635
|
},
|
|
@@ -3426,6 +3725,76 @@ function registerSketchTools(server, state) {
|
|
|
3426
3725
|
}
|
|
3427
3726
|
);
|
|
3428
3727
|
}
|
|
3728
|
+
function registerComponentTools(server, state) {
|
|
3729
|
+
server.tool(
|
|
3730
|
+
"list_components",
|
|
3731
|
+
"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.",
|
|
3732
|
+
{
|
|
3733
|
+
renderer: z2.enum(["p5", "three", "glsl", "canvas2d", "svg"]).optional().describe("Filter by renderer compatibility"),
|
|
3734
|
+
category: z2.enum([
|
|
3735
|
+
"randomness",
|
|
3736
|
+
"noise",
|
|
3737
|
+
"math",
|
|
3738
|
+
"easing",
|
|
3739
|
+
"color",
|
|
3740
|
+
"vector",
|
|
3741
|
+
"geometry",
|
|
3742
|
+
"grid",
|
|
3743
|
+
"particle",
|
|
3744
|
+
"physics",
|
|
3745
|
+
"distribution",
|
|
3746
|
+
"pattern",
|
|
3747
|
+
"sdf",
|
|
3748
|
+
"transform",
|
|
3749
|
+
"animation",
|
|
3750
|
+
"string",
|
|
3751
|
+
"data-structure",
|
|
3752
|
+
"imaging"
|
|
3753
|
+
]).optional().describe("Filter by component category")
|
|
3754
|
+
},
|
|
3755
|
+
async (args) => {
|
|
3756
|
+
try {
|
|
3757
|
+
const result = await listComponents(state, args);
|
|
3758
|
+
return jsonResult(result);
|
|
3759
|
+
} catch (e) {
|
|
3760
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
);
|
|
3764
|
+
server.tool(
|
|
3765
|
+
"add_component",
|
|
3766
|
+
"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.",
|
|
3767
|
+
{
|
|
3768
|
+
sketchId: z2.string().describe("ID of the sketch to add the component to"),
|
|
3769
|
+
component: z2.string().describe("Component name (e.g. 'prng', 'noise-2d', 'glsl-noise')"),
|
|
3770
|
+
version: z2.string().optional().describe("Version range (default: '^1.0.0')")
|
|
3771
|
+
},
|
|
3772
|
+
async (args) => {
|
|
3773
|
+
try {
|
|
3774
|
+
const result = await addComponent(state, args);
|
|
3775
|
+
return jsonResult(result);
|
|
3776
|
+
} catch (e) {
|
|
3777
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
);
|
|
3781
|
+
server.tool(
|
|
3782
|
+
"remove_component",
|
|
3783
|
+
"Remove a component dependency from a sketch. Checks for dependent components and warns if the algorithm references the component's exports.",
|
|
3784
|
+
{
|
|
3785
|
+
sketchId: z2.string().describe("ID of the sketch to remove the component from"),
|
|
3786
|
+
component: z2.string().describe("Component name to remove")
|
|
3787
|
+
},
|
|
3788
|
+
async (args) => {
|
|
3789
|
+
try {
|
|
3790
|
+
const result = await removeComponent(state, args);
|
|
3791
|
+
return jsonResult(result);
|
|
3792
|
+
} catch (e) {
|
|
3793
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
);
|
|
3797
|
+
}
|
|
3429
3798
|
function registerSelectionTools(server, state) {
|
|
3430
3799
|
server.tool(
|
|
3431
3800
|
"get_selection",
|