ebade 0.4.1 β†’ 0.4.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/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to **ebade** will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.5] - 2025-01-10
9
+
10
+ ### Added
11
+ - UI/UX Overhaul: Premium dark-mode aesthetic with ambient glow and glassmorphism by default.
12
+ - Dynamic Color Support: Real-time Hex-to-HSL conversion to apply user's primary color choice to CSS variables.
13
+ - Component "Intents": Placeholders are now high-quality "Glass Cards" that look like part of a finished UI.
14
+ - Test Organization: All unit tests are now generated in a centralized `tests/` directory instead of being cluttered with components.
15
+
16
+ ### Fixed
17
+ - API Pathing: Fixed a bug that caused double-nesting (e.g., `/api/api/...`) in generated routes.
18
+ - Removed debug information (headers, route labels) from generated pages for a "turnkey" production feel.
19
+
20
+ ## [0.4.4] - 2025-01-10
21
+
22
+ ### Fixed
23
+ - Added `postcss.config.js` generation to enable Tailwind styling in scaffolded projects.
24
+ - Fixed double-nesting issues when initializing projects (removed redundant `projectName/projectName` folder creation).
25
+ - Refined Tailwind config to ensure all component paths are correctly watched.
26
+
27
+ ## [0.4.3] - 2025-01-10
28
+
29
+ ### Fixed
30
+ - Critical bug where templates were not found when running via `npx` (fix using `import.meta.url`).
31
+ - Added missing component templates for SaaS Dashboard and Landing Pages (`testimonials`, `faq-accordion`, `cta-banner`, `activity-chart`, `stats-grid`, `recent-events`).
32
+ - Updated `tsconfig.json` generation to use `jsx: preserve` and added path aliases (`@/*`).
33
+ - Added slugification for `package.json` names to ensure valid naming conventions.
34
+ - Synced framework versions across all packages.
35
+
8
36
  ## [0.4.1] - 2025-01-10
9
37
 
10
38
  ### Added
@@ -28,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
56
  ### Added
29
57
 
30
58
  - πŸŽ‰ Initial alpha release of **ebade**
31
- - MCP Server package (`@ebade/mcp-server`) with 4 tools:
59
+ - MCP Server package (`ebade-mcp-server`) with 4 tools:
32
60
  - `ebade_scaffold` - Create full projects from ebade definitions
33
61
  - `ebade_validate` - Validate ebade files against schema
34
62
  - `ebade_compile` - Compile single ebade to code
package/README.md CHANGED
@@ -60,7 +60,7 @@ Add `ebade` to your AI agent (Claude, Cursor, Windsurf) via the Model Context Pr
60
60
  "mcpServers": {
61
61
  "ebade": {
62
62
  "command": "npx",
63
- "args": ["-y", "@ebade/mcp-server"]
63
+ "args": ["-y", "ebade-mcp-server"]
64
64
  }
65
65
  }
66
66
  }
package/cli/scaffold.js CHANGED
@@ -14,6 +14,10 @@ import ora from "ora";
14
14
  import prompts from "prompts";
15
15
  import chokidar from "chokidar";
16
16
  import { execSync } from "child_process";
17
+ import { fileURLToPath } from "url";
18
+
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = path.dirname(__filename);
17
21
 
18
22
  // ============================================
19
23
  // ANSI Renk KodlarΔ± (Terminal Γ§Δ±ktΔ±sΔ± iΓ§in)
@@ -40,7 +44,7 @@ ${colors.magenta} β–ˆβ–ˆβ•”β•β•β• ${colors.cyan}β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—$
40
44
  ${colors.magenta} β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—${colors.cyan}β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•${colors.magenta}β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘${colors.cyan}β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•${colors.magenta}β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
41
45
  ${colors.magenta} β•šβ•β•β•β•β•β•β•${colors.cyan}β•šβ•β•β•β•β•β• ${colors.magenta}β•šβ•β• β•šβ•β•${colors.cyan}β•šβ•β•β•β•β•β• ${colors.magenta}β•šβ•β•β•β•β•β•β•${colors.reset}
42
46
 
