helix-lang 11.0.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 (193) hide show
  1. package/README.md +168 -0
  2. package/dist/architect.d.ts +14 -0
  3. package/dist/architect.d.ts.map +1 -0
  4. package/dist/architect.js +127 -0
  5. package/dist/architect.js.map +1 -0
  6. package/dist/bin/helix.d.ts +20 -0
  7. package/dist/bin/helix.d.ts.map +1 -0
  8. package/dist/bin/helix.js +921 -0
  9. package/dist/bin/helix.js.map +1 -0
  10. package/dist/commands/collaborate/index.d.ts +2 -0
  11. package/dist/commands/collaborate/index.d.ts.map +1 -0
  12. package/dist/commands/collaborate/index.js +129 -0
  13. package/dist/commands/collaborate/index.js.map +1 -0
  14. package/dist/commands/collaborate/server.d.ts +31 -0
  15. package/dist/commands/collaborate/server.d.ts.map +1 -0
  16. package/dist/commands/collaborate/server.js +159 -0
  17. package/dist/commands/collaborate/server.js.map +1 -0
  18. package/dist/commands/deploy/index.d.ts +25 -0
  19. package/dist/commands/deploy/index.d.ts.map +1 -0
  20. package/dist/commands/deploy/index.js +130 -0
  21. package/dist/commands/deploy/index.js.map +1 -0
  22. package/dist/commands/deploy/platforms/fly.d.ts +9 -0
  23. package/dist/commands/deploy/platforms/fly.d.ts.map +1 -0
  24. package/dist/commands/deploy/platforms/fly.js +68 -0
  25. package/dist/commands/deploy/platforms/fly.js.map +1 -0
  26. package/dist/commands/deploy/platforms/railway.d.ts +9 -0
  27. package/dist/commands/deploy/platforms/railway.d.ts.map +1 -0
  28. package/dist/commands/deploy/platforms/railway.js +115 -0
  29. package/dist/commands/deploy/platforms/railway.js.map +1 -0
  30. package/dist/commands/deploy/platforms/vercel.d.ts +10 -0
  31. package/dist/commands/deploy/platforms/vercel.d.ts.map +1 -0
  32. package/dist/commands/deploy/platforms/vercel.js +126 -0
  33. package/dist/commands/deploy/platforms/vercel.js.map +1 -0
  34. package/dist/commands/deploy.d.ts +6 -0
  35. package/dist/commands/deploy.d.ts.map +1 -0
  36. package/dist/commands/deploy.js +56 -0
  37. package/dist/commands/deploy.js.map +1 -0
  38. package/dist/commands/evolve/analyzers/performance.d.ts +13 -0
  39. package/dist/commands/evolve/analyzers/performance.d.ts.map +1 -0
  40. package/dist/commands/evolve/analyzers/performance.js +591 -0
  41. package/dist/commands/evolve/analyzers/performance.js.map +1 -0
  42. package/dist/commands/evolve/analyzers/security.d.ts +21 -0
  43. package/dist/commands/evolve/analyzers/security.d.ts.map +1 -0
  44. package/dist/commands/evolve/analyzers/security.js +280 -0
  45. package/dist/commands/evolve/analyzers/security.js.map +1 -0
  46. package/dist/commands/evolve/index.d.ts +2 -0
  47. package/dist/commands/evolve/index.d.ts.map +1 -0
  48. package/dist/commands/evolve/index.js +122 -0
  49. package/dist/commands/evolve/index.js.map +1 -0
  50. package/dist/commands/generate.d.ts +6 -0
  51. package/dist/commands/generate.d.ts.map +1 -0
  52. package/dist/commands/generate.js +277 -0
  53. package/dist/commands/generate.js.map +1 -0
  54. package/dist/commands/install.d.ts +2 -0
  55. package/dist/commands/install.d.ts.map +1 -0
  56. package/dist/commands/install.js +176 -0
  57. package/dist/commands/install.js.map +1 -0
  58. package/dist/commands/library/index.d.ts +27 -0
  59. package/dist/commands/library/index.d.ts.map +1 -0
  60. package/dist/commands/library/index.js +126 -0
  61. package/dist/commands/library/index.js.map +1 -0
  62. package/dist/commands/migrate.d.ts +5 -0
  63. package/dist/commands/migrate.d.ts.map +1 -0
  64. package/dist/commands/migrate.js +258 -0
  65. package/dist/commands/migrate.js.map +1 -0
  66. package/dist/commands/new.d.ts +6 -0
  67. package/dist/commands/new.d.ts.map +1 -0
  68. package/dist/commands/new.js +195 -0
  69. package/dist/commands/new.js.map +1 -0
  70. package/dist/commands/preflight.d.ts +20 -0
  71. package/dist/commands/preflight.d.ts.map +1 -0
  72. package/dist/commands/preflight.js +182 -0
  73. package/dist/commands/preflight.js.map +1 -0
  74. package/dist/commands/preview.d.ts +13 -0
  75. package/dist/commands/preview.d.ts.map +1 -0
  76. package/dist/commands/preview.js +260 -0
  77. package/dist/commands/preview.js.map +1 -0
  78. package/dist/commands/run.d.ts +6 -0
  79. package/dist/commands/run.d.ts.map +1 -0
  80. package/dist/commands/run.js +96 -0
  81. package/dist/commands/run.js.map +1 -0
  82. package/dist/commands/spawn.d.ts +11 -0
  83. package/dist/commands/spawn.d.ts.map +1 -0
  84. package/dist/commands/spawn.js +916 -0
  85. package/dist/commands/spawn.js.map +1 -0
  86. package/dist/compiler.d.ts +12 -0
  87. package/dist/compiler.d.ts.map +1 -0
  88. package/dist/compiler.js +92 -0
  89. package/dist/compiler.js.map +1 -0
  90. package/dist/core/file-writer.d.ts +36 -0
  91. package/dist/core/file-writer.d.ts.map +1 -0
  92. package/dist/core/file-writer.js +268 -0
  93. package/dist/core/file-writer.js.map +1 -0
  94. package/dist/core/registry.d.ts +57 -0
  95. package/dist/core/registry.d.ts.map +1 -0
  96. package/dist/core/registry.js +222 -0
  97. package/dist/core/registry.js.map +1 -0
  98. package/dist/core/self-healing.d.ts +47 -0
  99. package/dist/core/self-healing.d.ts.map +1 -0
  100. package/dist/core/self-healing.js +250 -0
  101. package/dist/core/self-healing.js.map +1 -0
  102. package/dist/core/types.d.ts +126 -0
  103. package/dist/core/types.d.ts.map +1 -0
  104. package/dist/core/types.js +7 -0
  105. package/dist/core/types.js.map +1 -0
  106. package/dist/generators/databases/mongodb.d.ts +10 -0
  107. package/dist/generators/databases/mongodb.d.ts.map +1 -0
  108. package/dist/generators/databases/mongodb.js +83 -0
  109. package/dist/generators/databases/mongodb.js.map +1 -0
  110. package/dist/generators/databases/redis.d.ts +2 -0
  111. package/dist/generators/databases/redis.d.ts.map +1 -0
  112. package/dist/generators/databases/redis.js +140 -0
  113. package/dist/generators/databases/redis.js.map +1 -0
  114. package/dist/generators/flutter.d.ts +32 -0
  115. package/dist/generators/flutter.d.ts.map +1 -0
  116. package/dist/generators/flutter.js +628 -0
  117. package/dist/generators/flutter.js.map +1 -0
  118. package/dist/openrouter.d.ts +68 -0
  119. package/dist/openrouter.d.ts.map +1 -0
  120. package/dist/openrouter.js +241 -0
  121. package/dist/openrouter.js.map +1 -0
  122. package/dist/page-generator.d.ts +22 -0
  123. package/dist/page-generator.d.ts.map +1 -0
  124. package/dist/page-generator.js +192 -0
  125. package/dist/page-generator.js.map +1 -0
  126. package/dist/parser.d.ts +76 -0
  127. package/dist/parser.d.ts.map +1 -0
  128. package/dist/parser.js +691 -0
  129. package/dist/parser.js.map +1 -0
  130. package/dist/prompts/master-architect.d.ts +9 -0
  131. package/dist/prompts/master-architect.d.ts.map +1 -0
  132. package/dist/prompts/master-architect.js +150 -0
  133. package/dist/prompts/master-architect.js.map +1 -0
  134. package/dist/researcher.d.ts +12 -0
  135. package/dist/researcher.d.ts.map +1 -0
  136. package/dist/researcher.js +85 -0
  137. package/dist/researcher.js.map +1 -0
  138. package/dist/self-heal.d.ts +29 -0
  139. package/dist/self-heal.d.ts.map +1 -0
  140. package/dist/self-heal.js +260 -0
  141. package/dist/self-heal.js.map +1 -0
  142. package/dist/services/SupabaseDeployer.d.ts +9 -0
  143. package/dist/services/SupabaseDeployer.d.ts.map +1 -0
  144. package/dist/services/SupabaseDeployer.js +50 -0
  145. package/dist/services/SupabaseDeployer.js.map +1 -0
  146. package/dist/test-generator.d.ts +18 -0
  147. package/dist/test-generator.d.ts.map +1 -0
  148. package/dist/test-generator.js +180 -0
  149. package/dist/test-generator.js.map +1 -0
  150. package/dist/themes/index.d.ts +52 -0
  151. package/dist/themes/index.d.ts.map +1 -0
  152. package/dist/themes/index.js +273 -0
  153. package/dist/themes/index.js.map +1 -0
  154. package/dist/types.d.ts +9 -0
  155. package/dist/types.d.ts.map +1 -0
  156. package/dist/types.js +81 -0
  157. package/dist/types.js.map +1 -0
  158. package/dist/utils/constitutional-validator.d.ts +73 -0
  159. package/dist/utils/constitutional-validator.d.ts.map +1 -0
  160. package/dist/utils/constitutional-validator.js +249 -0
  161. package/dist/utils/constitutional-validator.js.map +1 -0
  162. package/library/auth-flow/app/api/auth/[...nextauth]/route.ts +31 -0
  163. package/library/auth-flow/app/api/auth/login/route.ts +90 -0
  164. package/library/auth-flow/app/api/auth/register/route.ts +91 -0
  165. package/library/auth-flow/components/auth/AuthMiddleware.tsx +139 -0
  166. package/library/auth-flow/components/auth/LoginForm.tsx +125 -0
  167. package/library/auth-flow/components/auth/RegisterForm.tsx +168 -0
  168. package/library/auth-flow/components/auth/nextauth-config.ts +99 -0
  169. package/library/auth-flow/manifest.json +29 -0
  170. package/library/auth-flow/schema.prisma +45 -0
  171. package/library/dashboard-analytics/components/dashboard/ActivityFeed.tsx +109 -0
  172. package/library/dashboard-analytics/components/dashboard/LineChart.tsx +180 -0
  173. package/library/dashboard-analytics/components/dashboard/StatsCard.tsx +47 -0
  174. package/library/dashboard-analytics/components/dashboard/SummaryGrid.tsx +39 -0
  175. package/library/dashboard-analytics/manifest.json +19 -0
  176. package/library/data-table/components/table/BulkActions.tsx +59 -0
  177. package/library/data-table/components/table/ColumnToggle.tsx +65 -0
  178. package/library/data-table/components/table/DataTable.tsx +318 -0
  179. package/library/data-table/components/table/ExportCSV.tsx +52 -0
  180. package/library/data-table/components/table/SearchFilter.tsx +48 -0
  181. package/library/data-table/manifest.json +20 -0
  182. package/library/file-upload/app/api/upload/route.ts +107 -0
  183. package/library/file-upload/components/upload/DropZone.tsx +268 -0
  184. package/library/file-upload/components/upload/FilePreview.tsx +82 -0
  185. package/library/file-upload/components/upload/UploadProgress.tsx +92 -0
  186. package/library/file-upload/components/upload/fileStorage.ts +142 -0
  187. package/library/file-upload/manifest.json +21 -0
  188. package/library/notification-system/app/api/notifications/route.ts +121 -0
  189. package/library/notification-system/components/notifications/NotificationBell.tsx +154 -0
  190. package/library/notification-system/components/notifications/NotificationProvider.tsx +161 -0
  191. package/library/notification-system/components/notifications/Toast.tsx +112 -0
  192. package/library/notification-system/manifest.json +20 -0
  193. package/package.json +66 -0
@@ -0,0 +1,916 @@
1
+ "use strict";
2
+ /**
3
+ * Helix Command: spawn
4
+ * ONE-SHOT GENERATION - Full-stack app from natural language with ZERO intervention
5
+ * v11.0 - Clean Factory: All builds isolated to builds/{app_name}/
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.spawnApp = spawnApp;
45
+ const chalk_1 = __importDefault(require("chalk"));
46
+ const fs = __importStar(require("fs-extra"));
47
+ const path = __importStar(require("path"));
48
+ const execa = require("execa");
49
+ const ora_1 = __importDefault(require("ora"));
50
+ const openrouter_1 = require("../openrouter");
51
+ const parser_1 = require("../parser");
52
+ const types_1 = require("../types");
53
+ const constitutional_validator_1 = require("../utils/constitutional-validator");
54
+ const themes_1 = require("../themes");
55
+ const self_heal_1 = require("../self-heal");
56
+ const test_generator_1 = require("../test-generator");
57
+ const page_generator_1 = require("../page-generator");
58
+ const MAX_RETRY_ATTEMPTS = 3;
59
+ // =============================================================================
60
+ // CLEAN FACTORY: All projects scoped to builds/
61
+ // =============================================================================
62
+ const HELIX_ROOT = path.resolve(__dirname, "..", "..");
63
+ const BUILDS_DIR = path.join(HELIX_ROOT, "builds");
64
+ const MASTER_ENV = path.join(HELIX_ROOT, ".env");
65
+ /**
66
+ * Spawn a complete full-stack application from a natural language prompt
67
+ */
68
+ async function spawnApp(prompt, options = {}, constitution, connectionString) {
69
+ console.log(chalk_1.default.cyan("\n🧬 HELIX SPAWN v11.0 - Clean Factory\n"));
70
+ console.log(chalk_1.default.gray(`Prompt: "${prompt}"\n`));
71
+ if (connectionString) {
72
+ console.log(chalk_1.default.gray(`Supabase Autopilot: ENABLED\n`));
73
+ }
74
+ // =========================================================================
75
+ // CONSTITUTIONAL VALIDATION (V10.2)
76
+ // =========================================================================
77
+ if (!options.noConstitution) {
78
+ console.log(chalk_1.default.cyan("📜 Validating Constitutional Compliance...\n"));
79
+ const report = (0, constitutional_validator_1.validateConstitution)(prompt, options);
80
+ (0, constitutional_validator_1.printConstitutionalReport)(report);
81
+ if (report.violations.length > 0) {
82
+ const autoFixableCount = report.violations.filter(v => v.autoFixable).length;
83
+ if (autoFixableCount > 0) {
84
+ console.log(chalk_1.default.yellow(`\n✨ Auto-correcting ${autoFixableCount} violations...\n`));
85
+ options = (0, constitutional_validator_1.autoCorrectOptions)(options, report.violations);
86
+ }
87
+ }
88
+ prompt = (0, constitutional_validator_1.enhancePromptWithConstitution)(prompt);
89
+ console.log(chalk_1.default.green("✅ Constitutional validation complete\n"));
90
+ }
91
+ // =========================================================================
92
+ // CLEAN FACTORY: Enforce builds/ isolation
93
+ // =========================================================================
94
+ const projectName = generateProjectName(prompt);
95
+ await fs.ensureDir(BUILDS_DIR);
96
+ const projectPath = path.join(BUILDS_DIR, projectName);
97
+ if (fs.existsSync(projectPath)) {
98
+ console.error(chalk_1.default.red(`❌ Build "${projectName}" already exists in builds/`));
99
+ console.error(chalk_1.default.gray(` Path: ${projectPath}`));
100
+ console.error(chalk_1.default.gray(` Remove it first or use a different prompt.`));
101
+ process.exit(1);
102
+ }
103
+ console.log(chalk_1.default.cyan(`📂 Clean Factory: builds/${projectName}/\n`));
104
+ try {
105
+ // =========================================================================
106
+ // PHASE 1: Scaffolding
107
+ // =========================================================================
108
+ console.log(chalk_1.default.cyan("\n📦 Phase 1: Scaffolding Next.js project...\n"));
109
+ const createNextArgs = [
110
+ "create-next-app@latest",
111
+ projectPath,
112
+ "--typescript",
113
+ "--tailwind",
114
+ "--eslint",
115
+ "--app",
116
+ "--src-dir",
117
+ "--use-npm",
118
+ "--no-git",
119
+ "--yes",
120
+ ];
121
+ console.log(chalk_1.default.gray(`⬇️ Executing: npx ${createNextArgs.join(" ")}\n`));
122
+ try {
123
+ await execa("npx", createNextArgs, { stdio: "inherit" });
124
+ }
125
+ catch (scaffoldError) {
126
+ console.error(chalk_1.default.red(`\n❌ Scaffolding failed: ${scaffoldError.message}`));
127
+ if (scaffoldError.stderr) {
128
+ console.error(chalk_1.default.red(scaffoldError.stderr));
129
+ }
130
+ await cleanupOnFailure(projectPath);
131
+ process.exit(1);
132
+ }
133
+ console.log(chalk_1.default.green("\n✓ Next.js project created\n"));
134
+ // =========================================================================
135
+ // SELF-CONTAINMENT: .env, Dockerfile, docker-compose, .gitignore
136
+ // =========================================================================
137
+ console.log(chalk_1.default.cyan("🔒 Self-Containment: Injecting isolation files...\n"));
138
+ // Clone master .env + add project-specific vars
139
+ let masterEnvContent = "";
140
+ if (fs.existsSync(MASTER_ENV)) {
141
+ masterEnvContent = await fs.readFile(MASTER_ENV, "utf-8");
142
+ }
143
+ const projectEnv = `# Helix Clean Factory - Project Environment
144
+ # Cloned from master .env at ${new Date().toISOString()}
145
+ ${masterEnvContent}
146
+ # Project-specific
147
+ DATABASE_URL="file:./dev.db"
148
+ NEXT_PUBLIC_APP_NAME="${projectName}"
149
+ `;
150
+ await fs.writeFile(path.join(projectPath, ".env"), projectEnv);
151
+ console.log(chalk_1.default.green(" ✅ .env (cloned from master)"));
152
+ // Dockerfile
153
+ await fs.writeFile(path.join(projectPath, "Dockerfile"), `# Helix Clean Factory - Auto-generated Dockerfile
154
+ FROM node:20-alpine AS base
155
+
156
+ FROM base AS deps
157
+ WORKDIR /app
158
+ COPY package.json package-lock.json* ./
159
+ RUN npm ci --only=production
160
+
161
+ FROM base AS builder
162
+ WORKDIR /app
163
+ COPY --from=deps /app/node_modules ./node_modules
164
+ COPY . .
165
+ RUN npx prisma generate
166
+ RUN npm run build
167
+
168
+ FROM base AS runner
169
+ WORKDIR /app
170
+ ENV NODE_ENV=production
171
+ RUN addgroup --system --gid 1001 nodejs
172
+ RUN adduser --system --uid 1001 nextjs
173
+ COPY --from=builder /app/public ./public
174
+ COPY --from=builder /app/.next/standalone ./
175
+ COPY --from=builder /app/.next/static ./.next/static
176
+ COPY --from=builder /app/prisma ./prisma
177
+ USER nextjs
178
+ EXPOSE 3000
179
+ ENV PORT=3000
180
+ CMD ["node", "server.js"]
181
+ `);
182
+ console.log(chalk_1.default.green(" ✅ Dockerfile"));
183
+ // docker-compose.yml
184
+ await fs.writeFile(path.join(projectPath, "docker-compose.yml"), `# Helix Clean Factory - Auto-generated
185
+ version: "3.8"
186
+ services:
187
+ app:
188
+ build: .
189
+ ports:
190
+ - "3000:3000"
191
+ env_file:
192
+ - .env
193
+ volumes:
194
+ - app-data:/app/prisma
195
+ restart: unless-stopped
196
+
197
+ volumes:
198
+ app-data:
199
+ `);
200
+ console.log(chalk_1.default.green(" ✅ docker-compose.yml"));
201
+ // .gitignore
202
+ await fs.writeFile(path.join(projectPath, ".gitignore"), `node_modules/
203
+ .next/
204
+ .env
205
+ *.db
206
+ *.db-journal
207
+ dist/
208
+ .turbo/
209
+ `);
210
+ console.log(chalk_1.default.green(" ✅ .gitignore"));
211
+ // next.config.ts - LAN-friendly
212
+ await fs.writeFile(path.join(projectPath, "next.config.ts"), [
213
+ 'import type { NextConfig } from "next";',
214
+ '',
215
+ 'const nextConfig: NextConfig = {',
216
+ ' allowedDevOrigins: ["*"],',
217
+ '};',
218
+ '',
219
+ 'export default nextConfig;',
220
+ ''
221
+ ].join("\n"));
222
+ console.log(chalk_1.default.green(" ✅ next.config.ts (LAN-friendly)"));
223
+ // Override layout.tsx - no Google Fonts (blocks hydration over LAN)
224
+ const layoutPath = path.join(projectPath, "src", "app", "layout.tsx");
225
+ await fs.writeFile(layoutPath, [
226
+ 'import type { Metadata } from "next";',
227
+ 'import "./globals.css";',
228
+ '',
229
+ 'export const metadata: Metadata = {',
230
+ ' title: "' + projectName + '",',
231
+ ' description: "Built by Helix v11.0",',
232
+ '};',
233
+ '',
234
+ 'export default function RootLayout({',
235
+ ' children,',
236
+ '}: Readonly<{',
237
+ ' children: React.ReactNode;',
238
+ '}>) {',
239
+ ' return (',
240
+ ' <html lang="en">',
241
+ ' <body>{children}</body>',
242
+ ' </html>',
243
+ ' );',
244
+ '}',
245
+ ''
246
+ ].join("\n"));
247
+ console.log(chalk_1.default.green(" ✅ layout.tsx (no Google Fonts)"));
248
+ // =========================================================================
249
+ // Prisma Setup
250
+ // =========================================================================
251
+ const prismaSpinner = (0, ora_1.default)("Setting up Prisma@5.22.0...").start();
252
+ await execa("npm", ["install", "-D", "prisma@5.22.0"], {
253
+ cwd: projectPath,
254
+ stdio: "pipe",
255
+ });
256
+ await execa("npm", ["install", "@prisma/client@5.22.0"], {
257
+ cwd: projectPath,
258
+ stdio: "pipe",
259
+ });
260
+ // Install test dependencies
261
+ await execa("npm", ["install", "-D", "vitest", "@vitejs/plugin-react", "jsdom", "@testing-library/react", "@testing-library/jest-dom"], {
262
+ cwd: projectPath,
263
+ stdio: "pipe",
264
+ });
265
+ const prismaDir = path.join(projectPath, "prisma");
266
+ await fs.ensureDir(prismaDir);
267
+ await fs.writeFile(path.join(prismaDir, "schema.prisma"), `// Helix Generated Prisma Schema
268
+ generator client {
269
+ provider = "prisma-client-js"
270
+ }
271
+
272
+ datasource db {
273
+ provider = "sqlite"
274
+ url = env("DATABASE_URL")
275
+ }
276
+ `);
277
+ prismaSpinner.succeed("Prisma setup complete (v5.22.0)");
278
+ // Install additional dependencies
279
+ const depsSpinner = (0, ora_1.default)("Installing additional dependencies...").start();
280
+ await execa("npm", [
281
+ "install",
282
+ "lucide-react",
283
+ "clsx",
284
+ "tailwind-merge",
285
+ ], {
286
+ cwd: projectPath,
287
+ stdio: "pipe",
288
+ });
289
+ depsSpinner.succeed("Dependencies installed");
290
+ // Create Prisma client helper
291
+ const libDir = path.join(projectPath, "src", "lib");
292
+ await fs.ensureDir(libDir);
293
+ await fs.writeFile(path.join(libDir, "prisma.ts"), `import { PrismaClient } from '@prisma/client';
294
+
295
+ const globalForPrisma = globalThis as unknown as {
296
+ prisma: PrismaClient | undefined;
297
+ };
298
+
299
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient();
300
+
301
+ if (process.env.NODE_ENV !== 'production') {
302
+ globalForPrisma.prisma = prisma;
303
+ }
304
+ `);
305
+ // Create lib/utils.ts
306
+ await fs.writeFile(path.join(libDir, "utils.ts"), `import { clsx, type ClassValue } from "clsx";
307
+ import { twMerge } from "tailwind-merge";
308
+
309
+ export function cn(...inputs: ClassValue[]) {
310
+ return twMerge(clsx(inputs));
311
+ }
312
+ `);
313
+ // Apply Helix theme (V2: data-driven theme engine)
314
+ // Theme is resolved later after blueprint parsing; write default for now
315
+ // The actual theme CSS will be written in the post-blueprint phase
316
+ const defaultThemeCSS = (0, themes_1.generateThemeCSS)((0, themes_1.resolveTheme)(options.theme));
317
+ await fs.writeFile(path.join(projectPath, "src", "app", "globals.css"), defaultThemeCSS);
318
+ // Tailwind v4: config via CSS @theme, remove scaffold-generated tailwind.config.ts
319
+ const twConfigPath = path.join(projectPath, "tailwind.config.ts");
320
+ if (fs.existsSync(twConfigPath)) {
321
+ await fs.remove(twConfigPath);
322
+ }
323
+ // Helix config
324
+ await fs.writeJSON(path.join(projectPath, "helix.config.json"), { version: "10.3.0", spawned: true, cleanFactory: true, prompt, generatedAt: new Date().toISOString() }, { spaces: 2 });
325
+ console.log(chalk_1.default.green("✓ Project scaffolded\n"));
326
+ // =========================================================================
327
+ // PHASE 1.5: SCOPE Stage (V2 — conditional, for complex prompts only)
328
+ // =========================================================================
329
+ let scopeRequirements = null;
330
+ const isComplexPrompt = prompt.split(/\s+/).length > 30 || countEntityMentions(prompt) >= 3;
331
+ if (isComplexPrompt) {
332
+ const spinnerScope = (0, ora_1.default)("Analyzing requirements (complex prompt detected)...").start();
333
+ try {
334
+ scopeRequirements = await generateRequirements(prompt);
335
+ spinnerScope.succeed("Requirements analyzed");
336
+ // Save for reference
337
+ await fs.writeFile(path.join(projectPath, "requirements.json"), scopeRequirements);
338
+ }
339
+ catch (error) {
340
+ spinnerScope.warn("SCOPE stage failed — proceeding with direct blueprint generation");
341
+ scopeRequirements = null;
342
+ }
343
+ }
344
+ // =========================================================================
345
+ // PHASE 2: Blueprint Generation with Self-Healing Retry
346
+ // =========================================================================
347
+ let helixBlueprint = "";
348
+ let blueprintAttempts = 0;
349
+ const maxBlueprintRetries = MAX_RETRY_ATTEMPTS;
350
+ let blueprintSuccess = false;
351
+ // Enrich prompt with SCOPE requirements if available
352
+ const enrichedPrompt = scopeRequirements
353
+ ? `${prompt}\n\n=== REQUIREMENTS ANALYSIS ===\n${scopeRequirements}\n=== END REQUIREMENTS ===`
354
+ : prompt;
355
+ while (blueprintAttempts < maxBlueprintRetries && !blueprintSuccess) {
356
+ blueprintAttempts++;
357
+ const spinner2 = (0, ora_1.default)(`Designing blueprint... (attempt ${blueprintAttempts}/${maxBlueprintRetries})`).start();
358
+ try {
359
+ helixBlueprint = await generateBlueprint(enrichedPrompt, constitution);
360
+ // Validate the blueprint is parseable
361
+ (0, parser_1.parseHelix)(helixBlueprint);
362
+ blueprintSuccess = true;
363
+ spinner2.succeed("Blueprint designed");
364
+ }
365
+ catch (error) {
366
+ const errMsg = error.message || String(error);
367
+ if (blueprintAttempts < maxBlueprintRetries) {
368
+ spinner2.warn(`Blueprint parse failed, self-healing... (${errMsg.substring(0, 80)})`);
369
+ // Feed error back to LLM for retry
370
+ helixBlueprint = await repairBlueprint(helixBlueprint, errMsg);
371
+ try {
372
+ (0, parser_1.parseHelix)(helixBlueprint);
373
+ blueprintSuccess = true;
374
+ console.log(chalk_1.default.green(" ✅ Self-healed blueprint"));
375
+ }
376
+ catch {
377
+ // Will retry in next loop iteration
378
+ }
379
+ }
380
+ else {
381
+ spinner2.fail("Blueprint generation failed after retries");
382
+ await cleanupOnFailure(projectPath);
383
+ throw error;
384
+ }
385
+ }
386
+ }
387
+ // Save blueprint for reference
388
+ await fs.writeFile(path.join(projectPath, "blueprint.helix"), helixBlueprint);
389
+ // =========================================================================
390
+ // PHASE 3: Database Generation with Self-Healing
391
+ // =========================================================================
392
+ const spinner3 = (0, ora_1.default)("Building database...").start();
393
+ let ast;
394
+ try {
395
+ ast = (0, parser_1.parseHelix)(helixBlueprint);
396
+ }
397
+ catch (error) {
398
+ spinner3.fail("Failed to parse blueprint");
399
+ await cleanupOnFailure(projectPath);
400
+ throw error;
401
+ }
402
+ // V2: Re-apply theme from blueprint AST (overrides default if theme specified in view)
403
+ const blueprintThemeName = ast.views.find(v => v.properties.theme)?.properties.theme;
404
+ if (blueprintThemeName) {
405
+ const resolvedTheme = (0, themes_1.resolveTheme)(blueprintThemeName);
406
+ await fs.writeFile(path.join(projectPath, "src", "app", "globals.css"), (0, themes_1.generateThemeCSS)(resolvedTheme));
407
+ console.log(chalk_1.default.cyan(` Theme: ${resolvedTheme.name} (from blueprint)`));
408
+ }
409
+ let attempts = 0;
410
+ let lastError = "";
411
+ let currentSchema = (0, parser_1.generatePrismaSchema)(ast);
412
+ while (attempts < MAX_RETRY_ATTEMPTS) {
413
+ attempts++;
414
+ try {
415
+ await fs.writeFile(path.join(projectPath, "prisma", "schema.prisma"), currentSchema);
416
+ // CLEANUP: Remove any hallucinated prisma.config.ts
417
+ const badConfigPath = path.join(projectPath, "prisma.config.ts");
418
+ if (fs.existsSync(badConfigPath)) {
419
+ await fs.remove(badConfigPath);
420
+ }
421
+ if (connectionString) {
422
+ const spinnerMigrate = (0, ora_1.default)("Supabase: Deploying Schema...").start();
423
+ try {
424
+ const envPath = path.join(projectPath, ".env");
425
+ let envContent = await fs.readFile(envPath, 'utf-8');
426
+ envContent = envContent.replace(/DATABASE_URL=".*"/, `DATABASE_URL="${connectionString}"`);
427
+ await fs.writeFile(envPath, envContent);
428
+ await execa("npm", ["exec", "--", "prisma", "db", "push", "--accept-data-loss"], {
429
+ cwd: projectPath,
430
+ stdio: "pipe",
431
+ });
432
+ spinnerMigrate.succeed("Supabase: Schema Deployed Successfully");
433
+ }
434
+ catch (err) {
435
+ spinnerMigrate.fail("Supabase Deployment Failed");
436
+ console.error(chalk_1.default.red(err.message));
437
+ }
438
+ }
439
+ else {
440
+ await execa("npm", ["exec", "--", "prisma", "db", "push", "--accept-data-loss"], {
441
+ cwd: projectPath,
442
+ stdio: "pipe",
443
+ });
444
+ }
445
+ await execa("npm", ["exec", "--", "prisma", "generate"], {
446
+ cwd: projectPath,
447
+ stdio: "pipe",
448
+ });
449
+ break;
450
+ }
451
+ catch (error) {
452
+ lastError = error.stderr || error.message || String(error);
453
+ if (attempts < MAX_RETRY_ATTEMPTS) {
454
+ spinner3.text = `Building database... (self-healing attempt ${attempts + 1}/${MAX_RETRY_ATTEMPTS})`;
455
+ currentSchema = await fixPrismaSchema(currentSchema, lastError);
456
+ }
457
+ else {
458
+ spinner3.fail(`Database build failed after ${MAX_RETRY_ATTEMPTS} attempts`);
459
+ console.error(chalk_1.default.red(lastError));
460
+ await cleanupOnFailure(projectPath);
461
+ throw new Error("Failed to build database");
462
+ }
463
+ }
464
+ }
465
+ spinner3.succeed("Database built");
466
+ // =========================================================================
467
+ // PHASE 4: Generate API & UI
468
+ // =========================================================================
469
+ const spinner4 = (0, ora_1.default)("Generating application...").start();
470
+ // Resolve the active theme for component class generation
471
+ const activeThemeName = blueprintThemeName || options.theme;
472
+ const activeThemeClasses = (0, themes_1.getThemeClasses)(activeThemeName);
473
+ for (const strand of ast.strands) {
474
+ const apiDir = path.join(projectPath, "src", "app", "api", strand.name.toLowerCase());
475
+ await fs.ensureDir(apiDir);
476
+ await fs.writeFile(path.join(apiDir, "route.ts"), (0, parser_1.generateAPIRoute)(strand));
477
+ }
478
+ for (const view of ast.views) {
479
+ const strandName = view.properties["list"]?.split(".")[0] || ast.strands[0]?.name;
480
+ const strand = ast.strands.find((s) => s.name === strandName) || ast.strands[0];
481
+ if (strand) {
482
+ const viewDir = path.join(projectPath, "src", "app", view.name.toLowerCase());
483
+ await fs.ensureDir(viewDir);
484
+ await fs.writeFile(path.join(viewDir, "page.tsx"), (0, parser_1.generateUIPage)(view, strand, ast.strands, activeThemeClasses));
485
+ }
486
+ }
487
+ // Multi-page generation: if blueprint defines pages, generate per-page routes + layout
488
+ if (ast.pages && ast.pages.length > 0) {
489
+ // Generate layout based on page config
490
+ const useSidebar = ast.pages.some(p => p.layout === 'sidebar');
491
+ const layoutContent = useSidebar
492
+ ? (0, page_generator_1.generateSidebarLayout)(ast.pages, prompt.split(' ').slice(0, 4).join(' '))
493
+ : (0, page_generator_1.generateLayout)(ast.pages, prompt.split(' ').slice(0, 4).join(' '));
494
+ await fs.writeFile(path.join(projectPath, "src", "app", "layout.tsx"), layoutContent);
495
+ // Generate each page route
496
+ for (const page of ast.pages) {
497
+ const pageDir = path.join(projectPath, "src", "app", page.route.replace(/^\//, ''));
498
+ await fs.ensureDir(pageDir);
499
+ await fs.writeFile(path.join(pageDir, "page.tsx"), (0, page_generator_1.generatePageComponent)(page, ast.strands));
500
+ }
501
+ // Root redirect to first page
502
+ await fs.writeFile(path.join(projectPath, "src", "app", "page.tsx"), (0, page_generator_1.generateRootRedirect)(ast.pages[0].route));
503
+ }
504
+ else {
505
+ // Single-page default
506
+ await fs.writeFile(path.join(projectPath, "src", "app", "page.tsx"), generateSpawnHomePage(prompt, ast, activeThemeClasses));
507
+ }
508
+ // Generate test files for each strand
509
+ const testApiDir = path.join(projectPath, "__tests__", "api");
510
+ const testCompDir = path.join(projectPath, "__tests__", "components");
511
+ await fs.ensureDir(testApiDir);
512
+ await fs.ensureDir(testCompDir);
513
+ for (const strand of ast.strands) {
514
+ await fs.writeFile(path.join(testApiDir, `${strand.name.toLowerCase()}.test.ts`), (0, test_generator_1.generateAPITests)(strand));
515
+ await fs.writeFile(path.join(testCompDir, `${strand.name.toLowerCase()}.test.tsx`), (0, test_generator_1.generateComponentTests)(strand));
516
+ }
517
+ await fs.writeFile(path.join(projectPath, "vitest.config.ts"), (0, test_generator_1.generateTestConfig)());
518
+ // Exclude vitest.config.ts from Next.js tsconfig so `next build` doesn't type-check it
519
+ const tsconfigPath = path.join(projectPath, "tsconfig.json");
520
+ try {
521
+ const tsconfigRaw = await fs.readFile(tsconfigPath, "utf-8");
522
+ const tsconfig = JSON.parse(tsconfigRaw);
523
+ if (tsconfig.exclude && !tsconfig.exclude.includes("vitest.config.ts")) {
524
+ tsconfig.exclude.push("vitest.config.ts");
525
+ }
526
+ else if (!tsconfig.exclude) {
527
+ tsconfig.exclude = ["node_modules", "vitest.config.ts"];
528
+ }
529
+ await fs.writeFile(tsconfigPath, JSON.stringify(tsconfig, null, 2));
530
+ }
531
+ catch {
532
+ // Non-fatal — vitest config will still work, just might cause build warnings
533
+ }
534
+ spinner4.succeed("Application generated (with test suite)");
535
+ // =========================================================================
536
+ // PHASE 5: Post-Build Cleanup
537
+ // =========================================================================
538
+ const cleanupSpinner = (0, ora_1.default)("Cleaning temp files...").start();
539
+ await postBuildCleanup(projectPath);
540
+ cleanupSpinner.succeed("Clean build verified");
541
+ // =========================================================================
542
+ // PHASE 5.5: Build Verification with Self-Heal
543
+ // =========================================================================
544
+ const buildOk = await (0, self_heal_1.verifyBuild)(projectPath);
545
+ // =========================================================================
546
+ // PHASE 6: Final Report (NO auto-launch on server)
547
+ // =========================================================================
548
+ console.log(chalk_1.default.green("\n✅ App spawned successfully!\n"));
549
+ console.log(chalk_1.default.white(`📂 Project: ${projectPath}`));
550
+ console.log(chalk_1.default.white(`📦 Docker: cd builds/${projectName} && docker compose up`));
551
+ console.log(chalk_1.default.white(`🔗 Dev: cd builds/${projectName} && npm run dev`));
552
+ console.log(chalk_1.default.white(`🌐 URL: http://localhost:3000\n`));
553
+ // Do NOT auto-launch dev server or open browser on headless server
554
+ // The user can start it manually
555
+ }
556
+ catch (error) {
557
+ console.error(chalk_1.default.red(`\n❌ Spawn failed: ${error.message}`));
558
+ await cleanupOnFailure(projectPath);
559
+ process.exit(1);
560
+ }
561
+ }
562
+ // =============================================================================
563
+ // HELPERS
564
+ // =============================================================================
565
+ function generateProjectName(prompt) {
566
+ return prompt
567
+ .toLowerCase()
568
+ .replace(/[^a-z0-9\s]/g, "")
569
+ .split(/\s+/)
570
+ .slice(0, 3)
571
+ .join("-")
572
+ .substring(0, 30)
573
+ || "helix-app";
574
+ }
575
+ async function generateBlueprint(prompt, constitution) {
576
+ let constitutionSection = '';
577
+ if (constitution) {
578
+ constitutionSection = `
579
+ === CONSTITUTION / PROJECT CONTEXT ===
580
+ The following guidelines MUST be followed when designing the application:
581
+
582
+ ${constitution}
583
+
584
+ === END CONSTITUTION ===
585
+
586
+ `;
587
+ }
588
+ const systemPrompt = `${constitutionSection}You are the Helix Architect v11.0.
589
+ Your task is to convert a natural language app description into a valid Helix blueprint.
590
+
591
+ ${types_1.HELIX_SYNTAX_GUIDE}
592
+
593
+ RULES:
594
+ - Create appropriate strands for the data models needed
595
+ - Create views that make sense for the user's request
596
+ - Keep it simple but complete
597
+ - Output ONLY valid Helix code, no markdown fences or explanations
598
+ ${constitution ? '- IMPORTANT: Follow ALL guidelines from the CONSTITUTION section above' : ''}
599
+
600
+ Example output:
601
+ strand Task {
602
+ field title: String
603
+ field is_completed: Boolean
604
+ field priority: Int
605
+ }
606
+
607
+ view TaskList {
608
+ list: Task.all()
609
+ theme: Glassmorphism
610
+ }
611
+ `;
612
+ return await (0, openrouter_1.createCompletion)(systemPrompt, `Create a Helix blueprint for: ${prompt}`, { model: openrouter_1.DEFAULT_MODEL, maxTokens: 2048 });
613
+ }
614
+ /**
615
+ * Self-healing: Repair a broken blueprint by feeding error back to LLM
616
+ */
617
+ // Self-healing with Qwen Thinking Mode for deeper reasoning
618
+ async function repairBlueprint(blueprint, error) {
619
+ const systemPrompt = `You are a Helix blueprint repair specialist.
620
+ Fix the syntax errors in the provided .helix blueprint.
621
+
622
+ RULES:
623
+ - Output ONLY valid Helix code, no markdown fences or explanations
624
+ - Keep the same strands and views, just fix the syntax
625
+ - strands use: strand Name { field fieldName: Type }
626
+ - views use: view Name { list: StrandName.all() }
627
+ - Valid types: String, Int, Float, Boolean, DateTime
628
+ `;
629
+ return await (0, openrouter_1.createCompletion)(systemPrompt, `Fix this broken Helix blueprint:
630
+
631
+ ERROR: ${error}
632
+
633
+ BLUEPRINT:
634
+ ${blueprint}
635
+
636
+ Output the corrected blueprint:`, { model: openrouter_1.DEFAULT_MODEL, maxTokens: 2048, thinking: true });
637
+ }
638
+ async function fixPrismaSchema(schema, error) {
639
+ const systemPrompt = `You are a Prisma schema repair assistant.
640
+ Your task is to fix syntax errors in Prisma schema files.
641
+
642
+ CRITICAL RULES:
643
+ - You are STRICTLY FORBIDDEN from generating or referencing prisma.config.ts
644
+ - Standard Prisma uses ONLY schema.prisma - nothing else
645
+ - Output ONLY the fixed schema content, no configuration code
646
+ - Output ONLY the fixed schema, no explanations or markdown
647
+ - Keep the same models and fields, just fix the syntax
648
+ - Ensure all types are valid Prisma types (String, Int, Float, Boolean, DateTime)
649
+ - Ensure proper formatting with @id, @default, etc.
650
+ - Do NOT include any TypeScript/JavaScript code
651
+ `;
652
+ return await (0, openrouter_1.createCompletion)(systemPrompt, `Fix this Prisma schema that has an error:
653
+
654
+ ERROR:
655
+ ${error}
656
+
657
+ SCHEMA:
658
+ ${schema}
659
+
660
+ Output the corrected schema:`, { model: openrouter_1.DEFAULT_MODEL, maxTokens: 2048, thinking: true });
661
+ }
662
+ /**
663
+ * Clean up orphaned/temp files after a successful build
664
+ */
665
+ async function postBuildCleanup(projectPath) {
666
+ const junkPatterns = [
667
+ "prisma.config.ts",
668
+ "*.tmp",
669
+ "*.log",
670
+ ".npm",
671
+ "tsconfig.tsbuildinfo",
672
+ ];
673
+ for (const pattern of junkPatterns) {
674
+ if (pattern.includes("*"))
675
+ continue; // Skip glob patterns for now
676
+ const junkPath = path.join(projectPath, pattern);
677
+ if (fs.existsSync(junkPath)) {
678
+ await fs.remove(junkPath);
679
+ }
680
+ }
681
+ }
682
+ /**
683
+ * Clean up failed build directory
684
+ */
685
+ async function cleanupOnFailure(projectPath) {
686
+ if (fs.existsSync(projectPath)) {
687
+ console.log(chalk_1.default.yellow(`🧹 Cleaning up failed build: ${projectPath}`));
688
+ await fs.remove(projectPath);
689
+ }
690
+ }
691
+ // =============================================================================
692
+ // HOME PAGE GENERATOR (unchanged logic, same smart view detection)
693
+ // =============================================================================
694
+ function generateSpawnHomePage(prompt, ast, themeClasses) {
695
+ const tc = themeClasses || (0, themes_1.getThemeClasses)();
696
+ const appTitle = prompt.split(" ").slice(0, 5).join(" ");
697
+ if (ast.strands.length === 0) {
698
+ return `// Spawned by Helix v11.0 - Clean Factory\nexport default function Home() { return (<main className="min-h-screen p-8 flex items-center justify-center"><div className="text-center"><h1 className="text-4xl font-bold ${tc.heading} mb-4">🧬 ${appTitle}</h1><p className="${tc.textMuted}">No strands</p></div></main>); }`;
699
+ }
700
+ const interfaces = ast.strands.map(s => {
701
+ const f = s.fields.map(f => `${f.name}: ${f.type === 'String' ? 'string' : f.type === 'Int' || f.type === 'Float' ? 'number' : 'string'}`).join('; ');
702
+ return `interface ${s.name} { id: string; ${f}; createdAt: string; }`;
703
+ }).join('\n');
704
+ const states = ast.strands.map(s => {
705
+ const l = s.name.toLowerCase();
706
+ const init = s.fields.map(f => `${f.name}: ${f.type === 'Int' || f.type === 'Float' ? '0' : "''"}`).join(', ');
707
+ return `const [${l}s, set${s.name}s] = useState<${s.name}[]>([]);
708
+ const [show${s.name}Form, setShow${s.name}Form] = useState(false);
709
+ const [${l}Form, set${s.name}Form] = useState({ ${init} });`;
710
+ }).join('\n ');
711
+ const funcs = ast.strands.map(s => {
712
+ const l = s.name.toLowerCase();
713
+ const resetForm = s.fields.map(ff => `${ff.name}: ${ff.type === 'Int' || ff.type === 'Float' ? '0' : "''"}`).join(', ');
714
+ return `const fetch${s.name}s = async () => { try { const r = await fetch('/api/${l}'); const j = await r.json(); set${s.name}s(j.data || j); } catch { setError('Failed to load ${l}s'); } };
715
+ const submit${s.name} = async (e: React.FormEvent) => { e.preventDefault(); try { const r = await fetch('/api/${l}', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(${l}Form) }); if (!r.ok) { const err = await r.json(); setError(err.details?.join(', ') || err.error || 'Failed'); return; } setShow${s.name}Form(false); set${s.name}Form({ ${resetForm} }); fetch${s.name}s(); } catch { setError('Failed to create ${l}'); } };
716
+ const del${s.name} = async (id: string) => { if (!confirm('Delete?')) return; try { await fetch('/api/${l}?id=' + id, { method: 'DELETE' }); fetch${s.name}s(); } catch { setError('Failed to delete ${l}'); } };`;
717
+ }).join('\n ');
718
+ const fetchAll = ast.strands.map(s => `fetch${s.name}s()`).join('; ');
719
+ const detectViewType = (fields) => {
720
+ const fieldNames = fields.map(f => f.name.toLowerCase());
721
+ if (fieldNames.some(n => ['image', 'photo', 'avatar', 'thumbnail', 'cover', 'picture', 'img'].includes(n)))
722
+ return 'gallery';
723
+ if (fieldNames.some(n => ['status', 'stage', 'phase', 'state', 'progress'].includes(n)))
724
+ return 'kanban';
725
+ const hasTitle = fieldNames.some(n => ['title', 'name', 'headline'].includes(n));
726
+ const hasContent = fieldNames.some(n => ['body', 'content', 'description', 'message', 'text', 'note'].includes(n));
727
+ if (hasTitle && hasContent)
728
+ return 'feed';
729
+ return 'grid';
730
+ };
731
+ const sections = ast.strands.map(s => {
732
+ const l = s.name.toLowerCase();
733
+ const viewType = detectViewType(s.fields);
734
+ const inputs = s.fields.map(f => {
735
+ const t = f.type === 'Int' || f.type === 'Float' ? 'number' : 'text';
736
+ return `<div className="mb-3"><label className="block ${tc.textMuted} text-sm mb-1">${f.name}</label><input type="${t}" value={${l}Form.${f.name} || ''} onChange={e => set${s.name}Form({...${l}Form, ${f.name}: ${t === 'number' ? 'Number(e.target.value)' : 'e.target.value'}})} className="w-full rounded-md p-3 transition-colors" /></div>`;
737
+ }).join('\n ');
738
+ const titleField = s.fields.find(f => ['title', 'name', 'headline', 'codename'].includes(f.name.toLowerCase()))?.name || s.fields[0]?.name || 'id';
739
+ const statusField = s.fields.find(f => ['status', 'stage', 'phase', 'state', 'progress'].includes(f.name.toLowerCase()))?.name;
740
+ const contentField = s.fields.find(f => ['body', 'content', 'description', 'message', 'text', 'note'].includes(f.name.toLowerCase()))?.name;
741
+ let itemsLayout = '';
742
+ if (viewType === 'kanban' && statusField) {
743
+ itemsLayout = `<div className="flex gap-4 overflow-x-auto pb-4">
744
+ {['Todo', 'In Progress', 'Done', 'Pending', 'Active', 'Complete'].filter(status => ${l}s.some(i => i.${statusField}?.toLowerCase().includes(status.toLowerCase()))).map(status => (
745
+ <div key={status} className="min-w-[280px] glass rounded-xl p-4">
746
+ <h4 className="${tc.heading} font-bold mb-3 flex items-center gap-2">
747
+ <span className="w-3 h-3 rounded-full" style={{background: status === 'Done' || status === 'Complete' ? '${tc.statusColors.success}' : status === 'In Progress' || status === 'Active' ? '${tc.statusColors.warning}' : '${tc.statusColors.info}'}}></span>
748
+ {status}
749
+ </h4>
750
+ <div className="space-y-2">
751
+ {${l}s.filter(i => i.${statusField}?.toLowerCase().includes(status.toLowerCase())).map(item => (
752
+ <div key={item.id} className="bg-white/5 rounded-lg p-3 group">
753
+ <div className="${tc.text} text-sm font-medium">{item.${titleField}}</div>
754
+ <button onClick={() => del${s.name}(item.id)} className="opacity-0 group-hover:opacity-100 text-red-400 text-xs mt-1">Delete</button>
755
+ </div>
756
+ ))}
757
+ </div>
758
+ </div>
759
+ ))}
760
+ </div>`;
761
+ }
762
+ else if (viewType === 'feed' && contentField) {
763
+ itemsLayout = `<div className="space-y-4">
764
+ {${l}s.map(item => (
765
+ <article key={item.id} className="glass rounded-xl p-5 group">
766
+ <div className="flex justify-between items-start mb-2">
767
+ <h3 className="text-xl font-bold ${tc.heading}">{item.${titleField}}</h3>
768
+ <button onClick={() => del${s.name}(item.id)} className="opacity-0 group-hover:opacity-100 text-red-400">🗑️</button>
769
+ </div>
770
+ <p className="${tc.textMuted} leading-relaxed">{item.${contentField}}</p>
771
+ <div className="mt-3 ${tc.textMuted} text-sm">{new Date(item.createdAt).toLocaleString()}</div>
772
+ </article>
773
+ ))}
774
+ </div>`;
775
+ }
776
+ else {
777
+ const display = s.fields.slice(0, 4).map(f => `<div><span className="${tc.textMuted} text-xs">${f.name}</span><div className="${tc.text} text-sm">{String(item.${f.name})}</div></div>`).join('\n ');
778
+ itemsLayout = `<div className="space-y-2">
779
+ {${l}s.map(item => (
780
+ <div key={item.id} className="glass rounded-lg p-4 group flex justify-between hover:bg-white/5">
781
+ <div className="grid grid-cols-4 gap-4 flex-1">${display}</div>
782
+ <button onClick={() => del${s.name}(item.id)} className="opacity-0 group-hover:opacity-100 text-red-400">🗑️</button>
783
+ </div>
784
+ ))}
785
+ </div>`;
786
+ }
787
+ const viewLabel = viewType === 'gallery' ? '🖼️ Gallery' : viewType === 'kanban' ? '📋 Board' : viewType === 'feed' ? '📰 Feed' : '📊 Grid';
788
+ return `
789
+ <section className="mb-10">
790
+ <div className="flex justify-between items-center mb-4">
791
+ <div>
792
+ <h2 className="text-2xl font-bold ${tc.heading}">${s.name}s</h2>
793
+ <span className="text-xs ${tc.textMuted}">${viewLabel}</span>
794
+ </div>
795
+ <button onClick={() => setShow${s.name}Form(true)} className="${tc.primaryButton} px-4 py-2 rounded-lg">+ Add</button>
796
+ </div>
797
+ {show${s.name}Form && (
798
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
799
+ <div className="bg-gray-900 border border-white/10 rounded-xl p-6 w-full max-w-md">
800
+ <h3 className="text-xl font-bold ${tc.heading} mb-4">Add ${s.name}</h3>
801
+ <form onSubmit={submit${s.name}}>
802
+ ${inputs}
803
+ <div className="flex gap-3 mt-4">
804
+ <button type="button" onClick={() => setShow${s.name}Form(false)} className="flex-1 ${tc.secondaryButton} py-2 rounded-lg">Cancel</button>
805
+ <button type="submit" className="flex-1 ${tc.primaryButton} py-2 rounded-lg">Create</button>
806
+ </div>
807
+ </form>
808
+ </div>
809
+ </div>
810
+ )}
811
+ {${l}s.length === 0 ? <div className="glass rounded-lg p-6 text-center ${tc.textMuted}">No ${l}s yet</div> : ${itemsLayout}}
812
+ </section>`;
813
+ }).join('\n');
814
+ return `// Spawned by Helix v11.0 — With error handling, loading states, pagination support
815
+ 'use client';
816
+ import { useState, useEffect } from 'react';
817
+ ${interfaces}
818
+
819
+ // Error Boundary Component
820
+ function ErrorBoundaryFallback({ error, reset }: { error: string; reset: () => void }) {
821
+ return (
822
+ <div className="glass rounded-xl p-6 text-center">
823
+ <p className="text-red-400 mb-4">{error}</p>
824
+ <button onClick={reset} className="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg">Try Again</button>
825
+ </div>
826
+ );
827
+ }
828
+
829
+ export default function Home() {
830
+ const [loading, setLoading] = useState(true);
831
+ const [error, setError] = useState<string | null>(null);
832
+ ${states}
833
+ ${funcs}
834
+ useEffect(() => { ${fetchAll}; setLoading(false); }, []);
835
+
836
+ // Auto-dismiss error toast after 5 seconds
837
+ useEffect(() => { if (error) { const t = setTimeout(() => setError(null), 5000); return () => clearTimeout(t); } }, [error]);
838
+
839
+ if (loading) return (
840
+ <main className="min-h-screen p-8 flex items-center justify-center">
841
+ <div className="text-center">
842
+ <div className="w-8 h-8 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
843
+ <p className="${tc.textMuted}">Loading...</p>
844
+ </div>
845
+ </main>
846
+ );
847
+
848
+ return (
849
+ <main className="min-h-screen p-8">
850
+ {/* Error Toast */}
851
+ {error && (
852
+ <div className="fixed top-4 right-4 z-50 bg-red-900/90 border border-red-500/50 text-red-200 px-4 py-3 rounded-lg shadow-lg flex items-center gap-3 animate-in">
853
+ <span>{error}</span>
854
+ <button onClick={() => setError(null)} className="text-red-400 hover:text-red-300">✕</button>
855
+ </div>
856
+ )}
857
+ <div className="max-w-6xl mx-auto">
858
+ <div className="mb-8">
859
+ <span className="text-sm text-indigo-400 font-mono">🧬 Helix v11.0</span>
860
+ <h1 className="text-4xl font-bold ${tc.heading} mt-1">${appTitle}</h1>
861
+ <p className="${tc.textMuted}">${ast.strands.length} data types</p>
862
+ </div>
863
+ ${sections}
864
+ </div>
865
+ </main>
866
+ );
867
+ }
868
+ `;
869
+ }
870
+ // =============================================================================
871
+ // SCOPE STAGE (V2) - Requirements generation for complex prompts
872
+ // =============================================================================
873
+ /**
874
+ * Count potential entity/model mentions in a prompt.
875
+ * Heuristic: capitalized nouns that could be data models.
876
+ */
877
+ function countEntityMentions(prompt) {
878
+ const entityPattern = /\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b/g;
879
+ const matches = prompt.match(entityPattern) || [];
880
+ // Deduplicate
881
+ const unique = new Set(matches.map(m => m.toLowerCase()));
882
+ return unique.size;
883
+ }
884
+ /**
885
+ * SCOPE Stage: Generate structured requirements from a complex prompt.
886
+ * Uses the fast/cheap model (Qwen Flash) to produce a JSON requirements doc.
887
+ */
888
+ async function generateRequirements(prompt) {
889
+ const systemPrompt = `You are a requirements analyst for a code generation system.
890
+ Analyze the user's app description and produce a structured JSON requirements document.
891
+
892
+ Output ONLY valid JSON with this structure:
893
+ {
894
+ "models": [
895
+ {
896
+ "name": "ModelName",
897
+ "fields": [{ "name": "fieldName", "type": "String|Int|Float|Boolean|DateTime" }],
898
+ "relations": [{ "name": "relName", "target": "OtherModel", "type": "belongs_to|has_many" }]
899
+ }
900
+ ],
901
+ "views": [
902
+ { "name": "ViewName", "displays": "ModelName", "type": "list|kanban|feed|gallery" }
903
+ ],
904
+ "theme": "glassmorphism|professional|minimal|vibrant",
905
+ "complexity": "simple|moderate|complex"
906
+ }
907
+
908
+ Rules:
909
+ - Identify ALL data models and their relationships
910
+ - Choose appropriate field types
911
+ - Detect the best view type for each model
912
+ - Suggest a theme that matches the domain
913
+ - Output ONLY JSON, no markdown fences or explanations`;
914
+ return await (0, openrouter_1.createCompletion)(systemPrompt, `Analyze requirements for: ${prompt}`, { model: openrouter_1.LOCAL_MODEL, maxTokens: 2048 });
915
+ }
916
+ //# sourceMappingURL=spawn.js.map