Skip to main content
Glama
EnvGenerator.ts11.2 kB
/** * Environment Variable Generator * * Generates dynamic environment variables based on project analysis * * Created: 2025-07-31 */ import * as os from 'os'; import * as path from 'path'; import { Logger } from 'winston'; import { EnvironmentConfig, ProjectInfo, RuntimeConfig } from './types.js'; export interface EnvGenerationOptions { /** Custom environment prefix */ prefix?: string; /** Include development settings */ includeDevelopment?: boolean; /** Include AI configuration */ includeAI?: boolean; /** Environment target (development, production, test) */ target?: 'development' | 'production' | 'test'; } export class EnvGenerator { private logger: Logger; private projectInfo: ProjectInfo; constructor(projectInfo: ProjectInfo, logger: Logger) { this.projectInfo = projectInfo; this.logger = logger; } /** * Generate environment configuration */ generateEnvironmentConfig(options: EnvGenerationOptions = {}): EnvironmentConfig { const prefix = this.generatePrefix(options.prefix); const variables = this.generateEnvironmentVariables(prefix, options); const defaults = this.generateDefaultValues(prefix, options); this.logger.debug(`Generated environment config with prefix: ${prefix}`); return { prefix, variables, defaults }; } /** * Generate environment variable prefix based on project name */ private generatePrefix(customPrefix?: string): string { if (customPrefix) { return this.sanitizePrefix(customPrefix); } // Generate from project name const sanitized = this.sanitizePrefix(this.projectInfo.name); // Ensure it's not too generic if (sanitized.length < 3) { return 'PROJECT'; } // Avoid common conflicts const conflicts = ['NODE', 'NPM', 'PATH', 'HOME', 'USER', 'TEMP', 'TMP']; if (conflicts.includes(sanitized)) { return `${sanitized}_MCP`; } return sanitized; } /** * Sanitize prefix for environment variable usage */ private sanitizePrefix(prefix: string): string { return prefix .toUpperCase() .replace(/[^A-Z0-9]/g, '_') // Replace non-alphanumeric with underscore .replace(/_+/g, '_') // Collapse multiple underscores .replace(/^_|_$/g, ''); // Remove leading/trailing underscores } /** * Generate environment variables map from current environment */ private generateEnvironmentVariables(prefix: string, options: EnvGenerationOptions): Record<string, string | undefined> { const variables: Record<string, string | undefined> = {}; // Get all environment variables with our prefix for (const [key, value] of Object.entries(process.env)) { if (key.startsWith(`${prefix}_`)) { variables[key] = value; } } return variables; } /** * Generate default environment variables */ private generateDefaultValues(prefix: string, options: EnvGenerationOptions): Record<string, string> { const defaults: Record<string, string> = {}; // Core configuration defaults[`${prefix}_PROJECT_ROOT`] = this.projectInfo.root; defaults[`${prefix}_PROJECT_NAME`] = this.projectInfo.name.toLowerCase(); // Database path defaults[`${prefix}_DATABASE_PATH`] = path.join(this.projectInfo.root, '.castplan', 'data.db'); // Service toggles defaults[`${prefix}_ENABLE_BMAD`] = 'true'; defaults[`${prefix}_ENABLE_DOCS`] = 'true'; defaults[`${prefix}_ENABLE_HOOKS`] = 'true'; defaults[`${prefix}_ENABLE_ENHANCED`] = 'true'; // AI configuration (disabled by default for wider compatibility) if (options.includeAI) { defaults[`${prefix}_ENABLE_AI`] = 'false'; defaults[`${prefix}_AI_PROVIDER`] = 'openai'; defaults[`${prefix}_AI_MODEL`] = 'gpt-4'; // Note: API key should be set manually by user } else { defaults[`${prefix}_ENABLE_AI`] = 'false'; } // Localization (use system defaults) defaults[`${prefix}_TIMEZONE`] = this.detectTimezone(); defaults[`${prefix}_LOCALE`] = this.detectLocale(); // Logging defaults[`${prefix}_LOG_LEVEL`] = options.target === 'development' ? 'debug' : 'info'; if (options.includeDevelopment) { defaults[`${prefix}_LOG_FILE`] = path.join(this.projectInfo.root, '.castplan', 'logs', 'server.log'); } // Performance settings defaults[`${prefix}_ENABLE_CACHE`] = 'true'; defaults[`${prefix}_MAX_CONCURRENT`] = '5'; // Watch mode (enabled for development) if (options.target === 'development' || options.includeDevelopment) { defaults[`${prefix}_WATCH_MODE`] = 'true'; defaults[`${prefix}_WATCH_PATTERNS`] = this.generateWatchPatterns().join(','); defaults[`${prefix}_WATCH_IGNORED`] = this.generateIgnorePatterns().join(','); } else { defaults[`${prefix}_WATCH_MODE`] = 'false'; } return defaults; } /** * Detect system timezone */ private detectTimezone(): string { try { return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'; } catch { return 'UTC'; } } /** * Detect system locale */ private detectLocale(): string { try { return Intl.DateTimeFormat().resolvedOptions().locale || 'en-US'; } catch { return 'en-US'; } } /** * Generate watch patterns based on project type */ private generateWatchPatterns(): string[] { const patterns = ['**/*.md', '**/README.md']; // Add type-specific patterns switch (this.projectInfo.type) { case 'node': patterns.push('**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx', '**/*.json'); break; case 'python': patterns.push('**/*.py', '**/*.pyi', '**/*.toml', '**/*.txt'); break; case 'mixed': patterns.push('**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx', '**/*.json'); patterns.push('**/*.py', '**/*.pyi', '**/*.toml', '**/*.txt'); break; } // Add framework-specific patterns if (this.projectInfo.framework) { switch (this.projectInfo.framework) { case 'react': case 'vue': patterns.push('**/components/**/*', '**/pages/**/*', '**/styles/**/*'); break; case 'express': patterns.push('**/routes/**/*', '**/middleware/**/*', '**/controllers/**/*'); break; } } return [...new Set(patterns)]; } /** * Generate ignore patterns */ private generateIgnorePatterns(): string[] { return [ '**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/coverage/**', '**/__pycache__/**', '**/*.pyc', '**/venv/**', '**/.venv/**', '**/env/**', '**/.env/**' ]; } /** * Generate .env file content */ generateEnvFile(options: EnvGenerationOptions = {}): string { const config = this.generateEnvironmentConfig(options); const lines: string[] = []; // Add header comment lines.push(`# ${this.projectInfo.name} Environment Configuration`); lines.push(`# Generated on ${new Date().toISOString()}`); lines.push(''); // Add project information lines.push('# Project Information'); lines.push(`${config.prefix}_PROJECT_ROOT=${config.defaults[`${config.prefix}_PROJECT_ROOT`]}`); lines.push(`${config.prefix}_PROJECT_NAME=${config.defaults[`${config.prefix}_PROJECT_NAME`]}`); lines.push(''); // Add service configuration lines.push('# Service Configuration'); lines.push(`${config.prefix}_ENABLE_BMAD=${config.defaults[`${config.prefix}_ENABLE_BMAD`]}`); lines.push(`${config.prefix}_ENABLE_DOCS=${config.defaults[`${config.prefix}_ENABLE_DOCS`]}`); lines.push(`${config.prefix}_ENABLE_HOOKS=${config.defaults[`${config.prefix}_ENABLE_HOOKS`]}`); lines.push(`${config.prefix}_ENABLE_ENHANCED=${config.defaults[`${config.prefix}_ENABLE_ENHANCED`]}`); lines.push(''); // Add AI configuration lines.push('# AI Configuration (Optional)'); lines.push(`${config.prefix}_ENABLE_AI=${config.defaults[`${config.prefix}_ENABLE_AI`]}`); if (options.includeAI) { lines.push(`${config.prefix}_AI_PROVIDER=${config.defaults[`${config.prefix}_AI_PROVIDER`]}`); lines.push(`${config.prefix}_AI_MODEL=${config.defaults[`${config.prefix}_AI_MODEL`]}`); lines.push(`# ${config.prefix}_AI_API_KEY=your_api_key_here`); } lines.push(''); // Add localization lines.push('# Localization'); lines.push(`${config.prefix}_TIMEZONE=${config.defaults[`${config.prefix}_TIMEZONE`]}`); lines.push(`${config.prefix}_LOCALE=${config.defaults[`${config.prefix}_LOCALE`]}`); lines.push(''); // Add logging lines.push('# Logging'); lines.push(`${config.prefix}_LOG_LEVEL=${config.defaults[`${config.prefix}_LOG_LEVEL`]}`); if (config.defaults[`${config.prefix}_LOG_FILE`]) { lines.push(`${config.prefix}_LOG_FILE=${config.defaults[`${config.prefix}_LOG_FILE`]}`); } lines.push(''); // Add performance settings lines.push('# Performance'); lines.push(`${config.prefix}_ENABLE_CACHE=${config.defaults[`${config.prefix}_ENABLE_CACHE`]}`); lines.push(`${config.prefix}_MAX_CONCURRENT=${config.defaults[`${config.prefix}_MAX_CONCURRENT`]}`); lines.push(''); // Add watch mode settings if (config.defaults[`${config.prefix}_WATCH_MODE`]) { lines.push('# Watch Mode'); lines.push(`${config.prefix}_WATCH_MODE=${config.defaults[`${config.prefix}_WATCH_MODE`]}`); if (config.defaults[`${config.prefix}_WATCH_PATTERNS`]) { lines.push(`${config.prefix}_WATCH_PATTERNS=${config.defaults[`${config.prefix}_WATCH_PATTERNS`]}`); } if (config.defaults[`${config.prefix}_WATCH_IGNORED`]) { lines.push(`${config.prefix}_WATCH_IGNORED=${config.defaults[`${config.prefix}_WATCH_IGNORED`]}`); } lines.push(''); } // Add database path lines.push('# Database'); lines.push(`${config.prefix}_DATABASE_PATH=${config.defaults[`${config.prefix}_DATABASE_PATH`]}`); return lines.join('\n'); } /** * Generate shell export script */ generateShellExports(options: EnvGenerationOptions = {}): string { const config = this.generateEnvironmentConfig(options); const lines: string[] = []; lines.push(`#!/bin/bash`); lines.push(`# ${this.projectInfo.name} Environment Variables`); lines.push(`# Generated on ${new Date().toISOString()}`); lines.push(''); for (const [key, value] of Object.entries(config.defaults)) { lines.push(`export ${key}="${value}"`); } return lines.join('\n'); } /** * Generate PowerShell script */ generatePowerShellScript(options: EnvGenerationOptions = {}): string { const config = this.generateEnvironmentConfig(options); const lines: string[] = []; lines.push(`# ${this.projectInfo.name} Environment Variables`); lines.push(`# Generated on ${new Date().toISOString()}`); lines.push(''); for (const [key, value] of Object.entries(config.defaults)) { lines.push(`$env:${key} = "${value}"`); } return lines.join('\n'); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ghostseller/CastPlan_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server