@agenticsql/cli
Version:
Generate a production-ready SQL schema from your codebase using the power of Agentic AI.
773 lines (651 loc) ⢠28.3 kB
JavaScript
import fs from 'fs/promises';
import path from 'path';
import { glob } from 'glob';
import axios from 'axios';
import ora from 'ora';
import prompts from 'prompts';
import open from 'open';
import chalk from 'chalk';
import { execSync } from 'child_process';
const API_URL = 'https://3mlh63k42evnbx4yucvas2y3pu0frbqx.lambda-url.us-east-1.on.aws/';
const DASHBOARD_URL = 'https://agenticsql.ai/dashboard';
const GITHUB_DASHBOARD_URL = 'https://agenticsql.ai/dashboard/github';
const CLI_DEPLOY_URL = process.env.CLI_DEPLOY_URL || 'https://agenticsql.ai/api/cli/deploy';
const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.agenticsql');
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
// Smart detection - only scan what the project actually uses
async function detectProjectType() {
const detections = {
isNextJS: false,
isReactNative: false,
isPython: false,
isFlutter: false,
isGo: false,
isRuby: false,
hasPrisma: false,
hasDrizzle: false,
hasSupabase: false,
hasFirebase: false,
// Specific feature detection
hasNextAuth: false,
hasBlog: false,
hasWebhooks: false,
hasStripeCheckout: false
};
try {
// Check for Next.js
const nextConfig = await glob('next.config.{js,mjs,ts}', { nodir: true });
detections.isNextJS = nextConfig.length > 0;
// Check for React Native
const rnConfig = await glob('@(metro.config.js|app.json)', { nodir: true });
detections.isReactNative = rnConfig.length > 0;
// Check for Python
const pythonFiles = await glob('@(requirements.txt|setup.py|pyproject.toml)', { nodir: true });
detections.isPython = pythonFiles.length > 0;
// Check for Flutter
const flutterConfig = await glob('pubspec.yaml', { nodir: true });
detections.isFlutter = flutterConfig.length > 0;
// Check for Go
const goFiles = await glob('go.mod', { nodir: true });
detections.isGo = goFiles.length > 0;
// Check for Ruby/Rails
const rubyFiles = await glob('@(Gemfile|config/application.rb)', { nodir: true });
detections.isRuby = rubyFiles.length > 0;
// Check for ORMs
const prismaSchema = await glob('prisma/schema.prisma', { nodir: true });
detections.hasPrisma = prismaSchema.length > 0;
const drizzleConfig = await glob('drizzle.config.{ts,js}', { nodir: true });
detections.hasDrizzle = drizzleConfig.length > 0;
// Check for Backend services
const supabaseFiles = await glob('supabase/**', { nodir: true });
detections.hasSupabase = supabaseFiles.length > 0;
const firebaseFiles = await glob('firebase.json', { nodir: true });
detections.hasFirebase = firebaseFiles.length > 0;
// CRITICAL: Specific feature detection for mandatory tables
// NextAuth detection (sessions table required!)
const nextAuthFiles = await glob('**/api/auth/**/*.{ts,js}', { nodir: true, ignore: IGNORE_PATTERNS });
const hasNextAuthRoute = nextAuthFiles.some(f => f.includes('nextauth') || f.includes('[...nextauth]'));
const authConfigFiles = await glob('@(auth.ts|auth.config.ts|auth.config.js)', { nodir: true });
detections.hasNextAuth = hasNextAuthRoute || authConfigFiles.length > 0;
// Blog detection (blog_posts table required!)
const blogRoutes = await glob('**/*blog*/**/*.{ts,tsx,js,jsx}', { nodir: true, ignore: IGNORE_PATTERNS });
const blogApiRoutes = await glob('**/api/*blog*/**/*.{ts,js}', { nodir: true, ignore: IGNORE_PATTERNS });
detections.hasBlog = blogRoutes.length > 0 || blogApiRoutes.length > 0;
// Webhook detection (webhooks table required!)
const webhookRoutes = await glob('**/api/webhook*/**/*.{ts,js}', { nodir: true, ignore: IGNORE_PATTERNS });
detections.hasWebhooks = webhookRoutes.length > 0;
// Stripe Checkout detection (payment_methods table required!)
const stripeCheckout = await glob('**/api/stripe/checkout/**/*.{ts,js}', { nodir: true, ignore: IGNORE_PATTERNS });
detections.hasStripeCheckout = stripeCheckout.length > 0;
} catch (e) { /* ignore */ }
return detections;
}
function buildSmartPatterns(detections) {
const patterns = [];
// LAYER 1: Schema/Model Files (STRONGEST SIGNAL)
// ORM schemas - highest priority
if (detections.hasPrisma) {
patterns.push('prisma/schema.prisma');
}
if (detections.hasDrizzle) {
patterns.push('drizzle.config.{ts,js}', 'drizzle/**/*.{ts,js}');
}
// Explicit model/schema files
patterns.push(
'**/models.{ts,js,py,go,rb}',
'**/models/**/*.{ts,js,py,go,rb}', // Files inside models folder
'**/schema.{ts,js,go}',
'**/types.{ts,js,go}',
'**/types/**/*.{ts,js,go}', // Files inside types folder
'**/*.entity.{ts,js,go}',
'**/*.model.{ts,js,go,rb}',
'**/schemas.py',
'**/database.{py,go}',
'**/db.{ts,js,py,go}'
);
// LAYER 2: API Routes & Server Logic (STRONG SIGNAL)
if (detections.isNextJS) {
patterns.push(
'app/api/**/*.{ts,js}',
'pages/api/**/*.{ts,js}',
'src/app/api/**/*.{ts,js}',
'src/pages/api/**/*.{ts,js}'
);
}
if (detections.isPython) {
patterns.push(
'app/**/*.py',
'api/**/*.py',
'routes/**/*.py',
'views/**/*.py'
);
}
if (detections.isGo) {
patterns.push(
'models/**/*.go',
'api/**/*.go',
'handlers/**/*.go',
'controllers/**/*.go',
'internal/**/*.go'
);
}
if (detections.isRuby) {
patterns.push(
'app/models/**/*.rb',
'app/controllers/**/*.rb',
'db/schema.rb'
);
}
if (detections.isFlutter) {
patterns.push('lib/@(models|services|data)/**/*.dart');
}
// Generic API/server patterns
patterns.push(
'api/**/*.{ts,js,go}',
'server/**/*.{ts,js,go}',
'routes/**/*.{ts,js,go}',
'**/*.controller.{ts,js,go,rb}',
'**/*.service.{ts,js,go,rb}',
'**/*.repository.{ts,js,go,rb}'
);
// LAYER 3: Frontend Components (CONTEXTUAL SIGNAL)
if (detections.isNextJS || detections.isReactNative) {
patterns.push(
'components/**/*@(Form|Card|Profile|List|Table|Detail).{tsx,jsx}',
'src/components/**/*@(Form|Card|Profile|List|Table|Detail).{tsx,jsx}',
'app/**/*@(Form|Card|Profile|List|Table|Detail).{tsx,jsx}'
);
}
if (detections.isFlutter) {
patterns.push('lib/widgets/**/*.dart', 'lib/screens/**/*.dart');
}
// Backend service patterns
if (detections.hasSupabase) {
patterns.push('supabase/**/*.{ts,js,sql}');
}
if (detections.hasFirebase) {
patterns.push('firebase/**/*.{ts,js}');
}
// Fallback: comprehensive scan if nothing specific detected
if (patterns.length === 0) {
patterns.push(
'src/**/@(models|types|schemas|api|components)/**/*.{ts,js,py,go,rb}',
'app/**/@(models|types|schemas|api|components)/**/*.{ts,js,py,go,rb}',
'**/models.{ts,js,py,go,rb}',
'**/schema.{ts,js,py,go,rb}'
);
}
return patterns;
}
const WIDE_SCAN_PATTERN = '**/*.{ts,tsx,js,jsx,py,dart,go,rb,mjs,mts}';
const IGNORE_PATTERNS = [
// Dependencies & Build artifacts
'**/node_modules/**', '**/dist/**', '**/build/**', '**/coverage/**', '**/.git/**',
'**/.next/**', '**/.vercel/**', '**/.turbo/**', '**/out/**', '**/vendor/**',
// Test files
'*.test.*', '*.spec.*', '**/test/**', '**/tests/**', '**/__tests__/**',
// Type definitions (not actual code)
'*.d.ts',
// Hidden/system files (macOS ghost files) - CRITICAL!
'**/.DS_Store', '**/.AppleDouble/**', '**/.LSOverride',
'**/._*', // macOS resource forks
'._*', // Root level ghost files
// Documentation & Scripts (CRITICAL: Exclude these!)
'**/*.md', '**/*.sh',
// Config files
'**/*.config.{js,ts,mjs}', '**/tsconfig.json', '**/package.json',
'**/*.lock', '**/*.log',
// Static assets
'**/public/**', '**/static/**', '**/assets/**', '**/images/**',
// UI-only (no data models) - BUT keep forms!
'**/styles/**', '**/css/**', '**/scss/**'
];
// --- THE MISSING PISTONS - START ---
const INTELLIGENCE_KEYWORDS = {
'AUTH': ['auth', 'password', 'jwt', 'token', 'login', 'user', 'session'],
'BILLING': ['stripe', 'paddle', 'lemonsqueezy', 'charge', 'subscription', 'payment', 'invoice'],
'API_KEY': ['apiKey', 'api_key', 'secret', 'client_id', 'client_secret'],
'GEOLOCATION': ['location', 'address', 'geo', 'latitude', 'longitude', 'coordinates']
};
function extractIntelligence(content) {
const tags = new Set();
const lowerCaseContent = content.toLowerCase();
for (const tag in INTELLIGENCE_KEYWORDS) {
for (const keyword of INTELLIGENCE_KEYWORDS[tag]) {
if (lowerCaseContent.includes(keyword)) {
tags.add(tag);
break;
}
}
}
return Array.from(tags);
}
const MAX_TOTAL_CHARS = 200000; // Increased for comprehensive code analysis
async function getFileContent(filePath) {
try {
const content = await fs.readFile(filePath, 'utf-8');
return content.substring(0, 20000);
} catch { return null; }
}
function extractCodeIntelligence(content, filePath) {
const intelligence = {
entities: [],
apiRoutes: [],
relationships: [],
features: {
hasAuth: false,
hasBilling: false,
hasWebhooks: false,
hasEmail: false,
hasMultiTenant: false
}
};
const lower = content.toLowerCase();
// Feature detection
intelligence.features.hasAuth = /auth|login|register|password|session|jwt|token/.test(lower);
intelligence.features.hasBilling = /stripe|paddle|payment|subscription|invoice|checkout/.test(lower);
intelligence.features.hasWebhooks = /webhook/.test(lower);
intelligence.features.hasEmail = /email.*template|sendgrid|mailgun|resend|nodemailer/.test(lower);
intelligence.features.hasMultiTenant = /organization|tenant|workspace/.test(lower);
// Extract TypeScript interfaces/types (improved regex for multi-line)
const interfaceRegex = /(?:export\s+)?(?:interface|type)\s+(\w+)\s*\{([^}]*)\}/gs;
let match;
while ((match = interfaceRegex.exec(content)) !== null) {
const name = match[1];
const body = match[2];
const fields = extractFields(body);
if (Object.keys(fields).length > 0) {
intelligence.entities.push({ name, fields, source: 'interface' });
}
}
// Extract Python classes
const pythonClassRegex = /class\s+(\w+).*?:\s*([\s\S]*?)(?=\nclass|\n\n|$)/g;
while ((match = pythonClassRegex.exec(content)) !== null) {
const name = match[1];
const body = match[2];
const fields = extractPythonFields(body);
if (Object.keys(fields).length > 0) {
intelligence.entities.push({ name, fields, source: 'class' });
}
}
// Extract API routes (Next.js, Express, FastAPI)
const apiPatterns = [
/(?:app|router)\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/g,
/export\s+async\s+function\s+(GET|POST|PUT|DELETE|PATCH)/g,
/@(?:app|router)\.(get|post|put|delete|patch)\(['"`]([^'"`]+)['"`]/g
];
for (const pattern of apiPatterns) {
while ((match = pattern.exec(content)) !== null) {
intelligence.apiRoutes.push({
method: match[1].toUpperCase(),
path: match[2] || 'unknown'
});
}
}
// Extract relationships (foreign keys)
const fkRegex = /(\w+)_id[:\s]/g;
while ((match = fkRegex.exec(content)) !== null) {
if (!intelligence.relationships.includes(match[1])) {
intelligence.relationships.push(match[1]);
}
}
return intelligence;
}
function extractFields(interfaceBody) {
const fields = {};
// Match: fieldName?: type or fieldName: type
const fieldRegex = /(\w+)\??\s*:\s*(string|number|boolean|Date|any|\w+(?:\[\])?)/g;
let match;
while ((match = fieldRegex.exec(interfaceBody)) !== null) {
fields[match[1]] = match[2];
}
return fields;
}
function extractPythonFields(classBody) {
const fields = {};
// Match: self.field = ... or field: type
const fieldRegex = /(?:self\.(\w+)|(\w+)\s*:\s*(\w+))/g;
let match;
while ((match = fieldRegex.exec(classBody)) !== null) {
const fieldName = match[1] || match[2];
const fieldType = match[3] || 'any';
if (fieldName && !fieldName.startsWith('_')) {
fields[fieldName] = fieldType;
}
}
return fields;
}
async function getProjectTitle(directory) {
try {
const htmlFiles = await glob('**/index.html', { cwd: directory, ignore: IGNORE_PATTERNS });
if (htmlFiles.length > 0) {
const htmlContent = await fs.readFile(path.join(directory, htmlFiles[0]), 'utf-8');
const match = htmlContent.match(/<title>(.*?)<\/title>/i);
if (match && match[1]) {
return match[1].split(/[-|]/)[0].trim().replace(/\s+/g, '_');
}
}
} catch (e) { /* ignore */ }
return path.basename(directory);
}
// --- THE MISSING PISTONS - END ---
// Detect git repository
function detectGitRepo() {
try {
const remoteUrl = execSync('git config --get remote.origin.url', {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore']
}).trim();
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore']
}).trim();
let fullName = null;
if (remoteUrl.includes('github.com')) {
const match = remoteUrl.match(/github\.com[:/](.+?)(?:\.git)?$/);
if (match) {
fullName = match[1];
}
}
if (!fullName) return null;
return { fullName, url: remoteUrl, branch };
} catch (error) {
return null;
}
}
// Config management
async function getApiKey() {
try {
await fs.mkdir(CONFIG_DIR, { recursive: true });
const configData = await fs.readFile(CONFIG_FILE, 'utf-8');
const config = JSON.parse(configData);
return config.apiKey || null;
} catch (error) {
return null;
}
}
async function saveApiKey(apiKey) {
try {
await fs.mkdir(CONFIG_DIR, { recursive: true });
await fs.writeFile(CONFIG_FILE, JSON.stringify({ apiKey }, null, 2));
return true;
} catch (error) {
console.error(chalk.red('Failed to save API key:'), error.message);
return false;
}
}
// Direct deploy with API key - ENTERPRISE FLOW
async function deployWithApiKey() {
console.log(chalk.bold.cyan('\nš Direct Deploy\n'));
let spinner = ora('Detecting git repository...').start();
const repoInfo = detectGitRepo();
if (!repoInfo) {
spinner.fail('Not a git repository');
console.log(chalk.gray('\nš” Initialize git first:'));
console.log(chalk.gray(' git init'));
console.log(chalk.gray(' git remote add origin <url>\n'));
console.log(chalk.yellow('š” Or use: npx @agenticsql/cli (for local scan)\n'));
return;
}
spinner.succeed(`Repository: ${chalk.cyan(repoInfo.fullName)} (${repoInfo.branch})`);
// Check for API key
let apiKey = await getApiKey();
if (!apiKey) {
console.log(chalk.yellow('\nā ļø No API key found'));
console.log(chalk.gray('š Get your API key: https://agenticsql.ai/dashboard (API tab)\n'));
const response = await prompts({
type: 'password',
name: 'key',
message: 'Enter your API key:',
});
if (!response.key) {
console.log(chalk.red('\nā API key required for direct deploy\n'));
return;
}
apiKey = response.key;
await saveApiKey(apiKey);
console.log(chalk.green('ā
API key saved to ~/.agenticsql/config.json\n'));
}
// Note: Schema will be generated during deployment
// The deployment process will save it to the database
spinner = ora('Preparing deployment...').start();
spinner.succeed(chalk.green('ā
Ready to deploy'));
// Deploy
spinner = ora('Deploying database...').start();
try {
const response = await axios.post(
CLI_DEPLOY_URL,
{
repoFullName: repoInfo.fullName,
branch: repoInfo.branch,
},
{
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
}
);
const { deploymentId, repoFullName, branch, schema } = response.data;
spinner.succeed(chalk.green('ā
Deployment started!'));
// Save schema to file if available
if (schema) {
try {
const projectName = repoInfo.fullName.split('/')[1];
const schemaFilename = `${projectName}_schema.sql`;
const currentDirectory = process.cwd();
const schemaPath = path.join(currentDirectory, schemaFilename);
await fs.writeFile(schemaPath, schema);
console.log('');
console.log(chalk.green('ā
Schema saved to:'), chalk.white(schemaFilename));
} catch (err) {
console.log(chalk.yellow('ā ļø Could not save schema file'));
}
}
console.log('');
console.log(chalk.gray('š¦ Repository:'), chalk.white(repoFullName));
console.log(chalk.gray('šæ Branch:'), chalk.white(branch));
console.log(chalk.gray('š Deployment ID:'), chalk.white(deploymentId));
console.log('');
console.log(chalk.gray('š§ Email notification sent'));
console.log(chalk.gray('š Dashboard:'), chalk.cyan('https://agenticsql.ai/dashboard'));
console.log('');
console.log(chalk.green('⨠Your database will be ready in ~30 seconds!\n'));
} catch (error) {
spinner.fail(chalk.red('Deployment failed'));
if (error.response) {
const status = error.response.status;
const data = error.response.data;
if (status === 401) {
console.log(chalk.red('\nā Invalid API key'));
console.log(chalk.gray('š” Get a new key: https://agenticsql.ai/dashboard (API tab)'));
console.log(chalk.gray('š” Or run: npx @agenticsql/cli config --reset\n'));
} else if (status === 400 && data.error?.includes('GitHub not connected')) {
console.log(chalk.red('\nā GitHub not connected'));
console.log(chalk.gray('š” Connect GitHub first: https://agenticsql.ai/dashboard/github'));
console.log(chalk.gray('š” Or run: npx @agenticsql/cli --github\n'));
} else {
console.log(chalk.red(`\nā Error: ${data.error || 'Unknown error'}\n`));
}
} else {
console.log(chalk.red(`\nā Error: ${error.message}\n`));
}
}
}
// GitHub deploy flow - SEPARATE from main CLI
async function deployWithGitHub() {
console.log(chalk.bold.cyan('\nš GitHub Auto-Deploy\n'));
let spinner = ora('Detecting git repository...').start();
const repoInfo = detectGitRepo();
if (!repoInfo) {
spinner.fail('Not a git repository');
console.log(chalk.gray('\nš” Initialize git first:'));
console.log(chalk.gray(' git init'));
console.log(chalk.gray(' git remote add origin <url>\n'));
return;
}
spinner.succeed(`Repository: ${chalk.cyan(repoInfo.fullName)}`);
// Open dashboard for connection management
console.log(chalk.yellow('\nš Opening GitHub dashboard...\n'));
await open(GITHUB_DASHBOARD_URL);
console.log(chalk.gray('Next steps:'));
console.log(chalk.gray('1. Connect your repository (if not connected)'));
console.log(chalk.gray('2. Click "Deploy Now" button'));
console.log(chalk.gray('3. Your database will be deployed automatically!\n'));
console.log(chalk.green('ā
Dashboard opened!\n'));
}
async function main() {
console.log(chalk.bold.yellow('ā” AgenticSQL') + chalk.gray(' - Your Code is the Schema.'));
console.log('');
// Check for flags
const args = process.argv.slice(2);
if (args.includes('--deploy')) {
await deployWithApiKey();
return;
}
if (args.includes('--github')) {
await deployWithGitHub();
return;
}
let spinner = ora('Detecting project type...').start();
try {
// Smart detection
const detections = await detectProjectType();
const smartPatterns = buildSmartPatterns(detections);
// Log detected project type
const projectTypes = [];
if (detections.isNextJS) projectTypes.push('Next.js');
if (detections.isReactNative) projectTypes.push('React Native');
if (detections.isPython) projectTypes.push('Python');
if (detections.isFlutter) projectTypes.push('Flutter');
if (detections.isGo) projectTypes.push('Go');
if (detections.isRuby) projectTypes.push('Ruby');
if (detections.hasPrisma) projectTypes.push('Prisma');
if (detections.hasDrizzle) projectTypes.push('Drizzle');
if (projectTypes.length > 0) {
spinner.succeed(`Detected: ${projectTypes.join(', ')}`);
spinner = ora('Scanning relevant files...').start();
} else {
spinner.text = 'Scanning relevant files...';
}
let allFiles = [];
for (const pattern of smartPatterns) {
allFiles.push(...(await glob(pattern, { ignore: IGNORE_PATTERNS, nodir: true })));
}
let uniqueFiles = [...new Set(allFiles)];
if (uniqueFiles.length === 0) {
spinner.warn('ā ļø No model or schema files found. Please ensure your project has API routes, models, or schema definitions.');
return;
}
spinner.succeed(`Found ${uniqueFiles.length} files`);
spinner = ora('Analyzing your codebase...').start();
let smartSummary = [];
let totalChars = 0;
for (const file of uniqueFiles) {
if (totalChars >= MAX_TOTAL_CHARS) {
break;
}
const content = await getFileContent(file);
if (content) {
const intelligenceTags = extractIntelligence(content);
const codeIntel = extractCodeIntelligence(content, file);
smartSummary.push({
filePath: file,
definitions: content,
tags: intelligenceTags,
intelligence: codeIntel
});
totalChars += content.length;
}
}
if (smartSummary.length === 0) {
spinner.warn('ā ļø No readable content with schema definitions found. Cannot generate a blueprint.');
return;
}
spinner.succeed(`Analyzed ${smartSummary.length} files`);
const currentDirectory = process.cwd();
const projectName = await getProjectTitle(currentDirectory);
// Build structured intelligence summary
const structuredIntelligence = {
entities: [],
apiRoutes: [],
relationships: [],
features: {
hasAuth: false,
hasBilling: false,
hasWebhooks: false,
hasEmail: false,
hasMultiTenant: false
}
};
// Aggregate intelligence from all files
for (const item of smartSummary) {
if (item.intelligence) {
structuredIntelligence.entities.push(...item.intelligence.entities);
structuredIntelligence.apiRoutes.push(...item.intelligence.apiRoutes);
structuredIntelligence.relationships.push(...item.intelligence.relationships);
// Merge features
Object.keys(item.intelligence.features).forEach(key => {
if (item.intelligence.features[key]) {
structuredIntelligence.features[key] = true;
}
});
}
}
// Remove duplicates
structuredIntelligence.entities = Array.from(new Set(structuredIntelligence.entities.map(JSON.stringify))).map(JSON.parse);
structuredIntelligence.apiRoutes = Array.from(new Set(structuredIntelligence.apiRoutes.map(JSON.stringify))).map(JSON.parse);
structuredIntelligence.relationships = [...new Set(structuredIntelligence.relationships)];
console.log(chalk.cyan(`\nš Intelligence Summary:`));
console.log(chalk.gray(` Entities: ${structuredIntelligence.entities.length}`));
console.log(chalk.gray(` API Routes: ${structuredIntelligence.apiRoutes.length}`));
console.log(chalk.gray(` Relationships: ${structuredIntelligence.relationships.length}`));
console.log(chalk.gray(` Features: ${Object.keys(structuredIntelligence.features).filter(k => structuredIntelligence.features[k]).join(', ')}`));
console.log('');
spinner = ora('Generating your production schema...').start();
const response = await axios.post(API_URL, {
summary: smartSummary,
projectName: projectName,
detections: detections,
intelligence: structuredIntelligence
}, { headers: { 'Content-Type': 'application/json' } });
const { schema } = response.data;
const outputFilename = `${projectName}_schema.sql`;
const outputPath = path.join(currentDirectory, outputFilename);
await fs.writeFile(outputPath, schema);
spinner.succeed(chalk.green('ā
Schema Generated!') + chalk.gray(` Saved to `) + chalk.white.bold(outputFilename));
console.log('');
const deployChoice = await prompts({
type: 'confirm',
name: 'value',
message: chalk.bold.red('Deploy your foundation? (Free Tier)'),
initial: true
});
if (deployChoice.value) {
if (process.env.REPL_ID || process.env.REPL_SLUG) {
console.log('');
console.log(chalk.bgMagenta.bold(' YOUR NEXT STEP: DEPLOY YOUR FOUNDATION '));
console.log(chalk.gray(' Replit\'s environment blocks automatic browser opening.'));
console.log(chalk.white(' Please copy this link and paste it into your browser:'));
console.log(chalk.cyan.underline(` ${DASHBOARD_URL}`));
console.log('');
} else {
spinner.start('š Activating Command Center...');
await open(DASHBOARD_URL);
spinner.succeed(chalk.green('Command Center is online.'));
}
} else {
console.log(chalk.yellow('š Blueprint is ready. Deploy your foundation when you are.'));
}
} catch (error) {
if (spinner && spinner.isSpinning) spinner.fail();
console.error(chalk.red('ā An error occurred:'));
if (error.response) {
console.error(chalk.red(` Server Error (${error.response.status}): ${JSON.stringify(error.response.data)}`));
} else {
console.error(chalk.red(` Error: ${error.message}`));
}
}
}
main();