vibetachyon 1.5.3 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -179,10 +179,28 @@ function printAgentBanner(personaId) {
179
179
  console.log(`\x1b[90m ${persona.agentTagline}\x1b[0m`);
180
180
  console.log('');
181
181
  }
182
- // Detecta se foi invocado por nome de agente (ex: `cipher audit`)
182
+ // Detecta se foi invocado por nome de agente (ex: `vtcipher` ou `vibetachyon cipher`)
183
183
  function detectAgentBin() {
184
184
  const binName = path_1.default.basename(process.argv[1] ?? '').replace(/\.js$/, '').toLowerCase();
185
- return config_js_3.AGENT_BIN_MAP[binName] ?? null;
185
+ if (config_js_3.AGENT_BIN_MAP[binName])
186
+ return config_js_3.AGENT_BIN_MAP[binName];
187
+ // Suporte a subcomando: `vibetachyon cipher` ou `vibetachyon vtcipher`
188
+ const subcommand = (process.argv[2] ?? '').toLowerCase().replace(/^vt/, '');
189
+ const subcommandMap = {
190
+ cipher: 'security',
191
+ quasar: 'architect',
192
+ lumen: 'frontend',
193
+ axiom: 'backend',
194
+ flux: 'performance',
195
+ prism: 'reviewer',
196
+ zenith: 'growth',
197
+ };
198
+ if (subcommandMap[subcommand]) {
199
+ // Remove o subcomando dos args para não confundir o commander
200
+ process.argv.splice(2, 1);
201
+ return subcommandMap[subcommand];
202
+ }
203
+ return null;
186
204
  }
187
205
  async function runInteractiveMode() {
188
206
  printBanner();
@@ -334,6 +334,163 @@ async function startMcpServer() {
334
334
  };
335
335
  }
336
336
  });
