vibetachyon 1.9.0 → 2.1.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.
Files changed (2) hide show
  1. package/dist/mcp-server.js +258 -15
  2. package/package.json +1 -1
@@ -47,8 +47,148 @@ async function checkSanity() {
47
47
  throw new Error(`[VIBETACHYON SANITY TRAP] Execution blocked. Agent exceeded ${CALL_LIMIT_PER_SESSION} tool calls in a single session. This prevents infinite loops or excessive API usage. Please restart the CLI if this was intentional.`);
48
48
  }
49
49
  }
50
+ // --- Style Palettes — real CSS values per preset ─────────────────────────
51
+ const STYLE_PALETTES = {
52
+ linear: {
53
+ '--background': '#0a0a0f',
54
+ '--foreground': '#f4f4f5',
55
+ '--primary': '#6366f1',
56
+ '--primary-fg': '#ffffff',
57
+ '--muted': '#1c1c28',
58
+ '--muted-fg': '#8585a8',
59
+ '--border': 'rgba(255,255,255,0.08)',
60
+ '--card': '#111120',
61
+ '--radius': '8px',
62
+ '--font': "'Inter', system-ui, sans-serif",
63
+ '--gradient': 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
64
+ '--shadow': '0 1px 3px rgba(0,0,0,0.4)',
65
+ },
66
+ vercel: {
67
+ '--background': '#000000',
68
+ '--foreground': '#ffffff',
69
+ '--primary': '#ffffff',
70
+ '--primary-fg': '#000000',
71
+ '--muted': '#111111',
72
+ '--muted-fg': '#888888',
73
+ '--border': '#333333',
74
+ '--card': '#0a0a0a',
75
+ '--radius': '4px',
76
+ '--font': "'Geist', 'Inter', system-ui, sans-serif",
77
+ '--gradient': 'none',
78
+ '--shadow': '0 0 0 1px #333',
79
+ },
80
+ stripe: {
81
+ '--background': '#ffffff',
82
+ '--foreground': '#0a2540',
83
+ '--primary': '#635bff',
84
+ '--primary-fg': '#ffffff',
85
+ '--muted': '#f6f9fc',
86
+ '--muted-fg': '#425466',
87
+ '--border': '#e3e8ee',
88
+ '--card': '#ffffff',
89
+ '--radius': '6px',
90
+ '--font': "'Sohne', 'Inter', system-ui, sans-serif",
91
+ '--gradient': 'linear-gradient(135deg, #635bff 0%, #0a2540 100%)',
92
+ '--shadow': '0 2px 8px rgba(0,0,0,0.08)',
93
+ },
94
+ notion: {
95
+ '--background': '#ffffff',
96
+ '--foreground': '#37352f',
97
+ '--primary': '#2eaadc',
98
+ '--primary-fg': '#ffffff',
99
+ '--muted': '#f7f6f3',
100
+ '--muted-fg': '#9b9a97',
101
+ '--border': '#e9e9e7',
102
+ '--card': '#ffffff',
103
+ '--radius': '3px',
104
+ '--font': "'ui-sans-serif', 'Inter', system-ui, sans-serif",
105
+ '--gradient': 'none',
106
+ '--shadow': '0 1px 3px rgba(0,0,0,0.07)',
107
+ },
108
+ apple: {
109
+ '--background': '#f5f5f7',
110
+ '--foreground': '#1d1d1f',
111
+ '--primary': '#0066cc',
112
+ '--primary-fg': '#ffffff',
113
+ '--muted': '#e8e8ed',
114
+ '--muted-fg': '#6e6e73',
115
+ '--border': 'rgba(0,0,0,0.1)',
116
+ '--card': '#ffffff',
117
+ '--radius': '12px',
118
+ '--font': "'-apple-system', 'SF Pro Display', 'Inter', sans-serif",
119
+ '--gradient': 'linear-gradient(180deg, #f5f5f7 0%, #e8e8ed 100%)',
120
+ '--shadow': '0 4px 20px rgba(0,0,0,0.08)',
121
+ },
122
+ 'bold-dark': {
123
+ '--background': '#060b18',
124
+ '--foreground': '#e2e8f0',
125
+ '--primary': '#06b6d4',
126
+ '--primary-fg': '#000000',
127
+ '--muted': '#0f1d2e',
128
+ '--muted-fg': '#94a3b8',
129
+ '--border': 'rgba(6,182,212,0.15)',
130
+ '--card': '#0d1a2e',
131
+ '--radius': '10px',
132
+ '--font': "'Inter', system-ui, sans-serif",
133
+ '--gradient': 'linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%)',
134
+ '--shadow': '0 4px 24px rgba(6,182,212,0.15)',
135
+ },
136
+ agency: {
137
+ '--background': '#0f0f0f',
138
+ '--foreground': '#ffffff',
139
+ '--primary': '#ff6b35',
140
+ '--primary-fg': '#ffffff',
141
+ '--muted': '#1a1a1a',
142
+ '--muted-fg': '#999999',
143
+ '--border': 'rgba(255,107,53,0.2)',
144
+ '--card': '#1a1a1a',
145
+ '--radius': '2px',
146
+ '--font': "'Space Grotesk', 'Inter', system-ui, sans-serif",
147
+ '--gradient': 'linear-gradient(135deg, #ff6b35 0%, #f7c59f 100%)',
148
+ '--shadow': '0 4px 20px rgba(255,107,53,0.2)',
149
+ },
150
+ aceternity: {
151
+ '--background': '#000000',
152
+ '--foreground': '#ffffff',
153
+ '--primary': '#a855f7',
154
+ '--primary-fg': '#ffffff',
155
+ '--muted': '#0a0a0a',
156
+ '--muted-fg': '#71717a',
157
+ '--border': 'rgba(168,85,247,0.2)',
158
+ '--card': '#0a0a0a',
159
+ '--radius': '16px',
160
+ '--font': "'Inter', system-ui, sans-serif",
161
+ '--gradient': 'linear-gradient(135deg, #a855f7 0%, #6366f1 50%, #ec4899 100%)',
162
+ '--shadow': '0 0 40px rgba(168,85,247,0.25)',
163
+ },
164
+ custom: {
165
+ '--background': 'var(--background, #ffffff)',
166
+ '--foreground': 'var(--foreground, #000000)',
167
+ '--primary': 'var(--primary, #3b82f6)',
168
+ '--primary-fg': 'var(--primary-fg, #ffffff)',
169
+ '--muted': 'var(--muted, #f4f4f5)',
170
+ '--muted-fg': 'var(--muted-fg, #71717a)',
171
+ '--border': 'var(--border, #e4e4e7)',
172
+ '--card': 'var(--card, #ffffff)',
173
+ '--radius': 'var(--radius, 8px)',
174
+ '--font': "var(--font-sans, 'Inter', sans-serif)",
175
+ '--gradient': 'none',
176
+ '--shadow': '0 1px 3px rgba(0,0,0,0.1)',
177
+ },
178
+ };
50
179
  // --- Changelog (per version) ---
