vibetachyon 1.5.8 → 1.6.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/mcp-server.js +445 -106
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -173,37 +173,46 @@ async function validateTokenAtStartup() {
|
|
|
173
173
|
async function startMcpServer() {
|
|
174
174
|
await validateTokenAtStartup();
|
|
175
175
|
const server = new mcp_js_1.McpServer({
|
|
176
|
-
name: "VibeTachyon MCP",
|
|
177
|
-
version: "1.
|
|
176
|
+
name: "VibeTachyon MCP — Animmaster Engine",
|
|
177
|
+
version: "1.6.0"
|
|
178
178
|
});
|
|
179
179
|
/**
|
|
180
|
-
* VIBETACHYON FRONTEND PERSONA — Senior Frontend Designer
|
|
180
|
+
* VIBETACHYON FRONTEND PERSONA — Senior Frontend Designer (Animmaster Engine)
|
|
181
181
|
*
|
|
182
|
-
* You are a Senior Frontend Designer
|
|
183
|
-
*
|
|
182
|
+
* You are a Senior Frontend Designer powered by the Animmaster component system.
|
|
183
|
+
* Animmaster is a curated set of 42 premium vanilla JS/SCSS/GSAP components that work
|
|
184
|
+
* as a unified system — not as isolated snippets. You do NOT generate generic code.
|
|
184
185
|
*
|
|
185
186
|
* YOUR MANDATORY WORKFLOW — follow this EXACT sequence for every page/section request:
|
|
186
|
-
* 1. vibe_project_dna
|
|
187
|
-
* 2. vibe_design_brief
|
|
187
|
+
* 1. vibe_project_dna — read the project first, always
|
|
188
|
+
* 2. vibe_design_brief — if no brief exists, ASK the user before touching any code
|
|
188
189
|
* 3. vibe_read_design_brief — read active brief before each new section
|
|
189
|
-
* 4. vibe_search_snippets
|
|
190
|
-
* 5.
|
|
190
|
+
* 4. vibe_search_snippets — search Animmaster components by use_case and visual_tone
|
|
191
|
+
* 5. vibe_get_component — get the complete bundle (JS+HTML+SCSS) for chosen components
|
|
192
|
+
* 6. Generate code — only after steps 1-5 are complete
|
|
193
|
+
*
|
|
194
|
+
* ANIMMASTER SYSTEM RULES:
|
|
195
|
+
* - Components must be initialized in init_order sequence (0→6): preloader first, then cursor/header, then effects
|
|
196
|
+
* - Always respect compatible_with: do NOT combine components that aren't listed as compatible
|
|
197
|
+
* - animation_budget is 1-5: total page budget should not exceed 10. Hero can have budget 4-5, rest 1-2.
|
|
198
|
+
* - Each component comes with standalone JS (no build system needed), HTML template, and SCSS
|
|
199
|
+
* - CDN deps: GSAP (gsap.min.js), SplitType, Swiper, Chart.js — load only what's needed
|
|
200
|
+
* - data-attributes activate Animmaster components: always include them in the HTML
|
|
191
201
|
*
|
|
192
202
|
* YOUR NON-NEGOTIABLE RULES:
|
|
193
203
|
* - NEVER mix more than 2 animation styles on a single page
|
|
194
204
|
* - NEVER use more than 3 brand colors (primary, secondary, accent)
|
|
195
|
-
* - NEVER hardcode hex/rgb — always use CSS variables
|
|
205
|
+
* - NEVER hardcode hex/rgb — always use CSS variables
|
|
196
206
|
* - NEVER generate a section without first checking the active design brief
|
|
197
207
|
* - ALWAYS explain WHY you chose each component (1 sentence per component)
|
|
198
|
-
* - ALWAYS build sections in
|
|
208
|
+
* - ALWAYS build sections in init_order (preloader → cursor → header → effects)
|
|
199
209
|
* - ALWAYS reject components that violate the brief's avoid list
|
|
200
|
-
* - If a component from vibe_search_snippets conflicts with the brief, search again with different terms
|
|
201
210
|
*
|
|
202
211
|
* VISUAL COHERENCE RULES:
|
|
203
212
|
* - Dark brief → all sections dark. No random light cards in a dark page.
|
|
204
|
-
* - Minimal brief →
|
|
205
|
-
* -
|
|
206
|
-
* - Max 1 heavy-animation section per page (
|
|
213
|
+
* - Minimal brief → animation_budget max 2 per section, no heavy GSAP timelines
|
|
214
|
+
* - Immersive brief → use parallax + mouse tracking + splittype for full effect
|
|
215
|
+
* - Max 1 heavy-animation section per page (hero). Rest = subtle or static.
|
|
207
216
|
*
|
|
208
217
|
* CONVERSION RULES (SaaS pages):
|
|
209
218
|
* - Section order: hero → social-proof → features → how-it-works → pricing → faq → cta
|
|
@@ -211,17 +220,17 @@ async function startMcpServer() {
|
|
|
211
220
|
* - Social proof (logos/testimonials) before pricing — builds trust first
|
|
212
221
|
* - Every section must have ONE clear action or message. Not two.
|
|
213
222
|
*/
|
|
214
|
-
// Tool: Searching Snippets
|
|
215
|
-
server.tool("vibe_search_snippets", "Search for UI components
|
|
216
|
-
query: zod_1.z.string().describe("Search query for the UI component (e.g. 'hero section
|
|
223
|
+
// Tool: Searching Snippets (Animmaster Engine)
|
|
224
|
+
server.tool("vibe_search_snippets", "Search for Animmaster UI components. Returns standalone JS + HTML + SCSS bundles ready to use. Automatically loads the active design brief to filter by visual_tone and use_case. Always call vibe_project_dna and vibe_design_brief before this tool.", {
|
|
225
|
+
query: zod_1.z.string().describe("Search query for the UI component (e.g. 'hero section', 'text animation', 'parallax scroll')"),
|
|
217
226
|
limit: zod_1.z.number().optional().describe("Max number of results to return (default: 5)"),
|
|
218
227
|
projectDir: zod_1.z.string().optional().describe("Absolute path to the user's project root."),
|
|
219
|
-
useCaseFilter: zod_1.z.string().optional().describe("Filter by use case: 'hero', 'cta', 'feature-section', 'navigation', 'footer', 'pricing', 'social-proof', 'background-effect', 'text-effect', 'modal', 'form'"),
|
|
220
|
-
styleFilter: zod_1.z.array(zod_1.z.string()).optional().describe("Filter by
|
|
221
|
-
complexityFilter: zod_1.z.enum(['simple', 'medium', 'complex']).optional().describe("Filter by
|
|
228
|
+
useCaseFilter: zod_1.z.string().optional().describe("Filter by use case: 'hero', 'cta', 'feature-section', 'navigation', 'footer', 'pricing', 'social-proof', 'background-effect', 'text-effect', 'modal', 'form', 'preloader', 'cursor'"),
|
|
229
|
+
styleFilter: zod_1.z.array(zod_1.z.string()).optional().describe("Filter by visual tone: 'dark', 'light', 'animated', 'minimal', 'modern', 'immersive'"),
|
|
230
|
+
complexityFilter: zod_1.z.enum(['simple', 'medium', 'complex']).optional().describe("Filter by animation budget: simple=1-2, medium=3, complex=4-5")
|
|
222
231
|
}, async ({ query, limit = 5, projectDir, useCaseFilter, styleFilter, complexityFilter }) => {
|
|
223
232
|
await checkSanity();
|
|
224
|
-
console.error(`[VibeTachyon] Searching DB for: ${query}`);
|
|
233
|
+
console.error(`[VibeTachyon] Searching Animmaster DB for: ${query}`);
|
|
225
234
|
const rawRoot = projectDir || process.cwd();
|
|
226
235
|
const root = await findProjectRoot(rawRoot);
|
|
227
236
|
// Auto-load design brief to enrich filters
|
|
@@ -259,107 +268,191 @@ async function startMcpServer() {
|
|
|
259
268
|
resolvedStyleFilter = derivedTags;
|
|
260
269
|
}
|
|
261
270
|
try {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
.
|
|
271
|
+
// Build Animmaster query
|
|
272
|
+
let queryBuilder = supabase
|
|
273
|
+
.from('animmaster_components')
|
|
274
|
+
.select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, compatible_with, animation_budget, visual_tone, use_cases, data_attributes')
|
|
275
|
+
.eq('is_active', true);
|
|
276
|
+
// Text search across name and description
|
|
277
|
+
if (query) {
|
|
278
|
+
queryBuilder = queryBuilder.or(`name.ilike.%${query}%,description.ilike.%${query}%`);
|
|
279
|
+
}
|
|
280
|
+
// Use case filter
|
|
281
|
+
if (resolvedUseCaseFilter) {
|
|
282
|
+
queryBuilder = queryBuilder.contains('use_cases', [resolvedUseCaseFilter]);
|
|
283
|
+
}
|
|
284
|
+
// Visual tone / style filter
|
|
285
|
+
if (resolvedStyleFilter && resolvedStyleFilter.length > 0) {
|
|
286
|
+
queryBuilder = queryBuilder.overlaps('visual_tone', resolvedStyleFilter);
|
|
287
|
+
}
|
|
288
|
+
// Animation budget filter (complexity mapping)
|
|
289
|
+
if (resolvedComplexity) {
|
|
290
|
+
if (resolvedComplexity === 'simple')
|
|
291
|
+
queryBuilder = queryBuilder.lte('animation_budget', 2);
|
|
292
|
+
else if (resolvedComplexity === 'medium')
|
|
293
|
+
queryBuilder = queryBuilder.eq('animation_budget', 3);
|
|
294
|
+
else if (resolvedComplexity === 'complex')
|
|
295
|
+
queryBuilder = queryBuilder.gte('animation_budget', 4);
|
|
296
|
+
}
|
|
297
|
+
queryBuilder = queryBuilder.order('animation_budget').limit(limit);
|
|
298
|
+
const { data: allResults, error } = await queryBuilder;
|
|
274
299
|
if (error) {
|
|
275
|
-
console.error("[VibeTachyon] Supabase
|
|
300
|
+
console.error("[VibeTachyon] Supabase Error:", error);
|
|
276
301
|
throw error;
|
|
277
302
|
}
|
|
278
|
-
// If filtered search returns nothing, fallback to unfiltered
|
|
303
|
+
// If filtered search returns nothing, fallback to unfiltered text search
|
|
279
304
|
let results = allResults;
|
|
280
305
|
let fallbackNote = '';
|
|
281
306
|
if ((!results || results.length === 0) && (resolvedStyleFilter || resolvedUseCaseFilter)) {
|
|
282
307
|
console.error(`[VibeTachyon] No filtered results, falling back to unfiltered search`);
|
|
283
|
-
const { data: fallback } = await supabase
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
308
|
+
const { data: fallback } = await supabase
|
|
309
|
+
.from('animmaster_components')
|
|
310
|
+
.select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, compatible_with, animation_budget, visual_tone, use_cases, data_attributes')
|
|
311
|
+
.eq('is_active', true)
|
|
312
|
+
.or(`name.ilike.%${query}%,description.ilike.%${query}%`)
|
|
313
|
+
.order('animation_budget')
|
|
314
|
+
.limit(limit);
|
|
315
|
+
results = fallback ?? [];
|
|
316
|
+
fallbackNote = `\n⚠️ No components matched the style/use-case filters. Showing best text matches instead — review fit with design brief.\n`;
|
|
289
317
|
}
|
|
290
318
|
if (!results || results.length === 0) {
|
|
291
319
|
return {
|
|
292
|
-
content: [{ type: "text", text: `No
|
|
320
|
+
content: [{ type: "text", text: `No Animmaster components found for "${query}". Try terms like: parallax, marquee, cursor, preloader, splittype, ripple, accordion, digcounter, slider, gallery.` }]
|
|
293
321
|
};
|
|
294
322
|
}
|
|
295
323
|
// Coherence check — flag components that might break brief rules
|
|
296
324
|
const briefAvoid = brief?.rules?.mustAvoid || [];
|
|
297
325
|
const formattedResults = results.map((item) => {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const useCaseLine = item.use_case?.length
|
|
306
|
-
? `Use case: ${item.use_case.join(', ')}`
|
|
307
|
-
: '';
|
|
308
|
-
const complexityLine = item.complexity ? `Complexity: ${item.complexity}` : '';
|
|
326
|
+
const budgetLabel = item.animation_budget <= 2 ? 'subtle' : item.animation_budget === 3 ? 'medium' : 'heavy';
|
|
327
|
+
const toneLine = item.visual_tone?.length ? `Tone: ${item.visual_tone.join(', ')}` : '';
|
|
328
|
+
const useCaseLine = item.use_cases?.length ? `Use cases: ${item.use_cases.join(', ')}` : '';
|
|
329
|
+
const budgetLine = `Animation budget: ${item.animation_budget}/5 (${budgetLabel})`;
|
|
330
|
+
const initLine = `Init order: ${item.init_order}`;
|
|
331
|
+
const compatLine = item.compatible_with?.length ? `Compatible with: ${item.compatible_with.join(', ')}` : '';
|
|
332
|
+
const depsLine = item.npm_deps?.length ? `CDN deps: ${item.npm_deps.join(', ')}` : '';
|
|
309
333
|
// Coherence warning
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
const ruleKey = rule.toLowerCase();
|
|
313
|
-
return codeStr.includes(ruleKey) || (item.title || '').toLowerCase().includes(ruleKey);
|
|
314
|
-
});
|
|
334
|
+
const violations = briefAvoid.filter((rule) => (item.name || '').toLowerCase().includes(rule.toLowerCase()) ||
|
|
335
|
+
(item.description || '').toLowerCase().includes(rule.toLowerCase()));
|
|
315
336
|
const coherenceNote = violations.length > 0
|
|
316
|
-
? `\n🚨 BRIEF CONFLICT: This component may
|
|
337
|
+
? `\n🚨 BRIEF CONFLICT: This component may conflict with "${violations.join(', ')}" in the avoid list. Review before using.\n`
|
|
317
338
|
: '';
|
|
318
|
-
const
|
|
319
|
-
|
|
339
|
+
const minifiedJs = minifySnippet(item.js_standalone || '');
|
|
340
|
+
const htmlSnippet = (item.html_template || '').split('\n').slice(0, 15).join('\n');
|
|
341
|
+
const scssSnippet = (item.scss_styles || '').split('\n').slice(0, 10).join('\n');
|
|
342
|
+
return [
|
|
343
|
+
`### ${item.name} [🎬 ANIMMASTER | ${item.category?.toUpperCase()}]`,
|
|
344
|
+
[toneLine, useCaseLine, budgetLine, initLine].filter(Boolean).join(' | '),
|
|
345
|
+
item.description || '',
|
|
346
|
+
compatLine,
|
|
347
|
+
depsLine,
|
|
348
|
+
coherenceNote,
|
|
349
|
+
`**HTML template:**\n\`\`\`html\n${htmlSnippet}\n\`\`\``,
|
|
350
|
+
`**JS standalone:**\n\`\`\`js\n${minifiedJs}\n\`\`\``,
|
|
351
|
+
scssSnippet ? `**SCSS (preview):**\n\`\`\`scss\n${scssSnippet}\n...\n\`\`\`` : '',
|
|
352
|
+
`→ Call vibe_get_component("${item.name}") for the complete SCSS and full metadata.`
|
|
353
|
+
].filter(Boolean).join('\n');
|
|
320
354
|
}).join('\n\n---\n\n');
|
|
321
|
-
// Framework guardrails
|
|
322
|
-
let frameworkContext = "";
|
|
323
|
-
try {
|
|
324
|
-
const pkgPath = path_1.default.join(root, 'package.json');
|
|
325
|
-
if (await fs_extra_1.default.pathExists(pkgPath)) {
|
|
326
|
-
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
327
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
328
|
-
const isVite = !!deps['vite'];
|
|
329
|
-
const isNext = !!deps['next'];
|
|
330
|
-
frameworkContext += `\n[FRAMEWORK: ${isNext ? 'Next.js' : isVite ? 'Vite/React' : 'React'}]\n`;
|
|
331
|
-
if (isVite)
|
|
332
|
-
frameworkContext += `- Remove "use client", replace next/image with <img>, replace next/link with <a>\n`;
|
|
333
|
-
if (isNext)
|
|
334
|
-
frameworkContext += `- Add "use client" if component uses hooks or event handlers\n`;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
catch { /* silent */ }
|
|
338
|
-
// Peer dep audit
|
|
339
|
-
const standardDeps = { "framer-motion": "11.0.0", "lucide-react": "0.400.0" };
|
|
340
|
-
const audit = await auditPeerDeps(root, standardDeps);
|
|
341
|
-
const auditPrefix = audit.warnings.length > 0
|
|
342
|
-
? `⚠️ [VIBESHIELD]\n${audit.warnings.join('\n')}\n\n`
|
|
343
|
-
: '';
|
|
344
355
|
const briefSummary = brief
|
|
345
356
|
? `[Active Brief: ${brief.referenceStyle} | ${brief.rules?.visualTone} | max ${brief.rules?.maxAnimationTypes} animations]\n`
|
|
346
357
|
: '';
|
|
347
358
|
return {
|
|
348
359
|
content: [{
|
|
349
360
|
type: "text",
|
|
350
|
-
text: `${
|
|
351
|
-
|
|
352
|
-
(resolvedStyleFilter ? ` [
|
|
361
|
+
text: `${briefWarning}${fallbackNote}${briefSummary}` +
|
|
362
|
+
`🎬 VibeTachyon Animmaster found ${results.length} components for "${query}"` +
|
|
363
|
+
(resolvedStyleFilter ? ` [tone: ${resolvedStyleFilter.join(', ')}]` : '') +
|
|
353
364
|
(resolvedUseCaseFilter ? ` [use: ${resolvedUseCaseFilter}]` : '') +
|
|
354
|
-
`:\n\n${formattedResults}\n\n
|
|
355
|
-
|
|
365
|
+
`:\n\n${formattedResults}\n\n` +
|
|
366
|
+
`INSTRUCTION FOR AI:\n` +
|
|
367
|
+
`- Initialize components in init_order sequence (lower = first)\n` +
|
|
368
|
+
`- Check compatible_with before combining components on one page\n` +
|
|
369
|
+
`- Use vibe_get_component(name) to get the complete SCSS bundle\n` +
|
|
370
|
+
`- Respect animation_budget: page total should not exceed 10`
|
|
356
371
|
}]
|
|
357
372
|
};
|
|
358
373
|
}
|
|
359
374
|
catch (error) {
|
|
360
375
|
console.error("[VibeTachyon] Search Error:", error);
|
|
361
376
|
return {
|
|
362
|
-
content: [{ type: "text", text: `Error searching
|
|
377
|
+
content: [{ type: "text", text: `Error searching Animmaster repository: ${error instanceof Error ? error.message : String(error)}` }]
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
// Tool: Get Complete Animmaster Component Bundle
|
|
382
|
+
server.tool("vibe_get_component", "Fetch the complete Animmaster component bundle by name: full JS (standalone), complete SCSS, HTML template, CDN deps list, init order, and compatible components. Call this after vibe_search_snippets to get the full implementation code.", {
|
|
383
|
+
name: zod_1.z.string().describe("Animmaster component name (e.g. 'parallax', 'cursor', 'marquee', 'splittype', 'preloader', 'ripple')")
|
|
384
|
+
}, async ({ name }) => {
|
|
385
|
+
await checkSanity();
|
|
386
|
+
try {
|
|
387
|
+
const { data, error } = await supabase
|
|
388
|
+
.from('animmaster_components')
|
|
389
|
+
.select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, init_trigger, compatible_with, incompatible_with, animation_budget, visual_tone, use_cases, data_attributes')
|
|
390
|
+
.eq('name', name)
|
|
391
|
+
.single();
|
|
392
|
+
if (error || !data) {
|
|
393
|
+
// Try partial match
|
|
394
|
+
const { data: partial } = await supabase
|
|
395
|
+
.from('animmaster_components')
|
|
396
|
+
.select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, core_deps, init_order, compatible_with, animation_budget, visual_tone, use_cases')
|
|
397
|
+
.ilike('name', `%${name}%`)
|
|
398
|
+
.eq('is_active', true)
|
|
399
|
+
.limit(1)
|
|
400
|
+
.single();
|
|
401
|
+
if (!partial) {
|
|
402
|
+
return { content: [{ type: "text", text: `Component "${name}" not found in Animmaster. Use vibe_search_snippets to find available components.` }] };
|
|
403
|
+
}
|
|
404
|
+
return buildComponentResponse(partial);
|
|
405
|
+
}
|
|
406
|
+
return buildComponentResponse(data);
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
return { content: [{ type: "text", text: `Error fetching component: ${err}` }] };
|
|
410
|
+
}
|
|
411
|
+
function buildComponentResponse(item) {
|
|
412
|
+
const budgetLabel = item.animation_budget <= 2 ? 'subtle' : item.animation_budget === 3 ? 'medium' : 'heavy';
|
|
413
|
+
const dataAttrsStr = item.data_attributes
|
|
414
|
+
? Object.entries(item.data_attributes).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`).join('\n')
|
|
415
|
+
: 'none';
|
|
416
|
+
return {
|
|
417
|
+
content: [{
|
|
418
|
+
type: "text",
|
|
419
|
+
text: [
|
|
420
|
+
`# ${item.name} [🎬 ANIMMASTER | ${item.category?.toUpperCase()}]`,
|
|
421
|
+
`**Description:** ${item.description || 'No description'}`,
|
|
422
|
+
`**Animation budget:** ${item.animation_budget}/5 (${budgetLabel})`,
|
|
423
|
+
`**Init order:** ${item.init_order} (initialize in this sequence on page)`,
|
|
424
|
+
`**Init trigger:** ${item.init_trigger || 'load'}`,
|
|
425
|
+
`**Visual tone:** ${item.visual_tone?.join(', ') || 'universal'}`,
|
|
426
|
+
`**Use cases:** ${item.use_cases?.join(', ') || 'general'}`,
|
|
427
|
+
`**CDN dependencies:** ${item.npm_deps?.length ? item.npm_deps.join(', ') : 'none'}`,
|
|
428
|
+
`**Core deps (Animmaster):** ${item.core_deps?.length ? item.core_deps.join(', ') : 'none'}`,
|
|
429
|
+
`**Compatible with:** ${item.compatible_with?.length ? item.compatible_with.join(', ') : 'all'}`,
|
|
430
|
+
`**Incompatible with:** ${item.incompatible_with?.length ? item.incompatible_with.join(', ') : 'none'}`,
|
|
431
|
+
`**Data attributes:**\n${dataAttrsStr}`,
|
|
432
|
+
'',
|
|
433
|
+
`## HTML Template`,
|
|
434
|
+
`\`\`\`html`,
|
|
435
|
+
item.html_template || '<!-- No HTML template -->',
|
|
436
|
+
`\`\`\``,
|
|
437
|
+
'',
|
|
438
|
+
`## JS Standalone (no imports needed)`,
|
|
439
|
+
`\`\`\`js`,
|
|
440
|
+
item.js_standalone || '// No standalone JS',
|
|
441
|
+
`\`\`\``,
|
|
442
|
+
'',
|
|
443
|
+
`## SCSS Styles`,
|
|
444
|
+
`\`\`\`scss`,
|
|
445
|
+
item.scss_styles || '/* No SCSS */',
|
|
446
|
+
`\`\`\``,
|
|
447
|
+
'',
|
|
448
|
+
`INSTRUCTION FOR AI:`,
|
|
449
|
+
`- Include the HTML template in the page structure`,
|
|
450
|
+
`- Add the JS standalone code in a <script> tag (after DOM ready)`,
|
|
451
|
+
`- Include the SCSS/CSS in your stylesheet`,
|
|
452
|
+
`- Install CDN deps: ${item.npm_deps?.join(', ') || 'none required'}`,
|
|
453
|
+
`- This component initializes at order ${item.init_order} — load after lower-order components`
|
|
454
|
+
].join('\n')
|
|
455
|
+
}]
|
|
363
456
|
};
|
|
364
457
|
}
|
|
365
458
|
});
|
|
@@ -981,21 +1074,28 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
|
|
|
981
1074
|
await checkSanity();
|
|
982
1075
|
try {
|
|
983
1076
|
let allResults = [];
|
|
984
|
-
for (const
|
|
985
|
-
const { data
|
|
986
|
-
.
|
|
987
|
-
|
|
1077
|
+
for (const compName of componentNames) {
|
|
1078
|
+
const { data } = await supabase
|
|
1079
|
+
.from('animmaster_components')
|
|
1080
|
+
.select('name, category, description, html_template, js_standalone, scss_styles, npm_deps, init_order, compatible_with, animation_budget')
|
|
1081
|
+
.or(`name.eq.${compName},name.ilike.%${compName}%`)
|
|
1082
|
+
.eq('is_active', true)
|
|
1083
|
+
.limit(1);
|
|
1084
|
+
if (data && data.length > 0) {
|
|
988
1085
|
allResults.push(data[0]);
|
|
989
1086
|
}
|
|
990
1087
|
}
|
|
991
1088
|
if (allResults.length === 0) {
|
|
992
|
-
return { content: [{ type: "text", text: `Could not find any of the requested components in the
|
|
1089
|
+
return { content: [{ type: "text", text: `Could not find any of the requested components in the Animmaster registry.` }] };
|
|
993
1090
|
}
|
|
1091
|
+
// Sort by init_order so they're listed in initialization sequence
|
|
1092
|
+
allResults.sort((a, b) => (a.init_order || 5) - (b.init_order || 5));
|
|
994
1093
|
const formattedResults = allResults.map((item) => {
|
|
995
|
-
|
|
1094
|
+
const minifiedJs = minifySnippet(item.js_standalone || '');
|
|
1095
|
+
return `### ${item.name} [🎬 ANIMMASTER | init_order: ${item.init_order}]\nCategory: ${item.category} | Budget: ${item.animation_budget}/5\nDeps: ${item.npm_deps?.join(', ') || 'none'}\n\n**HTML:**\n\`\`\`html\n${item.html_template || ''}\n\`\`\`\n\n**JS:**\n\`\`\`js\n${minifiedJs}\n\`\`\``;
|
|
996
1096
|
}).join('\n\n---\n\n');
|
|
997
1097
|
return {
|
|
998
|
-
content: [{ type: "text", text: `[
|
|
1098
|
+
content: [{ type: "text", text: `[ANIMMASTER BUNDLE RESOLVED — ${allResults.length} components, sorted by init_order]\n\nINSTRUCTION FOR AI: Initialize these components in the order listed (lowest init_order first).\n\n${formattedResults}` }]
|
|
999
1099
|
};
|
|
1000
1100
|
}
|
|
1001
1101
|
catch (err) {
|
|
@@ -1032,16 +1132,51 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
|
|
|
1032
1132
|
}, async ({ themeOrIndustry }) => {
|
|
1033
1133
|
await checkSanity();
|
|
1034
1134
|
try {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1135
|
+
// Map sections to Animmaster use cases and search terms
|
|
1136
|
+
const sectionMap = [
|
|
1137
|
+
{ section: 'preloader', query: 'preloader', useCase: 'preloader' },
|
|
1138
|
+
{ section: 'cursor', query: 'cursor', useCase: 'cursor' },
|
|
1139
|
+
{ section: 'navbar', query: 'header navigation', useCase: 'navigation' },
|
|
1140
|
+
{ section: 'hero', query: `hero ${themeOrIndustry}`, useCase: 'hero' },
|
|
1141
|
+
{ section: 'features', query: 'features section', useCase: 'feature-section' },
|
|
1142
|
+
{ section: 'footer', query: 'footer', useCase: 'footer' }
|
|
1143
|
+
];
|
|
1144
|
+
let combinedPage = `[🎬 ANIMMASTER PAGE COMPOSER: ${themeOrIndustry}]\n\nINSTRUCTION FOR AI: Assemble a complete page using Animmaster components in init_order sequence.\n\n`;
|
|
1145
|
+
const allComponents = [];
|
|
1146
|
+
for (const { section, query, useCase } of sectionMap) {
|
|
1147
|
+
const { data } = await supabase
|
|
1148
|
+
.from('animmaster_components')
|
|
1149
|
+
.select('name, category, description, html_template, js_standalone, npm_deps, init_order, animation_budget, compatible_with')
|
|
1150
|
+
.eq('is_active', true)
|
|
1151
|
+
.contains('use_cases', [useCase])
|
|
1152
|
+
.limit(1);
|
|
1153
|
+
if (data && data.length > 0) {
|
|
1154
|
+
allComponents.push({ section, ...data[0] });
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
// Fallback: text search
|
|
1158
|
+
const { data: fallback } = await supabase
|
|
1159
|
+
.from('animmaster_components')
|
|
1160
|
+
.select('name, category, description, html_template, js_standalone, npm_deps, init_order, animation_budget, compatible_with')
|
|
1161
|
+
.eq('is_active', true)
|
|
1162
|
+
.or(`name.ilike.%${query}%,description.ilike.%${query}%`)
|
|
1163
|
+
.limit(1);
|
|
1164
|
+
if (fallback && fallback.length > 0)
|
|
1165
|
+
allComponents.push({ section, ...fallback[0] });
|
|
1043
1166
|
}
|
|
1044
1167
|
}
|
|
1168
|
+
// Sort by init_order
|
|
1169
|
+
allComponents.sort((a, b) => (a.init_order || 5) - (b.init_order || 5));
|
|
1170
|
+
for (const item of allComponents) {
|
|
1171
|
+
const minifiedJs = minifySnippet(item.js_standalone || '');
|
|
1172
|
+
combinedPage += `### === ${item.section.toUpperCase()} → ${item.name} [init_order: ${item.init_order}] ===\n`;
|
|
1173
|
+
combinedPage += `Budget: ${item.animation_budget}/5 | Deps: ${item.npm_deps?.join(', ') || 'none'}\n\n`;
|
|
1174
|
+
combinedPage += `**HTML:**\n\`\`\`html\n${item.html_template || ''}\n\`\`\`\n\n`;
|
|
1175
|
+
combinedPage += `**JS:**\n\`\`\`js\n${minifiedJs}\n\`\`\`\n\n`;
|
|
1176
|
+
}
|
|
1177
|
+
const totalBudget = allComponents.reduce((sum, c) => sum + (c.animation_budget || 0), 0);
|
|
1178
|
+
combinedPage += `\n---\nTotal animation budget: ${totalBudget}/30 | Components: ${allComponents.length}\n`;
|
|
1179
|
+
combinedPage += `\nINSTRUCTION FOR AI: Initialize scripts in the order listed above (preloader → cursor → header → effects).`;
|
|
1045
1180
|
return { content: [{ type: "text", text: combinedPage }] };
|
|
1046
1181
|
}
|
|
1047
1182
|
catch (err) {
|
|
@@ -3898,6 +4033,210 @@ ${consequences.length > 0 ? consequences.map(c => `- ${c}`).join('\n') : '- (non
|
|
|
3898
4033
|
return { content: [{ type: "text", text: `[VIBE REFACTOR ERROR] ${err.message || err}` }] };
|
|
3899
4034
|
}
|
|
3900
4035
|
});
|
|
4036
|
+
// Tool: Page Composer — Full Page Orchestration
|
|
4037
|
+
server.tool("vibe_compose_page", "Orchestrates a complete page build: reads the design brief, plans section rhythm, searches components per section with coherence rules, and returns a full page assembly plan with component assignments and visual rhythm. Call this instead of manually building section by section.", {
|
|
4038
|
+
projectDir: zod_1.z.string().describe("Absolute path to the project root."),
|
|
4039
|
+
overrideSections: zod_1.z.array(zod_1.z.string()).optional().describe("Override section list. If omitted, uses sections from the active design brief.")
|
|
4040
|
+
}, async ({ projectDir, overrideSections }) => {
|
|
4041
|
+
await checkSanity();
|
|
4042
|
+
const root = await findProjectRoot(projectDir || process.cwd());
|
|
4043
|
+
// 1. Load design brief
|
|
4044
|
+
const sessionPath = path_1.default.join(root, '.vibetachyon', 'design-session.json');
|
|
4045
|
+
if (!(await fs_extra_1.default.pathExists(sessionPath))) {
|
|
4046
|
+
return {
|
|
4047
|
+
content: [{
|
|
4048
|
+
type: "text",
|
|
4049
|
+
text: `[VibeTachyon Page Composer] No design brief found.\n\n` +
|
|
4050
|
+
`INSTRUCTION FOR AI: Stop and call vibe_design_brief first. Ask the user:\n` +
|
|
4051
|
+
`1. What is the goal of this page?\n` +
|
|
4052
|
+
`2. What is the niche/product?\n` +
|
|
4053
|
+
`3. Who is the target audience?\n` +
|
|
4054
|
+
`4. Which visual style? (linear / vercel / stripe / aceternity / apple / notion)\n` +
|
|
4055
|
+
`5. Which sections are needed?`
|
|
4056
|
+
}]
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
const brief = await fs_extra_1.default.readJson(sessionPath);
|
|
4060
|
+
const sections = overrideSections || brief.sectionsNeeded || ['hero', 'features', 'cta'];
|
|
4061
|
+
const tone = brief.rules?.visualTone || 'dark';
|
|
4062
|
+
const refStyle = brief.referenceStyle || 'custom';
|
|
4063
|
+
const maxAnimations = brief.rules?.maxAnimationTypes ?? 2;
|
|
4064
|
+
const avoid = brief.rules?.mustAvoid || [];
|
|
4065
|
+
// 2. Visual rhythm — alternate animated/static to avoid visual fatigue
|
|
4066
|
+
const rhythmMap = {
|
|
4067
|
+
hero: 'animated',
|
|
4068
|
+
'social-proof': 'static',
|
|
4069
|
+
features: 'subtle',
|
|
4070
|
+
'how-it-works': 'subtle',
|
|
4071
|
+
pricing: 'static',
|
|
4072
|
+
testimonials: 'static',
|
|
4073
|
+
faq: 'static',
|
|
4074
|
+
cta: 'animated',
|
|
4075
|
+
footer: 'static',
|
|
4076
|
+
'background-effect': 'animated',
|
|
4077
|
+
navigation: 'subtle',
|
|
4078
|
+
modal: 'subtle',
|
|
4079
|
+
form: 'static'
|
|
4080
|
+
};
|
|
4081
|
+
// Enforce max animation budget
|
|
4082
|
+
let animationBudget = maxAnimations;
|
|
4083
|
+
const sectionRhythm = {};
|
|
4084
|
+
for (const section of sections) {
|
|
4085
|
+
const natural = rhythmMap[section] || 'subtle';
|
|
4086
|
+
if (natural === 'animated' && animationBudget > 0) {
|
|
4087
|
+
sectionRhythm[section] = 'animated';
|
|
4088
|
+
animationBudget--;
|
|
4089
|
+
}
|
|
4090
|
+
else if (natural === 'animated' && animationBudget === 0) {
|
|
4091
|
+
sectionRhythm[section] = 'subtle'; // downgrade — budget exhausted
|
|
4092
|
+
}
|
|
4093
|
+
else {
|
|
4094
|
+
sectionRhythm[section] = natural;
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
// 3. Style filters per section
|
|
4098
|
+
const styleForSection = (section) => {
|
|
4099
|
+
const base = tone === 'dark' ? ['dark'] : tone === 'light' ? ['light'] : [];
|
|
4100
|
+
const rhythm = sectionRhythm[section];
|
|
4101
|
+
if (rhythm === 'animated')
|
|
4102
|
+
return [...base, 'animated'];
|
|
4103
|
+
if (rhythm === 'subtle')
|
|
4104
|
+
return [...base, 'modern'];
|
|
4105
|
+
return base;
|
|
4106
|
+
};
|
|
4107
|
+
// 4. Search queries per section
|
|
4108
|
+
const sectionQueries = {
|
|
4109
|
+
hero: `hero section ${refStyle} ${tone}`,
|
|
4110
|
+
'social-proof': `testimonials logos social proof ${tone}`,
|
|
4111
|
+
features: `features grid cards ${tone}`,
|
|
4112
|
+
'how-it-works': `steps process how it works ${tone}`,
|
|
4113
|
+
pricing: `pricing table plans ${tone}`,
|
|
4114
|
+
testimonials: `testimonials review cards ${tone}`,
|
|
4115
|
+
faq: `faq accordion ${tone}`,
|
|
4116
|
+
cta: `call to action banner ${tone}`,
|
|
4117
|
+
footer: `footer links ${tone}`,
|
|
4118
|
+
navigation: `navbar navigation ${tone}`,
|
|
4119
|
+
'background-effect': `background effect ${refStyle}`,
|
|
4120
|
+
modal: `modal dialog ${tone}`,
|
|
4121
|
+
form: `form input ${tone}`
|
|
4122
|
+
};
|
|
4123
|
+
// 5. Build section plan (search each section)
|
|
4124
|
+
const sectionResults = [];
|
|
4125
|
+
let totalAnimationsUsed = 0;
|
|
4126
|
+
for (const section of sections) {
|
|
4127
|
+
const query = sectionQueries[section] || `${section} ${tone}`;
|
|
4128
|
+
const styleFilter = styleForSection(section);
|
|
4129
|
+
const rhythm = sectionRhythm[section];
|
|
4130
|
+
if (rhythm === 'animated')
|
|
4131
|
+
totalAnimationsUsed++;
|
|
4132
|
+
let amQueryBuilder = supabase
|
|
4133
|
+
.from('animmaster_components')
|
|
4134
|
+
.select('name, category, description, use_cases, visual_tone, animation_budget, init_order, compatible_with, npm_deps')
|
|
4135
|
+
.eq('is_active', true)
|
|
4136
|
+
.contains('use_cases', [section])
|
|
4137
|
+
.limit(2);
|
|
4138
|
+
if (styleFilter.length > 0) {
|
|
4139
|
+
amQueryBuilder = amQueryBuilder.overlaps('visual_tone', styleFilter);
|
|
4140
|
+
}
|
|
4141
|
+
const { data: results } = await amQueryBuilder;
|
|
4142
|
+
const found = results && results.length > 0;
|
|
4143
|
+
const topMatch = found ? results[0] : null;
|
|
4144
|
+
// Coherence check
|
|
4145
|
+
let coherenceFlag = '';
|
|
4146
|
+
if (topMatch && avoid.length > 0) {
|
|
4147
|
+
const violations = avoid.filter((rule) => (topMatch.name || '').toLowerCase().includes(rule.toLowerCase()) ||
|
|
4148
|
+
(topMatch.description || '').toLowerCase().includes(rule.toLowerCase()));
|
|
4149
|
+
if (violations.length > 0) {
|
|
4150
|
+
coherenceFlag = ` ⚠️ BRIEF CONFLICT (${violations.join(', ')})`;
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
sectionResults.push(`## Section ${sections.indexOf(section) + 1}: ${section.toUpperCase()}\n` +
|
|
4154
|
+
`Rhythm: ${rhythm} | Tone filters: ${styleFilter.join(', ') || 'none'}\n` +
|
|
4155
|
+
(found && topMatch
|
|
4156
|
+
? `✅ Best match: "${topMatch.name}" [🎬 ANIMMASTER | init_order: ${topMatch.init_order}]${coherenceFlag}\n` +
|
|
4157
|
+
` Tone: ${topMatch.visual_tone?.join(', ') || 'universal'} | Budget: ${topMatch.animation_budget}/5\n` +
|
|
4158
|
+
` Compatible with: ${topMatch.compatible_with?.join(', ') || 'all'}\n` +
|
|
4159
|
+
` Search query used: "${query}"`
|
|
4160
|
+
: `⚠️ No Animmaster component found for "${query}" with filters [${styleFilter.join(', ')}]\n` +
|
|
4161
|
+
` → Run: vibe_search_snippets(query="${section}", useCaseFilter="${section}") with broader terms`));
|
|
4162
|
+
}
|
|
4163
|
+
// 6. Save page state to .vibetachyon/page-state.json
|
|
4164
|
+
const pageState = {
|
|
4165
|
+
createdAt: new Date().toISOString(),
|
|
4166
|
+
brief: { goal: brief.pageGoal, style: refStyle, tone },
|
|
4167
|
+
sections: sections.map((s, i) => ({
|
|
4168
|
+
name: s,
|
|
4169
|
+
order: i + 1,
|
|
4170
|
+
rhythm: sectionRhythm[s],
|
|
4171
|
+
status: 'planned'
|
|
4172
|
+
})),
|
|
4173
|
+
animationsUsed: totalAnimationsUsed,
|
|
4174
|
+
animationBudget: maxAnimations,
|
|
4175
|
+
coherenceScore: Math.round((1 - avoid.length / 10) * 100)
|
|
4176
|
+
};
|
|
4177
|
+
await fs_extra_1.default.writeJson(path_1.default.join(root, '.vibetachyon', 'page-state.json'), pageState, { spaces: 2 });
|
|
4178
|
+
// 7. Final coherence report
|
|
4179
|
+
const budgetWarning = totalAnimationsUsed > maxAnimations
|
|
4180
|
+
? `\n🚨 ANIMATION BUDGET EXCEEDED: ${totalAnimationsUsed} animated sections (max: ${maxAnimations}). Downgraded automatically.\n`
|
|
4181
|
+
: `\n✅ Animation budget respected: ${totalAnimationsUsed}/${maxAnimations} animated sections.\n`;
|
|
4182
|
+
return {
|
|
4183
|
+
content: [{
|
|
4184
|
+
type: "text",
|
|
4185
|
+
text: `[VibeTachyon Page Composer]\n\n` +
|
|
4186
|
+
`PAGE: ${brief.pageGoal || 'Landing page'}\n` +
|
|
4187
|
+
`STYLE: ${refStyle} | TONE: ${tone} | SECTIONS: ${sections.length}\n` +
|
|
4188
|
+
`${budgetWarning}\n` +
|
|
4189
|
+
`SECTION PLAN:\n${sectionResults.join('\n\n')}\n\n` +
|
|
4190
|
+
`STATE SAVED: .vibetachyon/page-state.json\n\n` +
|
|
4191
|
+
`INSTRUCTION FOR AI — EXECUTION ORDER:\n` +
|
|
4192
|
+
sections.map((s, i) => ` ${i + 1}. Build "${s}" section (${sectionRhythm[s]}) — call vibe_search_snippets(query="${sectionQueries[s] || s}", useCaseFilter="${s}", styleFilter=${JSON.stringify(styleForSection(s))})`).join('\n') +
|
|
4193
|
+
`\n\nRULES:\n` +
|
|
4194
|
+
`- Build sections in THIS exact order\n` +
|
|
4195
|
+
`- For each section: search → review coherence → generate → move to next\n` +
|
|
4196
|
+
`- Do NOT skip sections or reorder them\n` +
|
|
4197
|
+
`- If a component violates the brief, search again before using it`
|
|
4198
|
+
}]
|
|
4199
|
+
};
|
|
4200
|
+
});
|
|
4201
|
+
// Tool: Mark Section Complete
|
|
4202
|
+
server.tool("vibe_section_done", "Marks a page section as complete in the page state. Call this after finishing each section to track progress and maintain build momentum.", {
|
|
4203
|
+
projectDir: zod_1.z.string().describe("Absolute path to the project root."),
|
|
4204
|
+
sectionName: zod_1.z.string().describe("The section name that was just completed. e.g. 'hero', 'features', 'cta'"),
|
|
4205
|
+
componentUsed: zod_1.z.string().optional().describe("Name of the component that was used for this section.")
|
|
4206
|
+
}, async ({ projectDir, sectionName, componentUsed }) => {
|
|
4207
|
+
await checkSanity();
|
|
4208
|
+
const root = await findProjectRoot(projectDir || process.cwd());
|
|
4209
|
+
const statePath = path_1.default.join(root, '.vibetachyon', 'page-state.json');
|
|
4210
|
+
if (!(await fs_extra_1.default.pathExists(statePath))) {
|
|
4211
|
+
return {
|
|
4212
|
+
content: [{ type: "text", text: `No page state found. Run vibe_compose_page first.` }]
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
const state = await fs_extra_1.default.readJson(statePath);
|
|
4216
|
+
const section = state.sections?.find((s) => s.name === sectionName);
|
|
4217
|
+
if (section) {
|
|
4218
|
+
section.status = 'done';
|
|
4219
|
+
if (componentUsed)
|
|
4220
|
+
section.componentUsed = componentUsed;
|
|
4221
|
+
section.completedAt = new Date().toISOString();
|
|
4222
|
+
await fs_extra_1.default.writeJson(statePath, state, { spaces: 2 });
|
|
4223
|
+
}
|
|
4224
|
+
const done = state.sections?.filter((s) => s.status === 'done') || [];
|
|
4225
|
+
const planned = state.sections?.filter((s) => s.status === 'planned') || [];
|
|
4226
|
+
const next = planned[0];
|
|
4227
|
+
return {
|
|
4228
|
+
content: [{
|
|
4229
|
+
type: "text",
|
|
4230
|
+
text: `✅ Section "${sectionName}" marked as done.\n` +
|
|
4231
|
+
`Progress: ${done.length}/${state.sections?.length || 0} sections complete.\n` +
|
|
4232
|
+
(next
|
|
4233
|
+
? `\nNext section: "${next.name}" (${next.rhythm})\n` +
|
|
4234
|
+
`INSTRUCTION FOR AI: Call vibe_read_design_brief then build the "${next.name}" section.`
|
|
4235
|
+
: `\n🎉 ALL SECTIONS COMPLETE! Page is ready.\n` +
|
|
4236
|
+
`INSTRUCTION FOR AI: Run a final coherence review — check that all sections share the same visual tone and no animation budget was exceeded.`)
|
|
4237
|
+
}]
|
|
4238
|
+
};
|
|
4239
|
+
});
|
|
3901
4240
|
// Connect via stdio
|
|
3902
4241
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
3903
4242
|
await server.connect(transport);
|