@supacontrol/cli 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/status.ts","../src/config/loader.ts","../src/config/schema.ts","../src/config/resolver.ts","../src/utils/git.ts","../src/guards/project-guard.ts","../src/guards/types.ts","../src/utils/supabase.ts","../src/auth/credentials.ts","../src/api/supabase-client.ts","../src/commands/push.ts","../src/guards/index.ts","../src/guards/lock-guard.ts","../src/guards/operation-guard.ts","../src/guards/git-guard.ts","../src/guards/confirm.ts","../src/utils/migrations.ts","../src/commands/reset.ts","../src/commands/pull.ts","../src/commands/switch.ts","../src/commands/lock.ts","../src/config/writer.ts","../src/commands/doctor.ts","../src/commands/init.ts","../src/api/project-selector.ts"],"sourcesContent":["import { Command } from 'commander';\nimport pc from 'picocolors';\nimport { createRequire } from 'node:module';\nimport { createStatusCommand } from './commands/status.js';\nimport { createPushCommand } from './commands/push.js';\nimport { createResetCommand } from './commands/reset.js';\nimport { createPullCommand } from './commands/pull.js';\nimport { createSwitchCommand } from './commands/switch.js';\nimport { createLockCommand, createUnlockCommand } from './commands/lock.js';\nimport { createDoctorCommand } from './commands/doctor.js';\nimport { createInitCommand } from './commands/init.js';\n\nconst require = createRequire(import.meta.url);\nconst packageJson = require('../package.json') as { version: string; description: string };\n\n// Global state for CLI options\nexport interface GlobalOptions {\n verbose: boolean;\n ci: boolean;\n env?: string;\n}\n\nconst program = new Command();\n\nprogram\n .name('supacontrol')\n .description(packageJson.description)\n .version(packageJson.version, '-v, --version', 'Show version number')\n .option('--verbose', 'Enable verbose output', false)\n .option('--ci', 'Run in CI mode (non-interactive, strict)', false)\n .option('-e, --env <environment>', 'Target environment')\n .configureHelp({\n sortSubcommands: true,\n sortOptions: true,\n });\n\n// Error handling wrapper\nfunction withErrorHandling<T extends (...args: unknown[]) => Promise<void>>(\n fn: T\n): (...args: Parameters<T>) => Promise<void> {\n return async (...args: Parameters<T>) => {\n try {\n await fn(...args);\n } catch (error) {\n const opts = program.opts<GlobalOptions>();\n \n if (error instanceof Error) {\n console.error(pc.red('\\u2717'), error.message);\n if (opts.verbose && error.stack) {\n console.error(pc.dim(error.stack));\n }\n } else {\n console.error(pc.red('\\u2717'), 'An unexpected error occurred');\n if (opts.verbose) {\n console.error(pc.dim(String(error)));\n }\n }\n \n process.exit(1);\n }\n };\n}\n\n// Export for use in commands\nexport { program, withErrorHandling };\n\n// Register commands\nprogram.addCommand(createInitCommand());\nprogram.addCommand(createStatusCommand());\nprogram.addCommand(createPushCommand());\nprogram.addCommand(createResetCommand());\nprogram.addCommand(createPullCommand());\nprogram.addCommand(createSwitchCommand());\nprogram.addCommand(createLockCommand());\nprogram.addCommand(createUnlockCommand());\nprogram.addCommand(createDoctorCommand());\n\n// Parse and execute\nprogram.parse();\n","import { Command } from 'commander';\r\nimport pc from 'picocolors';\r\nimport { loadConfig } from '../config/loader.js';\r\nimport { resolveEnvironmentByProjectRef } from '../config/resolver.js';\r\nimport { isEnvironmentLocked } from '../config/schema.js';\r\nimport { getCurrentBranch, hasUncommittedChanges } from '../utils/git.js';\r\nimport { getCurrentLinkedProject } from '../guards/project-guard.js';\r\nimport { isSupabaseCLIInstalled, getSupabaseVersion } from '../utils/supabase.js';\r\nimport { getAccessToken } from '../auth/credentials.js';\r\nimport { createSupabaseClient, type Project } from '../api/supabase-client.js';\r\n\r\n/**\r\n * Create the status command\r\n */\r\nexport function createStatusCommand(): Command {\r\n return new Command('status')\r\n .description('Show current environment and project status')\r\n .action(async () => {\r\n await runStatus();\r\n });\r\n}\r\n\r\n/**\r\n * Info about the linked project/branch\r\n */\r\ninterface LinkedInfo {\r\n ref: string;\r\n type: 'project' | 'branch' | 'unknown';\r\n name?: string;\r\n parentProjectName?: string;\r\n parentProjectRef?: string;\r\n}\r\n\r\n/**\r\n * Resolve a project ref to human-readable info\r\n */\r\nasync function resolveLinkedRef(\r\n linkedRef: string,\r\n projects: Project[],\r\n client: ReturnType<typeof createSupabaseClient>\r\n): Promise<LinkedInfo> {\r\n // Check if it's a direct project match\r\n const directMatch = projects.find(p => p.id === linkedRef);\r\n if (directMatch) {\r\n return {\r\n ref: linkedRef,\r\n type: 'project',\r\n name: directMatch.name,\r\n };\r\n }\r\n\r\n // Not a direct project - check if it's a branch\r\n for (const project of projects) {\r\n if (project.status !== 'ACTIVE_HEALTHY') continue;\r\n \r\n try {\r\n const branches = await client.getBranches(project.id);\r\n const matchingBranch = branches.find(b => b.project_ref === linkedRef);\r\n \r\n if (matchingBranch) {\r\n return {\r\n ref: linkedRef,\r\n type: 'branch',\r\n name: matchingBranch.name,\r\n parentProjectName: project.name,\r\n parentProjectRef: project.id,\r\n };\r\n }\r\n } catch {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n return { ref: linkedRef, type: 'unknown' };\r\n}\r\n\r\n/**\r\n * Run the status command\r\n */\r\nasync function runStatus(): Promise<void> {\r\n console.log();\r\n console.log(pc.bold('SupaControl Status'));\r\n console.log(pc.dim('─'.repeat(50)));\r\n\r\n // Load config first - we need it for everything\r\n const config = await loadConfig();\r\n \r\n if (!config) {\r\n console.log();\r\n console.log(pc.yellow('⚠'), 'No supacontrol.toml found');\r\n console.log(pc.dim(' Run: supacontrol init'));\r\n console.log();\r\n return;\r\n }\r\n\r\n // Get linked project\r\n const linkedRef = await getCurrentLinkedProject();\r\n \r\n // Try to resolve linked ref to human-readable info\r\n let linkedInfo: LinkedInfo | null = null;\r\n const token = await getAccessToken();\r\n \r\n let apiError = false;\r\n if (linkedRef && token) {\r\n try {\r\n const client = createSupabaseClient(token);\r\n const projects = await client.getProjects();\r\n linkedInfo = await resolveLinkedRef(linkedRef, projects, client);\r\n } catch {\r\n // Failed to fetch from API - show raw ref but note the error\r\n apiError = true;\r\n }\r\n }\r\n\r\n // Resolve active environment from linked project ref\r\n const activeEnv = resolveEnvironmentByProjectRef(linkedRef, config);\r\n\r\n // === ACTIVE ENVIRONMENT (Primary info) ===\r\n console.log();\r\n \r\n if (activeEnv) {\r\n const isLocked = isEnvironmentLocked(activeEnv.name, activeEnv.config);\r\n const lockIcon = isLocked ? pc.red('🔒') : pc.green('🔓');\r\n const statusText = isLocked ? pc.red('LOCKED') : pc.green('unlocked');\r\n\r\n console.log(pc.bold(`Active Environment: ${pc.cyan(activeEnv.name)} ${lockIcon}`));\r\n \r\n // Show project info\r\n if (linkedInfo?.type === 'project') {\r\n console.log(` Project: ${linkedInfo.name}`);\r\n } else if (linkedInfo?.type === 'branch') {\r\n console.log(` Project: ${linkedInfo.parentProjectName} ${pc.dim(`(${linkedInfo.name} branch)`)}`);\r\n } else if (linkedRef) {\r\n console.log(` Project: ${linkedRef}`);\r\n }\r\n \r\n console.log(` Status: ${statusText}`);\r\n \r\n if (activeEnv.config.protected_operations.length > 0) {\r\n console.log(` Protected: ${pc.yellow(activeEnv.config.protected_operations.join(', '))}`);\r\n }\r\n } else if (linkedRef) {\r\n // Linked to something, but not in config\r\n console.log(pc.bold(`Active Environment: ${pc.yellow('unknown')}`));\r\n \r\n if (linkedInfo?.type === 'project') {\r\n console.log(` Linked to: ${linkedInfo.name} ${pc.dim(`(${linkedRef})`)}`);\r\n } else if (linkedInfo?.type === 'branch') {\r\n console.log(` Linked to: ${linkedInfo.name} branch ${pc.dim(`(${linkedRef})`)}`);\r\n console.log(` Parent: ${linkedInfo.parentProjectName}`);\r\n } else {\r\n console.log(` Linked to: ${linkedRef}`);\r\n }\r\n \r\n console.log();\r\n console.log(pc.yellow(' ⚠ This project is not configured in supacontrol.toml'));\r\n console.log(pc.dim(' Add it to an environment or run: supacontrol init'));\r\n } else {\r\n console.log(pc.bold(`Active Environment: ${pc.dim('none')}`));\r\n console.log(pc.dim(' No Supabase project linked'));\r\n console.log(pc.dim(' Run: supacontrol switch <environment>'));\r\n }\r\n\r\n // === GIT INFO (Secondary) ===\r\n console.log();\r\n const branch = await getCurrentBranch();\r\n const dirty = await hasUncommittedChanges();\r\n \r\n if (branch) {\r\n const dirtyIndicator = dirty ? pc.yellow(' *') : '';\r\n console.log(`Git: ${pc.cyan(branch)}${dirtyIndicator}`);\r\n } else {\r\n console.log(`Git: ${pc.dim('not a git repository')}`);\r\n }\r\n\r\n // === ENVIRONMENTS LIST ===\r\n const envNames = Object.keys(config.environments);\r\n \r\n if (envNames.length > 0) {\r\n console.log();\r\n console.log(pc.bold('Environments'));\r\n \r\n for (const name of envNames) {\r\n const env = config.environments[name];\r\n if (!env) continue;\r\n\r\n const isLocked = isEnvironmentLocked(name, env);\r\n const lockIcon = isLocked ? pc.red('🔒') : pc.green('🔓');\r\n const isActive = activeEnv?.name === name;\r\n \r\n // Show marker for active environment\r\n const marker = isActive ? pc.cyan('→') : ' ';\r\n const activeLabel = isActive ? pc.cyan(' ← active') : '';\r\n \r\n console.log(` ${marker} ${name} ${lockIcon}${activeLabel}`);\r\n }\r\n }\r\n\r\n // === SUPABASE CLI INFO (Footer) ===\r\n console.log();\r\n const supabaseInstalled = await isSupabaseCLIInstalled();\r\n if (supabaseInstalled) {\r\n const version = await getSupabaseVersion();\r\n console.log(pc.dim(`Supabase CLI v${version}`));\r\n } else {\r\n console.log(pc.red('Supabase CLI not installed'));\r\n }\r\n \r\n // Show API error hint if we couldn't fetch project details\r\n if (apiError) {\r\n console.log();\r\n console.log(pc.dim('Note: Could not fetch project details from API'));\r\n console.log(pc.dim(' Your access token may have expired'));\r\n console.log(pc.dim(' Generate a new one: https://supabase.com/dashboard/account/tokens'));\r\n }\r\n \r\n console.log();\r\n}\r\n","import { readFile } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { parse } from 'smol-toml';\nimport { ZodError } from 'zod';\nimport pc from 'picocolors';\nimport { ConfigSchema, RawConfigSchema, type Config } from './schema.js';\n\n/**\n * Default config file paths to search (in order)\n */\nconst CONFIG_PATHS = ['supacontrol.toml', 'config/supacontrol.toml'];\n\n/**\n * Error thrown when config file is invalid\n */\nexport class ConfigError extends Error {\n public readonly filePath: string | undefined;\n\n constructor(\n message: string,\n filePath?: string,\n cause?: Error\n ) {\n super(message, { cause });\n this.name = 'ConfigError';\n this.filePath = filePath;\n }\n}\n\n/**\n * Result of finding a config file\n */\ninterface ConfigFileResult {\n content: string;\n path: string;\n}\n\n/**\n * Try to find and read a config file from the search paths\n */\nasync function findConfigFile(cwd: string): Promise<ConfigFileResult | null> {\n for (const configPath of CONFIG_PATHS) {\n const fullPath = resolve(cwd, configPath);\n try {\n const content = await readFile(fullPath, 'utf-8');\n return { content, path: fullPath };\n } catch (error) {\n // File not found, try next path\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {\n continue;\n }\n // Other error (permission denied, etc.)\n throw new ConfigError(\n `Failed to read config file: ${fullPath}`,\n fullPath,\n error instanceof Error ? error : undefined\n );\n }\n }\n return null;\n}\n\n/**\n * Format Zod validation errors into readable messages\n */\nfunction formatZodError(error: ZodError): string {\n const issues = error.issues.map((issue) => {\n const path = issue.path.join('.');\n return ` ${pc.dim(path ? `${path}: ` : '')}${issue.message}`;\n });\n return issues.join('\\n');\n}\n\n/**\n * Load and validate supacontrol.toml configuration\n *\n * @param cwd - Directory to search for config (defaults to process.cwd())\n * @returns Validated config object, or null if no config file found\n * @throws ConfigError if config file exists but is invalid\n */\nexport async function loadConfig(cwd?: string): Promise<Config | null> {\n const searchDir = cwd ?? process.cwd();\n const result = await findConfigFile(searchDir);\n\n if (!result) {\n return null;\n }\n\n // Parse TOML\n let rawData: unknown;\n try {\n rawData = parse(result.content);\n } catch (error) {\n throw new ConfigError(\n `Invalid TOML syntax in ${result.path}:\\n ${error instanceof Error ? error.message : String(error)}`,\n result.path,\n error instanceof Error ? error : undefined\n );\n }\n\n // Validate raw structure first\n const rawResult = RawConfigSchema.safeParse(rawData);\n if (!rawResult.success) {\n throw new ConfigError(\n `Invalid config in ${result.path}:\\n${formatZodError(rawResult.error)}`,\n result.path\n );\n }\n\n // Validate and transform with full schema (applies defaults)\n const configResult = ConfigSchema.safeParse(rawData);\n if (!configResult.success) {\n throw new ConfigError(\n `Invalid config in ${result.path}:\\n${formatZodError(configResult.error)}`,\n result.path\n );\n }\n\n return configResult.data;\n}\n\n/**\n * Load config or exit with error\n * Convenience function for CLI commands that require config\n */\nexport async function loadConfigOrExit(cwd?: string): Promise<Config> {\n try {\n const config = await loadConfig(cwd);\n if (!config) {\n console.error(pc.red('\\u2717'), 'No supacontrol.toml found');\n console.error(pc.dim(' Run `supacontrol init` to create one'));\n process.exit(1);\n }\n return config;\n } catch (error) {\n if (error instanceof ConfigError) {\n console.error(pc.red('\\u2717'), error.message);\n process.exit(1);\n }\n throw error;\n }\n}\n\n/**\n * Get the directory containing the config file (useful for resolving relative paths)\n */\nexport async function getConfigDir(cwd?: string): Promise<string | null> {\n const searchDir = cwd ?? process.cwd();\n const result = await findConfigFile(searchDir);\n return result ? dirname(result.path) : null;\n}\n","import { z } from 'zod';\n\n/**\n * Operations that can be protected in an environment\n */\nexport const ProtectedOperation = z.enum([\n 'push',\n 'reset',\n 'pull',\n 'seed',\n 'link',\n 'unlink',\n]);\n\nexport type ProtectedOperation = z.infer<typeof ProtectedOperation>;\n\n/**\n * Settings that apply globally to the CLI\n */\nexport const SettingsSchema = z.object({\n /** Fail on any guard warning, not just errors */\n strict_mode: z.boolean().default(false),\n /** Require clean git working tree before destructive operations */\n require_clean_git: z.boolean().default(true),\n /** Show migration diff before push */\n show_migration_diff: z.boolean().default(true),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n\n/**\n * Configuration for a single environment (staging, production, etc.)\n */\nexport const EnvironmentSchema = z.object({\n /** Supabase project reference (optional, can be auto-detected) */\n project_ref: z.string().optional(),\n /** Git branches that map to this environment */\n git_branches: z.array(z.string()).default([]),\n /** Operations that require confirmation in this environment */\n protected_operations: z.array(ProtectedOperation).default([]),\n /** Custom confirmation word (defaults to environment name) */\n confirm_word: z.string().optional(),\n /** Lock environment to prevent all destructive operations */\n locked: z.boolean().optional(), // undefined = use default (true for production)\n});\n\nexport type Environment = z.infer<typeof EnvironmentSchema>;\n\n/**\n * Root configuration schema for supacontrol.toml\n */\nexport const ConfigSchema = z.object({\n settings: SettingsSchema.default({\n strict_mode: false,\n require_clean_git: true,\n show_migration_diff: true,\n }),\n environments: z.record(z.string(), EnvironmentSchema).default({}),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/**\n * Raw TOML structure before transformation\n * TOML uses [environments.production] syntax which creates nested objects\n */\nexport const RawConfigSchema = z.object({\n settings: SettingsSchema.optional(),\n environments: z.record(z.string(), EnvironmentSchema).optional(),\n});\n\nexport type RawConfig = z.infer<typeof RawConfigSchema>;\n\n/**\n * Check if an environment is effectively locked\n * Production is locked by default if not explicitly set\n */\nexport function isEnvironmentLocked(\n envName: string,\n env: Environment\n): boolean {\n if (env.locked !== undefined) {\n return env.locked;\n }\n\n // Production-like environments are locked by default\n const isProduction =\n envName === 'production' ||\n env.git_branches.includes('main') ||\n env.git_branches.includes('master');\n\n return isProduction;\n}\n\n/**\n * Default settings if none provided\n */\nexport const DEFAULT_SETTINGS: Settings = {\n strict_mode: false,\n require_clean_git: true,\n show_migration_diff: true,\n};\n","import type { Config, Environment } from './schema.js';\n\n/**\n * Result of resolving an environment from git branch\n */\nexport interface ResolvedEnvironment {\n /** Environment name (e.g., 'staging', 'production') */\n name: string;\n /** Environment configuration */\n config: Environment;\n /** Supabase project reference (if configured) */\n projectRef: string | undefined;\n /** Whether this was an exact match or wildcard/fallback */\n matchType: 'exact' | 'wildcard' | 'fallback';\n}\n\n/**\n * Check if a branch matches a pattern (supports wildcards)\n *\n * Patterns:\n * - 'main' - exact match\n * - 'feature/*' - matches 'feature/foo', 'feature/bar/baz'\n * - '*' - matches anything\n */\nfunction matchBranchPattern(branch: string, pattern: string): boolean {\n // Exact match\n if (pattern === branch) {\n return true;\n }\n\n // Convert glob pattern to regex\n // Escape regex special chars except * and ?\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(branch);\n}\n\n/**\n * Resolve which environment a git branch maps to\n *\n * @param branch - Current git branch name (or null if unknown)\n * @param config - Parsed supacontrol config\n * @returns Resolved environment, or null if no match found\n */\nexport function resolveEnvironment(\n branch: string | null,\n config: Config\n): ResolvedEnvironment | null {\n const environments = Object.entries(config.environments);\n\n if (environments.length === 0) {\n return null;\n }\n\n // If we don't know the branch, we can't auto-resolve\n if (branch === null) {\n // Check if there's a 'local' environment as fallback\n const localEnv = config.environments['local'];\n if (localEnv) {\n return {\n name: 'local',\n config: localEnv,\n projectRef: localEnv.project_ref,\n matchType: 'fallback',\n };\n }\n return null;\n }\n\n // First pass: look for exact matches\n for (const [name, env] of environments) {\n if (!env) continue;\n if (env.git_branches.includes(branch)) {\n return {\n name,\n config: env,\n projectRef: env.project_ref,\n matchType: 'exact',\n };\n }\n }\n\n // Second pass: look for wildcard matches\n for (const [name, env] of environments) {\n if (!env) continue;\n for (const pattern of env.git_branches) {\n if (pattern.includes('*') || pattern.includes('?')) {\n if (matchBranchPattern(branch, pattern)) {\n return {\n name,\n config: env,\n projectRef: env.project_ref,\n matchType: 'wildcard',\n };\n }\n }\n }\n }\n\n // No match found - check for 'local' fallback\n const localEnv = config.environments['local'];\n if (localEnv) {\n return {\n name: 'local',\n config: localEnv,\n projectRef: localEnv.project_ref,\n matchType: 'fallback',\n };\n }\n\n return null;\n}\n\n/**\n * Get environment by explicit name\n *\n * @param envName - Environment name to look up\n * @param config - Parsed supacontrol config\n * @returns Environment config, or null if not found\n */\nexport function getEnvironmentByName(\n envName: string,\n config: Config\n): ResolvedEnvironment | null {\n const env = config.environments[envName];\n if (!env) {\n return null;\n }\n\n return {\n name: envName,\n config: env,\n projectRef: env.project_ref,\n matchType: 'exact',\n };\n}\n\n/**\n * List all configured environments\n */\nexport function listEnvironments(config: Config): string[] {\n return Object.keys(config.environments);\n}\n\n/**\n * Check if an environment name exists in the config\n */\nexport function hasEnvironment(envName: string, config: Config): boolean {\n return envName in config.environments;\n}\n\n/**\n * Resolve which environment a project ref belongs to\n * \n * This is the PRIMARY way to determine active environment - based on what\n * Supabase project/branch is currently linked, not the git branch.\n *\n * @param linkedRef - Currently linked Supabase project ref\n * @param config - Parsed supacontrol config\n * @returns Resolved environment, or null if no match found\n */\nexport function resolveEnvironmentByProjectRef(\n linkedRef: string | null,\n config: Config\n): ResolvedEnvironment | null {\n if (!linkedRef) {\n return null;\n }\n\n const environments = Object.entries(config.environments);\n\n for (const [name, env] of environments) {\n if (!env) continue;\n if (env.project_ref === linkedRef) {\n return {\n name,\n config: env,\n projectRef: env.project_ref,\n matchType: 'exact',\n };\n }\n }\n\n return null;\n}\n","import { execa } from 'execa';\n\n/**\n * Cache for git results within a single command execution\n */\nlet branchCache: string | null = null;\nlet dirtyCache: boolean | null = null;\nlet isRepoCache: boolean | null = null;\n\n/**\n * Clear the git cache (call at start of each CLI command)\n */\nexport function clearGitCache(): void {\n branchCache = null;\n dirtyCache = null;\n isRepoCache = null;\n}\n\n/**\n * Run git command with optional cwd\n */\nasync function runGit(args: string[], cwd?: string): Promise<string> {\n const result = cwd\n ? await execa('git', args, { cwd })\n : await execa('git', args);\n return typeof result.stdout === 'string' ? result.stdout : '';\n}\n\n/**\n * Check if the current directory is inside a git repository\n */\nexport async function isGitRepository(cwd?: string): Promise<boolean> {\n if (isRepoCache !== null) {\n return isRepoCache;\n }\n\n try {\n await runGit(['rev-parse', '--is-inside-work-tree'], cwd);\n isRepoCache = true;\n return true;\n } catch {\n isRepoCache = false;\n return false;\n }\n}\n\n/**\n * Get the current git branch name\n *\n * @returns Branch name, or null if not in a git repo or detached HEAD\n */\nexport async function getCurrentBranch(cwd?: string): Promise<string | null> {\n if (branchCache !== null) {\n return branchCache;\n }\n\n try {\n // First check if we're in a git repo\n const isRepo = await isGitRepository(cwd);\n if (!isRepo) {\n return null;\n }\n\n // Try to get the symbolic ref (branch name)\n const stdout = await runGit(['symbolic-ref', '--short', 'HEAD'], cwd);\n branchCache = stdout.trim();\n return branchCache;\n } catch {\n // Could be detached HEAD or other error\n branchCache = null;\n return null;\n }\n}\n\n/**\n * Check if there are uncommitted changes in the working directory\n *\n * @returns true if there are uncommitted changes\n */\nexport async function hasUncommittedChanges(cwd?: string): Promise<boolean> {\n if (dirtyCache !== null) {\n return dirtyCache;\n }\n\n try {\n const isRepo = await isGitRepository(cwd);\n if (!isRepo) {\n // Not a git repo, consider it \"clean\" for our purposes\n dirtyCache = false;\n return false;\n }\n\n // Check for staged and unstaged changes\n const stdout = await runGit(['status', '--porcelain'], cwd);\n dirtyCache = stdout.trim().length > 0;\n return dirtyCache;\n } catch {\n // Error checking status, assume dirty to be safe\n dirtyCache = true;\n return true;\n }\n}\n\n/**\n * Get the git root directory\n *\n * @returns Root directory path, or null if not in a git repo\n */\nexport async function getGitRoot(cwd?: string): Promise<string | null> {\n try {\n const isRepo = await isGitRepository(cwd);\n if (!isRepo) {\n return null;\n }\n\n const stdout = await runGit(['rev-parse', '--show-toplevel'], cwd);\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Get the short hash of the current commit\n *\n * @returns Short commit hash, or null if not in a git repo\n */\nexport async function getCurrentCommitHash(cwd?: string): Promise<string | null> {\n try {\n const isRepo = await isGitRepository(cwd);\n if (!isRepo) {\n return null;\n }\n\n const stdout = await runGit(['rev-parse', '--short', 'HEAD'], cwd);\n return stdout.trim();\n } catch {\n return null;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { blocked, allowed, type GuardContext, type GuardResult } from './types.js';\n\n/**\n * Path to Supabase project ref file (relative to project root)\n */\nconst PROJECT_REF_PATH = 'supabase/.temp/project-ref';\n\n/**\n * Cache for current linked project\n */\nlet linkedProjectCache: string | null | undefined;\n\n/**\n * Clear the project cache\n */\nexport function clearProjectCache(): void {\n linkedProjectCache = undefined;\n}\n\n/**\n * Get the currently linked Supabase project ref\n *\n * @param cwd - Directory to search from (defaults to process.cwd())\n * @returns Project ref string, or null if not linked\n */\nexport async function getCurrentLinkedProject(cwd?: string): Promise<string | null> {\n if (linkedProjectCache !== undefined) {\n return linkedProjectCache;\n }\n\n const searchDir = cwd ?? process.cwd();\n const refPath = resolve(searchDir, PROJECT_REF_PATH);\n\n try {\n const content = await readFile(refPath, 'utf-8');\n linkedProjectCache = content.trim();\n return linkedProjectCache;\n } catch (error) {\n // File not found or not readable\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {\n linkedProjectCache = null;\n return null;\n }\n // Other error - assume not linked\n linkedProjectCache = null;\n return null;\n }\n}\n\n/**\n * Check if the currently linked project matches the expected environment\n */\nexport async function checkProjectMatch(context: GuardContext): Promise<GuardResult> {\n const { environment, environmentName } = context;\n\n // If no project_ref is configured, skip this check\n if (!environment.project_ref) {\n return allowed({\n suggestions: [\n `Consider adding 'project_ref' to [environments.${environmentName}] for extra safety`,\n ],\n });\n }\n\n const linkedProject = await getCurrentLinkedProject();\n\n // If no project is linked, warn but don't block\n if (linkedProject === null) {\n return allowed({\n suggestions: [\n 'No Supabase project is currently linked',\n `Run 'supabase link --project-ref ${environment.project_ref}' to link`,\n ],\n });\n }\n\n // Check if the linked project matches\n if (linkedProject !== environment.project_ref) {\n return blocked(\n `Project mismatch: linked to '${linkedProject}' but '${environmentName}' expects '${environment.project_ref}'`,\n {\n suggestions: [\n `Run 'supabase link --project-ref ${environment.project_ref}' to switch`,\n `Or update [environments.${environmentName}].project_ref in supacontrol.toml`,\n ],\n riskLevel: 'high',\n }\n );\n }\n\n return allowed();\n}\n\n/**\n * Guard name for logging/debugging\n */\nexport const GUARD_NAME = 'project-guard';\n","import type { Config, Environment, ProtectedOperation } from '../config/schema.js';\r\n\r\n/**\r\n * Operations that can be guarded\r\n */\r\nexport type OperationType = ProtectedOperation | 'migrate' | 'diff';\r\n\r\n/**\r\n * Operations that modify the database and require safety checks.\r\n * Used by lock-guard and git-guard to determine which operations to block.\r\n */\r\nexport const DESTRUCTIVE_OPERATIONS: readonly OperationType[] = [\r\n 'push',\r\n 'reset',\r\n 'seed',\r\n 'pull',\r\n 'migrate',\r\n] as const;\r\n\r\n/**\r\n * Risk level of an operation in the current context\r\n */\r\nexport type RiskLevel = 'low' | 'medium' | 'high' | 'critical';\r\n\r\n/**\r\n * Result of running a guard check\r\n */\r\nexport interface GuardResult {\r\n /** Whether the operation is allowed to proceed */\r\n allowed: boolean;\r\n /** Human-readable reason if not allowed */\r\n reason?: string;\r\n /** Suggestions for how to proceed */\r\n suggestions?: string[];\r\n /** Risk level of this operation */\r\n riskLevel?: RiskLevel;\r\n /** Whether user confirmation is required (even if allowed) */\r\n requiresConfirmation?: boolean;\r\n /** Word the user must type to confirm */\r\n confirmWord?: string;\r\n}\r\n\r\n/**\r\n * Context passed to guard functions\r\n */\r\nexport interface GuardContext {\r\n /** The operation being attempted */\r\n operation: OperationType;\r\n /** Name of the target environment */\r\n environmentName: string;\r\n /** Environment configuration */\r\n environment: Environment;\r\n /** Full config */\r\n config: Config;\r\n /** Current git branch (null if unknown) */\r\n gitBranch: string | null;\r\n /** Whether running in CI mode */\r\n isCI: boolean;\r\n /** Whether there are uncommitted changes */\r\n hasUncommittedChanges: boolean;\r\n}\r\n\r\n/**\r\n * A guard function that checks if an operation should be allowed\r\n */\r\nexport type GuardFn = (context: GuardContext) => GuardResult;\r\n\r\n/**\r\n * Helper to create an allowed result\r\n */\r\nexport function allowed(options?: Partial<GuardResult>): GuardResult {\r\n return {\r\n allowed: true,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Helper to create a blocked result\r\n */\r\nexport function blocked(\r\n reason: string,\r\n options?: { suggestions?: string[]; riskLevel?: RiskLevel }\r\n): GuardResult {\r\n return {\r\n allowed: false,\r\n reason,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Helper to create a result that requires confirmation\r\n */\r\nexport function requiresConfirmation(\r\n confirmWord: string,\r\n options?: { reason?: string; riskLevel?: RiskLevel }\r\n): GuardResult {\r\n return {\r\n allowed: true,\r\n requiresConfirmation: true,\r\n confirmWord,\r\n ...options,\r\n };\r\n}\r\n\r\n/**\r\n * Combine multiple guard results\r\n * Returns the first blocking result, or the highest risk allowed result\r\n */\r\nexport function combineResults(results: GuardResult[]): GuardResult {\r\n // Check for any blocking results first\r\n for (const result of results) {\r\n if (!result.allowed) {\r\n return result;\r\n }\r\n }\r\n\r\n // Find highest risk level and any confirmation requirements\r\n const riskOrder: RiskLevel[] = ['low', 'medium', 'high', 'critical'];\r\n let highestRisk: RiskLevel = 'low';\r\n let needsConfirmation = false;\r\n let confirmWord: string | undefined;\r\n const suggestions: string[] = [];\r\n\r\n for (const result of results) {\r\n if (result.riskLevel) {\r\n const currentIndex = riskOrder.indexOf(highestRisk);\r\n const newIndex = riskOrder.indexOf(result.riskLevel);\r\n if (newIndex > currentIndex) {\r\n highestRisk = result.riskLevel;\r\n }\r\n }\r\n\r\n if (result.requiresConfirmation) {\r\n needsConfirmation = true;\r\n if (result.confirmWord) {\r\n confirmWord = result.confirmWord;\r\n }\r\n }\r\n\r\n if (result.suggestions) {\r\n suggestions.push(...result.suggestions);\r\n }\r\n }\r\n\r\n const result: GuardResult = {\r\n allowed: true,\r\n riskLevel: highestRisk,\r\n requiresConfirmation: needsConfirmation,\r\n };\r\n\r\n if (confirmWord !== undefined) {\r\n result.confirmWord = confirmWord;\r\n }\r\n\r\n if (suggestions.length > 0) {\r\n result.suggestions = [...new Set(suggestions)];\r\n }\r\n\r\n return result;\r\n}\r\n","import { execa, type Options } from 'execa';\r\nimport pc from 'picocolors';\r\n\r\n/**\r\n * Cache for supabase CLI availability\r\n */\r\nlet supabaseAvailable: boolean | undefined;\r\n\r\n/**\r\n * Check if the Supabase CLI is installed and available\r\n */\r\nexport async function isSupabaseCLIInstalled(): Promise<boolean> {\r\n if (supabaseAvailable !== undefined) {\r\n return supabaseAvailable;\r\n }\r\n\r\n try {\r\n await execa('supabase', ['--version']);\r\n supabaseAvailable = true;\r\n return true;\r\n } catch {\r\n supabaseAvailable = false;\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Get the installed Supabase CLI version\r\n */\r\nexport async function getSupabaseVersion(): Promise<string | null> {\r\n try {\r\n const result = await execa('supabase', ['--version']);\r\n const stdout = typeof result.stdout === 'string' ? result.stdout : '';\r\n // Output is like \"1.123.0\" or \"Supabase CLI 1.123.0\"\r\n const match = stdout.match(/(\\d+\\.\\d+\\.\\d+)/);\r\n if (match && match[1]) {\r\n return match[1];\r\n }\r\n return stdout.trim() || null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Options for running supabase CLI\r\n */\r\nexport interface RunSupabaseOptions {\r\n /** Working directory */\r\n cwd?: string;\r\n /** Whether to stream output to terminal */\r\n stream?: boolean;\r\n /** Environment variables to pass */\r\n env?: Record<string, string>;\r\n /** Stdin input to pass to the command */\r\n input?: string;\r\n}\r\n\r\n/**\r\n * Result of running supabase CLI\r\n */\r\nexport interface SupabaseResult {\r\n /** Exit code */\r\n exitCode: number;\r\n /** Standard output */\r\n stdout: string;\r\n /** Standard error */\r\n stderr: string;\r\n /** Whether the command succeeded */\r\n success: boolean;\r\n}\r\n\r\n/**\r\n * Run a supabase CLI command\r\n *\r\n * @param args - Arguments to pass to supabase CLI\r\n * @param options - Execution options\r\n * @returns Result of the command\r\n */\r\nexport async function runSupabase(\r\n args: string[],\r\n options: RunSupabaseOptions = {}\r\n): Promise<SupabaseResult> {\r\n const { cwd, stream = true, env } = options;\r\n\r\n // Check if supabase is installed\r\n const isInstalled = await isSupabaseCLIInstalled();\r\n if (!isInstalled) {\r\n console.error(pc.red('\\u2717'), 'Supabase CLI is not installed');\r\n console.error(pc.dim(' Install it with: npm install -g supabase'));\r\n console.error(pc.dim(' Or: brew install supabase/tap/supabase'));\r\n return {\r\n exitCode: 1,\r\n stdout: '',\r\n stderr: 'Supabase CLI not installed',\r\n success: false,\r\n };\r\n }\r\n\r\n try {\r\n const execaOptions: Options = {\r\n reject: false,\r\n ...(cwd ? { cwd } : {}),\r\n ...(env ? { env } : {}),\r\n ...(stream ? { stdio: 'inherit' as const } : {}),\r\n ...(options.input ? { input: options.input } : {}),\r\n };\r\n\r\n const result = await execa('supabase', args, execaOptions);\r\n\r\n return {\r\n exitCode: result.exitCode ?? 0,\r\n stdout: typeof result.stdout === 'string' ? result.stdout : '',\r\n stderr: typeof result.stderr === 'string' ? result.stderr : '',\r\n success: result.exitCode === 0,\r\n };\r\n } catch (error) {\r\n // This shouldn't happen with reject: false, but handle it anyway\r\n const message = error instanceof Error ? error.message : String(error);\r\n return {\r\n exitCode: 1,\r\n stdout: '',\r\n stderr: message,\r\n success: false,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Run supabase CLI and exit with its exit code\r\n * Useful for passthrough commands\r\n */\r\nexport async function runSupabaseAndExit(\r\n args: string[],\r\n options: RunSupabaseOptions = {}\r\n): Promise<never> {\r\n const result = await runSupabase(args, options);\r\n process.exit(result.exitCode);\r\n}\r\n\r\n/**\r\n * Check supabase CLI installation and exit if not found\r\n */\r\nexport async function requireSupabaseCLI(): Promise<void> {\r\n const isInstalled = await isSupabaseCLIInstalled();\r\n if (!isInstalled) {\r\n console.error(pc.red('\\u2717'), 'Supabase CLI is not installed');\r\n console.error();\r\n console.error(pc.dim('Install it with one of:'));\r\n console.error(pc.dim(' npm install -g supabase'));\r\n console.error(pc.dim(' brew install supabase/tap/supabase'));\r\n console.error(pc.dim(' scoop install supabase'));\r\n console.error();\r\n process.exit(1);\r\n }\r\n}\r\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { readFile, writeFile, mkdir, chmod } from 'node:fs/promises';\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\n\n/**\n * Environment variable for access token\n */\nconst TOKEN_ENV_VAR = 'SUPABASE_ACCESS_TOKEN';\n\n/**\n * Config directory name\n */\nconst CONFIG_DIR = '.supacontrol';\n\n/**\n * Credentials file name\n */\nconst CREDENTIALS_FILE = 'credentials';\n\n/**\n * URL for generating access tokens\n */\nconst TOKEN_URL = 'https://supabase.com/dashboard/account/tokens';\n\n/**\n * Get the path to the credentials file\n */\nfunction getCredentialsPath(): string {\n return join(homedir(), CONFIG_DIR, CREDENTIALS_FILE);\n}\n\n/**\n * Get the config directory path\n */\nfunction getConfigDir(): string {\n return join(homedir(), CONFIG_DIR);\n}\n\n/**\n * Get access token from environment or file\n *\n * @returns Access token, or null if not found\n */\nexport async function getAccessToken(): Promise<string | null> {\n // Check environment variable first\n const envToken = process.env[TOKEN_ENV_VAR];\n if (envToken) {\n return envToken;\n }\n\n // Check credentials file\n try {\n const credentialsPath = getCredentialsPath();\n const content = await readFile(credentialsPath, 'utf-8');\n const token = content.trim();\n if (token) {\n return token;\n }\n } catch {\n // File doesn't exist or can't be read\n }\n\n return null;\n}\n\n/**\n * Save access token to credentials file\n *\n * @param token - Access token to save\n */\nexport async function saveAccessToken(token: string): Promise<void> {\n const configDir = getConfigDir();\n const credentialsPath = getCredentialsPath();\n\n // Ensure config directory exists\n await mkdir(configDir, { recursive: true });\n\n // Write token to file\n await writeFile(credentialsPath, token, 'utf-8');\n\n // Set restrictive permissions (owner read/write only)\n // Note: chmod doesn't work the same on Windows, but we'll call it anyway\n try {\n await chmod(credentialsPath, 0o600);\n } catch {\n // Ignore permission errors on Windows\n }\n}\n\n/**\n * Prompt user for access token\n *\n * @returns Access token, or null if cancelled\n */\nexport async function promptForToken(): Promise<string | null> {\n console.log();\n p.note(\n [\n 'To fetch your Supabase projects, we need an access token.',\n '',\n `Generate one at: ${pc.cyan(TOKEN_URL)}`,\n '',\n 'Select \"Generate new token\" and copy the token.',\n ].join('\\n'),\n 'Authentication Required'\n );\n\n const token = await p.password({\n message: 'Paste your Supabase access token:',\n validate(value) {\n if (!value || value.length < 10) {\n return 'Please enter a valid access token';\n }\n return undefined;\n },\n });\n\n if (p.isCancel(token)) {\n return null;\n }\n\n return token;\n}\n\n/**\n * Get access token, prompting if needed\n *\n * @param options - Options for token retrieval\n * @returns Access token, or null if not available\n */\nexport async function getOrPromptForToken(options?: {\n skipPrompt?: boolean;\n saveToken?: boolean;\n}): Promise<string | null> {\n const { skipPrompt = false, saveToken = true } = options ?? {};\n\n // Try to get existing token\n const existingToken = await getAccessToken();\n if (existingToken) {\n return existingToken;\n }\n\n // Don't prompt if skipPrompt is true (e.g., CI mode)\n if (skipPrompt) {\n return null;\n }\n\n // Prompt for token\n const token = await promptForToken();\n if (!token) {\n return null;\n }\n\n // Offer to save token\n if (saveToken) {\n const shouldSave = await p.confirm({\n message: 'Save token for future use?',\n initialValue: true,\n });\n\n if (!p.isCancel(shouldSave) && shouldSave) {\n await saveAccessToken(token);\n console.log(pc.dim(` Saved to ${getCredentialsPath()}`));\n }\n }\n\n return token;\n}\n\n/**\n * Check if we have a stored access token\n */\nexport async function hasStoredToken(): Promise<boolean> {\n const token = await getAccessToken();\n return token !== null;\n}\n\n/**\n * Clear stored access token\n */\nexport async function clearStoredToken(): Promise<void> {\n const credentialsPath = getCredentialsPath();\n try {\n await writeFile(credentialsPath, '', 'utf-8');\n } catch {\n // Ignore errors\n }\n}\n","import { z } from 'zod';\n\n/**\n * Supabase Management API base URL\n */\nconst API_BASE = 'https://api.supabase.com/v1';\n\n/**\n * Rate limit: 120 requests per minute\n */\nconst RATE_LIMIT_PER_MINUTE = 120;\n\n/**\n * Project status enum\n */\nexport const ProjectStatus = z.enum([\n 'ACTIVE_HEALTHY',\n 'ACTIVE_UNHEALTHY',\n 'COMING_UP',\n 'GOING_DOWN',\n 'INACTIVE',\n 'INIT_FAILED',\n 'REMOVED',\n 'RESTORING',\n 'UNKNOWN',\n 'UPGRADING',\n 'PAUSING',\n 'PAUSED',\n]);\n\nexport type ProjectStatus = z.infer<typeof ProjectStatus>;\n\n/**\n * Organization plan types\n * - free: No branching capability\n * - pro/team/enterprise: Branching available\n */\nexport const OrganizationPlan = z.enum(['free', 'pro', 'team', 'enterprise', 'platform']);\nexport type OrganizationPlan = z.infer<typeof OrganizationPlan>;\n\n/**\n * Organization schema (from GET /v1/organizations/{slug})\n */\nexport const OrganizationSchema = z.object({\n id: z.string(),\n name: z.string(),\n plan: OrganizationPlan.optional(),\n // Additional fields from detail endpoint\n allowed_release_channels: z.array(z.string()).optional(),\n opt_in_tags: z.array(z.string()).optional(),\n});\n\nexport type Organization = z.infer<typeof OrganizationSchema>;\n\n/**\n * Supabase project schema\n */\nexport const ProjectSchema = z.object({\n id: z.string(),\n organization_id: z.string(),\n organization_slug: z.string().optional(),\n name: z.string(),\n region: z.string(),\n created_at: z.string(),\n database: z.object({\n host: z.string(),\n version: z.string(),\n }).optional(),\n status: ProjectStatus,\n // Branching support (Pro plan feature)\n is_branch_enabled: z.boolean().optional(),\n preview_branch_refs: z.array(z.string()).optional(),\n});\n\nexport type Project = z.infer<typeof ProjectSchema>;\n\n/**\n * Branch schema\n */\nexport const BranchSchema = z.object({\n id: z.string(),\n name: z.string(),\n project_ref: z.string(),\n parent_project_ref: z.string(),\n is_default: z.boolean(),\n status: z.string(),\n created_at: z.string().optional(),\n});\n\nexport type Branch = z.infer<typeof BranchSchema>;\n\n/**\n * API error response\n */\nexport class SupabaseAPIError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly response?: unknown\n ) {\n super(message);\n this.name = 'SupabaseAPIError';\n }\n}\n\n/**\n * Supabase Management API client\n */\nexport class SupabaseManagementClient {\n private accessToken: string;\n private requestCount = 0;\n private requestWindowStart = Date.now();\n\n constructor(accessToken: string) {\n this.accessToken = accessToken;\n }\n\n /**\n * Make an authenticated API request\n */\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n // Simple rate limiting check\n await this.checkRateLimit();\n\n const url = `${API_BASE}${endpoint}`;\n const response = await fetch(url, {\n ...options,\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n });\n\n this.requestCount++;\n\n if (!response.ok) {\n let errorMessage = `API request failed: ${response.status} ${response.statusText}`;\n let responseBody: unknown;\n\n try {\n responseBody = await response.json();\n if (typeof responseBody === 'object' && responseBody !== null && 'message' in responseBody) {\n errorMessage = String((responseBody as { message: unknown }).message);\n }\n } catch {\n // Ignore JSON parse errors\n }\n\n if (response.status === 401) {\n throw new SupabaseAPIError(\n 'Invalid or expired access token. Run `supabase login` to re-authenticate.',\n response.status,\n responseBody\n );\n }\n\n if (response.status === 429) {\n throw new SupabaseAPIError(\n 'Rate limit exceeded. Please wait a moment and try again.',\n response.status,\n responseBody\n );\n }\n\n throw new SupabaseAPIError(errorMessage, response.status, responseBody);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Check and enforce rate limiting\n */\n private async checkRateLimit(): Promise<void> {\n const now = Date.now();\n const windowDuration = 60 * 1000; // 1 minute\n\n // Reset counter if window has passed\n if (now - this.requestWindowStart > windowDuration) {\n this.requestCount = 0;\n this.requestWindowStart = now;\n }\n\n // If we've hit the limit, wait for the window to reset\n if (this.requestCount >= RATE_LIMIT_PER_MINUTE) {\n const waitTime = windowDuration - (now - this.requestWindowStart);\n if (waitTime > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitTime));\n this.requestCount = 0;\n this.requestWindowStart = Date.now();\n }\n }\n }\n\n /**\n * Validate that the access token works\n */\n async authenticate(): Promise<boolean> {\n try {\n await this.getProjects();\n return true;\n } catch (error) {\n if (error instanceof SupabaseAPIError && error.statusCode === 401) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Get all projects for the authenticated user\n */\n async getProjects(): Promise<Project[]> {\n const response = await this.request<unknown[]>('/projects');\n \n // Validate and filter valid projects\n const projects: Project[] = [];\n for (const item of response) {\n const result = ProjectSchema.safeParse(item);\n if (result.success) {\n projects.push(result.data);\n }\n }\n\n return projects;\n }\n\n /**\n * Get a single project by reference ID\n */\n async getProject(projectRef: string): Promise<Project | null> {\n try {\n const response = await this.request<unknown>(`/projects/${projectRef}`);\n const result = ProjectSchema.safeParse(response);\n return result.success ? result.data : null;\n } catch (error) {\n if (error instanceof SupabaseAPIError && error.statusCode === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Get project by name (searches all projects)\n */\n async getProjectByName(name: string): Promise<Project | null> {\n const projects = await this.getProjects();\n return projects.find((p) => p.name.toLowerCase() === name.toLowerCase()) ?? null;\n }\n\n /**\n * Get organization details by slug\n * The detail endpoint includes the 'plan' field which is not in the list endpoint\n */\n async getOrganization(slug: string): Promise<Organization | null> {\n try {\n const response = await this.request<unknown>(`/organizations/${slug}`);\n const result = OrganizationSchema.safeParse(response);\n return result.success ? result.data : null;\n } catch (error) {\n if (error instanceof SupabaseAPIError && error.statusCode === 404) {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Get organization for a project\n * Fetches the organization details using the project's organization_slug or organization_id\n */\n async getOrganizationForProject(project: Project): Promise<Organization | null> {\n // Prefer organization_slug if available (from /v1/projects response)\n const slug = project.organization_slug || project.organization_id;\n return this.getOrganization(slug);\n }\n\n /**\n * Get branches for a project\n * Returns empty array if branching is not enabled (403)\n */\n async getBranches(projectRef: string): Promise<Branch[]> {\n try {\n const response = await this.request<unknown[]>(`/projects/${projectRef}/branches`);\n \n const branches: Branch[] = [];\n for (const item of response) {\n const result = BranchSchema.safeParse(item);\n if (result.success) {\n branches.push(result.data);\n }\n }\n return branches;\n } catch (error) {\n // 403 = branching not enabled, 400 = not available\n if (error instanceof SupabaseAPIError && (error.statusCode === 403 || error.statusCode === 400)) {\n return [];\n }\n throw error;\n }\n }\n\n /**\n * Create a new branch for a project\n * @param projectRef - Parent project reference\n * @param branchName - Name for the new branch\n * @returns The created branch\n * @throws SupabaseAPIError if branch creation fails\n */\n async createBranch(projectRef: string, branchName: string): Promise<Branch | null> {\n const response = await this.request<unknown>(`/projects/${projectRef}/branches`, {\n method: 'POST',\n body: JSON.stringify({ branch_name: branchName }),\n });\n \n const result = BranchSchema.safeParse(response);\n return result.success ? result.data : null;\n }\n\n /**\n * Delete a branch\n * @param projectRef - Parent project reference\n * @param branchId - Branch ID to delete\n */\n async deleteBranch(projectRef: string, branchId: string): Promise<boolean> {\n try {\n await this.request<void>(`/projects/${projectRef}/branches/${branchId}`, {\n method: 'DELETE',\n });\n return true;\n } catch (error) {\n if (error instanceof SupabaseAPIError && error.statusCode === 404) {\n // Branch already deleted or doesn't exist\n return true;\n }\n return false;\n }\n }\n\n /**\n * Check if branching is available for a project.\n * \n * IMPORTANT: This method uses READ-ONLY operations only:\n * 1. Fetches organization details to check the plan (free vs pro/team/enterprise)\n * 2. Fetches existing branches for the UX flow\n * \n * Branching is available on Pro, Team, and Enterprise plans only.\n * Free plan projects cannot use branching.\n * \n * @returns Object with availability status, plan info, and existing branches\n */\n async checkBranchingCapability(project: Project): Promise<{\n available: boolean;\n plan: OrganizationPlan | 'unknown';\n branches: Branch[];\n reason: string;\n }> {\n // Step 1: Get organization to check the plan\n let plan: OrganizationPlan | 'unknown' = 'unknown';\n \n try {\n const org = await this.getOrganizationForProject(project);\n if (org?.plan) {\n plan = org.plan;\n }\n } catch {\n // If we can't fetch org, continue with unknown plan\n }\n\n // Step 2: Determine availability based on plan\n // - free: No branching\n // - pro/team/enterprise/platform: Branching available\n // - unknown: Fall back to checking for existing branches\n const paidPlans: OrganizationPlan[] = ['pro', 'team', 'enterprise', 'platform'];\n const isPaidPlan = plan !== 'unknown' && paidPlans.includes(plan as OrganizationPlan);\n const isFreePlan = plan === 'free';\n\n // Step 3: Fetch existing branches (needed for UX flow regardless of plan)\n let branches: Branch[] = [];\n try {\n branches = await this.getBranches(project.id);\n } catch {\n // If we can't fetch branches, continue with empty array\n }\n\n // Step 4: Return result based on plan\n if (isFreePlan) {\n return {\n available: false,\n plan,\n branches,\n reason: 'free_plan',\n };\n }\n\n if (isPaidPlan) {\n return {\n available: true,\n plan,\n branches,\n reason: 'paid_plan',\n };\n }\n\n // Plan is unknown - fall back to heuristic based on existing branches\n // If there are non-default branches, branching must be available\n const nonDefaultBranches = branches.filter(b => !b.is_default);\n if (nonDefaultBranches.length > 0) {\n return {\n available: true,\n plan,\n branches,\n reason: 'has_non_default_branches',\n };\n }\n\n // Unknown plan and no non-default branches - we can't determine capability\n // Report as unavailable but the init flow can still offer to try\n return {\n available: false,\n plan,\n branches,\n reason: 'unknown_capability',\n };\n }\n\n /**\n * @deprecated Use checkBranchingCapability instead for accurate results\n */\n async isBranchingAvailable(project: Project): Promise<boolean> {\n const result = await this.checkBranchingCapability(project);\n return result.available;\n }\n}\n\n/**\n * Create a client with the given access token\n */\nexport function createSupabaseClient(accessToken: string): SupabaseManagementClient {\n return new SupabaseManagementClient(accessToken);\n}\n","import { Command } from 'commander';\nimport pc from 'picocolors';\nimport { loadConfigOrExit } from '../config/loader.js';\nimport { resolveEnvironmentByProjectRef, getEnvironmentByName } from '../config/resolver.js';\nimport { getCurrentBranch, hasUncommittedChanges, clearGitCache } from '../utils/git.js';\nimport { runSupabase, requireSupabaseCLI } from '../utils/supabase.js';\nimport { runGuards, buildGuardContext, clearProjectCache, getCurrentLinkedProject } from '../guards/index.js';\nimport { interactiveMigrationSync } from '../utils/migrations.js';\nimport type { GlobalOptions } from '../index.js';\n\ninterface PushOptions extends GlobalOptions {\n force?: boolean;\n dryRun?: boolean;\n iKnowWhatImDoing?: boolean;\n}\n\n/**\n * Create the push command\n */\nexport function createPushCommand(): Command {\n return new Command('push')\n .description('Push local migrations to the remote database')\n .option('--force', 'Bypass all safety guards (use with caution)', false)\n .option('--dry-run', 'Show what would be pushed without executing', false)\n .option(\n '--i-know-what-im-doing',\n 'Required flag for production operations in CI mode',\n false\n )\n .action(async function (this: Command) {\n const opts = this.optsWithGlobals<PushOptions>();\n await runPush(opts);\n });\n}\n\n/**\n * Run the push command\n */\nasync function runPush(options: PushOptions): Promise<void> {\n // Clear caches at start of command\n clearGitCache();\n clearProjectCache();\n\n // Check supabase CLI is available\n await requireSupabaseCLI();\n\n // Load config\n const config = await loadConfigOrExit();\n\n // Get current git state\n const gitBranch = await getCurrentBranch();\n const uncommittedChanges = await hasUncommittedChanges();\n\n // Resolve environment from linked project (not git branch)\n let resolved;\n if (options.env) {\n // Explicit --env flag takes precedence\n resolved = getEnvironmentByName(options.env, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), `Environment '${options.env}' not found in config`);\n console.error(pc.dim(' Available environments:'), Object.keys(config.environments).join(', '));\n process.exit(1);\n }\n } else {\n // Resolve from currently linked Supabase project\n const linkedRef = await getCurrentLinkedProject();\n if (!linkedRef) {\n console.error(pc.red('\\u2717'), 'No Supabase project linked');\n console.error(pc.dim(' Run: supacontrol switch <environment>'));\n process.exit(1);\n }\n \n resolved = resolveEnvironmentByProjectRef(linkedRef, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), 'Linked project is not configured in supacontrol.toml');\n console.error(pc.dim(` Linked to: ${linkedRef}`));\n console.error(pc.dim(' Run: supacontrol init or add this project to your config'));\n process.exit(1);\n }\n }\n\n // Force mode bypasses guards\n if (options.force) {\n console.log(pc.yellow('\\u26A0'), 'Force mode: bypassing all safety guards');\n console.log();\n } else {\n // Run guards\n const context = buildGuardContext({\n operation: 'push',\n environmentName: resolved.name,\n environment: resolved.config,\n config,\n gitBranch,\n isCI: options.ci,\n hasUncommittedChanges: uncommittedChanges,\n });\n\n const guardResult = await runGuards(context);\n\n if (!guardResult.allowed) {\n if (guardResult.cancelled) {\n process.exit(0);\n }\n process.exit(1);\n }\n\n // In CI mode with confirmation needed, require --i-know-what-im-doing\n if (options.ci && guardResult.requiresConfirmation && !options.iKnowWhatImDoing) {\n console.error(pc.red('\\u2717'), 'CI mode requires --i-know-what-im-doing flag for this operation');\n process.exit(1);\n }\n }\n\n // Check migration sync before pushing\n if (!options.force) {\n const syncResult = await interactiveMigrationSync();\n if (!syncResult.success) {\n if (syncResult.cancelled) {\n process.exit(0);\n }\n console.log();\n console.log(pc.dim('Use --force to push anyway (not recommended)'));\n process.exit(1);\n }\n }\n\n // Show migration diff if configured (but suppress the verbose output)\n if (config.settings.show_migration_diff && !options.dryRun) {\n console.log(pc.blue('\\u2192'), 'Checking for pending migrations...');\n\n // Run diff without streaming to capture output\n const diffResult = await runSupabase(['db', 'diff'], { stream: false });\n \n if (diffResult.success && diffResult.stdout) {\n // Check if there are actual schema changes (not just noise)\n const hasChanges = diffResult.stdout.trim().length > 0;\n if (hasChanges) {\n // Count the types of changes for a summary\n const lines = diffResult.stdout.split('\\n');\n const createCount = lines.filter(l => l.trim().startsWith('create ')).length;\n const alterCount = lines.filter(l => l.trim().startsWith('alter ')).length;\n const dropCount = lines.filter(l => l.trim().startsWith('drop ')).length;\n \n if (createCount > 0 || alterCount > 0 || dropCount > 0) {\n console.log(pc.dim(` Found schema differences: ${createCount} create, ${alterCount} alter, ${dropCount} drop`));\n } else {\n console.log(pc.dim(' Schema is in sync'));\n }\n } else {\n console.log(pc.dim(' No pending schema changes'));\n }\n } else {\n console.log(pc.dim(' Could not check for schema changes'));\n }\n console.log();\n }\n\n // Dry run mode\n if (options.dryRun) {\n console.log(pc.yellow('\\u26A0'), 'Dry run mode: no changes will be made');\n console.log();\n console.log('Would execute:');\n console.log(pc.dim(' supabase db push'));\n console.log();\n return;\n }\n\n // Execute push with --yes to auto-confirm (we already did our own confirmation via guards)\n console.log(pc.blue('\\u2192'), 'Pushing migrations to', pc.cyan(resolved.name));\n console.log();\n\n const result = await runSupabase(['db', 'push', '--yes'], { stream: true });\n\n if (result.success) {\n console.log();\n console.log(pc.green('\\u2713'), 'Push completed successfully');\n } else {\n console.log();\n console.error(pc.red('\\u2717'), 'Push failed');\n process.exit(result.exitCode);\n }\n}\n","import pc from 'picocolors';\nimport { checkLock } from './lock-guard.js';\nimport { checkOperation, getOperationRiskLevel } from './operation-guard.js';\nimport { checkProjectMatch } from './project-guard.js';\nimport { checkCleanGit } from './git-guard.js';\nimport { requestConfirmation, showOperationSummary } from './confirm.js';\nimport {\n combineResults,\n type GuardContext,\n type GuardResult,\n type OperationType,\n} from './types.js';\n\n// Re-export types and utilities\nexport * from './types.js';\nexport { requestConfirmation, showOperationSummary } from './confirm.js';\nexport { getCurrentLinkedProject, clearProjectCache } from './project-guard.js';\nexport { getOperationRiskLevel } from './operation-guard.js';\n\n/**\n * Result of running all guards\n */\nexport interface GuardRunResult extends GuardResult {\n /** Whether confirmation was given (if required) */\n confirmed?: boolean;\n /** Whether user cancelled the operation */\n cancelled?: boolean;\n}\n\n/**\n * Run all guards in sequence and optionally request confirmation\n *\n * Order of guards:\n * 1. Lock guard - environment must not be locked\n * 2. Operation guard - check if operation is protected\n * 3. Project guard - verify linked project matches\n * 4. Git guard - verify clean working directory\n *\n * If all guards pass and confirmation is required, prompts the user.\n */\nexport async function runGuards(context: GuardContext): Promise<GuardRunResult> {\n const results: GuardResult[] = [];\n\n // 1. Lock guard - most important, check first\n const lockResult = checkLock(context);\n if (!lockResult.allowed) {\n printGuardError(lockResult);\n return lockResult;\n }\n results.push(lockResult);\n\n // 2. Operation guard - check if this op is protected\n const operationResult = checkOperation(context);\n if (!operationResult.allowed) {\n printGuardError(operationResult);\n return operationResult;\n }\n results.push(operationResult);\n\n // 3. Project guard - verify we're on the right project\n const projectResult = await checkProjectMatch(context);\n if (!projectResult.allowed) {\n printGuardError(projectResult);\n return projectResult;\n }\n results.push(projectResult);\n\n // 4. Git guard - verify clean working directory\n const gitResult = checkCleanGit(context);\n if (!gitResult.allowed) {\n printGuardError(gitResult);\n return gitResult;\n }\n results.push(gitResult);\n\n // Combine all results\n const combined = combineResults(results);\n\n // If confirmation is required, prompt the user\n if (combined.requiresConfirmation) {\n const riskLevel = combined.riskLevel ?? getOperationRiskLevel(context.operation);\n\n const { confirmed, cancelled } = await requestConfirmation({\n environmentName: context.environmentName,\n operation: context.operation,\n riskLevel,\n confirmWord: combined.confirmWord,\n isCI: context.isCI,\n reason: combined.reason,\n });\n\n if (cancelled) {\n return {\n ...combined,\n allowed: false,\n cancelled: true,\n };\n }\n\n if (!confirmed) {\n return {\n ...combined,\n allowed: false,\n reason: 'Confirmation declined',\n };\n }\n\n return {\n ...combined,\n allowed: true,\n confirmed: true,\n };\n }\n\n // Show summary even when no confirmation needed\n showOperationSummary(\n context.operation,\n context.environmentName,\n context.environment.project_ref,\n combined.riskLevel ?? 'low'\n );\n\n return combined;\n}\n\n/**\n * Print guard error with formatting\n */\nfunction printGuardError(result: GuardResult): void {\n console.error();\n console.error(pc.red('\\u2717'), result.reason ?? 'Operation blocked');\n\n if (result.suggestions && result.suggestions.length > 0) {\n console.error();\n console.error(pc.dim('Suggestions:'));\n for (const suggestion of result.suggestions) {\n console.error(pc.dim(` \\u2022 ${suggestion}`));\n }\n }\n\n console.error();\n}\n\n/**\n * Build guard context from common parameters\n */\nexport function buildGuardContext(params: {\n operation: OperationType;\n environmentName: string;\n environment: GuardContext['environment'];\n config: GuardContext['config'];\n gitBranch: string | null;\n isCI: boolean;\n hasUncommittedChanges: boolean;\n}): GuardContext {\n return params;\n}\n","import { isEnvironmentLocked } from '../config/schema.js';\r\nimport { blocked, allowed, DESTRUCTIVE_OPERATIONS, type GuardContext, type GuardResult } from './types.js';\r\n\r\n/**\r\n * Check if an environment is locked and block destructive operations\r\n *\r\n * Production environments are locked by default if not explicitly set.\r\n * Locked environments block ALL destructive operations.\r\n */\r\nexport function checkLock(context: GuardContext): GuardResult {\r\n const { environmentName, environment, operation } = context;\r\n\r\n // Check if this operation type is affected by locks\r\n // Read-only operations like 'diff' are always allowed\r\n if (!DESTRUCTIVE_OPERATIONS.includes(operation)) {\r\n return allowed();\r\n }\r\n\r\n const isLocked = isEnvironmentLocked(environmentName, environment);\r\n\r\n if (isLocked) {\r\n return blocked(\r\n `Environment '${environmentName}' is locked`,\r\n {\r\n suggestions: [\r\n `Set 'locked = false' in supacontrol.toml for [environments.${environmentName}]`,\r\n `Or use --force flag to override (not recommended for production)`,\r\n ],\r\n riskLevel: 'critical',\r\n }\r\n );\r\n }\r\n\r\n return allowed();\r\n}\r\n\r\n/**\r\n * Guard name for logging/debugging\r\n */\r\nexport const GUARD_NAME = 'lock-guard';\r\n","import type { ProtectedOperation } from '../config/schema.js';\nimport {\n allowed,\n requiresConfirmation,\n type GuardContext,\n type GuardResult,\n type RiskLevel,\n type OperationType,\n} from './types.js';\n\n/**\n * Risk levels for each operation type\n */\nconst OPERATION_RISK: Record<OperationType, RiskLevel> = {\n diff: 'low',\n pull: 'low',\n push: 'medium',\n migrate: 'medium',\n seed: 'high',\n reset: 'critical',\n link: 'low',\n unlink: 'medium',\n};\n\n/**\n * Check if an operation is protected and requires confirmation\n */\nexport function checkOperation(context: GuardContext): GuardResult {\n const { operation, environment, environmentName, isCI } = context;\n\n const riskLevel = OPERATION_RISK[operation] ?? 'medium';\n\n // Check if this operation is in the protected list\n const isProtected = environment.protected_operations.includes(\n operation as ProtectedOperation\n );\n\n if (!isProtected) {\n // Not protected, but still include risk level\n return allowed({ riskLevel });\n }\n\n // Operation is protected - requires confirmation\n const confirmWord = environment.confirm_word ?? environmentName;\n\n // In CI mode, we need explicit flag instead of interactive confirmation\n if (isCI) {\n return requiresConfirmation(confirmWord, {\n reason: `Operation '${operation}' on '${environmentName}' requires confirmation`,\n riskLevel,\n });\n }\n\n return requiresConfirmation(confirmWord, {\n reason: `This operation is protected. Type '${confirmWord}' to confirm.`,\n riskLevel,\n });\n}\n\n/**\n * Get the risk level for an operation\n */\nexport function getOperationRiskLevel(operation: OperationType): RiskLevel {\n return OPERATION_RISK[operation] ?? 'medium';\n}\n\n/**\n * Guard name for logging/debugging\n */\nexport const GUARD_NAME = 'operation-guard';\n","import { blocked, allowed, DESTRUCTIVE_OPERATIONS, type GuardContext, type GuardResult } from './types.js';\r\n\r\n/**\r\n * Check if git working directory is clean when required\r\n *\r\n * This guard enforces the require_clean_git setting from config.\r\n * If enabled, blocks operations when there are uncommitted changes.\r\n */\r\nexport function checkCleanGit(context: GuardContext): GuardResult {\r\n const { config, hasUncommittedChanges, operation } = context;\r\n\r\n // If setting is disabled, skip this check\r\n if (!config.settings.require_clean_git) {\r\n return allowed();\r\n }\r\n\r\n // Only check for destructive operations\r\n if (!DESTRUCTIVE_OPERATIONS.includes(operation)) {\r\n return allowed();\r\n }\r\n\r\n if (hasUncommittedChanges) {\r\n return blocked(\r\n 'Git working directory has uncommitted changes',\r\n {\r\n suggestions: [\r\n 'Run `git stash` to temporarily store changes',\r\n 'Or run `git commit` to commit your changes',\r\n 'Or set `require_clean_git = false` in supacontrol.toml',\r\n ],\r\n riskLevel: 'medium',\r\n }\r\n );\r\n }\r\n\r\n return allowed();\r\n}\r\n\r\n/**\r\n * Guard name for logging/debugging\r\n */\r\nexport const GUARD_NAME = 'git-guard';\r\n","import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport type { RiskLevel, OperationType } from './types.js';\n\n/**\n * Risk level colors and labels\n */\nconst RISK_DISPLAY: Record<RiskLevel, { color: (s: string) => string; label: string }> = {\n low: { color: pc.blue, label: 'Low Risk' },\n medium: { color: pc.yellow, label: 'Medium Risk' },\n high: { color: pc.red, label: 'High Risk' },\n critical: { color: (s) => pc.bold(pc.red(s)), label: 'CRITICAL RISK' },\n};\n\n/**\n * Operation descriptions for confirmation prompts\n */\nconst OPERATION_DESCRIPTIONS: Record<OperationType, string> = {\n push: 'Push local migrations to the remote database',\n reset: 'Reset the remote database to match local migrations',\n pull: 'Pull remote schema changes to local migrations',\n seed: 'Run seed data on the remote database',\n migrate: 'Run database migrations',\n diff: 'Show differences between local and remote schemas',\n link: 'Link to a Supabase project',\n unlink: 'Unlink from the current Supabase project',\n};\n\n/**\n * Options for confirmation request\n */\nexport interface ConfirmationOptions {\n environmentName: string;\n operation: OperationType;\n riskLevel: RiskLevel;\n confirmWord: string | undefined;\n isCI: boolean;\n reason: string | undefined;\n}\n\n/**\n * Result of confirmation request\n */\nexport interface ConfirmationResult {\n confirmed: boolean;\n cancelled?: boolean;\n}\n\n/**\n * Request user confirmation for an operation\n */\nexport async function requestConfirmation(\n options: ConfirmationOptions\n): Promise<ConfirmationResult> {\n const { environmentName, operation, riskLevel, confirmWord, isCI, reason } = options;\n\n const riskDisplay = RISK_DISPLAY[riskLevel];\n const description = OPERATION_DESCRIPTIONS[operation] ?? operation;\n\n // In CI mode, we can't prompt - just fail\n if (isCI) {\n console.error(pc.red('\\u2717'), 'Cannot confirm interactively in CI mode');\n console.error(\n pc.dim(' Use --i-know-what-im-doing flag to bypass confirmation')\n );\n return { confirmed: false };\n }\n\n // Show warning box\n p.note(\n [\n riskDisplay.color(`${riskDisplay.label}`),\n '',\n `Operation: ${pc.bold(operation)}`,\n `Environment: ${pc.bold(environmentName)}`,\n `Description: ${description}`,\n reason ? `\\nReason: ${reason}` : '',\n ]\n .filter(Boolean)\n .join('\\n'),\n riskDisplay.color('\\u26A0 Confirmation Required')\n );\n\n // For critical operations, require typing the confirm word\n if (riskLevel === 'critical' || confirmWord) {\n const word = confirmWord ?? environmentName;\n\n const response = await p.text({\n message: `Type '${pc.bold(word)}' to confirm:`,\n placeholder: word,\n validate(value) {\n if (value !== word) {\n return `Please type exactly '${word}' to confirm`;\n }\n return undefined;\n },\n });\n\n if (p.isCancel(response)) {\n p.cancel('Operation cancelled');\n return { confirmed: false, cancelled: true };\n }\n\n return { confirmed: response === word };\n }\n\n // For non-critical, just yes/no\n const confirmed = await p.confirm({\n message: 'Do you want to proceed?',\n initialValue: false,\n });\n\n if (p.isCancel(confirmed)) {\n p.cancel('Operation cancelled');\n return { confirmed: false, cancelled: true };\n }\n\n return { confirmed };\n}\n\n/**\n * Show a summary of the operation before executing\n */\nexport function showOperationSummary(\n operation: OperationType,\n environmentName: string,\n projectRef: string | undefined,\n riskLevel: RiskLevel\n): void {\n const riskDisplay = RISK_DISPLAY[riskLevel];\n\n console.log();\n console.log(\n pc.blue('\\u2192'),\n pc.bold(operation),\n 'on',\n riskDisplay.color(environmentName)\n );\n\n if (projectRef) {\n console.log(pc.dim(` Project: ${projectRef}`));\n }\n\n console.log(pc.dim(` Risk: ${riskDisplay.label}`));\n console.log();\n}\n","import { readdir, readFile, writeFile, mkdir, rename } from 'node:fs/promises';\r\nimport { resolve, join, basename } from 'node:path';\r\nimport { createHash } from 'node:crypto';\r\nimport pc from 'picocolors';\r\nimport * as p from '@clack/prompts';\r\nimport { runSupabase } from './supabase.js';\r\n\r\n// Note: generateMigrationTimestamp was removed - no longer needed with fetch-based sync\r\n\r\n/**\r\n * Calculate SHA256 hash of content\r\n */\r\nfunction hashContent(content: string): string {\r\n return createHash('sha256').update(content).digest('hex').slice(0, 16);\r\n}\r\n\r\n/**\r\n * Result of checking migration sync status\r\n */\r\nexport interface MigrationSyncStatus {\r\n /** Whether sync is needed */\r\n needsSync: boolean;\r\n /** Migrations on remote but not local (timestamps only) */\r\n remoteMissing: string[];\r\n /** Migrations on local but not remote (timestamps only) */\r\n localMissing: string[];\r\n /** Error message if check failed */\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Detailed migration info including full filename and content\r\n */\r\ninterface LocalMigration {\r\n timestamp: string;\r\n name: string;\r\n filename: string;\r\n fullPath: string;\r\n content?: string;\r\n}\r\n\r\n/**\r\n * Result of fetching remote migrations\r\n */\r\ninterface FetchResult {\r\n success: boolean;\r\n error?: string;\r\n /** Migrations fetched from remote */\r\n fetched: LocalMigration[];\r\n}\r\n\r\n/**\r\n * Conflict between local and remote migration content\r\n */\r\ninterface MigrationConflict {\r\n timestamp: string;\r\n filename: string;\r\n localPath: string;\r\n localContent: string;\r\n remoteContent: string;\r\n localHash: string;\r\n remoteHash: string;\r\n}\r\n\r\n/**\r\n * User's choice for resolving a conflict\r\n */\r\ntype ConflictResolution = 'create-migration' | 'keep-local' | 'keep-remote' | 'save-both' | 'cancel';\r\n\r\n/**\r\n * Get list of local migration files\r\n */\r\nasync function getLocalMigrations(): Promise<string[]> {\r\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\r\n \r\n try {\r\n const files = await readdir(migrationsDir);\r\n return files\r\n .filter(f => f.endsWith('.sql') && !f.endsWith('.remote.sql'))\r\n .map(f => f.replace('.sql', ''))\r\n .sort();\r\n } catch {\r\n // No migrations directory\r\n return [];\r\n }\r\n}\r\n\r\n/**\n * Get detailed local migration info\n */\nasync function getLocalMigrationDetails(): Promise<LocalMigration[]> {\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\n \n try {\n const files = await readdir(migrationsDir);\n return files\n .filter(f => f.endsWith('.sql') && !f.endsWith('.remote.sql'))\n .map(f => {\n const match = f.match(/^(\\d{14})_?(.*)\\.sql$/);\n const timestamp = match?.[1] ?? f.replace('.sql', '');\n const name = match?.[2] ?? '';\n return {\n timestamp,\n name,\n filename: f,\n fullPath: join(migrationsDir, f),\n };\n })\n .sort((a, b) => a.timestamp.localeCompare(b.timestamp));\n } catch {\n return [];\n }\n}\n\r\n/**\r\n * Get list of remote migrations from Supabase\r\n * This parses the output of `supabase migration list`\r\n */\r\nasync function getRemoteMigrations(): Promise<string[]> {\r\n const result = await runSupabase(['migration', 'list'], { stream: false });\r\n \r\n if (!result.success || !result.stdout) {\r\n return [];\r\n }\r\n \r\n // Parse migration list output\r\n // Format is:\r\n // Local | Remote | Time (UTC)\r\n // ----------------|----------------|---------------------\r\n // 20260116000044 | 20260116000044 | 2026-01-16 00:00:44 <- Both\r\n // | 20260116082525 | 2026-01-16 08:25:25 <- Remote only\r\n // 20260116123456 | | 2026-01-16 12:34:56 <- Local only\r\n //\r\n // We need to parse the REMOTE column specifically (second column)\r\n \r\n const lines = result.stdout.split('\\n');\r\n const migrations: string[] = [];\r\n \r\n for (const line of lines) {\r\n // Skip header/separator lines\r\n if (line.includes('Local') || line.includes('---') || !line.trim()) {\r\n continue;\r\n }\r\n \r\n // Split by | to get columns\n const parts = line.split('|');\n if (parts.length >= 2) {\n // Remote column is the second one (index 1)\n const remoteCol = parts[1]?.trim();\n if (remoteCol) {\n const match = remoteCol.match(/^(\\d{14})$/);\n if (match?.[1]) {\n migrations.push(match[1]);\n }\n }\n }\n }\n \r\n return migrations.sort();\r\n}\r\n\r\n/**\r\n * Check if local and remote migrations are in sync\r\n */\r\nexport async function checkMigrationSync(): Promise<MigrationSyncStatus> {\r\n try {\r\n const [local, remote] = await Promise.all([\r\n getLocalMigrations(),\r\n getRemoteMigrations(),\r\n ]);\r\n \r\n // Extract just the timestamp portion from local migrations\n // Local files are like \"20260116000044_test_dummy_table\"\n const localTimestamps: string[] = local.map(m => {\n const match = m.match(/^(\\d{14})/);\n return match?.[1] ?? m;\n });\n \n // Find migrations on remote but not local\n const remoteMissing = remote.filter(r => !localTimestamps.includes(r));\n \n // Find migrations on local but not remote\n const localMissing = localTimestamps.filter(l => l !== undefined && !remote.includes(l));\n \r\n return {\r\n needsSync: remoteMissing.length > 0,\r\n remoteMissing,\r\n localMissing,\r\n };\r\n } catch (error) {\r\n return {\r\n needsSync: false,\r\n remoteMissing: [],\r\n localMissing: [],\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Fetch remote migrations using `supabase migration fetch`\r\n * This downloads the actual SQL content from the remote history table\r\n */\r\nasync function fetchRemoteMigrations(): Promise<FetchResult> {\r\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\r\n \r\n // Ensure migrations directory exists\r\n try {\r\n await mkdir(migrationsDir, { recursive: true });\r\n } catch {\r\n // Already exists\r\n }\r\n\r\n // Fetch migrations from remote\r\n const result = await runSupabase(\r\n ['migration', 'fetch', '--linked'],\r\n { stream: false, input: 'y\\n' } // Auto-confirm the overwrite prompt\r\n );\r\n \r\n if (!result.success) {\r\n return {\r\n success: false,\r\n error: result.stderr || 'Failed to fetch migrations',\r\n fetched: [],\r\n };\r\n }\r\n \r\n // Get the list of migrations that now exist locally\r\n const fetched = await getLocalMigrationDetails();\r\n \r\n return {\r\n success: true,\r\n fetched,\r\n };\r\n}\r\n\r\n/**\r\n * Read content of a local migration file\r\n */\r\nasync function readMigrationContent(migration: LocalMigration): Promise<string> {\r\n return await readFile(migration.fullPath, 'utf-8');\r\n}\r\n\r\n/**\r\n * Find conflicts between local and remote migration content\r\n * Note: This function is kept for potential future use but currently\r\n * conflict detection is done inline in interactiveMigrationSync\r\n */\r\nasync function _findContentConflicts(\r\n localMigrations: LocalMigration[],\r\n remoteMigrations: LocalMigration[]\r\n): Promise<MigrationConflict[]> {\r\n const conflicts: MigrationConflict[] = [];\r\n \r\n // Build map of remote migrations by timestamp\r\n const remoteByTimestamp = new Map<string, LocalMigration>();\r\n for (const m of remoteMigrations) {\r\n remoteByTimestamp.set(m.timestamp, m);\r\n }\r\n \r\n // Check each local migration for conflicts\r\n for (const local of localMigrations) {\r\n const remote = remoteByTimestamp.get(local.timestamp);\r\n if (!remote) continue; // No remote version, no conflict\r\n \r\n const localContent = await readMigrationContent(local);\r\n const remoteContent = await readMigrationContent(remote);\r\n \r\n const localHash = hashContent(localContent);\r\n const remoteHash = hashContent(remoteContent);\r\n \r\n if (localHash !== remoteHash) {\r\n conflicts.push({\r\n timestamp: local.timestamp,\r\n filename: local.filename,\r\n localPath: local.fullPath,\r\n localContent,\r\n remoteContent,\r\n localHash,\r\n remoteHash,\r\n });\r\n }\r\n }\r\n \r\n return conflicts;\r\n}\r\n\r\n/**\r\n * Simple diff: find lines only in local (additions) and only in remote (removals)\r\n */\r\nfunction computeSimpleDiff(localContent: string, remoteContent: string): {\r\n additions: string[];\r\n removals: string[];\r\n} {\r\n const localLines = new Set(localContent.split('\\n').map(l => l.trim()).filter(Boolean));\r\n const remoteLines = new Set(remoteContent.split('\\n').map(l => l.trim()).filter(Boolean));\r\n \r\n const additions: string[] = [];\r\n const removals: string[] = [];\r\n \r\n // Lines in local but not in remote (additions)\r\n for (const line of localLines) {\r\n if (!remoteLines.has(line)) {\r\n additions.push(line);\r\n }\r\n }\r\n \r\n // Lines in remote but not in local (removals)\r\n for (const line of remoteLines) {\r\n if (!localLines.has(line)) {\r\n removals.push(line);\r\n }\r\n }\r\n \r\n return { additions, removals };\r\n}\r\n\r\n/**\r\n * Display a diff between local and remote content\r\n */\r\nfunction displayDiff(conflict: MigrationConflict): void {\r\n const { additions, removals } = computeSimpleDiff(conflict.localContent, conflict.remoteContent);\r\n \r\n console.log();\r\n console.log(pc.bold(` File: ${conflict.filename}`));\r\n console.log();\r\n \r\n // Show what's different\r\n if (removals.length > 0) {\r\n console.log(pc.dim(' In database (will be kept):'));\r\n for (const line of removals.slice(0, 8)) {\r\n console.log(pc.red(` - ${line.slice(0, 70)}`));\r\n }\r\n if (removals.length > 8) {\r\n console.log(pc.dim(` ... and ${removals.length - 8} more lines`));\r\n }\r\n console.log();\r\n }\r\n \r\n if (additions.length > 0) {\r\n console.log(pc.dim(' In your local file (NOT in database):'));\r\n for (const line of additions.slice(0, 8)) {\r\n console.log(pc.green(` + ${line.slice(0, 70)}`));\r\n }\r\n if (additions.length > 8) {\r\n console.log(pc.dim(` ... and ${additions.length - 8} more lines`));\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Prompt user to resolve a migration conflict\r\n */\r\nasync function resolveConflict(conflict: MigrationConflict): Promise<ConflictResolution> {\r\n console.log();\r\n console.log(pc.yellow('⚠'), pc.bold('Local file differs from applied migration'));\r\n console.log();\r\n \r\n displayDiff(conflict);\r\n \r\n console.log();\r\n p.note(\r\n [\r\n pc.bold(pc.yellow('Your local edits are NOT in the database.')),\r\n '',\r\n 'It looks like this migration file was edited after it was',\r\n 'already applied to your database. That\\'s a common mistake!',\r\n '',\r\n pc.bold('How migrations work:'),\r\n ' • Each migration runs ONCE, then is marked as \"done\"',\r\n ' • Editing the file later has no effect - it won\\'t re-run',\r\n ' • To change your schema, create a NEW migration',\r\n '',\r\n pc.dim('Example: To add a column, don\\'t edit the CREATE TABLE.'),\r\n pc.dim('Instead, create a new migration with ALTER TABLE ... ADD COLUMN.'),\r\n ].join('\\n'),\r\n 'What happened?'\r\n );\r\n\r\n const choice = await p.select({\r\n message: 'How do you want to handle this?',\r\n options: [\r\n {\r\n value: 'create-migration',\r\n label: pc.green('Create new migration from my edits') + ' ' + pc.green('(Recommended)'),\r\n hint: 'Applies your changes to the database properly',\r\n },\r\n {\r\n value: 'keep-remote',\r\n label: 'Restore file to match database',\r\n hint: 'Discards your local edits',\r\n },\r\n {\r\n value: 'save-both',\r\n label: 'Keep both versions',\r\n hint: 'Save applied version as .remote.sql for reference',\r\n },\r\n {\r\n value: 'keep-local',\r\n label: pc.dim('Keep local file as-is (not recommended)'),\r\n hint: 'File won\\'t match database - you\\'ll need to fix manually',\r\n },\r\n {\r\n value: 'cancel',\r\n label: pc.dim('Cancel'),\r\n hint: 'Abort the sync',\r\n },\r\n ],\r\n });\r\n\r\n if (p.isCancel(choice)) {\r\n return 'cancel';\r\n }\r\n \r\n return choice as ConflictResolution;\r\n}\r\n\r\n/**\r\n * Generate a timestamp for a new migration\r\n */\r\nfunction generateMigrationTimestamp(): string {\r\n const now = new Date();\r\n return [\r\n now.getUTCFullYear(),\r\n String(now.getUTCMonth() + 1).padStart(2, '0'),\r\n String(now.getUTCDate()).padStart(2, '0'),\r\n String(now.getUTCHours()).padStart(2, '0'),\r\n String(now.getUTCMinutes()).padStart(2, '0'),\r\n String(now.getUTCSeconds()).padStart(2, '0'),\r\n ].join('');\r\n}\r\n\r\n/**\r\n * Create a new migration containing the differences between local and remote\r\n */\r\nasync function createMigrationFromDiff(\r\n conflict: MigrationConflict,\r\n latestRemoteTimestamp?: string\r\n): Promise<{ success: boolean; migrationPath?: string; error?: string }> {\r\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\r\n \r\n // Calculate timestamp - must be after all existing migrations\r\n let timestamp = generateMigrationTimestamp();\r\n if (latestRemoteTimestamp && timestamp <= latestRemoteTimestamp) {\r\n // Ensure our new migration comes after the latest remote\r\n const nextTimestamp = parseInt(latestRemoteTimestamp, 10) + 1;\r\n timestamp = String(nextTimestamp).padStart(14, '0');\r\n }\r\n \r\n // Extract the base name from the original migration\r\n const nameMatch = conflict.filename.match(/^\\d{14}_(.+)\\.sql$/);\r\n const baseName = nameMatch ? nameMatch[1] : 'update';\r\n \r\n // Create a descriptive filename\r\n const newFilename = `${timestamp}_${baseName}_changes.sql`;\r\n const newPath = join(migrationsDir, newFilename);\r\n \r\n // Find the actual differences\r\n const { additions } = computeSimpleDiff(conflict.localContent, conflict.remoteContent);\r\n \r\n if (additions.length === 0) {\r\n return { \r\n success: false, \r\n error: 'No differences found to migrate' \r\n };\r\n }\r\n \r\n // Build the migration content\r\n // Note: This is a simple approach - it extracts new lines but the user\r\n // should review and potentially adjust the migration\r\n const header = [\r\n '-- Migration generated by SupaControl',\r\n `-- Based on local edits to: ${conflict.filename}`,\r\n `-- Generated: ${new Date().toISOString()}`,\r\n '--',\r\n '-- ⚠️ REVIEW THIS MIGRATION BEFORE APPLYING',\r\n '-- The following changes were detected in your local file:',\r\n '--',\r\n ];\r\n \r\n // Try to extract meaningful SQL statements\r\n const meaningfulAdditions = additions.filter(line => {\r\n const l = line.toLowerCase();\r\n return (\r\n l.includes('create ') ||\r\n l.includes('alter ') ||\r\n l.includes('add ') ||\r\n l.includes('drop ') ||\r\n l.includes('index') ||\r\n l.includes('column') ||\r\n l.includes('constraint') ||\r\n l.includes('default') ||\r\n l.includes('comment on')\r\n );\r\n });\r\n \r\n let content: string;\r\n if (meaningfulAdditions.length > 0) {\r\n content = [\r\n ...header,\r\n '',\r\n '-- Detected changes (may need manual adjustment):',\r\n ...meaningfulAdditions.map(line => `-- ${line}`),\r\n '',\r\n '-- TODO: Write the actual SQL to apply these changes',\r\n '-- Example: ALTER TABLE ... ADD COLUMN ...;',\r\n '',\r\n ].join('\\n');\r\n } else {\r\n content = [\r\n ...header,\r\n '',\r\n '-- Could not automatically extract SQL statements.',\r\n '-- Please review the diff and write the appropriate migration.',\r\n '',\r\n '-- Local file additions:',\r\n ...additions.slice(0, 20).map(line => `-- ${line}`),\r\n additions.length > 20 ? `-- ... and ${additions.length - 20} more lines` : '',\r\n '',\r\n ].filter(Boolean).join('\\n');\r\n }\r\n \r\n await writeFile(newPath, content);\r\n \r\n // Restore the original file to match remote (database state)\r\n await writeFile(conflict.localPath, conflict.remoteContent);\r\n \r\n return { success: true, migrationPath: newPath };\r\n}\r\n\r\n/**\r\n * Apply conflict resolution\r\n */\r\nasync function applyConflictResolution(\r\n conflict: MigrationConflict,\r\n resolution: ConflictResolution,\r\n latestRemoteTimestamp?: string\r\n): Promise<boolean> {\r\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\r\n \r\n switch (resolution) {\r\n case 'create-migration': {\r\n const result = await createMigrationFromDiff(conflict, latestRemoteTimestamp);\r\n if (result.success && result.migrationPath) {\r\n console.log(pc.green('✓'), `Restored ${conflict.filename} to match database`);\r\n console.log(pc.green('✓'), `Created new migration: ${basename(result.migrationPath)}`);\r\n console.log();\r\n console.log(pc.yellow('⚠'), 'Please review and edit the new migration before pushing');\r\n console.log(pc.dim(` ${result.migrationPath}`));\r\n } else if (result.error === 'No differences found to migrate') {\r\n // No meaningful differences - just restore to match database\r\n // This can happen with whitespace/formatting-only changes\r\n await writeFile(conflict.localPath, conflict.remoteContent);\r\n console.log(pc.green('✓'), `Restored ${conflict.filename} to match database`);\r\n console.log(pc.dim(' (No meaningful code differences found)'));\r\n } else {\r\n console.log(pc.red('✗'), 'Failed to create migration:', result.error);\r\n return false;\r\n }\r\n return true;\r\n }\r\n \r\n case 'keep-local':\r\n // Nothing to do - local file is already what we want\r\n console.log(pc.yellow('⚠'), `Keeping local version of ${conflict.filename}`);\r\n console.log(pc.dim(' Note: This file does not match what\\'s in your database'));\r\n return true;\r\n \r\n case 'keep-remote':\r\n // Overwrite local with remote content\r\n await writeFile(conflict.localPath, conflict.remoteContent);\r\n console.log(pc.green('✓'), `Restored ${conflict.filename} to match database`);\r\n return true;\r\n \r\n case 'save-both': {\r\n // Save remote as a .remote.sql file for reference\r\n const remotePath = join(migrationsDir, conflict.filename.replace('.sql', '.remote.sql'));\r\n await writeFile(remotePath, conflict.remoteContent);\r\n console.log(pc.yellow('⚠'), `Keeping local ${conflict.filename} (doesn't match database)`);\r\n console.log(pc.green('✓'), `Saved database version as ${basename(remotePath)}`);\r\n return true;\r\n }\r\n \r\n case 'cancel':\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Rename local-only migrations to come after the latest remote migration\r\n */\r\nasync function reorderLocalMigrations(\r\n localOnly: LocalMigration[],\r\n latestRemoteTimestamp: string\r\n): Promise<{ original: string; renamed: string }[]> {\r\n const renamed: { original: string; renamed: string }[] = [];\r\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\r\n \r\n // Sort by timestamp to process in order\r\n const sorted = [...localOnly].sort((a, b) => a.timestamp.localeCompare(b.timestamp));\r\n \r\n // Find migrations that need reordering (timestamp <= latest remote)\r\n const needsReorder = sorted.filter(m => m.timestamp <= latestRemoteTimestamp);\r\n \r\n if (needsReorder.length === 0) {\r\n return renamed;\r\n }\r\n \r\n console.log();\r\n console.log(pc.blue('→'), 'Reordering local migrations to come after remote...');\r\n \r\n // Calculate new timestamps starting 1 second after the latest remote\r\n let nextTimestamp = parseInt(latestRemoteTimestamp, 10) + 1;\r\n \r\n for (const migration of needsReorder) {\r\n const newTimestamp = String(nextTimestamp).padStart(14, '0');\r\n const newFilename = migration.filename.replace(migration.timestamp, newTimestamp);\r\n const newPath = join(migrationsDir, newFilename);\r\n \r\n // Rename the file\r\n await rename(migration.fullPath, newPath);\r\n \r\n renamed.push({\r\n original: migration.filename,\r\n renamed: newFilename,\r\n });\r\n \r\n console.log(pc.dim(` ${migration.filename}`));\r\n console.log(pc.green(` → ${newFilename}`));\r\n \r\n nextTimestamp++;\r\n }\r\n \r\n return renamed;\r\n}\r\n\r\n/**\r\n * Result of migration repair operation\r\n */\r\nexport interface RepairResult {\r\n success: boolean;\r\n error?: string;\r\n /** Remote migrations that were marked as reverted */\r\n repairedRemote: string[];\r\n}\r\n\r\n/**\r\n * Repair migration history mismatch between local and remote.\r\n * \r\n * Strategy:\r\n * - Mark remote-only migrations as 'reverted' (tells Supabase to forget them)\r\n * - Local-only migrations are left alone - they'll be pushed normally\r\n * \r\n * This allows the next push to proceed without conflicts.\r\n * Local migrations become the source of truth.\r\n */\r\nexport async function repairMigrationHistory(\r\n status: MigrationSyncStatus\r\n): Promise<RepairResult> {\r\n const result: RepairResult = {\r\n success: false,\r\n repairedRemote: [],\r\n };\r\n\r\n const spinner = p.spinner();\r\n\r\n // Mark remote-only migrations as reverted (Supabase forgets about them)\r\n if (status.remoteMissing.length > 0) {\r\n spinner.start('Marking remote-only migrations as reverted...');\r\n \r\n for (const version of status.remoteMissing) {\r\n const repairResult = await runSupabase(\r\n ['migration', 'repair', '--status', 'reverted', version],\r\n { stream: false }\r\n );\r\n \r\n if (!repairResult.success) {\r\n spinner.stop('Repair failed');\r\n result.error = `Failed to revert remote migration ${version}`;\r\n return result;\r\n }\r\n \r\n result.repairedRemote.push(version);\r\n }\r\n \r\n spinner.stop(`Reverted ${status.remoteMissing.length} remote migration(s)`);\r\n }\r\n\r\n result.success = true;\r\n return result;\r\n}\r\n\r\n/**\r\n * Sync migrations from remote to local\r\n */\r\nexport async function syncMigrations(): Promise<boolean> {\r\n console.log(pc.blue('→'), 'Pulling migrations from remote...');\r\n \r\n const result = await runSupabase(['db', 'pull'], { stream: true });\r\n \r\n return result.success;\r\n}\r\n\r\n/**\r\n * Check migration sync and provide user-friendly output\r\n * Returns true if in sync or user chose to continue\r\n */\r\nexport async function ensureMigrationSync(): Promise<boolean> {\r\n const status = await checkMigrationSync();\r\n \r\n if (status.error) {\r\n console.log(pc.yellow('⚠'), 'Could not check migration sync status');\r\n console.log(pc.dim(` ${status.error}`));\r\n return true; // Continue anyway\r\n }\r\n \r\n if (status.remoteMissing.length > 0) {\r\n console.log(pc.red('✗'), 'Remote has migrations not in your local directory:');\r\n for (const m of status.remoteMissing) {\r\n console.log(pc.dim(` - ${m}`));\r\n }\r\n console.log();\r\n console.log(pc.dim('Run `supabase db pull` to sync, or `spc switch <env>` to auto-sync'));\r\n return false;\r\n }\r\n \r\n return true;\r\n}\r\n\r\n/**\r\n * Full migration sync workflow with user interaction.\r\n * \r\n * NEW APPROACH using `supabase migration fetch`:\r\n * 1. Fetch remote migrations (downloads actual SQL files)\r\n * 2. Compare content of matched migrations (same timestamp)\r\n * 3. Resolve any conflicts with user input\r\n * 4. Reorder local-only migrations if needed (timestamp after remote)\r\n * 5. Push local-only migrations\r\n */\r\nexport async function interactiveMigrationSync(): Promise<{ success: boolean; cancelled?: boolean }> {\r\n const status = await checkMigrationSync();\r\n \r\n if (status.error) {\r\n console.log(pc.yellow('⚠'), 'Could not check migration sync status');\r\n console.log(pc.dim(` ${status.error}`));\r\n return { success: true }; // Continue anyway\r\n }\r\n\r\n // No mismatch - all good\r\n if (status.remoteMissing.length === 0 && status.localMissing.length === 0) {\r\n return { success: true };\r\n }\r\n\r\n // NORMAL CASE: Local has new migrations to push, but no conflicts\r\n // This is NOT a mismatch - just new work ready to push\r\n // BUT we still need to check timestamp ordering!\r\n if (status.remoteMissing.length === 0 && status.localMissing.length > 0) {\r\n // Get the latest remote migration timestamp\r\n const remoteMigrations = await getRemoteMigrations();\r\n const latestRemote = remoteMigrations.length > 0 \r\n ? remoteMigrations.sort()[remoteMigrations.length - 1] \r\n : null;\r\n \r\n // Check if any local-only migrations have timestamps before the latest remote\r\n const needsReorder = latestRemote && status.localMissing.some(ts => ts <= latestRemote);\r\n \r\n if (needsReorder) {\r\n console.log(pc.blue('→'), `${status.localMissing.length} new migration(s) need timestamp adjustment`);\r\n \r\n // Get full migration details for reordering\r\n const localDetails = await getLocalMigrationDetails();\r\n const localOnlyMigrations = localDetails.filter(m => status.localMissing.includes(m.timestamp));\r\n \r\n const renames = await reorderLocalMigrations(localOnlyMigrations, latestRemote);\r\n \r\n if (renames.length > 0) {\r\n console.log(pc.green('✓'), `Reordered ${renames.length} migration(s) to come after remote`);\r\n }\r\n } else {\r\n console.log(pc.blue('→'), `${status.localMissing.length} new migration(s) ready to push`);\r\n }\r\n \r\n return { success: true };\r\n }\r\n\r\n // We have remote migrations that aren't local\r\n // This is the scenario where we need to fetch and potentially resolve conflicts\r\n \r\n console.log();\r\n console.log(pc.yellow('⚠'), 'Migration sync needed');\r\n console.log();\r\n \r\n if (status.remoteMissing.length > 0) {\r\n console.log(pc.dim(' Remote has migrations not saved locally:'));\r\n for (const m of status.remoteMissing) {\r\n console.log(pc.blue(` + ${m}`));\r\n }\r\n }\r\n \r\n if (status.localMissing.length > 0) {\r\n console.log(pc.dim(' Local has migrations not on remote:'));\r\n for (const m of status.localMissing) {\r\n console.log(pc.green(` + ${m}`));\r\n }\r\n }\r\n \r\n console.log();\r\n\r\n // Explain what we're about to do\r\n p.note(\r\n [\r\n pc.bold('We will:'),\r\n '',\r\n `${pc.blue('1.')} Download ${status.remoteMissing.length} migration file(s) from remote`,\r\n `${pc.blue('2.')} Check for any content conflicts with your local files`,\r\n `${pc.blue('3.')} Let you resolve any conflicts`,\r\n status.localMissing.length > 0 \r\n ? `${pc.blue('4.')} Reorder your local migrations if needed, then push`\r\n : '',\r\n '',\r\n pc.dim('This preserves both remote and local work.'),\r\n ].filter(Boolean).join('\\n'),\r\n 'Migration Sync'\r\n );\r\n\r\n const proceed = await p.confirm({\r\n message: 'Proceed with sync?',\r\n initialValue: true,\r\n });\r\n\r\n if (p.isCancel(proceed) || !proceed) {\r\n return { success: false, cancelled: true };\r\n }\r\n\r\n // Step 1: Capture current local migrations BEFORE fetch\r\n const localBefore = await getLocalMigrationDetails();\r\n const localBeforeMap = new Map<string, LocalMigration>();\r\n for (const m of localBefore) {\r\n localBeforeMap.set(m.timestamp, m);\r\n // Read content now, before fetch potentially overwrites\r\n m.content = await readMigrationContent(m);\r\n }\r\n\r\n // Step 2: Fetch remote migrations\r\n const spinner = p.spinner();\r\n spinner.start('Fetching remote migrations...');\r\n \r\n const fetchResult = await fetchRemoteMigrations();\r\n \r\n if (!fetchResult.success) {\r\n spinner.stop('Fetch failed');\r\n console.log(pc.red('✗'), 'Failed to fetch remote migrations:', fetchResult.error);\r\n return { success: false };\r\n }\r\n \r\n spinner.stop(`Fetched ${status.remoteMissing.length} migration(s) from remote`);\r\n\r\n // Step 3: Find content conflicts (same timestamp, different content)\r\n // We compare the local content BEFORE fetch with what fetch wrote\r\n const conflicts: MigrationConflict[] = [];\r\n \r\n for (const [timestamp, localMigration] of localBeforeMap) {\r\n // Only check migrations that existed both locally and remotely\r\n if (status.remoteMissing.includes(timestamp) || status.localMissing.includes(timestamp)) {\r\n continue; // This was unique to one side, not a conflict\r\n }\r\n \r\n // Read what fetch wrote (remote content)\r\n const remotePath = localMigration.fullPath;\r\n let remoteContent: string;\r\n try {\r\n remoteContent = await readFile(remotePath, 'utf-8');\r\n } catch {\r\n continue; // File doesn't exist, skip\r\n }\r\n \r\n const localContent = localMigration.content ?? '';\r\n const localHash = hashContent(localContent);\r\n const remoteHash = hashContent(remoteContent);\r\n \r\n if (localHash !== remoteHash) {\r\n // Double-check: do we have MEANINGFUL differences?\r\n // (Hashes can differ due to whitespace/line endings)\r\n const { additions, removals } = computeSimpleDiff(localContent, remoteContent);\r\n \r\n if (additions.length === 0 && removals.length === 0) {\r\n // No meaningful differences - just whitespace/formatting\r\n // Silently keep the remote version (it's what's in the DB)\r\n continue;\r\n }\r\n \r\n conflicts.push({\r\n timestamp,\r\n filename: localMigration.filename,\r\n localPath: remotePath,\r\n localContent,\r\n remoteContent,\r\n localHash,\r\n remoteHash,\r\n });\r\n }\r\n }\r\n\r\n // Step 4: Resolve any conflicts\r\n // Get the latest remote timestamp for new migration ordering\r\n const sortedRemote = [...status.remoteMissing].sort();\r\n const latestRemoteTimestamp = sortedRemote.length > 0 \r\n ? sortedRemote[sortedRemote.length - 1] \r\n : undefined;\r\n \r\n if (conflicts.length > 0) {\r\n console.log();\r\n console.log(pc.yellow('⚠'), `Found ${conflicts.length} file(s) with content differences`);\r\n console.log();\r\n \r\n for (let i = 0; i < conflicts.length; i++) {\n const conflict = conflicts[i]!;\n const conflictNum = i + 1;\n const totalConflicts = conflicts.length;\n \n // Show which conflict we're on\n console.log(pc.blue('─'.repeat(60)));\n console.log(pc.blue(` Conflict ${conflictNum} of ${totalConflicts}`));\n console.log(pc.blue('─'.repeat(60)));\n \n const resolution = await resolveConflict(conflict);\n \n if (resolution === 'cancel') {\n // Restore original local files\n const restoreSpinner = p.spinner();\n restoreSpinner.start('Restoring original files...');\n for (const [, localMigration] of localBeforeMap) {\n if (localMigration.content) {\n await writeFile(localMigration.fullPath, localMigration.content);\n }\n }\n restoreSpinner.stop('Restored original files');\n return { success: false, cancelled: true };\n }\n \n // Show processing feedback\n const actionSpinner = p.spinner();\n const actionLabel = resolution === 'create-migration' \n ? 'Creating migration from your edits...'\n : resolution === 'keep-remote'\n ? 'Restoring file to match database...'\n : resolution === 'save-both'\n ? 'Saving both versions...'\n : 'Processing...';\n \n actionSpinner.start(actionLabel);\n \n const applied = await applyConflictResolution(conflict, resolution, latestRemoteTimestamp);\n \r\n // Stop spinner before showing results\r\n actionSpinner.stop(applied ? 'Complete' : 'Failed');\r\n \r\n if (!applied) {\r\n return { success: false, cancelled: true };\r\n }\r\n \r\n // If there are more conflicts, add a visual break\r\n if (i < conflicts.length - 1) {\r\n console.log();\r\n console.log(pc.dim(' Moving to next conflict...'));\r\n console.log();\r\n }\r\n }\r\n \r\n console.log();\r\n console.log(pc.green('✓'), `Resolved ${conflicts.length} conflict(s)`);\r\n }\r\n\r\n // Step 5: Restore local-only migrations that might have been lost\r\n // (fetch overwrites the directory, so we need to restore files that were only local)\r\n const migrationsDir = resolve(process.cwd(), 'supabase', 'migrations');\r\n \r\n for (const timestamp of status.localMissing) {\r\n const localMigration = localBeforeMap.get(timestamp);\r\n if (localMigration && localMigration.content) {\r\n const targetPath = join(migrationsDir, localMigration.filename);\r\n await writeFile(targetPath, localMigration.content);\r\n }\r\n }\r\n\r\n // Step 6: Reorder local-only migrations if their timestamps are before remote migrations\r\n if (status.localMissing.length > 0 && latestRemoteTimestamp) {\r\n // Get fresh list of local migrations\r\n const currentLocal = await getLocalMigrationDetails();\r\n const localOnlyMigrations = currentLocal.filter(m => status.localMissing.includes(m.timestamp));\r\n \r\n const renames = await reorderLocalMigrations(localOnlyMigrations, latestRemoteTimestamp);\r\n \r\n if (renames.length > 0) {\r\n console.log(pc.green('✓'), `Reordered ${renames.length} local migration(s)`);\r\n }\r\n }\r\n\r\n console.log();\r\n console.log(pc.green('✓'), 'Migration sync complete!');\r\n \r\n // Show summary\r\n const finalLocal = await getLocalMigrationDetails();\r\n const finalRemote = await getRemoteMigrations();\r\n const stillMissing = finalLocal\r\n .filter(m => !finalRemote.includes(m.timestamp))\r\n .map(m => m.filename);\r\n \r\n if (stillMissing.length > 0) {\r\n console.log();\r\n console.log(pc.blue('→'), `${stillMissing.length} migration(s) ready to push:`);\r\n for (const f of stillMissing) {\r\n console.log(pc.dim(` ${f}`));\r\n }\r\n }\r\n\r\n return { success: true };\r\n}\r\n\r\n/**\r\n * Result of migration rescue operation\r\n */\r\nexport interface RescueResult {\r\n success: boolean;\r\n error?: string;\r\n /** Path to the created baseline migration file */\r\n baselinePath?: string;\r\n /** Remote migrations that were marked as reverted */\r\n revertedMigrations: string[];\r\n}\r\n\r\n/**\r\n * Legacy rescue function - now just calls interactiveMigrationSync\r\n * which handles all scenarios including rescue\r\n */\r\nexport async function rescueMigrations(\r\n _remoteMigrations: string[]\r\n): Promise<RescueResult> {\r\n // This is now handled by the new fetch-based sync\n const syncResult = await interactiveMigrationSync();\n \n const result: RescueResult = {\n success: syncResult.success,\n revertedMigrations: [],\n };\n \n if (syncResult.cancelled) {\n result.error = 'Cancelled by user';\n }\n \n return result;\n}\n\r\n/**\r\n * Legacy rescue flow - now redirects to interactiveMigrationSync\r\n */\r\nexport async function interactiveMigrationRescue(): Promise<{ success: boolean; cancelled?: boolean; rescued?: boolean }> {\r\n const result = await interactiveMigrationSync();\r\n return {\r\n ...result,\r\n rescued: result.success,\r\n };\r\n}\r\n","import { Command } from 'commander';\nimport pc from 'picocolors';\nimport * as p from '@clack/prompts';\nimport { loadConfigOrExit } from '../config/loader.js';\nimport { resolveEnvironmentByProjectRef, getEnvironmentByName } from '../config/resolver.js';\nimport { isEnvironmentLocked } from '../config/schema.js';\nimport { getCurrentBranch, hasUncommittedChanges, clearGitCache } from '../utils/git.js';\nimport { runSupabase, requireSupabaseCLI } from '../utils/supabase.js';\nimport { runGuards, buildGuardContext, clearProjectCache, getCurrentLinkedProject } from '../guards/index.js';\nimport type { GlobalOptions } from '../index.js';\n\ninterface ResetOptions extends GlobalOptions {\n force?: boolean;\n linked?: boolean;\n iKnowWhatImDoing?: boolean;\n}\n\n/**\n * Create the reset command\n */\nexport function createResetCommand(): Command {\n return new Command('reset')\n .description('Reset database to match local migrations (DESTRUCTIVE)')\n .option('--force', 'Bypass all safety guards (DANGEROUS)', false)\n .option('--linked', 'Reset the linked remote database instead of local', false)\n .option(\n '--i-know-what-im-doing',\n 'Required flag for this operation in CI mode',\n false\n )\n .action(async function (this: Command) {\n const opts = this.optsWithGlobals<ResetOptions>();\n await runReset(opts);\n });\n}\n\n/**\n * Run the reset command\n */\nasync function runReset(options: ResetOptions): Promise<void> {\n // Clear caches at start of command\n clearGitCache();\n clearProjectCache();\n\n // Check supabase CLI is available\n await requireSupabaseCLI();\n\n // Load config\n const config = await loadConfigOrExit();\n\n // Get current git state\n const gitBranch = await getCurrentBranch();\n const uncommittedChanges = await hasUncommittedChanges();\n\n // Resolve environment from linked project (not git branch)\n let resolved;\n if (options.env) {\n // Explicit --env flag takes precedence\n resolved = getEnvironmentByName(options.env, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), `Environment '${options.env}' not found in config`);\n console.error(pc.dim(' Available environments:'), Object.keys(config.environments).join(', '));\n process.exit(1);\n }\n } else {\n // Resolve from currently linked Supabase project\n const linkedRef = await getCurrentLinkedProject();\n if (!linkedRef) {\n console.error(pc.red('\\u2717'), 'No Supabase project linked');\n console.error(pc.dim(' Run: supacontrol switch <environment>'));\n process.exit(1);\n }\n \n resolved = resolveEnvironmentByProjectRef(linkedRef, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), 'Linked project is not configured in supacontrol.toml');\n console.error(pc.dim(` Linked to: ${linkedRef}`));\n console.error(pc.dim(' Run: supacontrol init or add this project to your config'));\n process.exit(1);\n }\n }\n\n const isLocked = isEnvironmentLocked(resolved.name, resolved.config);\n\n // CI mode requires explicit flags\n if (options.ci) {\n if (!options.env) {\n console.error(pc.red('\\u2717'), 'CI mode requires explicit --env flag for reset');\n process.exit(1);\n }\n if (!options.iKnowWhatImDoing) {\n console.error(pc.red('\\u2717'), 'CI mode requires --i-know-what-im-doing flag for reset');\n process.exit(1);\n }\n }\n\n // Show critical warning\n console.log();\n p.note(\n [\n pc.bold(pc.red('CRITICAL WARNING')),\n '',\n `This will ${pc.red('DROP ALL TABLES')} and recreate the database`,\n `from your local migrations.`,\n '',\n `Environment: ${pc.cyan(resolved.name)}`,\n options.linked ? `Target: ${pc.red('REMOTE DATABASE')}` : `Target: ${pc.yellow('Local database')}`,\n isLocked ? `\\n${pc.red('\\u{1F512} Environment is LOCKED')}` : '',\n ]\n .filter(Boolean)\n .join('\\n'),\n pc.red('\\u26A0\\uFE0F Database Reset')\n );\n\n // Force mode bypasses guards (with additional warning)\n if (options.force) {\n console.log();\n console.log(pc.red('\\u26A0'), pc.bold('FORCE MODE ENABLED'));\n console.log(pc.red(' All safety guards are being bypassed!'));\n console.log();\n\n if (!options.ci) {\n const forceConfirm = await p.confirm({\n message: pc.red('Are you absolutely sure you want to proceed?'),\n initialValue: false,\n });\n\n if (p.isCancel(forceConfirm) || !forceConfirm) {\n p.cancel('Operation cancelled');\n process.exit(0);\n }\n }\n } else {\n // Run guards\n const context = buildGuardContext({\n operation: 'reset',\n environmentName: resolved.name,\n environment: resolved.config,\n config,\n gitBranch,\n isCI: options.ci,\n hasUncommittedChanges: uncommittedChanges,\n });\n\n const guardResult = await runGuards(context);\n\n if (!guardResult.allowed) {\n if (guardResult.cancelled) {\n process.exit(0);\n }\n process.exit(1);\n }\n }\n\n // Additional confirmation for critical operation\n if (!options.ci && !options.force) {\n const confirmWord = resolved.config.confirm_word ?? resolved.name;\n\n console.log();\n const finalConfirm = await p.text({\n message: `Type '${pc.bold(pc.red(confirmWord))}' to confirm database reset:`,\n validate(value) {\n if (value !== confirmWord) {\n return `Please type exactly '${confirmWord}' to confirm`;\n }\n return undefined;\n },\n });\n\n if (p.isCancel(finalConfirm)) {\n p.cancel('Operation cancelled');\n process.exit(0);\n }\n\n if (finalConfirm !== confirmWord) {\n console.error(pc.red('\\u2717'), 'Confirmation failed');\n process.exit(1);\n }\n }\n\n // Build reset command args\n const resetArgs = ['db', 'reset'];\n if (options.linked) {\n resetArgs.push('--linked');\n }\n\n // Execute reset\n console.log();\n console.log(pc.blue('\\u2192'), 'Resetting database for', pc.cyan(resolved.name));\n console.log();\n\n const result = await runSupabase(resetArgs, { stream: true });\n\n if (result.success) {\n console.log();\n console.log(pc.green('\\u2713'), 'Database reset completed successfully');\n } else {\n console.log();\n console.error(pc.red('\\u2717'), 'Database reset failed');\n process.exit(result.exitCode);\n }\n}\n","import { Command } from 'commander';\nimport pc from 'picocolors';\nimport { loadConfigOrExit } from '../config/loader.js';\nimport { resolveEnvironmentByProjectRef, getEnvironmentByName } from '../config/resolver.js';\nimport { getCurrentBranch, hasUncommittedChanges, clearGitCache } from '../utils/git.js';\nimport { runSupabase, requireSupabaseCLI } from '../utils/supabase.js';\nimport { runGuards, buildGuardContext, clearProjectCache, getCurrentLinkedProject } from '../guards/index.js';\nimport type { GlobalOptions } from '../index.js';\n\ninterface PullOptions extends GlobalOptions {\n force?: boolean;\n}\n\n/**\n * Create the pull command\n */\nexport function createPullCommand(): Command {\n return new Command('pull')\n .description('Pull remote schema changes to local migrations')\n .option('--force', 'Bypass safety guards', false)\n .action(async function (this: Command) {\n const opts = this.optsWithGlobals<PullOptions>();\n await runPull(opts);\n });\n}\n\n/**\n * Run the pull command\n */\nasync function runPull(options: PullOptions): Promise<void> {\n // Clear caches at start of command\n clearGitCache();\n clearProjectCache();\n\n // Check supabase CLI is available\n await requireSupabaseCLI();\n\n // Load config\n const config = await loadConfigOrExit();\n\n // Get current git state\n const gitBranch = await getCurrentBranch();\n const uncommittedChanges = await hasUncommittedChanges();\n\n // Resolve environment from linked project (not git branch)\n let resolved;\n if (options.env) {\n // Explicit --env flag takes precedence\n resolved = getEnvironmentByName(options.env, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), `Environment '${options.env}' not found in config`);\n console.error(pc.dim(' Available environments:'), Object.keys(config.environments).join(', '));\n process.exit(1);\n }\n } else {\n // Resolve from currently linked Supabase project\n const linkedRef = await getCurrentLinkedProject();\n if (!linkedRef) {\n console.error(pc.red('\\u2717'), 'No Supabase project linked');\n console.error(pc.dim(' Run: supacontrol switch <environment>'));\n process.exit(1);\n }\n \n resolved = resolveEnvironmentByProjectRef(linkedRef, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), 'Linked project is not configured in supacontrol.toml');\n console.error(pc.dim(` Linked to: ${linkedRef}`));\n console.error(pc.dim(' Run: supacontrol init or add this project to your config'));\n process.exit(1);\n }\n }\n\n // Force mode bypasses guards\n if (options.force) {\n console.log(pc.yellow('\\u26A0'), 'Force mode: bypassing safety guards');\n console.log();\n } else {\n // Run guards\n const context = buildGuardContext({\n operation: 'pull',\n environmentName: resolved.name,\n environment: resolved.config,\n config,\n gitBranch,\n isCI: options.ci,\n hasUncommittedChanges: uncommittedChanges,\n });\n\n const guardResult = await runGuards(context);\n\n if (!guardResult.allowed) {\n if (guardResult.cancelled) {\n process.exit(0);\n }\n process.exit(1);\n }\n }\n\n // Execute pull\n console.log(pc.blue('\\u2192'), 'Pulling schema from', pc.cyan(resolved.name));\n console.log();\n\n const result = await runSupabase(['db', 'pull'], { stream: true });\n\n if (result.success) {\n console.log();\n console.log(pc.green('\\u2713'), 'Pull completed successfully');\n console.log(pc.dim(' Review the generated migrations in supabase/migrations/'));\n } else {\n console.log();\n console.error(pc.red('\\u2717'), 'Pull failed');\n process.exit(result.exitCode);\n }\n}\n","import { Command } from 'commander';\nimport pc from 'picocolors';\nimport * as p from '@clack/prompts';\nimport { loadConfigOrExit } from '../config/loader.js';\nimport { getEnvironmentByName, listEnvironments } from '../config/resolver.js';\nimport { runSupabase, requireSupabaseCLI } from '../utils/supabase.js';\nimport { getCurrentLinkedProject, clearProjectCache } from '../guards/project-guard.js';\nimport { checkMigrationSync, syncMigrations } from '../utils/migrations.js';\n\n/**\n * Create the switch command\n */\nexport function createSwitchCommand(): Command {\n return new Command('switch')\n .description('Switch to a different environment (link to its project)')\n .argument('<environment>', 'Target environment name')\n .action(async (envName: string) => {\n await runSwitch(envName);\n });\n}\n\n/**\n * Run the switch command\n */\nasync function runSwitch(envName: string): Promise<void> {\n // Clear project cache\n clearProjectCache();\n\n // Check supabase CLI is available\n await requireSupabaseCLI();\n\n // Load config\n const config = await loadConfigOrExit();\n\n // Get environment\n const resolved = getEnvironmentByName(envName, config);\n if (!resolved) {\n console.error(pc.red('\\u2717'), `Environment '${envName}' not found in config`);\n console.error();\n console.error(pc.dim('Available environments:'));\n for (const name of listEnvironments(config)) {\n const env = config.environments[name];\n if (env) {\n console.error(pc.dim(` - ${name}${env.project_ref ? ` (${env.project_ref})` : ''}`));\n }\n }\n process.exit(1);\n }\n\n const currentProject = await getCurrentLinkedProject();\n\n // Handle local environment (no project_ref)\n if (!resolved.projectRef) {\n if (envName === 'local') {\n console.log(pc.blue('\\u2192'), 'Switching to local development');\n\n if (currentProject) {\n console.log(pc.dim(' Unlinking from remote project...'));\n const result = await runSupabase(['unlink'], { stream: false });\n if (result.success) {\n console.log(pc.green('\\u2713'), 'Unlinked from remote project');\n console.log(pc.dim(' Now using local database'));\n } else {\n console.log(pc.yellow('\\u26A0'), 'Could not unlink (may already be unlinked)');\n }\n } else {\n console.log(pc.green('\\u2713'), 'Already using local database');\n }\n return;\n }\n\n console.error(pc.red('\\u2717'), `Environment '${envName}' has no project_ref configured`);\n console.error(pc.dim(` Add 'project_ref' to [environments.${envName}] in supacontrol.toml`));\n process.exit(1);\n }\n\n // Check if already linked to the right project\n if (currentProject === resolved.projectRef) {\n console.log(pc.green('\\u2713'), `Already linked to ${pc.cyan(resolved.projectRef)}`);\n console.log(pc.dim(` Environment: ${envName}`));\n return;\n }\n\n // Switch to the new project\n console.log(pc.blue('\\u2192'), `Switching to ${pc.cyan(envName)}`);\n\n if (currentProject) {\n console.log(pc.dim(` From: ${currentProject}`));\n }\n console.log(pc.dim(` To: ${resolved.projectRef}`));\n console.log();\n\n const result = await runSupabase(\n ['link', '--project-ref', resolved.projectRef],\n { stream: true }\n );\n\n if (result.success) {\n console.log();\n console.log(pc.green('\\u2713'), `Linked to ${pc.cyan(resolved.projectRef)}`);\n console.log(pc.dim(` Environment: ${envName}`));\n \n // Check if we need to sync migrations\n console.log();\n await checkAndSyncMigrations();\n } else {\n console.log();\n console.error(pc.red('\\u2717'), 'Failed to link to project');\n console.error(pc.dim(' Make sure you are logged in: supabase login'));\n process.exit(result.exitCode);\n }\n}\n\n/**\n * Check if remote has migrations we don't have locally, and offer to sync\n */\nasync function checkAndSyncMigrations(): Promise<void> {\n const syncStatus = await checkMigrationSync();\n \n if (syncStatus.needsSync && syncStatus.remoteMissing.length > 0) {\n console.log(pc.yellow('⚠'), `Remote has ${syncStatus.remoteMissing.length} migration(s) not in local`);\n \n for (const migration of syncStatus.remoteMissing) {\n console.log(pc.dim(` - ${migration}`));\n }\n \n console.log();\n const shouldSync = await p.confirm({\n message: 'Pull remote migrations to sync local state?',\n initialValue: true,\n });\n \n if (p.isCancel(shouldSync)) {\n return;\n }\n \n if (shouldSync) {\n const success = await syncMigrations();\n if (success) {\n console.log(pc.green('✓'), 'Migrations synced');\n } else {\n console.log(pc.yellow('⚠'), 'Migration sync had issues - check output above');\n }\n } else {\n console.log(pc.dim('Skipped migration sync'));\n console.log(pc.dim(' Run `supabase db pull` manually to sync later'));\n }\n } else if (syncStatus.localMissing.length > 0) {\n // Local has migrations not on remote - this is normal, they need to push\n console.log(pc.blue('→'), `You have ${syncStatus.localMissing.length} local migration(s) to push`);\n console.log(pc.dim(' Run `spc push` when ready'));\n } else {\n console.log(pc.green('✓'), 'Migrations are in sync');\n }\n}\n","import { Command } from 'commander';\nimport pc from 'picocolors';\nimport * as p from '@clack/prompts';\nimport { loadConfig, loadConfigOrExit } from '../config/loader.js';\nimport { resolveEnvironmentByProjectRef, getEnvironmentByName, listEnvironments } from '../config/resolver.js';\nimport { isEnvironmentLocked } from '../config/schema.js';\nimport { writeConfig } from '../config/writer.js';\nimport { clearGitCache } from '../utils/git.js';\nimport { getCurrentLinkedProject, clearProjectCache } from '../guards/project-guard.js';\nimport type { GlobalOptions } from '../index.js';\n\n/**\n * Create the lock command\n */\nexport function createLockCommand(): Command {\n return new Command('lock')\n .description('Lock an environment to prevent destructive operations')\n .argument('[environment]', 'Environment to lock (defaults to current)')\n .action(async function (this: Command, envName?: string) {\n const opts = this.optsWithGlobals<GlobalOptions>();\n await runLock(envName, opts);\n });\n}\n\n/**\n * Create the unlock command\n */\nexport function createUnlockCommand(): Command {\n return new Command('unlock')\n .description('Unlock an environment to allow destructive operations')\n .argument('[environment]', 'Environment to unlock (defaults to current)')\n .action(async function (this: Command, envName?: string) {\n const opts = this.optsWithGlobals<GlobalOptions>();\n await runUnlock(envName, opts);\n });\n}\n\n/**\n * Run the lock command\n */\nasync function runLock(envName: string | undefined, _options: GlobalOptions): Promise<void> {\n clearGitCache();\n\n const config = await loadConfigOrExit();\n const targetEnv = await resolveTargetEnvironment(envName, config);\n\n if (!targetEnv) {\n process.exit(1);\n }\n\n const env = config.environments[targetEnv];\n if (!env) {\n console.error(pc.red('\\u2717'), `Environment '${targetEnv}' not found`);\n process.exit(1);\n }\n\n const isAlreadyLocked = isEnvironmentLocked(targetEnv, env);\n if (isAlreadyLocked) {\n console.log(pc.green('\\u2713'), `Environment '${targetEnv}' is already locked`);\n return;\n }\n\n // Update config\n env.locked = true;\n await writeConfig(config);\n\n console.log(pc.green('\\u2713'), `Locked environment '${pc.cyan(targetEnv)}'`);\n console.log(pc.dim(' Destructive operations are now blocked'));\n}\n\n/**\n * Run the unlock command\n */\nasync function runUnlock(envName: string | undefined, options: GlobalOptions): Promise<void> {\n clearGitCache();\n\n const config = await loadConfigOrExit();\n const targetEnv = await resolveTargetEnvironment(envName, config);\n\n if (!targetEnv) {\n process.exit(1);\n }\n\n const env = config.environments[targetEnv];\n if (!env) {\n console.error(pc.red('\\u2717'), `Environment '${targetEnv}' not found`);\n process.exit(1);\n }\n\n const isLocked = isEnvironmentLocked(targetEnv, env);\n if (!isLocked) {\n console.log(pc.green('\\u2713'), `Environment '${targetEnv}' is already unlocked`);\n return;\n }\n\n // Check if this is a production environment\n const isProduction =\n targetEnv === 'production' ||\n env.git_branches.includes('main') ||\n env.git_branches.includes('master');\n\n if (isProduction && !options.ci) {\n console.log();\n p.note(\n [\n pc.yellow('Warning: Unlocking production environment'),\n '',\n 'This will allow destructive operations like:',\n ' - db push',\n ' - db reset',\n ' - db seed',\n ].join('\\n'),\n pc.yellow('\\u26A0 Production Unlock')\n );\n\n const confirmed = await p.confirm({\n message: `Are you sure you want to unlock '${targetEnv}'?`,\n initialValue: false,\n });\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel('Operation cancelled');\n process.exit(0);\n }\n }\n\n // Update config\n env.locked = false;\n await writeConfig(config);\n\n console.log(pc.yellow('\\u26A0'), `Unlocked environment '${pc.cyan(targetEnv)}'`);\n console.log(pc.dim(' Destructive operations are now allowed'));\n console.log(pc.dim(` Run 'supacontrol lock ${targetEnv}' to re-lock`));\n}\n\n/**\n * Resolve target environment from argument or current branch\n */\nasync function resolveTargetEnvironment(\n envName: string | undefined,\n config: Awaited<ReturnType<typeof loadConfig>>\n): Promise<string | null> {\n if (!config) {\n return null;\n }\n\n if (envName) {\n const env = getEnvironmentByName(envName, config);\n if (!env) {\n console.error(pc.red('\\u2717'), `Environment '${envName}' not found in config`);\n console.error();\n console.error(pc.dim('Available environments:'));\n for (const name of listEnvironments(config)) {\n console.error(pc.dim(` - ${name}`));\n }\n return null;\n }\n return envName;\n }\n\n // Resolve from linked project\n clearProjectCache();\n const linkedRef = await getCurrentLinkedProject();\n \n if (!linkedRef) {\n console.error(pc.red('\\u2717'), 'No Supabase project linked');\n console.error(pc.dim(' Specify environment: supacontrol lock <environment>'));\n console.error(pc.dim(' Or run: supacontrol switch <environment>'));\n return null;\n }\n \n const resolved = resolveEnvironmentByProjectRef(linkedRef, config);\n\n if (!resolved) {\n console.error(pc.red('\\u2717'), 'Linked project is not configured in supacontrol.toml');\n console.error(pc.dim(' Specify environment: supacontrol lock <environment>'));\n return null;\n }\n\n return resolved.name;\n}\n","import { writeFile, access, constants } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { Config, Environment, Settings } from './schema.js';\n\n/**\n * Default config file name\n */\nconst CONFIG_FILENAME = 'supacontrol.toml';\n\n/**\n * Generate TOML string from settings object\n */\nfunction settingsToToml(settings: Settings): string {\n const lines: string[] = [\n '[settings]',\n '# Fail on any guard warning, not just errors',\n `strict_mode = ${settings.strict_mode}`,\n '',\n '# Require clean git working tree before destructive operations',\n `require_clean_git = ${settings.require_clean_git}`,\n '',\n '# Show migration diff before push',\n `show_migration_diff = ${settings.show_migration_diff}`,\n ];\n return lines.join('\\n');\n}\n\n/**\n * Generate TOML string for a single environment\n */\nfunction environmentToToml(name: string, env: Environment): string {\n const lines: string[] = [`[environments.${name}]`];\n\n if (env.project_ref !== undefined) {\n lines.push(`# Supabase project reference`);\n lines.push(`project_ref = \"${env.project_ref}\"`);\n }\n\n if (env.git_branches.length > 0) {\n lines.push(`# Git branches that map to this environment`);\n lines.push(`git_branches = [${env.git_branches.map((b) => `\"${b}\"`).join(', ')}]`);\n }\n\n if (env.protected_operations.length > 0) {\n lines.push(`# Operations that require confirmation`);\n lines.push(\n `protected_operations = [${env.protected_operations.map((o) => `\"${o}\"`).join(', ')}]`\n );\n }\n\n if (env.confirm_word !== undefined) {\n lines.push(`# Custom confirmation word (type this to confirm)`);\n lines.push(`confirm_word = \"${env.confirm_word}\"`);\n }\n\n if (env.locked !== undefined) {\n lines.push(`# Lock environment to prevent all destructive operations`);\n lines.push(`locked = ${env.locked}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Convert a Config object to TOML string with comments\n */\nexport function configToToml(config: Config): string {\n const sections: string[] = [\n '# SupaControl Configuration',\n '# https://github.com/your-org/supacontrol',\n '',\n settingsToToml(config.settings),\n ];\n\n const envNames = Object.keys(config.environments);\n if (envNames.length > 0) {\n sections.push('');\n for (const name of envNames) {\n const env = config.environments[name];\n if (env) {\n sections.push(environmentToToml(name, env));\n sections.push('');\n }\n }\n }\n\n return sections.join('\\n').trimEnd() + '\\n';\n}\n\n/**\n * Generate a default/example config\n */\nexport function generateDefaultConfig(): Config {\n return {\n settings: {\n strict_mode: false,\n require_clean_git: true,\n show_migration_diff: true,\n },\n environments: {\n staging: {\n project_ref: undefined,\n git_branches: ['develop', 'staging'],\n protected_operations: ['reset'],\n confirm_word: undefined,\n locked: undefined,\n },\n production: {\n project_ref: undefined,\n git_branches: ['main', 'master'],\n protected_operations: ['push', 'reset', 'seed'],\n confirm_word: 'production',\n locked: true,\n },\n },\n };\n}\n\n/**\n * Generate example TOML config string\n */\nexport function generateExampleToml(): string {\n return configToToml(generateDefaultConfig());\n}\n\n/**\n * Check if config file already exists\n */\nexport async function configExists(cwd?: string): Promise<boolean> {\n const searchDir = cwd ?? process.cwd();\n const configPath = resolve(searchDir, CONFIG_FILENAME);\n\n try {\n await access(configPath, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Write config to supacontrol.toml\n *\n * @param config - Config object to write\n * @param cwd - Directory to write to (defaults to process.cwd())\n * @returns Path to written config file\n */\nexport async function writeConfig(config: Config, cwd?: string): Promise<string> {\n const searchDir = cwd ?? process.cwd();\n const configPath = resolve(searchDir, CONFIG_FILENAME);\n const toml = configToToml(config);\n\n await writeFile(configPath, toml, 'utf-8');\n return configPath;\n}\n","import { Command } from 'commander';\nimport pc from 'picocolors';\nimport { access, constants } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { loadConfig } from '../config/loader.js';\nimport { listEnvironments } from '../config/resolver.js';\nimport { isEnvironmentLocked } from '../config/schema.js';\nimport { isSupabaseCLIInstalled, getSupabaseVersion } from '../utils/supabase.js';\nimport { isGitRepository, getCurrentBranch } from '../utils/git.js';\nimport { getCurrentLinkedProject } from '../guards/project-guard.js';\n\ninterface DoctorOptions {\n verbose?: boolean;\n report?: boolean;\n}\n\ninterface CheckResult {\n name: string;\n status: 'pass' | 'warn' | 'fail' | 'info';\n message: string;\n details?: string[];\n fix?: string;\n}\n\n/**\n * Create the doctor command\n */\nexport function createDoctorCommand(): Command {\n return new Command('doctor')\n .description('Check for common issues and misconfigurations')\n .option('--verbose', 'Show detailed output', false)\n .option('--report', 'Show summary only', false)\n .action(async (options: DoctorOptions) => {\n await runDoctor(options);\n });\n}\n\n/**\n * Run all health checks\n */\nasync function runDoctor(options: DoctorOptions): Promise<void> {\n console.log();\n console.log(pc.bold('SupaControl Doctor'));\n console.log(pc.dim('Checking your project setup...'));\n console.log();\n\n const results: CheckResult[] = [];\n\n // Run all checks\n results.push(await checkSupabaseCLI());\n results.push(await checkGitRepository());\n results.push(await checkSupacontrolConfig());\n results.push(await checkSupabaseProject());\n results.push(await checkLinkedProject());\n results.push(await checkEnvironmentSafety());\n results.push(await checkMigrationsFolder());\n\n // Count results\n const passes = results.filter((r) => r.status === 'pass').length;\n const warns = results.filter((r) => r.status === 'warn').length;\n const fails = results.filter((r) => r.status === 'fail').length;\n\n // Print results\n if (!options.report) {\n for (const result of results) {\n printResult(result, options.verbose);\n }\n console.log();\n }\n\n // Summary\n console.log(pc.bold('Summary'));\n console.log(pc.dim('─'.repeat(40)));\n console.log(` ${pc.green('\\u2713')} ${passes} passed`);\n if (warns > 0) {\n console.log(` ${pc.yellow('\\u26A0')} ${warns} warnings`);\n }\n if (fails > 0) {\n console.log(` ${pc.red('\\u2717')} ${fails} failed`);\n }\n console.log();\n\n // Tips\n if (warns > 0 || fails > 0) {\n console.log(pc.dim('Fix the issues above to improve your setup.'));\n console.log();\n }\n\n // Exit with error code if any failures\n if (fails > 0) {\n process.exit(1);\n }\n}\n\n/**\n * Print a check result\n */\nfunction printResult(result: CheckResult, verbose?: boolean): void {\n const icon =\n result.status === 'pass'\n ? pc.green('\\u2713')\n : result.status === 'warn'\n ? pc.yellow('\\u26A0')\n : result.status === 'fail'\n ? pc.red('\\u2717')\n : pc.blue('\\u2139');\n\n console.log(`${icon} ${result.name}`);\n console.log(pc.dim(` ${result.message}`));\n\n if (verbose && result.details) {\n for (const detail of result.details) {\n console.log(pc.dim(` - ${detail}`));\n }\n }\n\n if (result.fix && (result.status === 'warn' || result.status === 'fail')) {\n console.log(pc.dim(` Fix: ${result.fix}`));\n }\n\n console.log();\n}\n\n/**\n * Check: Supabase CLI installed\n */\nasync function checkSupabaseCLI(): Promise<CheckResult> {\n const installed = await isSupabaseCLIInstalled();\n if (!installed) {\n return {\n name: 'Supabase CLI',\n status: 'fail',\n message: 'Supabase CLI is not installed',\n fix: 'npm install -g supabase',\n };\n }\n\n const version = await getSupabaseVersion();\n return {\n name: 'Supabase CLI',\n status: 'pass',\n message: `Installed${version ? ` (v${version})` : ''}`,\n };\n}\n\n/**\n * Check: Git repository\n */\nasync function checkGitRepository(): Promise<CheckResult> {\n const isRepo = await isGitRepository();\n if (!isRepo) {\n return {\n name: 'Git Repository',\n status: 'warn',\n message: 'Not a git repository',\n details: ['Git branch detection will not work', 'Auto-environment switching disabled'],\n fix: 'git init',\n };\n }\n\n const branch = await getCurrentBranch();\n return {\n name: 'Git Repository',\n status: 'pass',\n message: branch ? `On branch '${branch}'` : 'Repository detected',\n };\n}\n\n/**\n * Check: supacontrol.toml exists\n */\nasync function checkSupacontrolConfig(): Promise<CheckResult> {\n const config = await loadConfig();\n if (!config) {\n return {\n name: 'SupaControl Config',\n status: 'warn',\n message: 'No supacontrol.toml found',\n details: ['Create a config to enable environment protection'],\n fix: 'supacontrol init',\n };\n }\n\n const envCount = listEnvironments(config).length;\n return {\n name: 'SupaControl Config',\n status: 'pass',\n message: `Loaded with ${envCount} environment${envCount !== 1 ? 's' : ''}`,\n };\n}\n\n/**\n * Check: supabase project folder exists\n */\nasync function checkSupabaseProject(): Promise<CheckResult> {\n const supabasePath = resolve(process.cwd(), 'supabase');\n\n try {\n await access(supabasePath, constants.F_OK);\n return {\n name: 'Supabase Project',\n status: 'pass',\n message: 'supabase/ directory found',\n };\n } catch {\n return {\n name: 'Supabase Project',\n status: 'warn',\n message: 'No supabase/ directory found',\n details: ['Initialize a Supabase project to use database features'],\n fix: 'supabase init',\n };\n }\n}\n\n/**\n * Check: Project is linked\n */\nasync function checkLinkedProject(): Promise<CheckResult> {\n const linkedProject = await getCurrentLinkedProject();\n\n if (!linkedProject) {\n return {\n name: 'Linked Project',\n status: 'info',\n message: 'No project linked (using local database)',\n details: ['Link a project to push migrations to remote'],\n };\n }\n\n return {\n name: 'Linked Project',\n status: 'pass',\n message: `Linked to ${linkedProject}`,\n };\n}\n\n/**\n * Check: Environment safety configuration\n */\nasync function checkEnvironmentSafety(): Promise<CheckResult> {\n const config = await loadConfig();\n if (!config) {\n return {\n name: 'Environment Safety',\n status: 'info',\n message: 'No config to check',\n };\n }\n\n const envs = listEnvironments(config);\n const lockedEnvs: string[] = [];\n const unlockedProdEnvs: string[] = [];\n\n for (const name of envs) {\n const env = config.environments[name];\n if (!env) continue;\n\n const isLocked = isEnvironmentLocked(name, env);\n if (isLocked) {\n lockedEnvs.push(name);\n } else {\n // Check if this looks like production but isn't locked\n const isProdLike =\n name === 'production' ||\n env.git_branches.includes('main') ||\n env.git_branches.includes('master');\n if (isProdLike) {\n unlockedProdEnvs.push(name);\n }\n }\n }\n\n if (unlockedProdEnvs.length > 0) {\n return {\n name: 'Environment Safety',\n status: 'warn',\n message: `Production environment(s) unlocked: ${unlockedProdEnvs.join(', ')}`,\n details: ['Consider locking production to prevent accidental changes'],\n fix: `supacontrol lock ${unlockedProdEnvs[0]}`,\n };\n }\n\n if (lockedEnvs.length > 0) {\n return {\n name: 'Environment Safety',\n status: 'pass',\n message: `${lockedEnvs.length} environment${lockedEnvs.length !== 1 ? 's' : ''} locked`,\n details: lockedEnvs.map((e) => `${e} is locked`),\n };\n }\n\n return {\n name: 'Environment Safety',\n status: 'info',\n message: 'No locked environments',\n };\n}\n\n/**\n * Check: Migrations folder exists\n */\nasync function checkMigrationsFolder(): Promise<CheckResult> {\n const migrationsPath = resolve(process.cwd(), 'supabase/migrations');\n\n try {\n await access(migrationsPath, constants.F_OK);\n return {\n name: 'Migrations',\n status: 'pass',\n message: 'Migrations directory found',\n };\n } catch {\n return {\n name: 'Migrations',\n status: 'info',\n message: 'No migrations directory',\n details: ['Create migrations with `supabase migration new <name>`'],\n };\n }\n}\n","import { Command } from 'commander';\r\nimport { access, constants } from 'node:fs/promises';\r\nimport { resolve } from 'node:path';\r\nimport * as p from '@clack/prompts';\r\nimport pc from 'picocolors';\r\nimport { type GlobalOptions, program, withErrorHandling } from '../index.js';\r\nimport { configExists, writeConfig } from '../config/writer.js';\r\nimport type { Config } from '../config/schema.js';\r\nimport { getOrPromptForToken, getAccessToken } from '../auth/credentials.js';\r\nimport { createSupabaseClient, type Project, type Branch, type SupabaseManagementClient } from '../api/supabase-client.js';\r\nimport { displayProjectSummary } from '../api/project-selector.js';\r\nimport { runSupabase } from '../utils/supabase.js';\r\n\r\n/**\r\n * Check if Supabase is initialized in the project\r\n */\r\nasync function checkSupabaseInit(): Promise<boolean> {\r\n const configPath = resolve(process.cwd(), 'supabase', 'config.toml');\r\n try {\r\n await access(configPath, constants.F_OK);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Environment preset options\r\n */\r\ntype EnvPreset = 'local' | 'local-staging' | 'local-staging-production';\r\n\r\nconst ENV_PRESETS: Record<EnvPreset, { label: string; hint: string }> = {\r\n local: {\r\n label: 'Local only',\r\n hint: 'Just local development with Supabase CLI',\r\n },\r\n 'local-staging': {\r\n label: 'Local + Staging',\r\n hint: 'Local dev + one remote staging environment',\r\n },\r\n 'local-staging-production': {\r\n label: 'Local + Staging + Production',\r\n hint: 'Full setup with staging and production',\r\n },\r\n};\r\n\r\n/**\r\n * Create environment config based on preset\r\n */\r\nfunction createEnvironmentConfig(\r\n preset: EnvPreset,\r\n projectRefs: Record<string, string | undefined>\r\n): Config['environments'] {\r\n const environments: Config['environments'] = {};\r\n\r\n // Local environment is always included but never in config\r\n // (local is auto-detected)\r\n\r\n if (preset === 'local-staging' || preset === 'local-staging-production') {\r\n environments['staging'] = {\r\n project_ref: projectRefs['staging'],\r\n git_branches: ['develop', 'staging'],\r\n protected_operations: ['reset'],\r\n confirm_word: undefined,\r\n locked: undefined,\r\n };\r\n }\r\n\r\n if (preset === 'local-staging-production') {\r\n environments['production'] = {\r\n project_ref: projectRefs['production'],\r\n git_branches: ['main', 'master'],\r\n protected_operations: ['push', 'reset', 'seed'],\r\n confirm_word: 'production',\r\n locked: true,\r\n };\r\n }\r\n\r\n return environments;\r\n}\r\n\r\n/**\r\n * Show branching education tip\r\n */\r\nfunction showBranchingTip(): void {\r\n p.note(\r\n [\r\n `${pc.bold('Supabase Branching')} is now available!`,\r\n '',\r\n 'With branching, you can have isolated database environments',\r\n 'for each Git branch or PR, without managing multiple projects.',\r\n '',\r\n `Learn more: ${pc.cyan('https://supabase.com/docs/guides/platform/branching')}`,\r\n ].join('\\n'),\r\n 'Pro Tip'\r\n );\r\n}\r\n\r\n/**\r\n * Result of the branching gate check\r\n */\r\ntype BranchingGateResult = \r\n | { canProceed: true; useBranching: false }\r\n | { canProceed: true; useBranching: true; parentProject: Project; branches: Branch[] }\r\n | { canProceed: false };\r\n\r\n/**\r\n * Check if user can set up staging + production with their current resources.\r\n * This gate runs BEFORE environment selection when user picks \"Local + Staging + Production\".\r\n * \r\n * ALWAYS offers the choice between separate projects or branching.\r\n */\r\nasync function checkBranchingGate(\r\n client: SupabaseManagementClient,\r\n projects: Project[]\r\n): Promise<BranchingGateResult> {\r\n // No projects at all\r\n if (projects.length === 0) {\r\n p.note(\r\n [\r\n `${pc.red('No Supabase projects found in your account.')}`,\r\n '',\r\n 'To set up staging + production environments, you need either:',\r\n '',\r\n `${pc.cyan('1.')} Two separate Supabase projects`,\r\n ` ${pc.dim('Create projects at: https://supabase.com/dashboard/projects')}`,\r\n '',\r\n `${pc.cyan('2.')} One project with Supabase Branching enabled (Pro plan)`,\r\n ` ${pc.dim('Your main project = production')}`,\r\n ` ${pc.dim('A branch = staging')}`,\r\n ` ${pc.dim('Learn more: https://supabase.com/docs/guides/platform/branching')}`,\r\n ].join('\\n'),\r\n `${pc.yellow('⚠')} Cannot set up staging + production`\r\n );\r\n\r\n const fallback = await p.select({\r\n message: 'What would you like to do?',\r\n options: [\r\n { value: 'local-staging', label: 'Continue with Local + Staging only', hint: 'Single remote environment' },\r\n { value: 'local', label: 'Continue with Local only', hint: 'No remote environments' },\r\n { value: 'cancel', label: 'Cancel setup', hint: 'Create projects first' },\r\n ],\r\n });\r\n\r\n if (p.isCancel(fallback) || fallback === 'cancel') {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n return { canProceed: false };\r\n }\r\n\r\n // Check which projects have REAL branching capability\n // This does a proper preflight check, not just trusting is_branch_enabled\n const spinner = p.spinner();\n spinner.start('Checking branching capability...');\n\n const projectsWithBranching: Array<{ project: Project; branches: Branch[] }> = [];\n \n // Filter to active projects only\n const activeProjects = projects.filter(proj => proj.status === 'ACTIVE_HEALTHY');\n \n // Process in batches to avoid rate limiting (120 req/min)\n const BATCH_SIZE = 10;\n const BATCH_DELAY_MS = 500;\n \n for (let i = 0; i < activeProjects.length; i += BATCH_SIZE) {\n const batch = activeProjects.slice(i, i + BATCH_SIZE);\n \n // Process batch in parallel\n const results = await Promise.all(\n batch.map(async (proj) => {\n const capability = await client.checkBranchingCapability(proj);\n return { proj, capability };\n })\n );\n \n // Collect successful results\n for (const { proj, capability } of results) {\n if (capability.available) {\n projectsWithBranching.push({ \n project: proj, \n branches: capability.branches,\n });\n }\n }\n \n // Add delay between batches if more to process (avoid rate limits)\n if (i + BATCH_SIZE < activeProjects.length) {\n await new Promise(resolve => setTimeout(resolve, BATCH_DELAY_MS));\n }\n }\n\n spinner.stop(\n projectsWithBranching.length > 0\n ? `Found ${projectsWithBranching.length} project${projectsWithBranching.length > 1 ? 's' : ''} with branching`\n : 'No projects with branching capability'\n );\n\r\n // Build the choice prompt\r\n console.log();\r\n \r\n const hasMultipleProjects = projects.length >= 2;\r\n const hasBranchingProjects = projectsWithBranching.length > 0;\r\n\r\n // Always show the strategy selection\r\n type StrategyChoice = 'separate-projects' | 'use-branching' | 'cancel';\r\n \r\n const strategyOptions: Array<{ value: StrategyChoice; label: string; hint: string }> = [];\r\n\r\n if (hasMultipleProjects) {\r\n strategyOptions.push({\r\n value: 'separate-projects',\r\n label: `Use separate Supabase projects (${projects.length} available)`,\r\n hint: 'Each environment uses its own project',\r\n });\r\n }\r\n\r\n if (hasBranchingProjects) {\r\n const branchingCount = projectsWithBranching.length;\r\n strategyOptions.push({\r\n value: 'use-branching',\r\n label: `Use Supabase Branching (${branchingCount} project${branchingCount > 1 ? 's' : ''} with branching)`,\r\n hint: 'Main project = production, branch = staging',\r\n });\r\n }\r\n\r\n // If neither option is available\n if (strategyOptions.length === 0) {\n const firstProject = projects[0]!;\n p.note(\r\n [\r\n `${pc.yellow('⚠')} You have 1 project without branching enabled.`,\n '',\n `Project: ${pc.cyan(firstProject.name)}`,\n '',\n 'To set up staging + production, you need either:',\n '',\n `${pc.cyan('1.')} Enable Supabase Branching (Pro plan required)`,\n ` ${pc.dim(`https://supabase.com/dashboard/project/${firstProject.id}/settings/general`)}`,\n ` → Main project = production`,\r\n ` → Branch = staging`,\r\n '',\r\n `${pc.cyan('2.')} Create a second Supabase project`,\r\n ` ${pc.dim('https://supabase.com/dashboard/projects')}`,\r\n ].join('\\n'),\r\n 'Additional Setup Required'\r\n );\r\n\r\n const choice = await p.select({\r\n message: 'What would you like to do?',\r\n options: [\r\n { value: 'open-branching', label: 'Open branching settings in browser', hint: 'Enable branching, then re-run init' },\r\n { value: 'open-projects', label: 'Open Supabase dashboard to create project', hint: 'Create second project, then re-run init' },\r\n { value: 'local-staging', label: 'Continue with Local + Staging only', hint: 'Use single project for staging' },\r\n { value: 'cancel', label: 'Cancel setup' },\r\n ],\r\n });\r\n\r\n if (p.isCancel(choice) || choice === 'cancel') {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n if (choice === 'open-branching') {\n const url = `https://supabase.com/dashboard/project/${firstProject.id}/settings/general`;\n console.log();\r\n console.log(pc.cyan('→'), `Open this URL to enable branching:`);\r\n console.log(pc.dim(` ${url}`));\r\n console.log();\r\n console.log(pc.dim('After enabling branching, run `supacontrol init` again.'));\r\n process.exit(0);\r\n }\r\n\r\n if (choice === 'open-projects') {\r\n const url = 'https://supabase.com/dashboard/projects';\r\n console.log();\r\n console.log(pc.cyan('→'), `Open this URL to create a new project:`);\r\n console.log(pc.dim(` ${url}`));\r\n console.log();\r\n console.log(pc.dim('After creating a project, run `supacontrol init` again.'));\r\n process.exit(0);\r\n }\r\n\r\n return { canProceed: false };\r\n }\r\n\r\n strategyOptions.push({\r\n value: 'cancel',\r\n label: pc.dim('Cancel setup'),\r\n hint: '',\r\n });\r\n\r\n // Show strategy selection\r\n p.note(\r\n [\r\n 'You selected Local + Staging + Production.',\r\n '',\r\n `${pc.bold('Separate projects:')} Each environment uses its own Supabase project.`,\r\n `${pc.dim(' Best for: Complete isolation between environments')}`,\r\n '',\r\n `${pc.bold('Supabase Branching:')} Production uses main project, staging uses a branch.`,\r\n `${pc.dim(' Best for: Easier schema sync, single billing, team workflows')}`,\r\n ].join('\\n'),\r\n 'Environment Strategy'\r\n );\r\n\r\n const strategy = await p.select({\r\n message: 'How would you like to configure your environments?',\r\n options: strategyOptions,\r\n });\r\n\r\n if (p.isCancel(strategy) || strategy === 'cancel') {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n if (strategy === 'use-branching') {\n // Let user pick which project to use for branching (if multiple have it)\n let selectedBranchingProject = projectsWithBranching[0]!;\n\n if (projectsWithBranching.length > 1) {\n const projectChoice = await p.select({\n message: 'Which project should be your production environment?',\n options: projectsWithBranching.map(({ project: proj, branches }) => ({\n value: proj.id,\n label: `${proj.name} ${pc.dim(`(${proj.region})`)} ${pc.green('[Active]')}`,\n hint: branches.length > 0 ? `${branches.length} existing branch${branches.length > 1 ? 'es' : ''}` : 'No branches yet',\n })),\n });\n\n if (p.isCancel(projectChoice)) {\n p.cancel('Setup cancelled');\n process.exit(0);\n }\n\n const found = projectsWithBranching.find(item => item.project.id === projectChoice);\n if (found) {\n selectedBranchingProject = found;\n }\n }\n\n return {\n canProceed: true,\n useBranching: true,\n parentProject: selectedBranchingProject.project,\n branches: selectedBranchingProject.branches,\n };\n }\n\r\n // User chose separate projects\r\n return { canProceed: true, useBranching: false };\r\n}\r\n\r\n/**\r\n * Select or create a branch for staging environment\r\n * \r\n * Handles three scenarios:\r\n * 1. No non-default branches exist → Create \"staging\" automatically\r\n * 2. One non-default branch exists → Ask user if they want to use it or create new\r\n * 3. Multiple non-default branches → Let user choose which one to use or create new\r\n */\r\nasync function selectOrCreateBranch(\r\n client: SupabaseManagementClient,\r\n parentProject: Project,\r\n existingBranches: Branch[]\r\n): Promise<string> {\r\n // Get non-default branches (these are the actual \"feature\" branches)\r\n const nonDefaultBranches = existingBranches.filter((b) => !b.is_default);\r\n \r\n // Check if a \"staging\" branch already exists\r\n const existingStagingBranch = nonDefaultBranches.find(\r\n (b) => b.name.toLowerCase() === 'staging'\r\n );\r\n\r\n // SCENARIO 1: No non-default branches - create \"staging\" automatically\r\n if (nonDefaultBranches.length === 0) {\r\n console.log(pc.dim('No existing branches found. Creating \"staging\" branch...'));\r\n return createNewBranch(client, parentProject, existingBranches, 'staging');\r\n }\r\n\r\n // SCENARIO 2: Exactly one non-default branch - ask user about it\n if (nonDefaultBranches.length === 1) {\n const singleBranch = nonDefaultBranches[0]!;\n const isStagingNamed = singleBranch.name.toLowerCase() === 'staging';\n \n console.log(pc.green('✓'), `Found existing branch: \"${singleBranch.name}\"`);\n \n // Show branch details\n p.note(\n [\n `${pc.bold('Branch details:')}`,\n ` Name: ${pc.cyan(singleBranch.name)}`,\n ` Ref: ${pc.dim(singleBranch.project_ref)}`,\n ` Status: ${singleBranch.status}`,\n '',\n isStagingNamed \n ? pc.dim('This branch is named \"staging\" - likely intended for staging environment.')\n : pc.yellow('This branch has a custom name. Please confirm its intended use.'),\n ].join('\\n'),\n 'Existing Branch Found'\n );\n\n type BranchChoice = 'use' | 'create' | 'cancel';\n const branchOptions: Array<{ value: BranchChoice; label: string; hint?: string }> = [];\n \n branchOptions.push({ \n value: 'use', \n label: `Use \"${singleBranch.name}\" as staging environment`,\n });\n if (isStagingNamed) {\n branchOptions[0]!.hint = 'Recommended';\n }\n \n branchOptions.push({ \n value: 'create', \n label: 'Create a new \"staging\" branch instead',\n hint: 'This branch may be for another purpose',\n });\n branchOptions.push({ \n value: 'cancel', \n label: pc.dim('Cancel setup'),\n hint: 'Let me check this first',\n });\n\n const choice = await p.select({\n message: `What would you like to do with this branch?`,\n options: branchOptions,\n });\n\n if (p.isCancel(choice) || choice === 'cancel') {\n p.cancel('Setup cancelled');\n process.exit(0);\n }\n\n if (choice === 'use') {\n return singleBranch.project_ref;\n }\n\r\n // User chose to create a new branch\r\n const suggestedName = existingStagingBranch ? 'staging-env' : 'staging';\r\n return createNewBranch(client, parentProject, existingBranches, suggestedName);\r\n }\r\n\r\n // SCENARIO 3: Multiple non-default branches - let user choose\r\n console.log(pc.green('✓'), `Found ${nonDefaultBranches.length} existing branches`);\r\n \r\n p.note(\r\n [\r\n `${pc.bold('Existing branches:')}`,\r\n ...nonDefaultBranches.map((b) => ` • ${b.name} ${pc.dim(`(ref: ${b.project_ref})`)}`),\r\n '',\r\n pc.yellow('Please select which branch to use for staging, or create a new one.'),\r\n ].join('\\n'),\r\n 'Multiple Branches Found'\r\n );\r\n\r\n const options: Array<{ value: string; label: string; hint?: string }> = [];\r\n\r\n // Add existing branches - prioritize \"staging\" if it exists\r\n const sortedBranches = [...nonDefaultBranches].sort((a, b) => {\r\n // Put \"staging\" first\r\n if (a.name.toLowerCase() === 'staging') return -1;\r\n if (b.name.toLowerCase() === 'staging') return 1;\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n for (const branch of sortedBranches) {\r\n const isStagingNamed = branch.name.toLowerCase() === 'staging';\r\n options.push({\r\n value: branch.project_ref,\r\n label: `\"${branch.name}\"`,\r\n hint: isStagingNamed ? 'Recommended - named \"staging\"' : `ref: ${branch.project_ref}`,\r\n });\r\n }\r\n\r\n // Add create new branch option\r\n options.push({\r\n value: '__create__',\r\n label: pc.cyan('+ Create a new branch'),\r\n hint: 'Creates a new branch for staging',\r\n });\r\n\r\n // Add cancel option\r\n options.push({\r\n value: '__cancel__',\r\n label: pc.dim('Cancel setup'),\r\n hint: 'Let me check these branches first',\r\n });\r\n\r\n const selected = await p.select({\r\n message: 'Select a branch for staging environment:',\r\n options,\r\n });\r\n\r\n if (p.isCancel(selected) || selected === '__cancel__') {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n if (selected === '__create__') {\r\n const suggestedName = existingStagingBranch ? 'staging-env' : 'staging';\r\n return createNewBranch(client, parentProject, existingBranches, suggestedName);\r\n }\r\n\r\n return selected as string;\r\n}\r\n\r\n/**\r\n * Create a new branch for staging environment\r\n */\r\nasync function createNewBranch(\r\n client: SupabaseManagementClient,\r\n parentProject: Project,\r\n existingBranches: Branch[],\r\n suggestedName: string\r\n): Promise<string> {\r\n const branchName = await p.text({\r\n message: 'Enter a name for the new branch:',\r\n placeholder: suggestedName,\r\n defaultValue: suggestedName,\r\n validate: (value) => {\r\n if (!value || value.trim().length === 0) {\r\n return 'Branch name is required';\r\n }\r\n if (!/^[a-zA-Z0-9_-]+$/.test(value)) {\r\n return 'Branch name can only contain letters, numbers, hyphens, and underscores';\r\n }\r\n if (existingBranches.some((b) => b.name.toLowerCase() === value.toLowerCase())) {\r\n return 'A branch with this name already exists';\r\n }\r\n return undefined;\r\n },\r\n });\r\n\r\n if (p.isCancel(branchName)) {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n const spinner = p.spinner();\r\n spinner.start(`Creating branch \"${branchName}\"...`);\r\n\r\n try {\r\n const newBranch = await client.createBranch(parentProject.id, branchName as string);\r\n\r\n if (!newBranch) {\r\n spinner.stop('Failed to create branch');\r\n p.cancel('Could not parse branch response. Please try again.');\r\n process.exit(1);\r\n }\r\n\r\n spinner.stop(`Branch \"${branchName}\" created`);\r\n console.log(pc.green('✓'), `Branch ref: ${pc.dim(newBranch.project_ref)}`);\r\n\r\n // Auto-link and pull migrations from the new branch\r\n await syncMigrationsFromBranch(newBranch.project_ref);\r\n\r\n return newBranch.project_ref;\r\n } catch (error) {\r\n spinner.stop('Failed to create branch');\r\n \r\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\r\n console.error(pc.red('✗'), errorMessage);\r\n \r\n // Provide helpful guidance based on common errors\r\n if (errorMessage.includes('branch limit') || errorMessage.includes('limit')) {\r\n console.log();\r\n console.log(pc.yellow('This may be a plan limitation:'));\r\n console.log(pc.dim(' • Check your branch limit at:'));\r\n console.log(pc.dim(` https://supabase.com/dashboard/project/${parentProject.id}/settings/general`));\r\n }\r\n \r\n console.log();\r\n p.cancel('Could not create branch.');\r\n process.exit(1);\r\n }\r\n}\r\n\r\n/**\r\n * Link to a branch and pull its migrations to sync local state\r\n * This ensures we have the initial migration that Supabase creates automatically\r\n */\r\nasync function syncMigrationsFromBranch(branchRef: string): Promise<void> {\r\n const spinner = p.spinner();\r\n \r\n // First, link to the branch\r\n spinner.start('Linking to branch...');\r\n const linkResult = await runSupabase(['link', '--project-ref', branchRef], { stream: false });\r\n \r\n if (!linkResult.success) {\r\n spinner.stop('Failed to link');\r\n console.log(pc.yellow('⚠'), 'Could not link to branch automatically');\r\n console.log(pc.dim(` Run manually: supabase link --project-ref ${branchRef}`));\r\n return;\r\n }\r\n spinner.stop('Linked to branch');\r\n\r\n // Now pull migrations to sync\r\n spinner.start('Syncing migrations from remote...');\r\n const pullResult = await runSupabase(['db', 'pull'], { stream: false });\r\n \r\n if (pullResult.success) {\r\n spinner.stop('Migrations synced');\r\n console.log(pc.green('✓'), 'Remote migrations pulled to local');\r\n } else {\r\n spinner.stop('Migration sync skipped');\r\n // This is okay - might mean no remote migrations yet\r\n console.log(pc.dim(' No remote migrations to sync (this is normal for new branches)'));\r\n }\r\n}\r\n\r\n/**\r\n * Show next steps after init\r\n */\r\nfunction showNextSteps(preset: EnvPreset): void {\r\n const steps = [\r\n `${pc.cyan('1.')} Review ${pc.bold('supacontrol.toml')} and adjust as needed`,\r\n ];\r\n\r\n if (preset !== 'local') {\r\n steps.push(`${pc.cyan('2.')} Run ${pc.bold('supacontrol switch <env>')} to link a project`);\r\n steps.push(`${pc.cyan('3.')} Run ${pc.bold('supacontrol status')} to verify setup`);\r\n } else {\r\n steps.push(`${pc.cyan('2.')} Run ${pc.bold('supacontrol status')} to verify setup`);\r\n }\r\n\r\n steps.push(`${pc.cyan(steps.length + 1 + '.')} Run ${pc.bold('supacontrol push')} to push migrations`);\r\n\r\n console.log();\r\n console.log(pc.bold('Next steps:'));\r\n for (const step of steps) {\r\n console.log(` ${step}`);\r\n }\r\n console.log();\r\n}\r\n\r\n/**\r\n * Init command action\r\n */\r\nasync function initAction(): Promise<void> {\r\n const opts = program.opts<GlobalOptions>();\r\n\r\n p.intro(pc.bgCyan(pc.black(' SupaControl Setup ')));\r\n\r\n // Step 1: Check for supabase init\r\n const hasSupabase = await checkSupabaseInit();\r\n if (!hasSupabase) {\r\n p.cancel('Supabase not initialized');\r\n console.error(pc.red('✗'), 'No supabase/config.toml found');\r\n console.error(pc.dim(' Run `supabase init` first to initialize Supabase'));\r\n process.exit(1);\r\n }\r\n console.log(pc.green('✓'), 'Supabase project detected');\r\n\r\n // Step 2: Check for existing supacontrol.toml\r\n const hasConfig = await configExists();\r\n if (hasConfig) {\r\n const overwrite = await p.confirm({\r\n message: 'supacontrol.toml already exists. Overwrite?',\r\n initialValue: false,\r\n });\r\n\r\n if (p.isCancel(overwrite) || !overwrite) {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n }\r\n\r\n // Step 3: Ask about environment setup\r\n let preset = await p.select({\r\n message: 'How many environments do you need?',\r\n options: [\r\n { value: 'local' as EnvPreset, ...ENV_PRESETS.local },\r\n { value: 'local-staging' as EnvPreset, ...ENV_PRESETS['local-staging'] },\r\n {\r\n value: 'local-staging-production' as EnvPreset,\r\n ...ENV_PRESETS['local-staging-production'],\r\n },\r\n ],\r\n });\r\n\r\n if (p.isCancel(preset)) {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n const projectRefs: Record<string, string | undefined> = {};\r\n let branchingContext: { parentProject: Project; branches: Branch[] } | null = null;\r\n\r\n // Step 4: If remote environments, get access token and fetch projects\r\n if (preset !== 'local') {\r\n // Check for existing token first\r\n let token = await getAccessToken();\r\n\r\n if (!token) {\r\n // CI mode: require token to be set\r\n if (opts.ci) {\r\n p.cancel('No access token found');\r\n console.error(pc.red('✗'), 'SUPABASE_ACCESS_TOKEN not set');\r\n console.error(pc.dim(' Set the environment variable or run interactively'));\r\n process.exit(1);\r\n }\r\n\r\n token = await getOrPromptForToken({ saveToken: true });\r\n if (!token) {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n } else {\r\n console.log(pc.green('✓'), 'Using saved access token');\r\n }\r\n\r\n // Create API client and validate token\r\n const client = createSupabaseClient(token as string);\r\n\r\n const spinner = p.spinner();\r\n spinner.start('Validating access token...');\r\n\r\n const isValid = await client.authenticate();\r\n if (!isValid) {\r\n spinner.stop('Invalid token');\r\n p.cancel('Invalid or expired access token');\r\n console.error(pc.dim(' Generate a new token at https://supabase.com/dashboard/account/tokens'));\r\n process.exit(1);\r\n }\r\n spinner.stop('Token validated');\r\n\r\n // Fetch projects once\r\n const projects = await client.getProjects();\r\n\r\n // Step 4.5: BRANCHING GATE - Check if user can set up staging + production\r\n if (preset === 'local-staging-production') {\r\n const gateResult = await checkBranchingGate(client, projects);\r\n\r\n if (!gateResult.canProceed) {\r\n // User chose to downgrade to local-staging or local\r\n // Re-prompt for their choice\r\n const fallbackPreset = await p.select({\r\n message: 'Continue with a different setup?',\r\n options: [\r\n { value: 'local-staging' as EnvPreset, label: 'Local + Staging', hint: 'Single remote environment' },\r\n { value: 'local' as EnvPreset, label: 'Local only', hint: 'No remote environments' },\r\n { value: 'cancel', label: 'Cancel setup' },\r\n ],\r\n });\r\n\r\n if (p.isCancel(fallbackPreset) || fallbackPreset === 'cancel') {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n preset = fallbackPreset as EnvPreset;\r\n } else if (gateResult.useBranching) {\r\n // User wants to use branching\r\n branchingContext = {\r\n parentProject: gateResult.parentProject,\r\n branches: gateResult.branches,\r\n };\r\n }\r\n }\r\n\r\n // Step 5: Select projects/branches for each environment\r\n if (preset === 'local-staging-production' && branchingContext) {\r\n // BRANCHING FLOW: main project = production, branch = staging\r\n console.log();\r\n console.log(pc.bold(`Configure ${pc.cyan('production')} environment:`));\r\n console.log(pc.green('✓'), `Using main project: ${pc.cyan(branchingContext.parentProject.name)}`);\r\n projectRefs['production'] = branchingContext.parentProject.id;\r\n displayProjectSummary(branchingContext.parentProject);\r\n\r\n console.log();\r\n console.log(pc.bold(`Configure ${pc.cyan('staging')} environment:`));\r\n const stagingRef = await selectOrCreateBranch(client, branchingContext.parentProject, branchingContext.branches);\r\n projectRefs['staging'] = stagingRef;\r\n } else if (preset !== 'local') {\r\n // STANDARD FLOW: select separate projects\r\n const envsToSetup = preset === 'local-staging' ? ['staging'] : ['production', 'staging'];\r\n\r\n for (const envName of envsToSetup) {\r\n console.log();\r\n console.log(pc.bold(`Configure ${pc.cyan(envName)} environment:`));\r\n\r\n if (projects.length === 0) {\r\n console.log(pc.yellow('⚠'), 'No projects found in your account');\r\n console.log(pc.dim(' You can add the project_ref manually to supacontrol.toml'));\r\n continue;\r\n }\r\n\r\n // Get list of already-selected project refs to exclude\r\n const alreadySelectedRefs = Object.values(projectRefs).filter(\r\n (ref): ref is string => ref !== undefined\r\n );\r\n\r\n const selectedRef = await selectProjectFromList(projects, envName, alreadySelectedRefs);\r\n if (selectedRef) {\r\n projectRefs[envName] = selectedRef;\r\n const project = projects.find((proj) => proj.id === selectedRef);\r\n if (project) {\r\n displayProjectSummary(project);\r\n }\r\n } else {\r\n console.log(pc.dim(` Skipped - configure ${envName}.project_ref in supacontrol.toml`));\r\n }\r\n }\r\n }\r\n\r\n // Show branching tip only if they didn't use branching\r\n if (!branchingContext) {\r\n showBranchingTip();\r\n }\r\n }\r\n\r\n // Step 6: Generate and write config\r\n const config: Config = {\r\n settings: {\r\n strict_mode: false,\r\n require_clean_git: false,\r\n show_migration_diff: true,\r\n },\r\n environments: createEnvironmentConfig(preset, projectRefs),\r\n };\r\n\r\n await writeConfig(config);\r\n console.log(pc.green('✓'), `Created ${pc.bold('supacontrol.toml')}`);\r\n\r\n // Step 7: Show next steps\r\n showNextSteps(preset);\r\n\r\n p.outro(pc.green('Setup complete!'));\r\n}\r\n\r\n/**\r\n * Select a project from the list using @clack/prompts\r\n * \r\n * @param projects - List of available projects\r\n * @param envName - Environment name being configured\r\n * @param alreadySelectedRefs - Project refs already selected for other environments (will be excluded)\r\n */\r\nasync function selectProjectFromList(\r\n projects: Project[],\r\n envName: string,\r\n alreadySelectedRefs: string[] = []\r\n): Promise<string | null> {\r\n // Filter out already-selected projects\r\n const availableProjects = projects.filter(\r\n (project) => !alreadySelectedRefs.includes(project.id)\r\n );\r\n\r\n // Check if we have any projects left after filtering\r\n if (availableProjects.length === 0 && alreadySelectedRefs.length > 0) {\r\n // All projects are already used - show hard stop\r\n p.note(\r\n [\r\n `${pc.red('All your Supabase projects are already assigned to other environments.')}`,\r\n '',\r\n 'Each environment MUST have a unique project_ref to prevent',\r\n 'accidentally running operations on the wrong database.',\r\n '',\r\n `${pc.bold('Options:')}`,\r\n '',\r\n `${pc.cyan('1.')} Create a new Supabase project for ${envName}:`,\r\n ` ${pc.dim('https://supabase.com/dashboard/projects')}`,\r\n '',\r\n `${pc.cyan('2.')} Use Supabase Branching (recommended for teams):`,\r\n ` ${pc.dim('https://supabase.com/docs/guides/platform/branching')}`,\r\n ` Branching creates isolated environments from a single project.`,\r\n '',\r\n `${pc.cyan('3.')} Skip ${envName} for now and configure manually later.`,\r\n ].join('\\n'),\r\n `${pc.yellow('⚠')} No available projects for ${envName}`\r\n );\r\n\r\n const skipConfirm = await p.confirm({\r\n message: `Skip ${envName} configuration for now?`,\r\n initialValue: true,\r\n });\r\n\r\n if (p.isCancel(skipConfirm)) {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n if (skipConfirm) {\r\n return null;\r\n }\r\n\r\n // User said no to skipping - cancel setup entirely\r\n p.cancel('Setup cancelled - create additional projects and try again');\r\n process.exit(0);\r\n }\r\n\r\n const sortedProjects = [...availableProjects].sort((a, b) => {\r\n if (a.status === 'ACTIVE_HEALTHY' && b.status !== 'ACTIVE_HEALTHY') return -1;\r\n if (b.status === 'ACTIVE_HEALTHY' && a.status !== 'ACTIVE_HEALTHY') return 1;\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n const options = [\r\n ...sortedProjects.map((project) => {\r\n const statusLabel = project.status === 'ACTIVE_HEALTHY'\r\n ? pc.green('[Active]')\r\n : project.status === 'PAUSED'\r\n ? pc.yellow('[Paused]')\r\n : pc.red(`[${project.status}]`);\r\n return {\r\n value: project.id,\r\n label: `${project.name} ${pc.dim(`(${project.region})`)} ${statusLabel}`,\r\n hint: `ref: ${project.id}`,\r\n };\r\n }),\r\n {\r\n value: '__skip__',\r\n label: pc.dim('Skip (configure manually later)'),\r\n hint: `Set ${envName}.project_ref in supacontrol.toml`,\r\n },\r\n ];\r\n\r\n const selected = await p.select({\r\n message: `Select project for ${envName}:`,\r\n options,\r\n });\r\n\r\n if (p.isCancel(selected)) {\r\n p.cancel('Setup cancelled');\r\n process.exit(0);\r\n }\r\n\r\n if (selected === '__skip__') {\r\n return null;\r\n }\r\n\r\n return selected as string;\r\n}\r\n\r\n/**\r\n * Create the init command\r\n */\r\nexport function createInitCommand(): Command {\r\n const cmd = new Command('init')\r\n .description('Initialize SupaControl in your project')\r\n .action(withErrorHandling(initAction));\r\n\r\n return cmd;\r\n}\r\n","import * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport { type Project, type SupabaseManagementClient } from './supabase-client.js';\n\n/**\n * Format a date string for display\n */\nfunction formatDate(dateString: string): string {\n const date = new Date(dateString);\n return date.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n}\n\n/**\n * Format a project for display in the selection list\n */\nfunction formatProjectLabel(project: Project): string {\n const statusLabel = project.status === 'ACTIVE_HEALTHY' \n ? pc.green('[Active]') \n : project.status === 'PAUSED'\n ? pc.yellow('[Paused]')\n : pc.red(`[${project.status}]`);\n \n return `${project.name} ${pc.dim(`(${project.region})`)} ${statusLabel}`;\n}\n\n/**\n * Format project hint (shown below the label)\n */\nfunction formatProjectHint(project: Project): string {\n return `ref: ${project.id} • created: ${formatDate(project.created_at)}`;\n}\n\n/**\n * Special value for skipping project selection\n */\nconst SKIP_VALUE = '__skip__';\n\n/**\n * Fetch projects from Supabase and display interactive selector\n * \n * @param client - Authenticated Supabase Management API client\n * @returns Selected project ref, or null if skipped\n */\nexport async function fetchAndDisplayProjects(\n client: SupabaseManagementClient\n): Promise<string | null> {\n const spinner = p.spinner();\n spinner.start('Fetching your Supabase projects...');\n\n let projects: Project[];\n try {\n projects = await client.getProjects();\n spinner.stop('Found projects');\n } catch (error) {\n spinner.stop('Failed to fetch projects');\n throw error;\n }\n\n if (projects.length === 0) {\n p.note(\n 'No projects found in your Supabase account.\\n' +\n 'Create a project at https://supabase.com/dashboard',\n 'No projects'\n );\n return null;\n }\n\n // Sort projects: active first, then by name\n const sortedProjects = [...projects].sort((a, b) => {\n // Active healthy projects first\n if (a.status === 'ACTIVE_HEALTHY' && b.status !== 'ACTIVE_HEALTHY') return -1;\n if (b.status === 'ACTIVE_HEALTHY' && a.status !== 'ACTIVE_HEALTHY') return 1;\n // Then by name\n return a.name.localeCompare(b.name);\n });\n\n // Build selection options\n const options = [\n ...sortedProjects.map((project) => ({\n value: project.id,\n label: formatProjectLabel(project),\n hint: formatProjectHint(project),\n })),\n {\n value: SKIP_VALUE,\n label: pc.dim('Skip (configure manually later)'),\n hint: 'You can set project_ref in supacontrol.toml',\n },\n ];\n\n const selected = await p.select({\n message: 'Select a project for this environment:',\n options,\n });\n\n if (p.isCancel(selected)) {\n return null;\n }\n\n if (selected === SKIP_VALUE) {\n return null;\n }\n\n return selected as string;\n}\n\n/**\n * Display a summary of the selected project\n */\nexport function displayProjectSummary(project: Project): void {\n const statusText = project.status === 'ACTIVE_HEALTHY'\n ? pc.green('Active')\n : project.status === 'PAUSED'\n ? pc.yellow('Paused')\n : pc.red(project.status);\n\n console.log();\n console.log(pc.bold('Selected project:'));\n console.log(` ${pc.cyan('Name:')} ${project.name}`);\n console.log(` ${pc.cyan('Ref:')} ${project.id}`);\n console.log(` ${pc.cyan('Region:')} ${project.region}`);\n console.log(` ${pc.cyan('Status:')} ${statusText}`);\n console.log(` ${pc.cyan('Created:')} ${formatDate(project.created_at)}`);\n if (project.database) {\n console.log(` ${pc.cyan('DB Host:')} ${pc.dim(project.database.host)}`);\n }\n console.log();\n}\n\n/**\n * Filter projects by search query (name or ref)\n */\nexport function filterProjects(projects: Project[], query: string): Project[] {\n const lowerQuery = query.toLowerCase();\n return projects.filter(\n (p) =>\n p.name.toLowerCase().includes(lowerQuery) ||\n p.id.toLowerCase().includes(lowerQuery)\n );\n}\n"],"mappings":"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","names":["Command","pc","createRequire","Command","pc","readFile","resolve","dirname","parse","pc","z","ProtectedOperation","SettingsSchema","EnvironmentSchema","ConfigSchema","RawConfigSchema","isEnvironmentLocked","envName","env","CONFIG_PATHS","ConfigError","message","filePath","cause","findConfigFile","cwd","configPath","fullPath","resolve","readFile","error","formatZodError","issue","path","pc","loadConfig","searchDir","result","rawData","parse","rawResult","RawConfigSchema","configResult","ConfigSchema","loadConfigOrExit","config","getEnvironmentByName","envName","config","env","listEnvironments","resolveEnvironmentByProjectRef","linkedRef","config","environments","name","env","execa","branchCache","dirtyCache","isRepoCache","clearGitCache","runGit","args","cwd","result","isGitRepository","getCurrentBranch","hasUncommittedChanges","readFile","resolve","DESTRUCTIVE_OPERATIONS","allowed","options","blocked","reason","requiresConfirmation","confirmWord","combineResults","results","result","riskOrder","highestRisk","needsConfirmation","suggestions","currentIndex","PROJECT_REF_PATH","linkedProjectCache","clearProjectCache","getCurrentLinkedProject","cwd","searchDir","refPath","resolve","readFile","error","checkProjectMatch","context","environment","environmentName","allowed","linkedProject","blocked","execa","pc","supabaseAvailable","isSupabaseCLIInstalled","getSupabaseVersion","result","stdout","match","runSupabase","args","options","cwd","stream","env","execaOptions","error","requireSupabaseCLI","isSupabaseCLIInstalled","pc","homedir","join","readFile","writeFile","mkdir","chmod","p","pc","TOKEN_ENV_VAR","CONFIG_DIR","CREDENTIALS_FILE","TOKEN_URL","getCredentialsPath","getConfigDir","getAccessToken","envToken","credentialsPath","token","saveAccessToken","configDir","promptForToken","value","getOrPromptForToken","options","skipPrompt","saveToken","existingToken","shouldSave","z","API_BASE","RATE_LIMIT_PER_MINUTE","ProjectStatus","OrganizationPlan","OrganizationSchema","ProjectSchema","BranchSchema","SupabaseAPIError","message","statusCode","response","SupabaseManagementClient","accessToken","endpoint","options","url","errorMessage","responseBody","now","windowDuration","waitTime","resolve","error","projects","item","result","projectRef","name","p","slug","project","branches","branchName","branchId","plan","org","isPaidPlan","isFreePlan","b","createSupabaseClient","createStatusCommand","Command","runStatus","resolveLinkedRef","linkedRef","projects","client","directMatch","p","project","matchingBranch","b","pc","config","loadConfig","getCurrentLinkedProject","linkedInfo","token","getAccessToken","apiError","createSupabaseClient","activeEnv","resolveEnvironmentByProjectRef","isLocked","isEnvironmentLocked","lockIcon","statusText","branch","getCurrentBranch","dirty","hasUncommittedChanges","dirtyIndicator","envNames","name","env","isActive","marker","activeLabel","isSupabaseCLIInstalled","version","getSupabaseVersion","Command","pc","pc","checkLock","context","environmentName","environment","operation","DESTRUCTIVE_OPERATIONS","isEnvironmentLocked","blocked","allowed","OPERATION_RISK","checkOperation","context","operation","environment","environmentName","isCI","riskLevel","allowed","confirmWord","requiresConfirmation","getOperationRiskLevel","checkCleanGit","context","config","hasUncommittedChanges","operation","DESTRUCTIVE_OPERATIONS","blocked","allowed","p","pc","RISK_DISPLAY","s","OPERATION_DESCRIPTIONS","requestConfirmation","options","environmentName","operation","riskLevel","confirmWord","isCI","reason","riskDisplay","description","word","response","value","confirmed","showOperationSummary","projectRef","runGuards","context","results","lockResult","checkLock","printGuardError","operationResult","checkOperation","projectResult","checkProjectMatch","gitResult","checkCleanGit","combined","combineResults","riskLevel","getOperationRiskLevel","confirmed","cancelled","requestConfirmation","showOperationSummary","result","pc","suggestion","readdir","readFile","writeFile","mkdir","rename","resolve","join","basename","createHash","pc","p","hashContent","content","createHash","getLocalMigrations","migrationsDir","resolve","readdir","f","getLocalMigrationDetails","match","timestamp","name","join","a","b","getRemoteMigrations","result","runSupabase","lines","migrations","line","parts","remoteCol","checkMigrationSync","local","remote","localTimestamps","m","remoteMissing","r","localMissing","l","error","fetchRemoteMigrations","mkdir","readMigrationContent","migration","readFile","computeSimpleDiff","localContent","remoteContent","localLines","l","remoteLines","additions","removals","line","displayDiff","conflict","pc","resolveConflict","choice","generateMigrationTimestamp","now","createMigrationFromDiff","latestRemoteTimestamp","migrationsDir","resolve","timestamp","nextTimestamp","nameMatch","baseName","newFilename","newPath","join","header","meaningfulAdditions","content","writeFile","applyConflictResolution","resolution","result","basename","remotePath","reorderLocalMigrations","localOnly","renamed","needsReorder","b","m","migration","newTimestamp","rename","syncMigrations","pc","runSupabase","interactiveMigrationSync","status","checkMigrationSync","pc","remoteMigrations","getRemoteMigrations","latestRemote","ts","localOnlyMigrations","getLocalMigrationDetails","m","renames","reorderLocalMigrations","proceed","localBefore","localBeforeMap","readMigrationContent","spinner","fetchResult","fetchRemoteMigrations","conflicts","timestamp","localMigration","remotePath","remoteContent","readFile","localContent","localHash","hashContent","remoteHash","additions","removals","computeSimpleDiff","sortedRemote","latestRemoteTimestamp","i","conflict","conflictNum","totalConflicts","resolution","resolveConflict","restoreSpinner","writeFile","actionSpinner","actionLabel","applied","applyConflictResolution","migrationsDir","resolve","targetPath","join","finalLocal","finalRemote","stillMissing","f","createPushCommand","Command","opts","runPush","options","clearGitCache","clearProjectCache","requireSupabaseCLI","config","loadConfigOrExit","gitBranch","getCurrentBranch","uncommittedChanges","hasUncommittedChanges","resolved","getEnvironmentByName","pc","linkedRef","getCurrentLinkedProject","resolveEnvironmentByProjectRef","context","guardResult","runGuards","syncResult","interactiveMigrationSync","diffResult","runSupabase","lines","createCount","l","alterCount","dropCount","result","Command","pc","p","createResetCommand","Command","opts","runReset","options","clearGitCache","clearProjectCache","requireSupabaseCLI","config","loadConfigOrExit","gitBranch","getCurrentBranch","uncommittedChanges","hasUncommittedChanges","resolved","getEnvironmentByName","pc","linkedRef","getCurrentLinkedProject","resolveEnvironmentByProjectRef","isLocked","isEnvironmentLocked","forceConfirm","context","guardResult","runGuards","confirmWord","finalConfirm","value","resetArgs","result","runSupabase","Command","pc","createPullCommand","Command","opts","runPull","options","clearGitCache","clearProjectCache","requireSupabaseCLI","config","loadConfigOrExit","gitBranch","getCurrentBranch","uncommittedChanges","hasUncommittedChanges","resolved","getEnvironmentByName","pc","linkedRef","getCurrentLinkedProject","resolveEnvironmentByProjectRef","context","guardResult","runGuards","result","runSupabase","Command","pc","p","createSwitchCommand","Command","envName","runSwitch","clearProjectCache","requireSupabaseCLI","config","loadConfigOrExit","resolved","getEnvironmentByName","pc","name","listEnvironments","env","currentProject","getCurrentLinkedProject","runSupabase","result","checkAndSyncMigrations","syncStatus","checkMigrationSync","migration","shouldSync","syncMigrations","Command","pc","p","writeFile","access","constants","resolve","CONFIG_FILENAME","settingsToToml","settings","environmentToToml","name","env","lines","b","o","configToToml","config","sections","envNames","configExists","cwd","searchDir","configPath","resolve","CONFIG_FILENAME","access","constants","writeConfig","config","toml","configToToml","writeFile","createLockCommand","Command","envName","opts","runLock","createUnlockCommand","runUnlock","_options","clearGitCache","config","loadConfigOrExit","targetEnv","resolveTargetEnvironment","env","pc","isEnvironmentLocked","writeConfig","options","confirmed","getEnvironmentByName","name","listEnvironments","clearProjectCache","linkedRef","getCurrentLinkedProject","resolved","resolveEnvironmentByProjectRef","Command","pc","access","constants","resolve","createDoctorCommand","Command","options","runDoctor","pc","results","checkSupabaseCLI","checkGitRepository","checkSupacontrolConfig","checkSupabaseProject","checkLinkedProject","checkEnvironmentSafety","checkMigrationsFolder","passes","r","warns","fails","result","printResult","verbose","icon","detail","isSupabaseCLIInstalled","version","getSupabaseVersion","isGitRepository","branch","getCurrentBranch","config","loadConfig","envCount","listEnvironments","supabasePath","resolve","access","constants","linkedProject","getCurrentLinkedProject","envs","lockedEnvs","unlockedProdEnvs","name","env","isEnvironmentLocked","e","migrationsPath","Command","access","constants","resolve","p","pc","p","pc","formatDate","dateString","displayProjectSummary","project","statusText","pc","formatDate","checkSupabaseInit","configPath","resolve","access","constants","ENV_PRESETS","createEnvironmentConfig","preset","projectRefs","environments","showBranchingTip","pc","checkBranchingGate","client","projects","fallback","spinner","projectsWithBranching","activeProjects","proj","BATCH_SIZE","BATCH_DELAY_MS","i","batch","results","capability","hasMultipleProjects","hasBranchingProjects","strategyOptions","branchingCount","firstProject","choice","url","strategy","selectedBranchingProject","projectChoice","branches","found","item","selectOrCreateBranch","parentProject","existingBranches","nonDefaultBranches","b","existingStagingBranch","createNewBranch","singleBranch","isStagingNamed","branchOptions","options","sortedBranches","a","branch","selected","suggestedName","branchName","value","newBranch","syncMigrationsFromBranch","error","errorMessage","branchRef","runSupabase","showNextSteps","steps","step","initAction","opts","program","configExists","overwrite","branchingContext","token","getAccessToken","getOrPromptForToken","createSupabaseClient","gateResult","fallbackPreset","displayProjectSummary","stagingRef","envsToSetup","envName","alreadySelectedRefs","ref","selectedRef","selectProjectFromList","project","config","writeConfig","availableProjects","skipConfirm","statusLabel","createInitCommand","Command","withErrorHandling","require","createRequire","packageJson","program","Command","withErrorHandling","fn","args","error","opts","pc","program","createInitCommand","createStatusCommand","createPushCommand","createResetCommand","createPullCommand","createSwitchCommand","createLockCommand","createUnlockCommand","createDoctorCommand"]}