vectorvesper 2.0.7 → 2.1.1
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.js
CHANGED
|
@@ -993,7 +993,7 @@ Remove ${slug}
|
|
|
993
993
|
import fs6 from "fs";
|
|
994
994
|
import path6 from "path";
|
|
995
995
|
import pc8 from "picocolors";
|
|
996
|
-
var CLI_VERSION = true ? "2.
|
|
996
|
+
var CLI_VERSION = true ? "2.1.1" : "0.0.0-dev";
|
|
997
997
|
async function infoCommand() {
|
|
998
998
|
console.log(pc8.bold(pc8.cyan("\nVector Vesper Diagnostics\n")));
|
|
999
999
|
const projectInfo = detectProject();
|
|
@@ -1291,7 +1291,7 @@ async function whoamiCommand() {
|
|
|
1291
1291
|
}
|
|
1292
1292
|
|
|
1293
1293
|
// src/index.ts
|
|
1294
|
-
var version = true ? "2.
|
|
1294
|
+
var version = true ? "2.1.1" : "0.0.0-dev";
|
|
1295
1295
|
var program = new Command();
|
|
1296
1296
|
program.name("vv").description("Vector Vesper CLI \u2014 add visual components to your React project").version(version).option("--verbose", "Show detailed debug output").hook("preAction", (thisCommand) => {
|
|
1297
1297
|
const opts = thisCommand.opts();
|
|
@@ -1330,7 +1330,7 @@ program.command("whoami", { hidden: true }).description("Show current authentica
|
|
|
1330
1330
|
await whoamiCommand();
|
|
1331
1331
|
});
|
|
1332
1332
|
program.command("mcp [action]").description("Run as an MCP server (stdio) for AI coding agents. `vv mcp status` checks the setup.").option("--stdio", "Force server mode even from an interactive terminal").action(async (action, options) => {
|
|
1333
|
-
const { mcpCommand } = await import("./mcp-
|
|
1333
|
+
const { mcpCommand } = await import("./mcp-FCM3FZSK.js");
|
|
1334
1334
|
await mcpCommand(action, options);
|
|
1335
1335
|
});
|
|
1336
1336
|
process.on("unhandledRejection", (error) => {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadHookManifest
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CPZKZO3A.js";
|
|
4
4
|
import {
|
|
5
5
|
getAuthToken
|
|
6
6
|
} from "./chunk-SFP5K3ZO.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/mcp.ts
|
|
9
9
|
import pc from "picocolors";
|
|
10
|
-
var VERSION = true ? "2.
|
|
10
|
+
var VERSION = true ? "2.1.1" : "0.0.0-dev";
|
|
11
11
|
var CONFIG_SNIPPET = `{
|
|
12
12
|
"mcpServers": {
|
|
13
13
|
"vectorvesper": {
|
|
@@ -33,7 +33,14 @@ function printStatus() {
|
|
|
33
33
|
console.log(pc.bold(pc.cyan(" Vector Vesper MCP\n")));
|
|
34
34
|
console.log(` ${pc.bold("CLI version:")} ${VERSION}`);
|
|
35
35
|
if (manifest) {
|
|
36
|
-
|
|
36
|
+
const hookCount = manifest.hooks.filter((h) => h.category !== "core").length;
|
|
37
|
+
const coreCount = manifest.hooks.length - hookCount;
|
|
38
|
+
const patternCount = manifest.patterns?.length ?? 0;
|
|
39
|
+
console.log(
|
|
40
|
+
` ${ok(
|
|
41
|
+
`Hook manifest: ${hookCount} hooks, ${coreCount} core primitives, ${patternCount} patterns, engine v${manifest.engineVersion}`
|
|
42
|
+
)}`
|
|
43
|
+
);
|
|
37
44
|
console.log(` ${pc.dim(`Contract level: ${manifest.contractLevel}`)}`);
|
|
38
45
|
} else {
|
|
39
46
|
console.log(` ${bad("Hook manifest: MISSING")}`);
|
|
@@ -53,7 +60,7 @@ function printStatus() {
|
|
|
53
60
|
console.log("");
|
|
54
61
|
console.log(
|
|
55
62
|
pc.dim(
|
|
56
|
-
" Tools: list_hooks \xB7 get_hook \xB7 list_components \xB7 get_component \xB7 get_setup_guidance \xB7 search \xB7 check_motion"
|
|
63
|
+
" Tools: list_hooks \xB7 get_hook \xB7 list_patterns \xB7 get_pattern \xB7 list_components \xB7 get_component \xB7 get_setup_guidance \xB7 search \xB7 check_motion"
|
|
57
64
|
)
|
|
58
65
|
);
|
|
59
66
|
console.log("");
|
|
@@ -67,7 +74,7 @@ function printInteractiveHelp() {
|
|
|
67
74
|
console.log(" started from a terminal it would just wait for a client that never connects.");
|
|
68
75
|
console.log("");
|
|
69
76
|
console.log(` ${pc.bold("Check your setup:")} ${pc.cyan("vv mcp status")}`);
|
|
70
|
-
console.log(` ${pc.bold("Add to Claude Code:")} ${pc.cyan("claude mcp add vectorvesper -- npx -y vectorvesper mcp")}`);
|
|
77
|
+
console.log(` ${pc.bold("Add to Claude Code:")} ${pc.cyan("claude mcp add vectorvesper -- npx -y vectorvesper@latest mcp")}`);
|
|
71
78
|
console.log("");
|
|
72
79
|
console.log(pc.dim(" To run the server here anyway (for debugging): vv mcp --stdio"));
|
|
73
80
|
console.log("");
|
|
@@ -94,7 +101,7 @@ ${pc.red("\u2717")} Unknown argument "${action}" for \`vv mcp\`.
|
|
|
94
101
|
return;
|
|
95
102
|
}
|
|
96
103
|
protectStdout();
|
|
97
|
-
const { startMcpServer } = await import("./server-
|
|
104
|
+
const { startMcpServer } = await import("./server-WQ564YNA.js");
|
|
98
105
|
try {
|
|
99
106
|
await startMcpServer();
|
|
100
107
|
} catch (error) {
|
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-5YSVDVLK.js";
|
|
5
5
|
import {
|
|
6
6
|
findHook,
|
|
7
|
+
findPattern,
|
|
7
8
|
loadHookManifest
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-CPZKZO3A.js";
|
|
9
10
|
import {
|
|
10
11
|
getAuthToken
|
|
11
12
|
} from "./chunk-SFP5K3ZO.js";
|
|
@@ -50,6 +51,8 @@ function collect(ast, traverse) {
|
|
|
50
51
|
usesUseGSAP: false,
|
|
51
52
|
usesR3F: false,
|
|
52
53
|
raf: [],
|
|
54
|
+
rafSelfScheduling: [],
|
|
55
|
+
rafUnresolved: 0,
|
|
53
56
|
hasCancelRaf: false,
|
|
54
57
|
addEventListener: [],
|
|
55
58
|
hasRemoveEventListener: false,
|
|
@@ -112,10 +115,14 @@ function collect(ast, traverse) {
|
|
|
112
115
|
const line = node.loc?.start.line ?? 0;
|
|
113
116
|
const argPaths = typeof p.get === "function" ? p.get("arguments") : [];
|
|
114
117
|
switch (name) {
|
|
115
|
-
case "requestAnimationFrame":
|
|
118
|
+
case "requestAnimationFrame": {
|
|
116
119
|
f.raf.push(line);
|
|
117
|
-
|
|
120
|
+
const cb = resolveFunction(argPaths[0]);
|
|
121
|
+
if (!cb) f.rafUnresolved++;
|
|
122
|
+
else if (callbackSchedulesAnotherFrame(cb, traverse)) f.rafSelfScheduling.push(line);
|
|
123
|
+
scanHotCallback(cb, "a requestAnimationFrame loop", true);
|
|
118
124
|
break;
|
|
125
|
+
}
|
|
119
126
|
case "cancelAnimationFrame":
|
|
120
127
|
f.hasCancelRaf = true;
|
|
121
128
|
break;
|
|
@@ -210,6 +217,9 @@ function collect(ast, traverse) {
|
|
|
210
217
|
},
|
|
211
218
|
Identifier(p) {
|
|
212
219
|
const name = p.node.name;
|
|
220
|
+
if (/^(reducedMotion|prefersReducedMotion|prefersReduced)$/.test(name)) {
|
|
221
|
+
f.hasReducedMotionGuard = true;
|
|
222
|
+
}
|
|
213
223
|
if (!BROWSER_GLOBALS.has(name)) return;
|
|
214
224
|
if (fnDepth !== 0) return;
|
|
215
225
|
const parent = p.parent;
|
|
@@ -264,8 +274,27 @@ function checkClientBoundary(f, file, framework) {
|
|
|
264
274
|
}
|
|
265
275
|
return out;
|
|
266
276
|
}
|
|
277
|
+
function callbackSchedulesAnotherFrame(cbNode, traverse) {
|
|
278
|
+
const isDeclaration = cbNode.type === "FunctionDeclaration";
|
|
279
|
+
const isExpression = cbNode.type === "ArrowFunctionExpression" || cbNode.type === "FunctionExpression";
|
|
280
|
+
if (!isDeclaration && !isExpression) return false;
|
|
281
|
+
const statement = isDeclaration ? cbNode : { type: "ExpressionStatement", expression: cbNode };
|
|
282
|
+
let found = false;
|
|
283
|
+
traverse(
|
|
284
|
+
{ type: "File", program: { type: "Program", body: [statement], directives: [] } },
|
|
285
|
+
{
|
|
286
|
+
CallExpression(p) {
|
|
287
|
+
if (calleeName(p.node.callee) === "requestAnimationFrame") found = true;
|
|
288
|
+
},
|
|
289
|
+
OptionalCallExpression(p) {
|
|
290
|
+
if (calleeName(p.node.callee) === "requestAnimationFrame") found = true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
return found;
|
|
295
|
+
}
|
|
267
296
|
function isRafLoop(f) {
|
|
268
|
-
return f.
|
|
297
|
+
return f.rafSelfScheduling.length > 0 || f.rafUnresolved >= 2;
|
|
269
298
|
}
|
|
270
299
|
function checkOrphanRaf(f, file) {
|
|
271
300
|
if (!isRafLoop(f)) return [];
|
|
@@ -274,8 +303,10 @@ function checkOrphanRaf(f, file) {
|
|
|
274
303
|
rule: "orphan-raf",
|
|
275
304
|
severity: "warning",
|
|
276
305
|
file,
|
|
277
|
-
|
|
278
|
-
|
|
306
|
+
// Point at the self-scheduling call when we found one — that is the loop
|
|
307
|
+
// itself, not merely the first rAF in the file.
|
|
308
|
+
line: f.rafSelfScheduling[0] ?? f.raf[0],
|
|
309
|
+
message: `This file runs its own \`requestAnimationFrame\` loop${f.rafSelfScheduling.length > 1 ? ` (\xD7${f.rafSelfScheduling.length})` : ""}. Independent loops each schedule their own frame and cannot shed work under load \u2014 the coordination the runtime exists to provide is lost.`,
|
|
279
310
|
fix: "Drive the animation from the shared conductor (`getConductor().subscribe(...)` in `@vectorvesper/motion`) or a hook that already joins it, so it shares one frame budget.",
|
|
280
311
|
nudge: "Call `list_hooks` \u2014 a sensor or governor primitive may already cover this without a hand-written loop."
|
|
281
312
|
}
|
|
@@ -399,7 +430,7 @@ async function checkMotionFiles(files, cwd) {
|
|
|
399
430
|
}
|
|
400
431
|
|
|
401
432
|
// src/mcp/server.ts
|
|
402
|
-
var VERSION = true ? "2.
|
|
433
|
+
var VERSION = true ? "2.1.1" : "0.0.0-dev";
|
|
403
434
|
function text(body) {
|
|
404
435
|
return { content: [{ type: "text", text: body }] };
|
|
405
436
|
}
|
|
@@ -584,6 +615,41 @@ ${c.docsUrl}`);
|
|
|
584
615
|
);
|
|
585
616
|
return out.join("\n");
|
|
586
617
|
}
|
|
618
|
+
function formatPatternSummary(p) {
|
|
619
|
+
return `**${p.name}** \u2014 ${p.tagline}
|
|
620
|
+
Composes: ${p.uses.map((u) => `\`${u}\``).join(" + ")}`;
|
|
621
|
+
}
|
|
622
|
+
function formatPatternDetail(p) {
|
|
623
|
+
const out = [];
|
|
624
|
+
out.push(`# ${p.title} \`${p.name}\``);
|
|
625
|
+
out.push(`> ${p.tagline}`);
|
|
626
|
+
out.push("");
|
|
627
|
+
out.push(`**The problem it removes:** ${p.problem}`);
|
|
628
|
+
out.push("");
|
|
629
|
+
out.push(
|
|
630
|
+
`**Composes:** ${p.uses.map((u) => `\`${u}\``).join(" + ")} \u2014 call \`get_hook\` on any of these you have not already read; the pattern assumes their contracts.`
|
|
631
|
+
);
|
|
632
|
+
out.push("\n## Wiring order");
|
|
633
|
+
p.wiring.forEach((step, i) => out.push(`${i + 1}. ${step}`));
|
|
634
|
+
out.push("\n## Complete example");
|
|
635
|
+
out.push("```tsx");
|
|
636
|
+
out.push(p.code);
|
|
637
|
+
out.push("```");
|
|
638
|
+
out.push("\n## Pitfalls");
|
|
639
|
+
for (const pit of p.pitfalls) {
|
|
640
|
+
const caught = pit.rule ? ` *(caught by \`check_motion\`: ${pit.rule})*` : "";
|
|
641
|
+
out.push(`- **${pit.mistake}** \u2014 ${pit.consequence}${caught}`);
|
|
642
|
+
}
|
|
643
|
+
out.push("\n## Verify");
|
|
644
|
+
p.verify.forEach((step, i) => out.push(`${i + 1}. ${step}`));
|
|
645
|
+
if (p.docsUrl) out.push(`
|
|
646
|
+
## Docs
|
|
647
|
+
${p.docsUrl}`);
|
|
648
|
+
out.push(
|
|
649
|
+
"\n---\n**After writing your version, run `check_motion` on the file(s).** The pitfalls above are the findings it looks for; a clean report is how you confirm the composition held."
|
|
650
|
+
);
|
|
651
|
+
return out.join("\n");
|
|
652
|
+
}
|
|
587
653
|
function severityMark(s) {
|
|
588
654
|
return s === "error" ? "\u2717 error" : s === "warning" ? "\u25B2 warning" : "\xB7 note";
|
|
589
655
|
}
|
|
@@ -629,10 +695,10 @@ async function registerTools(server) {
|
|
|
629
695
|
server.registerTool(
|
|
630
696
|
"list_hooks",
|
|
631
697
|
{
|
|
632
|
-
title: "List motion hooks",
|
|
633
|
-
description: "List every React hook in the @vectorvesper/motion runtime, with the problem each one solves. Start here when deciding which primitive fits a motion task.",
|
|
698
|
+
title: "List motion hooks and core primitives",
|
|
699
|
+
description: "List every React hook in the @vectorvesper/motion runtime, with the problem each one solves, plus the core primitives (frame conductor, sensor bus, math, devtools) that custom effects are built on. Start here when deciding which primitive fits a motion task.",
|
|
634
700
|
inputSchema: {
|
|
635
|
-
category: z.enum(["sensor", "governor", "gate", "interaction", "media", "effect"]).optional().describe(
|
|
701
|
+
category: z.enum(["sensor", "governor", "gate", "interaction", "media", "effect", "core"]).optional().describe('Filter to one category. "core" lists the custom-effect primitives.')
|
|
636
702
|
}
|
|
637
703
|
},
|
|
638
704
|
async ({ category }) => {
|
|
@@ -642,16 +708,32 @@ async function registerTools(server) {
|
|
|
642
708
|
"Hook manifest unavailable \u2014 this CLI build is missing dist/hooks.json. Reinstall with `npm i -g vectorvesper@latest`."
|
|
643
709
|
);
|
|
644
710
|
}
|
|
645
|
-
const
|
|
646
|
-
if (!
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
711
|
+
const all = category ? manifest.hooks.filter((h) => h.category === category) : manifest.hooks;
|
|
712
|
+
if (!all.length) return text(`No hooks in category "${category}".`);
|
|
713
|
+
const reactHooks = all.filter((h) => h.category !== "core");
|
|
714
|
+
const core = all.filter((h) => h.category === "core");
|
|
715
|
+
const patternCount = manifest.patterns?.length ?? 0;
|
|
716
|
+
const lines = [];
|
|
717
|
+
if (reactHooks.length) {
|
|
718
|
+
lines.push(
|
|
719
|
+
`${reactHooks.length} hook${reactHooks.length === 1 ? "" : "s"} in @vectorvesper/motion v${manifest.engineVersion}`,
|
|
720
|
+
"",
|
|
721
|
+
...reactHooks.map(formatHookSummary)
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
if (core.length) {
|
|
725
|
+
if (lines.length) lines.push("");
|
|
726
|
+
lines.push(
|
|
727
|
+
`**Core primitives** \u2014 the layer custom effects are built on. Never start a private requestAnimationFrame loop; subscribe to the conductor instead.`,
|
|
728
|
+
"",
|
|
729
|
+
...core.map(formatHookSummary)
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
lines.push(
|
|
651
733
|
"",
|
|
652
|
-
"Call `get_hook` for the full contract of any of these before writing code with it."
|
|
653
|
-
|
|
654
|
-
return text(
|
|
734
|
+
"Call `get_hook` for the full contract of any of these before writing code with it." + (patternCount > 0 ? ` For compositions \u2014 scrollytelling, predictive cards, custom effects \u2014 call \`list_patterns\` (${patternCount} available).` : "")
|
|
735
|
+
);
|
|
736
|
+
return text(lines.join("\n"));
|
|
655
737
|
}
|
|
656
738
|
);
|
|
657
739
|
server.registerTool(
|
|
@@ -673,6 +755,53 @@ async function registerTools(server) {
|
|
|
673
755
|
return text(formatHookDetail(hook));
|
|
674
756
|
}
|
|
675
757
|
);
|
|
758
|
+
server.registerTool(
|
|
759
|
+
"list_patterns",
|
|
760
|
+
{
|
|
761
|
+
title: "List composition patterns",
|
|
762
|
+
description: "List the composition patterns \u2014 hooks and core primitives wired together into things people build: scrollytelling video, predictive media cards, adaptive shader heroes, custom frame effects. Use these when a task combines primitives, or when no single hook covers it; a pattern carries the wiring order, complete code, the pitfalls (each mapped to the check_motion rule that catches it), and how to verify the result.",
|
|
763
|
+
inputSchema: {}
|
|
764
|
+
},
|
|
765
|
+
async () => {
|
|
766
|
+
const manifest = loadHookManifest();
|
|
767
|
+
if (!manifest) {
|
|
768
|
+
return text(
|
|
769
|
+
"Hook manifest unavailable \u2014 this CLI build is missing dist/hooks.json. Reinstall with `npm i -g vectorvesper@latest`."
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
const patterns = manifest.patterns ?? [];
|
|
773
|
+
if (!patterns.length) {
|
|
774
|
+
return text("This manifest carries no patterns. Reinstall with `npm i -g vectorvesper@latest`.");
|
|
775
|
+
}
|
|
776
|
+
const body = [
|
|
777
|
+
`${patterns.length} composition pattern${patterns.length === 1 ? "" : "s"} (engine v${manifest.engineVersion})`,
|
|
778
|
+
"",
|
|
779
|
+
...patterns.map(formatPatternSummary),
|
|
780
|
+
"",
|
|
781
|
+
"Call `get_pattern` for the wiring order, complete code, pitfalls, and verification steps. Building something none of these cover? Start from `custom-frame-effect` \u2014 it is the canonical skeleton every custom effect follows."
|
|
782
|
+
].join("\n");
|
|
783
|
+
return text(body);
|
|
784
|
+
}
|
|
785
|
+
);
|
|
786
|
+
server.registerTool(
|
|
787
|
+
"get_pattern",
|
|
788
|
+
{
|
|
789
|
+
title: "Get a composition pattern",
|
|
790
|
+
description: "Full recipe for one composition: the primitives it combines, the wiring order, one complete runnable example, the pitfalls (each naming the check_motion rule that catches it), and the verification steps. Read this BEFORE writing a multi-primitive feature \u2014 and read `custom-frame-effect` before building any effect the hooks do not cover.",
|
|
791
|
+
inputSchema: {
|
|
792
|
+
name: z.string().describe('Pattern name, e.g. "scrollytelling-video" or "custom-frame-effect".')
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
async ({ name }) => {
|
|
796
|
+
const pattern = findPattern(name);
|
|
797
|
+
if (!pattern) {
|
|
798
|
+
const manifest = loadHookManifest();
|
|
799
|
+
const available = manifest?.patterns?.map((p) => p.name).join(", ") ?? "none loaded";
|
|
800
|
+
return text(`No pattern named "${name}". Available: ${available}`);
|
|
801
|
+
}
|
|
802
|
+
return text(formatPatternDetail(pattern));
|
|
803
|
+
}
|
|
804
|
+
);
|
|
676
805
|
server.registerTool(
|
|
677
806
|
"list_components",
|
|
678
807
|
{
|
|
@@ -779,8 +908,8 @@ Hook discovery still works offline \u2014 try \`list_hooks\`.`
|
|
|
779
908
|
server.registerTool(
|
|
780
909
|
"search",
|
|
781
910
|
{
|
|
782
|
-
title: "Search
|
|
783
|
-
description: 'Keyword search across
|
|
911
|
+
title: "Search primitives, patterns and components",
|
|
912
|
+
description: 'Keyword search across everything: React hooks, the core primitives, multi-hook composition patterns, and installable components. Use this when you know the effect you want ("magnetic button", "scroll video", "lazy 3d scene") but not which primitive provides it. A **pattern** result is usually the one to follow for anything combining two or more primitives \u2014 it carries the wiring order and the failure modes that using each hook correctly in isolation still gets wrong.',
|
|
784
913
|
inputSchema: { query: z.string().describe("What you are trying to build.") }
|
|
785
914
|
},
|
|
786
915
|
async ({ query }) => {
|
|
@@ -794,8 +923,17 @@ Hook discovery still works offline \u2014 try \`list_hooks\`.`
|
|
|
794
923
|
const results = [];
|
|
795
924
|
const manifest = loadHookManifest();
|
|
796
925
|
for (const h of manifest?.hooks ?? []) {
|
|
797
|
-
const
|
|
798
|
-
|
|
926
|
+
const kind = h.category === "core" ? "core" : "hook";
|
|
927
|
+
const s = score(
|
|
928
|
+
[h.name, h.tagline, h.problem, h.summary, h.category, ...h.aliases ?? [], ...h.exports ?? []].join(" ")
|
|
929
|
+
);
|
|
930
|
+
if (s > 0) results.push({ score: s, line: `**${kind}** \`${h.name}\` \u2014 ${h.tagline}` });
|
|
931
|
+
}
|
|
932
|
+
for (const p of manifest?.patterns ?? []) {
|
|
933
|
+
const s = score(
|
|
934
|
+
[p.name, p.title, p.tagline, p.problem, ...p.aliases ?? [], ...p.uses].join(" ")
|
|
935
|
+
);
|
|
936
|
+
if (s > 0) results.push({ score: s, line: `**pattern** \`${p.name}\` \u2014 ${p.tagline}` });
|
|
799
937
|
}
|
|
800
938
|
try {
|
|
801
939
|
const index = await fetchRegistryIndex();
|
|
@@ -819,7 +957,7 @@ Hook discovery still works offline \u2014 try \`list_hooks\`.`
|
|
|
819
957
|
"",
|
|
820
958
|
...results.slice(0, 12).map((r) => r.line),
|
|
821
959
|
"",
|
|
822
|
-
"Then call `get_hook` or `get_component` for the full contract before writing code."
|
|
960
|
+
"Then call `get_hook`, `get_pattern`, or `get_component` for the full contract before writing code."
|
|
823
961
|
].join("\n")
|
|
824
962
|
);
|
|
825
963
|
}
|
|
@@ -848,7 +986,7 @@ async function startMcpServer() {
|
|
|
848
986
|
const server = new McpServerCtor(
|
|
849
987
|
{ name: "vectorvesper", version: VERSION },
|
|
850
988
|
{
|
|
851
|
-
instructions:
|
|
989
|
+
instructions: 'Vector Vesper\'s motion runtime and component registry.\n\nBefore writing any animation, scroll, pointer or WebGL code in this project, call `search` or `list_hooks` to check whether a Vector Vesper primitive already covers it, then `get_hook` / `get_component` for the contract. The contract states whether something needs a client boundary, whether it owns an element\'s transform, and what it conflicts with \u2014 details that do not surface as compile errors but do surface as jank.\n\nWhen the task COMBINES primitives (scrollytelling, a predictive media card, an adaptive hero, a marquee) call `list_patterns` / `get_pattern` first: a pattern carries the wiring order, one complete example, the pitfalls with the `check_motion` rule that catches each, and how to verify. When no hook or pattern covers the task, build a custom effect on the runtime: `get_pattern("custom-frame-effect")` is the canonical skeleton, and `get_hook("FrameConductor")`, `get_hook("SensorBus")` and `get_hook("damp")` are the core primitives it rides on. Never start a private requestAnimationFrame loop \u2014 subscribing to the shared conductor is always the answer.\n\nAfter writing animation, scroll, pointer or WebGL code, call `check_motion` on the file(s) you changed. It reports the same failure class the contracts warn about \u2014 an unmanaged frame loop, missing cleanup, state written every frame, a transform animated by two owners, motion with no reduced-motion guard, a browser API at module scope \u2014 checked against what you actually wrote. This is how you confirm the contract was followed; none of it shows up as a type or build error.\n\nHooks are consumed by importing `@vectorvesper/motion`. Never reimplement or inline a hook: a copy runs its own requestAnimationFrame loop instead of joining the shared frame conductor, which removes the coordination the runtime exists to provide, and nothing errors when that happens.\n\nIf asked how a hook works internally, call `get_hook` \u2014 its `How it works inside` section explains the architecture, the frame lane, and the smoothing constants directly. Prefer that over reading `node_modules/@vectorvesper/motion`: it is the same design stated at the level the question is actually about, and it stays correct as the package is bundled and rebundled.'
|
|
852
990
|
}
|
|
853
991
|
);
|
|
854
992
|
await registerTools(server);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vectorvesper",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Add WebGL, React Three Fiber & advanced motion components to your project via CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"typecheck": "tsc --noEmit",
|
|
45
45
|
"test": "vitest run",
|
|
46
46
|
"test:watch": "vitest",
|
|
47
|
+
"test:mcp": "node scripts/mcp-e2e/harness.mjs --local",
|
|
48
|
+
"test:mcp:published": "node scripts/mcp-e2e/harness.mjs",
|
|
47
49
|
"postbuild": "node scripts/bundle-manifest.mjs",
|
|
48
50
|
"prepublishOnly": "npm run build"
|
|
49
51
|
},
|