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.
- package/README.md +168 -0
- package/dist/architect.d.ts +14 -0
- package/dist/architect.d.ts.map +1 -0
- package/dist/architect.js +127 -0
- package/dist/architect.js.map +1 -0
- package/dist/bin/helix.d.ts +20 -0
- package/dist/bin/helix.d.ts.map +1 -0
- package/dist/bin/helix.js +921 -0
- package/dist/bin/helix.js.map +1 -0
- package/dist/commands/collaborate/index.d.ts +2 -0
- package/dist/commands/collaborate/index.d.ts.map +1 -0
- package/dist/commands/collaborate/index.js +129 -0
- package/dist/commands/collaborate/index.js.map +1 -0
- package/dist/commands/collaborate/server.d.ts +31 -0
- package/dist/commands/collaborate/server.d.ts.map +1 -0
- package/dist/commands/collaborate/server.js +159 -0
- package/dist/commands/collaborate/server.js.map +1 -0
- package/dist/commands/deploy/index.d.ts +25 -0
- package/dist/commands/deploy/index.d.ts.map +1 -0
- package/dist/commands/deploy/index.js +130 -0
- package/dist/commands/deploy/index.js.map +1 -0
- package/dist/commands/deploy/platforms/fly.d.ts +9 -0
- package/dist/commands/deploy/platforms/fly.d.ts.map +1 -0
- package/dist/commands/deploy/platforms/fly.js +68 -0
- package/dist/commands/deploy/platforms/fly.js.map +1 -0
- package/dist/commands/deploy/platforms/railway.d.ts +9 -0
- package/dist/commands/deploy/platforms/railway.d.ts.map +1 -0
- package/dist/commands/deploy/platforms/railway.js +115 -0
- package/dist/commands/deploy/platforms/railway.js.map +1 -0
- package/dist/commands/deploy/platforms/vercel.d.ts +10 -0
- package/dist/commands/deploy/platforms/vercel.d.ts.map +1 -0
- package/dist/commands/deploy/platforms/vercel.js +126 -0
- package/dist/commands/deploy/platforms/vercel.js.map +1 -0
- package/dist/commands/deploy.d.ts +6 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +56 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/evolve/analyzers/performance.d.ts +13 -0
- package/dist/commands/evolve/analyzers/performance.d.ts.map +1 -0
- package/dist/commands/evolve/analyzers/performance.js +591 -0
- package/dist/commands/evolve/analyzers/performance.js.map +1 -0
- package/dist/commands/evolve/analyzers/security.d.ts +21 -0
- package/dist/commands/evolve/analyzers/security.d.ts.map +1 -0
- package/dist/commands/evolve/analyzers/security.js +280 -0
- package/dist/commands/evolve/analyzers/security.js.map +1 -0
- package/dist/commands/evolve/index.d.ts +2 -0
- package/dist/commands/evolve/index.d.ts.map +1 -0
- package/dist/commands/evolve/index.js +122 -0
- package/dist/commands/evolve/index.js.map +1 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +277 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +176 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/library/index.d.ts +27 -0
- package/dist/commands/library/index.d.ts.map +1 -0
- package/dist/commands/library/index.js +126 -0
- package/dist/commands/library/index.js.map +1 -0
- package/dist/commands/migrate.d.ts +5 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +258 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/new.d.ts +6 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +195 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/preflight.d.ts +20 -0
- package/dist/commands/preflight.d.ts.map +1 -0
- package/dist/commands/preflight.js +182 -0
- package/dist/commands/preflight.js.map +1 -0
- package/dist/commands/preview.d.ts +13 -0
- package/dist/commands/preview.d.ts.map +1 -0
- package/dist/commands/preview.js +260 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +96 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/spawn.d.ts +11 -0
- package/dist/commands/spawn.d.ts.map +1 -0
- package/dist/commands/spawn.js +916 -0
- package/dist/commands/spawn.js.map +1 -0
- package/dist/compiler.d.ts +12 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +92 -0
- package/dist/compiler.js.map +1 -0
- package/dist/core/file-writer.d.ts +36 -0
- package/dist/core/file-writer.d.ts.map +1 -0
- package/dist/core/file-writer.js +268 -0
- package/dist/core/file-writer.js.map +1 -0
- package/dist/core/registry.d.ts +57 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +222 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/self-healing.d.ts +47 -0
- package/dist/core/self-healing.d.ts.map +1 -0
- package/dist/core/self-healing.js +250 -0
- package/dist/core/self-healing.js.map +1 -0
- package/dist/core/types.d.ts +126 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +7 -0
- package/dist/core/types.js.map +1 -0
- package/dist/generators/databases/mongodb.d.ts +10 -0
- package/dist/generators/databases/mongodb.d.ts.map +1 -0
- package/dist/generators/databases/mongodb.js +83 -0
- package/dist/generators/databases/mongodb.js.map +1 -0
- package/dist/generators/databases/redis.d.ts +2 -0
- package/dist/generators/databases/redis.d.ts.map +1 -0
- package/dist/generators/databases/redis.js +140 -0
- package/dist/generators/databases/redis.js.map +1 -0
- package/dist/generators/flutter.d.ts +32 -0
- package/dist/generators/flutter.d.ts.map +1 -0
- package/dist/generators/flutter.js +628 -0
- package/dist/generators/flutter.js.map +1 -0
- package/dist/openrouter.d.ts +68 -0
- package/dist/openrouter.d.ts.map +1 -0
- package/dist/openrouter.js +241 -0
- package/dist/openrouter.js.map +1 -0
- package/dist/page-generator.d.ts +22 -0
- package/dist/page-generator.d.ts.map +1 -0
- package/dist/page-generator.js +192 -0
- package/dist/page-generator.js.map +1 -0
- package/dist/parser.d.ts +76 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +691 -0
- package/dist/parser.js.map +1 -0
- package/dist/prompts/master-architect.d.ts +9 -0
- package/dist/prompts/master-architect.d.ts.map +1 -0
- package/dist/prompts/master-architect.js +150 -0
- package/dist/prompts/master-architect.js.map +1 -0
- package/dist/researcher.d.ts +12 -0
- package/dist/researcher.d.ts.map +1 -0
- package/dist/researcher.js +85 -0
- package/dist/researcher.js.map +1 -0
- package/dist/self-heal.d.ts +29 -0
- package/dist/self-heal.d.ts.map +1 -0
- package/dist/self-heal.js +260 -0
- package/dist/self-heal.js.map +1 -0
- package/dist/services/SupabaseDeployer.d.ts +9 -0
- package/dist/services/SupabaseDeployer.d.ts.map +1 -0
- package/dist/services/SupabaseDeployer.js +50 -0
- package/dist/services/SupabaseDeployer.js.map +1 -0
- package/dist/test-generator.d.ts +18 -0
- package/dist/test-generator.d.ts.map +1 -0
- package/dist/test-generator.js +180 -0
- package/dist/test-generator.js.map +1 -0
- package/dist/themes/index.d.ts +52 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +273 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +81 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/constitutional-validator.d.ts +73 -0
- package/dist/utils/constitutional-validator.d.ts.map +1 -0
- package/dist/utils/constitutional-validator.js +249 -0
- package/dist/utils/constitutional-validator.js.map +1 -0
- package/library/auth-flow/app/api/auth/[...nextauth]/route.ts +31 -0
- package/library/auth-flow/app/api/auth/login/route.ts +90 -0
- package/library/auth-flow/app/api/auth/register/route.ts +91 -0
- package/library/auth-flow/components/auth/AuthMiddleware.tsx +139 -0
- package/library/auth-flow/components/auth/LoginForm.tsx +125 -0
- package/library/auth-flow/components/auth/RegisterForm.tsx +168 -0
- package/library/auth-flow/components/auth/nextauth-config.ts +99 -0
- package/library/auth-flow/manifest.json +29 -0
- package/library/auth-flow/schema.prisma +45 -0
- package/library/dashboard-analytics/components/dashboard/ActivityFeed.tsx +109 -0
- package/library/dashboard-analytics/components/dashboard/LineChart.tsx +180 -0
- package/library/dashboard-analytics/components/dashboard/StatsCard.tsx +47 -0
- package/library/dashboard-analytics/components/dashboard/SummaryGrid.tsx +39 -0
- package/library/dashboard-analytics/manifest.json +19 -0
- package/library/data-table/components/table/BulkActions.tsx +59 -0
- package/library/data-table/components/table/ColumnToggle.tsx +65 -0
- package/library/data-table/components/table/DataTable.tsx +318 -0
- package/library/data-table/components/table/ExportCSV.tsx +52 -0
- package/library/data-table/components/table/SearchFilter.tsx +48 -0
- package/library/data-table/manifest.json +20 -0
- package/library/file-upload/app/api/upload/route.ts +107 -0
- package/library/file-upload/components/upload/DropZone.tsx +268 -0
- package/library/file-upload/components/upload/FilePreview.tsx +82 -0
- package/library/file-upload/components/upload/UploadProgress.tsx +92 -0
- package/library/file-upload/components/upload/fileStorage.ts +142 -0
- package/library/file-upload/manifest.json +21 -0
- package/library/notification-system/app/api/notifications/route.ts +121 -0
- package/library/notification-system/components/notifications/NotificationBell.tsx +154 -0
- package/library/notification-system/components/notifications/NotificationProvider.tsx +161 -0
- package/library/notification-system/components/notifications/Toast.tsx +112 -0
- package/library/notification-system/manifest.json +20 -0
- package/package.json +66 -0
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Helix CLI v11.0 - Complete Development Platform
|
|
5
|
+
* AI-Native Programming Language with Full-Stack Generation
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* spawn <prompt> - ONE-SHOT: Full app from natural language
|
|
9
|
+
* new <project> - Scaffold a new Helix project
|
|
10
|
+
* generate <blueprint> - Generate Prisma + API + UI from .helix
|
|
11
|
+
* run - Start the dev server
|
|
12
|
+
* preview - Hot-reload preview with file watching
|
|
13
|
+
* deploy - One-command deployment
|
|
14
|
+
* research <topic> - Generate domain research
|
|
15
|
+
* draft <idea> - Create a .helix blueprint
|
|
16
|
+
* build <file> - Compile .helix to React component
|
|
17
|
+
* plugins - List registered generator plugins
|
|
18
|
+
* models - List available AI models
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
54
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
55
|
+
};
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
const commander_1 = require("commander");
|
|
58
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
59
|
+
const dotenv = __importStar(require("dotenv"));
|
|
60
|
+
const fs = __importStar(require("fs"));
|
|
61
|
+
const path = __importStar(require("path"));
|
|
62
|
+
// Load environment variables
|
|
63
|
+
dotenv.config();
|
|
64
|
+
// Import core modules
|
|
65
|
+
const researcher_1 = require("../researcher");
|
|
66
|
+
const architect_1 = require("../architect");
|
|
67
|
+
const compiler_1 = require("../compiler");
|
|
68
|
+
const openrouter_1 = require("../openrouter");
|
|
69
|
+
// Import v11.0 command modules
|
|
70
|
+
const new_1 = require("../commands/new");
|
|
71
|
+
const generate_1 = require("../commands/generate");
|
|
72
|
+
const run_1 = require("../commands/run");
|
|
73
|
+
// Import v11.0 command modules (spawn & flutter)
|
|
74
|
+
const spawn_1 = require("../commands/spawn");
|
|
75
|
+
const flutter_1 = require("../generators/flutter");
|
|
76
|
+
// Import v11.0 command modules (deploy & platform)
|
|
77
|
+
const deploy_1 = require("../commands/deploy");
|
|
78
|
+
const preview_1 = require("../commands/preview");
|
|
79
|
+
const registry_1 = require("../core/registry");
|
|
80
|
+
const evolve_1 = require("../commands/evolve");
|
|
81
|
+
// ASCII Art Banner
|
|
82
|
+
const banner = `
|
|
83
|
+
${chalk_1.default.cyan("╦ ╦╔═╗╦ ╦═╗ ╦")}
|
|
84
|
+
${chalk_1.default.cyan("╠═╣║╣ ║ ║╔╩╦╝")}
|
|
85
|
+
${chalk_1.default.cyan("╩ ╩╚═╝╩═╝╩╩ ╚═")} ${chalk_1.default.magenta("v11.0")}
|
|
86
|
+
${chalk_1.default.gray("AI-Native Development Platform")}
|
|
87
|
+
${chalk_1.default.gray("Generate • Preview • Deploy • Evolve")}
|
|
88
|
+
`;
|
|
89
|
+
const program = new commander_1.Command();
|
|
90
|
+
program
|
|
91
|
+
.name("helix")
|
|
92
|
+
.description("Helix - AI-Native Development Platform")
|
|
93
|
+
.version("11.0.0")
|
|
94
|
+
.addHelpText("before", banner);
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// V4.0 COMMAND: One-Shot Spawn
|
|
97
|
+
// ============================================================================
|
|
98
|
+
program
|
|
99
|
+
.command("spawn <prompt>")
|
|
100
|
+
.description("One-shot generation: Full app from natural language")
|
|
101
|
+
.option("-t, --target <platform>", "Target: 'web' (Next.js) or 'flutter'", "web")
|
|
102
|
+
.option("-d, --db <database>", "Database: 'postgres', 'mongodb', 'redis', or comma-separated")
|
|
103
|
+
.option("--theme <theme>", "UI theme: glassmorphism, professional, minimal, vibrant", "glassmorphism")
|
|
104
|
+
.option("--ai-context", "Enable AI context layer with Redis")
|
|
105
|
+
.option("--cache", "Add Redis caching layer")
|
|
106
|
+
.option("--no-constitution", "Bypass constitutional validation")
|
|
107
|
+
.option("--components <ids>", "Helix Library component IDs (comma-separated)")
|
|
108
|
+
.option("--constitution <file>", "Path to constitution.md file")
|
|
109
|
+
.option("--ai <provider>", "AI provider (for Flutter): 'openrouter'")
|
|
110
|
+
.option("--dry-run", "Show what would be generated without creating files")
|
|
111
|
+
.action(async (prompt, options) => {
|
|
112
|
+
console.log(banner);
|
|
113
|
+
// Build spawn options
|
|
114
|
+
const spawnOptions = {
|
|
115
|
+
target: options.target,
|
|
116
|
+
db: options.db,
|
|
117
|
+
theme: options.theme,
|
|
118
|
+
aiContext: options.aiContext,
|
|
119
|
+
cache: options.cache,
|
|
120
|
+
noConstitution: options.noConstitution,
|
|
121
|
+
components: options.components ? options.components.split(',') : [],
|
|
122
|
+
dryRun: options.dryRun || false,
|
|
123
|
+
};
|
|
124
|
+
// Dry run mode - show what would happen
|
|
125
|
+
if (options.dryRun) {
|
|
126
|
+
console.log(chalk_1.default.yellow("🔍 DRY RUN MODE — No files will be created\n"));
|
|
127
|
+
console.log(chalk_1.default.white(" Configuration:"));
|
|
128
|
+
console.log(chalk_1.default.gray(` Target: ${options.target}`));
|
|
129
|
+
console.log(chalk_1.default.gray(` Theme: ${options.theme}`));
|
|
130
|
+
console.log(chalk_1.default.gray(` Database: ${options.db || 'sqlite (default)'}`));
|
|
131
|
+
console.log(chalk_1.default.gray(` Constitution: ${options.noConstitution ? 'disabled' : 'enabled'}`));
|
|
132
|
+
console.log(chalk_1.default.gray(` Components: ${spawnOptions.components.length > 0 ? spawnOptions.components.join(', ') : 'none'}`));
|
|
133
|
+
console.log(chalk_1.default.gray(` AI Context: ${options.aiContext ? 'yes' : 'no'}`));
|
|
134
|
+
console.log(chalk_1.default.gray(` Cache: ${options.cache ? 'yes' : 'no'}`));
|
|
135
|
+
console.log(chalk_1.default.white("\n Would generate:"));
|
|
136
|
+
console.log(chalk_1.default.gray(" • Next.js project scaffold"));
|
|
137
|
+
console.log(chalk_1.default.gray(" • AI-generated .helix blueprint from prompt"));
|
|
138
|
+
console.log(chalk_1.default.gray(" • Prisma schema + SQLite database"));
|
|
139
|
+
console.log(chalk_1.default.gray(" • API routes with validation, rate limiting, pagination"));
|
|
140
|
+
console.log(chalk_1.default.gray(" • React UI pages with theme applied"));
|
|
141
|
+
console.log(chalk_1.default.gray(" • globals.css with theme CSS"));
|
|
142
|
+
console.log(chalk_1.default.yellow("\n Run without --dry-run to generate the app."));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Load constitution if specified
|
|
146
|
+
let constitutionContent;
|
|
147
|
+
let constitutionSource = null;
|
|
148
|
+
if (options.constitution) {
|
|
149
|
+
// Priority 1: Explicit --constitution flag
|
|
150
|
+
const userConstitutionPath = path.isAbsolute(options.constitution)
|
|
151
|
+
? options.constitution
|
|
152
|
+
: path.join(process.cwd(), options.constitution);
|
|
153
|
+
if (fs.existsSync(userConstitutionPath)) {
|
|
154
|
+
constitutionContent = fs.readFileSync(userConstitutionPath, "utf-8");
|
|
155
|
+
constitutionSource = path.basename(userConstitutionPath);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.error(chalk_1.default.red(`❌ Constitution file not found: ${options.constitution}`));
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Priority 2: Auto-detect constitution.md in current directory
|
|
164
|
+
const defaultConstitution = path.join(process.cwd(), "constitution.md");
|
|
165
|
+
if (fs.existsSync(defaultConstitution)) {
|
|
166
|
+
constitutionContent = fs.readFileSync(defaultConstitution, "utf-8");
|
|
167
|
+
constitutionSource = "constitution.md";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Log if constitution is loaded
|
|
171
|
+
if (constitutionSource) {
|
|
172
|
+
console.log(chalk_1.default.yellow(`⚖️ Constitution Loaded: ${constitutionSource}`));
|
|
173
|
+
}
|
|
174
|
+
// Check API key for ALL targets (web uses OpenRouter for blueprint generation)
|
|
175
|
+
if (!process.env.OPENROUTER_API_KEY) {
|
|
176
|
+
console.error(chalk_1.default.red("❌ OPENROUTER_API_KEY not found in environment"));
|
|
177
|
+
console.error(chalk_1.default.gray(" Set it in .env or export OPENROUTER_API_KEY=sk-or-v1-..."));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
// Route to appropriate generator based on target
|
|
181
|
+
if (options.target === "flutter") {
|
|
182
|
+
console.log(chalk_1.default.magenta("📱 Target: Flutter Mobile App"));
|
|
183
|
+
if (options.db === "supabase") {
|
|
184
|
+
console.log(chalk_1.default.blue("☁️ Database: Supabase Cloud (Realtime)"));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.log(chalk_1.default.gray("💾 Database: Local (In-Memory)"));
|
|
188
|
+
}
|
|
189
|
+
if (options.ai === "openrouter") {
|
|
190
|
+
console.log(chalk_1.default.green("🤖 AI: OpenRouter (Cloud Intelligence)"));
|
|
191
|
+
}
|
|
192
|
+
await (0, flutter_1.generateFlutterApp)(prompt, constitutionContent, options.db, options.ai);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.log(chalk_1.default.cyan("🌐 Target: Next.js Web App"));
|
|
196
|
+
await (0, spawn_1.spawnApp)(prompt, spawnOptions, constitutionContent);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// ============================================================================
|
|
200
|
+
// V3.0 COMMANDS: Full-Stack Generation
|
|
201
|
+
// ============================================================================
|
|
202
|
+
program
|
|
203
|
+
.command("new <project-name>")
|
|
204
|
+
.description("Scaffold a new Helix project (Next.js + Prisma + Tailwind)")
|
|
205
|
+
.action(async (projectName) => {
|
|
206
|
+
console.log(banner);
|
|
207
|
+
await (0, new_1.createProject)(projectName);
|
|
208
|
+
});
|
|
209
|
+
program
|
|
210
|
+
.command("generate [blueprint]")
|
|
211
|
+
.alias("gen")
|
|
212
|
+
.description("Generate full stack from .helix blueprint or prompt")
|
|
213
|
+
.option("-t, --target <platform>", "Target platform: 'web' (Next.js) or 'flutter' (Mobile)", "web")
|
|
214
|
+
.option("-p, --prompt <text>", "Direct prompt for generation (Flutter only)")
|
|
215
|
+
.action(async (blueprint, options) => {
|
|
216
|
+
console.log(banner);
|
|
217
|
+
// Route to appropriate generator based on target
|
|
218
|
+
if (options.target === "flutter") {
|
|
219
|
+
console.log(chalk_1.default.magenta("📱 Target: Flutter Mobile App"));
|
|
220
|
+
let generationPrompt;
|
|
221
|
+
// Priority 1: Direct --prompt flag
|
|
222
|
+
if (options.prompt) {
|
|
223
|
+
generationPrompt = options.prompt;
|
|
224
|
+
console.log(chalk_1.default.yellow("📝 Using direct prompt"));
|
|
225
|
+
}
|
|
226
|
+
// Priority 2: Blueprint file
|
|
227
|
+
else if (blueprint) {
|
|
228
|
+
const blueprintPath = path.resolve(process.cwd(), blueprint);
|
|
229
|
+
if (!fs.existsSync(blueprintPath)) {
|
|
230
|
+
console.error(chalk_1.default.red(`❌ Blueprint file not found: ${blueprint}`));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
const blueprintContent = fs.readFileSync(blueprintPath, "utf-8");
|
|
234
|
+
generationPrompt = `Generate from this .helix blueprint:\n\n${blueprintContent}`;
|
|
235
|
+
console.log(chalk_1.default.yellow(`📄 Using blueprint: ${blueprint}`));
|
|
236
|
+
}
|
|
237
|
+
// No input provided
|
|
238
|
+
else {
|
|
239
|
+
console.error(chalk_1.default.red("❌ Please provide a blueprint file or use --prompt"));
|
|
240
|
+
console.log(chalk_1.default.gray(" helix generate blueprint.helix --target flutter"));
|
|
241
|
+
console.log(chalk_1.default.gray(' helix generate --target flutter --prompt "A task app with..."'));
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
await (0, flutter_1.regenerateFlutterDart)(generationPrompt);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Web target requires a blueprint file
|
|
248
|
+
if (!blueprint) {
|
|
249
|
+
console.error(chalk_1.default.red("❌ Please provide a blueprint file for web generation"));
|
|
250
|
+
console.log(chalk_1.default.gray(" helix generate blueprint.helix"));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
console.log(chalk_1.default.cyan("🌐 Target: Next.js Web App"));
|
|
254
|
+
await (0, generate_1.generateStack)(blueprint);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
program
|
|
258
|
+
.command("run")
|
|
259
|
+
.description("Start the dev server and open browser")
|
|
260
|
+
.action(async () => {
|
|
261
|
+
console.log(banner);
|
|
262
|
+
await (0, run_1.runDevServer)();
|
|
263
|
+
});
|
|
264
|
+
// ============================================================================
|
|
265
|
+
// RESEARCH COMMAND
|
|
266
|
+
// ============================================================================
|
|
267
|
+
program
|
|
268
|
+
.command("research <topic>")
|
|
269
|
+
.description("Conduct deep domain research and generate context file")
|
|
270
|
+
.option("-m, --model <model>", "AI model to use")
|
|
271
|
+
.action(async (topic, options) => {
|
|
272
|
+
console.log(banner);
|
|
273
|
+
if (!process.env.OPENROUTER_API_KEY) {
|
|
274
|
+
console.error(chalk_1.default.red("❌ OPENROUTER_API_KEY not found in environment"));
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
const model = options.model || openrouter_1.RESEARCH_MODEL;
|
|
278
|
+
console.log(chalk_1.default.yellow(`💰 Using research-optimized model: ${model}`));
|
|
279
|
+
try {
|
|
280
|
+
await (0, researcher_1.conductResearch)(topic, model);
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// DRAFT COMMAND
|
|
288
|
+
// ============================================================================
|
|
289
|
+
program
|
|
290
|
+
.command("draft <idea>")
|
|
291
|
+
.description("Draft a .helix blueprint from a user idea")
|
|
292
|
+
.option("-c, --context <file>", "Path to context/research file")
|
|
293
|
+
.option("-m, --model <model>", "AI model to use")
|
|
294
|
+
.action(async (idea, options) => {
|
|
295
|
+
console.log(banner);
|
|
296
|
+
if (!process.env.OPENROUTER_API_KEY) {
|
|
297
|
+
console.error(chalk_1.default.red("❌ OPENROUTER_API_KEY not found in environment"));
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
const model = options.model || openrouter_1.DEFAULT_MODEL;
|
|
301
|
+
try {
|
|
302
|
+
let contextFile = options.context;
|
|
303
|
+
if (!contextFile) {
|
|
304
|
+
const defaultContext = path.join(process.cwd(), "research.md");
|
|
305
|
+
if (fs.existsSync(defaultContext)) {
|
|
306
|
+
contextFile = defaultContext;
|
|
307
|
+
console.log(chalk_1.default.yellow(`📎 Auto-using context: research.md`));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
await (0, architect_1.draftBlueprint)(idea, contextFile, model);
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// BUILD COMMAND
|
|
318
|
+
// ============================================================================
|
|
319
|
+
program
|
|
320
|
+
.command("build <file>")
|
|
321
|
+
.description("Compile a .helix blueprint to React/Next.js component")
|
|
322
|
+
.option("-m, --model <model>", "AI model to use")
|
|
323
|
+
.action(async (file, options) => {
|
|
324
|
+
console.log(banner);
|
|
325
|
+
if (!process.env.OPENROUTER_API_KEY) {
|
|
326
|
+
console.error(chalk_1.default.red("❌ OPENROUTER_API_KEY not found in environment"));
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
const model = options.model || openrouter_1.DEFAULT_MODEL;
|
|
330
|
+
if (!file.endsWith(".helix")) {
|
|
331
|
+
console.error(chalk_1.default.red(`❌ Invalid file type. Expected .helix file`));
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
await (0, compiler_1.compileToReact)(file, model);
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// PIPELINE COMMAND
|
|
343
|
+
// ============================================================================
|
|
344
|
+
program
|
|
345
|
+
.command("pipeline <topic> <idea>")
|
|
346
|
+
.description("Run full AI pipeline: research -> draft -> build")
|
|
347
|
+
.option("-m, --model <model>", "AI model for draft/build")
|
|
348
|
+
.action(async (topic, idea, options) => {
|
|
349
|
+
console.log(banner);
|
|
350
|
+
console.log(chalk_1.default.magenta("\n🚀 Starting Helix Pipeline...\n"));
|
|
351
|
+
if (!process.env.OPENROUTER_API_KEY) {
|
|
352
|
+
console.error(chalk_1.default.red("❌ OPENROUTER_API_KEY not found"));
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
const draftModel = options.model || openrouter_1.DEFAULT_MODEL;
|
|
356
|
+
try {
|
|
357
|
+
console.log(chalk_1.default.magenta("━━━ STEP 1: RESEARCH ━━━"));
|
|
358
|
+
await (0, researcher_1.conductResearch)(topic, openrouter_1.RESEARCH_MODEL);
|
|
359
|
+
console.log(chalk_1.default.magenta("\n━━━ STEP 2: DRAFT ━━━"));
|
|
360
|
+
const contextFile = path.join(process.cwd(), "research.md");
|
|
361
|
+
const helixFile = await (0, architect_1.draftBlueprint)(idea, contextFile, draftModel);
|
|
362
|
+
console.log(chalk_1.default.magenta("\n━━━ STEP 3: BUILD ━━━"));
|
|
363
|
+
await (0, compiler_1.compileToReact)(helixFile, draftModel);
|
|
364
|
+
console.log(chalk_1.default.green("\n🎉 Pipeline complete!"));
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
console.error(chalk_1.default.red("\n❌ Pipeline failed"));
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
// ============================================================================
|
|
372
|
+
// MODELS COMMAND
|
|
373
|
+
// ============================================================================
|
|
374
|
+
program
|
|
375
|
+
.command("models")
|
|
376
|
+
.description("List available AI models and current defaults")
|
|
377
|
+
.action(() => {
|
|
378
|
+
console.log(banner);
|
|
379
|
+
console.log(chalk_1.default.cyan("\n⚙️ Current Configuration:\n"));
|
|
380
|
+
console.log(` ${chalk_1.default.gray("Default Model:")} ${chalk_1.default.green(openrouter_1.DEFAULT_MODEL)}`);
|
|
381
|
+
console.log(` ${chalk_1.default.gray("Research Model:")} ${chalk_1.default.green(openrouter_1.RESEARCH_MODEL)} ${chalk_1.default.yellow("(cost-optimized)")}`);
|
|
382
|
+
console.log(chalk_1.default.cyan("\n📋 Available Models:\n"));
|
|
383
|
+
openrouter_1.AVAILABLE_MODELS.forEach((model, index) => {
|
|
384
|
+
const isDefault = model === openrouter_1.DEFAULT_MODEL;
|
|
385
|
+
const isResearch = model === openrouter_1.RESEARCH_MODEL;
|
|
386
|
+
let suffix = "";
|
|
387
|
+
if (isDefault)
|
|
388
|
+
suffix = chalk_1.default.green(" ← default");
|
|
389
|
+
if (isResearch)
|
|
390
|
+
suffix = chalk_1.default.yellow(" ← research");
|
|
391
|
+
console.log(` ${chalk_1.default.gray(`${index + 1}.`)} ${chalk_1.default.white(model)}${suffix}`);
|
|
392
|
+
});
|
|
393
|
+
console.log(chalk_1.default.gray("\nUsage: helix <command> --model <model>\n"));
|
|
394
|
+
});
|
|
395
|
+
// ============================================================================
|
|
396
|
+
// V10.0 COMMANDS: Platform Features
|
|
397
|
+
// ============================================================================
|
|
398
|
+
program
|
|
399
|
+
.command("preview")
|
|
400
|
+
.description("Launch hot-reload preview server with .helix file watching")
|
|
401
|
+
.action(async () => {
|
|
402
|
+
console.log(banner);
|
|
403
|
+
await (0, preview_1.preview)();
|
|
404
|
+
});
|
|
405
|
+
program
|
|
406
|
+
.command("deploy")
|
|
407
|
+
.description("One-command deployment to cloud platforms")
|
|
408
|
+
.option("-p, --platform <platform>", "Deployment platform: vercel, firebase, netlify", "vercel")
|
|
409
|
+
.option("-t, --token <token>", "Auth token for the platform (optional)")
|
|
410
|
+
.action(async (options) => {
|
|
411
|
+
console.log(banner);
|
|
412
|
+
await (0, deploy_1.deploy)(options.platform, options.token);
|
|
413
|
+
});
|
|
414
|
+
program
|
|
415
|
+
.command("plugins")
|
|
416
|
+
.description("List registered generator plugins")
|
|
417
|
+
.action(async () => {
|
|
418
|
+
console.log(banner);
|
|
419
|
+
const registry = (0, registry_1.getRegistry)();
|
|
420
|
+
await registry.scanForPlugins();
|
|
421
|
+
registry.listPlugins();
|
|
422
|
+
});
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// EVOLVE COMMAND: Codebase Evolution & Analysis
|
|
425
|
+
// ============================================================================
|
|
426
|
+
program
|
|
427
|
+
.command("evolve [action] [category]")
|
|
428
|
+
.description("Evolve codebase: scan, suggest, apply fixes, security-audit")
|
|
429
|
+
.option("-p, --path <path>", "Project path to analyze", process.cwd())
|
|
430
|
+
.action(async (action, category, options) => {
|
|
431
|
+
console.log(banner);
|
|
432
|
+
await (0, evolve_1.evolveCodebase)(action || 'scan', category, options.path);
|
|
433
|
+
});
|
|
434
|
+
// ============================================================================
|
|
435
|
+
// LIST COMMAND: Show all generated projects
|
|
436
|
+
// ============================================================================
|
|
437
|
+
program
|
|
438
|
+
.command("list")
|
|
439
|
+
.alias("ls")
|
|
440
|
+
.description("List all generated projects in builds/")
|
|
441
|
+
.action(async () => {
|
|
442
|
+
console.log(banner);
|
|
443
|
+
const buildsDir = path.resolve(__dirname, "..", "..", "builds");
|
|
444
|
+
if (!fs.existsSync(buildsDir)) {
|
|
445
|
+
console.log(chalk_1.default.yellow("No builds directory found. Run 'helix spawn' to generate your first app."));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const projects = fs.readdirSync(buildsDir, { withFileTypes: true })
|
|
449
|
+
.filter((d) => d.isDirectory())
|
|
450
|
+
.map((d) => {
|
|
451
|
+
const configPath = path.join(buildsDir, d.name, "helix.config.json");
|
|
452
|
+
let config = {};
|
|
453
|
+
if (fs.existsSync(configPath)) {
|
|
454
|
+
try {
|
|
455
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
456
|
+
}
|
|
457
|
+
catch { }
|
|
458
|
+
}
|
|
459
|
+
const stat = fs.statSync(path.join(buildsDir, d.name));
|
|
460
|
+
return { name: d.name, prompt: config.prompt || '(unknown)', date: config.generatedAt || stat.mtime.toISOString(), version: config.version || '?' };
|
|
461
|
+
});
|
|
462
|
+
if (projects.length === 0) {
|
|
463
|
+
console.log(chalk_1.default.yellow("No projects found. Run 'helix spawn' to generate your first app."));
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
console.log(chalk_1.default.cyan(`\n📦 Generated Projects (${projects.length}):\n`));
|
|
467
|
+
projects.forEach((p, i) => {
|
|
468
|
+
console.log(` ${chalk_1.default.white(`${i + 1}.`)} ${chalk_1.default.green(p.name)}`);
|
|
469
|
+
console.log(` ${chalk_1.default.gray('Prompt:')} ${p.prompt.substring(0, 60)}`);
|
|
470
|
+
console.log(` ${chalk_1.default.gray('Date:')} ${new Date(p.date).toLocaleDateString()}`);
|
|
471
|
+
console.log(` ${chalk_1.default.gray('Run:')} cd builds/${p.name} && npm run dev\n`);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
// ============================================================================
|
|
475
|
+
// COST COMMAND: Show session cost summary
|
|
476
|
+
// ============================================================================
|
|
477
|
+
program
|
|
478
|
+
.command("cost")
|
|
479
|
+
.description("Show AI model cost summary for the current session")
|
|
480
|
+
.action(async () => {
|
|
481
|
+
console.log(banner);
|
|
482
|
+
const { getCostSummary } = await Promise.resolve().then(() => __importStar(require("../openrouter")));
|
|
483
|
+
const summary = getCostSummary();
|
|
484
|
+
if (summary.callCount === 0) {
|
|
485
|
+
console.log(chalk_1.default.yellow("No API calls made in this session."));
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
console.log(chalk_1.default.cyan(`\n💰 Session Cost Summary:\n`));
|
|
489
|
+
console.log(` Total Calls: ${chalk_1.default.white(String(summary.callCount))}`);
|
|
490
|
+
console.log(` Total Cost: ${chalk_1.default.green('$' + summary.totalCost.toFixed(6))}`);
|
|
491
|
+
console.log(`\n ${chalk_1.default.gray('Breakdown:')}`);
|
|
492
|
+
summary.entries.forEach(e => {
|
|
493
|
+
console.log(` ${chalk_1.default.gray(e.timestamp.toLocaleTimeString())} ${e.model} — $${e.cost.toFixed(6)} (${e.promptTokens}+${e.completionTokens} tokens)`);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
// ============================================================================
|
|
497
|
+
// DOCTOR COMMAND: System Health Check
|
|
498
|
+
// ============================================================================
|
|
499
|
+
program
|
|
500
|
+
.command("doctor")
|
|
501
|
+
.description("Check system health: API keys, Node version, dependencies")
|
|
502
|
+
.action(async () => {
|
|
503
|
+
console.log(banner);
|
|
504
|
+
console.log(chalk_1.default.cyan("\n🩺 Helix Doctor — System Health Check\n"));
|
|
505
|
+
let issues = 0;
|
|
506
|
+
// Check Node.js version
|
|
507
|
+
const nodeVersion = process.version;
|
|
508
|
+
const major = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
509
|
+
if (major >= 18) {
|
|
510
|
+
console.log(chalk_1.default.green(` ✅ Node.js ${nodeVersion}`));
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
console.log(chalk_1.default.red(` ❌ Node.js ${nodeVersion} — requires >= 18`));
|
|
514
|
+
issues++;
|
|
515
|
+
}
|
|
516
|
+
// Check API key
|
|
517
|
+
if (process.env.OPENROUTER_API_KEY) {
|
|
518
|
+
const key = process.env.OPENROUTER_API_KEY;
|
|
519
|
+
console.log(chalk_1.default.green(` ✅ OPENROUTER_API_KEY configured (${key.substring(0, 12)}...)`));
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
console.log(chalk_1.default.red(" ❌ OPENROUTER_API_KEY not set"));
|
|
523
|
+
issues++;
|
|
524
|
+
}
|
|
525
|
+
// Check .env file
|
|
526
|
+
const envPath = path.resolve(__dirname, "..", "..", ".env");
|
|
527
|
+
if (fs.existsSync(envPath)) {
|
|
528
|
+
console.log(chalk_1.default.green(` ✅ .env file found`));
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
console.log(chalk_1.default.yellow(" ⚠️ No .env file found (using environment variables)"));
|
|
532
|
+
}
|
|
533
|
+
// Check builds directory
|
|
534
|
+
const buildsDir = path.resolve(__dirname, "..", "..", "builds");
|
|
535
|
+
if (fs.existsSync(buildsDir)) {
|
|
536
|
+
const projects = fs.readdirSync(buildsDir, { withFileTypes: true })
|
|
537
|
+
.filter((d) => d.isDirectory());
|
|
538
|
+
console.log(chalk_1.default.green(` ✅ Builds directory: ${projects.length} project(s)`));
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
console.log(chalk_1.default.gray(" ℹ️ No builds directory yet (run 'helix spawn' to create one)"));
|
|
542
|
+
}
|
|
543
|
+
// Check npm
|
|
544
|
+
try {
|
|
545
|
+
const { stdout } = await Promise.resolve().then(() => __importStar(require("execa"))).then(m => m.default("npm", ["--version"]));
|
|
546
|
+
console.log(chalk_1.default.green(` ✅ npm v${stdout.trim()}`));
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
console.log(chalk_1.default.red(" ❌ npm not found"));
|
|
550
|
+
issues++;
|
|
551
|
+
}
|
|
552
|
+
// Check npx (for prisma, create-next-app)
|
|
553
|
+
try {
|
|
554
|
+
const { stdout } = await Promise.resolve().then(() => __importStar(require("execa"))).then(m => m.default("npx", ["--version"]));
|
|
555
|
+
console.log(chalk_1.default.green(` ✅ npx v${stdout.trim()}`));
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
console.log(chalk_1.default.red(" ❌ npx not found"));
|
|
559
|
+
issues++;
|
|
560
|
+
}
|
|
561
|
+
// Check disk space (basic)
|
|
562
|
+
try {
|
|
563
|
+
const { stdout } = await Promise.resolve().then(() => __importStar(require("execa"))).then(m => m.default("df", ["-h", "."]));
|
|
564
|
+
const lines = stdout.split('\n');
|
|
565
|
+
if (lines.length > 1) {
|
|
566
|
+
const parts = lines[1].split(/\s+/);
|
|
567
|
+
const available = parts[3];
|
|
568
|
+
console.log(chalk_1.default.green(` ✅ Disk space available: ${available}`));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
console.log(chalk_1.default.gray(" ℹ️ Could not check disk space"));
|
|
573
|
+
}
|
|
574
|
+
// Summary
|
|
575
|
+
console.log('');
|
|
576
|
+
if (issues === 0) {
|
|
577
|
+
console.log(chalk_1.default.green(" 🎉 All checks passed! Helix is ready to go."));
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
console.log(chalk_1.default.red(` ⚠️ ${issues} issue(s) found. Fix them above and run 'helix doctor' again.`));
|
|
581
|
+
}
|
|
582
|
+
console.log('');
|
|
583
|
+
});
|
|
584
|
+
// ============================================================================
|
|
585
|
+
// DRIFT COMMAND: Detect manual changes vs original blueprint
|
|
586
|
+
// ============================================================================
|
|
587
|
+
program
|
|
588
|
+
.command("drift [project]")
|
|
589
|
+
.description("Detect manual changes made after generation")
|
|
590
|
+
.option("-p, --path <path>", "Path to project directory")
|
|
591
|
+
.action(async (project, options) => {
|
|
592
|
+
console.log(banner);
|
|
593
|
+
const buildsDir = path.resolve(__dirname, "..", "..", "builds");
|
|
594
|
+
let projectPath;
|
|
595
|
+
if (options.path) {
|
|
596
|
+
projectPath = path.resolve(options.path);
|
|
597
|
+
}
|
|
598
|
+
else if (project) {
|
|
599
|
+
projectPath = path.join(buildsDir, project);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
projectPath = process.cwd();
|
|
603
|
+
}
|
|
604
|
+
if (!fs.existsSync(projectPath)) {
|
|
605
|
+
console.error(chalk_1.default.red(`Project not found: ${projectPath}`));
|
|
606
|
+
process.exit(1);
|
|
607
|
+
}
|
|
608
|
+
const configPath = path.join(projectPath, "helix.config.json");
|
|
609
|
+
if (!fs.existsSync(configPath)) {
|
|
610
|
+
console.error(chalk_1.default.red("No helix.config.json found. Is this a Helix-generated project?"));
|
|
611
|
+
process.exit(1);
|
|
612
|
+
}
|
|
613
|
+
console.log(chalk_1.default.cyan(`\n🔍 Drift Detection: ${path.basename(projectPath)}\n`));
|
|
614
|
+
// Load the original generation manifest
|
|
615
|
+
let config = {};
|
|
616
|
+
try {
|
|
617
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
618
|
+
}
|
|
619
|
+
catch { }
|
|
620
|
+
// Track files by category
|
|
621
|
+
const drifted = [];
|
|
622
|
+
const generated = [];
|
|
623
|
+
// Scan key generated directories
|
|
624
|
+
const scanDirs = ['app', 'prisma', 'components'];
|
|
625
|
+
for (const dir of scanDirs) {
|
|
626
|
+
const dirPath = path.join(projectPath, dir);
|
|
627
|
+
if (!fs.existsSync(dirPath))
|
|
628
|
+
continue;
|
|
629
|
+
const walkDir = (dirPath) => {
|
|
630
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
631
|
+
const files = [];
|
|
632
|
+
for (const entry of entries) {
|
|
633
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
634
|
+
if (entry.isDirectory()) {
|
|
635
|
+
if (entry.name !== 'node_modules' && entry.name !== '.next') {
|
|
636
|
+
files.push(...walkDir(fullPath));
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
files.push(fullPath);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return files;
|
|
644
|
+
};
|
|
645
|
+
const files = walkDir(dirPath);
|
|
646
|
+
for (const file of files) {
|
|
647
|
+
const relPath = path.relative(projectPath, file);
|
|
648
|
+
generated.push(relPath);
|
|
649
|
+
// Check git status for this file
|
|
650
|
+
try {
|
|
651
|
+
const { execSync } = require('child_process');
|
|
652
|
+
const gitStatus = execSync(`git -C "${projectPath}" diff --name-only HEAD -- "${relPath}" 2>/dev/null`, { encoding: 'utf-8' }).trim();
|
|
653
|
+
if (gitStatus) {
|
|
654
|
+
// Get the diff stats
|
|
655
|
+
const diffStat = execSync(`git -C "${projectPath}" diff --stat HEAD -- "${relPath}" 2>/dev/null`, { encoding: 'utf-8' }).trim();
|
|
656
|
+
drifted.push({
|
|
657
|
+
file: relPath,
|
|
658
|
+
type: 'modified',
|
|
659
|
+
detail: diffStat.split('\n').pop()?.trim() || 'modified'
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
// Not a git repo or file not tracked — check modification time vs config
|
|
665
|
+
const fileStat = fs.statSync(file);
|
|
666
|
+
const genTime = config.generatedAt ? new Date(config.generatedAt) : new Date(0);
|
|
667
|
+
if (fileStat.mtime > genTime) {
|
|
668
|
+
drifted.push({
|
|
669
|
+
file: relPath,
|
|
670
|
+
type: 'modified-after-gen',
|
|
671
|
+
detail: `modified ${fileStat.mtime.toLocaleDateString()}`
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// Report
|
|
678
|
+
if (drifted.length === 0) {
|
|
679
|
+
console.log(chalk_1.default.green(" ✅ No drift detected — project matches original generation."));
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
console.log(chalk_1.default.yellow(` ⚠️ ${drifted.length} file(s) have drifted from original generation:\n`));
|
|
683
|
+
drifted.forEach((d, i) => {
|
|
684
|
+
const icon = d.type === 'modified' ? '📝' : '🔧';
|
|
685
|
+
console.log(` ${icon} ${chalk_1.default.white(d.file)}`);
|
|
686
|
+
console.log(` ${chalk_1.default.gray(d.detail)}`);
|
|
687
|
+
});
|
|
688
|
+
console.log(chalk_1.default.gray(`\n Total generated files: ${generated.length}`));
|
|
689
|
+
console.log(chalk_1.default.gray(` Drifted files: ${drifted.length}`));
|
|
690
|
+
console.log(chalk_1.default.yellow(`\n 💡 Use 'helix spawn' with --preserve-drift to keep these changes during regeneration.`));
|
|
691
|
+
}
|
|
692
|
+
console.log('');
|
|
693
|
+
});
|
|
694
|
+
// ============================================================================
|
|
695
|
+
// SNAPSHOT COMMAND: Generate Docker configuration
|
|
696
|
+
// ============================================================================
|
|
697
|
+
program
|
|
698
|
+
.command("snapshot [project]")
|
|
699
|
+
.description("Bundle project with optimized Dockerfile for portable deployment")
|
|
700
|
+
.option("-p, --path <path>", "Path to project directory")
|
|
701
|
+
.option("--docker", "Generate Docker configuration (default)")
|
|
702
|
+
.option("--compose", "Also generate docker-compose.yml")
|
|
703
|
+
.action(async (project, options) => {
|
|
704
|
+
console.log(banner);
|
|
705
|
+
const buildsDir = path.resolve(__dirname, "..", "..", "builds");
|
|
706
|
+
let projectPath;
|
|
707
|
+
if (options.path) {
|
|
708
|
+
projectPath = path.resolve(options.path);
|
|
709
|
+
}
|
|
710
|
+
else if (project) {
|
|
711
|
+
projectPath = path.join(buildsDir, project);
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
projectPath = process.cwd();
|
|
715
|
+
}
|
|
716
|
+
if (!fs.existsSync(projectPath)) {
|
|
717
|
+
console.error(chalk_1.default.red(`Project not found: ${projectPath}`));
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
const projectName = path.basename(projectPath);
|
|
721
|
+
console.log(chalk_1.default.cyan(`\n📦 Snapshot: ${projectName}\n`));
|
|
722
|
+
// Detect project type
|
|
723
|
+
const hasPrisma = fs.existsSync(path.join(projectPath, "prisma", "schema.prisma"));
|
|
724
|
+
const hasNextConfig = fs.existsSync(path.join(projectPath, "next.config.js")) || fs.existsSync(path.join(projectPath, "next.config.mjs")) || fs.existsSync(path.join(projectPath, "next.config.ts"));
|
|
725
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
726
|
+
let pkg = {};
|
|
727
|
+
if (fs.existsSync(pkgPath)) {
|
|
728
|
+
try {
|
|
729
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
730
|
+
}
|
|
731
|
+
catch { }
|
|
732
|
+
}
|
|
733
|
+
// Generate optimized multi-stage Dockerfile
|
|
734
|
+
const dockerfile = `# Generated by Helix Snapshot
|
|
735
|
+
# Multi-stage build for optimized production image
|
|
736
|
+
|
|
737
|
+
# Stage 1: Dependencies
|
|
738
|
+
FROM node:20-alpine AS deps
|
|
739
|
+
RUN apk add --no-cache libc6-compat openssl
|
|
740
|
+
WORKDIR /app
|
|
741
|
+
COPY package.json package-lock.json* yarn.lock* pnpm-lock.yaml* ./
|
|
742
|
+
${hasPrisma ? 'COPY prisma ./prisma/' : ''}
|
|
743
|
+
RUN \\
|
|
744
|
+
if [ -f yarn.lock ]; then yarn install --frozen-lockfile; \\
|
|
745
|
+
elif [ -f package-lock.json ]; then npm ci; \\
|
|
746
|
+
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm install --frozen-lockfile; \\
|
|
747
|
+
else npm install; \\
|
|
748
|
+
fi
|
|
749
|
+
${hasPrisma ? 'RUN npx prisma generate' : ''}
|
|
750
|
+
|
|
751
|
+
# Stage 2: Build
|
|
752
|
+
FROM node:20-alpine AS builder
|
|
753
|
+
WORKDIR /app
|
|
754
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
755
|
+
COPY . .
|
|
756
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
757
|
+
ENV NODE_ENV=production
|
|
758
|
+
RUN npm run build
|
|
759
|
+
|
|
760
|
+
# Stage 3: Production
|
|
761
|
+
FROM node:20-alpine AS runner
|
|
762
|
+
WORKDIR /app
|
|
763
|
+
ENV NODE_ENV=production
|
|
764
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
765
|
+
|
|
766
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
767
|
+
RUN adduser --system --uid 1001 nextjs
|
|
768
|
+
|
|
769
|
+
COPY --from=builder /app/public ./public
|
|
770
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
771
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
772
|
+
${hasPrisma ? 'COPY --from=builder /app/prisma ./prisma\nCOPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma' : ''}
|
|
773
|
+
|
|
774
|
+
USER nextjs
|
|
775
|
+
EXPOSE 3000
|
|
776
|
+
ENV PORT=3000
|
|
777
|
+
ENV HOSTNAME="0.0.0.0"
|
|
778
|
+
|
|
779
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \\
|
|
780
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1
|
|
781
|
+
|
|
782
|
+
CMD ["node", "server.js"]
|
|
783
|
+
`;
|
|
784
|
+
const dockerignore = `node_modules
|
|
785
|
+
.next
|
|
786
|
+
.git
|
|
787
|
+
.env*
|
|
788
|
+
*.md
|
|
789
|
+
.DS_Store
|
|
790
|
+
builds/
|
|
791
|
+
`;
|
|
792
|
+
// Write Dockerfile
|
|
793
|
+
const dockerfilePath = path.join(projectPath, "Dockerfile");
|
|
794
|
+
fs.writeFileSync(dockerfilePath, dockerfile);
|
|
795
|
+
console.log(chalk_1.default.green(` ✅ Created: Dockerfile`));
|
|
796
|
+
// Write .dockerignore
|
|
797
|
+
const dockerignorePath = path.join(projectPath, ".dockerignore");
|
|
798
|
+
fs.writeFileSync(dockerignorePath, dockerignore);
|
|
799
|
+
console.log(chalk_1.default.green(` ✅ Created: .dockerignore`));
|
|
800
|
+
// Generate docker-compose.yml if requested or by default
|
|
801
|
+
const composeContent = `# Generated by Helix Snapshot
|
|
802
|
+
version: "3.8"
|
|
803
|
+
|
|
804
|
+
services:
|
|
805
|
+
${projectName}:
|
|
806
|
+
build:
|
|
807
|
+
context: .
|
|
808
|
+
dockerfile: Dockerfile
|
|
809
|
+
ports:
|
|
810
|
+
- "3000:3000"
|
|
811
|
+
environment:
|
|
812
|
+
- NODE_ENV=production
|
|
813
|
+
- DATABASE_URL=file:./dev.db
|
|
814
|
+
restart: unless-stopped
|
|
815
|
+
healthcheck:
|
|
816
|
+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"]
|
|
817
|
+
interval: 30s
|
|
818
|
+
timeout: 3s
|
|
819
|
+
retries: 3
|
|
820
|
+
start_period: 10s
|
|
821
|
+
`;
|
|
822
|
+
const composePath = path.join(projectPath, "docker-compose.yml");
|
|
823
|
+
fs.writeFileSync(composePath, composeContent);
|
|
824
|
+
console.log(chalk_1.default.green(` ✅ Created: docker-compose.yml`));
|
|
825
|
+
// Update next.config for standalone output if needed
|
|
826
|
+
if (hasNextConfig) {
|
|
827
|
+
const nextConfigPath = fs.existsSync(path.join(projectPath, "next.config.mjs"))
|
|
828
|
+
? path.join(projectPath, "next.config.mjs")
|
|
829
|
+
: fs.existsSync(path.join(projectPath, "next.config.ts"))
|
|
830
|
+
? path.join(projectPath, "next.config.ts")
|
|
831
|
+
: path.join(projectPath, "next.config.js");
|
|
832
|
+
const nextConfig = fs.readFileSync(nextConfigPath, "utf-8");
|
|
833
|
+
if (!nextConfig.includes("standalone")) {
|
|
834
|
+
console.log(chalk_1.default.yellow(` ⚠️ Add output: "standalone" to ${path.basename(nextConfigPath)} for optimized Docker builds`));
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
console.log(chalk_1.default.cyan(`\n 📋 Quick start:`));
|
|
838
|
+
console.log(chalk_1.default.gray(` cd ${projectPath}`));
|
|
839
|
+
console.log(chalk_1.default.gray(` docker compose up --build`));
|
|
840
|
+
console.log(chalk_1.default.gray(` # App available at http://localhost:3000\n`));
|
|
841
|
+
});
|
|
842
|
+
// ============================================================================
|
|
843
|
+
// PREFLIGHT COMMAND: Validate blueprint before generation
|
|
844
|
+
// ============================================================================
|
|
845
|
+
program
|
|
846
|
+
.command("preflight <file>")
|
|
847
|
+
.description("Validate a .helix blueprint before generation")
|
|
848
|
+
.action(async (file) => {
|
|
849
|
+
console.log(banner);
|
|
850
|
+
const filePath = path.isAbsolute(file) ? file : path.resolve(process.cwd(), file);
|
|
851
|
+
if (!fs.existsSync(filePath)) {
|
|
852
|
+
console.error(chalk_1.default.red(`File not found: ${file}`));
|
|
853
|
+
process.exit(1);
|
|
854
|
+
}
|
|
855
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
856
|
+
try {
|
|
857
|
+
const { preflight, formatPreflightResult } = await Promise.resolve().then(() => __importStar(require("../commands/preflight")));
|
|
858
|
+
const result = preflight(content);
|
|
859
|
+
console.log('\n' + formatPreflightResult(result));
|
|
860
|
+
process.exit(result.passed ? 0 : 1);
|
|
861
|
+
}
|
|
862
|
+
catch (e) {
|
|
863
|
+
console.error(chalk_1.default.red(`Preflight error: ${e.message}`));
|
|
864
|
+
process.exit(1);
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
// ============================================================================
|
|
868
|
+
// INSTALL COMMAND: Component Marketplace
|
|
869
|
+
// ============================================================================
|
|
870
|
+
program
|
|
871
|
+
.command("install [component]")
|
|
872
|
+
.description("Install a component from the Helix library")
|
|
873
|
+
.option("-p, --path <path>", "Target project directory", process.cwd())
|
|
874
|
+
.action(async (component, options) => {
|
|
875
|
+
console.log(banner);
|
|
876
|
+
const { installComponent } = await Promise.resolve().then(() => __importStar(require("../commands/install")));
|
|
877
|
+
await installComponent(component || 'list', options.path);
|
|
878
|
+
});
|
|
879
|
+
// ============================================================================
|
|
880
|
+
// HELP ENHANCEMENTS
|
|
881
|
+
// ============================================================================
|
|
882
|
+
program.addHelpText("after", `
|
|
883
|
+
${chalk_1.default.cyan("Examples:")}
|
|
884
|
+
${chalk_1.default.gray("# ONE-SHOT: Complete app from natural language")}
|
|
885
|
+
$ helix spawn "Expense tracker for my small business"
|
|
886
|
+
$ helix spawn "Task app" --target flutter --db supabase --ai openrouter
|
|
887
|
+
|
|
888
|
+
${chalk_1.default.gray("# Development workflow:")}
|
|
889
|
+
$ helix preview ${chalk_1.default.gray("# Hot-reload with file watching")}
|
|
890
|
+
$ helix deploy --platform vercel ${chalk_1.default.gray("# Ship to production")}
|
|
891
|
+
|
|
892
|
+
${chalk_1.default.gray("# Manual workflow:")}
|
|
893
|
+
$ helix new my-app
|
|
894
|
+
$ helix generate app.helix
|
|
895
|
+
$ helix run
|
|
896
|
+
|
|
897
|
+
${chalk_1.default.gray("# Project management:")}
|
|
898
|
+
$ helix list ${chalk_1.default.gray("# Show all generated projects")}
|
|
899
|
+
$ helix cost ${chalk_1.default.gray("# Show AI cost summary")}
|
|
900
|
+
$ helix doctor ${chalk_1.default.gray("# System health check")}
|
|
901
|
+
$ helix drift my-app ${chalk_1.default.gray("# Detect manual changes")}
|
|
902
|
+
$ helix snapshot my-app ${chalk_1.default.gray("# Generate Docker config")}
|
|
903
|
+
$ helix preflight app.helix ${chalk_1.default.gray("# Validate blueprint")}
|
|
904
|
+
$ helix evolve scan ${chalk_1.default.gray("# Analyze codebase health")}
|
|
905
|
+
$ helix evolve apply ${chalk_1.default.gray("# Auto-fix issues")}
|
|
906
|
+
$ helix spawn "..." --dry-run ${chalk_1.default.gray("# Preview without generating")}
|
|
907
|
+
|
|
908
|
+
${chalk_1.default.gray("# Component library:")}
|
|
909
|
+
$ helix install ${chalk_1.default.gray("# List available components")}
|
|
910
|
+
$ helix install auth-flow ${chalk_1.default.gray("# Install a component package")}
|
|
911
|
+
|
|
912
|
+
${chalk_1.default.gray("# Plugin system:")}
|
|
913
|
+
$ helix plugins ${chalk_1.default.gray("# List available generators")}
|
|
914
|
+
`);
|
|
915
|
+
// Parse command line arguments
|
|
916
|
+
program.parse(process.argv);
|
|
917
|
+
// Show help if no command provided
|
|
918
|
+
if (!process.argv.slice(2).length) {
|
|
919
|
+
program.outputHelp();
|
|
920
|
+
}
|
|
921
|
+
//# sourceMappingURL=helix.js.map
|