337
+ // Tool: Project DNA Scanner
338
+ server.tool("vibe_project_dna", "Scans the project and returns a complete ProjectDNA: framework, colors, fonts, animation level, visual tone, and existing components. ALWAYS call this before searching or generating any frontend component.", {
339
+ projectDir: zod_1.z.string().describe("Absolute path to the project root.")
340
+ }, async ({ projectDir }) => {
341
+ await checkSanity();
342
+ const root = await findProjectRoot(projectDir || process.cwd());
343
+ const dna = {
344
+ root,
345
+ framework: 'unknown',
346
+ styleSystem: 'unknown',
347
+ visualTone: 'unknown',
348
+ animationLevel: 'none',
349
+ colorPalette: {},
350
+ fonts: [],
351
+ existingComponents: [],
352
+ aliases: {},
353
+ installedDeps: []
354
+ };
355
+ try {
356
+ // 1. package.json — framework, deps, animation level
357
+ const pkgPath = path_1.default.join(root, 'package.json');
358
+ if (await fs_extra_1.default.pathExists(pkgPath)) {
359
+ const pkg = await fs_extra_1.default.readJson(pkgPath);
360
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
361
+ dna.framework = deps['next'] ? 'next' : deps['remix'] ? 'remix' : deps['vite'] ? 'vite' : deps['react'] ? 'react' : 'unknown';
362
+ dna.styleSystem = deps['tailwindcss'] ? 'tailwind' : deps['styled-components'] ? 'styled-components' : deps['@emotion/react'] ? 'emotion' : 'css-modules';
363
+ const animDeps = ['framer-motion', 'motion', 'gsap', 'react-spring', 'animejs', '@react-spring/web'];
364
+ const installedAnim = animDeps.filter(d => deps[d]);
365
+ dna.animationLevel = installedAnim.length >= 2 ? 'heavy' : installedAnim.length === 1 ? 'subtle' : 'none';
366
+ dna.animationLibraries = installedAnim;
367
+ const uiDeps = ['@radix-ui/react-dialog', 'shadcn', '@headlessui/react', '@mui/material', 'antd', 'chakra-ui'];
368
+ dna.installedDeps = [...animDeps, ...uiDeps, 'lucide-react', 'clsx', 'tailwind-merge', 'class-variance-authority']
369
+ .filter(d => deps[d]);
370
+ }
371
+ // 2. globals.css / index.css — color palette, fonts, visual tone
372
+ const cssCandidates = [
373
+ path_1.default.join(root, 'src/app/globals.css'),
374
+ path_1.default.join(root, 'src/index.css'),
375
+ path_1.default.join(root, 'app/globals.css'),
376
+ path_1.default.join(root, 'styles/globals.css'),
377
+ path_1.default.join(root, 'src/styles/globals.css'),
378
+ ];
379
+ for (const cssPath of cssCandidates) {
380
+ if (await fs_extra_1.default.pathExists(cssPath)) {
381
+ const css = await fs_extra_1.default.readFile(cssPath, 'utf-8');
382
+ // Extract CSS variables with values
383
+ const varMatches = css.matchAll(/--([a-zA-Z0-9-]+)\s*:\s*([^;]+);/g);
384
+ const palette = {};
385
+ for (const m of varMatches) {
386
+ palette[`--${m[1]}`] = m[2].trim();
387
+ }
388
+ dna.colorPalette = palette;
389
+ // Detect background tone
390
+ const bg = palette['--background'] || palette['--bg'] || '';
391
+ if (bg) {
392
+ const isHsl = bg.includes('hsl') || /^\d+\s+\d+/.test(bg);
393
+ const lightness = isHsl ? parseFloat(bg.split(' ').pop() || '50') : 50;
394
+ dna.visualTone = lightness < 30 ? 'dark' : lightness > 70 ? 'light' : 'mixed';
395
+ }
396
+ // Detect fonts
397
+ const fontMatches = css.matchAll(/font-family[^:]*:\s*([^;]+);/g);
398
+ const fonts = [];
399
+ for (const f of fontMatches) {
400
+ fonts.push(f[1].trim().split(',')[0].replace(/['"]/g, ''));
401
+ }
402
+ dna.fonts = [...new Set(fonts)];
403
+ break;
404
+ }
405
+ }
406
+ // 3. tailwind.config — custom colors and plugins
407
+ const twCandidates = ['tailwind.config.ts', 'tailwind.config.js', 'tailwind.config.mjs'];
408
+ for (const twFile of twCandidates) {
409
+ const twPath = path_1.default.join(root, twFile);
410
+ if (await fs_extra_1.default.pathExists(twPath)) {
411
+ const twContent = await fs_extra_1.default.readFile(twPath, 'utf-8');
412
+ const hasCustomColors = twContent.includes('colors') && twContent.includes('extend');
413
+ const hasAnimation = twContent.includes('keyframes') || twContent.includes('animation');
414
+ dna.tailwindCustomColors = hasCustomColors;
415
+ dna.tailwindCustomAnimations = hasAnimation;
416
+ break;
417
+ }
418
+ }
419
+ // 4. Existing components — what's already built
420
+ const componentsDirs = [
421
+ path_1.default.join(root, 'src/components'),
422
+ path_1.default.join(root, 'components'),
423
+ path_1.default.join(root, 'src/app/components'),
424
+ ];
425
+ const existing = [];
426
+ for (const dir of componentsDirs) {
427
+ if (await fs_extra_1.default.pathExists(dir)) {
428
+ const walk = async (d) => {
429
+ const entries = await fs_extra_1.default.readdir(d, { withFileTypes: true });
430
+ for (const e of entries) {
431
+ if (e.isFile() && (e.name.endsWith('.tsx') || e.name.endsWith('.jsx'))) {
432
+ existing.push(e.name.replace(/\.(tsx|jsx)$/, ''));
433
+ }
434
+ else if (e.isDirectory() && existing.length < 50) {
435
+ await walk(path_1.default.join(d, e.name));
436
+ }
437
+ }
438
+ };
439
+ await walk(dir);
440
+ break;
441
+ }
442
+ }
443
+ dna.existingComponents = existing;
444
+ // 5. tsconfig aliases
445
+ const tsConfigPath = path_1.default.join(root, 'tsconfig.json');
446
+ if (await fs_extra_1.default.pathExists(tsConfigPath)) {
447
+ try {
448
+ const tsRaw = await fs_extra_1.default.readFile(tsConfigPath, 'utf-8');
449
+ const tsClean = tsRaw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
450
+ const tsJson = JSON.parse(tsClean);
451
+ dna.aliases = tsJson?.compilerOptions?.paths || {};
452
+ }
453
+ catch { /* ignore parse errors */ }
454
+ }
455
+ // 6. components.json (shadcn)
456
+ const compJsonPath = path_1.default.join(root, 'components.json');
457
+ if (await fs_extra_1.default.pathExists(compJsonPath)) {
458
+ const compJson = await fs_extra_1.default.readJson(compJsonPath);
459
+ dna.shadcn = {
460
+ componentsPath: compJson?.aliases?.components || '@/components',
461
+ utilsPath: compJson?.aliases?.utils || '@/lib/utils',
462
+ style: compJson?.style || 'default'
463
+ };
464
+ }
465
+ const summary = [
466
+ `Framework: ${dna.framework}`,
467
+ `Style System: ${dna.styleSystem}`,
468
+ `Visual Tone: ${dna.visualTone}`,
469
+ `Animation Level: ${dna.animationLevel}${dna.animationLibraries.length ? ` (${dna.animationLibraries.join(', ')})` : ''}`,
470
+ `Fonts: ${dna.fonts.join(', ') || 'not detected'}`,
471
+ `CSS Variables: ${Object.keys(dna.colorPalette).length} found`,
472
+ `Existing Components: ${dna.existingComponents.length} files`,
473
+ `Installed UI Deps: ${dna.installedDeps.join(', ') || 'none'}`,
474
+ ].join('\n');
475
+ return {
476
+ content: [{
477
+ type: "text",
478
+ text: `[VibeTachyon ProjectDNA]\n\n${summary}\n\n${JSON.stringify(dna, null, 2)}\n\n` +
479
+ `INSTRUCTION FOR AI: This is the project's DNA. Use it to:\n` +
480
+ `1. Match ALL generated components to the visual tone (${dna.visualTone})\n` +
481
+ `2. Use ONLY installed animation libraries (${dna.animationLibraries.join(', ') || 'none — use CSS only'})\n` +
482
+ `3. Respect existing aliases for imports\n` +
483
+ `4. Never duplicate existing components: ${dna.existingComponents.slice(0, 10).join(', ')}${dna.existingComponents.length > 10 ? '...' : ''}\n` +
484
+ `5. Use CSS variables for colors — never hardcode hex/rgb values`
485
+ }]
486
+ };
487
+ }
488
+ catch (err) {
489
+ return {
490
+ content: [{ type: "text", text: `Error scanning project DNA: ${String(err)}` }]
491
+ };
492
+ }
493
+ });
337
494
  // Tool: Get Vibe Colors
338
495
  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.", {
339
496
  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.")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibetachyon",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "VibeCodes MCP CLI Installer and Server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {