UNPKG

@vibeship/devtools

Version:

Comprehensive markdown-based project management system with AI capabilities for Next.js applications

1,013 lines (1,004 loc) 31.2 kB
#!/usr/bin/env node "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/config/index.ts var config_exports = {}; __export(config_exports, { PartialVibeshipConfigSchema: () => PartialVibeshipConfigSchema, VibeshipConfigSchema: () => VibeshipConfigSchema, applyEnvironmentOverrides: () => applyEnvironmentOverrides, cleanupBackups: () => cleanupBackups, clearConfigCache: () => clearConfigCache, createConfigBackup: () => createConfigBackup, createConfigPatch: () => createConfigPatch, deepMerge: () => deepMerge, defaultConfig: () => defaultConfig, defineConfig: () => defineConfig, developmentConfig: () => developmentConfig, freezeConfig: () => freezeConfig, getAvailableMigrations: () => getAvailableMigrations, getConfig: () => getConfig, getEnvironmentDefaults: () => getEnvironmentDefaults, interactiveMigration: () => interactiveMigration, isPatchSafe: () => isPatchSafe, isValidConfig: () => isValidConfig, loadConfig: () => loadConfig, mergeConfigurations: () => mergeConfigurations, migrateConfig: () => migrateConfig, needsMigration: () => needsMigration, productionConfig: () => productionConfig, rollbackConfig: () => rollbackConfig, testConfig: () => testConfig, validateConfig: () => validateConfig, validatePartialConfig: () => validatePartialConfig, watchConfig: () => watchConfig, z: () => import_zod2.z }); module.exports = __toCommonJS(config_exports); // src/config/defaults.ts var defaultConfig = { // File scanning scanPaths: ["./docs", "./README.md"], include: ["**/*.md", "**/*.mdx"], exclude: ["**/node_modules/**", "**/.next/**", "**/.git/**", "**/dist/**", "**/build/**"], // Features features: { tasks: true, ai: false, realtime: true, commandPalette: true, fileEditing: false, markdownPreview: true }, // Security security: { authentication: false, rateLimit: true, cors: true, blockedPatterns: [ "**/.env*", "**/*.key", "**/*.pem", "**/*.p12", "**/*.pfx", "**/secrets/**", "**/credentials/**" ] }, // Task patterns taskPatterns: { todo: true, fixme: true, hack: true, note: true }, // UI configuration ui: { theme: "system", position: "bottom-right", defaultSize: { width: "400px", height: "600px" }, hotkey: "cmd+shift+k", showInProduction: false }, // API configuration api: { basePath: "/api/vibeship", version: "v1", documentation: true, timeout: 3e4 // 30 seconds }, // Cache configuration cache: { enabled: true, directory: ".vibeship/cache", maxSize: 52428800, // 50MB ttl: 36e5, // 1 hour strategy: "lru" } }; var developmentConfig = { features: { ...defaultConfig.features, fileEditing: true }, security: { ...defaultConfig.security, authentication: false, rateLimitConfig: { windowMs: 6e4, maxRequests: 1e3 // More lenient in development } }, ui: { ...defaultConfig.ui, showInProduction: true // Allow in dev mode }, cache: { ...defaultConfig.cache, ttl: 3e5 // 5 minutes in dev } }; var productionConfig = { features: { ...defaultConfig.features, fileEditing: false // Never allow file editing in production }, security: { ...defaultConfig.security, authentication: true, rateLimit: true, rateLimitConfig: { windowMs: 6e4, maxRequests: 100, tiers: { anonymous: { windowMs: 6e4, maxRequests: 20 }, authenticated: { windowMs: 6e4, maxRequests: 100 }, premium: { windowMs: 6e4, maxRequests: 1e3 } } }, corsConfig: { allowedOrigins: (origin) => { const allowedDomains = process.env.ALLOWED_DOMAINS?.split(",") || []; return allowedDomains.some((domain) => origin.includes(domain)); }, credentials: true, maxAge: 86400 } }, ui: { ...defaultConfig.ui, showInProduction: false }, cache: { ...defaultConfig.cache, maxSize: 104857600, // 100MB in production ttl: 72e5 // 2 hours in production } }; var testConfig = { scanPaths: ["./test-docs"], security: { ...defaultConfig.security, authentication: false, rateLimit: false }, cache: { ...defaultConfig.cache, enabled: false // Disable cache in tests }, ui: { ...defaultConfig.ui, showInProduction: true } }; function getEnvironmentDefaults() { const env = "development"; switch (env) { case "production": return productionConfig; case "test": return testConfig; case "development": default: return developmentConfig; } } // src/config/schema.ts var import_zod = require("zod"); var CustomTaskPatternSchema = import_zod.z.object({ name: import_zod.z.string().min(1, "Pattern name is required"), pattern: import_zod.z.union([import_zod.z.string(), import_zod.z.instanceof(RegExp)]), priority: import_zod.z.enum(["low", "medium", "high"]).optional(), color: import_zod.z.string().optional() }); var TaskPatternsSchema = import_zod.z.object({ todo: import_zod.z.boolean(), fixme: import_zod.z.boolean(), hack: import_zod.z.boolean(), note: import_zod.z.boolean(), warning: import_zod.z.boolean().optional(), optimize: import_zod.z.boolean().optional(), custom: import_zod.z.array(CustomTaskPatternSchema).optional() }); var FeatureFlagsSchema = import_zod.z.object({ tasks: import_zod.z.boolean(), ai: import_zod.z.boolean(), realtime: import_zod.z.boolean(), commandPalette: import_zod.z.boolean(), fileEditing: import_zod.z.boolean(), markdownPreview: import_zod.z.boolean() }); var AuthProviderSchema = import_zod.z.object({ id: import_zod.z.string(), type: import_zod.z.enum(["oauth", "oidc", "saml"]), clientId: import_zod.z.string(), clientSecret: import_zod.z.string().optional(), issuer: import_zod.z.string().optional() }); var AuthConfigSchema = import_zod.z.object({ jwtSecret: import_zod.z.string().optional(), apiKey: import_zod.z.string().optional(), session: import_zod.z.object({ secret: import_zod.z.string(), maxAge: import_zod.z.number().optional() }).optional(), providers: import_zod.z.array(AuthProviderSchema).optional() }); var RateLimitConfigSchema = import_zod.z.object({ windowMs: import_zod.z.number().positive(), maxRequests: import_zod.z.number().positive(), skipSuccessfulRequests: import_zod.z.boolean().optional(), skipFailedRequests: import_zod.z.boolean().optional(), tiers: import_zod.z.object({ anonymous: import_zod.z.object({ windowMs: import_zod.z.number().positive(), maxRequests: import_zod.z.number().positive() }).optional(), authenticated: import_zod.z.object({ windowMs: import_zod.z.number().positive(), maxRequests: import_zod.z.number().positive() }).optional(), premium: import_zod.z.object({ windowMs: import_zod.z.number().positive(), maxRequests: import_zod.z.number().positive() }).optional() }).optional() }); var CORSConfigSchema = import_zod.z.object({ allowedOrigins: import_zod.z.union([ import_zod.z.array(import_zod.z.string()), import_zod.z.string(), import_zod.z.function().args(import_zod.z.string()).returns(import_zod.z.boolean()) ]), allowedMethods: import_zod.z.array(import_zod.z.string()).optional(), allowedHeaders: import_zod.z.array(import_zod.z.string()).optional(), exposedHeaders: import_zod.z.array(import_zod.z.string()).optional(), credentials: import_zod.z.boolean().optional(), maxAge: import_zod.z.number().optional() }); var SecurityConfigSchema = import_zod.z.object({ authentication: import_zod.z.boolean(), auth: AuthConfigSchema.optional(), rateLimit: import_zod.z.boolean(), rateLimitConfig: RateLimitConfigSchema.optional(), cors: import_zod.z.boolean(), corsConfig: CORSConfigSchema.optional(), allowedPaths: import_zod.z.array(import_zod.z.string()).optional(), blockedPatterns: import_zod.z.array(import_zod.z.string()).optional() }); var UIConfigSchema = import_zod.z.object({ theme: import_zod.z.enum(["light", "dark", "system"]).optional(), position: import_zod.z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).optional(), defaultSize: import_zod.z.object({ width: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]).optional(), height: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]).optional() }).optional(), hotkey: import_zod.z.string().optional(), showInProduction: import_zod.z.boolean().optional(), customCSS: import_zod.z.string().optional() }); var APIConfigSchema = import_zod.z.object({ basePath: import_zod.z.string(), version: import_zod.z.string().optional(), documentation: import_zod.z.boolean().optional(), middleware: import_zod.z.array(import_zod.z.string()).optional(), timeout: import_zod.z.number().positive().optional() }); var CacheConfigSchema = import_zod.z.object({ enabled: import_zod.z.boolean(), directory: import_zod.z.string(), maxSize: import_zod.z.number().positive(), ttl: import_zod.z.number().positive(), strategy: import_zod.z.enum(["lru", "fifo", "lfu"]) }); var ExperimentalConfigSchema = import_zod.z.object({ markdownExtensions: import_zod.z.boolean().optional(), customTaskProviders: import_zod.z.boolean().optional(), plugins: import_zod.z.boolean().optional(), pluginConfigs: import_zod.z.record(import_zod.z.any()).optional() }); var VibeshipConfigSchema = import_zod.z.object({ scanPaths: import_zod.z.array(import_zod.z.string()).min(1, "At least one scan path is required"), include: import_zod.z.array(import_zod.z.string()).min(1, "At least one include pattern is required"), exclude: import_zod.z.array(import_zod.z.string()), features: FeatureFlagsSchema, security: SecurityConfigSchema, taskPatterns: TaskPatternsSchema, ui: UIConfigSchema, api: APIConfigSchema, cache: CacheConfigSchema, experimental: ExperimentalConfigSchema.optional() }); var PartialVibeshipConfigSchema = VibeshipConfigSchema.deepPartial(); function validateConfig(config) { try { const result = VibeshipConfigSchema.parse(config); return { success: true, data: result }; } catch (error) { if (error instanceof import_zod.z.ZodError) { const errors = error.errors.map((err) => { const path = err.path.join("."); const message = err.message; return `${path}: ${message}`; }); return { success: false, errors }; } return { success: false, errors: ["Invalid configuration format"] }; } } function validatePartialConfig(config) { try { const result = PartialVibeshipConfigSchema.parse(config); return { success: true, data: result }; } catch (error) { if (error instanceof import_zod.z.ZodError) { const errors = error.errors.map((err) => { const path = err.path.join("."); const message = err.message; return `${path}: ${message}`; }); return { success: false, errors }; } return { success: false, errors: ["Invalid configuration format"] }; } } function isValidConfig(config) { return VibeshipConfigSchema.safeParse(config).success; } // src/config/loader.ts var import_fs = require("fs"); var import_path = require("path"); var import_url = require("url"); // src/config/merger.ts function deepMerge(target, ...sources) { if (!sources.length) return target; const source = sources.shift(); if (!source) return target; const output = { ...target }; Object.keys(source).forEach((key) => { const targetValue = output[key]; const sourceValue = source[key]; if (sourceValue === void 0) { return; } if (sourceValue === null) { output[key] = null; return; } if (isObject(targetValue) && isObject(sourceValue)) { output[key] = deepMerge( targetValue, sourceValue ); } else if (Array.isArray(sourceValue)) { output[key] = [...sourceValue]; } else { output[key] = sourceValue; } }); return deepMerge(output, ...sources); } function isObject(value) { return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp); } function mergeConfigurations(defaultConfig2, environmentDefaults, environmentOverrides, userConfig) { return deepMerge( defaultConfig2, environmentDefaults, environmentOverrides, userConfig ); } function applyEnvironmentOverrides(config) { const overrides = {}; if (process.env.VIBECODE_AUTH_SECRET) { overrides.security = { ...overrides.security, auth: { ...overrides.security?.auth, jwtSecret: process.env.VIBECODE_AUTH_SECRET } }; } if (process.env.VIBECODE_API_KEY) { overrides.security = { ...overrides.security, auth: { ...overrides.security?.auth, apiKey: process.env.VIBECODE_API_KEY } }; } if (process.env.VIBECODE_ALLOWED_ORIGINS) { const origins = process.env.VIBECODE_ALLOWED_ORIGINS.split(",").map((s) => s.trim()); overrides.security = { ...overrides.security, corsConfig: { ...overrides.security?.corsConfig, allowedOrigins: origins } }; } if (process.env.VIBECODE_RATE_LIMIT) { const [windowMs, maxRequests] = process.env.VIBECODE_RATE_LIMIT.split(",").map(Number); if (windowMs && maxRequests) { overrides.security = { ...overrides.security, rateLimitConfig: { ...overrides.security?.rateLimitConfig, windowMs, maxRequests } }; } } if (process.env.VIBECODE_CACHE_ENABLED !== void 0) { overrides.cache = { ...overrides.cache, enabled: process.env.VIBECODE_CACHE_ENABLED === "true" }; } if (process.env.VIBECODE_AI_PROVIDER) { overrides.features = { ...overrides.features, ai: true }; } const featureFlags = [ "VIBECODE_FEATURE_TASKS", "VIBECODE_FEATURE_AI", "VIBECODE_FEATURE_REALTIME", "VIBECODE_FEATURE_COMMAND_PALETTE", "VIBECODE_FEATURE_FILE_EDITING", "VIBECODE_FEATURE_MARKDOWN_PREVIEW" ]; featureFlags.forEach((flag) => { if (process.env[flag] !== void 0) { const featureName = flag.replace("VIBECODE_FEATURE_", "").toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); overrides.features = { ...overrides.features, [featureName]: process.env[flag] === "true" }; } }); return deepMerge(config, overrides); } function freezeConfig(config) { return deepFreeze(config); } function deepFreeze(obj) { Object.freeze(obj); Object.getOwnPropertyNames(obj).forEach((prop) => { const value = obj[prop]; if (value && typeof value === "object" && !Object.isFrozen(value)) { deepFreeze(value); } }); return obj; } function createConfigPatch(baseConfig, newConfig) { const patch = {}; Object.keys(newConfig).forEach((key) => { const baseValue = baseConfig[key]; const newValue = newConfig[key]; if (JSON.stringify(baseValue) !== JSON.stringify(newValue)) { patch[key] = newValue; } }); return patch; } function isPatchSafe(patch) { const dangerousPaths = ["/", "..", "~", "$HOME"]; if (patch.scanPaths && Array.isArray(patch.scanPaths)) { for (const path of patch.scanPaths) { if (path && dangerousPaths.some((dangerous) => path.includes(dangerous))) { return false; } } } if (patch.security?.allowedPaths && Array.isArray(patch.security.allowedPaths)) { for (const path of patch.security.allowedPaths) { if (path && dangerousPaths.some((dangerous) => path.includes(dangerous))) { return false; } } } if (patch.security?.blockedPatterns) { const criticalPatterns = ["**/.env*", "**/*.key", "**/*.pem"]; for (const pattern of criticalPatterns) { if (!patch.security.blockedPatterns.includes(pattern)) { return false; } } } return true; } // src/config/loader.ts var CONFIG_FILE_NAMES = [ "vibeship.config.ts", "vibeship.config.js", "vibeship.config.mjs", "vibeship.config.cjs", "vibeship.config.json", ".vibeshiprc.json", ".vibeshiprc" ]; async function loadConfig(options = {}) { const { configPath, rootDir = process.cwd(), applyEnvOverrides = true, validate = true, freeze = true } = options; const configFile = configPath || findConfigFile(rootDir); let userConfig = {}; if (configFile) { userConfig = await loadConfigFile(configFile); if (validate) { const validation = validatePartialConfig(userConfig); if (!validation.success) { throw new Error( `Invalid configuration in ${configFile}: ${validation.errors?.join("\n")}` ); } } } const environmentDefaults = getEnvironmentDefaults(); let config = mergeConfigurations( defaultConfig, environmentDefaults, {}, // Environment overrides will be applied separately userConfig ); if (applyEnvOverrides) { config = applyEnvironmentOverrides(config); } if (validate) { const validation = validateConfig(config); if (!validation.success) { throw new Error( `Invalid final configuration: ${validation.errors?.join("\n")}` ); } config = validation.data; } if (freeze) { config = freezeConfig(config); } return config; } function findConfigFile(rootDir) { for (const fileName of CONFIG_FILE_NAMES) { const filePath = (0, import_path.join)(rootDir, fileName); if ((0, import_fs.existsSync)(filePath)) { return filePath; } } let currentDir = rootDir; for (let i = 0; i < 3; i++) { const parentDir = (0, import_path.resolve)(currentDir, ".."); if (parentDir === currentDir) break; for (const fileName of CONFIG_FILE_NAMES) { const filePath = (0, import_path.join)(parentDir, fileName); if ((0, import_fs.existsSync)(filePath)) { return filePath; } } currentDir = parentDir; } return void 0; } async function loadConfigFile(filePath) { const ext = (0, import_path.extname)(filePath); switch (ext) { case ".ts": case ".mjs": return loadTypeScriptConfig(filePath); case ".js": case ".cjs": return loadJavaScriptConfig(filePath); case ".json": case "": return loadJsonConfig(filePath); default: throw new Error(`Unsupported configuration file format: ${ext}`); } } async function loadTypeScriptConfig(filePath) { const supportsTS = await checkTypeScriptSupport(); if (supportsTS) { const fileUrl2 = (0, import_url.pathToFileURL)(filePath).href; const module3 = await import(fileUrl2); return extractConfigFromModule(module3); } try { await tryRegisterLoader("tsx/cjs"); } catch { try { await tryRegisterLoader("ts-node/register"); } catch { throw new Error( "TypeScript configuration files require either:\n1. Running with --loader tsx or --loader ts-node/esm\n2. Having tsx or ts-node installed\n3. Using a .js configuration file instead" ); } } const fileUrl = (0, import_url.pathToFileURL)(filePath).href; const module2 = await import(fileUrl); return extractConfigFromModule(module2); } async function loadJavaScriptConfig(filePath) { const fileUrl = (0, import_url.pathToFileURL)(filePath).href; const module2 = await import(fileUrl); return extractConfigFromModule(module2); } function loadJsonConfig(filePath) { const content = (0, import_fs.readFileSync)(filePath, "utf-8"); try { return JSON.parse(content); } catch (error) { throw new Error(`Invalid JSON in configuration file ${filePath}: ${error}`); } } function extractConfigFromModule(module2) { if (module2.default) { if (typeof module2.default === "function") { return module2.default(); } return module2.default; } if (module2.config) { if (typeof module2.config === "function") { return module2.config(); } return module2.config; } if (typeof module2 === "function") { return module2(); } if (typeof module2 === "object" && module2 !== null) { return module2; } throw new Error("Configuration file must export a configuration object"); } async function checkTypeScriptSupport() { if (process.env.TSX || process.env.TS_NODE_DEV) { return true; } const nodeVersion = process.version; const major = parseInt(nodeVersion.split(".")[0].substring(1)); if (major >= 20 && process.execArgv.includes("--experimental-strip-types")) { return true; } return false; } async function tryRegisterLoader(loader) { try { await import(loader); } catch (error) { throw new Error(`Failed to load ${loader}`); } } function defineConfig(config) { return config; } async function watchConfig(options) { const { onChange, ...loadOptions } = options; const configFile = options.configPath || findConfigFile(options.rootDir || process.cwd()); if (!configFile) { const config = await loadConfig(loadOptions); onChange(config); return () => { }; } let currentConfig = await loadConfig(loadOptions); onChange(currentConfig); const { watch } = await import("fs"); const watcher = watch(configFile, async (eventType) => { if (eventType === "change") { try { delete require.cache[require.resolve(configFile)]; const newConfig = await loadConfig(loadOptions); if (JSON.stringify(newConfig) !== JSON.stringify(currentConfig)) { currentConfig = newConfig; onChange(newConfig); } } catch (error) { console.error("Error reloading configuration:", error); } } }); return () => { watcher.close(); }; } var cachedConfig = null; var cacheKey = null; async function getConfig(options = {}) { const key = JSON.stringify(options); if (cachedConfig && cacheKey === key) { return cachedConfig; } cachedConfig = await loadConfig(options); cacheKey = key; return cachedConfig; } function clearConfigCache() { cachedConfig = null; cacheKey = null; } // src/config/migration.ts var MIGRATIONS = [ { version: "1.0.0", description: "Migrate legacy devtools config to vibecode config", shouldApply: (config) => { return config.devtools && !config.scanPaths; }, migrate: (config) => { const { devtools, ...rest } = config; return { ...rest, scanPaths: devtools.scanPaths || ["./docs"], features: { tasks: devtools.enableTasks !== false, ai: devtools.enableAI === true, realtime: devtools.enableRealtime !== false, commandPalette: devtools.enableCommandPalette !== false, fileEditing: devtools.enableFileEditing === true, markdownPreview: devtools.enableMarkdownPreview !== false }, ui: { theme: devtools.theme || "system", position: devtools.position || "bottom-right", hotkey: devtools.hotkey || "ctrl+shift+d", showInProduction: devtools.showInProduction === true } }; } }, { version: "1.1.0", description: "Migrate old security config structure", shouldApply: (config) => { return config.auth && !config.security?.auth; }, migrate: (config) => { const { auth, cors, rateLimit, ...rest } = config; return { ...rest, security: { ...rest.security, authentication: !!auth, auth: auth ? { jwtSecret: auth.jwtSecret, apiKey: auth.apiKey, session: auth.session, providers: auth.providers } : void 0, cors: cors !== false, corsConfig: cors && typeof cors === "object" ? cors : void 0, rateLimit: rateLimit !== false, rateLimitConfig: rateLimit && typeof rateLimit === "object" ? rateLimit : void 0 } }; } }, { version: "1.2.0", description: "Migrate task patterns from arrays to objects", shouldApply: (config) => { return config.taskPatterns && Array.isArray(config.taskPatterns); }, migrate: (config) => { const patterns = config.taskPatterns; return { ...config, taskPatterns: { todo: patterns.includes("TODO") || patterns.includes("todo"), fixme: patterns.includes("FIXME") || patterns.includes("fixme"), hack: patterns.includes("HACK") || patterns.includes("hack"), note: patterns.includes("NOTE") || patterns.includes("note"), warning: patterns.includes("WARNING") || patterns.includes("warning"), optimize: patterns.includes("OPTIMIZE") || patterns.includes("optimize") } }; } }, { version: "1.3.0", description: "Migrate cache config from string to object", shouldApply: (config) => { return typeof config.cache === "string" || typeof config.cache === "boolean"; }, migrate: (config) => { const cacheValue = config.cache; return { ...config, cache: { enabled: cacheValue !== false && cacheValue !== "false", directory: typeof cacheValue === "string" ? cacheValue : ".vibecode/cache", maxSize: 52428800, // 50MB ttl: 36e5, // 1 hour strategy: "lru" } }; } }, { version: "1.4.0", description: "Migrate API config from string to object", shouldApply: (config) => { return typeof config.api === "string"; }, migrate: (config) => { const apiValue = config.api; return { ...config, api: { basePath: apiValue.startsWith("/") ? apiValue : `/api/${apiValue}`, version: "v1", documentation: true, timeout: 3e4 } }; } } ]; function migrateConfig(config) { const result = { migrated: false, appliedMigrations: [], warnings: [], config: { ...config } }; for (const migration of MIGRATIONS) { if (migration.shouldApply(result.config)) { try { const migratedConfig = migration.migrate(result.config); if (migration.validate && !migration.validate(migratedConfig)) { result.warnings.push( `Migration ${migration.version} validation failed: ${migration.description}` ); continue; } const validation = validatePartialConfig(migratedConfig); if (!validation.success) { result.warnings.push( `Migration ${migration.version} produced invalid config: ${validation.errors?.join(", ")}` ); continue; } result.config = migratedConfig; result.migrated = true; result.appliedMigrations.push(migration.version); console.log(`Applied migration ${migration.version}: ${migration.description}`); } catch (error) { result.warnings.push( `Migration ${migration.version} failed: ${error instanceof Error ? error.message : String(error)}` ); } } } return result; } function needsMigration(config) { return MIGRATIONS.some((migration) => migration.shouldApply(config)); } function getAvailableMigrations(config) { return MIGRATIONS.filter((migration) => migration.shouldApply(config)); } function createConfigBackup(config, backupPath) { const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"); const backupName = backupPath || `vibecode.config.backup.${timestamp}.json`; const { writeFileSync } = require("fs"); writeFileSync(backupName, JSON.stringify(config, null, 2)); return backupName; } async function interactiveMigration(config, options = {}) { const { prompt, createBackup = true, backupPath } = options; const availableMigrations = getAvailableMigrations(config); if (availableMigrations.length === 0) { return { migrated: false, appliedMigrations: [], warnings: [], config }; } console.log("\nAvailable migrations:"); for (const migration of availableMigrations) { console.log(` ${migration.version}: ${migration.description}`); } const shouldMigrate = prompt ? await prompt(`Apply ${availableMigrations.length} migration(s)?`) : true; if (!shouldMigrate) { return { migrated: false, appliedMigrations: [], warnings: ["Migration cancelled by user"], config }; } if (createBackup) { const backupFile = createConfigBackup(config, backupPath); console.log(`Configuration backup created: ${backupFile}`); } return migrateConfig(config); } function rollbackConfig(backupPath) { const { readFileSync: readFileSync2, existsSync: existsSync2 } = require("fs"); if (!existsSync2(backupPath)) { throw new Error(`Backup file not found: ${backupPath}`); } const backupContent = readFileSync2(backupPath, "utf-8"); return JSON.parse(backupContent); } function cleanupBackups(maxAge = 30 * 24 * 60 * 60 * 1e3) { const { readdirSync, statSync, unlinkSync } = require("fs"); const { join: join2 } = require("path"); const currentDir = process.cwd(); const files = readdirSync(currentDir); const backupFiles = files.filter((file) => file.startsWith("vibecode.config.backup.")); const now = Date.now(); for (const file of backupFiles) { const filePath = join2(currentDir, file); const stats = statSync(filePath); if (now - stats.mtime.getTime() > maxAge) { unlinkSync(filePath); console.log(`Cleaned up old backup: ${file}`); } } } // src/config/index.ts var import_zod2 = require("zod"); //# sourceMappingURL=config.js.map