ebade 0.2.2 → 0.4.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 (52) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +49 -38
  3. package/ROADMAP.md +19 -14
  4. package/cli/scaffold.js +502 -186
  5. package/cli/simulate.js +102 -0
  6. package/cli/templates/feature-grid.tsx +80 -0
  7. package/cli/templates/footer.tsx +121 -0
  8. package/cli/templates/hero-section.tsx +34 -0
  9. package/cli/templates/login-form.tsx +124 -0
  10. package/cli/templates/navbar.tsx +53 -0
  11. package/cli/templates/pricing-table.tsx +140 -0
  12. package/cli/templates/signup-form.tsx +111 -0
  13. package/demo.tape +2 -2
  14. package/examples/saas-dashboard.ebade.yaml +2 -0
  15. package/netlify.toml +7 -0
  16. package/package.json +3 -1
  17. package/packages/mcp-server/README.md +3 -3
  18. package/packages/mcp-server/package.json +2 -2
  19. package/packages/mcp-server/src/index.ts +12 -16
  20. package/packages/mcp-server/src/tools/scaffold.ts +153 -404
  21. package/packages/vscode-extension/README.md +45 -0
  22. package/packages/vscode-extension/ebade-0.1.0.vsix +0 -0
  23. package/packages/vscode-extension/ebade-0.3.0.vsix +0 -0
  24. package/packages/vscode-extension/ebade-0.3.1.vsix +0 -0
  25. package/packages/vscode-extension/ebade-0.3.2.vsix +0 -0
  26. package/packages/vscode-extension/images/icon.png +0 -0
  27. package/packages/vscode-extension/language-configuration.json +24 -0
  28. package/packages/vscode-extension/package.json +54 -0
  29. package/packages/vscode-extension/snippets/ebade.json +86 -0
  30. package/packages/vscode-extension/syntaxes/ebade.tmLanguage.json +54 -0
  31. package/www/README.md +36 -0
  32. package/www/app/favicon.ico +0 -0
  33. package/www/app/globals.css +1256 -0
  34. package/www/app/layout.tsx +66 -0
  35. package/www/app/page.tsx +374 -0
  36. package/www/app/playground/page.tsx +627 -0
  37. package/www/components/ThreeCanvas.tsx +156 -0
  38. package/www/next.config.ts +19 -0
  39. package/www/package-lock.json +1779 -0
  40. package/www/package.json +27 -0
  41. package/www/postcss.config.mjs +7 -0
  42. package/www/public/logo.png +0 -0
  43. package/www/tsconfig.json +42 -0
  44. package/landing/index.html +0 -237
  45. package/landing/main.js +0 -147
  46. package/landing/style.css +0 -616
  47. /package/{demo.gif → assets/demo.gif} +0 -0
  48. /package/{demo.mp4 → assets/demo.mp4} +0 -0
  49. /package/{landing → www/public}/_headers +0 -0
  50. /package/{landing → www/public}/favicon.svg +0 -0
  51. /package/{landing → www/public}/og-image.png +0 -0
  52. /package/{landing → www/public}/og-readme.png +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Scaffold Tool
3
3
  *
4
- * Creates a complete project structure from an intent definition.
4
+ * v0.3.1 - Integrated with standard ebade templates
5
5
  */
6
6
 
7
7
  import fs from "fs";
@@ -10,465 +10,214 @@ import yaml from "yaml";
10
10
 
11
11
  interface ScaffoldArgs {
12
12
  projectName: string;
13
- projectType: string;
14
- features?: string[];
13
+ projectType:
14
+ | "SaaS Dashboard"
15
+ | "E-commerce"
16
+ | "Landing Page"
17
+ | "Empty Project";
18
+ primaryColor?: string;
15
19
  outputDir: string;
16
20
  }
17
21
 