51
180
  const CHANGELOG = {
181
+ '2.1.0': [
182
+ 'Fix: vibe_compose_landing_page detecta framework (Next.js/Vite) automaticamente',
183
+ 'Fix: output sempre .tsx React, nunca index.html',
184
+ 'Instruções explícitas de conversão HTML→JSX (class→className, useEffect)',
185
+ 'Detecta src/app vs app para outputTarget correto',
186
+ ],
187
+ '2.0.0': [
188
+ '8 presets visuais reais: linear, vercel, stripe, bold-dark, agency...',
189
+ 'Paletas CSS aplicadas automaticamente — sem mais neon hardcoded',
190
+ 'vibe_compose_landing_page lê o brief e injeta cores corretas',
191
+ ],
52
192
  '1.9.0': [
53
193
  'vibe_clone_design — clone design system de qualquer URL',
54
194
  'Detecta cores, tipografia, radius e sombras automaticamente',
@@ -203,7 +343,7 @@ async function validateTokenAtStartup() {
203
343
  }
204
344
  // --- Update Check ---
205
345
  try {
206
- const CURRENT_VERSION = '1.9.0'; // keep in sync with package.json
346
+ const CURRENT_VERSION = '2.0.0'; // keep in sync with package.json
207
347
  const npmRes = await fetch('https://registry.npmjs.org/vibetachyon/latest', {
208
348
  signal: AbortSignal.timeout(4000)
209
349
  });
@@ -232,7 +372,7 @@ async function startMcpServer() {
232
372
  await validateTokenAtStartup();
233
373
  const server = new mcp_js_1.McpServer({
234
374
  name: "VibeTachyon MCP — Animmaster Engine",
235
- version: "1.9.0"
375
+ version: "2.0.0"
236
376
  });
237
377
  /**
238
378
  * VIBETACHYON FRONTEND PERSONA — Senior Frontend Designer (Animmaster Engine)
@@ -981,7 +1121,7 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
981
1121
  niche: zod_1.z.string().describe("What is the product niche? e.g. 'B2B SaaS fintech', 'developer tool', 'e-commerce fashion'"),
982
1122
  targetAudience: zod_1.z.string().describe("Who is the target user? e.g. 'CTO of early-stage startup', 'solo developer', 'non-technical founder'"),
983
1123
  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']"),
984
- referenceStyle: zod_1.z.enum(['linear', 'vercel', 'stripe', 'aceternity', 'apple', 'notion', 'custom']).describe("Which product's visual style to reference as inspiration."),
1124
+ referenceStyle: zod_1.z.enum(['linear', 'vercel', 'stripe', 'aceternity', 'apple', 'notion', 'bold-dark', 'agency', 'custom']).describe("Visual preset: 'linear'=dark minimal indigo, 'vercel'=black/white terminal, 'stripe'=light professional, 'notion'=clean editorial, 'apple'=light cinematic, 'bold-dark'=dark navy cyan, 'agency'=dark orange dramatic, 'aceternity'=dark glowing purple, 'custom'=use project CSS vars"),
985
1125
  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']")
986
1126
  }, async ({ projectDir, pageGoal, niche, targetAudience, styleWords, referenceStyle, sectionsNeeded }) => {
987
1127
  await checkSanity();
@@ -1064,6 +1204,10 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1064
1204
  if (sectionsNeeded.includes('pricing') && !sectionsNeeded.includes('social-proof') && !sectionsNeeded.includes('testimonials')) {
1065
1205
  sectionWarnings.push('⚠️ Pricing without social proof reduces trust — consider adding testimonials before pricing.');
1066
1206
  }
1207
+ const palette = STYLE_PALETTES[referenceStyle] || STYLE_PALETTES.custom;
1208
+ const paletteCSS = Object.entries(palette)
1209
+ .map(([k, v]) => ` ${k}: ${v};`)
1210
+ .join('\n');
1067
1211
  const brief = {
1068
1212
  createdAt: new Date().toISOString(),
1069
1213
  pageGoal,
@@ -1072,6 +1216,8 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1072
1216
  styleWords,
1073
1217
  referenceStyle,
1074
1218
  sectionsNeeded,
1219
+ palette,
1220
+ paletteCSS: `:root {\n${paletteCSS}\n}`,
1075
1221
  styleProfile: profile,
1076
1222
  rules: {
1077
1223
  maxAnimationTypes: profile.animationLevel === 'heavy' ? 3 : profile.animationLevel === 'subtle' ? 2 : 0,
@@ -1117,14 +1263,17 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1117
1263
  `DESIGN RULES:\n${rulesText}\n\n` +
1118
1264
  `SECTION SEQUENCE:\n${sectionsText}` +
1119
1265
  `${warningsText}\n\n` +
1266
+ `COLOR PALETTE (USE THESE EXACT VALUES — NO EXCEPTIONS):\n` +
1267
+ `\`\`\`css\n${brief.paletteCSS}\n\`\`\`\n\n` +
1120
1268
  `INSTRUCTION FOR AI — CRITICAL RULES FOR THIS SESSION:\n` +
1121
- `1. Every component you choose MUST match the "${referenceStyle}" reference style\n` +
1122
- `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` +
1123
- `3. MAXIMUM ${brief.rules.maxAnimationTypes} different animation styles on the entire page\n` +
1124
- `4. NEVER mix styles from the avoid list: ${profile.avoid.join(', ')}\n` +
1125
- `5. Build sections in this EXACT order: ${sectionsNeeded.join(' ')}\n` +
1126
- `6. Before searching each section, re-read this brief to ensure coherence\n` +
1127
- `7. If a component from vibe_search_snippets does NOT match this brief, REJECT it and search again`
1269
+ `1. ALWAYS inject the CSS palette above as the FIRST thing inside <style> when generating HTML\n` +
1270
+ `2. NEVER hardcode colors like #7c3aed, #a855f7, #ec4899 use ONLY the CSS vars above\n` +
1271
+ `3. Every component you choose MUST match the "${referenceStyle}" reference style\n` +
1272
+ `4. Visual tone is "${profile.tone}" ${profile.tone === 'dark' ? 'dark backgrounds, light text' : profile.tone === 'light' ? 'light backgrounds, dark text' : 'adapt to project'}\n` +
1273
+ `5. MAXIMUM ${brief.rules.maxAnimationTypes} different animation styles on the entire page\n` +
1274
+ `6. NEVER use these: ${profile.avoid.join(', ')}\n` +
1275
+ `7. Build sections in this EXACT order: ${sectionsNeeded.join(' ')}\n` +
1276
+ `8. If a component does NOT match this brief, REJECT it and search again`
1128
1277
  }]
1129
1278
  };
1130
1279
  });
@@ -1566,11 +1715,69 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1566
1715
  }
1567
1716
  });
1568
1717
  // Tool: Compose Landing Page
1569
- server.tool("vibe_compose_landing_page", "Macro-composer that autonomously queries the VibeCodes RAG database for a full Landing Page structure (Navbar, Hero, Features, Pricing, Footer).", {
1570
- themeOrIndustry: zod_1.z.string().describe("The theme, industry, or style of the page (e.g., 'SaaS Finance Dark Mode', 'Dental Clinic Clean')")
1571
- }, async ({ themeOrIndustry }) => {
1718
+ server.tool("vibe_compose_landing_page", "Macro-composer that autonomously queries the VibeCodes RAG database for a full Landing Page structure (Navbar, Hero, Features, Pricing, Footer). ALWAYS call vibe_design_brief first — this tool reads the saved brief to apply the correct color palette. OUTPUT: Always generates Next.js TSX React components (never index.html). For Next.js projects, saves to src/app/page.tsx or app/page.tsx. Convert all HTML templates to JSX (class→className, style={{}} etc).", {
1719
+ themeOrIndustry: zod_1.z.string().describe("The theme, industry, or style of the page (e.g., 'SaaS Finance', 'Dental Clinic', 'Developer Tool')"),
1720
+ projectDir: zod_1.z.string().optional().describe("Absolute path to the project root. Used to read the design brief.")
1721
+ }, async ({ themeOrIndustry, projectDir }) => {
1572
1722
  await checkSanity();
1573
1723
  try {
1724
+ // Read design brief for palette injection
1725
+ let paletteCSS = '';
1726
+ let briefStyle = 'linear';
1727
+ let briefTone = 'dark';
1728
+ let briefAvoid = [];
1729
+ const cwd = projectDir || process.cwd();
1730
+ const root = await findProjectRoot(cwd);
1731
+ // Detect framework for output instructions
1732
+ let framework = 'unknown';
1733
+ let outputTarget = '';
1734
+ const pkgPath = path_1.default.join(root, 'package.json');
1735
+ if (await fs_extra_1.default.pathExists(pkgPath)) {
1736
+ try {
1737
+ const pkg = await fs_extra_1.default.readJson(pkgPath);
1738
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1739
+ if (deps['next']) {
1740
+ framework = 'nextjs';
1741
+ const hasAppSrc = await fs_extra_1.default.pathExists(path_1.default.join(root, 'src', 'app'));
1742
+ const hasApp = await fs_extra_1.default.pathExists(path_1.default.join(root, 'app'));
1743
+ if (hasAppSrc)
1744
+ outputTarget = 'src/app/page.tsx';
1745
+ else if (hasApp)
1746
+ outputTarget = 'app/page.tsx';
1747
+ else
1748
+ outputTarget = 'src/app/page.tsx';
1749
+ }
1750
+ else if (deps['vite'] || deps['react']) {
1751
+ framework = 'vite';
1752
+ outputTarget = 'src/App.tsx';
1753
+ }
1754
+ else if (deps['nuxt']) {
1755
+ framework = 'nuxt';
1756
+ outputTarget = 'pages/index.vue';
1757
+ }
1758
+ }
1759
+ catch { /* ignore */ }
1760
+ }
1761
+ const briefPath = path_1.default.join(root, '.vibetachyon', 'design-session.json');
1762
+ if (await fs_extra_1.default.pathExists(briefPath)) {
1763
+ try {
1764
+ const brief = await fs_extra_1.default.readJson(briefPath);
1765
+ if (brief.paletteCSS)
1766
+ paletteCSS = brief.paletteCSS;
1767
+ if (brief.referenceStyle)
1768
+ briefStyle = brief.referenceStyle;
1769
+ if (brief.styleProfile?.tone)
1770
+ briefTone = brief.styleProfile.tone;
1771
+ if (brief.rules?.mustAvoid)
1772
+ briefAvoid = brief.rules.mustAvoid;
1773
+ }
1774
+ catch { /* use defaults */ }
1775
+ }
1776
+ // Fallback: use linear palette if no brief
1777
+ if (!paletteCSS) {
1778
+ const fallbackPalette = STYLE_PALETTES[briefStyle] || STYLE_PALETTES.linear;
1779
+ paletteCSS = `:root {\n${Object.entries(fallbackPalette).map(([k, v]) => ` ${k}: ${v};`).join('\n')}\n}`;
1780
+ }
1574
1781
  // Map sections to Animmaster use cases and search terms
1575
1782
  const sectionMap = [
1576
1783
  { section: 'preloader', query: 'preloader', useCase: 'preloader' },
@@ -1580,7 +1787,37 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1580
1787
  { section: 'features', query: 'features section', useCase: 'feature-section' },
1581
1788
  { section: 'footer', query: 'footer', useCase: 'footer' }
1582
1789
  ];
1583
- let combinedPage = `[🎬 ANIMMASTER PAGE COMPOSER: ${themeOrIndustry}]\n\nINSTRUCTION FOR AI: Assemble a complete page using Animmaster components in init_order sequence.\n\n`;
1790
+ let combinedPage = `[🎬 ANIMMASTER PAGE COMPOSER: ${themeOrIndustry}]\n`;
1791
+ combinedPage += `Style: ${briefStyle} | Tone: ${briefTone}\n\n`;
1792
+ // Framework-aware output instructions
1793
+ combinedPage += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
1794
+ combinedPage += `📁 OUTPUT TARGET: ${outputTarget || 'src/app/page.tsx'}\n`;
1795
+ combinedPage += `🚨 NEVER create index.html — this is a ${framework === 'nextjs' ? 'Next.js' : framework === 'vite' ? 'React/Vite' : 'React'} project\n`;
1796
+ combinedPage += `🚨 ALL output MUST be .tsx React components, NOT plain HTML files\n`;
1797
+ if (framework === 'nextjs') {
1798
+ combinedPage += `\n📋 NEXTJS TSX RULES:\n`;
1799
+ combinedPage += ` • Convert all HTML → JSX: class→className, for→htmlFor, style={{}}\n`;
1800
+ combinedPage += ` • Add "use client" directive at top if using animations/events\n`;
1801
+ combinedPage += ` • Inline scripts → useEffect(() => { /* JS here */ }, [])\n`;
1802
+ combinedPage += ` • External CDN scripts → use next/script: <Script src="..." strategy="afterInteractive" />\n`;
1803
+ combinedPage += ` • Self-closing tags: <img />, <br />, <input />\n`;
1804
+ combinedPage += ` • Create each section as a separate component in src/components/\n`;
1805
+ combinedPage += ` • Page file: export default function Page() { return (<main>...</main>) }\n`;
1806
+ }
1807
+ else if (framework === 'vite') {
1808
+ combinedPage += `\n📋 VITE/REACT TSX RULES:\n`;
1809
+ combinedPage += ` • Convert HTML → JSX: class→className, for→htmlFor\n`;
1810
+ combinedPage += ` • Inline scripts → useEffect with cleanup\n`;
1811
+ combinedPage += ` • Add React imports at top\n`;
1812
+ }
1813
+ combinedPage += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
1814
+ combinedPage += `⚠️ MANDATORY COLOR RULES — INJECT THIS PALETTE IN globals.css:\n\`\`\`css\n${paletteCSS}\n\`\`\`\n\n`;
1815
+ combinedPage += `CRITICAL: Use ONLY var(--background), var(--primary), var(--foreground), var(--border), var(--card) etc.\n`;
1816
+ combinedPage += `NEVER hardcode hex colors. NEVER use purple/neon unless --primary IS purple in the palette above.\n`;
1817
+ if (briefAvoid.length > 0)
1818
+ combinedPage += `AVOID: ${briefAvoid.join(', ')}\n`;
1819
+ combinedPage += `\nINSTRUCTION FOR AI: Assemble a complete page using Animmaster components in init_order sequence.\n`;
1820
+ combinedPage += `Convert ALL HTML templates below to JSX. Initialize JS in useEffect hooks.\n\n`;
1584
1821
  const allComponents = [];
1585
1822
  for (const { section, query, useCase } of sectionMap) {
1586
1823
  const { data } = await supabase
@@ -1615,7 +1852,13 @@ COMPONENT SELECTION FILTER (apply mentally before using any component):
1615
1852
  }
1616
1853
  const totalBudget = allComponents.reduce((sum, c) => sum + (c.animation_budget || 0), 0);
1617
1854
  combinedPage += `\n---\nTotal animation budget: ${totalBudget}/30 | Components: ${allComponents.length}\n`;
1618
- combinedPage += `\nINSTRUCTION FOR AI: Initialize scripts in the order listed above (preloader → cursor → header → effects).`;
1855
+ combinedPage += `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
1856
+ combinedPage += `FINAL INSTRUCTION:\n`;
1857
+ combinedPage += `1. Initialize components in init_order sequence (preloader → cursor → header → effects)\n`;
1858
+ combinedPage += `2. Save the page to: ${outputTarget || 'src/app/page.tsx'}\n`;
1859
+ combinedPage += `3. ${framework === 'nextjs' ? 'Add "use client" at top, convert all HTML to JSX (class→className)' : 'Convert HTML to JSX React components'}\n`;
1860
+ combinedPage += `4. NEVER create index.html — always .tsx files only\n`;
1861
+ combinedPage += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`;
1619
1862
  return { content: [{ type: "text", text: combinedPage }] };
1620
1863
  }
1621
1864
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibetachyon",
3
- "version": "1.9.0",
3
+ "version": "2.1.0",
4
4
  "description": "VibeCodes MCP CLI Installer and Server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {