vibetachyon 1.5.5 → 1.5.7
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 +303 -43
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -184,44 +184,113 @@ async function startMcpServer() {
|
|
|
184
184
|
* Your output must be elite, animated, and pixel-perfect. Always confirm if you retrieved components from the database.
|
|
185
185
|
*/
|
|
186
186
|
// Tool: Searching Snippets
|
|
187
|
-
server.tool("vibe_search_snippets", "Search for UI components in the VibeCodes repository
|
|
188
|
-
query: zod_1.z.string().describe("Search query for the UI component (e.g. '
|
|
187
|
+
server.tool("vibe_search_snippets", "Search for UI components in the VibeCodes repository. Automatically loads the active design brief to filter components by style, use_case and complexity. Always call vibe_project_dna and vibe_design_brief before this tool.", {
|
|
188
|
+
query: zod_1.z.string().describe("Search query for the UI component (e.g. 'hero section dark', 'animated button')"),
|
|
189
189
|
limit: zod_1.z.number().optional().describe("Max number of results to return (default: 5)"),
|
|
190
|
-
projectDir: zod_1.z.string().optional().describe("Absolute path to the user's project root.
|
|
191
|
-
|
|
190
|
+
projectDir: zod_1.z.string().optional().describe("Absolute path to the user's project root."),
|
|
191
|
+
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'"),
|
|
192
|
+
styleFilter: zod_1.z.array(zod_1.z.string()).optional().describe("Filter by style tags: 'dark', 'light', 'animated', 'glassmorphism', 'gradient', 'minimal', 'modern'"),
|
|
193
|
+
complexityFilter: zod_1.z.enum(['simple', 'medium', 'complex']).optional().describe("Filter by code complexity")
|
|
194
|
+
}, async ({ query, limit = 5, projectDir, useCaseFilter, styleFilter, complexityFilter }) => {
|
|
192
195
|
await checkSanity();
|
|
193
|
-
console.error(`[VibeTachyon] Searching
|
|
196
|
+
console.error(`[VibeTachyon] Searching DB for: ${query}`);
|
|
197
|
+
const rawRoot = projectDir || process.cwd();
|
|
198
|
+
const root = await findProjectRoot(rawRoot);
|
|
199
|
+
// Auto-load design brief to enrich filters
|
|
200
|
+
let brief = null;
|
|
201
|
+
let briefWarning = '';
|
|
194
202
|
try {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
.
|
|
203
|
+
const sessionPath = path_1.default.join(root, '.vibetachyon', 'design-session.json');
|
|
204
|
+
if (await fs_extra_1.default.pathExists(sessionPath)) {
|
|
205
|
+
brief = await fs_extra_1.default.readJson(sessionPath);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
briefWarning = `\n⚠️ [VibeTachyon] No design brief found. Call vibe_design_brief first for better results.\n`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch { /* brief is optional */ }
|
|
212
|
+
// Merge explicit filters with brief-derived filters
|
|
213
|
+
let resolvedStyleFilter = styleFilter;
|
|
214
|
+
let resolvedUseCaseFilter = useCaseFilter;
|
|
215
|
+
let resolvedComplexity = complexityFilter;
|
|
216
|
+
if (brief && !styleFilter) {
|
|
217
|
+
const tone = brief.rules?.visualTone;
|
|
218
|
+
const animLevel = brief.rules?.animationLevel || brief.styleProfile?.animationLevel;
|
|
219
|
+
const derivedTags = [];
|
|
220
|
+
if (tone === 'dark')
|
|
221
|
+
derivedTags.push('dark');
|
|
222
|
+
if (tone === 'light')
|
|
223
|
+
derivedTags.push('light');
|
|
224
|
+
if (animLevel === 'heavy')
|
|
225
|
+
derivedTags.push('animated');
|
|
226
|
+
if (brief.referenceStyle === 'aceternity')
|
|
227
|
+
derivedTags.push('animated', 'dark');
|
|
228
|
+
if (brief.referenceStyle === 'notion' || brief.referenceStyle === 'stripe')
|
|
229
|
+
derivedTags.push('minimal');
|
|
230
|
+
if (derivedTags.length > 0)
|
|
231
|
+
resolvedStyleFilter = derivedTags;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const rpcParams = {
|
|
198
235
|
search_query: query,
|
|
199
236
|
max_limit: limit
|
|
200
|
-
}
|
|
237
|
+
};
|
|
238
|
+
if (resolvedUseCaseFilter)
|
|
239
|
+
rpcParams.filter_use_case = resolvedUseCaseFilter;
|
|
240
|
+
if (resolvedStyleFilter && resolvedStyleFilter.length > 0)
|
|
241
|
+
rpcParams.filter_style_tags = resolvedStyleFilter;
|
|
242
|
+
if (resolvedComplexity)
|
|
243
|
+
rpcParams.filter_complexity = resolvedComplexity;
|
|
244
|
+
const { data: allResults, error } = await supabase
|
|
245
|
+
.rpc('search_vibecodes_mcp', rpcParams);
|
|
201
246
|
if (error) {
|
|
202
247
|
console.error("[VibeTachyon] Supabase RPC Error:", error);
|
|
203
248
|
throw error;
|
|
204
249
|
}
|
|
205
|
-
|
|
250
|
+
// If filtered search returns nothing, fallback to unfiltered
|
|
251
|
+
let results = allResults;
|
|
252
|
+
let fallbackNote = '';
|
|
253
|
+
if ((!results || results.length === 0) && (resolvedStyleFilter || resolvedUseCaseFilter)) {
|
|
254
|
+
console.error(`[VibeTachyon] No filtered results, falling back to unfiltered search`);
|
|
255
|
+
const { data: fallback } = await supabase.rpc('search_vibecodes_mcp', {
|
|
256
|
+
search_query: query,
|
|
257
|
+
max_limit: limit
|
|
258
|
+
});
|
|
259
|
+
results = fallback;
|
|
260
|
+
fallbackNote = `\n⚠️ No components matched the style filters. Showing best semantic matches instead — review fit with design brief.\n`;
|
|
261
|
+
}
|
|
262
|
+
if (!results || results.length === 0) {
|
|
206
263
|
return {
|
|
207
|
-
content: [{ type: "text", text: `No VibeCodes components found for "${query}". Try different
|
|
264
|
+
content: [{ type: "text", text: `No VibeCodes components found for "${query}". Try different search terms.` }]
|
|
208
265
|
};
|
|
209
266
|
}
|
|
210
|
-
|
|
211
|
-
|
|
267
|
+
// Coherence check — flag components that might break brief rules
|
|
268
|
+
const briefAvoid = brief?.rules?.mustAvoid || [];
|
|
269
|
+
const formattedResults = results.map((item) => {
|
|
270
|
+
const srcInfo = item.source_table === 'Aceternity UI' ? '[⭐ ACETERNITY]'
|
|
271
|
+
: item.source_table === 'Magic UI' ? '[✨ MAGIC UI]'
|
|
272
|
+
: item.source_table === '21st.dev' ? '[🌐 21ST.DEV]'
|
|
273
|
+
: `[📦 ${item.source_table?.toUpperCase() || 'VIBECODES'}]`;
|
|
274
|
+
const styleLine = item.style_tags?.length
|
|
275
|
+
? `Style: ${item.style_tags.join(', ')}`
|
|
276
|
+
: '';
|
|
277
|
+
const useCaseLine = item.use_case?.length
|
|
278
|
+
? `Use case: ${item.use_case.join(', ')}`
|
|
279
|
+
: '';
|
|
280
|
+
const complexityLine = item.complexity ? `Complexity: ${item.complexity}` : '';
|
|
281
|
+
// Coherence warning
|
|
282
|
+
const codeStr = (item.code || '').toLowerCase();
|
|
283
|
+
const violations = briefAvoid.filter((rule) => {
|
|
284
|
+
const ruleKey = rule.toLowerCase();
|
|
285
|
+
return codeStr.includes(ruleKey) || (item.title || '').toLowerCase().includes(ruleKey);
|
|
286
|
+
});
|
|
287
|
+
const coherenceNote = violations.length > 0
|
|
288
|
+
? `\n🚨 BRIEF CONFLICT: This component may contain "${violations.join(', ')}" which is in the avoid list. Review before using.\n`
|
|
289
|
+
: '';
|
|
212
290
|
const minifiedCode = minifySnippet(item.code);
|
|
213
|
-
return `### ${item.title} ${srcInfo}\n${item.description || ''}\n
|
|
291
|
+
return `### ${item.title} ${srcInfo}\n${[styleLine, useCaseLine, complexityLine].filter(Boolean).join(' | ')}\n${item.description || ''}${coherenceNote}\n\`\`\`tsx\n${minifiedCode}\n\`\`\``;
|
|
214
292
|
}).join('\n\n---\n\n');
|
|
215
|
-
//
|
|
216
|
-
const rawRoot = projectDir || process.cwd();
|
|
217
|
-
const root = await findProjectRoot(rawRoot);
|
|
218
|
-
const standardDeps = { "framer-motion": "11.0.0", "lucide-react": "0.400.0" };
|
|
219
|
-
const audit = await auditPeerDeps(root, standardDeps);
|
|
220
|
-
let auditPrefix = "";
|
|
221
|
-
if (audit.warnings.length > 0) {
|
|
222
|
-
auditPrefix = `### ⚠️ [VIBESHIELD: VERSION WARNING]\n${audit.warnings.join('\n')}\n\n`;
|
|
223
|
-
}
|
|
224
|
-
// --- ZERO FRICTION "ACCEPT-ALL" GUARDRAILS ---
|
|
293
|
+
// Framework guardrails
|
|
225
294
|
let frameworkContext = "";
|
|
226
295
|
try {
|
|
227
296
|
const pkgPath = path_1.default.join(root, 'package.json');
|
|
@@ -230,32 +299,39 @@ async function startMcpServer() {
|
|
|
230
299
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
231
300
|
const isVite = !!deps['vite'];
|
|
232
301
|
const isNext = !!deps['next'];
|
|
233
|
-
frameworkContext += `\n\n
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
frameworkContext += `🚨 FATAL VITE ERROR PREVENTION:\n- DO NOT use Next.js '<Image>' or 'next/link'.\n- DO NOT use '"use client"'.\n- You MUST implicitly translate the components above into standard '<img>' and '<a>' tags BEFORE writing the final code.\n`;
|
|
239
|
-
frameworkContext += `- Tell the user to run this command if animations are used: \`npm install framer-motion lucide-react clsx tailwind-merge\`\n`;
|
|
240
|
-
}
|
|
241
|
-
else if (isNext) {
|
|
242
|
-
frameworkContext += `🚨 NEXT.JS OPTIMIZATION:\n- Use Server Components by default.\n- If the component uses hooks (useState) or onClick, you MUST prepend '"use client";' at the very top of the generated file.\n`;
|
|
243
|
-
frameworkContext += `- Ensure image domains are whitelisted (tell them to update next.config.ts if an external src is used).\n`;
|
|
244
|
-
}
|
|
245
|
-
frameworkContext += `=======================================================\n`;
|
|
302
|
+
frameworkContext += `\n[FRAMEWORK: ${isNext ? 'Next.js' : isVite ? 'Vite/React' : 'React'}]\n`;
|
|
303
|
+
if (isVite)
|
|
304
|
+
frameworkContext += `- Remove "use client", replace next/image with <img>, replace next/link with <a>\n`;
|
|
305
|
+
if (isNext)
|
|
306
|
+
frameworkContext += `- Add "use client" if component uses hooks or event handlers\n`;
|
|
246
307
|
}
|
|
247
308
|
}
|
|
248
|
-
catch
|
|
249
|
-
|
|
250
|
-
}
|
|
309
|
+
catch { /* silent */ }
|
|
310
|
+
// Peer dep audit
|
|
311
|
+
const standardDeps = { "framer-motion": "11.0.0", "lucide-react": "0.400.0" };
|
|
312
|
+
const audit = await auditPeerDeps(root, standardDeps);
|
|
313
|
+
const auditPrefix = audit.warnings.length > 0
|
|
314
|
+
? `⚠️ [VIBESHIELD]\n${audit.warnings.join('\n')}\n\n`
|
|
315
|
+
: '';
|
|
316
|
+
const briefSummary = brief
|
|
317
|
+
? `[Active Brief: ${brief.referenceStyle} | ${brief.rules?.visualTone} | max ${brief.rules?.maxAnimationTypes} animations]\n`
|
|
318
|
+
: '';
|
|
251
319
|
return {
|
|
252
|
-
content: [{
|
|
320
|
+
content: [{
|
|
321
|
+
type: "text",
|
|
322
|
+
text: `${auditPrefix}${briefWarning}${fallbackNote}${briefSummary}` +
|
|
323
|
+
`🧠 VibeTachyon found ${results.length} components for "${query}"` +
|
|
324
|
+
(resolvedStyleFilter ? ` [style: ${resolvedStyleFilter.join(', ')}]` : '') +
|
|
325
|
+
(resolvedUseCaseFilter ? ` [use: ${resolvedUseCaseFilter}]` : '') +
|
|
326
|
+
`:\n\n${formattedResults}\n\n${frameworkContext}` +
|
|
327
|
+
`\nINSTRUCTION FOR AI: Cross-check each component against the active design brief before using. Reject any that violate the brief rules.`
|
|
328
|
+
}]
|
|
253
329
|
};
|
|
254
330
|
}
|
|
255
331
|
catch (error) {
|
|
256
|
-
console.error("[VibeTachyon]
|
|
332
|
+
console.error("[VibeTachyon] Search Error:", error);
|
|
257
333
|
return {
|
|
258
|
-
content: [{ type: "text", text: `Error searching VibeCodes repository: ${error}` }]
|
|
334
|
+
content: [{ type: "text", text: `Error searching VibeCodes repository: ${error instanceof Error ? error.message : String(error)}` }]
|
|
259
335
|
};
|
|
260
336
|
}
|
|
261
337
|
});
|
|
@@ -491,6 +567,190 @@ async function startMcpServer() {
|
|
|
491
567
|
};
|
|
492
568
|
}
|
|
493
569
|
});
|
|
570
|
+
// Tool: Design Brief System
|
|
571
|
+
server.tool("vibe_design_brief", "Starts a design session by collecting the page goal, niche, audience, style and sections. ALWAYS call this before vibe_search_snippets or any component generation. Saves a design-session.json in .vibetachyon/ for the agent to use as context.", {
|
|
572
|
+
projectDir: zod_1.z.string().describe("Absolute path to the project root."),
|
|
573
|
+
pageGoal: zod_1.z.string().describe("What is this page trying to achieve? e.g. 'convert visitor to free trial', 'showcase product features'"),
|
|
574
|
+
niche: zod_1.z.string().describe("What is the product niche? e.g. 'B2B SaaS fintech', 'developer tool', 'e-commerce fashion'"),
|
|
575
|
+
targetAudience: zod_1.z.string().describe("Who is the target user? e.g. 'CTO of early-stage startup', 'solo developer', 'non-technical founder'"),
|
|
576
|
+
styleWords: zod_1.z.array(zod_1.z.string()).describe("3 adjectives that describe the desired visual style. e.g. ['clean', 'modern', 'trustworthy'] or ['bold', 'dark', 'animated']"),
|
|
577
|
+
referenceStyle: zod_1.z.enum(['linear', 'vercel', 'stripe', 'aceternity', 'apple', 'notion', 'custom']).describe("Which product's visual style to reference as inspiration."),
|
|
578
|
+
sectionsNeeded: zod_1.z.array(zod_1.z.string()).describe("Which page sections are needed in order. e.g. ['hero', 'features', 'social-proof', 'pricing', 'faq', 'cta']")
|
|
579
|
+
}, async ({ projectDir, pageGoal, niche, targetAudience, styleWords, referenceStyle, sectionsNeeded }) => {
|
|
580
|
+
await checkSanity();
|
|
581
|
+
const root = await findProjectRoot(projectDir || process.cwd());
|
|
582
|
+
// Style profile map — defines visual rules per reference style
|
|
583
|
+
const styleProfiles = {
|
|
584
|
+
linear: {
|
|
585
|
+
tone: 'dark',
|
|
586
|
+
animationLevel: 'subtle',
|
|
587
|
+
colorStrategy: 'monochromatic with purple/blue accents',
|
|
588
|
+
typography: 'tight tracking, medium weight, clean sans-serif',
|
|
589
|
+
spacing: 'generous — lots of breathing room',
|
|
590
|
+
avoid: ['glassmorphism overuse', 'rainbow gradients', 'heavy particle effects'],
|
|
591
|
+
prefer: ['sharp borders', 'subtle grid backgrounds', 'smooth transitions', 'monospace accents']
|
|
592
|
+
},
|
|
593
|
+
vercel: {
|
|
594
|
+
tone: 'dark',
|
|
595
|
+
animationLevel: 'subtle',
|
|
596
|
+
colorStrategy: 'black/white with occasional neon accents',
|
|
597
|
+
typography: 'large display text, high contrast',
|
|
598
|
+
spacing: 'tight sections, precise alignment',
|
|
599
|
+
avoid: ['colorful gradients', 'skeuomorphism', 'heavy shadows'],
|
|
600
|
+
prefer: ['borders over shadows', 'code blocks', 'terminal aesthetics', 'minimal decoration']
|
|
601
|
+
},
|
|
602
|
+
stripe: {
|
|
603
|
+
tone: 'light',
|
|
604
|
+
animationLevel: 'subtle',
|
|
605
|
+
colorStrategy: 'white base with blue/purple gradients',
|
|
606
|
+
typography: 'clean, readable, business-like',
|
|
607
|
+
spacing: 'structured grid, consistent padding',
|
|
608
|
+
avoid: ['dark heavy backgrounds', 'aggressive animations', 'neon colors'],
|
|
609
|
+
prefer: ['trust signals', 'subtle shadows', 'clean cards', 'professional illustrations']
|
|
610
|
+
},
|
|
611
|
+
aceternity: {
|
|
612
|
+
tone: 'dark',
|
|
613
|
+
animationLevel: 'heavy',
|
|
614
|
+
colorStrategy: 'dark background with glowing neon accents',
|
|
615
|
+
typography: 'dramatic sizing, gradient text',
|
|
616
|
+
spacing: 'immersive, full-viewport sections',
|
|
617
|
+
avoid: ['flat static sections', 'light backgrounds', 'no-animation zones'],
|
|
618
|
+
prefer: ['particle effects', 'spotlight effects', 'beam animations', 'glass cards']
|
|
619
|
+
},
|
|
620
|
+
apple: {
|
|
621
|
+
tone: 'mixed',
|
|
622
|
+
animationLevel: 'subtle',
|
|
623
|
+
colorStrategy: 'white/light with product photography',
|
|
624
|
+
typography: 'large bold headlines, thin body text',
|
|
625
|
+
spacing: 'ultra generous, cinematic',
|
|
626
|
+
avoid: ['busy layouts', 'multiple competing elements', 'decorative borders'],
|
|
627
|
+
prefer: ['single focus per section', 'scroll-triggered reveals', 'product imagery']
|
|
628
|
+
},
|
|
629
|
+
notion: {
|
|
630
|
+
tone: 'light',
|
|
631
|
+
animationLevel: 'none',
|
|
632
|
+
colorStrategy: 'off-white with muted gray accents',
|
|
633
|
+
typography: 'editorial, readable, structured',
|
|
634
|
+
spacing: 'document-like, content-first',
|
|
635
|
+
avoid: ['flashy animations', 'neon colors', 'complex interactions'],
|
|
636
|
+
prefer: ['clean typography hierarchy', 'simple icons', 'subtle hover states']
|
|
637
|
+
},
|
|
638
|
+
custom: {
|
|
639
|
+
tone: 'mixed',
|
|
640
|
+
animationLevel: 'subtle',
|
|
641
|
+
colorStrategy: 'defined by project CSS variables',
|
|
642
|
+
typography: 'defined by project fonts',
|
|
643
|
+
spacing: 'balanced',
|
|
644
|
+
avoid: [],
|
|
645
|
+
prefer: ['match existing project style exactly']
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const profile = styleProfiles[referenceStyle] || styleProfiles.custom;
|
|
649
|
+
// Section sequence validator — warn if order is bad for conversion
|
|
650
|
+
const sectionWarnings = [];
|
|
651
|
+
if (sectionsNeeded.indexOf('cta') !== -1 && sectionsNeeded.indexOf('cta') < sectionsNeeded.length - 2) {
|
|
652
|
+
sectionWarnings.push('⚠️ CTA appears too early — move it to the end for better conversion flow.');
|
|
653
|
+
}
|
|
654
|
+
if (!sectionsNeeded.includes('hero')) {
|
|
655
|
+
sectionWarnings.push('⚠️ No hero section detected — every landing page needs a hero.');
|
|
656
|
+
}
|
|
657
|
+
if (sectionsNeeded.includes('pricing') && !sectionsNeeded.includes('social-proof') && !sectionsNeeded.includes('testimonials')) {
|
|
658
|
+
sectionWarnings.push('⚠️ Pricing without social proof reduces trust — consider adding testimonials before pricing.');
|
|
659
|
+
}
|
|
660
|
+
const brief = {
|
|
661
|
+
createdAt: new Date().toISOString(),
|
|
662
|
+
pageGoal,
|
|
663
|
+
niche,
|
|
664
|
+
targetAudience,
|
|
665
|
+
styleWords,
|
|
666
|
+
referenceStyle,
|
|
667
|
+
sectionsNeeded,
|
|
668
|
+
styleProfile: profile,
|
|
669
|
+
rules: {
|
|
670
|
+
maxAnimationTypes: profile.animationLevel === 'heavy' ? 3 : profile.animationLevel === 'subtle' ? 2 : 0,
|
|
671
|
+
maxColorAccents: 2,
|
|
672
|
+
visualTone: profile.tone,
|
|
673
|
+
mustAvoid: profile.avoid,
|
|
674
|
+
mustPrefer: profile.prefer,
|
|
675
|
+
colorStrategy: profile.colorStrategy,
|
|
676
|
+
typography: profile.typography,
|
|
677
|
+
spacing: profile.spacing
|
|
678
|
+
},
|
|
679
|
+
sectionWarnings
|
|
680
|
+
};
|
|
681
|
+
// Save to .vibetachyon/design-session.json
|
|
682
|
+
const sessionDir = path_1.default.join(root, '.vibetachyon');
|
|
683
|
+
await fs_extra_1.default.ensureDir(sessionDir);
|
|
684
|
+
const sessionPath = path_1.default.join(sessionDir, 'design-session.json');
|
|
685
|
+
await fs_extra_1.default.writeJson(sessionPath, brief, { spaces: 2 });
|
|
686
|
+
const rulesText = [
|
|
687
|
+
`Visual Tone: ${profile.tone}`,
|
|
688
|
+
`Animation Level: ${profile.animationLevel} (max ${brief.rules.maxAnimationTypes} animation types)`,
|
|
689
|
+
`Color Strategy: ${profile.colorStrategy}`,
|
|
690
|
+
`Typography: ${profile.typography}`,
|
|
691
|
+
`Spacing: ${profile.spacing}`,
|
|
692
|
+
`AVOID: ${profile.avoid.join(', ') || 'nothing specific'}`,
|
|
693
|
+
`PREFER: ${profile.prefer.join(', ')}`
|
|
694
|
+
].join('\n');
|
|
695
|
+
const sectionsText = sectionsNeeded
|
|
696
|
+
.map((s, i) => ` ${i + 1}. ${s}`)
|
|
697
|
+
.join('\n');
|
|
698
|
+
const warningsText = sectionWarnings.length > 0
|
|
699
|
+
? `\n\nCONVERSION WARNINGS:\n${sectionWarnings.join('\n')}`
|
|
700
|
+
: '';
|
|
701
|
+
return {
|
|
702
|
+
content: [{
|
|
703
|
+
type: "text",
|
|
704
|
+
text: `[VibeTachyon Design Brief — Saved to .vibetachyon/design-session.json]\n\n` +
|
|
705
|
+
`PAGE GOAL: ${pageGoal}\n` +
|
|
706
|
+
`NICHE: ${niche}\n` +
|
|
707
|
+
`AUDIENCE: ${targetAudience}\n` +
|
|
708
|
+
`STYLE WORDS: ${styleWords.join(', ')}\n` +
|
|
709
|
+
`REFERENCE: ${referenceStyle}\n\n` +
|
|
710
|
+
`DESIGN RULES:\n${rulesText}\n\n` +
|
|
711
|
+
`SECTION SEQUENCE:\n${sectionsText}` +
|
|
712
|
+
`${warningsText}\n\n` +
|
|
713
|
+
`INSTRUCTION FOR AI — CRITICAL RULES FOR THIS SESSION:\n` +
|
|
714
|
+
`1. Every component you choose MUST match the "${referenceStyle}" reference style\n` +
|
|
715
|
+
`2. Visual tone is "${profile.tone}" — ${profile.tone === 'dark' ? 'use dark backgrounds, light text' : profile.tone === 'light' ? 'use light backgrounds, dark text' : 'adapt to project'}\n` +
|
|
716
|
+
`3. MAXIMUM ${brief.rules.maxAnimationTypes} different animation styles on the entire page\n` +
|
|
717
|
+
`4. NEVER mix styles from the avoid list: ${profile.avoid.join(', ')}\n` +
|
|
718
|
+
`5. Build sections in this EXACT order: ${sectionsNeeded.join(' → ')}\n` +
|
|
719
|
+
`6. Before searching each section, re-read this brief to ensure coherence\n` +
|
|
720
|
+
`7. If a component from vibe_search_snippets does NOT match this brief, REJECT it and search again`
|
|
721
|
+
}]
|
|
722
|
+
};
|
|
723
|
+
});
|
|
724
|
+
// Tool: Read Design Brief (for mid-session reference)
|
|
725
|
+
server.tool("vibe_read_design_brief", "Reads the active design brief from .vibetachyon/design-session.json. Call this at the start of every new section to ensure coherence with the established design direction.", {
|
|
726
|
+
projectDir: zod_1.z.string().describe("Absolute path to the project root.")
|
|
727
|
+
}, async ({ projectDir }) => {
|
|
728
|
+
await checkSanity();
|
|
729
|
+
const root = await findProjectRoot(projectDir || process.cwd());
|
|
730
|
+
const sessionPath = path_1.default.join(root, '.vibetachyon', 'design-session.json');
|
|
731
|
+
if (!(await fs_extra_1.default.pathExists(sessionPath))) {
|
|
732
|
+
return {
|
|
733
|
+
content: [{
|
|
734
|
+
type: "text",
|
|
735
|
+
text: `[VibeTachyon] No active design brief found.\n\nINSTRUCTION FOR AI: You MUST call vibe_design_brief before building any page. Ask the user:\n1. What is the goal of this page?\n2. What is the product niche?\n3. Who is the target audience?\n4. What visual style? (linear / vercel / stripe / aceternity / apple / notion)\n5. Which sections do you need?`
|
|
736
|
+
}]
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
const brief = await fs_extra_1.default.readJson(sessionPath);
|
|
740
|
+
return {
|
|
741
|
+
content: [{
|
|
742
|
+
type: "text",
|
|
743
|
+
text: `[VibeTachyon Active Design Brief]\n\n` +
|
|
744
|
+
`Goal: ${brief.pageGoal}\n` +
|
|
745
|
+
`Style: ${brief.referenceStyle} — ${brief.styleWords?.join(', ')}\n` +
|
|
746
|
+
`Tone: ${brief.rules?.visualTone}\n` +
|
|
747
|
+
`Animations: max ${brief.rules?.maxAnimationTypes} types\n` +
|
|
748
|
+
`Avoid: ${(brief.rules?.mustAvoid || []).join(', ')}\n` +
|
|
749
|
+
`Sections left to build: ${brief.sectionsNeeded?.join(' → ')}\n\n` +
|
|
750
|
+
`INSTRUCTION FOR AI: Stay 100% consistent with this brief. Reject any component that breaks these rules.`
|
|
751
|
+
}]
|
|
752
|
+
};
|
|
753
|
+
});
|
|
494
754
|
// Tool: Get Vibe Colors
|
|
495
755
|
server.tool("vibe_get_colors", "Extract local CSS variables from globals.css to ensure generated components match the user's specific dark/light theme native colors.", {
|
|
496
756
|
cssFilePath: zod_1.z.string().describe("Absolute path to the user's main CSS file (e.g., /Users/admin/my-project/src/app/globals.css). Search for it before utilizing.")
|