18
- // Project type templates
19
- const projectTemplates: Record<string, any> = {
20
- "e-commerce": {
22
+ const TEMPLATES: Record<string, any> = {
23
+ "SaaS Dashboard": {
24
+ features: ["user-auth", "billing", "analytics", "team-management"],
21
25
  pages: [
22
26
  {
23
27
  path: "/",
24
28
  intent: "landing-page",
25
- components: ["hero-section", "featured-products", "testimonials"],
26
- },
27
- {
28
- path: "/products",
29
- intent: "product-listing",
30
- components: ["search-bar", "filter-sidebar", "product-grid"],
31
- },
32
- {
33
- path: "/products/[slug]",
34
- intent: "product-detail",
35
- components: [
36
- "product-gallery",
37
- "product-info",
38
- "add-to-cart",
39
- "reviews",
40
- ],
41
- },
42
- {
43
- path: "/cart",
44
- intent: "shopping-cart",
45
- components: ["cart-items", "cart-summary", "checkout-cta"],
46
- },
47
- {
48
- path: "/checkout",
49
- intent: "checkout-flow",
50
- auth: "required",
51
- components: ["checkout-form", "payment-section", "order-summary"],
52
- },
53
- ],
54
- data: {
55
- Product: {
56
- fields: {
57
- id: { type: "uuid", required: true },
58
- name: { type: "string", required: true },
59
- price: { type: "decimal", required: true },
60
- images: { type: "array" },
61
- category: { type: "string" },
62
- stock: { type: "integer" },
63
- },
64
- },
65
- Cart: {
66
- fields: {
67
- id: { type: "uuid", required: true },
68
- items: { type: "array", required: true },
69
- total: { type: "decimal", required: true },
70
- },
71
- },
72
- },
73
- },
74
- "saas-dashboard": {
75
- pages: [
76
- {
77
- path: "/",
78
- intent: "landing-page",
79
- components: ["hero", "features", "pricing", "cta"],
29
+ components: ["hero", "features", "pricing"],
80
30
  },
81
31
  {
82
32
  path: "/dashboard",
83
- intent: "dashboard",
33
+ intent: "main-dashboard",
84
34
  auth: "required",
85
- components: ["stats-cards", "chart-section", "recent-activity"],
35
+ components: ["stats-grid", "activity-chart"],
86
36
  },
87
37
  {
88
38
  path: "/settings",
89
- intent: "settings",
39
+ intent: "user-settings",
90
40
  auth: "required",
91
- components: ["profile-form", "security-settings", "billing-section"],
41
+ components: ["profile-form", "security"],
92
42
  },
93
43
  ],
94
- data: {
95
- User: {
96
- fields: {
97
- id: { type: "uuid", required: true },
98
- email: { type: "string", required: true, unique: true },
99
- name: { type: "string", required: true },
100
- plan: { type: "enum" },
101
- },
102
- },
103
- },
44
+ design: { style: "modern-glassmorphism", borderRadius: "xl" },
104
45
  },
105
- blog: {
46
+ "E-commerce": {
47
+ features: ["product-catalog", "shopping-cart", "checkout", "search"],
106
48
  pages: [
107
49
  {
108
50
  path: "/",
109
- intent: "blog-home",
110
- components: ["featured-post", "post-grid", "newsletter"],
51
+ intent: "home-page",
52
+ components: ["hero-section", "product-grid"],
111
53
  },
112
54
  {
113
- path: "/posts",
114
- intent: "post-listing",
115
- components: ["post-list", "search", "categories"],
55
+ path: "/cart",
56
+ intent: "shopping-cart",
57
+ components: ["cart-list", "checkout-cta"],
116
58
  },
117
59
  {
118
- path: "/posts/[slug]",
119
- intent: "post-detail",
120
- components: ["post-content", "author-bio", "comments", "related-posts"],
60
+ path: "/checkout",
61
+ intent: "checkout-flow",
62
+ auth: "required",
63
+ components: ["payment-form"],
121
64
  },
122
65
  ],
123
- data: {
124
- Post: {
125
- fields: {
126
- id: { type: "uuid", required: true },
127
- title: { type: "string", required: true },
128
- slug: { type: "string", required: true, unique: true },
129
- content: { type: "text", required: true },
130
- publishedAt: { type: "timestamp" },
131
- },
132
- },
133
- },
66
+ design: { style: "clean-minimal", borderRadius: "md" },
134
67
  },
135
- "landing-page": {
68
+ "Landing Page": {
69
+ features: ["hero", "features", "testimonials", "cta", "footer"],
136
70
  pages: [
137
71
  {
138
72
  path: "/",
139
- intent: "landing",
73
+ intent: "landing-page",
140
74
  components: [
141
75
  "hero",
142
- "features",
143
- "testimonials",
144
- "pricing",
145
- "faq",
146
- "cta",
147
- "footer",
76
+ "feature-grid",
77
+ "testimonial-slider",
78
+ "cta-section",
148
79
  ],
149
80
  },
150
81
  ],
151
- data: {},
82
+ design: { style: "premium-dark", borderRadius: "full" },
152
83
  },
153
- portfolio: {
154
- pages: [
155
- {
156
- path: "/",
157
- intent: "portfolio-home",
158
- components: ["hero", "about", "projects-grid", "skills", "contact"],
159
- },
160
- {
161
- path: "/projects/[slug]",
162
- intent: "project-detail",
163
- components: ["project-gallery", "project-info", "tech-stack"],
164
- },
165
- ],
166
- data: {
167
- Project: {
168
- fields: {
169
- id: { type: "uuid", required: true },
170
- title: { type: "string", required: true },
171
- description: { type: "text" },
172
- images: { type: "array" },
173
- link: { type: "string" },
174
- },
175
- },
176
- },
177
- },
178
- };
179
-
180
- // Component templates
181
- const componentTemplates: Record<string, (name: string) => string> = {
182
- default: (name: string) => {
183
- const pascalName = name
184
- .split("-")
185
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
186
- .join("");
187
- return `"use client";
188
-
189
- import { useState } from "react";
190
-
191
- interface ${pascalName}Props {
192
- className?: string;
193
- }
194
-
195
- export function ${pascalName}({ className }: ${pascalName}Props) {
196
- return (
197
- <section className={\`${name} \${className || ""}\`}>
198
- <div className="container">
199
- {/* ${pascalName} content */}
200
- <h2>${pascalName}</h2>
201
- </div>
202
- </section>
203
- );
204
- }
205
- `;
84
+ "Empty Project": {
85
+ features: [],
86
+ pages: [{ path: "/", intent: "index", components: ["welcome"] }],
87
+ design: { style: "minimal", borderRadius: "md" },
206
88
  },
207
89
  };
208
90
 
209
- // Page template
210
- function generatePageTemplate(page: any): string {
211
- const componentImports = (page.components || [])
212
- .map((c: string) => {
213
- const pascalName = c
214
- .split("-")
215
- .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
216
- .join("");
217
- return `import { ${pascalName} } from "@/components/${c}";`;
218
- })
219
- .join("\n");
91
+ export async function scaffoldProject(args: ScaffoldArgs): Promise<string> {
92
+ const {
93
+ projectName,
94
+ projectType,
95
+ primaryColor = "#6366f1",
96
+ outputDir,
97
+ } = args;
220
98
 
221
- const componentUsage = (page.components || [])
222
- .map((c: string) => {
223
- const pascalName = c
224
- .split("-")
225
- .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
226
- .join("");
227
- return ` <${pascalName} />`;
228
- })
229
- .join("\n");
230
-
231
- const pageName = page.intent
232
- .split("-")
233
- .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
234
- .join("");
235
-
236
- return `/**
237
- * @page('${page.path}')
238
- * @ebade('${page.intent}')
239
- * ${page.auth ? `@requires({ auth: '${page.auth}' })` : ""}
240
- */
241
-
242
- ${componentImports}
243
-
244
- export default function ${pageName}Page() {
245
- return (
246
- <main className="page ${page.intent}">
247
- ${componentUsage}
248
- </main>
249
- );
250
- }
251
- `;
252
- }
99
+ const template = TEMPLATES[projectType];
100
+ const projectDir = path.join(outputDir, projectName);
253
101
 
254
- // Generate package.json
255
- function generatePackageJson(projectName: string, projectType: string): string {
256
- return JSON.stringify(
257
- {
102
+ try {
103
+ // 1. Create directory structure
104
+ const dirs = [
105
+ "app/api",
106
+ "components",
107
+ "lib",
108
+ "styles",
109
+ "public",
110
+ "types",
111
+ "database",
112
+ ];
113
+ if (!fs.existsSync(projectDir))
114
+ fs.mkdirSync(projectDir, { recursive: true });
115
+
116
+ dirs.forEach((d) => {
117
+ const fullPath = path.join(projectDir, d);
118
+ if (!fs.existsSync(fullPath)) fs.mkdirSync(fullPath, { recursive: true });
119
+ });
120
+
121
+ // 2. Generate ebade file
122
+ const ebadeConfig = {
258
123
  name: projectName,
259
- version: "0.1.0",
260
- private: true,
261
- scripts: {
262
- dev: "next dev",
263
- build: "next build",
264
- start: "next start",
265
- lint: "next lint",
266
- },
267
- dependencies: {
268
- next: "^14.0.0",
269
- react: "^18.2.0",
270
- "react-dom": "^18.2.0",
271
- },
272
- devDependencies: {
273
- "@types/node": "^20.0.0",
274
- "@types/react": "^18.2.0",
275
- typescript: "^5.3.0",
276
- },
277
- ebade: {
278
- type: projectType,
279
- version: "0.1.0",
280
- },
281
- },
282
- null,
283
- 2
284
- );
285
- }
286
-
287
- // Generate globals.css
288
- function generateGlobalsCss(): string {
289
- return `/* ebade Generated Design System */
290
-
291
- :root {
292
- --color-primary: #6366f1;
293
- --color-secondary: #f59e0b;
294
- --color-accent: #10b981;
295
-
296
- --font-family: 'Inter', system-ui, sans-serif;
297
-
298
- --radius-sm: 0.25rem;
299
- --radius-md: 0.5rem;
300
- --radius-lg: 1rem;
301
- }
302
-
303
- * {
304
- box-sizing: border-box;
305
- margin: 0;
306
- padding: 0;
307
- }
308
-
309
- body {
310
- font-family: var(--font-family);
311
- line-height: 1.6;
312
- color: #1f2937;
313
- }
314
-
315
- .container {
316
- max-width: 1200px;
317
- margin: 0 auto;
318
- padding: 0 1rem;
319
- }
124
+ type: projectType.toLowerCase().replace(" ", "-"),
125
+ description: `Generated ${projectType} via ebade MCP`,
126
+ features: template.features,
127
+ pages: template.pages,
128
+ design: {
129
+ ...template.design,
130
+ colors: { primary: primaryColor },
131
+ },
132
+ };
133
+
134
+ fs.writeFileSync(
135
+ path.join(projectDir, "project.ebade.yaml"),
136
+ yaml.stringify(ebadeConfig)
137
+ );
320
138
 
321
- .page {
322
- min-height: 100vh;
323
- }
324
- `;
325
- }
139
+ // 3. Generate Next.js files (Basic mocks to match CLI)
140
+ generateCoreFiles(projectDir, ebadeConfig);
326
141
 
327
- export async function scaffoldProject(args: ScaffoldArgs) {
328
- const { projectName, projectType, features = [], outputDir } = args;
142
+ return `✅ ebade v0.3.1 Scaffold Complete!
143
+
144
+ Project: ${projectName}
145
+ Type: ${projectType}
146
+ Dir: ${projectDir}
329
147
 
330
- // Get template for project type
331
- const template = projectTemplates[projectType];
332
- if (!template) {
148
+ Next Steps:
149
+ 1. Open this folder in Cursor/Claude
150
+ 2. Run 'npx ebade dev project.ebade.yaml' to start the agent engine.`;
151
+ } catch (error) {
333
152
  throw new Error(
334
- `Unknown project type: ${projectType}. Available: ${Object.keys(
335
- projectTemplates
336
- ).join(", ")}`
153
+ `Scaffold failed: ${
154
+ error instanceof Error ? error.message : String(error)
155
+ }`
337
156
  );
338
157
  }
158
+ }
339
159
 
340
- // Create project directory
341
- const projectDir = path.join(outputDir, projectName);
342
-
343
- // Create directory structure
344
- const dirs = ["", "app", "components", "lib", "styles", "public", "types"];
345
-
346
- for (const dir of dirs) {
347
- const fullPath = path.join(projectDir, dir);
348
- if (!fs.existsSync(fullPath)) {
349
- fs.mkdirSync(fullPath, { recursive: true });
350
- }
351
- }
352
-
353
- // Track created files
354
- const createdFiles: string[] = [];
355
-
356
- // Generate pages
357
- for (const page of template.pages) {
358
- // Create page directory if needed
359
- const pagePath =
360
- page.path === "/"
361
- ? "app/page.tsx"
362
- : `app${page.path.replace("[", "(").replace("]", ")")}/page.tsx`;
363
-
364
- const pageDir = path.dirname(path.join(projectDir, pagePath));
365
- if (!fs.existsSync(pageDir)) {
366
- fs.mkdirSync(pageDir, { recursive: true });
367
- }
368
-
369
- // Write page file
370
- const pageContent = generatePageTemplate(page);
371
- fs.writeFileSync(path.join(projectDir, pagePath), pageContent);
372
- createdFiles.push(pagePath);
373
-
374
- // Generate component files
375
- for (const component of page.components || []) {
376
- const componentPath = `components/${component}.tsx`;
377
- const fullComponentPath = path.join(projectDir, componentPath);
378
-
379
- if (!fs.existsSync(fullComponentPath)) {
380
- const componentContent = componentTemplates.default(component);
381
- fs.writeFileSync(fullComponentPath, componentContent);
382
- createdFiles.push(componentPath);
383
- }
384
- }
385
- }
386
-
387
- // Generate package.json
160
+ function generateCoreFiles(projectDir: string, config: any) {
161
+ // package.json
162
+ const pkg = {
163
+ name: config.name,
164
+ version: "0.1.0",
165
+ dependencies: {
166
+ next: "latest",
167
+ react: "latest",
168
+ "react-dom": "latest",
169
+ "lucide-react": "latest",
170
+ },
171
+ devDependencies: {
172
+ "@types/node": "latest",
173
+ "@types/react": "latest",
174
+ "@types/react-dom": "latest",
175
+ autoprefixer: "latest",
176
+ postcss: "latest",
177
+ tailwindcss: "latest",
178
+ typescript: "latest",
179
+ },
180
+ scripts: { dev: "next dev", build: "next build", start: "next start" },
181
+ };
388
182
  fs.writeFileSync(
389
183
  path.join(projectDir, "package.json"),
390
- generatePackageJson(projectName, projectType)
184
+ JSON.stringify(pkg, null, 2)
391
185
  );
392
- createdFiles.push("package.json");
393
186
 
394
- // Generate globals.css
395
- fs.writeFileSync(
396
- path.join(projectDir, "styles/globals.css"),
397
- generateGlobalsCss()
398
- );
399
- createdFiles.push("styles/globals.css");
400
-
401
- // Generate intent file for reference
402
- const intentContent = yaml.stringify({
403
- name: projectName,
404
- type: projectType,
405
- features: features,
406
- pages: template.pages,
407
- data: template.data,
408
- design: {
409
- style: "minimal-modern",
410
- colors: {
411
- primary: "#6366f1",
412
- secondary: "#f59e0b",
413
- accent: "#10b981",
414
- },
187
+ // tsconfig.json (Basic version)
188
+ const tsconfig = {
189
+ compilerOptions: {
190
+ target: "es5",
191
+ lib: ["dom", "dom.iterable", "esnext"],
192
+ allowJs: true,
193
+ skipLibCheck: true,
194
+ strict: true,
195
+ noEmit: true,
196
+ esModuleInterop: true,
197
+ module: "esnext",
198
+ moduleResolution: "bundler",
199
+ resolveJsonModule: true,
200
+ isolatedModules: true,
201
+ jsx: "preserve",
202
+ incremental: true,
203
+ plugins: [{ name: "next" }],
204
+ paths: { "@/*": ["./*"] },
415
205
  },
416
- });
417
- fs.writeFileSync(path.join(projectDir, "project.ebade.yaml"), intentContent);
418
- createdFiles.push("project.ebade.yaml");
419
-
420
- // Generate layout
421
- const layoutContent = `import "./globals.css";
422
- import type { Metadata } from "next";
423
- import { Inter } from "next/font/google";
424
-
425
- const inter = Inter({ subsets: ["latin"] });
426
-
427
- export const metadata: Metadata = {
428
- title: "${projectName}",
429
- description: "Built with ebade",
430
- };
431
-
432
- export default function RootLayout({
433
- children,
434
- }: {
435
- children: React.ReactNode;
436
- }) {
437
- return (
438
- <html lang="en">
439
- <body className={inter.className}>{children}</body>
440
- </html>
441
- );
442
- }
443
- `;
444
- fs.writeFileSync(path.join(projectDir, "app/layout.tsx"), layoutContent);
445
- // Copy globals.css to app folder for Next.js
206
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
207
+ exclude: ["node_modules"],
208
+ };
446
209
  fs.writeFileSync(
447
- path.join(projectDir, "app/globals.css"),
448
- generateGlobalsCss()
210
+ path.join(projectDir, "tsconfig.json"),
211
+ JSON.stringify(tsconfig, null, 2)
449
212
  );
450
- createdFiles.push("app/layout.tsx", "app/globals.css");
451
-
452
- return {
453
- content: [
454
- {
455
- type: "text",
456
- text: `✅ Successfully scaffolded "${projectName}" (${projectType})
457
213
 
458
- 📁 Project created at: ${projectDir}
459
-
460
- 📄 Files created:
461
- ${createdFiles.map((f) => ` • ${f}`).join("\n")}
462
-
463
- 🚀 Next steps:
464
- 1. cd ${projectDir}
465
- 2. npm install
466
- 3. npm run dev
467
-
468
- 📋 ebade file saved as: project.ebade.yaml
469
- This can be used by AI agents to understand and iterate on the project.
470
- `,
471
- },
472
- ],
473
- };
214
+ // .cursorrules
215
+ const rules = `
216
+ # ebade Agent Rules for ${config.name}
217
+ - Respect project.ebade.yaml as the source of truth.
218
+ - Follow the ${config.design.style} design style.
219
+ - Use ${config.design.colors.primary} as the primary color.
220
+ - Always import React from 'react' in .tsx files.
221
+ `;
222
+ fs.writeFileSync(path.join(projectDir, ".cursorrules"), rules);
474
223
  }
@@ -0,0 +1,45 @@
1
+ # ebade VS Code Extension
2
+
3
+ Syntax highlighting, snippets, and language support for `.ebade.yaml` files.
4
+
5
+ ## Features
6
+
7
+ ### 🎨 Syntax Highlighting
8
+
9
+ - Keywords: `project`, `pages`, `api`, `data`, `design`
10
+ - Decorators: `@page`, `@intent`, `@requires`
11
+ - Strings, numbers, booleans
12
+ - Comments
13
+
14
+ ### ✨ Snippets (v0.3.2 Updates)
15
+
16
+ | Prefix | Description |
17
+ | :--- | :--- |
18
+ | `eb-saas` | Full SaaS Dashboard template |
19
+ | `eb-shop` | Full E-commerce store template |
20
+ | `eb-page` | New page definition |
21
+ | `eb-comp` | Component reference |
22
+
23
+ ## Installation
24
+
25
+ ### From VSIX (Local)
26
+
27
+ ```bash
28
+ cd packages/vscode-extension
29
+ npx vsce package
30
+ code --install-extension ebade-*.vsix
31
+ ```
32
+
33
+ ### From Marketplace (Coming Soon)
34
+
35
+ Search for "ebade" in VS Code Extensions.
36
+
37
+ ## Usage
38
+
39
+ 1. Create a file with `.ebade.yaml` extension
40
+ 2. Start typing `eb-` to see available snippets
41
+ 3. Enjoy syntax highlighting! 🎉
42
+
43
+ ---
44
+
45
+ Built with 💜 by the ebade team.