ebade 0.4.5 โ 0.4.6
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/.agent/workflows/prompt-to-product.md +40 -0
- package/CHANGELOG.md +24 -0
- package/cli/scaffold.js +351 -68
- package/package.json +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/index.ts +41 -2
- package/packages/mcp-server/src/tools/build.ts +57 -0
- package/tests/cli/build.test.js +66 -0
- package/tests/framework/architect.test.js +102 -0
- package/www/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: ebade Prompt-to-Product Workflow
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# ๐ฃ ebade Prompt-to-Product Workflow
|
|
6
|
+
|
|
7
|
+
This workflow describes how an AI Agent should use the ebade framework to transform a user's natural language intent into a production-ready codebase.
|
|
8
|
+
|
|
9
|
+
## 1. Initial Project Scaffolding
|
|
10
|
+
When a user asks to "build", "create", or "start" a new web application:
|
|
11
|
+
|
|
12
|
+
// turbo
|
|
13
|
+
1. Use the `ebade build` command with the user's prompt.
|
|
14
|
+
```bash
|
|
15
|
+
node cli/scaffold.js build "[User's Prompt]"
|
|
16
|
+
```
|
|
17
|
+
*This tool will automatically choose the project type, color palette, and initial components.*
|
|
18
|
+
|
|
19
|
+
2. Navigate into the generated project directory.
|
|
20
|
+
|
|
21
|
+
## 2. Understanding the Architecture
|
|
22
|
+
ebade generates a `project.ebade.yaml` file in the project root. This is your **Source of Truth**.
|
|
23
|
+
1. Read `project.ebade.yaml` to understand the pages, components, and data models.
|
|
24
|
+
2. Respect the design system tokens in `app/globals.css`.
|
|
25
|
+
|
|
26
|
+
## 3. Iterative Development
|
|
27
|
+
Once the scaffold is ready, move from "Architect" to "Engineer":
|
|
28
|
+
|
|
29
|
+
1. **Refine Components**: Locate generated components in `components/`. They are high-quality visual placeholders. Add the specific business logic, API calls, or complex state management requested by the user.
|
|
30
|
+
2. **Implement API Logic**: Fill the `app/api/` route handlers with real backend logic (database queries, external service integrations).
|
|
31
|
+
3. **Data Schemas**: Use the generated SQL in `database/schema.sql` to set up the actual database.
|
|
32
|
+
|
|
33
|
+
## 4. Expanding the Project
|
|
34
|
+
To add new features:
|
|
35
|
+
1. Update `project.ebade.yaml` with the new intent.
|
|
36
|
+
2. Run `node cli/scaffold.js scaffold project.ebade.yaml .` to generate the new files while preserving your logic.
|
|
37
|
+
3. Apply the specific implementation details to the new files.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
*Built for the Agent-First Era. ebade empowers you to focus on logic, while we handle the architecture.*
|
package/CHANGELOG.md
CHANGED
|
@@ -5,20 +5,44 @@ 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.6] - 2025-01-10
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **EbadeArchitect (The Brain)**: New core engine that translates natural language prompts into structured `ebade` configurations.
|
|
13
|
+
- **`ebade build <prompt>`**: A "one-shot" project creation command. Designs, scaffolds, and prepares a project in seconds.
|
|
14
|
+
- **Architect Intelligence**: Projects now gain context-aware page structures (E-commerce get cart/products, Blog get posts, etc.) and smarter component selection via Regex.
|
|
15
|
+
- **Auto-Environment**: Automatically generates `.gitignore` and `.env.example` for professional project starts.
|
|
16
|
+
- **Agent Workflow Documentation**: Added `.agent/workflows/prompt-to-product.md` to guide AI agents on using the new tools.
|
|
17
|
+
- **MCP Server v0.4.6**: Integrated `ebade_build` as a tool for otonomous agent usage.
|
|
18
|
+
- **Testing Layer**: Added internal testing suites for both the framework logic and CLI integration.
|
|
19
|
+
- **CLI Branding**: Updated to `v0.4.6` with improved help menus and creative briefs.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Fixed CLI entry point to allow safe imports of `scaffold.js` without triggering help output.
|
|
24
|
+
- Improved project naming logic to filter out filler words (e.g., "Can you make a...").
|
|
25
|
+
- Fixed component integrity check to correctly locate test files in `tests/components/`.
|
|
26
|
+
|
|
27
|
+
## [0.4.5] - 2025-01-10
|
|
28
|
+
...
|
|
8
29
|
## [0.4.5] - 2025-01-10
|
|
9
30
|
|
|
10
31
|
### Added
|
|
32
|
+
|
|
11
33
|
- UI/UX Overhaul: Premium dark-mode aesthetic with ambient glow and glassmorphism by default.
|
|
12
34
|
- Dynamic Color Support: Real-time Hex-to-HSL conversion to apply user's primary color choice to CSS variables.
|
|
13
35
|
- Component "Intents": Placeholders are now high-quality "Glass Cards" that look like part of a finished UI.
|
|
14
36
|
- Test Organization: All unit tests are now generated in a centralized `tests/` directory instead of being cluttered with components.
|
|
15
37
|
|
|
16
38
|
### Fixed
|
|
39
|
+
|
|
17
40
|
- API Pathing: Fixed a bug that caused double-nesting (e.g., `/api/api/...`) in generated routes.
|
|
18
41
|
- Removed debug information (headers, route labels) from generated pages for a "turnkey" production feel.
|
|
19
42
|
|
|
20
43
|
## [0.4.4] - 2025-01-10
|
|
21
44
|
|
|
45
|
+
|
|
22
46
|
### Fixed
|
|
23
47
|
- Added `postcss.config.js` generation to enable Tailwind styling in scaffolded projects.
|
|
24
48
|
- Fixed double-nesting issues when initializing projects (removed redundant `projectName/projectName` folder creation).
|
package/cli/scaffold.js
CHANGED
|
@@ -44,7 +44,7 @@ ${colors.magenta} โโโโโโ ${colors.cyan}โโโโโโโโ$
|
|
|
44
44
|
${colors.magenta} โโโโโโโโ${colors.cyan}โโโโโโโโ${colors.magenta}โโโ โโโ${colors.cyan}โโโโโโโโ${colors.magenta}โโโโโโโโ
|
|
45
45
|
${colors.magenta} โโโโโโโโ${colors.cyan}โโโโโโโ ${colors.magenta}โโโ โโโ${colors.cyan}โโโโโโโ ${colors.magenta}โโโโโโโโ${colors.reset}
|
|
46
46
|
|
|
47
|
-
${colors.dim}โจ Agent-First Framework ${colors.yellow}v0.4.
|
|
47
|
+
${colors.dim}โจ Agent-First Framework ${colors.yellow}v0.4.6${colors.reset}
|
|
48
48
|
`;
|
|
49
49
|
|
|
50
50
|
const log = {
|
|
@@ -58,6 +58,166 @@ const log = {
|
|
|
58
58
|
console.log(`\n${colors.bright}${colors.magenta}โธ ${msg}${colors.reset}`),
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// ============================================
|
|
62
|
+
// ebade Architect (The Brain)
|
|
63
|
+
// ============================================
|
|
64
|
+
export class EbadeArchitect {
|
|
65
|
+
static async plan(prompt) {
|
|
66
|
+
const p = prompt.toLowerCase();
|
|
67
|
+
|
|
68
|
+
// Core App Type Detection
|
|
69
|
+
let type = "saas-dashboard";
|
|
70
|
+
if (p.includes("e-commerce") || p.includes("shop") || p.includes("store"))
|
|
71
|
+
type = "e-commerce";
|
|
72
|
+
if (p.includes("blog") || p.includes("article") || p.includes("news"))
|
|
73
|
+
type = "blog";
|
|
74
|
+
if (p.includes("portfolio") || p.includes("personal") || p.includes("cv"))
|
|
75
|
+
type = "portfolio";
|
|
76
|
+
|
|
77
|
+
// Component Intelligence
|
|
78
|
+
const components = ["navbar", "footer"];
|
|
79
|
+
const features = [];
|
|
80
|
+
|
|
81
|
+
const has = (keyword) => new RegExp(`\\b${keyword}`, "i").test(p);
|
|
82
|
+
|
|
83
|
+
if (has("chart") || has("analytics") || has("graph") || has("metric")) {
|
|
84
|
+
components.push("activity-chart", "stats-grid");
|
|
85
|
+
features.push("Advanced Analytics");
|
|
86
|
+
}
|
|
87
|
+
if (has("saas") || has("dashboard") || has("admin")) {
|
|
88
|
+
components.push("sidebar-navigation", "metrics-cards");
|
|
89
|
+
features.push("Admin Dashboard");
|
|
90
|
+
}
|
|
91
|
+
if (has("login") || has("auth") || has("sign") || has("user")) {
|
|
92
|
+
components.push("login-form", "signup-form");
|
|
93
|
+
features.push("Authentication");
|
|
94
|
+
}
|
|
95
|
+
if (has("price") || has("plan") || has("subscribe") || has("billing")) {
|
|
96
|
+
components.push("pricing-table", "cta-banner");
|
|
97
|
+
features.push("Subscription Tiers");
|
|
98
|
+
}
|
|
99
|
+
if (has("testim") || has("review") || has("social proof"))
|
|
100
|
+
components.push("testimonials-grid");
|
|
101
|
+
if (has("contact") || has("form") || has("help") || has("support"))
|
|
102
|
+
components.push("contact-form");
|
|
103
|
+
if (has("faq") || has("question")) components.push("faq-accordion");
|
|
104
|
+
|
|
105
|
+
// Dynamic Color Palette (Order matters - specific colors should win over vibes)
|
|
106
|
+
let primary = "#6366f1"; // Indigo default
|
|
107
|
+
if (has("gold") || has("luxury") || has("premium") || has("exclusive"))
|
|
108
|
+
primary = "#fbbf24";
|
|
109
|
+
if (has("green") || has("eco") || has("emerald") || has("nature"))
|
|
110
|
+
primary = "#10b981";
|
|
111
|
+
if (
|
|
112
|
+
has("blue") ||
|
|
113
|
+
has("ocean") ||
|
|
114
|
+
has("sky") ||
|
|
115
|
+
has("trust") ||
|
|
116
|
+
has("corp")
|
|
117
|
+
)
|
|
118
|
+
primary = "#3b82f6";
|
|
119
|
+
if (has("violet") || has("purple") || has("creative") || has("design"))
|
|
120
|
+
primary = "#8b5cf6";
|
|
121
|
+
if (has("orange") || has("fire") || has("warm") || has("brand"))
|
|
122
|
+
primary = "#f59e0b";
|
|
123
|
+
if (has("red") || has("danger") || has("hot") || has("love"))
|
|
124
|
+
primary = "#ef4444";
|
|
125
|
+
|
|
126
|
+
// Smart Pages based on Type
|
|
127
|
+
const pages = [
|
|
128
|
+
{
|
|
129
|
+
path: "/",
|
|
130
|
+
intent: "landing-page",
|
|
131
|
+
components: components.filter((c) =>
|
|
132
|
+
[
|
|
133
|
+
"navbar",
|
|
134
|
+
"hero-section",
|
|
135
|
+
"pricing-table",
|
|
136
|
+
"testimonials-grid",
|
|
137
|
+
"cta-banner",
|
|
138
|
+
"footer",
|
|
139
|
+
].includes(c)
|
|
140
|
+
),
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
if (type === "saas-dashboard") {
|
|
145
|
+
pages.push({
|
|
146
|
+
path: "/dashboard",
|
|
147
|
+
intent: "main-dashboard",
|
|
148
|
+
components: components.filter((c) =>
|
|
149
|
+
[
|
|
150
|
+
"sidebar-navigation",
|
|
151
|
+
"stats-grid",
|
|
152
|
+
"activity-chart",
|
|
153
|
+
"metrics-cards",
|
|
154
|
+
].includes(c)
|
|
155
|
+
),
|
|
156
|
+
});
|
|
157
|
+
} else if (type === "e-commerce") {
|
|
158
|
+
pages.push(
|
|
159
|
+
{
|
|
160
|
+
path: "/products",
|
|
161
|
+
intent: "product-list",
|
|
162
|
+
components: ["product-grid"],
|
|
163
|
+
},
|
|
164
|
+
{ path: "/cart", intent: "shopping-cart", components: ["cart-list"] }
|
|
165
|
+
);
|
|
166
|
+
} else if (type === "blog") {
|
|
167
|
+
pages.push(
|
|
168
|
+
{ path: "/posts", intent: "blog-index", components: ["post-list"] },
|
|
169
|
+
{
|
|
170
|
+
path: "/posts/[slug]",
|
|
171
|
+
intent: "blog-post",
|
|
172
|
+
components: ["post-body"],
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Build Internal Intent
|
|
178
|
+
const config = {
|
|
179
|
+
name:
|
|
180
|
+
p
|
|
181
|
+
.split(" ")
|
|
182
|
+
.filter(
|
|
183
|
+
(w) =>
|
|
184
|
+
![
|
|
185
|
+
"can",
|
|
186
|
+
"you",
|
|
187
|
+
"make",
|
|
188
|
+
"create",
|
|
189
|
+
"a",
|
|
190
|
+
"an",
|
|
191
|
+
"the",
|
|
192
|
+
"with",
|
|
193
|
+
"please",
|
|
194
|
+
"super",
|
|
195
|
+
"ultra",
|
|
196
|
+
"is",
|
|
197
|
+
"for",
|
|
198
|
+
"me",
|
|
199
|
+
"my",
|
|
200
|
+
"and",
|
|
201
|
+
].includes(w.toLowerCase())
|
|
202
|
+
)
|
|
203
|
+
.slice(0, 2)
|
|
204
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
205
|
+
.join("") || "EbadeApp",
|
|
206
|
+
type: type,
|
|
207
|
+
description: prompt,
|
|
208
|
+
features: features.length > 0 ? features : ["Turnkey Scaffold"],
|
|
209
|
+
design: {
|
|
210
|
+
colors: { primary },
|
|
211
|
+
style: "glass-modern",
|
|
212
|
+
},
|
|
213
|
+
pages: pages,
|
|
214
|
+
api: [{ path: "/api/data", methods: ["GET"], intent: "fetch-data" }],
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return config;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
61
221
|
// ============================================
|
|
62
222
|
// ebade Parser
|
|
63
223
|
// ============================================
|
|
@@ -536,6 +696,61 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
536
696
|
`;
|
|
537
697
|
}
|
|
538
698
|
|
|
699
|
+
function generateGitignore() {
|
|
700
|
+
return `# dependencies
|
|
701
|
+
/node_modules
|
|
702
|
+
/.pnp
|
|
703
|
+
.pnp.js
|
|
704
|
+
|
|
705
|
+
# testing
|
|
706
|
+
/coverage
|
|
707
|
+
|
|
708
|
+
# next.js
|
|
709
|
+
/.next/
|
|
710
|
+
/out/
|
|
711
|
+
|
|
712
|
+
# production
|
|
713
|
+
/build
|
|
714
|
+
|
|
715
|
+
# misc
|
|
716
|
+
.DS_Store
|
|
717
|
+
*.pem
|
|
718
|
+
|
|
719
|
+
# debug
|
|
720
|
+
npm-debug.log*
|
|
721
|
+
yarn-debug.log*
|
|
722
|
+
yarn-error.log*
|
|
723
|
+
|
|
724
|
+
# local env files
|
|
725
|
+
.env*.local
|
|
726
|
+
.env
|
|
727
|
+
|
|
728
|
+
# vercel
|
|
729
|
+
.vercel
|
|
730
|
+
|
|
731
|
+
# typescript
|
|
732
|
+
*.tsbuildinfo
|
|
733
|
+
next-env.d.ts
|
|
734
|
+
`;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function generateEnvExample(config) {
|
|
738
|
+
return `# ebade Generated Environment Variables
|
|
739
|
+
# Project: ${config.name}
|
|
740
|
+
|
|
741
|
+
# Database (Supabase / Postgres)
|
|
742
|
+
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
|
|
743
|
+
|
|
744
|
+
# Authentication (NextAuth / Clerk)
|
|
745
|
+
NEXTAUTH_SECRET="your-secret-here"
|
|
746
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
|
|
747
|
+
|
|
748
|
+
# API Keys
|
|
749
|
+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=""
|
|
750
|
+
STRIPE_SECRET_KEY=""
|
|
751
|
+
`;
|
|
752
|
+
}
|
|
753
|
+
|
|
539
754
|
// ============================================
|
|
540
755
|
// Design System CSS Generator
|
|
541
756
|
// ============================================
|
|
@@ -621,21 +836,22 @@ Built with ebade - The Agent-First Framework for the next era of development.
|
|
|
621
836
|
// ============================================
|
|
622
837
|
// Utility Functions
|
|
623
838
|
// ============================================
|
|
624
|
-
function toPascalCase(str) {
|
|
839
|
+
export function toPascalCase(str) {
|
|
625
840
|
return str
|
|
626
|
-
.split(
|
|
841
|
+
.split(/[-_]/)
|
|
627
842
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
628
843
|
.join("");
|
|
629
844
|
}
|
|
630
845
|
|
|
631
|
-
function toSnakeCase(str) {
|
|
846
|
+
export function toSnakeCase(str) {
|
|
632
847
|
return str
|
|
633
848
|
.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
|
849
|
+
.replace(/-/g, "_")
|
|
634
850
|
.toLowerCase()
|
|
635
851
|
.replace(/^_/, "");
|
|
636
852
|
}
|
|
637
853
|
|
|
638
|
-
function hexToHsl(hex) {
|
|
854
|
+
export function hexToHsl(hex) {
|
|
639
855
|
let r = 0,
|
|
640
856
|
g = 0,
|
|
641
857
|
b = 0;
|
|
@@ -882,6 +1098,17 @@ async function scaffold(ebadePath, outputDir) {
|
|
|
882
1098
|
);
|
|
883
1099
|
log.file("vitest.config.ts");
|
|
884
1100
|
|
|
1101
|
+
// .gitignore
|
|
1102
|
+
fs.writeFileSync(path.join(projectDir, ".gitignore"), generateGitignore());
|
|
1103
|
+
log.file(".gitignore");
|
|
1104
|
+
|
|
1105
|
+
// .env.example
|
|
1106
|
+
fs.writeFileSync(
|
|
1107
|
+
path.join(projectDir, ".env.example"),
|
|
1108
|
+
generateEnvExample(config)
|
|
1109
|
+
);
|
|
1110
|
+
log.file(".env.example");
|
|
1111
|
+
|
|
885
1112
|
// app/layout.tsx
|
|
886
1113
|
fs.writeFileSync(
|
|
887
1114
|
path.join(projectDir, "app/layout.tsx"),
|
|
@@ -1114,12 +1341,14 @@ ${colors.dim}Usage:${colors.reset}
|
|
|
1114
1341
|
|
|
1115
1342
|
${colors.dim}Commands:${colors.reset}
|
|
1116
1343
|
init Create a new ebade project interactively
|
|
1344
|
+
build <prompt> Generate and scaffold a project from a natural language prompt
|
|
1117
1345
|
scaffold <file> [output] Scaffold a project from ebade file
|
|
1118
1346
|
dev <file> [output] Watch ebade file and re-scaffold on changes
|
|
1119
1347
|
playground Open the ebade playground
|
|
1120
1348
|
|
|
1121
1349
|
${colors.dim}Examples:${colors.reset}
|
|
1122
1350
|
npx ebade init
|
|
1351
|
+
npx ebade build "A violet themed crypto dashboard"
|
|
1123
1352
|
npx ebade scaffold examples/saas-dashboard.ebade.yaml ./output
|
|
1124
1353
|
npx ebade dev my-project.ebade.yaml ./my-app
|
|
1125
1354
|
|
|
@@ -1283,80 +1512,134 @@ ${LOGO}
|
|
|
1283
1512
|
// ============================================
|
|
1284
1513
|
// Command Router
|
|
1285
1514
|
// ============================================
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
) {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1515
|
+
const isMain =
|
|
1516
|
+
process.argv[1] &&
|
|
1517
|
+
(process.argv[1].endsWith("scaffold.js") ||
|
|
1518
|
+
process.argv[1].endsWith("ebade"));
|
|
1519
|
+
|
|
1520
|
+
if (isMain) {
|
|
1521
|
+
if (
|
|
1522
|
+
args.length === 0 ||
|
|
1523
|
+
command === "help" ||
|
|
1524
|
+
command === "--help" ||
|
|
1525
|
+
command === "-h"
|
|
1526
|
+
) {
|
|
1527
|
+
showHelp();
|
|
1528
|
+
process.exit(0);
|
|
1529
|
+
}
|
|
1295
1530
|
|
|
1296
|
-
if (command === "init") {
|
|
1297
|
-
|
|
1298
|
-
} else if (command === "scaffold") {
|
|
1299
|
-
|
|
1300
|
-
|
|
1531
|
+
if (command === "init") {
|
|
1532
|
+
await init();
|
|
1533
|
+
} else if (command === "scaffold") {
|
|
1534
|
+
const ebadeFile = args[1];
|
|
1535
|
+
const outputDir = args[2] || "./output";
|
|
1301
1536
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1537
|
+
if (!ebadeFile) {
|
|
1538
|
+
console.error(
|
|
1539
|
+
`${colors.red}Error:${colors.reset} Please provide an ebade file path.`
|
|
1540
|
+
);
|
|
1541
|
+
console.log(
|
|
1542
|
+
`\n${colors.dim}Usage:${colors.reset} npx ebade scaffold <file.ebade.yaml> [output-dir]\n`
|
|
1543
|
+
);
|
|
1544
|
+
process.exit(1);
|
|
1545
|
+
}
|
|
1311
1546
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1547
|
+
if (!fs.existsSync(ebadeFile)) {
|
|
1548
|
+
console.error(
|
|
1549
|
+
`${colors.red}Error:${colors.reset} ebade file not found: ${ebadeFile}`
|
|
1550
|
+
);
|
|
1551
|
+
process.exit(1);
|
|
1552
|
+
}
|
|
1318
1553
|
|
|
1319
|
-
|
|
1320
|
-
} else if (command === "
|
|
1321
|
-
|
|
1322
|
-
const url = "https://ebade.dev/playground";
|
|
1323
|
-
const start =
|
|
1324
|
-
process.platform === "darwin"
|
|
1325
|
-
? "open"
|
|
1326
|
-
: process.platform === "win32"
|
|
1327
|
-
? "start"
|
|
1328
|
-
: "xdg-open";
|
|
1329
|
-
try {
|
|
1330
|
-
execSync(`${start} ${url}`);
|
|
1331
|
-
} catch (e) {
|
|
1332
|
-
console.log(`\n${colors.yellow}Please open:${colors.reset} ${url}`);
|
|
1333
|
-
}
|
|
1334
|
-
} else if (command === "dev") {
|
|
1335
|
-
const ebadeFile = args[1];
|
|
1336
|
-
const outputDir = args[2] || "./output";
|
|
1554
|
+
await scaffold(ebadeFile, outputDir);
|
|
1555
|
+
} else if (command === "build") {
|
|
1556
|
+
const prompt = args.slice(1).join(" ");
|
|
1337
1557
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1558
|
+
if (!prompt) {
|
|
1559
|
+
console.error(
|
|
1560
|
+
`${colors.red}Error:${colors.reset} Please provide a prompt. e.g. npx ebade build "A blue themed SaaS"`
|
|
1561
|
+
);
|
|
1562
|
+
process.exit(1);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
const spinner = ora(
|
|
1566
|
+
`${colors.cyan}EbadeArchitect is designing your project...${colors.reset}`
|
|
1567
|
+
).start();
|
|
1568
|
+
const config = await EbadeArchitect.plan(prompt);
|
|
1569
|
+
spinner.succeed(
|
|
1570
|
+
`Design complete: ${colors.bright}${config.name}${colors.reset}`
|
|
1341
1571
|
);
|
|
1572
|
+
|
|
1573
|
+
const outputDir = "./" + (config.name || "ebade-app");
|
|
1574
|
+
|
|
1575
|
+
// Create temporary YAML and scaffold
|
|
1576
|
+
const tempDir = path.join(process.cwd(), ".ebade_temp");
|
|
1577
|
+
ensureDir(tempDir);
|
|
1578
|
+
const tempFile = path.join(tempDir, "project.ebade.yaml");
|
|
1579
|
+
fs.writeFileSync(tempFile, yaml.stringify(config));
|
|
1580
|
+
|
|
1581
|
+
await scaffold(tempFile, outputDir);
|
|
1582
|
+
|
|
1583
|
+
// Final Brief
|
|
1584
|
+
console.log(`
|
|
1585
|
+
${colors.magenta}${colors.bright}๐จ Creative Brief from EbadeArchitect:${
|
|
1586
|
+
colors.reset
|
|
1587
|
+
}
|
|
1588
|
+
${colors.cyan}Project:${colors.reset} ${config.name}
|
|
1589
|
+
${colors.cyan}Theme:${colors.reset} ${
|
|
1590
|
+
config.design.colors.primary
|
|
1591
|
+
} (Detected from prompt)
|
|
1592
|
+
${colors.cyan}Pages:${colors.reset} ${config.pages
|
|
1593
|
+
.map((p) => p.path)
|
|
1594
|
+
.join(", ")}
|
|
1595
|
+
${colors.cyan}Features:${colors.reset} ${config.features.join(", ")}
|
|
1596
|
+
`);
|
|
1597
|
+
|
|
1598
|
+
// Cleanup
|
|
1599
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
1600
|
+
} else if (command === "playground") {
|
|
1342
1601
|
console.log(
|
|
1343
|
-
`\n${colors.
|
|
1602
|
+
`\n${colors.cyan}๐ Opening ebade playground...${colors.reset}`
|
|
1344
1603
|
);
|
|
1345
|
-
|
|
1346
|
-
|
|
1604
|
+
const url = "https://ebade.dev/playground";
|
|
1605
|
+
const start =
|
|
1606
|
+
process.platform === "darwin"
|
|
1607
|
+
? "open"
|
|
1608
|
+
: process.platform === "win32"
|
|
1609
|
+
? "start"
|
|
1610
|
+
: "xdg-open";
|
|
1611
|
+
try {
|
|
1612
|
+
execSync(`${start} ${url}`);
|
|
1613
|
+
} catch (e) {
|
|
1614
|
+
console.log(`\n${colors.yellow}Please open:${colors.reset} ${url}`);
|
|
1615
|
+
}
|
|
1616
|
+
} else if (command === "dev") {
|
|
1617
|
+
const ebadeFile = args[1];
|
|
1618
|
+
const outputDir = args[2] || "./output";
|
|
1347
1619
|
|
|
1348
|
-
|
|
1620
|
+
if (!ebadeFile) {
|
|
1621
|
+
console.error(
|
|
1622
|
+
`${colors.red}Error:${colors.reset} Please provide an ebade file path.`
|
|
1623
|
+
);
|
|
1624
|
+
console.log(
|
|
1625
|
+
`\n${colors.dim}Usage:${colors.reset} npx ebade dev <file.ebade.yaml> [output-dir]\n`
|
|
1626
|
+
);
|
|
1627
|
+
process.exit(1);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
if (!fs.existsSync(ebadeFile)) {
|
|
1631
|
+
console.error(
|
|
1632
|
+
`${colors.red}Error:${colors.reset} ebade file not found: ${ebadeFile}`
|
|
1633
|
+
);
|
|
1634
|
+
process.exit(1);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
await dev(ebadeFile, outputDir);
|
|
1638
|
+
} else {
|
|
1349
1639
|
console.error(
|
|
1350
|
-
`${colors.red}Error:${colors.reset}
|
|
1640
|
+
`${colors.red}Error:${colors.reset} Unknown command: ${command}`
|
|
1351
1641
|
);
|
|
1642
|
+
showHelp();
|
|
1352
1643
|
process.exit(1);
|
|
1353
1644
|
}
|
|
1354
|
-
|
|
1355
|
-
await dev(ebadeFile, outputDir);
|
|
1356
|
-
} else {
|
|
1357
|
-
console.error(
|
|
1358
|
-
`${colors.red}Error:${colors.reset} Unknown command: ${command}`
|
|
1359
|
-
);
|
|
1360
|
-
showHelp();
|
|
1361
|
-
process.exit(1);
|
|
1362
1645
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ebade-mcp-server",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "MCP Server for ebade v0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
|
+
"description": "MCP Server for ebade v0.4.6 - The Agent-First Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - Validate ebade files
|
|
10
10
|
* - Compile ebade to framework-specific code
|
|
11
11
|
* - Generate components from natural language descriptions
|
|
12
|
+
* - Build entire projects from natural language prompts (NEW in v0.4.6)
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -24,12 +25,13 @@ import { scaffoldProject } from "./tools/scaffold.js";
|
|
|
24
25
|
import { validateIntent } from "./tools/validate.js";
|
|
25
26
|
import { compileIntent } from "./tools/compile.js";
|
|
26
27
|
import { generateComponent } from "./tools/generate.js";
|
|
28
|
+
import { buildFromPrompt } from "./tools/build.js";
|
|
27
29
|
|
|
28
30
|
// Create the MCP server
|
|
29
31
|
const server = new Server(
|
|
30
32
|
{
|
|
31
33
|
name: "ebade",
|
|
32
|
-
version: "0.
|
|
34
|
+
version: "0.4.6",
|
|
33
35
|
},
|
|
34
36
|
{
|
|
35
37
|
capabilities: {
|
|
@@ -183,6 +185,33 @@ Use this when:
|
|
|
183
185
|
required: ["description"],
|
|
184
186
|
},
|
|
185
187
|
},
|
|
188
|
+
{
|
|
189
|
+
name: "ebade_build",
|
|
190
|
+
description: `Revolutionary "Prompt-to-Product" tool.
|
|
191
|
+
Generates a complete, production-ready project from a single natural language description.
|
|
192
|
+
|
|
193
|
+
Use this when the user says:
|
|
194
|
+
- "Bana mor temalฤฑ bir kripto borsasฤฑ yap"
|
|
195
|
+
- "Create a red themed SaaS for AI model store"
|
|
196
|
+
- "Make a sleek portfolio for a creative director"
|
|
197
|
+
|
|
198
|
+
This tool handles architecture, component selection, color palette, and scaffolding in one shot.`,
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
prompt: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "The natural language instruction for the project",
|
|
205
|
+
},
|
|
206
|
+
outputDir: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description:
|
|
209
|
+
"Base directory path where the project will be created (absolute)",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
required: ["prompt", "outputDir"],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
186
215
|
],
|
|
187
216
|
};
|
|
188
217
|
});
|
|
@@ -229,6 +258,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
229
258
|
style: args?.style as any,
|
|
230
259
|
});
|
|
231
260
|
|
|
261
|
+
case "ebade_build":
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: await buildFromPrompt(args as any),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
|
|
232
271
|
default:
|
|
233
272
|
throw new Error(`Unknown tool: ${name}`);
|
|
234
273
|
}
|
|
@@ -278,7 +317,7 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
278
317
|
const { uri } = request.params;
|
|
279
318
|
|
|
280
319
|
const resources: Record<string, string> = {
|
|
281
|
-
"ebade://syntax": `# ebade Syntax Reference
|
|
320
|
+
"ebade://syntax": `# ebade Syntax Reference\n\n@page, @ebade, @requires, @outcomes, @data, @validate, @style, @compose, @on, @expects\n\nCode = f(ebade)`,
|
|
282
321
|
"ebade://examples/ecommerce": `# E-commerce ebade\nname: my-store\ntype: e-commerce\nfeatures:\n - product-catalog\n - shopping-cart\n - checkout`,
|
|
283
322
|
"ebade://examples/saas": `# SaaS ebade\nname: my-saas\ntype: saas-dashboard\nfeatures:\n - user-auth\n - billing\n - analytics`,
|
|
284
323
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
|
|
5
|
+
interface BuildArgs {
|
|
6
|
+
prompt: string;
|
|
7
|
+
outputDir: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function buildFromPrompt(args: BuildArgs): Promise<string> {
|
|
11
|
+
const { prompt, outputDir } = args;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
// Find the CLI script path
|
|
15
|
+
// Assuming we are in packages/mcp-server/dist/tools/ (at runtime)
|
|
16
|
+
// or packages/mcp-server/src/tools/ (at dev time)
|
|
17
|
+
const cliPath = path.resolve(process.cwd(), "../../cli/scaffold.js");
|
|
18
|
+
|
|
19
|
+
if (!fs.existsSync(cliPath)) {
|
|
20
|
+
throw new Error(`CLI not found at ${cliPath}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(`๐ Executing ebade build from prompt: "${prompt}"`);
|
|
24
|
+
|
|
25
|
+
// Execute the CLI command
|
|
26
|
+
// We pass the prompt as an argument.
|
|
27
|
+
// We need to handle the outputDir carefully.
|
|
28
|
+
// The CLI build command currently uses its own naming logic,
|
|
29
|
+
// but it creates the folder in the current process.cwd().
|
|
30
|
+
|
|
31
|
+
const cmd = `node "${cliPath}" build "${prompt}"`;
|
|
32
|
+
|
|
33
|
+
// Run in the specified outputDir if provided
|
|
34
|
+
const result = execSync(cmd, {
|
|
35
|
+
cwd: outputDir,
|
|
36
|
+
encoding: "utf-8",
|
|
37
|
+
env: { ...process.env, NODE_ENV: "production" },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return `โ
ebade v0.4.6 Build Complete!
|
|
41
|
+
|
|
42
|
+
Prompt: "${prompt}"
|
|
43
|
+
Output: ${outputDir}
|
|
44
|
+
|
|
45
|
+
CLI Output:
|
|
46
|
+
${result}
|
|
47
|
+
|
|
48
|
+
Next Steps:
|
|
49
|
+
1. Navigate to the generated folder.
|
|
50
|
+
2. Run 'npm install && npm run dev'.
|
|
51
|
+
3. Enjoy your turnkey project!`;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Build failed: ${error instanceof Error ? error.message : String(error)}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import assert from "assert";
|
|
5
|
+
|
|
6
|
+
async function runCliTests() {
|
|
7
|
+
console.log("๐งช Running CLI Integration Tests...\n");
|
|
8
|
+
|
|
9
|
+
const PROJECT_NAME = "ModernBlog";
|
|
10
|
+
if (fs.existsSync(PROJECT_NAME)) {
|
|
11
|
+
fs.rmSync(PROJECT_NAME, { recursive: true, force: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// 1. Test 'ebade build' command
|
|
16
|
+
console.log(`Testing 'ebade build' command for ${PROJECT_NAME}...`);
|
|
17
|
+
const cmd = `node cli/scaffold.js build "A Modern Blog with blue theme"`;
|
|
18
|
+
|
|
19
|
+
execSync(cmd, { stdio: "inherit" });
|
|
20
|
+
|
|
21
|
+
const projectDir = path.join(process.cwd(), PROJECT_NAME);
|
|
22
|
+
|
|
23
|
+
assert.ok(
|
|
24
|
+
fs.existsSync(projectDir),
|
|
25
|
+
`Project directory (${PROJECT_NAME}) should exist`
|
|
26
|
+
);
|
|
27
|
+
assert.ok(
|
|
28
|
+
fs.existsSync(path.join(projectDir, "project.ebade.yaml")),
|
|
29
|
+
"project.ebade.yaml should be generated"
|
|
30
|
+
);
|
|
31
|
+
assert.ok(
|
|
32
|
+
fs.existsSync(path.join(projectDir, "app/page.tsx")),
|
|
33
|
+
"Main page should be generated"
|
|
34
|
+
);
|
|
35
|
+
assert.ok(
|
|
36
|
+
fs.existsSync(path.join(projectDir, ".env.example")),
|
|
37
|
+
".env.example should be generated"
|
|
38
|
+
);
|
|
39
|
+
assert.ok(
|
|
40
|
+
fs.existsSync(path.join(projectDir, ".gitignore")),
|
|
41
|
+
".gitignore should be generated"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Verify blog pages
|
|
45
|
+
assert.ok(
|
|
46
|
+
fs.existsSync(path.join(projectDir, "app/posts/page.tsx")),
|
|
47
|
+
"Blog index page should be generated"
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
console.log("โ
'ebade build' Integration OK");
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error("\nโ CLI Test failed:");
|
|
53
|
+
console.error(err);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
} finally {
|
|
56
|
+
// Cleanup
|
|
57
|
+
console.log("\n๐งน Cleaning up test artifacts...");
|
|
58
|
+
if (fs.existsSync(PROJECT_NAME)) {
|
|
59
|
+
fs.rmSync(PROJECT_NAME, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log("\nโจ CLI integration tests passed!");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
runCliTests();
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EbadeArchitect,
|
|
3
|
+
toPascalCase,
|
|
4
|
+
toSnakeCase,
|
|
5
|
+
hexToHsl,
|
|
6
|
+
} from "../../cli/scaffold.js";
|
|
7
|
+
import assert from "assert";
|
|
8
|
+
|
|
9
|
+
async function runTests() {
|
|
10
|
+
console.log("๐งช Running Framework Unit Tests (v0.4.6)...\n");
|
|
11
|
+
|
|
12
|
+
// 1. Utility Functions
|
|
13
|
+
console.log("Testing Utility Functions...");
|
|
14
|
+
assert.strictEqual(toPascalCase("hello-world"), "HelloWorld");
|
|
15
|
+
assert.strictEqual(toPascalCase("hello_world"), "HelloWorld");
|
|
16
|
+
assert.strictEqual(toSnakeCase("HelloWorld"), "hello_world");
|
|
17
|
+
assert.strictEqual(toSnakeCase("hello-world"), "hello_world");
|
|
18
|
+
|
|
19
|
+
// hexToHsl Test (Critical for Design System)
|
|
20
|
+
const hsl = hexToHsl("#8b5cf6"); // Violet
|
|
21
|
+
assert.ok(
|
|
22
|
+
hsl.includes("258"),
|
|
23
|
+
`HSL for #8b5cf6 should include hue 258, got ${hsl}`
|
|
24
|
+
);
|
|
25
|
+
console.log("โ
Utilities OK");
|
|
26
|
+
|
|
27
|
+
// 2. Architect Mapping
|
|
28
|
+
console.log("\nTesting EbadeArchitect (Complex Intent)...");
|
|
29
|
+
const prompt =
|
|
30
|
+
"Can you make a luxury gold themed dashboard with charts and login features?";
|
|
31
|
+
const config = await EbadeArchitect.plan(prompt);
|
|
32
|
+
|
|
33
|
+
// Check intelligence
|
|
34
|
+
assert.strictEqual(
|
|
35
|
+
config.name,
|
|
36
|
+
"LuxuryGold",
|
|
37
|
+
"Should filter filler words like 'Can you make a'"
|
|
38
|
+
);
|
|
39
|
+
assert.strictEqual(
|
|
40
|
+
config.design.colors.primary,
|
|
41
|
+
"#fbbf24",
|
|
42
|
+
"Gold detection failed"
|
|
43
|
+
);
|
|
44
|
+
assert.ok(
|
|
45
|
+
config.features.includes("Advanced Analytics"),
|
|
46
|
+
"Analytics detection failed"
|
|
47
|
+
);
|
|
48
|
+
assert.ok(
|
|
49
|
+
config.features.includes("Authentication"),
|
|
50
|
+
"Auth detection failed"
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Check Type-Aware Pages
|
|
54
|
+
assert.strictEqual(
|
|
55
|
+
config.pages.length,
|
|
56
|
+
2,
|
|
57
|
+
"Should have landing and dashboard"
|
|
58
|
+
);
|
|
59
|
+
assert.ok(
|
|
60
|
+
config.pages.some((p) => p.path === "/dashboard"),
|
|
61
|
+
"Dashboard page missing"
|
|
62
|
+
);
|
|
63
|
+
console.log("โ
Complex Intent mapping OK");
|
|
64
|
+
|
|
65
|
+
// 3. Project Type Specifics (E-commerce)
|
|
66
|
+
console.log("\nTesting EbadeArchitect (E-commerce)...");
|
|
67
|
+
const shopConfig = await EbadeArchitect.plan(
|
|
68
|
+
"Sleek shoe store with red theme"
|
|
69
|
+
);
|
|
70
|
+
assert.strictEqual(shopConfig.type, "e-commerce");
|
|
71
|
+
assert.ok(
|
|
72
|
+
shopConfig.pages.some((p) => p.path === "/products"),
|
|
73
|
+
"E-commerce missing /products"
|
|
74
|
+
);
|
|
75
|
+
assert.ok(
|
|
76
|
+
shopConfig.pages.some((p) => p.path === "/cart"),
|
|
77
|
+
"E-commerce missing /cart"
|
|
78
|
+
);
|
|
79
|
+
console.log("โ
E-commerce structure OK");
|
|
80
|
+
|
|
81
|
+
// 4. Project Type Specifics (Blog)
|
|
82
|
+
console.log("\nTesting EbadeArchitect (Blog)...");
|
|
83
|
+
const blogConfig = await EbadeArchitect.plan("Ocean themed news blog");
|
|
84
|
+
assert.strictEqual(blogConfig.type, "blog");
|
|
85
|
+
assert.ok(
|
|
86
|
+
blogConfig.pages.some((p) => p.path === "/posts"),
|
|
87
|
+
"Blog missing /posts index"
|
|
88
|
+
);
|
|
89
|
+
assert.ok(
|
|
90
|
+
blogConfig.pages.some((p) => p.path === "/posts/[slug]"),
|
|
91
|
+
"Blog missing /posts/[slug] dynamic route"
|
|
92
|
+
);
|
|
93
|
+
console.log("โ
Blog structure OK");
|
|
94
|
+
|
|
95
|
+
console.log("\nโจ All framework tests passed!");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
runTests().catch((err) => {
|
|
99
|
+
console.error("\nโ Test failed:");
|
|
100
|
+
console.error(err);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|