vectorvesper 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "vectorvesper",
3
- "version": "2.0.3",
4
- "description": "Add WebGL, React Three Fiber & advanced motion components to your project via CLI.",
5
- "type": "module",
6
- "license": "MIT",
7
- "main": "./dist/index.js",
8
- "bin": {
9
- "vectorvesper": "dist/index.js",
10
- "vv": "dist/index.js"
11
- },
12
- "files": [
13
- "dist",
14
- "package.json"
15
- ],
16
- "engines": {
17
- "node": ">=18"
18
- },
19
- "repository": {
20
- "type": "git",
21
- "url": "git+https://github.com/vectorvesper/vv-components.git"
22
- },
23
- "bugs": {
24
- "url": "https://github.com/vectorvesper/vv-components/issues"
25
- },
26
- "homepage": "https://vectorvesper.dev",
27
- "keywords": [
28
- "react",
29
- "webgl",
30
- "three",
31
- "threejs",
32
- "components",
33
- "cli",
34
- "ui",
35
- "visual",
36
- "animation",
37
- "gsap",
38
- "shader",
39
- "interactive"
40
- ],
41
- "scripts": {
42
- "build": "tsup",
43
- "dev": "tsup --watch",
44
- "typecheck": "tsc --noEmit",
45
- "test": "vitest run",
46
- "test:watch": "vitest",
47
- "postbuild": "node scripts/bundle-manifest.mjs",
48
- "prepublishOnly": "npm run build"
49
- },
50
- "dependencies": {
51
- "@babel/core": "^7.24.0",
52
- "@babel/preset-typescript": "^7.24.0",
53
- "@clack/prompts": "^0.7.0",
54
- "@modelcontextprotocol/sdk": "^1.29.0",
55
- "commander": "^12.0.0",
56
- "ora": "^8.0.1",
57
- "picocolors": "^1.0.0",
58
- "zod": "^3.22.4"
59
- },
60
- "devDependencies": {
61
- "@types/babel__core": "^7.20.5",
62
- "@types/node": "^25.9.2",
63
- "tsup": "^8.0.2",
64
- "typescript": "^6.0.3",
65
- "vitest": "^2.1.8"
66
- }
67
- }
1
+ {
2
+ "name": "vectorvesper",
3
+ "version": "2.0.4",
4
+ "description": "Add WebGL, React Three Fiber & advanced motion components to your project via CLI.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "bin": {
9
+ "vectorvesper": "dist/index.js",
10
+ "vv": "dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "package.json"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/vectorvesper/vv-components.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/vectorvesper/vv-components/issues"
25
+ },
26
+ "homepage": "https://vectorvesper.dev",
27
+ "keywords": [
28
+ "react",
29
+ "webgl",
30
+ "three",
31
+ "threejs",
32
+ "components",
33
+ "cli",
34
+ "ui",
35
+ "visual",
36
+ "animation",
37
+ "gsap",
38
+ "shader",
39
+ "interactive"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "postbuild": "node scripts/bundle-manifest.mjs",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "dependencies": {
51
+ "@babel/core": "^7.24.0",
52
+ "@babel/preset-typescript": "^7.24.0",
53
+ "@clack/prompts": "^0.7.0",
54
+ "@modelcontextprotocol/sdk": "^1.29.0",
55
+ "commander": "^12.0.0",
56
+ "ora": "^8.0.1",
57
+ "picocolors": "^1.0.0",
58
+ "zod": "^3.22.4"
59
+ },
60
+ "devDependencies": {
61
+ "@types/babel__core": "^7.20.5",
62
+ "@types/node": "^25.9.2",
63
+ "tsup": "^8.0.2",
64
+ "typescript": "^6.0.3",
65
+ "vitest": "^2.1.8"
66
+ }
67
+ }
@@ -1,416 +0,0 @@
1
- import {
2
- detectProject,
3
- fetchRegistryIndex
4
- } from "./chunk-5YSVDVLK.js";
5
- import {
6
- findHook,
7
- loadHookManifest
8
- } from "./chunk-XN3PPCIM.js";
9
- import {
10
- getAuthToken
11
- } from "./chunk-SFP5K3ZO.js";
12
-
13
- // src/mcp/server.ts
14
- import { z } from "zod";
15
- var VERSION = true ? "2.0.3" : "0.0.0-dev";
16
- function text(body) {
17
- return { content: [{ type: "text", text: body }] };
18
- }
19
- function formatHookSummary(hook) {
20
- return `**${hook.name}** (${hook.category}) \u2014 ${hook.tagline}
21
- Problem: ${hook.problem}`;
22
- }
23
- function formatRuntimeContract(hook) {
24
- const r = hook.runtime;
25
- const lines = [];
26
- lines.push(
27
- r.requiresClient ? '- **Client only.** Needs `"use client"`. On Next.js App Router, a parent server component must not render it directly.' : "- Runs anywhere (no browser-only APIs at module scope)."
28
- );
29
- if (r.ownsTransform) {
30
- lines.push(
31
- "- **Owns the element's inline `transform`.** Give it its own element; do not also animate that node's transform with CSS, framer-motion or GSAP."
32
- );
33
- }
34
- lines.push(
35
- r.respectsReducedMotion ? "- Honours `prefers-reduced-motion` automatically \u2014 no extra work needed." : "- Does **not** self-disable under `prefers-reduced-motion`; gate it yourself if it drives autonomous motion."
36
- );
37
- const caps = [
38
- r.usesPointer && "pointer",
39
- r.usesScroll && "scroll",
40
- r.usesWebGL && "WebGL"
41
- ].filter(Boolean);
42
- if (caps.length) lines.push(`- Uses: ${caps.join(", ")}.`);
43
- if (r.lane) {
44
- lines.push(
45
- `- Runs in the conductor's **${r.lane}** lane${r.priority ? ` at \`${r.priority}\` priority` : ""}. Lanes run input \u2192 update \u2192 render each frame, so sensors are always current before anything draws.`
46
- );
47
- } else if (r.lane === null) {
48
- lines.push("- Never joins the frame loop \u2014 no per-frame cost.");
49
- }
50
- if (typeof r.reRendersPerFrame === "number") {
51
- lines.push(
52
- r.reRendersPerFrame === 0 ? "- **Zero React re-renders per frame.** Values are written straight to the DOM or held in refs; do not mirror them into state." : `- Causes ${r.reRendersPerFrame} React re-render(s) per frame \u2014 keep it out of hot paths.`
53
- );
54
- }
55
- if (r.sharedSingletons?.length) {
56
- lines.push(
57
- `- Retains shared singletons: ${r.sharedSingletons.map((s) => `\`${s}\``).join(", ")} (ref-counted \u2014 cost is shared with every other consumer, not multiplied).`
58
- );
59
- }
60
- if (r.conflictsWith.length) {
61
- lines.push("- **Conflicts with:**");
62
- for (const c of r.conflictsWith) lines.push(` - ${c}`);
63
- }
64
- return lines.join("\n");
65
- }
66
- function formatHookDetail(hook) {
67
- const out = [];
68
- out.push(`# ${hook.name}`);
69
- out.push(`> ${hook.tagline}`);
70
- out.push("");
71
- out.push(`**The problem it removes:** ${hook.problem}`);
72
- out.push("");
73
- out.push(hook.summary);
74
- out.push("");
75
- out.push(`## Import
76
- \`\`\`ts
77
- import { ${hook.name} } from "${hook.importFrom}";
78
- \`\`\``);
79
- out.push(`Install with \`npm i ${hook.packageName}\`. Available since v${hook.since}.`);
80
- out.push("");
81
- out.push(`## Signature
82
- \`${hook.signature}\``);
83
- if (hook.options.length) {
84
- out.push("\n### Options");
85
- out.push("| Option | Type | Default | Description |");
86
- out.push("| --- | --- | --- | --- |");
87
- for (const o of hook.options) {
88
- out.push(`| \`${o.name}\`${o.required ? " *(required)*" : ""} | \`${o.type}\` | \`${o.default}\` | ${o.description} |`);
89
- }
90
- } else {
91
- out.push("\n### Options\nNone \u2014 this hook takes no arguments.");
92
- }
93
- out.push("\n### Returns");
94
- out.push("| Field | Type | Description |");
95
- out.push("| --- | --- | --- |");
96
- for (const r of hook.returns) out.push(`| \`${r.name}\` | \`${r.type}\` | ${r.description} |`);
97
- out.push("\n## Runtime contract");
98
- out.push(formatRuntimeContract(hook));
99
- if (hook.mechanism) {
100
- out.push("\n## How it works inside");
101
- out.push(hook.mechanism);
102
- }
103
- out.push("\n## Quick start");
104
- out.push("```tsx");
105
- out.push(hook.quickStart);
106
- out.push("```");
107
- if (hook.recipes?.length) {
108
- out.push("\n## Recipes");
109
- for (const r of hook.recipes) {
110
- out.push(`
111
- ### ${r.name}
112
- ${r.blurb}
113
-
114
- \`\`\`tsx
115
- ${r.code}
116
- \`\`\``);
117
- }
118
- }
119
- if (hook.dos?.length) out.push(`
120
- ## Do
121
- ${hook.dos.map((d) => `- ${d}`).join("\n")}`);
122
- if (hook.donts?.length) out.push(`
123
- ## Don't
124
- ${hook.donts.map((d) => `- ${d}`).join("\n")}`);
125
- out.push("\n## When NOT to use this");
126
- for (const w of hook.whenNotToUse) out.push(`- **${w.when}** \u2192 ${w.instead}`);
127
- if (hook.guardrails?.length) {
128
- out.push(`
129
- ## Required reading before writing motion code
130
- ${hook.guardrails.map((g) => `- ${g}`).join("\n")}`);
131
- }
132
- out.push(`
133
- ## Docs
134
- ${hook.docsUrl}${hook.labUrl ? `
135
- Live demo: ${hook.labUrl}` : ""}`);
136
- out.push(`
137
- ---
138
- **Consumption rule.** ${hook.disclosure.correctUsage} ${hook.disclosure.reason}`);
139
- if (hook.disclosure.readingSource) {
140
- out.push(`
141
- **Reading the implementation.** ${hook.disclosure.readingSource}`);
142
- }
143
- if (hook.contractLevel === "lean" && hook.upgrade) {
144
- out.push(`
145
- _${hook.upgrade}_`);
146
- }
147
- return out.join("\n");
148
- }
149
- function formatComponentDetail(c) {
150
- const out = [];
151
- out.push(`# ${c.title} \`${c.slug}\``);
152
- out.push(`> ${c.description}`);
153
- out.push("");
154
- out.push(`**Tier:** ${c.tier}${c.tier === "pro" ? " \u2014 requires a Vector Vesper membership" : ""}`);
155
- out.push(`**Category:** ${c.category} \xB7 **Type:** ${c.type} \xB7 **Version:** ${c.version}`);
156
- out.push(`**Frameworks:** ${c.frameworks.join(", ")}`);
157
- out.push("\n## Motion contract");
158
- const lines = [];
159
- lines.push(
160
- c.requiresClient ? '- **Client only.** Needs `"use client"`.' : "- No client boundary required."
161
- );
162
- if (c.usesWebGL) {
163
- lines.push(
164
- "- **Uses WebGL.** On Next.js, render it through `dynamic(() => import(...), { ssr: false })` or it will fail during server rendering."
165
- );
166
- }
167
- lines.push(
168
- c.supportsReducedMotion ? "- Respects `prefers-reduced-motion`." : "- Does **not** self-disable under `prefers-reduced-motion`."
169
- );
170
- const caps = [c.usesPointer && "pointer", c.usesScroll && "scroll"].filter(Boolean);
171
- if (caps.length) lines.push(`- Uses: ${caps.join(", ")}.`);
172
- if (c.usesTailwind) lines.push("- Ships hardcoded Tailwind classes \u2014 the project needs Tailwind configured.");
173
- if (c.fallbacks?.length) lines.push(`- Fallbacks: ${c.fallbacks.join(", ")}`);
174
- out.push(lines.join("\n"));
175
- if (c.dependencies.length || c.registryDependencies.length) {
176
- out.push("\n## Dependencies");
177
- if (c.dependencies.length) out.push(`- npm: ${c.dependencies.map((d) => `\`${d}\``).join(", ")}`);
178
- if (c.registryDependencies.length) {
179
- out.push(`- other VV components: ${c.registryDependencies.map((d) => `\`${d}\``).join(", ")} (installed automatically)`);
180
- }
181
- }
182
- out.push(`
183
- ## Install
184
- \`\`\`bash
185
- npx vectorvesper add ${c.slug}
186
- \`\`\``);
187
- if (c.tier === "pro") {
188
- out.push(
189
- "Requires an authenticated CLI: `npx vectorvesper login <token>`. Tokens come from https://vectorvesper.dev/account."
190
- );
191
- }
192
- if (c.docsUrl) out.push(`
193
- ## Docs
194
- ${c.docsUrl}`);
195
- out.push(
196
- "\n---\n**Note.** Component source is delivered by running the install command above, which writes the files into the project. It is deliberately not returned here \u2014 pasting a component out of a discovery response skips dependency resolution, the TypeScript/JavaScript transpile step, and the install manifest that makes `update` and `remove` work later."
197
- );
198
- return out.join("\n");
199
- }
200
- async function registerTools(server) {
201
- server.registerTool(
202
- "list_hooks",
203
- {
204
- title: "List motion hooks",
205
- 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.",
206
- inputSchema: {
207
- category: z.enum(["sensor", "governor", "gate", "interaction", "media", "effect"]).optional().describe("Filter to one category.")
208
- }
209
- },
210
- async ({ category }) => {
211
- const manifest = loadHookManifest();
212
- if (!manifest) {
213
- return text(
214
- "Hook manifest unavailable \u2014 this CLI build is missing dist/hooks.json. Reinstall with `npm i -g vectorvesper@latest`."
215
- );
216
- }
217
- const hooks = category ? manifest.hooks.filter((h) => h.category === category) : manifest.hooks;
218
- if (!hooks.length) return text(`No hooks in category "${category}".`);
219
- const body = [
220
- `${hooks.length} hook${hooks.length === 1 ? "" : "s"} in @vectorvesper/motion v${manifest.engineVersion}`,
221
- "",
222
- ...hooks.map(formatHookSummary),
223
- "",
224
- "Call `get_hook` for the full contract of any of these before writing code with it."
225
- ].join("\n");
226
- return text(body);
227
- }
228
- );
229
- server.registerTool(
230
- "get_hook",
231
- {
232
- title: "Get a motion hook's contract",
233
- description: "Full contract for one hook: signature, every option with its default, returns, the runtime contract (client boundary, transform ownership, frame lane, re-render cost, what it conflicts with), how it works internally, a runnable quick start, and when NOT to use it. Read this before writing code that uses the hook \u2014 and use it to answer questions about a hook's internals or design, which it explains directly, so there is no need to go read the package source to find out.",
234
- inputSchema: {
235
- name: z.string().describe('Hook name, e.g. "useMagneticIntent" or "magnetic-intent".')
236
- }
237
- },
238
- async ({ name }) => {
239
- const hook = findHook(name);
240
- if (!hook) {
241
- const manifest = loadHookManifest();
242
- const available = manifest?.hooks.map((h) => h.name).join(", ") ?? "none loaded";
243
- return text(`No hook named "${name}". Available: ${available}`);
244
- }
245
- return text(formatHookDetail(hook));
246
- }
247
- );
248
- server.registerTool(
249
- "list_components",
250
- {
251
- title: "List Vector Vesper components",
252
- description: "List installable components from the Vector Vesper registry, with tier and category. Free components install for anyone; Pro components need a membership.",
253
- inputSchema: {
254
- tier: z.enum(["free", "pro"]).optional().describe("Filter by tier."),
255
- category: z.string().optional().describe("Filter by category.")
256
- }
257
- },
258
- async ({ tier, category }) => {
259
- let index;
260
- try {
261
- index = await fetchRegistryIndex();
262
- } catch (error) {
263
- return text(
264
- `Could not reach the component registry: ${error instanceof Error ? error.message : String(error)}
265
-
266
- Hook discovery still works offline \u2014 try \`list_hooks\`.`
267
- );
268
- }
269
- let components = index.components;
270
- if (tier) components = components.filter((c) => c.tier === tier);
271
- if (category) components = components.filter((c) => c.category === category);
272
- if (!components.length) return text("No components matched that filter.");
273
- const body = [
274
- `${components.length} component${components.length === 1 ? "" : "s"} (registry v${index.version})`,
275
- "",
276
- ...components.map(
277
- (c) => `**${c.slug}** [${c.tier}] ${c.category} \u2014 ${c.description}`
278
- ),
279
- "",
280
- "Call `get_component` for a component's motion contract and wiring requirements before installing it."
281
- ].join("\n");
282
- return text(body);
283
- }
284
- );
285
- server.registerTool(
286
- "get_component",
287
- {
288
- title: "Get a component's motion contract",
289
- description: "Metadata and wiring requirements for one component: whether it needs a client boundary, whether it needs dynamic import to survive SSR, what it depends on, and how to install it. Does not return source \u2014 source is written into the project by the install command.",
290
- inputSchema: { slug: z.string().describe('Component slug, e.g. "code-rain".') }
291
- },
292
- async ({ slug }) => {
293
- let index;
294
- try {
295
- index = await fetchRegistryIndex();
296
- } catch (error) {
297
- return text(
298
- `Could not reach the component registry: ${error instanceof Error ? error.message : String(error)}`
299
- );
300
- }
301
- const component = index.components.find((c) => c.slug === slug);
302
- if (!component) {
303
- return text(
304
- `No component with slug "${slug}". Call \`list_components\` to see what exists.`
305
- );
306
- }
307
- return text(formatComponentDetail(component));
308
- }
309
- );
310
- server.registerTool(
311
- "get_setup_guidance",
312
- {
313
- title: "Inspect the current project",
314
- description: "Detect the framework, package manager, TypeScript/JavaScript, Tailwind and Next router of the project in the working directory, plus Vector Vesper auth status. Call this before generating install commands or import paths so they match the project.",
315
- inputSchema: {
316
- cwd: z.string().optional().describe("Project root. Defaults to the server's working directory.")
317
- }
318
- },
319
- async ({ cwd }) => {
320
- const info = detectProject(cwd ?? process.cwd());
321
- const authed = Boolean(getAuthToken());
322
- const out = [];
323
- out.push("# Project");
324
- out.push(`- Root: \`${info.rootPath}\``);
325
- out.push(`- Framework: **${info.framework}**${info.nextRouter ? ` (${info.nextRouter} router)` : ""}`);
326
- out.push(`- Language: **${info.isTypeScript ? "TypeScript" : "JavaScript"}**`);
327
- out.push(`- Package manager: **${info.packageManager}**`);
328
- out.push(`- Tailwind: ${info.hasTailwind ? "yes" : "no"}`);
329
- out.push(`- \`src/\` directory: ${info.hasSrcDir ? "yes" : "no"}`);
330
- out.push("\n# Vector Vesper");
331
- out.push(`- CLI authenticated: ${authed ? "yes \u2014 Pro components available" : "no \u2014 free components only"}`);
332
- if (!authed) {
333
- out.push(" - To unlock Pro: `npx vectorvesper login <token>` (tokens at https://vectorvesper.dev/account)");
334
- }
335
- out.push("\n# What this implies");
336
- if (info.framework === "next" && info.nextRouter === "app") {
337
- out.push(
338
- '- App Router: any component or hook marked **client only** needs `"use client"` at the top of the file that uses it.'
339
- );
340
- out.push(
341
- "- Anything using WebGL should be rendered via `dynamic(() => import(...), { ssr: false })`."
342
- );
343
- }
344
- if (!info.hasTailwind) {
345
- out.push("- No Tailwind detected: components that ship Tailwind classes will render unstyled.");
346
- }
347
- out.push("- Run `npx vectorvesper init` first if `vv.config.json` does not exist yet.");
348
- return text(out.join("\n"));
349
- }
350
- );
351
- server.registerTool(
352
- "search",
353
- {
354
- title: "Search hooks and components",
355
- description: 'Keyword search across both motion hooks 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.',
356
- inputSchema: { query: z.string().describe("What you are trying to build.") }
357
- },
358
- async ({ query }) => {
359
- const q = query.toLowerCase();
360
- const terms = q.split(/\s+/).filter((t) => t.length > 2);
361
- const score = (haystack) => {
362
- const h = haystack.toLowerCase();
363
- if (h.includes(q)) return 100;
364
- return terms.reduce((n, t) => n + (h.includes(t) ? 1 : 0), 0);
365
- };
366
- const results = [];
367
- const manifest = loadHookManifest();
368
- for (const h of manifest?.hooks ?? []) {
369
- const s = score([h.name, h.tagline, h.problem, h.summary, h.category].join(" "));
370
- if (s > 0) results.push({ score: s, line: `**hook** \`${h.name}\` \u2014 ${h.tagline}` });
371
- }
372
- try {
373
- const index = await fetchRegistryIndex();
374
- for (const c of index.components) {
375
- const s = score([c.slug, c.title, c.description, c.category, c.type].join(" "));
376
- if (s > 0) {
377
- results.push({ score: s, line: `**component** \`${c.slug}\` [${c.tier}] \u2014 ${c.description}` });
378
- }
379
- }
380
- } catch {
381
- }
382
- if (!results.length) {
383
- return text(
384
- `Nothing matched "${query}". Try \`list_hooks\` or \`list_components\` to browse everything.`
385
- );
386
- }
387
- results.sort((a, b) => b.score - a.score);
388
- return text(
389
- [
390
- `${results.length} result${results.length === 1 ? "" : "s"} for "${query}"`,
391
- "",
392
- ...results.slice(0, 12).map((r) => r.line),
393
- "",
394
- "Then call `get_hook` or `get_component` for the full contract before writing code."
395
- ].join("\n")
396
- );
397
- }
398
- );
399
- }
400
- async function startMcpServer() {
401
- const [{ McpServer: McpServerCtor }, { StdioServerTransport }] = await Promise.all([
402
- import("@modelcontextprotocol/sdk/server/mcp.js"),
403
- import("@modelcontextprotocol/sdk/server/stdio.js")
404
- ]);
405
- const server = new McpServerCtor(
406
- { name: "vectorvesper", version: VERSION },
407
- {
408
- 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\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."
409
- }
410
- );
411
- await registerTools(server);
412
- await server.connect(new StdioServerTransport());
413
- }
414
- export {
415
- startMcpServer
416
- };