43
- ${colors.dim}✨ Agent-First Framework ${colors.yellow}v0.4.1${colors.reset}
47
+ ${colors.dim}✨ Agent-First Framework ${colors.yellow}v0.4.5${colors.reset}
44
48
  `;
45
49
 
46
50
  const log = {
@@ -70,8 +74,8 @@ function parseEbade(ebadePath) {
70
74
  // ============================================
71
75
  function getComponentTemplate(componentName, design) {
72
76
  const templatePath = path.join(
73
- process.cwd(),
74
- "cli/templates",
77
+ __dirname,
78
+ "templates",
75
79
  `${componentName}.tsx`
76
80
  );
77
81
 
@@ -92,17 +96,19 @@ import { cn } from "@/lib/utils";
92
96
  /**
93
97
  * 🧠 Generated via ebade
94
98
  * Component: ${toPascalCase(componentName)}
95
- * Status: Placeholder (No template found in cli/templates)
99
+ * Status: Intent needs implementation
96
100
  */
97
101
  export function ${toPascalCase(componentName)}() {
98
102
  return (
99
- <div className="p-12 border-2 border-dashed border-border rounded-3xl text-center bg-muted/30">
100
- <div className="w-16 h-16 bg-primary/10 rounded-2xl flex items-center justify-center mx-auto mb-4">
101
- <span className="text-2xl">🧩</span>
103
+ <div className="p-12 glass-card rounded-[2.5rem] text-center min-h-[300px] flex flex-col items-center justify-center group hover:border-primary/50 transition-all">
104
+ <div className="w-20 h-20 bg-primary/10 rounded-[2rem] flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
105
+ <span className="text-3xl">✨</span>
102
106
  </div>
103
- <h3 className="text-xl font-bold mb-2">${toPascalCase(componentName)}</h3>
104
- <p className="text-sm text-muted-foreground max-w-xs mx-auto">
105
- No template found for this intent. Create a file at <code>cli/templates/${componentName}.tsx</code> to customize.
107
+ <h3 className="text-2xl font-bold mb-3 text-white">${toPascalCase(
108
+ componentName
109
+ )}</h3>
110
+ <p className="text-slate-400 max-w-sm mx-auto leading-relaxed">
111
+ This intent is defined for your AI agent. To customize, edit <code>components/${componentName}.tsx</code> or use the ebade compiler.
106
112
  </p>
107
113
  </div>
108
114
  );
@@ -172,19 +178,15 @@ ${componentImports}
172
178
  */
173
179
  export default function ${toPascalCase(page.intent)}Page() {
174
180
  return (
175
- <div className="min-h-screen bg-slate-950 text-white">
176
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
177
- <header className="mb-12">
178
- <h1 className="text-3xl font-bold tracking-tight opacity-90">${toPascalCase(
179
- page.intent
180
- )}</h1>
181
- <p className="text-sm opacity-40 mt-1">Route: ${page.path}</p>
182
- </header>
181
+ <div className="min-h-screen bg-slate-950 text-white selection:bg-indigo-500/30 selection:text-indigo-200">
182
+ <main className="relative overflow-hidden">
183
+ {/* Ambient background glow */}
184
+ <div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-[500px] bg-indigo-600/10 blur-[120px] rounded-full pointer-events-none" />
183
185
 
184
- <main className="space-y-12">
186
+ <div className="relative z-10">
185
187
  ${componentUsage}
186
- </main>
187
- </div>
188
+ </div>
189
+ </main>
188
190
  </div>
189
191
  );
190
192
  }
@@ -252,7 +254,7 @@ function generateDatabaseSchema(data) {
252
254
  function generatePackageJson(config) {
253
255
  return JSON.stringify(
254
256
  {
255
- name: config.name,
257
+ name: config.name.toLowerCase().replace(/[^a-z0-9]/g, "-"),
256
258
  version: "0.1.0",
257
259
  private: true,
258
260
  scripts: {
@@ -263,10 +265,10 @@ function generatePackageJson(config) {
263
265
  test: "vitest",
264
266
  },
265
267
  dependencies: {
266
- next: "^14.0.0",
267
- react: "^18.2.0",
268
- "react-dom": "^18.2.0",
269
- "lucide-react": "^0.300.0",
268
+ next: "^14.2.0",
269
+ react: "^18.3.0",
270
+ "react-dom": "^18.3.0",
271
+ "lucide-react": "^0.400.0",
270
272
  clsx: "^2.1.0",
271
273
  "tailwind-merge": "^2.2.0",
272
274
  "class-variance-authority": "^0.7.0",
@@ -380,6 +382,16 @@ export default nextConfig;
380
382
  `;
381
383
  }
382
384
 
385
+ function generatePostcssConfig() {
386
+ return `module.exports = {
387
+ plugins: {
388
+ tailwindcss: {},
389
+ autoprefixer: {},
390
+ },
391
+ }
392
+ `;
393
+ }
394
+
383
395
  function generateTsConfig() {
384
396
  return JSON.stringify(
385
397
  {
@@ -395,10 +407,12 @@ function generateTsConfig() {
395
407
  moduleResolution: "node",
396
408
  resolveJsonModule: true,
397
409
  isolatedModules: true,
398
- jsx: "react-jsx",
410
+ jsx: "preserve",
399
411
  incremental: true,
400
412
  plugins: [{ name: "next" }],
401
- paths: { "@/*": ["./*"] },
413
+ paths: {
414
+ "@/*": ["./*"],
415
+ },
402
416
  },
403
417
  include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
404
418
  exclude: ["node_modules"],
@@ -453,65 +467,35 @@ function generateGlobalsCss(design) {
453
467
 
454
468
  @layer base {
455
469
  :root {
456
- --background: 0 0% 100%;
457
- --foreground: 222.2 84% 4.9%;
458
-
459
- --card: 0 0% 100%;
460
- --card-foreground: 222.2 84% 4.9%;
461
-
462
- --popover: 0 0% 100%;
463
- --popover-foreground: 222.2 84% 4.9%;
464
-
465
- --primary: 221.2 83.2% 53.3%;
466
- --primary-foreground: 210 40% 98%;
467
-
468
- --secondary: 210 40% 96.1%;
469
- --secondary-foreground: 222.2 47.4% 11.2%;
470
-
471
- --muted: 210 40% 96.1%;
472
- --muted-foreground: 215.4 16.3% 46.9%;
473
-
474
- --accent: 210 40% 96.1%;
475
- --accent-foreground: 222.2 47.4% 11.2%;
476
-
477
- --destructive: 0 84.2% 60.2%;
478
- --destructive-foreground: 210 40% 98%;
479
-
480
- --border: 214.3 31.8% 91.4%;
481
- --input: 214.3 31.8% 91.4%;
482
- --ring: 221.2 83.2% 53.3%;
483
-
484
- --radius: 0.5rem;
485
- }
470
+ --background: 222 47% 4%;
471
+ --foreground: 213 31% 91%;
486
472
 
487
- .dark {
488
- --background: 222.2 84% 4.9%;
489
- --foreground: 210 40% 98%;
473
+ --card: 222 47% 4%;
474
+ --card-foreground: 213 31% 91%;
490
475
 
491
- --card: 222.2 84% 4.9%;
492
- --card-foreground: 210 40% 98%;
476
+ --popover: 222 47% 4%;
477
+ --popover-foreground: 213 31% 91%;
493
478
 
494
- --popover: 222.2 84% 4.9%;
495
- --popover-foreground: 210 40% 98%;
479
+ --primary: ${hexToHsl(primary)};
480
+ --primary-foreground: 0 0% 100%;
496
481
 
497
- --primary: 217.2 91.2% 59.8%;
498
- --primary-foreground: 222.2 47.4% 11.2%;
482
+ --secondary: 222 47% 11%;
483
+ --secondary-foreground: 213 31% 91%;
499
484
 
500
- --secondary: 217.2 32.6% 17.5%;
501
- --secondary-foreground: 210 40% 98%;
485
+ --muted: 223 47% 11%;
486
+ --muted-foreground: 215.4 16.3% 56.9%;
502
487
 
503
- --muted: 217.2 32.6% 17.5%;
504
- --muted-foreground: 215 20.2% 65.1%;
505
-
506
- --accent: 217.2 32.6% 17.5%;
488
+ --accent: 216 34% 17%;
507
489
  --accent-foreground: 210 40% 98%;
508
490
 
509
- --destructive: 0 62.8% 30.6%;
491
+ --destructive: 0 63% 31%;
510
492
  --destructive-foreground: 210 40% 98%;
511
493
 
512
- --border: 217.2 32.6% 17.5%;
513
- --input: 217.2 32.6% 17.5%;
514
- --ring: 224.3 76.3% 48%;
494
+ --border: 216 34% 17%;
495
+ --input: 216 34% 17%;
496
+ --ring: ${hexToHsl(primary)};
497
+
498
+ --radius: 1rem;
515
499
  }
516
500
  }
517
501
 
@@ -520,9 +504,25 @@ function generateGlobalsCss(design) {
520
504
  @apply border-border;
521
505
  }
522
506
  body {
523
- @apply bg-background text-foreground;
507
+ @apply bg-background text-foreground antialiased;
508
+ font-feature-settings: "cv11", "ss01";
524
509
  }
525
510
  }
511
+
512
+ /* Premium Animations */
513
+ @keyframes float {
514
+ 0% { transform: translateY(0px); }
515
+ 50% { transform: translateY(-10px); }
516
+ 100% { transform: translateY(0px); }
517
+ }
518
+
519
+ .animate-float {
520
+ animation: float 6s ease-in-out infinite;
521
+ }
522
+
523
+ .glass-card {
524
+ @apply bg-white/[0.03] border border-white/10 backdrop-blur-xl;
525
+ }
526
526
  `;
527
527
  }
528
528
 
@@ -630,11 +630,54 @@ function toPascalCase(str) {
630
630
 
631
631
  function toSnakeCase(str) {
632
632
  return str
633
- .replace(/([A-Z])/g, "_$1")
633
+ .replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
634
634
  .toLowerCase()
635
635
  .replace(/^_/, "");
636
636
  }
637
637
 
638
+ function hexToHsl(hex) {
639
+ let r = 0,
640
+ g = 0,
641
+ b = 0;
642
+ if (hex.length === 4) {
643
+ r = parseInt(hex[1] + hex[1], 16);
644
+ g = parseInt(hex[2] + hex[2], 16);
645
+ b = parseInt(hex[3] + hex[3], 16);
646
+ } else if (hex.length === 7) {
647
+ r = parseInt(hex.slice(1, 3), 16);
648
+ g = parseInt(hex.slice(3, 5), 16);
649
+ b = parseInt(hex.slice(5, 7), 16);
650
+ }
651
+ r /= 255;
652
+ g /= 255;
653
+ b /= 255;
654
+ let max = Math.max(r, g, b),
655
+ min = Math.min(r, g, b);
656
+ let h,
657
+ s,
658
+ l = (max + min) / 2;
659
+ if (max === min) h = s = 0;
660
+ else {
661
+ let d = max - min;
662
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
663
+ switch (max) {
664
+ case r:
665
+ h = (g - b) / d + (g < b ? 6 : 0);
666
+ break;
667
+ case g:
668
+ h = (b - r) / d + 2;
669
+ break;
670
+ case b:
671
+ h = (r - g) / d + 4;
672
+ break;
673
+ }
674
+ h /= 6;
675
+ }
676
+ return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(
677
+ l * 100
678
+ )}%`;
679
+ }
680
+
638
681
  function mapToSqlType(type) {
639
682
  const typeMap = {
640
683
  uuid: "UUID PRIMARY KEY DEFAULT gen_random_uuid()",
@@ -680,7 +723,10 @@ async function scaffold(ebadePath, outputDir) {
680
723
  );
681
724
 
682
725
  // Create output directory structure
683
- const projectDir = path.join(outputDir, config.name);
726
+ // If outputDir already ends with config.name, don't nest again
727
+ const projectDir = outputDir.endsWith(config.name)
728
+ ? outputDir
729
+ : path.join(outputDir, config.name);
684
730
  ensureDir(projectDir);
685
731
 
686
732
  // ========== Generate Structure ==========
@@ -745,8 +791,11 @@ async function scaffold(ebadePath, outputDir) {
745
791
 
746
792
  fs.writeFileSync(path.join(projectDir, componentPath), content.trim());
747
793
 
748
- // Generate unit test
749
- const testPath = `components/${component}.test.tsx`;
794
+ // Generate unit test in tests/ directory
795
+ const testPath = `tests/components/${component}.test.tsx`;
796
+ const testDir = path.dirname(path.join(projectDir, testPath));
797
+ ensureDir(testDir);
798
+
750
799
  fs.writeFileSync(
751
800
  path.join(projectDir, testPath),
752
801
  generateComponentTest(component).trim()
@@ -765,7 +814,12 @@ async function scaffold(ebadePath, outputDir) {
765
814
  if (config.api) {
766
815
  const spinner3 = ora("Generating API routes...").start();
767
816
  config.api.forEach((endpoint) => {
768
- const apiPath = `app/api${endpoint.path}/route.ts`;
817
+ // Fix potential /api/api double nesting
818
+ const cleanPath = endpoint.path.startsWith("/api")
819
+ ? endpoint.path.slice(4)
820
+ : endpoint.path;
821
+
822
+ const apiPath = `app/api${cleanPath}/route.ts`;
769
823
  const apiDir = path.dirname(path.join(projectDir, apiPath));
770
824
  ensureDir(apiDir);
771
825
 
@@ -814,6 +868,13 @@ async function scaffold(ebadePath, outputDir) {
814
868
  );
815
869
  log.file("tailwind.config.js");
816
870
 
871
+ // postcss.config.js
872
+ fs.writeFileSync(
873
+ path.join(projectDir, "postcss.config.js"),
874
+ generatePostcssConfig()
875
+ );
876
+ log.file("postcss.config.js");
877
+
817
878
  // vitest.config.ts
818
879
  fs.writeFileSync(
819
880
  path.join(projectDir, "vitest.config.ts"),
@@ -984,13 +1045,14 @@ async function verifyOutput(projectDir, config) {
984
1045
  }
985
1046
 
986
1047
  // 3. Test Coverage Check
987
- // Ensure every component has a matching test file
1048
+ // Ensure every component has a matching test file in tests/components/
988
1049
  const components = fs
989
1050
  .readdirSync(path.join(projectDir, "components"))
990
1051
  .filter((f) => f.endsWith(".tsx") && !f.endsWith(".test.tsx"));
991
1052
  components.forEach((comp) => {
992
1053
  const testFile = comp.replace(".tsx", ".test.tsx");
993
- if (!fs.existsSync(path.join(projectDir, "components", testFile))) {
1054
+ const testPath = path.join(projectDir, "tests/components", testFile);
1055
+ if (!fs.existsSync(testPath)) {
994
1056
  results.tests = false;
995
1057
  results.issues.push(`Missing test for component: ${comp}`);
996
1058
  }
@@ -0,0 +1,50 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ /**
5
+ * 🧠 Generated via ebade
6
+ * Component: ActivityChart
7
+ */
8
+ export function ActivityChart() {
9
+ const data = [40, 70, 45, 90, 65, 80, 50, 85, 95, 60, 75, 55];
10
+
11
+ return (
12
+ <div className="p-6 rounded-3xl bg-slate-900 border border-slate-800 shadow-sm">
13
+ <div className="flex items-center justify-between mb-8">
14
+ <div>
15
+ <h3 className="text-lg font-bold text-white">Activity Overview</h3>
16
+ <p className="text-sm text-slate-500">
17
+ System throughput over the last 12 hours
18
+ </p>
19
+ </div>
20
+ <div className="flex gap-2">
21
+ <div className="flex items-center gap-1.5">
22
+ <div className="w-2.5 h-2.5 rounded-full bg-indigo-500"></div>
23
+ <span className="text-xs text-slate-400">Requests</span>
24
+ </div>
25
+ </div>
26
+ </div>
27
+
28
+ <div className="flex items-end justify-between h-48 gap-2 px-2">
29
+ {data.map((val, i) => (
30
+ <div
31
+ key={i}
32
+ className="flex-1 flex flex-col items-center gap-2 group"
33
+ >
34
+ <div
35
+ className="w-full bg-indigo-500/20 rounded-t-lg group-hover:bg-indigo-500/40 transition-all relative"
36
+ style={{ height: `${val}%` }}
37
+ >
38
+ <div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-slate-800 text-white text-[10px] px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity">
39
+ {val}k
40
+ </div>
41
+ </div>
42
+ <span className="text-[10px] text-slate-600 font-mono">
43
+ H{i + 1}
44
+ </span>
45
+ </div>
46
+ ))}
47
+ </div>
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { ArrowRight } from "lucide-react";
4
+
5
+ /**
6
+ * 🧠 Generated via ebade
7
+ * Component: CtaBanner
8
+ */
9
+ export function CtaBanner() {
10
+ return (
11
+ <section className="py-24 px-4">
12
+ <div className="max-w-5xl mx-auto p-12 rounded-[2.5rem] bg-gradient-to-br from-indigo-600 to-indigo-700 text-center relative overflow-hidden shadow-2xl">
13
+ <div className="absolute top-0 right-0 -mr-20 -mt-20 w-80 h-80 bg-white/10 rounded-full blur-3xl"></div>
14
+ <div className="absolute bottom-0 left-0 -ml-20 -mb-20 w-60 h-60 bg-black/10 rounded-full blur-3xl"></div>
15
+
16
+ <h2 className="text-3xl md:text-5xl font-bold text-white mb-6 relative z-10">
17
+ Ready to build with Intent?
18
+ </h2>
19
+ <p className="text-indigo-100 text-lg mb-10 max-w-2xl mx-auto relative z-10 italic">
20
+ Join the revolution of Agent-First engineering. Transform your YAML
21
+ into production-ready software in seconds.
22
+ </p>
23
+
24
+ <div className="flex flex-col sm:flex-row gap-4 justify-center items-center relative z-10">
25
+ <button className="px-8 py-4 bg-white text-indigo-600 font-bold rounded-2xl hover:bg-slate-50 transition-all flex items-center gap-2 group shadow-xl">
26
+ Get Started Free
27
+ <ArrowRight
28
+ size={20}
29
+ className="group-hover:translate-x-1 transition-transform"
30
+ />
31
+ </button>
32
+ <button className="px-8 py-4 bg-transparent text-white border border-white/20 font-semibold rounded-2xl hover:bg-white/10 transition-all">
33
+ Contact Sales
34
+ </button>
35
+ </div>
36
+ </div>
37
+ </section>
38
+ );
39
+ }
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { ChevronDown } from "lucide-react";
4
+
5
+ /**
6
+ * 🧠 Generated via ebade
7
+ * Component: FaqAccordion
8
+ */
9
+ export function FaqAccordion() {
10
+ const faqs = [
11
+ {
12
+ q: "What is ebade?",
13
+ a: "ebade is an Agent-First Framework designed to help AI agents build production-ready applications with deterministic outcomes.",
14
+ },
15
+ {
16
+ q: "How does it reduce tokens?",
17
+ a: "By using YAML-based intent definitions, we provide only the essential context to the model, reducing entropy and token consumption.",
18
+ },
19
+ {
20
+ q: "Is it compatible with Next.js?",
21
+ a: "Yes, ebade scaffolds native Next.js projects using the App Router and Tailwind CSS by default.",
22
+ },
23
+ ];
24
+
25
+ return (
26
+ <section className="py-24 bg-slate-950">
27
+ <div className="max-w-3xl mx-auto px-4">
28
+ <h2 className="text-3xl font-bold text-white text-center mb-12">
29
+ Frequently Asked Questions
30
+ </h2>
31
+ <div className="space-y-4">
32
+ {faqs.map((f, i) => (
33
+ <div
34
+ key={i}
35
+ className="group rounded-2xl border border-slate-800 bg-slate-900/30 overflow-hidden transition-all"
36
+ >
37
+ <button className="flex items-center justify-between w-full p-6 text-left text-white hover:bg-white/5 transition-colors">
38
+ <span className="font-medium">{f.q}</span>
39
+ <ChevronDown
40
+ size={20}
41
+ className="text-slate-500 group-focus:rotate-180 transition-transform"
42
+ />
43
+ </button>
44
+ <div className="p-6 pt-0 text-slate-400 text-sm leading-relaxed border-t border-slate-800/50">
45
+ {f.a}
46
+ </div>
47
+ </div>
48
+ ))}
49
+ </div>
50
+ </div>
51
+ </section>
52
+ );
53
+ }
@@ -0,0 +1,80 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { CheckCircle2, Clock, Info, ShieldAlert } from "lucide-react";
4
+
5
+ /**
6
+ * 🧠 Generated via ebade
7
+ * Component: RecentEvents
8
+ */
9
+ export function RecentEvents() {
10
+ const events = [
11
+ {
12
+ title: "Deployment Successful",
13
+ time: "2m ago",
14
+ type: "success",
15
+ icon: CheckCircle2,
16
+ desc: "Production environment updated to v1.2.4",
17
+ },
18
+ {
19
+ title: "Security Alert",
20
+ time: "15m ago",
21
+ type: "error",
22
+ icon: ShieldAlert,
23
+ desc: "Anomalous login attempt from unknown IP",
24
+ },
25
+ {
26
+ title: "Backup Completed",
27
+ time: "1h ago",
28
+ type: "info",
29
+ icon: Clock,
30
+ desc: "Global database backup finished successfully",
31
+ },
32
+ {
33
+ title: "New Team Member",
34
+ time: "3h ago",
35
+ type: "info",
36
+ icon: Info,
37
+ desc: "Sarah Chen joined the Engineering team",
38
+ },
39
+ ];
40
+
41
+ return (
42
+ <div className="p-6 rounded-3xl bg-slate-900 border border-slate-800">
43
+ <h3 className="text-lg font-bold text-white mb-6">Recent Events</h3>
44
+ <div className="space-y-6">
45
+ {events.map((e, i) => {
46
+ const Icon = e.icon;
47
+ return (
48
+ <div key={i} className="flex gap-4 group">
49
+ <div
50
+ className={cn(
51
+ "p-2 rounded-xl h-fit",
52
+ e.type === "success"
53
+ ? "bg-emerald-500/10 text-emerald-500"
54
+ : e.type === "error"
55
+ ? "bg-red-500/10 text-red-500"
56
+ : "bg-blue-500/10 text-blue-500"
57
+ )}
58
+ >
59
+ <Icon size={18} />
60
+ </div>
61
+ <div className="flex-1 pb-6 border-b border-slate-800/50 group-last:border-0 group-last:pb-0">
62
+ <div className="flex justify-between mb-1">
63
+ <h4 className="text-sm font-semibold text-slate-200">
64
+ {e.title}
65
+ </h4>
66
+ <span className="text-[10px] text-slate-500 uppercase font-bold tracking-wider">
67
+ {e.time}
68
+ </span>
69
+ </div>
70
+ <p className="text-xs text-slate-500 leading-relaxed">
71
+ {e.desc}
72
+ </p>
73
+ </div>
74
+ </div>
75
+ );
76
+ })}
77
+ </div>
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,68 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import {
4
+ LayoutDashboard,
5
+ BarChart3,
6
+ Users2,
7
+ Settings,
8
+ CreditCard,
9
+ Key,
10
+ } from "lucide-react";
11
+
12
+ /**
13
+ * 🧠 Generated via ebade
14
+ * Component: SidebarNavigation
15
+ */
16
+ export function SidebarNavigation() {
17
+ const menuItems = [
18
+ { label: "Dashboard", icon: LayoutDashboard, active: true },
19
+ { label: "Analytics", icon: BarChart3 },
20
+ { label: "Team", icon: Users2 },
21
+ { label: "Billing", icon: CreditCard },
22
+ { label: "API Keys", icon: Key },
23
+ { label: "Settings", icon: Settings },
24
+ ];
25
+
26
+ return (
27
+ <div className="w-64 h-full bg-slate-900 border-r border-slate-800 flex flex-col p-6">
28
+ <div className="flex items-center gap-3 mb-12">
29
+ <div className="w-8 h-8 bg-indigo-500 rounded-lg flex items-center justify-center text-white font-bold">
30
+ A
31
+ </div>
32
+ <span className="text-white font-bold tracking-tight">AnalyticsHQ</span>
33
+ </div>
34
+
35
+ <div className="flex-1 space-y-2">
36
+ {menuItems.map((item, i) => {
37
+ const Icon = item.icon;
38
+ return (
39
+ <button
40
+ key={i}
41
+ className={cn(
42
+ "flex items-center gap-3 w-full px-4 py-3 rounded-2xl transition-all",
43
+ item.active
44
+ ? "bg-indigo-500 text-white"
45
+ : "text-slate-400 hover:bg-slate-800 hover:text-white"
46
+ )}
47
+ >
48
+ <Icon size={20} />
49
+ <span className="text-sm font-medium">{item.label}</span>
50
+ </button>
51
+ );
52
+ })}
53
+ </div>
54
+
55
+ <div className="mt-auto p-4 rounded-2xl bg-indigo-500/10 border border-indigo-500/20">
56
+ <p className="text-xs text-indigo-300 font-bold mb-1 italic">
57
+ PRO PLAN
58
+ </p>
59
+ <p className="text-[10px] text-indigo-400/70 mb-3">
60
+ Upgrade to Enterprise for unlimited seats.
61
+ </p>
62
+ <div className="w-full bg-slate-800 h-1 rounded-full overflow-hidden">
63
+ <div className="bg-indigo-500 h-full w-[65%]"></div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ );
68
+ }
@@ -0,0 +1,80 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Users, Zap, Shield, TrendingUp } from "lucide-react";
4
+
5
+ /**
6
+ * 🧠 Generated via ebade
7
+ * Component: StatsGrid
8
+ */
9
+ export function StatsGrid() {
10
+ const stats = [
11
+ {
12
+ label: "Active Users",
13
+ value: "12,402",
14
+ growth: "+12.5%",
15
+ icon: Users,
16
+ color: "text-blue-500",
17
+ bg: "bg-blue-500/10",
18
+ },
19
+ {
20
+ label: "Throughput",
21
+ value: "84.2k",
22
+ growth: "+8.1%",
23
+ icon: Zap,
24
+ color: "text-indigo-500",
25
+ bg: "bg-indigo-500/10",
26
+ },
27
+ {
28
+ label: "Security Score",
29
+ value: "98/100",
30
+ growth: "Stable",
31
+ icon: Shield,
32
+ color: "text-emerald-500",
33
+ bg: "bg-emerald-500/10",
34
+ },
35
+ {
36
+ label: "Revenue",
37
+ value: "$42.1k",
38
+ growth: "+24.3%",
39
+ icon: TrendingUp,
40
+ color: "text-violet-500",
41
+ bg: "bg-violet-500/10",
42
+ },
43
+ ];
44
+
45
+ return (
46
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
47
+ {stats.map((s, i) => {
48
+ const Icon = s.icon;
49
+ return (
50
+ <div
51
+ key={i}
52
+ className="p-6 rounded-3xl bg-slate-900 border border-slate-800 hover:border-slate-700 transition-colors"
53
+ >
54
+ <div className="flex items-start justify-between mb-4">
55
+ <div className={cn("p-3 rounded-2xl", s.bg)}>
56
+ <Icon size={24} className={s.color} />
57
+ </div>
58
+ <span
59
+ className={cn(
60
+ "text-xs font-bold px-2 py-1 rounded-full",
61
+ s.growth.startsWith("+")
62
+ ? "bg-emerald-500/10 text-emerald-500"
63
+ : "bg-slate-800 text-slate-400"
64
+ )}
65
+ >
66
+ {s.growth}
67
+ </span>
68
+ </div>
69
+ <div>
70
+ <p className="text-sm text-slate-500 font-medium mb-1">
71
+ {s.label}
72
+ </p>
73
+ <h4 className="text-2xl font-bold text-white">{s.value}</h4>
74
+ </div>
75
+ </div>
76
+ );
77
+ })}
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,69 @@
1
+ import React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Star } from "lucide-react";
4
+
5
+ /**
6
+ * 🧠 Generated via ebade
7
+ * Component: Testimonials
8
+ */
9
+ export function Testimonials() {
10
+ const reviews = [
11
+ {
12
+ name: "Alex Rivera",
13
+ role: "CTO at TechFlow",
14
+ content:
15
+ "ebade changed how we bridge the gap between AI intent and production-ready code. It's a game changer.",
16
+ rating: 5,
17
+ },
18
+ {
19
+ name: "Sarah Chen",
20
+ role: "Lead Developer",
21
+ content:
22
+ "The most efficient way to maintain deterministic outputs with LLMs. Low entropy, high quality.",
23
+ rating: 5,
24
+ },
25
+ {
26
+ name: "Marcus Thorne",
27
+ role: "Product Designer",
28
+ content:
29
+ "Finally, a framework that speaks the language of agents without sacrificing developer experience.",
30
+ rating: 5,
31
+ },
32
+ ];
33
+
34
+ return (
35
+ <section className="py-24 bg-slate-950">
36
+ <div className="max-w-7xl mx-auto px-4">
37
+ <div className="text-center mb-16">
38
+ <h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
39
+ Trusted by the Best
40
+ </h2>
41
+ <p className="text-slate-400">
42
+ Join thousands of developers building the future of AI-Native
43
+ software.
44
+ </p>
45
+ </div>
46
+
47
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
48
+ {reviews.map((s, i) => (
49
+ <div
50
+ key={i}
51
+ className="p-8 rounded-3xl bg-slate-900/50 border border-slate-800 hover:border-slate-700 transition-all"
52
+ >
53
+ <div className="flex gap-1 mb-4 text-yellow-500">
54
+ {[...Array(s.rating)].map((_, i) => (
55
+ <Star key={i} size={16} fill="currentColor" />
56
+ ))}
57
+ </div>
58
+ <p className="text-slate-300 mb-6 italic">"{s.content}"</p>
59
+ <div>
60
+ <p className="text-white font-semibold">{s.name}</p>
61
+ <p className="text-slate-500 text-sm">{s.role}</p>
62
+ </div>
63
+ </div>
64
+ ))}
65
+ </div>
66
+ </div>
67
+ </section>
68
+ );
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ebade",
3
- "version": "0.4.1",
3
+ "version": "0.4.5",
4
4
  "description": "ebade - Agent-First Framework. The first framework designed for AI agents, readable by humans.",
5
5
  "type": "module",
6
6
  "main": "cli/scaffold.js",
@@ -1,19 +1,17 @@
1
- # ebade MCP Server πŸ€–
1
+ # ebade MCP Server 🐣
2
2
 
3
- > Enable AI agents to build with ebade - The Essence of Code
4
-
5
- This MCP (Model Context Protocol) server allows AI agents like Claude, GPT, and others to use ebade for building web applications.
3
+ Model Context Protocol (MCP) server for the **ebade** Agent-First Framework.
6
4
 
7
5
  ## Installation
8
6
 
9
7
  ```bash
10
- npm install -g @ebade/mcp-server
8
+ npx -y ebade-mcp-server
11
9
  ```
12
10
 
13
11
  Or locally:
14
12
 
15
13
  ```bash
16
- npm install @ebade/mcp-server
14
+ npm install ebade-mcp-server
17
15
  ```
18
16
 
19
17
  ## Configuration
@@ -1,12 +1,12 @@
1
1
  {
2
- "name": "@ebade/mcp-server",
3
- "version": "0.1.0",
2
+ "name": "ebade-mcp-server",
3
+ "version": "0.4.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
- "name": "@ebade/mcp-server",
9
- "version": "0.1.0",
8
+ "name": "ebade-mcp-server",
9
+ "version": "0.4.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "^1.25.1",
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "@ebade/mcp-server",
3
- "version": "0.4.1",
4
- "description": "MCP Server for ebade v0.4.1 - The Agent-First Framework",
2
+ "name": "ebade-mcp-server",
3
+ "version": "0.4.5",
4
+ "description": "MCP Server for ebade v0.4.4 - The Agent-First Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
package/www/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "www",
3
- "version": "0.4.1",
3
+ "version": "0.4.5",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "dev": "next dev",