episoda 0.2.11 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../core/src/git-validator.ts","../../core/src/git-parser.ts","../../core/src/git-executor.ts","../../core/src/version.ts","../../core/src/websocket-client.ts","../../core/src/auth.ts","../../core/src/errors.ts","../../core/src/index.ts","../src/index.ts","../src/commands/dev.ts","../src/framework-detector.ts","../src/output.ts","../src/daemon/daemon-manager.ts","../src/ipc/ipc-client.ts","../src/utils/port-check.ts","../src/commands/auth.ts","../src/daemon/machine-id.ts","../src/git-helpers/git-credential-helper.ts","../src/commands/status.ts","../src/commands/stop.ts"],"sourcesContent":["/**\n * Git Input Validation Utilities\n *\n * Validates git command inputs to prevent injection and ensure safety.\n * Interface-agnostic: Returns boolean/error codes, not formatted messages.\n */\n\nimport { ErrorCode } from './command-protocol'\n\n/**\n * Validate branch name format\n * Git branch naming rules:\n * - Cannot start with - or /\n * - Cannot contain .. or @{ or \\\n * - Cannot end with .lock or /\n * - Cannot contain spaces or control characters\n * - Cannot be just @\n */\nexport function validateBranchName(branchName: string): { valid: boolean; error?: ErrorCode } {\n if (!branchName || branchName.trim().length === 0) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Check for invalid characters and patterns\n const invalidPatterns = [\n /^\\-/, // Starts with -\n /^\\//, // Starts with /\n /\\.\\./, // Contains ..\n /@\\{/, // Contains @{\n /\\\\/, // Contains backslash\n /\\.lock$/, // Ends with .lock\n /\\/$/, // Ends with /\n /\\s/, // Contains whitespace\n /[\\x00-\\x1F\\x7F]/, // Contains control characters\n /[\\*\\?\\[\\]~\\^:]/, // Contains special characters\n ]\n\n for (const pattern of invalidPatterns) {\n if (pattern.test(branchName)) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n }\n\n // Branch name cannot be just @\n if (branchName === '@') {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Maximum reasonable length (Git allows up to 255, but let's be conservative)\n if (branchName.length > 200) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n return { valid: true }\n}\n\n/**\n * Validate commit message\n * Basic validation to ensure message is not empty\n */\nexport function validateCommitMessage(message: string): { valid: boolean; error?: ErrorCode } {\n if (!message || message.trim().length === 0) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Reasonable length check\n if (message.length > 10000) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n return { valid: true }\n}\n\n/**\n * Validate file paths\n * Basic validation to prevent directory traversal\n */\nexport function validateFilePaths(files: string[]): { valid: boolean; error?: ErrorCode } {\n if (!files || files.length === 0) {\n return { valid: true }\n }\n\n for (const file of files) {\n // Check for null bytes (command injection)\n if (file.includes('\\0')) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Check for control characters\n if (/[\\x00-\\x1F\\x7F]/.test(file)) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Files should not be empty\n if (file.trim().length === 0) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Sanitize command arguments to prevent injection\n * Returns sanitized array of arguments\n */\nexport function sanitizeArgs(args: string[]): string[] {\n return args.map(arg => {\n // Remove null bytes\n return arg.replace(/\\0/g, '')\n })\n}\n","/**\n * Git Command Output Parser\n *\n * Parses git command output and error messages into structured data.\n * Interface-agnostic: Returns structured objects, not formatted strings.\n */\n\nimport { ErrorCode } from './command-protocol'\n\n/**\n * Parse git status output to extract uncommitted files\n */\nexport function parseGitStatus(output: string): {\n uncommittedFiles: string[]\n isClean: boolean\n currentBranch?: string\n} {\n const lines = output.split('\\n')\n const uncommittedFiles: string[] = []\n let currentBranch: string | undefined\n\n for (const line of lines) {\n // Porcelain format branch line: ## branch-name or ## HEAD (no branch)\n if (line.startsWith('## ')) {\n const branchInfo = line.substring(3)\n // Extract branch name (handle \"branch...origin/branch\" format)\n const branchMatch = branchInfo.match(/^([^\\s.]+)/)\n if (branchMatch && branchMatch[1]) {\n currentBranch = branchMatch[1] === 'HEAD' ? 'HEAD (detached)' : branchMatch[1]\n }\n continue\n }\n\n // Parse modified/added/deleted files\n // Porcelain format: \"XY filename\" where X is staged, Y is unstaged\n // Format: \" M file.txt\" or \"M file.txt\" or \"?? file.txt\" or \"MM file.txt\"\n if (line.length >= 3) {\n const status = line.substring(0, 2)\n const filePath = line.substring(3).trim()\n\n // Check if there's any status (not all spaces)\n if (status.trim().length > 0 && filePath.length > 0) {\n uncommittedFiles.push(filePath)\n }\n }\n }\n\n const isClean = uncommittedFiles.length === 0\n\n return { uncommittedFiles, isClean, currentBranch }\n}\n\n/**\n * Parse merge conflict output to extract conflicting files\n */\nexport function parseMergeConflicts(output: string): string[] {\n const lines = output.split('\\n')\n const conflictingFiles: string[] = []\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Look for conflict markers\n if (trimmed.startsWith('CONFLICT')) {\n // Format: \"CONFLICT (content): Merge conflict in file.txt\"\n const match = trimmed.match(/CONFLICT.*in (.+)$/)\n if (match && match[1]) {\n conflictingFiles.push(match[1])\n }\n }\n\n // Also check for \"both modified\" status\n if (trimmed.startsWith('UU ')) {\n conflictingFiles.push(trimmed.substring(3).trim())\n }\n }\n\n return conflictingFiles\n}\n\n/**\n * Map git error output to ErrorCode\n */\nexport function parseGitError(stderr: string, stdout: string, exitCode: number): ErrorCode {\n const combinedOutput = `${stderr}\\n${stdout}`.toLowerCase()\n\n // Check for git not installed\n if (combinedOutput.includes('git: command not found') ||\n combinedOutput.includes(\"'git' is not recognized\")) {\n return 'GIT_NOT_INSTALLED'\n }\n\n // Check for not a git repository\n if (combinedOutput.includes('not a git repository') ||\n combinedOutput.includes('not a git repo')) {\n return 'NOT_GIT_REPO'\n }\n\n // Check for merge conflicts\n if (combinedOutput.includes('conflict') ||\n combinedOutput.includes('merge conflict') ||\n exitCode === 1 && combinedOutput.includes('automatic merge failed')) {\n return 'MERGE_CONFLICT'\n }\n\n // Check for uncommitted changes\n if (combinedOutput.includes('please commit your changes') ||\n combinedOutput.includes('would be overwritten') ||\n combinedOutput.includes('cannot checkout') && combinedOutput.includes('files would be overwritten')) {\n return 'UNCOMMITTED_CHANGES'\n }\n\n // Check for authentication failures\n if (combinedOutput.includes('authentication failed') ||\n combinedOutput.includes('could not read username') ||\n combinedOutput.includes('permission denied') ||\n combinedOutput.includes('could not read password') ||\n combinedOutput.includes('fatal: authentication failed')) {\n return 'AUTH_FAILURE'\n }\n\n // Check for network errors\n if (combinedOutput.includes('could not resolve host') ||\n combinedOutput.includes('failed to connect') ||\n combinedOutput.includes('network is unreachable') ||\n combinedOutput.includes('connection timed out') ||\n combinedOutput.includes('could not read from remote')) {\n return 'NETWORK_ERROR'\n }\n\n // Check for branch not found\n if (combinedOutput.includes('did not match any file') ||\n combinedOutput.includes('branch') && combinedOutput.includes('not found') ||\n combinedOutput.includes('pathspec') && combinedOutput.includes('did not match')) {\n return 'BRANCH_NOT_FOUND'\n }\n\n // Check for branch already exists\n if (combinedOutput.includes('already exists') ||\n combinedOutput.includes('a branch named') && combinedOutput.includes('already exists')) {\n return 'BRANCH_ALREADY_EXISTS'\n }\n\n // Check for push rejected\n if (combinedOutput.includes('push rejected') ||\n combinedOutput.includes('failed to push') ||\n combinedOutput.includes('rejected') && combinedOutput.includes('non-fast-forward') ||\n combinedOutput.includes('updates were rejected')) {\n return 'PUSH_REJECTED'\n }\n\n // Check for timeout (exit code 124 is GNU timeout, 143 is SIGTERM)\n if (exitCode === 124 || exitCode === 143) {\n return 'COMMAND_TIMEOUT'\n }\n\n // Default to unknown error\n return 'UNKNOWN_ERROR'\n}\n\n/**\n * Extract branch name from git output\n */\nexport function extractBranchName(output: string): string | undefined {\n const lines = output.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Check for \"Switched to branch 'name'\"\n let match = trimmed.match(/Switched to (?:a new )?branch '(.+)'/)\n if (match && match[1]) {\n return match[1]\n }\n\n // Check for \"On branch name\"\n match = trimmed.match(/On branch (.+)/)\n if (match && match[1]) {\n return match[1]\n }\n }\n\n return undefined\n}\n\n/**\n * Check if output indicates detached HEAD state\n */\nexport function isDetachedHead(output: string): boolean {\n return output.toLowerCase().includes('head detached') ||\n output.toLowerCase().includes('you are in \\'detached head\\' state')\n}\n\n/**\n * Parse remote tracking information\n */\nexport function parseRemoteTracking(output: string): {\n hasUpstream: boolean\n remoteBranch?: string\n remote?: string\n} {\n const lines = output.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Check for \"Branch 'name' set up to track remote branch 'remote/branch'\"\n const match = trimmed.match(/set up to track remote branch '(.+?)'(?: from '(.+?)')?/)\n if (match) {\n return {\n hasUpstream: true,\n remoteBranch: match[1],\n remote: match[2] || 'origin'\n }\n }\n\n // Check for existing upstream: \"Your branch is up to date with 'origin/main'\"\n const upstreamMatch = trimmed.match(/(?:up to date with|ahead of|behind) '(.+?)\\/(.+?)'/)\n if (upstreamMatch) {\n return {\n hasUpstream: true,\n remote: upstreamMatch[1],\n remoteBranch: upstreamMatch[2]\n }\n }\n }\n\n return { hasUpstream: false }\n}\n","/**\n * Git Command Executor\n *\n * Executes git commands with error handling and returns structured results.\n * Interface-agnostic: Returns structured data, not formatted strings.\n */\n\nimport { exec } from 'child_process'\nimport { promisify } from 'util'\nimport { GitCommand, ExecutionResult, ExecutionOptions, ErrorCode } from './command-protocol'\nimport {\n validateBranchName,\n validateCommitMessage,\n validateFilePaths,\n sanitizeArgs\n} from './git-validator'\nimport {\n parseGitError,\n parseGitStatus,\n parseMergeConflicts,\n extractBranchName,\n isDetachedHead,\n parseRemoteTracking\n} from './git-parser'\n\nconst execAsync = promisify(exec)\n\n/**\n * Executes git commands with error handling\n *\n * DESIGN PRINCIPLES:\n * - Interface-agnostic: Returns structured data, not formatted strings\n * - No console.log: Let the interface handle output\n * - Error codes: Return error codes, not messages\n * - Reusable: Can be used by CLI, MCP, or any other interface\n */\nexport class GitExecutor {\n /**\n * Execute a git command\n * @param command - The git command to execute\n * @param options - Execution options (timeout, cwd, etc.)\n * @returns Structured result with success/error details\n */\n async execute(\n command: GitCommand,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Validate git is installed\n const gitInstalled = await this.validateGitInstalled()\n if (!gitInstalled) {\n return {\n success: false,\n error: 'GIT_NOT_INSTALLED'\n }\n }\n\n // Determine working directory\n const cwd = options?.cwd || process.cwd()\n\n // Validate this is a git repository (except for init-like commands)\n const isGitRepo = await this.isGitRepository(cwd)\n if (!isGitRepo) {\n return {\n success: false,\n error: 'NOT_GIT_REPO'\n }\n }\n\n // Route to appropriate handler based on action\n switch (command.action) {\n case 'checkout':\n return await this.executeCheckout(command, cwd, options)\n case 'create_branch':\n return await this.executeCreateBranch(command, cwd, options)\n case 'commit':\n return await this.executeCommit(command, cwd, options)\n case 'push':\n return await this.executePush(command, cwd, options)\n case 'status':\n return await this.executeStatus(cwd, options)\n case 'pull':\n return await this.executePull(command, cwd, options)\n case 'delete_branch':\n return await this.executeDeleteBranch(command, cwd, options)\n // EP597: Read operations for production local dev mode\n case 'branch_exists':\n return await this.executeBranchExists(command, cwd, options)\n case 'branch_has_commits':\n return await this.executeBranchHasCommits(command, cwd, options)\n // EP598: Main branch check for production\n case 'main_branch_check':\n return await this.executeMainBranchCheck(cwd, options)\n // EP599: Get commits for branch\n case 'get_commits':\n return await this.executeGetCommits(command, cwd, options)\n // EP599: Advanced operations for move-to-module and discard-main-changes\n case 'stash':\n return await this.executeStash(command, cwd, options)\n case 'reset':\n return await this.executeReset(command, cwd, options)\n case 'merge':\n return await this.executeMerge(command, cwd, options)\n case 'cherry_pick':\n return await this.executeCherryPick(command, cwd, options)\n case 'clean':\n return await this.executeClean(command, cwd, options)\n case 'add':\n return await this.executeAdd(command, cwd, options)\n case 'fetch':\n return await this.executeFetch(command, cwd, options)\n // EP599-3: Composite operations\n case 'move_to_module':\n return await this.executeMoveToModule(command, cwd, options)\n case 'discard_main_changes':\n return await this.executeDiscardMainChanges(cwd, options)\n // EP523: Branch sync operations\n case 'sync_status':\n return await this.executeSyncStatus(command, cwd, options)\n case 'sync_main':\n return await this.executeSyncMain(cwd, options)\n case 'rebase_branch':\n return await this.executeRebaseBranch(command, cwd, options)\n case 'rebase_abort':\n return await this.executeRebaseAbort(cwd, options)\n case 'rebase_continue':\n return await this.executeRebaseContinue(cwd, options)\n case 'rebase_status':\n return await this.executeRebaseStatus(cwd, options)\n default:\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: 'Unknown command action'\n }\n }\n } catch (error) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error instanceof Error ? error.message : 'Unknown error occurred'\n }\n }\n }\n\n /**\n * Execute checkout command\n */\n private async executeCheckout(\n command: { branch: string; create?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Check for uncommitted changes first\n const statusResult = await this.executeStatus(cwd, options)\n if (statusResult.success && statusResult.details?.uncommittedFiles?.length) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n details: {\n uncommittedFiles: statusResult.details.uncommittedFiles\n }\n }\n }\n\n // Build command\n const args = ['checkout']\n if (command.create) {\n args.push('-b')\n }\n args.push(command.branch)\n\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * Execute create_branch command\n */\n private async executeCreateBranch(\n command: { branch: string; from?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Validate source branch if provided\n if (command.from) {\n const fromValidation = validateBranchName(command.from)\n if (!fromValidation.valid) {\n return {\n success: false,\n error: fromValidation.error || 'UNKNOWN_ERROR'\n }\n }\n }\n\n // Build command - use checkout -b to create AND checkout the branch\n // This ensures the user is on the new branch immediately\n const args = ['checkout', '-b', command.branch]\n if (command.from) {\n args.push(command.from)\n }\n\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * Execute commit command\n */\n private async executeCommit(\n command: { message: string; files?: string[] },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate commit message\n const validation = validateCommitMessage(command.message)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Validate file paths if provided\n if (command.files) {\n const fileValidation = validateFilePaths(command.files)\n if (!fileValidation.valid) {\n return {\n success: false,\n error: fileValidation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Stage specific files first\n for (const file of command.files) {\n const addResult = await this.runGitCommand(['add', file], cwd, options)\n if (!addResult.success) {\n return addResult\n }\n }\n } else {\n // Stage all changes\n const addResult = await this.runGitCommand(['add', '-A'], cwd, options)\n if (!addResult.success) {\n return addResult\n }\n }\n\n // Execute commit\n const args = ['commit', '-m', command.message]\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * Execute push command\n * EP769: Added force parameter for pushing rebased branches\n */\n private async executePush(\n command: { branch: string; setUpstream?: boolean; force?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Build command\n const args = ['push']\n // EP769: Add --force flag for rebased branches\n if (command.force) {\n args.push('--force')\n }\n if (command.setUpstream) {\n args.push('-u', 'origin', command.branch)\n } else {\n args.push('origin', command.branch)\n }\n\n // Configure git credential helper for GitHub token if provided\n const env = { ...process.env }\n if (options?.githubToken) {\n env.GIT_ASKPASS = 'echo'\n env.GIT_USERNAME = 'x-access-token'\n env.GIT_PASSWORD = options.githubToken\n }\n\n return await this.runGitCommand(args, cwd, { ...options, env })\n }\n\n /**\n * Execute status command\n */\n private async executeStatus(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n const result = await this.runGitCommand(['status', '--porcelain', '-b'], cwd, options)\n\n if (result.success && result.output) {\n const statusInfo = parseGitStatus(result.output)\n return {\n success: true,\n output: result.output,\n details: {\n uncommittedFiles: statusInfo.uncommittedFiles,\n branchName: statusInfo.currentBranch\n }\n }\n }\n\n return result\n }\n\n /**\n * Execute pull command\n */\n private async executePull(\n command: { branch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name if provided\n if (command.branch) {\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n }\n\n // Check for uncommitted changes first\n const statusResult = await this.executeStatus(cwd, options)\n if (statusResult.success && statusResult.details?.uncommittedFiles?.length) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n details: {\n uncommittedFiles: statusResult.details.uncommittedFiles\n }\n }\n }\n\n // Build command\n const args = ['pull']\n if (command.branch) {\n args.push('origin', command.branch)\n }\n\n const result = await this.runGitCommand(args, cwd, options)\n\n // Check for merge conflicts\n if (!result.success && result.output) {\n const conflicts = parseMergeConflicts(result.output)\n if (conflicts.length > 0) {\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: result.output,\n details: {\n conflictingFiles: conflicts\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Execute delete_branch command\n */\n private async executeDeleteBranch(\n command: { branch: string; force?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Build command\n const args = ['branch']\n args.push(command.force ? '-D' : '-d')\n args.push(command.branch)\n\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * EP597: Execute branch_exists command\n * Checks if a branch exists locally and/or remotely\n */\n private async executeBranchExists(\n command: { branch: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n try {\n let isLocal = false\n let isRemote = false\n\n // Check local branches\n try {\n const { stdout: localBranches } = await execAsync('git branch --list', { cwd, timeout: options?.timeout || 10000 })\n isLocal = localBranches.split('\\n').some(line => {\n const branchName = line.replace(/^\\*?\\s*/, '').trim()\n return branchName === command.branch\n })\n } catch {\n // Ignore errors - branch doesn't exist locally\n }\n\n // Check remote branches\n try {\n const { stdout: remoteBranches } = await execAsync(\n `git ls-remote --heads origin ${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n isRemote = remoteBranches.trim().length > 0\n } catch {\n // Ignore errors - can't check remote (might be network issue)\n }\n\n const branchExists = isLocal || isRemote\n\n return {\n success: true,\n output: branchExists ? `Branch ${command.branch} exists` : `Branch ${command.branch} does not exist`,\n details: {\n branchName: command.branch,\n branchExists,\n isLocal,\n isRemote\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check branch existence'\n }\n }\n }\n\n /**\n * EP597: Execute branch_has_commits command\n * Checks if a branch has commits ahead of the base branch (default: main)\n */\n private async executeBranchHasCommits(\n command: { branch: string; baseBranch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n const baseBranch = command.baseBranch || 'main'\n\n try {\n // Use git cherry to find commits unique to the branch\n // This shows commits on branch that aren't on base\n const { stdout } = await execAsync(\n `git cherry origin/${baseBranch} ${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n\n // git cherry shows lines starting with + for unique commits\n const uniqueCommits = stdout.trim().split('\\n').filter(line => line.startsWith('+'))\n const hasCommits = uniqueCommits.length > 0\n\n return {\n success: true,\n output: hasCommits\n ? `Branch ${command.branch} has ${uniqueCommits.length} commits ahead of ${baseBranch}`\n : `Branch ${command.branch} has no commits ahead of ${baseBranch}`,\n details: {\n branchName: command.branch,\n hasCommits\n }\n }\n } catch (error: any) {\n // If git cherry fails (branch not found, etc.), try alternative method\n try {\n // Alternative: count commits with rev-list\n const { stdout } = await execAsync(\n `git rev-list --count origin/${baseBranch}..${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n\n const commitCount = parseInt(stdout.trim(), 10)\n const hasCommits = commitCount > 0\n\n return {\n success: true,\n output: hasCommits\n ? `Branch ${command.branch} has ${commitCount} commits ahead of ${baseBranch}`\n : `Branch ${command.branch} has no commits ahead of ${baseBranch}`,\n details: {\n branchName: command.branch,\n hasCommits\n }\n }\n } catch {\n // Both methods failed - branch likely doesn't exist or isn't tracked\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: error.message || `Failed to check commits for branch ${command.branch}`\n }\n }\n }\n }\n\n /**\n * EP598: Execute main branch check - returns current branch, uncommitted files, and unpushed commits\n */\n private async executeMainBranchCheck(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Get current branch\n let currentBranch = ''\n try {\n const { stdout } = await execAsync('git branch --show-current', { cwd, timeout: options?.timeout || 10000 })\n currentBranch = stdout.trim()\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to get current branch'\n }\n }\n\n // Get uncommitted files\n let uncommittedFiles: string[] = []\n try {\n const { stdout } = await execAsync('git status --porcelain', { cwd, timeout: options?.timeout || 10000 })\n if (stdout) {\n uncommittedFiles = stdout.split('\\n').filter(line => line.trim()).map(line => {\n const parts = line.trim().split(/\\s+/)\n return parts.slice(1).join(' ')\n })\n }\n } catch {\n // Ignore errors - just means no uncommitted changes\n }\n\n // Get unpushed commits (only if on main branch)\n let localCommits: Array<{ sha: string; message: string; author: string }> = []\n if (currentBranch === 'main') {\n try {\n const { stdout } = await execAsync('git log origin/main..HEAD --format=\"%H|%s|%an\"', { cwd, timeout: options?.timeout || 10000 })\n if (stdout) {\n localCommits = stdout.split('\\n').filter(line => line.trim()).map(line => {\n const [sha, message, author] = line.split('|')\n return {\n sha: sha ? sha.substring(0, 8) : '',\n message: message || '',\n author: author || ''\n }\n })\n }\n } catch {\n // Ignore errors - might not have origin/main or no remote\n }\n }\n\n return {\n success: true,\n output: `Branch: ${currentBranch}, Uncommitted: ${uncommittedFiles.length}, Unpushed: ${localCommits.length}`,\n details: {\n currentBranch,\n uncommittedFiles,\n localCommits\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check main branch'\n }\n }\n }\n\n /**\n * EP599: Execute get_commits command\n * Returns commits for a branch with pushed/unpushed status\n */\n private async executeGetCommits(\n command: { branch: string; limit?: number; baseBranch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n const limit = command.limit || 10\n const baseBranch = command.baseBranch || 'main'\n\n try {\n // Get commits unique to this branch (not in main)\n let stdout: string\n try {\n const result = await execAsync(\n `git log ${baseBranch}..\"${command.branch}\" --pretty=format:\"%H|%an|%ae|%aI|%s\" -n ${limit} --`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n stdout = result.stdout\n } catch (error) {\n // Fallback: if comparison fails, show all commits on this branch\n try {\n const result = await execAsync(\n `git log \"${command.branch}\" --pretty=format:\"%H|%an|%ae|%aI|%s\" -n ${limit} --`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n stdout = result.stdout\n } catch (branchError) {\n // Branch doesn't exist locally\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: `Branch ${command.branch} not found locally`\n }\n }\n }\n\n if (!stdout.trim()) {\n return {\n success: true,\n output: 'No commits found',\n details: {\n commits: []\n }\n }\n }\n\n // Parse commits\n const commitLines = stdout.trim().split('\\n')\n\n // Check which commits have been pushed to remote\n let remoteShas: Set<string> = new Set()\n try {\n const { stdout: remoteCommits } = await execAsync(\n `git log \"origin/${command.branch}\" --pretty=format:\"%H\" -n ${limit} --`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n remoteShas = new Set(remoteCommits.trim().split('\\n').filter(Boolean))\n } catch {\n // Remote branch doesn't exist - all commits are local/unpushed\n }\n\n const commits = commitLines.map((line) => {\n const [sha, authorName, authorEmail, date, ...messageParts] = line.split('|')\n const message = messageParts.join('|') // Handle pipes in commit messages\n const isPushed = remoteShas.has(sha)\n\n return {\n sha,\n message,\n authorName,\n authorEmail,\n date,\n isPushed\n }\n })\n\n return {\n success: true,\n output: `Found ${commits.length} commits`,\n details: {\n commits\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to get commits'\n }\n }\n }\n\n // ========================================\n // EP599: Advanced operations for move-to-module and discard-main-changes\n // ========================================\n\n /**\n * Execute git stash operations\n */\n private async executeStash(\n command: { operation: 'push' | 'pop' | 'drop' | 'list'; message?: string; includeUntracked?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args: string[] = ['stash']\n\n switch (command.operation) {\n case 'push':\n args.push('push')\n if (command.includeUntracked) {\n args.push('--include-untracked')\n }\n if (command.message) {\n args.push('-m', command.message)\n }\n break\n case 'pop':\n args.push('pop')\n break\n case 'drop':\n args.push('drop')\n break\n case 'list':\n args.push('list')\n break\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Stash operation failed'\n }\n }\n }\n\n /**\n * Execute git reset\n */\n private async executeReset(\n command: { mode: 'soft' | 'mixed' | 'hard'; target?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['reset', `--${command.mode}`]\n if (command.target) {\n args.push(command.target)\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Reset failed'\n }\n }\n }\n\n /**\n * Execute git merge\n */\n private async executeMerge(\n command: { branch: string; strategy?: 'ours' | 'theirs'; noEdit?: boolean; abort?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n if (command.abort) {\n return await this.runGitCommand(['merge', '--abort'], cwd, options)\n }\n\n // Validate branch name\n const branchValidation = validateBranchName(command.branch)\n if (!branchValidation.valid) {\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: branchValidation.error || 'Invalid branch name'\n }\n }\n\n const args = ['merge', command.branch]\n\n if (command.strategy === 'ours') {\n args.push('--strategy=ours')\n } else if (command.strategy === 'theirs') {\n args.push('--strategy-option=theirs')\n }\n\n if (command.noEdit) {\n args.push('--no-edit')\n }\n\n const result = await this.runGitCommand(args, cwd, options)\n\n // Check for merge conflicts\n if (!result.success && result.output?.includes('CONFLICT')) {\n result.details = result.details || {}\n result.details.mergeConflicts = true\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: error.message || 'Merge failed'\n }\n }\n }\n\n /**\n * Execute git cherry-pick\n */\n private async executeCherryPick(\n command: { sha: string; abort?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n if (command.abort) {\n return await this.runGitCommand(['cherry-pick', '--abort'], cwd, options)\n }\n\n const result = await this.runGitCommand(['cherry-pick', command.sha], cwd, options)\n\n // Check for cherry-pick conflicts\n if (!result.success && result.output?.includes('CONFLICT')) {\n result.details = result.details || {}\n result.details.cherryPickConflicts = true\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: error.message || 'Cherry-pick failed'\n }\n }\n }\n\n /**\n * Execute git clean\n */\n private async executeClean(\n command: { force?: boolean; directories?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['clean']\n\n if (command.force) {\n args.push('-f')\n }\n if (command.directories) {\n args.push('-d')\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Clean failed'\n }\n }\n }\n\n /**\n * Execute git add\n */\n private async executeAdd(\n command: { files?: string[]; all?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['add']\n\n if (command.all) {\n args.push('-A')\n } else if (command.files && command.files.length > 0) {\n // Validate file paths\n const validation = validateFilePaths(command.files)\n if (!validation.valid) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: validation.error || 'Invalid file paths'\n }\n }\n args.push(...command.files)\n } else {\n args.push('-A') // Default to all\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Add failed'\n }\n }\n }\n\n /**\n * Execute git fetch\n */\n private async executeFetch(\n command: { remote?: string; branch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['fetch']\n\n if (command.remote) {\n args.push(command.remote)\n if (command.branch) {\n args.push(command.branch)\n }\n } else {\n args.push('origin')\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: error.message || 'Fetch failed'\n }\n }\n }\n\n // ========================================\n // EP599-3: Composite operations\n // ========================================\n\n /**\n * Execute move_to_module - composite operation\n * Moves commits/changes to a module branch with conflict handling\n */\n private async executeMoveToModule(\n command: { targetBranch: string; commitShas?: string[]; conflictResolution?: 'ours' | 'theirs' },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n const { targetBranch, commitShas, conflictResolution } = command\n let hasStash = false\n const cherryPickedCommits: string[] = []\n\n try {\n // Step 1: Get current branch\n const { stdout: currentBranchOut } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd })\n const currentBranch = currentBranchOut.trim()\n\n // Step 2: Stash uncommitted changes\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd })\n if (statusOutput.trim()) {\n try {\n await execAsync('git add -A', { cwd })\n const { stdout: stashHash } = await execAsync('git stash create -m \"episoda-move-to-module\"', { cwd })\n if (stashHash && stashHash.trim()) {\n await execAsync(`git stash store -m \"episoda-move-to-module\" ${stashHash.trim()}`, { cwd })\n await execAsync('git reset --hard HEAD', { cwd })\n hasStash = true\n }\n } catch (stashError: any) {\n // Continue without stashing\n }\n }\n\n // Step 3: Switch to main if we have commits to move\n if (commitShas && commitShas.length > 0 && currentBranch !== 'main' && currentBranch !== 'master') {\n await execAsync('git checkout main', { cwd })\n }\n\n // Step 4: Create or checkout target branch\n let branchExists = false\n try {\n await execAsync(`git rev-parse --verify ${targetBranch}`, { cwd })\n branchExists = true\n } catch {\n branchExists = false\n }\n\n if (!branchExists) {\n await execAsync(`git checkout -b ${targetBranch}`, { cwd })\n } else {\n await execAsync(`git checkout ${targetBranch}`, { cwd })\n\n // Try to merge changes from main\n if (currentBranch === 'main' || currentBranch === 'master') {\n try {\n const mergeStrategy = conflictResolution === 'ours' ? '--strategy=ours' :\n conflictResolution === 'theirs' ? '--strategy-option=theirs' : ''\n await execAsync(`git merge ${currentBranch} ${mergeStrategy} --no-edit`, { cwd })\n } catch (mergeError: any) {\n // Check for conflicts\n const { stdout: conflictStatus } = await execAsync('git status --porcelain', { cwd })\n if (conflictStatus.includes('UU ') || conflictStatus.includes('AA ') || conflictStatus.includes('DD ')) {\n const { stdout: conflictFiles } = await execAsync('git diff --name-only --diff-filter=U', { cwd })\n const conflictedFiles = conflictFiles.trim().split('\\n').filter(Boolean)\n\n if (conflictResolution) {\n // Auto-resolve conflicts\n for (const file of conflictedFiles) {\n await execAsync(`git checkout --${conflictResolution} \"${file}\"`, { cwd })\n await execAsync(`git add \"${file}\"`, { cwd })\n }\n await execAsync('git commit --no-edit', { cwd })\n } else {\n // Abort merge and return conflict error\n await execAsync('git merge --abort', { cwd })\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: 'Merge conflicts detected',\n details: {\n hasConflicts: true,\n conflictedFiles,\n movedToBranch: targetBranch\n }\n }\n }\n }\n }\n }\n }\n\n // Step 5: Cherry-pick commits if provided\n if (commitShas && commitShas.length > 0 && (currentBranch === 'main' || currentBranch === 'master')) {\n for (const sha of commitShas) {\n try {\n // Check if commit already exists in branch\n const { stdout: logOutput } = await execAsync(\n `git log --format=%H ${targetBranch} | grep ${sha}`,\n { cwd }\n ).catch(() => ({ stdout: '' }))\n\n if (!logOutput.trim()) {\n await execAsync(`git cherry-pick ${sha}`, { cwd })\n cherryPickedCommits.push(sha)\n }\n } catch (err: any) {\n await execAsync('git cherry-pick --abort', { cwd }).catch(() => {})\n }\n }\n\n // Reset main to origin/main\n await execAsync('git checkout main', { cwd })\n await execAsync('git reset --hard origin/main', { cwd })\n await execAsync(`git checkout ${targetBranch}`, { cwd })\n }\n\n // Step 6: Apply stashed changes\n if (hasStash) {\n try {\n await execAsync('git stash pop', { cwd })\n } catch (stashError: any) {\n // Check for stash conflicts\n const { stdout: conflictStatus } = await execAsync('git status --porcelain', { cwd })\n if (conflictStatus.includes('UU ') || conflictStatus.includes('AA ')) {\n if (conflictResolution) {\n const { stdout: conflictFiles } = await execAsync('git diff --name-only --diff-filter=U', { cwd })\n const conflictedFiles = conflictFiles.trim().split('\\n').filter(Boolean)\n for (const file of conflictedFiles) {\n await execAsync(`git checkout --${conflictResolution} \"${file}\"`, { cwd })\n await execAsync(`git add \"${file}\"`, { cwd })\n }\n }\n }\n }\n }\n\n return {\n success: true,\n output: `Successfully moved to branch ${targetBranch}`,\n details: {\n movedToBranch: targetBranch,\n cherryPickedCommits,\n currentBranch: targetBranch\n }\n }\n } catch (error: any) {\n // Try to restore stash if something went wrong\n if (hasStash) {\n try {\n const { stdout: stashList } = await execAsync('git stash list', { cwd })\n if (stashList.includes('episoda-move-to-module')) {\n await execAsync('git stash pop', { cwd })\n }\n } catch (e) {\n // Ignore errors restoring stash\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Move to module failed'\n }\n }\n }\n\n /**\n * Execute discard_main_changes - composite operation\n * Discards all uncommitted files and local commits on main branch\n */\n private async executeDiscardMainChanges(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Step 1: Verify we're on main/master\n const { stdout: currentBranchOut } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd })\n const branch = currentBranchOut.trim()\n\n if (branch !== 'main' && branch !== 'master') {\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: `Cannot discard changes - not on main branch. Current branch: ${branch}`\n }\n }\n\n let discardedFiles = 0\n\n // Step 2: Stash uncommitted changes (will be dropped)\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd })\n if (statusOutput.trim()) {\n try {\n await execAsync('git stash --include-untracked', { cwd })\n discardedFiles = statusOutput.trim().split('\\n').length\n } catch (stashError: any) {\n // Continue - might be nothing to stash\n }\n }\n\n // Step 3: Fetch and reset to origin\n await execAsync('git fetch origin', { cwd })\n await execAsync(`git reset --hard origin/${branch}`, { cwd })\n\n // Step 4: Clean untracked files\n try {\n await execAsync('git clean -fd', { cwd })\n } catch (cleanError: any) {\n // Non-critical\n }\n\n // Step 5: Drop the stash\n try {\n await execAsync('git stash drop', { cwd })\n } catch (dropError: any) {\n // Stash might not exist\n }\n\n return {\n success: true,\n output: `Successfully discarded all changes and reset to origin/${branch}`,\n details: {\n currentBranch: branch,\n discardedFiles\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Discard main changes failed'\n }\n }\n }\n\n // ========================================\n // EP523: Branch sync operations\n // ========================================\n\n /**\n * EP523: Get sync status of a branch relative to main\n * Returns how many commits behind/ahead the branch is\n */\n private async executeSyncStatus(\n command: { branch: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Fetch latest from remote to get accurate counts\n try {\n await execAsync('git fetch origin', { cwd, timeout: options?.timeout || 30000 })\n } catch (fetchError: any) {\n // Network error - return what we can determine locally\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: 'Unable to fetch from remote. Check your network connection.'\n }\n }\n\n let commitsBehind = 0\n let commitsAhead = 0\n\n // Count commits the branch is BEHIND main (main has commits branch doesn't)\n try {\n const { stdout: behindOutput } = await execAsync(\n `git rev-list --count ${command.branch}..origin/main`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n commitsBehind = parseInt(behindOutput.trim(), 10) || 0\n } catch {\n // Branch might not exist or no common ancestor\n commitsBehind = 0\n }\n\n // Count commits the branch is AHEAD of main (branch has commits main doesn't)\n try {\n const { stdout: aheadOutput } = await execAsync(\n `git rev-list --count origin/main..${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n commitsAhead = parseInt(aheadOutput.trim(), 10) || 0\n } catch {\n // Branch might not exist or no common ancestor\n commitsAhead = 0\n }\n\n const isBehind = commitsBehind > 0\n const isAhead = commitsAhead > 0\n const needsSync = isBehind\n\n return {\n success: true,\n output: isBehind\n ? `Branch ${command.branch} is ${commitsBehind} commit(s) behind main`\n : `Branch ${command.branch} is up to date with main`,\n details: {\n branchName: command.branch,\n commitsBehind,\n commitsAhead,\n isBehind,\n isAhead,\n needsSync\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check sync status'\n }\n }\n }\n\n /**\n * EP523: Sync local main branch with remote\n * Used before creating new branches to ensure we branch from latest main\n */\n private async executeSyncMain(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Get current branch to restore later if needed\n let currentBranch = ''\n try {\n const { stdout } = await execAsync('git branch --show-current', { cwd, timeout: 5000 })\n currentBranch = stdout.trim()\n } catch {\n // Ignore - might be detached HEAD\n }\n\n // Fetch latest from remote\n try {\n await execAsync('git fetch origin main', { cwd, timeout: options?.timeout || 30000 })\n } catch (fetchError: any) {\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: 'Unable to fetch from remote. Check your network connection.'\n }\n }\n\n // Check if we need to switch to main\n const needsSwitch = currentBranch !== 'main' && currentBranch !== ''\n\n if (needsSwitch) {\n // Check for uncommitted changes first\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd, timeout: 5000 })\n if (statusOutput.trim()) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n output: 'Cannot sync main: you have uncommitted changes. Commit or stash them first.',\n details: {\n uncommittedFiles: statusOutput.trim().split('\\n').map(line => line.slice(3))\n }\n }\n }\n\n // Switch to main\n await execAsync('git checkout main', { cwd, timeout: options?.timeout || 10000 })\n }\n\n // Pull latest main\n try {\n await execAsync('git pull origin main', { cwd, timeout: options?.timeout || 30000 })\n } catch (pullError: any) {\n // Check for conflicts\n if (pullError.message?.includes('CONFLICT') || pullError.stderr?.includes('CONFLICT')) {\n // Abort the merge\n await execAsync('git merge --abort', { cwd, timeout: 5000 }).catch(() => {})\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: 'Conflict while syncing main. This is unexpected - main should not have local commits.'\n }\n }\n throw pullError\n }\n\n // Switch back to original branch if we switched\n if (needsSwitch && currentBranch) {\n await execAsync(`git checkout \"${currentBranch}\"`, { cwd, timeout: options?.timeout || 10000 })\n }\n\n return {\n success: true,\n output: 'Successfully synced main with remote',\n details: {\n currentBranch: needsSwitch ? currentBranch : 'main'\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to sync main'\n }\n }\n }\n\n /**\n * EP523: Rebase a branch onto main\n * Used when resuming work on a branch that's behind main\n */\n private async executeRebaseBranch(\n command: { branch: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Check for uncommitted changes\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd, timeout: 5000 })\n if (statusOutput.trim()) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n output: 'Cannot rebase: you have uncommitted changes. Commit or stash them first.',\n details: {\n uncommittedFiles: statusOutput.trim().split('\\n').map(line => line.slice(3))\n }\n }\n }\n\n // Get current branch\n const { stdout: currentBranchOut } = await execAsync('git branch --show-current', { cwd, timeout: 5000 })\n const currentBranch = currentBranchOut.trim()\n\n // Ensure we're on the target branch\n if (currentBranch !== command.branch) {\n await execAsync(`git checkout \"${command.branch}\"`, { cwd, timeout: options?.timeout || 10000 })\n }\n\n // Fetch latest main\n await execAsync('git fetch origin main', { cwd, timeout: options?.timeout || 30000 })\n\n // Perform rebase\n try {\n await execAsync('git rebase origin/main', { cwd, timeout: options?.timeout || 60000 })\n } catch (rebaseError: any) {\n const errorOutput = (rebaseError.stderr || '') + (rebaseError.stdout || '')\n\n // Check for conflicts\n if (errorOutput.includes('CONFLICT') || errorOutput.includes('could not apply')) {\n // Get conflicting files\n let conflictFiles: string[] = []\n try {\n const { stdout: conflictOutput } = await execAsync(\n 'git diff --name-only --diff-filter=U',\n { cwd, timeout: 5000 }\n )\n conflictFiles = conflictOutput.trim().split('\\n').filter(Boolean)\n } catch {\n // Ignore - try alternate method\n try {\n const { stdout: statusOut } = await execAsync('git status --porcelain', { cwd, timeout: 5000 })\n conflictFiles = statusOut\n .trim()\n .split('\\n')\n .filter(line => line.startsWith('UU ') || line.startsWith('AA ') || line.startsWith('DD '))\n .map(line => line.slice(3))\n } catch {\n // Couldn't get conflict files\n }\n }\n\n return {\n success: false,\n error: 'REBASE_CONFLICT',\n output: `Rebase conflict in ${conflictFiles.length} file(s). Resolve conflicts then use rebase_continue, or use rebase_abort to cancel.`,\n details: {\n inRebase: true,\n rebaseConflicts: conflictFiles,\n hasConflicts: true,\n conflictedFiles: conflictFiles\n }\n }\n }\n\n throw rebaseError\n }\n\n return {\n success: true,\n output: `Successfully rebased ${command.branch} onto main`,\n details: {\n branchName: command.branch,\n inRebase: false\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Rebase failed'\n }\n }\n }\n\n /**\n * EP523: Abort an in-progress rebase\n * Returns to the state before rebase was started\n */\n private async executeRebaseAbort(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n await execAsync('git rebase --abort', { cwd, timeout: options?.timeout || 10000 })\n\n return {\n success: true,\n output: 'Rebase aborted. Your branch has been restored to its previous state.',\n details: {\n inRebase: false\n }\n }\n } catch (error: any) {\n // Check if there's no rebase in progress\n if (error.message?.includes('No rebase in progress') || error.stderr?.includes('No rebase in progress')) {\n return {\n success: true,\n output: 'No rebase in progress.',\n details: {\n inRebase: false\n }\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to abort rebase'\n }\n }\n }\n\n /**\n * EP523: Continue a paused rebase after conflicts are resolved\n */\n private async executeRebaseContinue(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Stage all resolved files\n await execAsync('git add -A', { cwd, timeout: 5000 })\n\n // Continue the rebase\n await execAsync('git rebase --continue', { cwd, timeout: options?.timeout || 60000 })\n\n return {\n success: true,\n output: 'Rebase continued successfully.',\n details: {\n inRebase: false\n }\n }\n } catch (error: any) {\n const errorOutput = (error.stderr || '') + (error.stdout || '')\n\n // Check if there are still conflicts\n if (errorOutput.includes('CONFLICT') || errorOutput.includes('could not apply')) {\n // Get remaining conflict files\n let conflictFiles: string[] = []\n try {\n const { stdout: conflictOutput } = await execAsync(\n 'git diff --name-only --diff-filter=U',\n { cwd, timeout: 5000 }\n )\n conflictFiles = conflictOutput.trim().split('\\n').filter(Boolean)\n } catch {\n // Ignore\n }\n\n return {\n success: false,\n error: 'REBASE_CONFLICT',\n output: 'More conflicts encountered. Resolve them and try again.',\n details: {\n inRebase: true,\n rebaseConflicts: conflictFiles,\n hasConflicts: true,\n conflictedFiles: conflictFiles\n }\n }\n }\n\n // Check if there's no rebase in progress\n if (errorOutput.includes('No rebase in progress')) {\n return {\n success: true,\n output: 'No rebase in progress.',\n details: {\n inRebase: false\n }\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to continue rebase'\n }\n }\n }\n\n /**\n * EP523: Check if a rebase is currently in progress\n */\n private async executeRebaseStatus(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Check for rebase-merge directory (indicates rebase in progress)\n let inRebase = false\n let rebaseConflicts: string[] = []\n\n try {\n const { stdout: gitDir } = await execAsync('git rev-parse --git-dir', { cwd, timeout: 5000 })\n const gitDirPath = gitDir.trim()\n\n // Check for rebase directories\n const fs = await import('fs').then(m => m.promises)\n const rebaseMergePath = `${gitDirPath}/rebase-merge`\n const rebaseApplyPath = `${gitDirPath}/rebase-apply`\n\n try {\n await fs.access(rebaseMergePath)\n inRebase = true\n } catch {\n try {\n await fs.access(rebaseApplyPath)\n inRebase = true\n } catch {\n inRebase = false\n }\n }\n } catch {\n // If we can't determine, check via status\n try {\n const { stdout: statusOutput } = await execAsync('git status', { cwd, timeout: 5000 })\n inRebase = statusOutput.includes('rebase in progress') ||\n statusOutput.includes('interactive rebase in progress') ||\n statusOutput.includes('You are currently rebasing')\n } catch {\n inRebase = false\n }\n }\n\n // If in rebase, get conflict files\n if (inRebase) {\n try {\n const { stdout: conflictOutput } = await execAsync(\n 'git diff --name-only --diff-filter=U',\n { cwd, timeout: 5000 }\n )\n rebaseConflicts = conflictOutput.trim().split('\\n').filter(Boolean)\n } catch {\n // No conflicts or couldn't get them\n }\n }\n\n return {\n success: true,\n output: inRebase\n ? `Rebase in progress with ${rebaseConflicts.length} conflicting file(s)`\n : 'No rebase in progress',\n details: {\n inRebase,\n rebaseConflicts,\n hasConflicts: rebaseConflicts.length > 0\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check rebase status'\n }\n }\n }\n\n /**\n * Run a git command and return structured result\n */\n private async runGitCommand(\n args: string[],\n cwd: string,\n options?: ExecutionOptions & { env?: NodeJS.ProcessEnv }\n ): Promise<ExecutionResult> {\n try {\n // Sanitize arguments\n const sanitizedArgs = sanitizeArgs(args)\n\n // Build command\n const command = ['git', ...sanitizedArgs].join(' ')\n\n // Execute with timeout\n const timeout = options?.timeout || 30000 // 30 second default\n const execOptions = {\n cwd,\n timeout,\n env: options?.env || process.env,\n maxBuffer: 1024 * 1024 * 10 // 10MB buffer\n }\n\n const { stdout, stderr } = await execAsync(command, execOptions)\n\n // Combine output\n const output = (stdout + stderr).trim()\n\n // Extract additional details\n const details: ExecutionResult['details'] = {}\n\n // Try to extract branch name\n const branchName = extractBranchName(output)\n if (branchName) {\n details.branchName = branchName\n }\n\n // Check for detached HEAD\n if (isDetachedHead(output)) {\n details.branchName = 'HEAD (detached)'\n }\n\n return {\n success: true,\n output,\n details: Object.keys(details).length > 0 ? details : undefined\n }\n } catch (error: any) {\n // Parse error\n const stderr = error.stderr || ''\n const stdout = error.stdout || ''\n const exitCode = error.code || 1\n\n // Determine error code\n const errorCode = parseGitError(stderr, stdout, exitCode)\n\n // Extract additional details based on error type\n const details: ExecutionResult['details'] = {\n exitCode\n }\n\n // Parse conflicts if merge conflict\n if (errorCode === 'MERGE_CONFLICT') {\n const conflicts = parseMergeConflicts(stdout + stderr)\n if (conflicts.length > 0) {\n details.conflictingFiles = conflicts\n }\n }\n\n // Parse status for uncommitted changes\n if (errorCode === 'UNCOMMITTED_CHANGES') {\n try {\n const statusResult = await this.executeStatus(cwd, options)\n if (statusResult.details?.uncommittedFiles) {\n details.uncommittedFiles = statusResult.details.uncommittedFiles\n }\n } catch {\n // Ignore errors when getting status\n }\n }\n\n return {\n success: false,\n error: errorCode,\n output: (stdout + stderr).trim(),\n details\n }\n }\n }\n\n /**\n * Validate that git is installed\n */\n private async validateGitInstalled(): Promise<boolean> {\n try {\n await execAsync('git --version', { timeout: 5000 })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Check if directory is a git repository\n */\n private async isGitRepository(cwd: string): Promise<boolean> {\n try {\n await execAsync('git rev-parse --git-dir', { cwd, timeout: 5000 })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Detect the git working directory (repository root)\n * @returns Path to the git repository root\n */\n private async detectWorkingDirectory(startPath?: string): Promise<string | null> {\n try {\n const { stdout } = await execAsync('git rev-parse --show-toplevel', {\n cwd: startPath || process.cwd(),\n timeout: 5000\n })\n return stdout.trim()\n } catch {\n return null\n }\n }\n}\n","/**\n * Version management for @episoda/core\n *\n * Single source of truth: reads version from package.json\n * This ensures version is always in sync and only needs to be\n * updated in one place (package.json) during releases.\n */\n\nimport { readFileSync } from 'fs'\nimport { join } from 'path'\n\n// Read version from package.json at module load time\n// Using readFileSync to avoid require() caching issues during development\nconst packageJsonPath = join(__dirname, '..', 'package.json')\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))\n\nexport const VERSION: string = packageJson.version\n","/**\n * Episoda WebSocket Client\n *\n * EP589-9: Implemented with comprehensive error handling and reconnection logic\n *\n * Provides reliable WebSocket connection to episoda.dev CLI gateway with:\n * - Automatic reconnection with exponential backoff\n * - Heartbeat/ping-pong to keep connection alive\n * - Event-driven architecture for handling server commands\n * - Graceful error handling and recovery\n */\n\nimport WS from 'ws'\nimport https from 'https'\nimport { ServerMessage, ClientMessage, ConnectionStatus } from './command-protocol'\nimport { VERSION } from './version'\n\n// EP801: Create HTTPS agent that forces IPv4 to avoid IPv6 timeout issues\n// Many networks have IPv6 configured but not fully routable, causing connection failures\nconst ipv4Agent = new https.Agent({ family: 4 })\n\n// EP701: Client-side events emitted by EpisodaClient\nexport type DisconnectEvent = {\n type: 'disconnected'\n code: number\n reason: string\n willReconnect: boolean\n}\n\nexport type ClientEvent = ServerMessage | DisconnectEvent\n\nexport type EventHandler = (event: ClientEvent) => void | Promise<void>\n\n// EP605: Reconnection configuration for long-term stability\nconst INITIAL_RECONNECT_DELAY = 1000 // 1 second\nconst MAX_RECONNECT_DELAY = 60000 // 60 seconds standard max\nconst IDLE_RECONNECT_DELAY = 600000 // 10 minutes when idle (no commands for 1+ hour)\nconst MAX_RETRY_DURATION = 6 * 60 * 60 * 1000 // 6 hours maximum retry duration\nconst IDLE_THRESHOLD = 60 * 60 * 1000 // 1 hour - after this, use slower retry\n\n// EP648: Rate limit and rapid reconnect prevention\nconst RATE_LIMIT_BACKOFF = 60000 // 60 seconds when rate limited\nconst RAPID_CLOSE_THRESHOLD = 2000 // If connection closes within 2s, it's likely an error\nconst RAPID_CLOSE_BACKOFF = 30000 // 30 seconds backoff for rapid close scenarios\n\n// EP605: Client-side heartbeat to detect dead connections\nconst CLIENT_HEARTBEAT_INTERVAL = 45000 // 45 seconds (offset from server's 30s to avoid collision)\nconst CLIENT_HEARTBEAT_TIMEOUT = 15000 // 15 seconds to wait for pong\n\n// EP606: Connection timeout for initial WebSocket connection\nconst CONNECTION_TIMEOUT = 15000 // 15 seconds to establish connection\n\n/**\n * WebSocket client for connecting to episoda.dev/cli gateway\n *\n * DESIGN PRINCIPLES:\n * - Reverse WebSocket: Client connects TO server (bypasses NAT/firewall)\n * - Automatic reconnection with exponential backoff\n * - Server-initiated heartbeat via WebSocket ping/pong (30s interval)\n * - Event-driven architecture for handling server commands\n */\nexport class EpisodaClient {\n private ws?: WS\n private eventHandlers: Map<string, EventHandler[]> = new Map()\n private reconnectAttempts = 0\n private reconnectTimeout?: NodeJS.Timeout\n private url = ''\n private token = ''\n private machineId?: string\n private hostname?: string\n private osPlatform?: string\n private osArch?: string\n private daemonPid?: number\n private isConnected = false\n private isDisconnecting = false\n private isGracefulShutdown = false // Track if shutdown was graceful (server-initiated)\n // EP605: Client-side heartbeat for connection health monitoring\n private heartbeatTimer?: NodeJS.Timeout\n private heartbeatTimeoutTimer?: NodeJS.Timeout\n // EP605: Activity and retry duration tracking\n private lastCommandTime = Date.now() // Track last command for idle detection\n private firstDisconnectTime?: number // Track when reconnection attempts started\n private isIntentionalDisconnect = false // Prevent reconnection after intentional disconnect\n // EP648: Rate limit and rapid reconnect tracking\n private rateLimitBackoffUntil?: number // Timestamp until which we should wait (rate limited)\n private lastConnectAttemptTime = 0 // Track when we last attempted to connect\n private lastErrorCode?: string // Track the last error code received\n\n /**\n * Connect to episoda.dev WebSocket gateway\n * @param url - WebSocket URL (wss://episoda.dev/cli)\n * @param token - OAuth access token\n * @param machineId - Optional machine identifier for multi-machine support\n * @param deviceInfo - Optional device information (hostname, OS, daemonPid)\n */\n async connect(\n url: string,\n token: string,\n machineId?: string,\n deviceInfo?: { hostname?: string; osPlatform?: string; osArch?: string; daemonPid?: number }\n ): Promise<void> {\n this.url = url\n this.token = token\n this.machineId = machineId\n this.hostname = deviceInfo?.hostname\n this.osPlatform = deviceInfo?.osPlatform\n this.osArch = deviceInfo?.osArch\n this.daemonPid = deviceInfo?.daemonPid\n this.isDisconnecting = false\n this.isGracefulShutdown = false // Reset graceful shutdown flag on new connection\n this.isIntentionalDisconnect = false // Allow reconnection on fresh connect\n this.lastConnectAttemptTime = Date.now() // EP648: Track when this connection attempt started\n this.lastErrorCode = undefined // EP648: Clear last error code\n\n return new Promise((resolve, reject) => {\n // EP606: Connection timeout to prevent hanging indefinitely\n const connectionTimeout = setTimeout(() => {\n if (this.ws) {\n this.ws.terminate()\n }\n reject(new Error(`Connection timeout after ${CONNECTION_TIMEOUT / 1000}s - server may be unreachable`))\n }, CONNECTION_TIMEOUT)\n\n try {\n // EP801: Use IPv4 agent to avoid IPv6 timeout issues on networks with broken IPv6\n this.ws = new WS(url, { agent: ipv4Agent })\n\n // Connection opened\n this.ws.on('open', () => {\n clearTimeout(connectionTimeout) // EP606: Clear timeout on successful connection\n console.log('[EpisodaClient] WebSocket connected')\n this.isConnected = true\n this.reconnectAttempts = 0\n this.firstDisconnectTime = undefined // EP605: Reset retry duration tracking\n this.lastCommandTime = Date.now() // EP605: Reset activity timer\n\n // Send auth message (K722: includes machineId, EP596: includes device info, EP601: includes daemonPid)\n this.send({\n type: 'auth',\n token,\n version: VERSION,\n machineId,\n hostname: this.hostname,\n osPlatform: this.osPlatform,\n osArch: this.osArch,\n daemonPid: this.daemonPid\n })\n\n // EP605: Start client-side heartbeat\n this.startHeartbeat()\n\n resolve()\n })\n\n // EP605: Handle pong response from server (for client-initiated pings)\n this.ws.on('pong', () => {\n // Clear the timeout - connection is alive\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n this.heartbeatTimeoutTimer = undefined\n }\n })\n\n // Message received\n this.ws.on('message', (data: WS.Data) => {\n try {\n const message = JSON.parse(data.toString()) as ServerMessage\n this.handleMessage(message)\n } catch (error) {\n console.error('[EpisodaClient] Failed to parse message:', error)\n }\n })\n\n // EP603: Log ping events for debugging connection health\n this.ws.on('ping', () => {\n console.log('[EpisodaClient] Received ping from server')\n })\n\n // Connection closed\n this.ws.on('close', (code: number, reason: Buffer) => {\n console.log(`[EpisodaClient] WebSocket closed: ${code} ${reason.toString()}`)\n this.isConnected = false\n\n const willReconnect = !this.isDisconnecting\n\n // EP701: Emit 'disconnected' event so daemon can clean up connection\n this.emit({\n type: 'disconnected',\n code,\n reason: reason.toString(),\n willReconnect\n })\n\n // Attempt reconnection if not intentional disconnect\n if (willReconnect) {\n this.scheduleReconnect()\n }\n })\n\n // Error occurred\n this.ws.on('error', (error: Error) => {\n console.error('[EpisodaClient] WebSocket error:', error)\n\n if (!this.isConnected) {\n // Connection failed, reject the promise\n clearTimeout(connectionTimeout) // EP606: Clear timeout on error\n reject(error)\n }\n })\n\n } catch (error) {\n clearTimeout(connectionTimeout) // EP606: Clear timeout on exception\n reject(error)\n }\n })\n }\n\n /**\n * Disconnect from the server\n * @param intentional - If true, prevents automatic reconnection (user-initiated disconnect)\n */\n async disconnect(intentional = true): Promise<void> {\n this.isDisconnecting = true\n this.isIntentionalDisconnect = intentional // EP605: Prevent reconnection if user intentionally disconnected\n\n // EP605: Clear all timers\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = undefined\n }\n\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\n }\n\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n this.heartbeatTimeoutTimer = undefined\n }\n\n if (this.ws) {\n this.ws.close()\n this.ws = undefined\n }\n\n this.isConnected = false\n }\n\n /**\n * Register an event handler\n * @param event - Event type ('command', 'ping', 'error', 'auth_success')\n * @param handler - Handler function\n */\n on(event: string, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, [])\n }\n this.eventHandlers.get(event)!.push(handler)\n }\n\n /**\n * Send a message to the server\n * @param message - Client message to send\n */\n async send(message: ClientMessage): Promise<void> {\n if (!this.ws || !this.isConnected) {\n throw new Error('WebSocket not connected')\n }\n\n return new Promise((resolve, reject) => {\n this.ws!.send(JSON.stringify(message), (error) => {\n if (error) {\n console.error('[EpisodaClient] Failed to send message:', error)\n reject(error)\n } else {\n resolve()\n }\n })\n })\n }\n\n /**\n * Get current connection status\n */\n getStatus(): ConnectionStatus {\n return {\n connected: this.isConnected\n }\n }\n\n /**\n * EP605: Update last command time to reset idle detection\n * Call this when a command is received/executed\n */\n updateActivity(): void {\n this.lastCommandTime = Date.now()\n }\n\n /**\n * EP701: Emit a client-side event to registered handlers\n * Used for events like 'disconnected' that originate from the client, not server\n */\n private emit(event: ClientEvent): void {\n const handlers = this.eventHandlers.get(event.type) || []\n handlers.forEach(handler => {\n try {\n handler(event)\n } catch (error) {\n console.error(`[EpisodaClient] Handler error for ${event.type}:`, error)\n }\n })\n }\n\n /**\n * Handle incoming message from server\n */\n private handleMessage(message: ServerMessage): void {\n // Special handling for graceful shutdown messages from server\n if (message.type === 'shutdown') {\n console.log('[EpisodaClient] Received graceful shutdown message from server')\n this.isGracefulShutdown = true\n }\n\n // EP648: Handle error messages, especially rate limiting and rapid reconnect\n if (message.type === 'error') {\n const errorMessage = message as ServerMessage & { code?: string; retryAfter?: number }\n this.lastErrorCode = errorMessage.code\n\n if (errorMessage.code === 'RATE_LIMITED' || errorMessage.code === 'TOO_SOON') {\n // Use server-provided retryAfter or default to 60 seconds for rate limit, 5 seconds for too soon\n const defaultRetry = errorMessage.code === 'RATE_LIMITED' ? 60 : 5\n const retryAfterMs = (errorMessage.retryAfter || defaultRetry) * 1000\n this.rateLimitBackoffUntil = Date.now() + retryAfterMs\n console.log(`[EpisodaClient] ${errorMessage.code}: will retry after ${retryAfterMs / 1000}s`)\n }\n }\n\n const handlers = this.eventHandlers.get(message.type) || []\n\n // Execute all handlers for this message type\n handlers.forEach(handler => {\n try {\n handler(message)\n } catch (error) {\n console.error(`[EpisodaClient] Handler error for ${message.type}:`, error)\n }\n })\n }\n\n /**\n * Schedule reconnection with exponential backoff and randomization\n *\n * EP605: Conservative reconnection with multiple safeguards:\n * - 6-hour maximum retry duration to prevent indefinite retrying\n * - Activity-based backoff (10 min delay if idle for 1+ hour)\n * - No reconnection after intentional disconnect\n * - Randomization to prevent thundering herd\n *\n * EP648: Additional protections against reconnection loops:\n * - Rate limit awareness: respect server's RATE_LIMITED response\n * - Rapid close detection: if connection closes within 2s, apply longer backoff\n */\n private scheduleReconnect(): void {\n // EP605: Don't reconnect if user intentionally disconnected\n if (this.isIntentionalDisconnect) {\n console.log('[EpisodaClient] Intentional disconnect - not reconnecting')\n return\n }\n\n // EP605: Clear heartbeat timers before reconnection attempt\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\n }\n\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n this.heartbeatTimeoutTimer = undefined\n }\n\n // EP605: Track when reconnection attempts started\n if (!this.firstDisconnectTime) {\n this.firstDisconnectTime = Date.now()\n }\n\n // EP605: Check 6-hour maximum retry duration\n const retryDuration = Date.now() - this.firstDisconnectTime\n if (retryDuration >= MAX_RETRY_DURATION) {\n console.error(`[EpisodaClient] Maximum retry duration (6 hours) exceeded, giving up. Please restart the CLI.`)\n return\n }\n\n // EP605: Log periodically to show we're still trying\n if (this.reconnectAttempts > 0 && this.reconnectAttempts % 10 === 0) {\n const hoursRemaining = ((MAX_RETRY_DURATION - retryDuration) / (60 * 60 * 1000)).toFixed(1)\n console.log(`[EpisodaClient] Still attempting to reconnect (attempt ${this.reconnectAttempts}, ${hoursRemaining}h remaining)...`)\n }\n\n // EP648: Check if we're still in rate limit backoff period\n if (this.rateLimitBackoffUntil && Date.now() < this.rateLimitBackoffUntil) {\n const waitTime = this.rateLimitBackoffUntil - Date.now()\n console.log(`[EpisodaClient] Rate limited, waiting ${Math.round(waitTime / 1000)}s before retry`)\n this.reconnectAttempts++\n this.reconnectTimeout = setTimeout(() => {\n this.rateLimitBackoffUntil = undefined // Clear rate limit after waiting\n this.scheduleReconnect()\n }, waitTime)\n return\n }\n\n // EP648: Detect rapid close (connection closed within 2 seconds of opening)\n // This indicates an error condition like rate limiting, auth failure, etc.\n const timeSinceConnect = Date.now() - this.lastConnectAttemptTime\n const wasRapidClose = timeSinceConnect < RAPID_CLOSE_THRESHOLD && this.lastConnectAttemptTime > 0\n\n // EP605: Check if connection is idle (no commands for 1+ hour)\n const timeSinceLastCommand = Date.now() - this.lastCommandTime\n const isIdle = timeSinceLastCommand >= IDLE_THRESHOLD\n\n // Calculate base delay based on shutdown type and conditions\n let baseDelay: number\n if (this.isGracefulShutdown && this.reconnectAttempts < 3) {\n // Graceful shutdown: try reconnecting quickly (500ms, 1s, 2s)\n baseDelay = 500 * Math.pow(2, this.reconnectAttempts)\n } else if (wasRapidClose) {\n // EP648: Connection failed almost immediately - likely rate limited or auth error\n // Apply longer backoff to prevent spam\n baseDelay = Math.max(RAPID_CLOSE_BACKOFF, INITIAL_RECONNECT_DELAY * Math.pow(2, Math.min(this.reconnectAttempts, 6)))\n console.log(`[EpisodaClient] Rapid close detected (${timeSinceConnect}ms), applying ${baseDelay / 1000}s backoff`)\n } else if (isIdle) {\n // EP605: Use slower retry for idle connections (10 minutes)\n baseDelay = IDLE_RECONNECT_DELAY\n } else {\n // EP605: Use capped exponential backoff\n // After 6 attempts, we hit max delay and stay there\n const cappedAttempts = Math.min(this.reconnectAttempts, 6)\n baseDelay = INITIAL_RECONNECT_DELAY * Math.pow(2, cappedAttempts)\n }\n\n // Add randomization (±25%) to prevent thundering herd\n // When server restarts, all clients shouldn't hit it at the exact same time\n const jitter = baseDelay * 0.25 * (Math.random() * 2 - 1) // Random between -25% and +25%\n const maxDelay = isIdle ? IDLE_RECONNECT_DELAY : MAX_RECONNECT_DELAY\n const delay = Math.min(baseDelay + jitter, maxDelay)\n\n this.reconnectAttempts++\n const shutdownType = this.isGracefulShutdown ? 'graceful' : 'ungraceful'\n const idleStatus = isIdle ? ', idle' : ''\n const rapidStatus = wasRapidClose ? ', rapid-close' : ''\n\n // EP605: Only log first few attempts and then periodically\n if (this.reconnectAttempts <= 5 || this.reconnectAttempts % 10 === 0) {\n console.log(`[EpisodaClient] Reconnecting in ${Math.round(delay / 1000)}s (attempt ${this.reconnectAttempts}, ${shutdownType}${idleStatus}${rapidStatus})`)\n }\n\n this.reconnectTimeout = setTimeout(() => {\n console.log('[EpisodaClient] Attempting reconnection...')\n this.connect(this.url, this.token, this.machineId, {\n hostname: this.hostname,\n osPlatform: this.osPlatform,\n osArch: this.osArch,\n daemonPid: this.daemonPid\n }).then(() => {\n // EP605: Reset counters after successful reconnection\n console.log('[EpisodaClient] Reconnection successful, resetting retry counter')\n this.reconnectAttempts = 0\n this.isGracefulShutdown = false\n this.firstDisconnectTime = undefined\n this.rateLimitBackoffUntil = undefined // EP648: Clear rate limit on success\n }).catch(error => {\n console.error('[EpisodaClient] Reconnection failed:', error)\n // scheduleReconnect will be called again from the 'close' event\n })\n }, delay)\n }\n\n /**\n * EP605: Start client-side heartbeat to detect dead connections\n *\n * Sends ping every 45 seconds and expects pong within 15 seconds.\n * If no pong received, terminates connection to trigger reconnection.\n */\n private startHeartbeat(): void {\n // Clear any existing heartbeat\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n }\n\n this.heartbeatTimer = setInterval(() => {\n if (!this.ws || this.ws.readyState !== WS.OPEN) {\n return\n }\n\n // Send ping\n try {\n this.ws.ping()\n\n // Clear any existing timeout before setting new one (prevents timer leak)\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n }\n\n // Set timeout for pong response\n this.heartbeatTimeoutTimer = setTimeout(() => {\n console.log('[EpisodaClient] Heartbeat timeout - no pong received, terminating connection')\n if (this.ws) {\n this.ws.terminate() // Force close to trigger reconnection\n }\n }, CLIENT_HEARTBEAT_TIMEOUT)\n } catch (error) {\n console.error('[EpisodaClient] Error sending heartbeat ping:', error)\n }\n }, CLIENT_HEARTBEAT_INTERVAL)\n }\n}\n","/**\n * Authentication utilities\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as os from 'os'\nimport { execSync } from 'child_process'\nimport { EpisodaConfig } from './command-protocol'\n\nconst DEFAULT_CONFIG_FILE = 'config.json'\n\n/**\n * Get the config directory path\n * Supports EPISODA_CONFIG_DIR env var for running multiple environments\n */\nexport function getConfigDir(): string {\n return process.env.EPISODA_CONFIG_DIR || path.join(os.homedir(), '.episoda')\n}\n\n/**\n * Get the full path to the config file\n */\nexport function getConfigPath(configPath?: string): string {\n if (configPath) {\n return configPath\n }\n return path.join(getConfigDir(), DEFAULT_CONFIG_FILE)\n}\n\n/**\n * Ensure the config directory exists\n *\n * EP798: Also excludes the directory from iCloud/Dropbox sync on macOS\n * to prevent machine-specific files (machine-id, daemon.sock) from syncing.\n */\nfunction ensureConfigDir(configPath: string): void {\n const dir = path.dirname(configPath)\n const isNew = !fs.existsSync(dir)\n\n if (isNew) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n }\n\n // EP798: Exclude from cloud sync on macOS\n // Only do this once when directory is created (or if .nosync doesn't exist)\n if (process.platform === 'darwin') {\n const nosyncPath = path.join(dir, '.nosync')\n if (isNew || !fs.existsSync(nosyncPath)) {\n try {\n // Create .nosync file (respected by some cloud services)\n fs.writeFileSync(nosyncPath, '', { mode: 0o600 })\n // Set iCloud-specific exclusion attribute\n execSync(`xattr -w com.apple.fileprovider.ignore 1 \"${dir}\"`, {\n stdio: 'ignore',\n timeout: 5000\n })\n } catch {\n // Ignore errors - xattr may not be available or dir may already be excluded\n }\n }\n }\n}\n\n/**\n * Load configuration from .episoda/config.json\n */\nexport async function loadConfig(configPath?: string): Promise<EpisodaConfig | null> {\n const fullPath = getConfigPath(configPath)\n\n if (!fs.existsSync(fullPath)) {\n return null\n }\n\n try {\n const content = fs.readFileSync(fullPath, 'utf8')\n const config = JSON.parse(content) as EpisodaConfig\n return config\n } catch (error) {\n console.error('Error loading config:', error)\n return null\n }\n}\n\n/**\n * Save configuration to .episoda/config.json\n */\nexport async function saveConfig(config: EpisodaConfig, configPath?: string): Promise<void> {\n const fullPath = getConfigPath(configPath)\n ensureConfigDir(fullPath)\n\n try {\n const content = JSON.stringify(config, null, 2)\n fs.writeFileSync(fullPath, content, { mode: 0o600 })\n } catch (error) {\n throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Validate an access token\n */\nexport async function validateToken(token: string): Promise<boolean> {\n // For now, just check if token exists and is non-empty\n // In the future, we can make an API call to validate\n return Boolean(token && token.length > 0)\n}\n","/**\n * Error handling utilities\n */\n\nimport { ErrorCode } from './command-protocol'\n\n/**\n * Custom error class for Episoda operations\n */\nexport class EpisodaError extends Error {\n constructor(\n public code: ErrorCode,\n message: string,\n public details?: Record<string, any>\n ) {\n super(message)\n this.name = 'EpisodaError'\n }\n}\n\n// Note: parseGitError is exported from git-parser.ts\n\n/**\n * Create a user-friendly error message from error code\n * (For CLI use - MCP will format differently)\n */\nexport function formatErrorMessage(code: ErrorCode, details?: Record<string, any>): string {\n const messages: Record<ErrorCode, string> = {\n 'GIT_NOT_INSTALLED': 'Git is not installed or not in PATH',\n 'NOT_GIT_REPO': 'Not a git repository',\n 'MERGE_CONFLICT': 'Merge conflict detected',\n 'REBASE_CONFLICT': 'Rebase conflict detected - resolve conflicts or abort rebase',\n 'UNCOMMITTED_CHANGES': 'Uncommitted changes would be overwritten',\n 'NETWORK_ERROR': 'Network error occurred',\n 'AUTH_FAILURE': 'Authentication failed',\n 'BRANCH_NOT_FOUND': 'Branch not found',\n 'BRANCH_ALREADY_EXISTS': 'Branch already exists',\n 'PUSH_REJECTED': 'Push rejected by remote',\n 'COMMAND_TIMEOUT': 'Command timed out',\n 'UNKNOWN_ERROR': 'Unknown error occurred'\n }\n\n let message = messages[code] || `Error: ${code}`\n\n // Add details if provided\n if (details) {\n if (details.uncommittedFiles && details.uncommittedFiles.length > 0) {\n message += `\\nUncommitted files: ${details.uncommittedFiles.join(', ')}`\n }\n if (details.conflictingFiles && details.conflictingFiles.length > 0) {\n message += `\\nConflicting files: ${details.conflictingFiles.join(', ')}`\n }\n if (details.branchName) {\n message += `\\nBranch: ${details.branchName}`\n }\n }\n\n return message\n}\n","/**\n * @episoda/core\n *\n * Reusable business logic for Episoda local development orchestration.\n * Used by both episoda CLI (current) and @episoda/mcp-local-dev (future).\n *\n * DESIGN PRINCIPLES:\n * - Interface-agnostic: No CLI or MCP-specific code\n * - Structured data: Return objects, not formatted strings\n * - Error codes: Return codes, not messages\n * - Reusable: 80% code reuse target for MCP conversion\n */\n\n// Core types (LOCKED for Phase 2)\nexport * from './command-protocol'\n\n// Business logic classes\nexport { GitExecutor } from './git-executor'\nexport { EpisodaClient, type DisconnectEvent, type ClientEvent, type EventHandler } from './websocket-client'\n\n// Utilities\nexport * from './auth'\nexport * from './errors'\nexport * from './git-validator'\nexport * from './git-parser'\n\n// Package version - single source of truth from package.json\nexport { VERSION } from './version'\n","/**\n * Episoda CLI\n *\n * Local development workflow orchestration for episoda.dev\n */\n\nimport { program } from 'commander'\nimport { VERSION } from '@episoda/core'\nimport { devCommand } from './commands/dev'\nimport { authCommand } from './commands/auth'\nimport { statusCommand } from './commands/status'\nimport { stopCommand } from './commands/stop'\nimport { status } from './output'\n\nprogram\n .name('episoda')\n .description('Episoda local development workflow orchestration')\n .version(VERSION)\n\n// Auth command (K722)\nprogram\n .command('auth')\n .description('Authenticate to Episoda via OAuth and configure CLI')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (options) => {\n try {\n await authCommand(options)\n } catch (error) {\n status.error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Dev command\nprogram\n .command('dev')\n .description('Start dev server with Episoda orchestration')\n .option('--auto-restart', 'Auto-restart dev server if it crashes')\n .option('--connection-only', 'Connection-only mode (don\\'t start dev server)')\n .allowUnknownOption()\n .action(async (options, cmd) => {\n try {\n // Extract command after 'dev' as the dev server command\n // e.g., 'episoda dev -- npm run dev' -> [\"npm\", \"run\", \"dev\"]\n const args = process.argv.slice(process.argv.indexOf('dev') + 1)\n\n // Remove '--' separator if present\n const separatorIndex = args.indexOf('--')\n const command = separatorIndex >= 0 ? args.slice(separatorIndex + 1) : []\n\n // Filter out our own options\n const filteredCommand = command.filter(arg => arg !== '--auto-restart' && arg !== '--connection-only')\n\n await devCommand({\n command: options.connectionOnly ? [] : (filteredCommand.length > 0 ? filteredCommand : undefined),\n autoRestart: options.autoRestart\n })\n } catch (error) {\n status.error(`Dev command failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Status command\n// EP805: Added --verify flag for explicit connection health check\nprogram\n .command('status')\n .description('Show connection status')\n .option('--verify', 'Verify connection is healthy (not just connected)')\n .action(async (options) => {\n try {\n await statusCommand({ verify: options.verify })\n } catch (error) {\n status.error(`Status check failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Stop command (K722)\nprogram\n .command('stop')\n .description('Stop the Episoda daemon')\n .option('--force', 'Force kill daemon without graceful shutdown')\n .action(async (options) => {\n try {\n await stopCommand(options)\n } catch (error) {\n status.error(`Stop failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Disconnect command (alias for stop)\nprogram\n .command('disconnect')\n .description('Disconnect from episoda.dev (alias for stop)')\n .action(async () => {\n try {\n await stopCommand()\n } catch (error) {\n status.error(`Disconnect failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram.parse()\n","/**\n * `episoda dev` command\n *\n * K722 Architecture:\n * - Ensures daemon is running\n * - Adds project to daemon for WebSocket connection\n * - Optionally wraps user's dev server command\n * - Daemon handles WebSocket persistence across terminal sessions\n */\n\nimport { loadConfig, saveConfig } from '@episoda/core'\nimport { resolveDevCommand } from '../framework-detector'\nimport { status, printFrameworkDetection } from '../output'\nimport { isDaemonRunning, startDaemon, killAllEpisodaProcesses } from '../daemon/daemon-manager'\nimport { addProject, isDaemonReachable } from '../ipc/ipc-client'\nimport { spawn, ChildProcess, execSync } from 'child_process'\nimport * as path from 'path'\nimport { isPortInUse, getServerPort } from '../utils/port-check'\n\n// EP734: Connection retry configuration (polling removed - now event-driven)\nconst CONNECTION_MAX_RETRIES = 3 // Retry up to 3 times on failure\n\n/**\n * EP593: Find the git repository root directory\n * This ensures we always use the same project path regardless of where episoda dev is run\n */\nfunction findGitRoot(startDir: string): string | null {\n try {\n const result = execSync('git rev-parse --show-toplevel', {\n cwd: startDir,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe']\n })\n return result.trim()\n } catch {\n return null\n }\n}\n\nexport interface DevCommandOptions {\n command?: string[] // User-provided command (e.g., [\"npm\", \"run\", \"dev\"])\n autoRestart?: boolean // Auto-restart dev server if it crashes\n cwd?: string // Working directory\n}\n\n/**\n * Execute the `episoda dev` command\n * @param options - Command options\n */\nexport async function devCommand(options: DevCommandOptions = {}): Promise<void> {\n try {\n // K722: Load existing config (created by /api/start OAuth flow)\n const config = await loadConfig()\n if (!config || !config.access_token || !config.project_id) {\n status.error('No authentication found. Please run:')\n status.info('')\n status.info(' episoda auth')\n status.info('')\n status.info('This will authenticate with episoda.dev and configure the CLI.')\n process.exit(1)\n }\n\n // EP734: Clean up any stale episoda processes before starting\n // This ensures only one daemon runs per device\n const killedCount = killAllEpisodaProcesses()\n if (killedCount > 0) {\n status.info(`Cleaned up ${killedCount} stale process${killedCount > 1 ? 'es' : ''}`)\n }\n\n // EP734: Determine project path using cached settings (avoids network call)\n // Priority: 1. Cached settings (if fresh), 2. Git root detection, 3. Current working directory\n const serverUrl = config.api_url || process.env.EPISODA_API_URL || 'https://episoda.dev'\n let projectPath: string\n\n // EP734: Check if we have fresh cached settings (< 24 hours old)\n const SETTINGS_CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours in ms\n const cachedSettings = config.project_settings\n const isCacheFresh = cachedSettings?.cached_at &&\n (Date.now() - cachedSettings.cached_at) < SETTINGS_CACHE_TTL\n\n if (isCacheFresh && cachedSettings?.local_project_path) {\n // Use cached project path\n projectPath = cachedSettings.local_project_path\n status.debug(`Using cached project path: ${projectPath}`)\n } else {\n // No cache or stale - detect git root locally (no network call)\n const detectedRoot = findGitRoot(options.cwd || process.cwd())\n projectPath = detectedRoot || path.resolve(options.cwd || process.cwd())\n\n if (detectedRoot) {\n status.debug(`Detected project root: ${projectPath}`)\n } else {\n status.warning(`Could not detect git root, using: ${projectPath}`)\n }\n\n // Cache the detected path locally\n const updatedConfig = {\n ...config,\n project_settings: {\n ...cachedSettings,\n local_project_path: projectPath,\n cached_at: Date.now()\n }\n }\n await saveConfig(updatedConfig)\n status.debug('Cached project settings locally')\n\n // Background: Save to server (don't await - fire and forget)\n const settingsUrl = `${serverUrl}/api/projects/${config.project_id}/settings`\n fetch(settingsUrl, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ local_project_path: projectPath })\n }).catch(() => {}) // Ignore errors - server sync is best-effort\n }\n\n // K722: Ensure daemon is running\n let daemonPid = isDaemonRunning()\n if (!daemonPid) {\n status.info('Starting Episoda daemon...')\n try {\n daemonPid = await startDaemon()\n status.success(`Daemon started (PID: ${daemonPid})`)\n } catch (error) {\n status.error(`Failed to start daemon: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n } else {\n status.debug(`Daemon already running (PID: ${daemonPid})`)\n }\n\n // Verify daemon is reachable via IPC\n const reachable = await isDaemonReachable()\n if (!reachable) {\n status.error('Daemon is running but not responding. Try: episoda stop && episoda dev')\n process.exit(1)\n }\n\n // EP734: Add project to daemon - now blocking (no more polling)\n // The addProject IPC call waits for WebSocket connection to complete\n status.info(`Connecting project to Episoda...`)\n\n let connected = false\n let lastError: string | undefined\n\n for (let retry = 0; retry < CONNECTION_MAX_RETRIES && !connected; retry++) {\n if (retry > 0) {\n status.info(`Retrying connection (attempt ${retry + 1}/${CONNECTION_MAX_RETRIES})...`)\n await new Promise(resolve => setTimeout(resolve, 1000))\n }\n\n try {\n // EP734: Blocking call - waits for connection to complete\n const result = await addProject(config.project_id, projectPath)\n\n if (result.connected) {\n connected = true\n } else {\n lastError = result.error || 'Connection failed'\n }\n } catch (error) {\n lastError = error instanceof Error ? error.message : String(error)\n }\n }\n\n if (!connected) {\n status.error(`Failed to connect: ${lastError}`)\n status.info('Check server status at https://episoda.dev/api/system/health')\n process.exit(1)\n }\n\n status.success('Connected to Episoda')\n\n // Determine if we should start a dev server or just connect\n let command: string[] | undefined\n let detection: any\n let connectionOnlyMode = false\n\n // Check if user explicitly requested connection-only mode\n if (options.command && options.command.length === 0) {\n connectionOnlyMode = true\n status.debug('Connection-only mode (explicit)')\n } else if (!options.command) {\n // Auto-detect: Check if server is already running\n const serverPort = getServerPort()\n const portInUse = await isPortInUse(serverPort)\n\n if (portInUse) {\n connectionOnlyMode = true\n status.info(`Server already running on port ${serverPort}`)\n status.info('Connecting to existing server...')\n }\n }\n\n // Resolve dev command if not in connection-only mode\n if (!connectionOnlyMode) {\n try {\n const result = await resolveDevCommand(options.command || null, projectPath)\n command = result.command\n detection = result.detection\n\n // Show framework detection if auto-detected\n if (detection && !options.command) {\n printFrameworkDetection(detection.framework, detection.command, detection.confidence)\n }\n } catch (error) {\n // No command detected\n status.debug('No dev command detected')\n command = undefined\n }\n }\n\n // If dev command provided, wrap it\n if (command && command.length > 0) {\n await runDevServer(command, projectPath, options.autoRestart ?? false)\n } else {\n // Connection-only mode\n status.info('')\n status.info('Connected! Git operations will be executed by Episoda.')\n status.info('Press Ctrl+C to disconnect.')\n status.info('')\n\n // Keep process alive\n await new Promise(() => {})\n }\n } catch (error) {\n status.error(`Failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n}\n\n/**\n * Run dev server process\n */\nasync function runDevServer(\n command: string[],\n cwd: string,\n autoRestart: boolean\n): Promise<void> {\n let devProcess: ChildProcess | undefined\n let restartCount = 0\n let shuttingDown = false\n\n const startServer = () => {\n status.info(`Starting dev server: ${command.join(' ')}`)\n\n devProcess = spawn(command[0], command.slice(1), {\n cwd,\n stdio: ['inherit', 'inherit', 'inherit'],\n shell: true\n })\n\n devProcess.on('exit', (code, signal) => {\n if (shuttingDown) {\n status.info('Dev server stopped')\n return\n }\n\n if (code === 0) {\n status.success('Dev server exited successfully')\n } else {\n status.error(`Dev server exited with code ${code}${signal ? ` (signal: ${signal})` : ''}`)\n }\n\n // Auto-restart if enabled\n if (autoRestart && !shuttingDown) {\n restartCount++\n status.info(`Auto-restarting dev server (attempt ${restartCount})...`)\n setTimeout(() => {\n startServer()\n }, 2000)\n }\n })\n\n devProcess.on('error', (error) => {\n status.error(`Dev server error: ${error.message}`)\n })\n }\n\n // Setup shutdown handler\n const shutdownHandler = async (signal: string) => {\n if (shuttingDown) return\n shuttingDown = true\n\n status.info(`\\nReceived ${signal}, shutting down...`)\n\n if (devProcess) {\n devProcess.kill('SIGTERM')\n }\n\n // Note: Daemon keeps running in background\n status.info('Dev server stopped. Daemon continues running in background.')\n process.exit(0)\n }\n\n process.on('SIGTERM', () => shutdownHandler('SIGTERM'))\n process.on('SIGINT', () => shutdownHandler('SIGINT'))\n\n startServer()\n\n // Keep process alive\n await new Promise(() => {})\n}\n","/**\n * Framework Detection\n *\n * Detects the framework/stack based on project files and suggests\n * appropriate dev server command.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nexport interface FrameworkDetection {\n framework: string\n command: string[]\n confidence: 'high' | 'medium' | 'low'\n detectedFrom: string\n}\n\n/**\n * Detect framework from project files\n * @param cwd - Working directory to search (defaults to process.cwd())\n * @returns Detection result or null if no framework detected\n */\nexport async function detectFramework(cwd: string = process.cwd()): Promise<FrameworkDetection | null> {\n // Check for package.json (Node.js projects)\n const packageJsonPath = path.join(cwd, 'package.json')\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n // Check scripts first\n const scripts = packageJson.scripts || {}\n\n // Next.js detection\n if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {\n if (scripts.dev) {\n return {\n framework: 'Next.js',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (next dependency + dev script)'\n }\n }\n return {\n framework: 'Next.js',\n command: ['npx', 'next', 'dev'],\n confidence: 'medium',\n detectedFrom: 'package.json (next dependency)'\n }\n }\n\n // React (Create React App, Vite, etc.)\n if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {\n if (scripts.dev) {\n return {\n framework: 'React',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (react + dev script)'\n }\n }\n if (scripts.start) {\n return {\n framework: 'React',\n command: ['npm', 'start'],\n confidence: 'high',\n detectedFrom: 'package.json (react + start script)'\n }\n }\n }\n\n // Express\n if (packageJson.dependencies?.express) {\n if (scripts.dev) {\n return {\n framework: 'Express',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (express + dev script)'\n }\n }\n if (scripts.start) {\n return {\n framework: 'Express',\n command: ['npm', 'start'],\n confidence: 'medium',\n detectedFrom: 'package.json (express + start script)'\n }\n }\n }\n\n // Vue\n if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {\n if (scripts.dev) {\n return {\n framework: 'Vue',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (vue + dev script)'\n }\n }\n if (scripts.serve) {\n return {\n framework: 'Vue',\n command: ['npm', 'run', 'serve'],\n confidence: 'high',\n detectedFrom: 'package.json (vue + serve script)'\n }\n }\n }\n\n // Generic Node.js (fallback)\n if (scripts.dev) {\n return {\n framework: 'Node.js',\n command: ['npm', 'run', 'dev'],\n confidence: 'medium',\n detectedFrom: 'package.json (dev script)'\n }\n }\n if (scripts.start) {\n return {\n framework: 'Node.js',\n command: ['npm', 'start'],\n confidence: 'low',\n detectedFrom: 'package.json (start script)'\n }\n }\n }\n\n // Check for requirements.txt (Python projects)\n const requirementsPath = path.join(cwd, 'requirements.txt')\n if (fs.existsSync(requirementsPath)) {\n const requirements = fs.readFileSync(requirementsPath, 'utf-8')\n\n // Django\n if (requirements.includes('Django') || requirements.includes('django')) {\n const managePy = path.join(cwd, 'manage.py')\n if (fs.existsSync(managePy)) {\n return {\n framework: 'Django',\n command: ['python', 'manage.py', 'runserver'],\n confidence: 'high',\n detectedFrom: 'requirements.txt (Django) + manage.py'\n }\n }\n return {\n framework: 'Django',\n command: ['python', 'manage.py', 'runserver'],\n confidence: 'medium',\n detectedFrom: 'requirements.txt (Django)'\n }\n }\n\n // Flask\n if (requirements.includes('Flask') || requirements.includes('flask')) {\n // Look for common Flask app files\n const appFiles = ['app.py', 'application.py', 'wsgi.py']\n for (const file of appFiles) {\n if (fs.existsSync(path.join(cwd, file))) {\n return {\n framework: 'Flask',\n command: ['flask', 'run'],\n confidence: 'high',\n detectedFrom: `requirements.txt (Flask) + ${file}`\n }\n }\n }\n return {\n framework: 'Flask',\n command: ['flask', 'run'],\n confidence: 'medium',\n detectedFrom: 'requirements.txt (Flask)'\n }\n }\n\n // FastAPI / Uvicorn\n if (requirements.includes('fastapi') || requirements.includes('uvicorn')) {\n return {\n framework: 'FastAPI',\n command: ['uvicorn', 'main:app', '--reload'],\n confidence: 'medium',\n detectedFrom: 'requirements.txt (fastapi/uvicorn)'\n }\n }\n }\n\n // Check for Gemfile (Ruby projects)\n const gemfilePath = path.join(cwd, 'Gemfile')\n if (fs.existsSync(gemfilePath)) {\n const gemfile = fs.readFileSync(gemfilePath, 'utf-8')\n\n // Rails\n if (gemfile.includes('rails')) {\n return {\n framework: 'Rails',\n command: ['rails', 'server'],\n confidence: 'high',\n detectedFrom: 'Gemfile (rails)'\n }\n }\n\n // Sinatra\n if (gemfile.includes('sinatra')) {\n return {\n framework: 'Sinatra',\n command: ['ruby', 'app.rb'],\n confidence: 'medium',\n detectedFrom: 'Gemfile (sinatra)'\n }\n }\n }\n\n // Check for go.mod (Go projects)\n const goModPath = path.join(cwd, 'go.mod')\n if (fs.existsSync(goModPath)) {\n return {\n framework: 'Go',\n command: ['go', 'run', '.'],\n confidence: 'medium',\n detectedFrom: 'go.mod'\n }\n }\n\n // Check for Cargo.toml (Rust projects)\n const cargoTomlPath = path.join(cwd, 'Cargo.toml')\n if (fs.existsSync(cargoTomlPath)) {\n return {\n framework: 'Rust',\n command: ['cargo', 'run'],\n confidence: 'medium',\n detectedFrom: 'Cargo.toml'\n }\n }\n\n // No framework detected\n return null\n}\n\n/**\n * Suggest command based on user-provided command or auto-detection\n * @param providedCommand - Command provided by user (e.g., [\"npm\", \"run\", \"dev\"])\n * @param cwd - Working directory\n * @returns Final command to use\n */\nexport async function resolveDevCommand(\n providedCommand: string[] | null,\n cwd: string = process.cwd()\n): Promise<{ command: string[]; detection: FrameworkDetection | null }> {\n // If user provided command, use it as-is\n if (providedCommand && providedCommand.length > 0) {\n return { command: providedCommand, detection: null }\n }\n\n // Auto-detect framework\n const detection = await detectFramework(cwd)\n if (detection) {\n return { command: detection.command, detection }\n }\n\n // No detection - default to npm run dev\n return {\n command: ['npm', 'run', 'dev'],\n detection: {\n framework: 'Unknown',\n command: ['npm', 'run', 'dev'],\n confidence: 'low',\n detectedFrom: 'default fallback'\n }\n }\n}\n","/**\n * Terminal Output Formatting (CLI-specific)\n *\n * This module handles terminal output formatting.\n * CLI-specific - not exported from core/index.ts\n */\n\nimport chalk from 'chalk'\nimport ora, { Ora } from 'ora'\nimport { ErrorCode, ExecutionResult } from '@episoda/core'\n\n/**\n * Format a git execution result for terminal output\n */\nexport function formatResult(result: ExecutionResult): string {\n if (result.success) {\n return chalk.green('✓ ' + (result.output || 'Success'))\n }\n\n let message = chalk.red('✗ Failed')\n if (result.error) {\n message += `: ${formatError(result.error, result.details)}`\n }\n return message\n}\n\n/**\n * Format an error code with details for terminal output\n */\nexport function formatError(code: ErrorCode, details?: Record<string, any>): string {\n const errorMessages: Record<ErrorCode, string> = {\n GIT_NOT_INSTALLED: 'Git is not installed',\n NOT_GIT_REPO: 'Not a git repository',\n MERGE_CONFLICT: 'Merge conflict detected',\n REBASE_CONFLICT: 'Rebase conflict - resolve conflicts or abort rebase',\n UNCOMMITTED_CHANGES: 'You have uncommitted changes',\n NETWORK_ERROR: 'Network error',\n AUTH_FAILURE: 'Authentication failed',\n BRANCH_NOT_FOUND: 'Branch not found',\n BRANCH_ALREADY_EXISTS: 'Branch already exists',\n PUSH_REJECTED: 'Push rejected',\n COMMAND_TIMEOUT: 'Command timed out',\n UNKNOWN_ERROR: 'Unknown error'\n }\n\n let message = chalk.red(errorMessages[code] || code)\n\n if (details) {\n if (details.uncommittedFiles && details.uncommittedFiles.length > 0) {\n message += chalk.yellow('\\n Uncommitted files: ' + details.uncommittedFiles.join(', '))\n }\n if (details.conflictingFiles && details.conflictingFiles.length > 0) {\n message += chalk.yellow('\\n Conflicting files: ' + details.conflictingFiles.join(', '))\n }\n if (details.branchName) {\n message += chalk.cyan('\\n Branch: ' + details.branchName)\n }\n }\n\n return message\n}\n\n/**\n * Create a spinner for long-running operations\n */\nexport function createSpinner(text: string): Ora {\n return ora(text)\n}\n\n/**\n * Print status messages\n */\nexport const status = {\n success: (message: string) => console.log(chalk.green('✓'), message),\n error: (message: string) => console.log(chalk.red('✗'), message),\n warning: (message: string) => console.log(chalk.yellow('⚠'), message),\n info: (message: string) => console.log(chalk.blue('ℹ'), message),\n debug: (message: string) => {\n if (process.env.DEBUG) {\n console.log(chalk.gray('⚙'), chalk.gray(message))\n }\n },\n // EP606: Progress indicator that overwrites the current line\n progress: (message: string) => {\n process.stdout.write(`\\r${chalk.blue('⏳')} ${message}`)\n }\n}\n\n/**\n * Print a header with framework detection info\n */\nexport function printFrameworkDetection(framework: string, command: string[], confidence: string) {\n console.log(chalk.bold('\\n🔍 Framework Detection'))\n console.log(chalk.cyan(' Framework:'), framework)\n console.log(chalk.cyan(' Command:'), command.join(' '))\n console.log(chalk.cyan(' Confidence:'), confidence)\n console.log()\n}\n\n/**\n * Print connection status\n */\nexport function printConnectionStatus(connected: boolean, projectId?: string) {\n if (connected) {\n status.success(`Connected to episoda.dev${projectId ? ` (${projectId})` : ''}`)\n } else {\n status.warning('Not connected to episoda.dev')\n }\n}\n\n/**\n * Print dev server status\n */\nexport function printDevServerStatus(running: boolean, pid?: number) {\n if (running) {\n status.success(`Dev server running${pid ? ` (PID: ${pid})` : ''}`)\n } else {\n status.warning('Dev server stopped')\n }\n}\n\n/**\n * Print git command execution\n */\nexport function printGitCommand(command: string) {\n status.info(chalk.gray(`Executing: ${command}`))\n}\n","/**\n * Daemon lifecycle management\n *\n * Manages the Episoda daemon process lifecycle:\n * - Spawning daemon in detached mode\n * - Checking daemon status\n * - Stopping daemon gracefully\n * - PID file management\n *\n * Ensures only one daemon runs per user.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { spawn, execSync } from 'child_process'\nimport { getConfigDir } from '@episoda/core'\n\n/**\n * EP734: Kill all stale Episoda processes\n *\n * Finds and kills any existing episoda-related processes to ensure\n * a clean slate before starting a new daemon. This prevents:\n * - Multiple daemon processes running simultaneously\n * - Stale `episoda dev` foreground processes from previous sessions\n * - Orphaned node processes running daemon-process.js\n *\n * @returns Number of processes killed\n */\nexport function killAllEpisodaProcesses(): number {\n const currentPid = process.pid\n let killedCount = 0\n\n // EP734: Windows not supported for process cleanup - skip with warning\n if (process.platform === 'win32') {\n console.warn('[Cleanup] Process cleanup not supported on Windows - skipping')\n return 0\n }\n\n try {\n // Find all episoda-related processes using ps (Unix/macOS only)\n // Patterns: 'episoda dev', 'daemon-process.js', 'episoda status', 'ws-server.js'\n // EP797: Added ws-server.js to catch orphaned WebSocket server processes\n const psOutput = execSync(\n 'ps aux | grep -E \"(episoda (dev|status|stop)|daemon-process\\\\.js|node ws-server\\\\.js)\" | grep -v grep || true',\n { encoding: 'utf-8', timeout: 5000 }\n )\n\n const lines = psOutput.trim().split('\\n').filter(line => line.length > 0)\n\n for (const line of lines) {\n // Parse PID from ps aux output (second column)\n const parts = line.trim().split(/\\s+/)\n if (parts.length < 2) continue\n\n const pid = parseInt(parts[1], 10)\n if (isNaN(pid) || pid === currentPid) continue\n\n try {\n process.kill(pid, 'SIGTERM')\n killedCount++\n console.log(`[Cleanup] Killed stale process PID ${pid}`)\n } catch (err) {\n // Process might have already exited, ignore\n }\n }\n\n // Give processes a moment to terminate\n if (killedCount > 0) {\n execSync('sleep 0.5', { timeout: 2000 })\n }\n\n // Clean up stale files\n const configDir = getConfigDir()\n const pidPath = path.join(configDir, 'daemon.pid')\n const sockPath = path.join(configDir, 'daemon.sock')\n\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n if (fs.existsSync(sockPath)) {\n fs.unlinkSync(sockPath)\n }\n\n } catch (error) {\n // Non-fatal - best effort cleanup\n console.warn('[Cleanup] Error during process cleanup:', error instanceof Error ? error.message : error)\n }\n\n return killedCount\n}\n\n/**\n * Get path to daemon PID file\n */\nexport function getPidFilePath(): string {\n return path.join(getConfigDir(), 'daemon.pid')\n}\n\n/**\n * Check if daemon is running\n *\n * Reads PID from file and checks if process exists.\n *\n * @returns PID if running, null if not\n */\nexport function isDaemonRunning(): number | null {\n const pidPath = getPidFilePath()\n\n try {\n if (!fs.existsSync(pidPath)) {\n return null\n }\n\n const pidStr = fs.readFileSync(pidPath, 'utf-8').trim()\n const pid = parseInt(pidStr, 10)\n\n if (isNaN(pid)) {\n // Invalid PID, clean up file\n fs.unlinkSync(pidPath)\n return null\n }\n\n // Check if process exists\n try {\n // Sending signal 0 checks existence without killing\n process.kill(pid, 0)\n return pid\n } catch (error) {\n // Process doesn't exist, clean up stale PID file\n fs.unlinkSync(pidPath)\n return null\n }\n } catch (error) {\n console.error('Error checking daemon status:', error)\n return null\n }\n}\n\n/**\n * Start the daemon process\n *\n * Spawns daemon in detached mode. Daemon survives terminal close.\n *\n * @throws Error if daemon already running or spawn fails\n */\nexport async function startDaemon(): Promise<number> {\n // Check if already running\n const existingPid = isDaemonRunning()\n if (existingPid) {\n throw new Error(`Daemon already running (PID: ${existingPid})`)\n }\n\n // Ensure config directory exists\n const configDir = getConfigDir()\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true })\n }\n\n // Path to daemon entry point\n // When built: dist/daemon/daemon-process.js (tsup preserves directory structure)\n // __dirname is dist/ when running from bundled index.js\n const daemonScript = path.join(__dirname, 'daemon', 'daemon-process.js')\n\n if (!fs.existsSync(daemonScript)) {\n throw new Error(`Daemon script not found: ${daemonScript}. Make sure CLI is built.`)\n }\n\n // Spawn daemon as detached process\n const child = spawn('node', [daemonScript], {\n detached: true, // Run independently of parent\n stdio: 'ignore', // Don't inherit stdio (prevents hanging)\n env: {\n ...process.env,\n EPISODA_DAEMON_MODE: '1', // Signal to daemon it's running in daemon mode\n },\n })\n\n // Detach from parent process\n child.unref()\n\n const pid = child.pid!\n\n // Save PID to file\n const pidPath = getPidFilePath()\n fs.writeFileSync(pidPath, pid.toString(), 'utf-8')\n\n // Give daemon a moment to start\n await new Promise(resolve => setTimeout(resolve, 500))\n\n // Verify daemon actually started\n const runningPid = isDaemonRunning()\n if (!runningPid) {\n throw new Error('Daemon failed to start')\n }\n\n return pid\n}\n\n/**\n * Stop the daemon process\n *\n * Sends SIGTERM for graceful shutdown. If daemon doesn't stop\n * within timeout, sends SIGKILL.\n *\n * @param timeout Milliseconds to wait before SIGKILL (default: 5000)\n * @returns true if stopped, false if wasn't running\n */\nexport async function stopDaemon(timeout: number = 5000): Promise<boolean> {\n const pid = isDaemonRunning()\n if (!pid) {\n // Clean up PID file just in case\n const pidPath = getPidFilePath()\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n return false\n }\n\n try {\n // Send SIGTERM for graceful shutdown\n process.kill(pid, 'SIGTERM')\n\n // Wait for process to exit\n const startTime = Date.now()\n while (Date.now() - startTime < timeout) {\n try {\n process.kill(pid, 0) // Check if still alive\n await new Promise(resolve => setTimeout(resolve, 100))\n } catch (error) {\n // Process exited\n const pidPath = getPidFilePath()\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n return true\n }\n }\n\n // Timeout reached, force kill\n console.warn(`Daemon didn't stop gracefully, forcing shutdown (PID: ${pid})`)\n process.kill(pid, 'SIGKILL')\n\n // Clean up PID file\n const pidPath = getPidFilePath()\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n\n return true\n } catch (error) {\n console.error('Error stopping daemon:', error)\n return false\n }\n}\n\n/**\n * Restart the daemon\n *\n * Stops existing daemon and starts new one.\n *\n * @returns PID of new daemon\n */\nexport async function restartDaemon(): Promise<number> {\n await stopDaemon()\n return startDaemon()\n}\n\n/**\n * Get daemon status information\n *\n * @returns Status object with running state and PID\n */\nexport function getDaemonStatus(): { running: boolean; pid: number | null } {\n const pid = isDaemonRunning()\n return {\n running: pid !== null,\n pid,\n }\n}\n","/**\n * IPC Client - Used by CLI commands\n *\n * Sends commands to daemon via Unix domain socket.\n * Handles request/response communication.\n *\n * Socket location: ~/.episoda/daemon.sock\n */\n\nimport * as net from 'net'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport { getConfigDir } from '@episoda/core'\n\nconst getSocketPath = () => path.join(getConfigDir(), 'daemon.sock')\nconst DEFAULT_TIMEOUT = 15000 // 15 seconds (EP606: increased for device registration)\n\nexport interface IPCRequest {\n id: string\n command: string\n params?: any\n}\n\nexport interface IPCResponse {\n id: string\n success: boolean\n data?: any\n error?: string\n}\n\n/**\n * Send command to daemon\n *\n * Connects to daemon socket, sends command, waits for response.\n *\n * @param command Command name\n * @param params Command parameters\n * @param timeout Timeout in milliseconds (default: 15000)\n * @returns Response data\n * @throws Error if daemon not running or command fails\n */\nexport async function sendCommand(\n command: string,\n params?: any,\n timeout: number = DEFAULT_TIMEOUT\n): Promise<any> {\n return new Promise((resolve, reject) => {\n const socket = net.createConnection(getSocketPath())\n const requestId = crypto.randomUUID()\n let buffer = ''\n let timeoutHandle: NodeJS.Timeout\n\n // Set timeout\n timeoutHandle = setTimeout(() => {\n socket.destroy()\n reject(new Error(`Command timed out after ${timeout}ms`))\n }, timeout)\n\n socket.on('connect', () => {\n // Send request\n const request: IPCRequest = {\n id: requestId,\n command,\n params,\n }\n\n socket.write(JSON.stringify(request) + '\\n')\n })\n\n socket.on('data', (chunk) => {\n buffer += chunk.toString()\n\n // Check for complete response (delimited by newline)\n const newlineIndex = buffer.indexOf('\\n')\n if (newlineIndex === -1) return\n\n // Extract response\n const message = buffer.slice(0, newlineIndex)\n\n try {\n const response = JSON.parse(message) as IPCResponse\n\n // Verify response ID matches\n if (response.id !== requestId) {\n reject(new Error('Response ID mismatch'))\n return\n }\n\n clearTimeout(timeoutHandle)\n socket.end()\n\n if (response.success) {\n resolve(response.data)\n } else {\n reject(new Error(response.error || 'Command failed'))\n }\n } catch (error) {\n clearTimeout(timeoutHandle)\n socket.end()\n reject(new Error('Invalid response from daemon'))\n }\n })\n\n socket.on('error', (error) => {\n clearTimeout(timeoutHandle)\n\n // Check for common errors\n if ((error as any).code === 'ENOENT' || (error as any).code === 'ECONNREFUSED') {\n reject(new Error('Daemon is not running. Start it with: episoda dev'))\n } else {\n reject(error)\n }\n })\n\n socket.on('timeout', () => {\n clearTimeout(timeoutHandle)\n socket.destroy()\n reject(new Error('Connection timeout'))\n })\n })\n}\n\n/**\n * Check if daemon is reachable via IPC\n *\n * @returns true if daemon is running and responding\n */\nexport async function isDaemonReachable(): Promise<boolean> {\n try {\n await sendCommand('ping', {}, 1000)\n return true\n } catch (error) {\n return false\n }\n}\n\n/**\n * Add project to daemon\n *\n * EP734: Now blocking - waits for WebSocket connection to complete\n *\n * @param projectId Supabase project ID\n * @param projectPath Absolute path to project\n * @returns Connection result with success/error info\n */\nexport async function addProject(projectId: string, projectPath: string): Promise<{\n success: boolean\n connected: boolean\n error?: string\n}> {\n // EP734: Increased timeout to 30s to allow for WebSocket connection\n return await sendCommand('add-project', { projectId, projectPath }, 30000)\n}\n\n/**\n * Remove project from daemon\n *\n * @param projectPath Absolute path to project\n */\nexport async function removeProject(projectPath: string): Promise<void> {\n await sendCommand('remove-project', { projectPath })\n}\n\n/**\n * Get daemon status\n *\n * EP738: Added hostname, platform, arch for status command (HTTP server removed)\n *\n * @returns Status information including connected projects and device info\n */\nexport async function getStatus(): Promise<{\n running: boolean\n machineId: string\n deviceId?: string\n hostname: string\n platform: string\n arch: string\n projects: Array<{\n id: string\n path: string\n name: string\n connected: boolean\n }>\n}> {\n return await sendCommand('status')\n}\n\n/**\n * Request daemon to connect to a project's WebSocket\n *\n * @param projectPath Absolute path to project\n */\nexport async function connectProject(projectPath: string): Promise<void> {\n await sendCommand('connect-project', { projectPath })\n}\n\n/**\n * Request daemon to disconnect from a project's WebSocket\n *\n * @param projectPath Absolute path to project\n */\nexport async function disconnectProject(projectPath: string): Promise<void> {\n await sendCommand('disconnect-project', { projectPath })\n}\n\n/**\n * Request daemon shutdown\n */\nexport async function shutdownDaemon(): Promise<void> {\n await sendCommand('shutdown', {}, 2000)\n}\n\n/**\n * EP805: Verify connection health\n *\n * Checks if connections are actually healthy (in both connections Map and liveConnections Set).\n * Useful for detecting stale connections where WebSocket died but entry persists.\n *\n * @returns Health status with per-project details\n */\nexport async function verifyHealth(): Promise<{\n totalProjects: number\n healthyConnections: number\n staleConnections: number\n projects: Array<{\n id: string\n path: string\n name: string\n inConnectionsMap: boolean\n inLiveConnections: boolean\n isHealthy: boolean\n }>\n}> {\n return await sendCommand('verify-health')\n}\n\n// EP734: Removed getConnectionStatus - no longer needed with blocking add-project\n","/**\n * Port availability checker\n */\n\nimport * as net from 'net'\n\n/**\n * Check if a port is in use\n * @param port Port number to check\n * @returns true if port is in use, false otherwise\n */\nexport async function isPortInUse(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer()\n\n server.once('error', (err: any) => {\n if (err.code === 'EADDRINUSE') {\n resolve(true)\n } else {\n resolve(false)\n }\n })\n\n server.once('listening', () => {\n server.close()\n resolve(false)\n })\n\n // Don't specify host - bind to all interfaces to detect any listener\n // This matches how Next.js and other dev servers bind\n server.listen(port)\n })\n}\n\n/**\n * Get the server port from config or default\n * @returns Port number\n */\nexport function getServerPort(): number {\n // Check environment variable\n if (process.env.PORT) {\n return parseInt(process.env.PORT, 10)\n }\n\n // Default to 3000 (Next.js default)\n return 3000\n}\n","/**\n * `episoda auth` command (K722)\n *\n * Combines OAuth device flow with automatic configuration:\n * 1. Initiates OAuth device flow (gets device_code and user_code)\n * 2. Opens browser for user to authorize and select project\n * 3. Monitors SSE stream for authorization completion\n * 4. Exchanges device_code for access_token\n * 5. Saves complete config to ~/.episoda/config.json\n * 6. EP548: Installs git credential helper for seamless GitHub auth\n *\n * This replaces the old two-step flow (episoda init + setup token).\n * Now users just run `episoda auth` and select their project in the browser.\n */\n\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { spawn, execSync } from 'child_process'\nimport { saveConfig, VERSION, getConfigPath } from '@episoda/core'\nimport { status } from '../output'\nimport { getMachineId } from '../daemon/machine-id'\nimport { generateCredentialHelperScript } from '../git-helpers/git-credential-helper'\n\ninterface DeviceAuthorizationResponse {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n expires_in: number\n interval: number\n}\n\ninterface TokenResponse {\n access_token: string\n token_type: 'Bearer'\n expires_in: number\n refresh_token: string\n user_id: string\n workspace_id: string\n workspace_uid: string\n project_id: string\n project_uid: string\n}\n\ninterface SSEMessage {\n status: 'pending' | 'authorized' | 'denied' | 'expired'\n message?: string\n}\n\nexport interface AuthCommandOptions {\n apiUrl?: string\n}\n\n/**\n * Auth command - OAuth device flow with browser-based project selection\n */\nexport async function authCommand(options: AuthCommandOptions = {}): Promise<void> {\n const apiUrl = options.apiUrl || process.env.EPISODA_API_URL || 'https://episoda.dev'\n\n status.info('Initializing Episoda CLI...')\n status.info('')\n\n // Step 1: Initiate device flow\n status.info('Step 1/3: Requesting authorization code...')\n const deviceAuth = await initiateDeviceFlow(apiUrl)\n\n status.success(`✓ Authorization code received: ${deviceAuth.user_code}`)\n status.info('')\n\n // Step 2: Open browser and monitor for authorization\n status.info('Step 2/3: Opening browser for authorization...')\n status.info(` Visit: ${deviceAuth.verification_uri_complete}`)\n status.info(` User code: ${deviceAuth.user_code}`)\n status.info('')\n\n // Open browser\n openBrowser(deviceAuth.verification_uri_complete)\n\n status.info('Waiting for authorization...')\n const authorized = await monitorAuthorization(apiUrl, deviceAuth.device_code, deviceAuth.expires_in)\n\n if (!authorized) {\n throw new Error('Authorization failed or timed out')\n }\n\n status.success('✓ Authorization successful!')\n status.info('')\n\n // Step 3: Exchange device code for access token\n status.info('Step 3/3: Exchanging authorization for access token...')\n\n // EP609: Get machine ID to associate with token\n const machineId = await getMachineId()\n const tokenResponse = await exchangeDeviceCode(apiUrl, deviceAuth.device_code, machineId)\n\n status.success('✓ Access token received')\n status.info(` Project: ${tokenResponse.project_uid}`)\n status.info(` Workspace: ${tokenResponse.workspace_uid}`)\n status.info('')\n\n // Save configuration\n await saveConfig({\n project_id: tokenResponse.project_id,\n user_id: tokenResponse.user_id,\n workspace_id: tokenResponse.workspace_id,\n access_token: tokenResponse.access_token,\n api_url: apiUrl,\n cli_version: VERSION,\n })\n\n status.success(`✓ Configuration saved to ${getConfigPath()}`)\n status.info('')\n\n // EP548: Install git credential helper\n status.info('Installing git credential helper...')\n const credentialHelperInstalled = await installGitCredentialHelper(apiUrl)\n\n if (credentialHelperInstalled) {\n status.success('✓ Git credential helper installed')\n status.info(' Git operations will now use Episoda authentication automatically.')\n } else {\n status.warning('⚠ Git credential helper could not be installed automatically.')\n status.info(' You can still use git with manual token setup.')\n }\n status.info('')\n\n status.success('CLI initialized successfully!')\n status.info('')\n status.info('Next steps:')\n status.info(' • Start daemon: episoda dev')\n status.info(' • Check status: episoda status')\n status.info(' • Git operations will use Episoda auth automatically')\n status.info('')\n}\n\n/**\n * Step 1: Initiate OAuth device flow\n */\nasync function initiateDeviceFlow(apiUrl: string): Promise<DeviceAuthorizationResponse> {\n const response = await fetch(`${apiUrl}/api/oauth/code`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({}),\n })\n\n if (!response.ok) {\n const error = await response.json() as { error?: string; error_description?: string }\n throw new Error(error.error_description || error.error || 'Failed to initiate device flow')\n }\n\n return await response.json() as DeviceAuthorizationResponse\n}\n\n/**\n * Step 2: Monitor authorization via SSE stream\n */\nasync function monitorAuthorization(\n apiUrl: string,\n deviceCode: string,\n expiresIn: number\n): Promise<boolean> {\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n status.error('Authorization timed out')\n resolve(false)\n }, expiresIn * 1000) // Convert seconds to milliseconds\n\n // Start SSE stream\n const url = `${apiUrl}/api/oauth/authorize-stream?device_code=${deviceCode}`\n\n // Use curl to handle SSE stream (simpler than implementing SSE parser in Node)\n const curlProcess = spawn('curl', ['-N', url])\n\n let buffer = ''\n\n curlProcess.stdout.on('data', (chunk: Buffer) => {\n buffer += chunk.toString()\n\n // Process complete SSE messages\n const messages = buffer.split('\\n\\n')\n buffer = messages.pop() || '' // Keep incomplete message in buffer\n\n for (const message of messages) {\n if (!message.trim()) continue\n\n // Parse SSE message\n const lines = message.split('\\n')\n let eventType = ''\n let data = ''\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n eventType = line.substring(7)\n } else if (line.startsWith('data: ')) {\n data = line.substring(6)\n }\n }\n\n if (!eventType) continue\n\n try {\n // Handle based on event type\n if (eventType === 'authorized') {\n clearTimeout(timeout)\n curlProcess.kill()\n resolve(true)\n return\n } else if (eventType === 'denied') {\n clearTimeout(timeout)\n curlProcess.kill()\n status.error('Authorization denied by user')\n resolve(false)\n return\n } else if (eventType === 'expired') {\n clearTimeout(timeout)\n curlProcess.kill()\n status.error('Authorization code expired')\n resolve(false)\n return\n } else if (eventType === 'error') {\n const errorData = JSON.parse(data) as { error?: string; error_description?: string }\n clearTimeout(timeout)\n curlProcess.kill()\n status.error(`Authorization error: ${errorData.error_description || errorData.error || 'Unknown error'}`)\n resolve(false)\n return\n }\n // For 'pending' events, continue waiting\n } catch (error) {\n // Ignore parse errors, continue monitoring\n }\n }\n })\n\n curlProcess.on('error', (error) => {\n clearTimeout(timeout)\n status.error(`Failed to monitor authorization: ${error.message}`)\n resolve(false)\n })\n\n curlProcess.on('close', (code) => {\n clearTimeout(timeout)\n if (code !== 0 && code !== null) {\n status.error(`Authorization monitoring failed with code ${code}`)\n resolve(false)\n }\n })\n })\n}\n\n/**\n * Step 3: Exchange device code for access token\n * EP609: Now includes machine_id to associate token with device\n */\nasync function exchangeDeviceCode(apiUrl: string, deviceCode: string, machineId: string): Promise<TokenResponse> {\n const response = await fetch(`${apiUrl}/api/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n machine_id: machineId,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json() as { error?: string; error_description?: string }\n throw new Error(error.error_description || error.error || 'Failed to exchange device code')\n }\n\n const tokenResponse = await response.json() as TokenResponse\n\n // Validate that we got all required fields\n if (!tokenResponse.access_token || !tokenResponse.user_id || !tokenResponse.workspace_id || !tokenResponse.project_id) {\n throw new Error('Incomplete token response from server')\n }\n\n return tokenResponse\n}\n\n/**\n * Open browser to verification URL\n */\nfunction openBrowser(url: string): void {\n const platform = os.platform()\n\n let command: string\n let args: string[]\n\n switch (platform) {\n case 'darwin':\n command = 'open'\n args = [url]\n break\n case 'win32':\n command = 'cmd'\n args = ['/c', 'start', url]\n break\n default: // Linux and others\n command = 'xdg-open'\n args = [url]\n break\n }\n\n try {\n spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n }).unref()\n } catch (error) {\n status.warning(`Could not open browser automatically. Please visit: ${url}`)\n }\n}\n\n/**\n * EP548: Install git credential helper\n *\n * This function:\n * 1. Creates ~/.episoda/bin directory\n * 2. Writes the credential helper script\n * 3. Makes it executable\n * 4. Adds ~/.episoda/bin to PATH if needed\n * 5. Configures git to use the credential helper\n *\n * The credential helper is added to git's credential helper chain,\n * so existing credential helpers are preserved.\n */\nasync function installGitCredentialHelper(apiUrl: string): Promise<boolean> {\n try {\n const homeDir = os.homedir()\n const episodaBinDir = path.join(homeDir, '.episoda', 'bin')\n const helperPath = path.join(episodaBinDir, 'git-credential-episoda')\n\n // 1. Create bin directory\n fs.mkdirSync(episodaBinDir, { recursive: true })\n\n // 2. Write credential helper script\n const scriptContent = generateCredentialHelperScript(apiUrl)\n fs.writeFileSync(helperPath, scriptContent, { mode: 0o755 })\n\n // 3. Verify it's executable\n try {\n fs.accessSync(helperPath, fs.constants.X_OK)\n } catch {\n // On Windows, this might fail but the script should still work\n }\n\n // 4. Configure git to use the credential helper\n // Check if already configured (use --get-all to check all helpers in chain)\n try {\n const allHelpers = execSync('git config --global --get-all credential.helper', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe']\n }).trim().split('\\n')\n\n // If this exact helper path is already configured, we're done\n // This prevents duplicate entries if user runs `episoda auth` multiple times\n if (allHelpers.some(h => h.includes(helperPath) || h.includes('git-credential-episoda'))) {\n return true\n }\n } catch {\n // No credential helper configured yet, that's fine\n }\n\n // Add episoda credential helper to the chain\n // Using --add ensures we don't overwrite existing helpers\n execSync(`git config --global --add credential.helper \"${helperPath}\"`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe']\n })\n\n // 5. Add to PATH by updating shell profile if needed\n updateShellProfile(episodaBinDir)\n\n return true\n } catch (error: any) {\n // Log error but don't fail the auth flow\n status.warning(`Could not install credential helper: ${error.message}`)\n return false\n }\n}\n\n/**\n * Update shell profile to include ~/.episoda/bin in PATH\n */\nfunction updateShellProfile(binDir: string): void {\n const platform = os.platform()\n\n if (platform === 'win32') {\n // Windows uses a different mechanism (system PATH)\n // Skip for now - the full path in git config should work\n return\n }\n\n // For Unix-like systems, check common shell profiles\n const homeDir = os.homedir()\n const profiles = [\n path.join(homeDir, '.bashrc'),\n path.join(homeDir, '.zshrc'),\n path.join(homeDir, '.profile'),\n ]\n\n const exportLine = `export PATH=\"${binDir}:$PATH\" # Added by episoda auth`\n\n for (const profile of profiles) {\n try {\n if (fs.existsSync(profile)) {\n const content = fs.readFileSync(profile, 'utf8')\n\n // Check if already added\n if (content.includes('.episoda/bin')) {\n continue\n }\n\n // Append export line\n fs.appendFileSync(profile, `\\n# Episoda CLI\\n${exportLine}\\n`)\n }\n } catch {\n // Ignore errors - PATH update is optional\n }\n }\n}\n","/**\n * Machine ID generation and management\n *\n * Generates a stable machine identifier used for multi-machine connection tracking.\n * Format: {hostname}-{hardware-uuid-prefix}\n * Example: \"Alans-MacBook-Pro-a3f2b1c4\"\n *\n * EP731: Uses hardware UUID instead of random UUID to prevent duplicate device\n * registrations when ~/.episoda syncs between devices via iCloud/Dropbox.\n *\n * Properties:\n * - Stable across daemon restarts\n * - Unique per PHYSICAL machine (uses hardware UUID)\n * - Human-readable (includes hostname)\n * - Survives OS reboots\n * - Cannot sync between devices (hardware-based)\n */\n\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport { execSync } from 'child_process'\nimport { getConfigDir } from '@episoda/core'\n\n/**\n * Get or generate machine ID\n *\n * Reads from config directory's machine-id if exists, otherwise generates new one\n * and saves it for future use.\n *\n * @returns Machine ID string\n */\nexport async function getMachineId(): Promise<string> {\n const machineIdPath = path.join(getConfigDir(), 'machine-id')\n\n // Try to read existing machine ID\n try {\n if (fs.existsSync(machineIdPath)) {\n const machineId = fs.readFileSync(machineIdPath, 'utf-8').trim()\n if (machineId) {\n return machineId\n }\n }\n } catch (error) {\n // File doesn't exist or can't be read, generate new one\n }\n\n // Generate new machine ID\n const machineId = generateMachineId()\n\n // Save to file\n try {\n const dir = path.dirname(machineIdPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n fs.writeFileSync(machineIdPath, machineId, 'utf-8')\n } catch (error) {\n console.error('Warning: Could not save machine ID to disk:', error)\n // Continue anyway - machine ID will be regenerated next time\n }\n\n return machineId\n}\n\n/**\n * EP731: Get hardware UUID for the current machine\n *\n * Uses platform-specific methods to get a hardware identifier that is:\n * - Unique per physical machine\n * - Stable across reboots\n * - Cannot sync between devices\n *\n * Falls back to random UUID if hardware UUID cannot be obtained.\n *\n * @returns Hardware UUID string\n */\nfunction getHardwareUUID(): string {\n try {\n if (process.platform === 'darwin') {\n // macOS: Get IOPlatformUUID from ioreg\n const output = execSync(\n 'ioreg -d2 -c IOPlatformExpertDevice | awk -F\\\\\" \\'/IOPlatformUUID/{print $(NF-1)}\\'',\n { encoding: 'utf-8', timeout: 5000 }\n ).trim()\n if (output && output.length > 0) {\n return output\n }\n } else if (process.platform === 'linux') {\n // Linux: Read /etc/machine-id\n if (fs.existsSync('/etc/machine-id')) {\n const machineId = fs.readFileSync('/etc/machine-id', 'utf-8').trim()\n if (machineId && machineId.length > 0) {\n return machineId\n }\n }\n // Fallback: Try /var/lib/dbus/machine-id\n if (fs.existsSync('/var/lib/dbus/machine-id')) {\n const dbusId = fs.readFileSync('/var/lib/dbus/machine-id', 'utf-8').trim()\n if (dbusId && dbusId.length > 0) {\n return dbusId\n }\n }\n } else if (process.platform === 'win32') {\n // Windows: Get UUID from wmic\n const output = execSync('wmic csproduct get uuid', {\n encoding: 'utf-8',\n timeout: 5000\n })\n const lines = output.trim().split('\\n')\n if (lines.length >= 2) {\n const uuid = lines[1].trim()\n if (uuid && uuid.length > 0 && uuid !== 'UUID') {\n return uuid\n }\n }\n }\n } catch (error) {\n // Hardware UUID retrieval failed, will fall back to random\n console.warn('Could not get hardware UUID, using random fallback:', error)\n }\n\n // Fallback: Generate random UUID\n return crypto.randomUUID()\n}\n\n/**\n * Generate a new machine ID\n *\n * EP731: Uses hardware UUID instead of random UUID to ensure uniqueness\n * per physical machine, even if ~/.episoda syncs between devices.\n *\n * Format: {hostname}-{8-char-hardware-uuid}\n * Example: \"Alans-MacBook-Pro-a3f2b1c4\"\n *\n * @returns Generated machine ID\n */\nfunction generateMachineId(): string {\n const hostname = os.hostname()\n const hwUUID = getHardwareUUID()\n // Take first 8 chars of hardware UUID (or hash it if it's a long UUID)\n const shortId = hwUUID.replace(/-/g, '').slice(0, 8).toLowerCase()\n return `${hostname}-${shortId}`\n}\n\n/**\n * Reset machine ID (force regeneration)\n *\n * Deletes the stored machine ID file, forcing a new ID to be generated\n * on next getMachineId() call.\n *\n * Use case: Developer explicitly wants to change their machine identity\n */\nexport function resetMachineId(): void {\n const machineIdPath = path.join(getConfigDir(), 'machine-id')\n try {\n if (fs.existsSync(machineIdPath)) {\n fs.unlinkSync(machineIdPath)\n }\n } catch (error) {\n throw new Error(`Failed to reset machine ID: ${error}`)\n }\n}\n","/**\n * EP548/EP612: Git Credential Helper Script Generator\n *\n * This module generates the git credential helper script that is installed\n * during `episoda auth`. The script is called by git when credentials are needed.\n *\n * The generated script:\n * 1. Detects the environment (local vs cloud)\n * 2. Calls GET /api/git/credentials with appropriate auth\n * 3. Returns credentials in git credential protocol format\n * 4. Caches tokens locally (5 min TTL) to avoid API calls on every git operation\n *\n * EP612: Removed jq dependency - uses pure bash JSON parsing\n */\n\n/**\n * Generate the credential helper script content\n *\n * The script needs to:\n * - Be standalone (no external dependencies - just bash and curl)\n * - Work with curl (available on all platforms)\n * - Handle both local (OAuth) and cloud (machine ID) auth\n * - Cache tokens to avoid hitting API on every git operation\n */\nexport function generateCredentialHelperScript(apiUrl: string): string {\n // The script uses bash because it's universally available\n // No jq dependency - uses pure bash for JSON parsing\n return `#!/bin/bash\n#\n# Episoda Git Credential Helper\n# EP548/EP612: Unified git authentication for all environments\n#\n# This script is called by git when credentials are needed.\n# It calls the Episoda API to get a fresh GitHub token.\n#\n# Installation: episoda auth\n# Location: ~/.episoda/bin/git-credential-episoda (local)\n# /usr/local/bin/git-credential-episoda (cloud VM)\n#\n# Git credential protocol:\n# - git calls: git-credential-episoda get\n# - input on stdin: protocol=https\\\\nhost=github.com\\\\n\n# - output on stdout: username=x-access-token\\\\npassword=TOKEN\\\\n\n#\n# Dependencies: bash, curl (no jq required)\n#\n\nset -euo pipefail\n\nEPISODA_DIR=\"\\${HOME}/.episoda\"\nCONFIG_FILE=\"\\${EPISODA_DIR}/config.json\"\nCACHE_FILE=\"\\${EPISODA_DIR}/git-token-cache.json\"\nAPI_URL=\"${apiUrl}\"\n\n# Cache TTL in seconds (5 minutes)\nCACHE_TTL=300\n\n# Log function (to stderr so git doesn't see it)\nlog() {\n if [[ \"\\${GIT_CREDENTIAL_EPISODA_DEBUG:-}\" == \"1\" ]]; then\n echo \"[episoda-git] \\$(date '+%H:%M:%S') \\$*\" >&2\n fi\n}\n\n# Error log (always shown)\nerror() {\n echo \"[episoda-git] ERROR: \\$*\" >&2\n}\n\n# Pure bash JSON value extraction (no jq needed)\n# Usage: json_get '{\"foo\":\"bar\"}' \"foo\" -> \"bar\"\n# Handles simple flat JSON and nested paths like \"credentials.username\"\njson_get() {\n local json=\"\\$1\"\n local key=\"\\$2\"\n local value=\"\"\n\n # Handle nested keys (e.g., \"credentials.username\")\n if [[ \"\\$key\" == *.* ]]; then\n local outer=\"\\${key%%.*}\"\n local inner=\"\\${key#*.}\"\n # Extract outer object first, then inner key\n # Match \"outer\":{...} and extract the {...} part\n local nested\n nested=\\$(echo \"\\$json\" | sed -n 's/.*\"'\\$outer'\"[[:space:]]*:[[:space:]]*{\\\\([^}]*\\\\)}.*/\\\\1/p')\n if [[ -n \"\\$nested\" ]]; then\n json_get \"{\\$nested}\" \"\\$inner\"\n return\n fi\n return\n fi\n\n # Simple key extraction: \"key\":\"value\" or \"key\": \"value\"\n # Handle both quoted strings and unquoted values\n value=\\$(echo \"\\$json\" | sed -n 's/.*\"'\\$key'\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/p')\n\n if [[ -n \"\\$value\" ]]; then\n echo \"\\$value\"\n fi\n}\n\n# Check for required dependencies\ncheck_dependencies() {\n if ! command -v curl >/dev/null 2>&1; then\n error \"curl is required but not installed\"\n return 1\n fi\n return 0\n}\n\n# Parse git credential input from stdin\nparse_input() {\n while IFS= read -r line; do\n [[ -z \"\\$line\" ]] && break\n case \"\\$line\" in\n protocol=*) PROTOCOL=\"\\${line#protocol=}\" ;;\n host=*) HOST=\"\\${line#host=}\" ;;\n path=*) PATH_=\"\\${line#path=}\" ;;\n esac\n done\n}\n\n# Parse ISO 8601 date to unix timestamp (cross-platform)\nparse_iso_date() {\n local iso_date=\"\\$1\"\n # Try GNU date first (Linux)\n if date -d \"\\$iso_date\" +%s 2>/dev/null; then\n return\n fi\n # Try BSD date (macOS) - strip timezone for parsing\n local clean_date=\"\\${iso_date%+*}\" # Remove +00:00\n clean_date=\"\\${clean_date%Z}\" # Remove Z\n clean_date=\"\\${clean_date%.*}\" # Remove .milliseconds\n if date -jf \"%Y-%m-%dT%H:%M:%S\" \"\\$clean_date\" +%s 2>/dev/null; then\n return\n fi\n # Fallback: return 0 (expired)\n echo \"0\"\n}\n\n# Check if cached token is still valid\nget_cached_token() {\n if [[ ! -f \"\\$CACHE_FILE\" ]]; then\n log \"No cache file\"\n return 1\n fi\n\n # Read cache file\n local cache_content\n cache_content=\\$(cat \"\\$CACHE_FILE\" 2>/dev/null) || return 1\n\n # Parse cache file using pure bash\n local expires_at\n expires_at=\\$(json_get \"\\$cache_content\" \"expires_at\")\n\n if [[ -z \"\\$expires_at\" ]]; then\n log \"No expires_at in cache\"\n return 1\n fi\n\n # Check if expired (with 60 second buffer)\n local now expires_ts buffer\n now=\\$(date +%s)\n expires_ts=\\$(parse_iso_date \"\\$expires_at\")\n buffer=60\n\n if [[ \\$((expires_ts - buffer)) -le \\$now ]]; then\n log \"Cache expired (expires: \\$expires_at)\"\n return 1\n fi\n\n # Return cached token\n CACHED_TOKEN=\\$(json_get \"\\$cache_content\" \"password\")\n CACHED_USER=\\$(json_get \"\\$cache_content\" \"username\")\n\n if [[ -n \"\\$CACHED_TOKEN\" && -n \"\\$CACHED_USER\" ]]; then\n log \"Using cached token (expires: \\$expires_at)\"\n return 0\n fi\n\n log \"Invalid cache content\"\n return 1\n}\n\n# Save token to cache\nsave_to_cache() {\n local username=\"\\$1\"\n local password=\"\\$2\"\n local expires_at=\"\\$3\"\n\n mkdir -p \"\\$EPISODA_DIR\"\n cat > \"\\$CACHE_FILE\" <<CACHE_EOF\n{\"username\":\"\\$username\",\"password\":\"\\$password\",\"expires_at\":\"\\$expires_at\",\"cached_at\":\"\\$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")\"}\nCACHE_EOF\n chmod 600 \"\\$CACHE_FILE\"\n log \"Token cached until \\$expires_at\"\n}\n\n# Get credentials from Episoda API\nfetch_credentials() {\n local api_url=\"\\${EPISODA_API_URL:-\\${API_URL}}\"\n local response=\"\"\n local http_code=\"\"\n\n # Detect environment - check multiple ways to identify a cloud VM\n local machine_id=\"\"\n\n # Check FLY_MACHINE_ID (set by Fly.io on all machines)\n if [[ -n \"\\${FLY_MACHINE_ID:-}\" ]]; then\n machine_id=\"\\$FLY_MACHINE_ID\"\n log \"Cloud environment detected via FLY_MACHINE_ID: \\$machine_id\"\n # Legacy: check /app/.machine_id file\n elif [[ -f \"/app/.machine_id\" ]]; then\n machine_id=\\$(cat /app/.machine_id)\n log \"Cloud environment detected via /app/.machine_id: \\$machine_id\"\n fi\n\n if [[ -n \"\\$machine_id\" ]]; then\n # Cloud VM: use machine ID header\n log \"Fetching credentials for machine: \\$machine_id\"\n response=\\$(curl -s -w \"\\\\n%{http_code}\" --max-time 10 \"\\${api_url}/api/git/credentials\" \\\\\n -H \"X-Machine-ID: \\$machine_id\" \\\\\n -H \"Content-Type: application/json\" 2>&1) || {\n error \"curl failed: \\$response\"\n return 1\n }\n else\n # Local: use OAuth token from config\n if [[ ! -f \"\\$CONFIG_FILE\" ]]; then\n error \"No config found at \\$CONFIG_FILE. Run 'episoda auth' first.\"\n return 1\n fi\n\n # Parse config using pure bash\n local config_content\n config_content=\\$(cat \"\\$CONFIG_FILE\" 2>/dev/null) || {\n error \"Cannot read config file\"\n return 1\n }\n\n local access_token project_id\n access_token=\\$(json_get \"\\$config_content\" \"access_token\")\n project_id=\\$(json_get \"\\$config_content\" \"project_id\")\n\n if [[ -z \"\\$access_token\" ]]; then\n error \"No access token in config. Run 'episoda auth' to authenticate.\"\n return 1\n fi\n\n log \"Local environment (project: \\$project_id)\"\n response=\\$(curl -s -w \"\\\\n%{http_code}\" --max-time 10 \"\\${api_url}/api/git/credentials\" \\\\\n -H \"Authorization: Bearer \\$access_token\" \\\\\n -H \"X-Project-ID: \\$project_id\" \\\\\n -H \"Content-Type: application/json\" 2>&1) || {\n error \"curl failed: \\$response\"\n return 1\n }\n fi\n\n # Split response and HTTP code\n http_code=\\$(echo \"\\$response\" | tail -n1)\n response=\\$(echo \"\\$response\" | sed '\\$d')\n\n # Check HTTP status\n if [[ \"\\$http_code\" != \"200\" ]]; then\n error \"API returned HTTP \\$http_code\"\n log \"Response: \\$response\"\n return 1\n fi\n\n # Parse response using pure bash\n CRED_USERNAME=\\$(json_get \"\\$response\" \"credentials.username\")\n CRED_PASSWORD=\\$(json_get \"\\$response\" \"credentials.password\")\n CRED_EXPIRES=\\$(json_get \"\\$response\" \"credentials.expires_at\")\n\n if [[ -z \"\\$CRED_USERNAME\" || -z \"\\$CRED_PASSWORD\" ]]; then\n error \"Invalid credentials in response\"\n log \"Response: \\$response\"\n return 1\n fi\n\n # Cache the token\n save_to_cache \"\\$CRED_USERNAME\" \"\\$CRED_PASSWORD\" \"\\$CRED_EXPIRES\"\n\n log \"Credentials fetched successfully\"\n return 0\n}\n\n# Main\nmain() {\n local command=\"\\${1:-}\"\n\n # Check dependencies before processing\n if ! check_dependencies; then\n exit 0 # Exit gracefully so git tries other helpers\n fi\n\n case \"\\$command\" in\n get)\n parse_input\n\n # Only handle github.com\n if [[ \"\\${HOST:-}\" != \"github.com\" ]]; then\n log \"Not handling host: \\${HOST:-unknown}\"\n exit 0\n fi\n\n # Try cache first\n if get_cached_token; then\n echo \"username=\\$CACHED_USER\"\n echo \"password=\\$CACHED_TOKEN\"\n exit 0\n fi\n\n # Fetch fresh credentials\n if fetch_credentials; then\n echo \"username=\\$CRED_USERNAME\"\n echo \"password=\\$CRED_PASSWORD\"\n exit 0\n fi\n\n # Failed - let git try other credential helpers\n log \"Failed to get credentials, falling back to other helpers\"\n exit 0\n ;;\n\n store|erase)\n # We don't store or erase credentials\n exit 0\n ;;\n\n *)\n # Unknown command\n exit 0\n ;;\n esac\n}\n\nmain \"\\$@\"\n`\n}\n\n/**\n * Get the content of the credential helper for embedding in the CLI\n */\nexport const CREDENTIAL_HELPER_SCRIPT = generateCredentialHelperScript('https://episoda.dev')\n","/**\n * `episoda status` command\n *\n * Shows the current CLI connection status and configuration details.\n * EP734: Simplified to only check local daemon state (removed server comparison).\n * EP738: Now uses IPC instead of HTTP (localhost:3002 server removed).\n * EP805: Added --verify flag for explicit connection health check.\n */\n\nimport { loadConfig, getConfigPath } from '@episoda/core'\nimport { getStatus, verifyHealth } from '../ipc/ipc-client'\nimport { status } from '../output'\n\nexport interface StatusOptions {\n verify?: boolean\n}\n\nexport async function statusCommand(options: StatusOptions = {}): Promise<void> {\n status.info('Checking CLI status...')\n status.info('')\n\n // Check if config exists\n const config = await loadConfig()\n\n if (!config) {\n status.error('✗ CLI not initialized')\n status.info('')\n status.info('Run the following to initialize:')\n status.info(' episoda init --project <project_id> --token <setup_token>')\n status.info('')\n status.info('Get a setup token from: Settings > Local Development')\n return\n }\n\n // Show config details\n status.success('✓ CLI initialized')\n status.info('')\n status.info('Configuration:')\n status.info(` Project ID: ${config.project_id}`)\n status.info(` API URL: ${config.api_url}`)\n status.info(` CLI Version: ${config.cli_version}`)\n status.info(` Config file: ${getConfigPath()}`)\n status.info('')\n\n if (!config.access_token || config.access_token === '') {\n status.warning('⚠ Not authenticated')\n status.info(' Run \"episoda auth\" to authenticate.')\n return\n }\n\n // EP738: Query daemon via IPC instead of HTTP\n let daemonStatus: Awaited<ReturnType<typeof getStatus>> | null = null\n try {\n daemonStatus = await getStatus()\n } catch {\n // Daemon not running or IPC failed\n }\n\n if (!daemonStatus) {\n status.warning('⚠ Daemon not running')\n status.info(' Run \"episoda dev\" to start the daemon.')\n return\n }\n\n // Check if any project is connected\n const connectedProject = daemonStatus.projects.find(p => p.connected)\n\n if (connectedProject) {\n status.success('✓ Connected to server')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info(` Project: ${connectedProject.name}`)\n } else if (daemonStatus.projects.length > 0) {\n status.warning('⚠ Daemon running but not connected')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info(' Run \"episoda dev\" in a project directory to connect.')\n } else {\n status.warning('⚠ Daemon running but no projects registered')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(' Run \"episoda dev\" in a project directory to connect.')\n }\n\n // EP805: Verify connection health if --verify flag is set\n if (options.verify) {\n status.info('')\n status.info('Connection Health Check:')\n\n try {\n const health = await verifyHealth()\n\n if (health.staleConnections > 0) {\n status.warning(` ⚠ ${health.staleConnections} stale connection(s) detected`)\n for (const project of health.projects) {\n if (project.inConnectionsMap && !project.inLiveConnections) {\n status.warning(` - ${project.name}: in Map but WebSocket dead`)\n }\n }\n status.info(' Stale connections will be cleaned up on next connect attempt.')\n } else if (health.healthyConnections > 0) {\n status.success(` ✓ All ${health.healthyConnections} connection(s) healthy`)\n } else {\n status.info(' No active connections to verify.')\n }\n } catch (error) {\n status.error(` ✗ Health check failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n}\n","/**\n * `episoda stop` command\n *\n * K722: Stop the Episoda daemon\n * - Sends shutdown signal to daemon via IPC\n * - Waits for daemon to stop gracefully\n * - Falls back to forceful shutdown if needed\n */\n\nimport { status } from '../output'\nimport { isDaemonRunning, stopDaemon } from '../daemon/daemon-manager'\nimport { shutdownDaemon, isDaemonReachable } from '../ipc/ipc-client'\n\nexport interface StopCommandOptions {\n force?: boolean // Force kill without graceful shutdown\n}\n\n/**\n * Execute the `episoda stop` command\n * @param options - Command options\n */\nexport async function stopCommand(options: StopCommandOptions = {}): Promise<void> {\n try {\n // Check if daemon is running\n const pid = isDaemonRunning()\n if (!pid) {\n status.info('Daemon is not running')\n return\n }\n\n status.info(`Stopping daemon (PID: ${pid})...`)\n\n if (options.force) {\n // Force kill\n const stopped = await stopDaemon(1000) // 1 second timeout before SIGKILL\n if (stopped) {\n status.success('Daemon force-stopped')\n } else {\n status.error('Failed to stop daemon')\n process.exit(1)\n }\n } else {\n // Graceful shutdown via IPC\n try {\n // Check if daemon is reachable\n const reachable = await isDaemonReachable()\n if (reachable) {\n // Send shutdown command\n await shutdownDaemon()\n status.success('Daemon stopped')\n } else {\n // Daemon not reachable, force stop\n status.warning('Daemon not responding, forcing shutdown...')\n const stopped = await stopDaemon(1000)\n if (stopped) {\n status.success('Daemon stopped')\n } else {\n status.error('Failed to stop daemon')\n process.exit(1)\n }\n }\n } catch (error) {\n // IPC failed, try direct stop\n status.warning('Failed to communicate with daemon, trying direct shutdown...')\n const stopped = await stopDaemon(5000)\n if (stopped) {\n status.success('Daemon stopped')\n } else {\n status.error(`Failed to stop daemon: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n }\n }\n } catch (error) {\n status.error(`Failed to stop daemon: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n}\n"],"mappings":"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","names":["exports","exports","status","fs","exports","exports","resolve","exports","exports","getConfigDir","getConfigPath","loadConfig","saveConfig","fs","path","os","exports","exports","import_core","import_core","chalk","fs","path","resolve","pidPath","path","import_core","resolve","import_child_process","path","net","resolve","resolve","os","fs","path","import_child_process","import_core","fs","path","crypto","import_child_process","import_core","machineId","hostname","resolve","platform","import_core"]}
1
+ {"version":3,"sources":["../../core/src/git-validator.ts","../../core/src/git-parser.ts","../../core/src/git-executor.ts","../../core/src/version.ts","../../core/src/websocket-client.ts","../../core/src/auth.ts","../../core/src/errors.ts","../../core/src/index.ts","../src/index.ts","../src/commands/dev.ts","../src/framework-detector.ts","../src/output.ts","../src/daemon/daemon-manager.ts","../src/ipc/ipc-client.ts","../src/utils/port-check.ts","../src/commands/auth.ts","../src/daemon/machine-id.ts","../src/git-helpers/git-credential-helper.ts","../src/commands/status.ts","../src/commands/stop.ts"],"sourcesContent":["/**\n * Git Input Validation Utilities\n *\n * Validates git command inputs to prevent injection and ensure safety.\n * Interface-agnostic: Returns boolean/error codes, not formatted messages.\n */\n\nimport { ErrorCode } from './command-protocol'\n\n/**\n * Validate branch name format\n * Git branch naming rules:\n * - Cannot start with - or /\n * - Cannot contain .. or @{ or \\\n * - Cannot end with .lock or /\n * - Cannot contain spaces or control characters\n * - Cannot be just @\n */\nexport function validateBranchName(branchName: string): { valid: boolean; error?: ErrorCode } {\n if (!branchName || branchName.trim().length === 0) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Check for invalid characters and patterns\n const invalidPatterns = [\n /^\\-/, // Starts with -\n /^\\//, // Starts with /\n /\\.\\./, // Contains ..\n /@\\{/, // Contains @{\n /\\\\/, // Contains backslash\n /\\.lock$/, // Ends with .lock\n /\\/$/, // Ends with /\n /\\s/, // Contains whitespace\n /[\\x00-\\x1F\\x7F]/, // Contains control characters\n /[\\*\\?\\[\\]~\\^:]/, // Contains special characters\n ]\n\n for (const pattern of invalidPatterns) {\n if (pattern.test(branchName)) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n }\n\n // Branch name cannot be just @\n if (branchName === '@') {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Maximum reasonable length (Git allows up to 255, but let's be conservative)\n if (branchName.length > 200) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n return { valid: true }\n}\n\n/**\n * Validate commit message\n * Basic validation to ensure message is not empty\n */\nexport function validateCommitMessage(message: string): { valid: boolean; error?: ErrorCode } {\n if (!message || message.trim().length === 0) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Reasonable length check\n if (message.length > 10000) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n return { valid: true }\n}\n\n/**\n * Validate file paths\n * Basic validation to prevent directory traversal\n */\nexport function validateFilePaths(files: string[]): { valid: boolean; error?: ErrorCode } {\n if (!files || files.length === 0) {\n return { valid: true }\n }\n\n for (const file of files) {\n // Check for null bytes (command injection)\n if (file.includes('\\0')) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Check for control characters\n if (/[\\x00-\\x1F\\x7F]/.test(file)) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n\n // Files should not be empty\n if (file.trim().length === 0) {\n return { valid: false, error: 'UNKNOWN_ERROR' }\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Sanitize command arguments to prevent injection\n * Returns sanitized array of arguments\n */\nexport function sanitizeArgs(args: string[]): string[] {\n return args.map(arg => {\n // Remove null bytes\n return arg.replace(/\\0/g, '')\n })\n}\n","/**\n * Git Command Output Parser\n *\n * Parses git command output and error messages into structured data.\n * Interface-agnostic: Returns structured objects, not formatted strings.\n */\n\nimport { ErrorCode } from './command-protocol'\n\n/**\n * Parse git status output to extract uncommitted files\n */\nexport function parseGitStatus(output: string): {\n uncommittedFiles: string[]\n isClean: boolean\n currentBranch?: string\n} {\n const lines = output.split('\\n')\n const uncommittedFiles: string[] = []\n let currentBranch: string | undefined\n\n for (const line of lines) {\n // Porcelain format branch line: ## branch-name or ## HEAD (no branch)\n if (line.startsWith('## ')) {\n const branchInfo = line.substring(3)\n // Extract branch name (handle \"branch...origin/branch\" format)\n const branchMatch = branchInfo.match(/^([^\\s.]+)/)\n if (branchMatch && branchMatch[1]) {\n currentBranch = branchMatch[1] === 'HEAD' ? 'HEAD (detached)' : branchMatch[1]\n }\n continue\n }\n\n // Parse modified/added/deleted files\n // Porcelain format: \"XY filename\" where X is staged, Y is unstaged\n // Format: \" M file.txt\" or \"M file.txt\" or \"?? file.txt\" or \"MM file.txt\"\n if (line.length >= 3) {\n const status = line.substring(0, 2)\n const filePath = line.substring(3).trim()\n\n // Check if there's any status (not all spaces)\n if (status.trim().length > 0 && filePath.length > 0) {\n uncommittedFiles.push(filePath)\n }\n }\n }\n\n const isClean = uncommittedFiles.length === 0\n\n return { uncommittedFiles, isClean, currentBranch }\n}\n\n/**\n * Parse merge conflict output to extract conflicting files\n */\nexport function parseMergeConflicts(output: string): string[] {\n const lines = output.split('\\n')\n const conflictingFiles: string[] = []\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Look for conflict markers\n if (trimmed.startsWith('CONFLICT')) {\n // Format: \"CONFLICT (content): Merge conflict in file.txt\"\n const match = trimmed.match(/CONFLICT.*in (.+)$/)\n if (match && match[1]) {\n conflictingFiles.push(match[1])\n }\n }\n\n // Also check for \"both modified\" status\n if (trimmed.startsWith('UU ')) {\n conflictingFiles.push(trimmed.substring(3).trim())\n }\n }\n\n return conflictingFiles\n}\n\n/**\n * Map git error output to ErrorCode\n */\nexport function parseGitError(stderr: string, stdout: string, exitCode: number): ErrorCode {\n const combinedOutput = `${stderr}\\n${stdout}`.toLowerCase()\n\n // Check for git not installed\n if (combinedOutput.includes('git: command not found') ||\n combinedOutput.includes(\"'git' is not recognized\")) {\n return 'GIT_NOT_INSTALLED'\n }\n\n // Check for not a git repository\n if (combinedOutput.includes('not a git repository') ||\n combinedOutput.includes('not a git repo')) {\n return 'NOT_GIT_REPO'\n }\n\n // Check for merge conflicts\n if (combinedOutput.includes('conflict') ||\n combinedOutput.includes('merge conflict') ||\n exitCode === 1 && combinedOutput.includes('automatic merge failed')) {\n return 'MERGE_CONFLICT'\n }\n\n // Check for uncommitted changes\n if (combinedOutput.includes('please commit your changes') ||\n combinedOutput.includes('would be overwritten') ||\n combinedOutput.includes('cannot checkout') && combinedOutput.includes('files would be overwritten')) {\n return 'UNCOMMITTED_CHANGES'\n }\n\n // Check for authentication failures\n if (combinedOutput.includes('authentication failed') ||\n combinedOutput.includes('could not read username') ||\n combinedOutput.includes('permission denied') ||\n combinedOutput.includes('could not read password') ||\n combinedOutput.includes('fatal: authentication failed')) {\n return 'AUTH_FAILURE'\n }\n\n // Check for network errors\n if (combinedOutput.includes('could not resolve host') ||\n combinedOutput.includes('failed to connect') ||\n combinedOutput.includes('network is unreachable') ||\n combinedOutput.includes('connection timed out') ||\n combinedOutput.includes('could not read from remote')) {\n return 'NETWORK_ERROR'\n }\n\n // Check for branch not found\n if (combinedOutput.includes('did not match any file') ||\n combinedOutput.includes('branch') && combinedOutput.includes('not found') ||\n combinedOutput.includes('pathspec') && combinedOutput.includes('did not match')) {\n return 'BRANCH_NOT_FOUND'\n }\n\n // Check for branch already exists\n if (combinedOutput.includes('already exists') ||\n combinedOutput.includes('a branch named') && combinedOutput.includes('already exists')) {\n return 'BRANCH_ALREADY_EXISTS'\n }\n\n // Check for push rejected\n if (combinedOutput.includes('push rejected') ||\n combinedOutput.includes('failed to push') ||\n combinedOutput.includes('rejected') && combinedOutput.includes('non-fast-forward') ||\n combinedOutput.includes('updates were rejected')) {\n return 'PUSH_REJECTED'\n }\n\n // Check for timeout (exit code 124 is GNU timeout, 143 is SIGTERM)\n if (exitCode === 124 || exitCode === 143) {\n return 'COMMAND_TIMEOUT'\n }\n\n // Default to unknown error\n return 'UNKNOWN_ERROR'\n}\n\n/**\n * Extract branch name from git output\n */\nexport function extractBranchName(output: string): string | undefined {\n const lines = output.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Check for \"Switched to branch 'name'\"\n let match = trimmed.match(/Switched to (?:a new )?branch '(.+)'/)\n if (match && match[1]) {\n return match[1]\n }\n\n // Check for \"On branch name\"\n match = trimmed.match(/On branch (.+)/)\n if (match && match[1]) {\n return match[1]\n }\n }\n\n return undefined\n}\n\n/**\n * Check if output indicates detached HEAD state\n */\nexport function isDetachedHead(output: string): boolean {\n return output.toLowerCase().includes('head detached') ||\n output.toLowerCase().includes('you are in \\'detached head\\' state')\n}\n\n/**\n * Parse remote tracking information\n */\nexport function parseRemoteTracking(output: string): {\n hasUpstream: boolean\n remoteBranch?: string\n remote?: string\n} {\n const lines = output.split('\\n')\n\n for (const line of lines) {\n const trimmed = line.trim()\n\n // Check for \"Branch 'name' set up to track remote branch 'remote/branch'\"\n const match = trimmed.match(/set up to track remote branch '(.+?)'(?: from '(.+?)')?/)\n if (match) {\n return {\n hasUpstream: true,\n remoteBranch: match[1],\n remote: match[2] || 'origin'\n }\n }\n\n // Check for existing upstream: \"Your branch is up to date with 'origin/main'\"\n const upstreamMatch = trimmed.match(/(?:up to date with|ahead of|behind) '(.+?)\\/(.+?)'/)\n if (upstreamMatch) {\n return {\n hasUpstream: true,\n remote: upstreamMatch[1],\n remoteBranch: upstreamMatch[2]\n }\n }\n }\n\n return { hasUpstream: false }\n}\n","/**\n * Git Command Executor\n *\n * Executes git commands with error handling and returns structured results.\n * Interface-agnostic: Returns structured data, not formatted strings.\n */\n\nimport { exec } from 'child_process'\nimport { promisify } from 'util'\nimport { GitCommand, ExecutionResult, ExecutionOptions, ErrorCode } from './command-protocol'\nimport {\n validateBranchName,\n validateCommitMessage,\n validateFilePaths,\n sanitizeArgs\n} from './git-validator'\nimport {\n parseGitError,\n parseGitStatus,\n parseMergeConflicts,\n extractBranchName,\n isDetachedHead,\n parseRemoteTracking\n} from './git-parser'\n\nconst execAsync = promisify(exec)\n\n/**\n * Executes git commands with error handling\n *\n * DESIGN PRINCIPLES:\n * - Interface-agnostic: Returns structured data, not formatted strings\n * - No console.log: Let the interface handle output\n * - Error codes: Return error codes, not messages\n * - Reusable: Can be used by CLI, MCP, or any other interface\n */\nexport class GitExecutor {\n /**\n * Execute a git command\n * @param command - The git command to execute\n * @param options - Execution options (timeout, cwd, etc.)\n * @returns Structured result with success/error details\n */\n async execute(\n command: GitCommand,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Validate git is installed\n const gitInstalled = await this.validateGitInstalled()\n if (!gitInstalled) {\n return {\n success: false,\n error: 'GIT_NOT_INSTALLED'\n }\n }\n\n // Determine working directory\n const cwd = options?.cwd || process.cwd()\n\n // Validate this is a git repository (except for init-like commands)\n const isGitRepo = await this.isGitRepository(cwd)\n if (!isGitRepo) {\n return {\n success: false,\n error: 'NOT_GIT_REPO'\n }\n }\n\n // Route to appropriate handler based on action\n switch (command.action) {\n case 'checkout':\n return await this.executeCheckout(command, cwd, options)\n case 'create_branch':\n return await this.executeCreateBranch(command, cwd, options)\n case 'commit':\n return await this.executeCommit(command, cwd, options)\n case 'push':\n return await this.executePush(command, cwd, options)\n case 'status':\n return await this.executeStatus(cwd, options)\n case 'pull':\n return await this.executePull(command, cwd, options)\n case 'delete_branch':\n return await this.executeDeleteBranch(command, cwd, options)\n // EP597: Read operations for production local dev mode\n case 'branch_exists':\n return await this.executeBranchExists(command, cwd, options)\n case 'branch_has_commits':\n return await this.executeBranchHasCommits(command, cwd, options)\n // EP598: Main branch check for production\n case 'main_branch_check':\n return await this.executeMainBranchCheck(cwd, options)\n // EP599: Get commits for branch\n case 'get_commits':\n return await this.executeGetCommits(command, cwd, options)\n // EP599: Advanced operations for move-to-module and discard-main-changes\n case 'stash':\n return await this.executeStash(command, cwd, options)\n case 'reset':\n return await this.executeReset(command, cwd, options)\n case 'merge':\n return await this.executeMerge(command, cwd, options)\n case 'cherry_pick':\n return await this.executeCherryPick(command, cwd, options)\n case 'clean':\n return await this.executeClean(command, cwd, options)\n case 'add':\n return await this.executeAdd(command, cwd, options)\n case 'fetch':\n return await this.executeFetch(command, cwd, options)\n // EP599-3: Composite operations\n case 'move_to_module':\n return await this.executeMoveToModule(command, cwd, options)\n case 'discard_main_changes':\n return await this.executeDiscardMainChanges(cwd, options)\n // EP523: Branch sync operations\n case 'sync_status':\n return await this.executeSyncStatus(command, cwd, options)\n case 'sync_main':\n return await this.executeSyncMain(cwd, options)\n case 'rebase_branch':\n return await this.executeRebaseBranch(command, cwd, options)\n case 'rebase_abort':\n return await this.executeRebaseAbort(cwd, options)\n case 'rebase_continue':\n return await this.executeRebaseContinue(cwd, options)\n case 'rebase_status':\n return await this.executeRebaseStatus(cwd, options)\n default:\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: 'Unknown command action'\n }\n }\n } catch (error) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error instanceof Error ? error.message : 'Unknown error occurred'\n }\n }\n }\n\n /**\n * Execute checkout command\n */\n private async executeCheckout(\n command: { branch: string; create?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Check for uncommitted changes first\n const statusResult = await this.executeStatus(cwd, options)\n if (statusResult.success && statusResult.details?.uncommittedFiles?.length) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n details: {\n uncommittedFiles: statusResult.details.uncommittedFiles\n }\n }\n }\n\n // Build command\n const args = ['checkout']\n if (command.create) {\n args.push('-b')\n }\n args.push(command.branch)\n\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * Execute create_branch command\n */\n private async executeCreateBranch(\n command: { branch: string; from?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Validate source branch if provided\n if (command.from) {\n const fromValidation = validateBranchName(command.from)\n if (!fromValidation.valid) {\n return {\n success: false,\n error: fromValidation.error || 'UNKNOWN_ERROR'\n }\n }\n }\n\n // Build command - use checkout -b to create AND checkout the branch\n // This ensures the user is on the new branch immediately\n const args = ['checkout', '-b', command.branch]\n if (command.from) {\n args.push(command.from)\n }\n\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * Execute commit command\n */\n private async executeCommit(\n command: { message: string; files?: string[] },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate commit message\n const validation = validateCommitMessage(command.message)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Validate file paths if provided\n if (command.files) {\n const fileValidation = validateFilePaths(command.files)\n if (!fileValidation.valid) {\n return {\n success: false,\n error: fileValidation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Stage specific files first\n for (const file of command.files) {\n const addResult = await this.runGitCommand(['add', file], cwd, options)\n if (!addResult.success) {\n return addResult\n }\n }\n } else {\n // Stage all changes\n const addResult = await this.runGitCommand(['add', '-A'], cwd, options)\n if (!addResult.success) {\n return addResult\n }\n }\n\n // Execute commit\n const args = ['commit', '-m', command.message]\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * Execute push command\n * EP769: Added force parameter for pushing rebased branches\n */\n private async executePush(\n command: { branch: string; setUpstream?: boolean; force?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Build command\n const args = ['push']\n // EP769: Add --force flag for rebased branches\n if (command.force) {\n args.push('--force')\n }\n if (command.setUpstream) {\n args.push('-u', 'origin', command.branch)\n } else {\n args.push('origin', command.branch)\n }\n\n // Configure git credential helper for GitHub token if provided\n const env = { ...process.env }\n if (options?.githubToken) {\n env.GIT_ASKPASS = 'echo'\n env.GIT_USERNAME = 'x-access-token'\n env.GIT_PASSWORD = options.githubToken\n }\n\n return await this.runGitCommand(args, cwd, { ...options, env })\n }\n\n /**\n * Execute status command\n */\n private async executeStatus(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n const result = await this.runGitCommand(['status', '--porcelain', '-b'], cwd, options)\n\n if (result.success && result.output) {\n const statusInfo = parseGitStatus(result.output)\n return {\n success: true,\n output: result.output,\n details: {\n uncommittedFiles: statusInfo.uncommittedFiles,\n branchName: statusInfo.currentBranch\n }\n }\n }\n\n return result\n }\n\n /**\n * Execute pull command\n */\n private async executePull(\n command: { branch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name if provided\n if (command.branch) {\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n }\n\n // Check for uncommitted changes first\n const statusResult = await this.executeStatus(cwd, options)\n if (statusResult.success && statusResult.details?.uncommittedFiles?.length) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n details: {\n uncommittedFiles: statusResult.details.uncommittedFiles\n }\n }\n }\n\n // Build command\n const args = ['pull']\n if (command.branch) {\n args.push('origin', command.branch)\n }\n\n const result = await this.runGitCommand(args, cwd, options)\n\n // Check for merge conflicts\n if (!result.success && result.output) {\n const conflicts = parseMergeConflicts(result.output)\n if (conflicts.length > 0) {\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: result.output,\n details: {\n conflictingFiles: conflicts\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Execute delete_branch command\n */\n private async executeDeleteBranch(\n command: { branch: string; force?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Build command\n const args = ['branch']\n args.push(command.force ? '-D' : '-d')\n args.push(command.branch)\n\n return await this.runGitCommand(args, cwd, options)\n }\n\n /**\n * EP597: Execute branch_exists command\n * Checks if a branch exists locally and/or remotely\n */\n private async executeBranchExists(\n command: { branch: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n try {\n let isLocal = false\n let isRemote = false\n\n // Check local branches\n try {\n const { stdout: localBranches } = await execAsync('git branch --list', { cwd, timeout: options?.timeout || 10000 })\n isLocal = localBranches.split('\\n').some(line => {\n const branchName = line.replace(/^\\*?\\s*/, '').trim()\n return branchName === command.branch\n })\n } catch {\n // Ignore errors - branch doesn't exist locally\n }\n\n // Check remote branches\n try {\n const { stdout: remoteBranches } = await execAsync(\n `git ls-remote --heads origin ${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n isRemote = remoteBranches.trim().length > 0\n } catch {\n // Ignore errors - can't check remote (might be network issue)\n }\n\n const branchExists = isLocal || isRemote\n\n return {\n success: true,\n output: branchExists ? `Branch ${command.branch} exists` : `Branch ${command.branch} does not exist`,\n details: {\n branchName: command.branch,\n branchExists,\n isLocal,\n isRemote\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check branch existence'\n }\n }\n }\n\n /**\n * EP597: Execute branch_has_commits command\n * Checks if a branch has commits ahead of the base branch (default: main)\n */\n private async executeBranchHasCommits(\n command: { branch: string; baseBranch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n const baseBranch = command.baseBranch || 'main'\n\n try {\n // Use git cherry to find commits unique to the branch\n // This shows commits on branch that aren't on base\n const { stdout } = await execAsync(\n `git cherry origin/${baseBranch} ${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n\n // git cherry shows lines starting with + for unique commits\n const uniqueCommits = stdout.trim().split('\\n').filter(line => line.startsWith('+'))\n const hasCommits = uniqueCommits.length > 0\n\n return {\n success: true,\n output: hasCommits\n ? `Branch ${command.branch} has ${uniqueCommits.length} commits ahead of ${baseBranch}`\n : `Branch ${command.branch} has no commits ahead of ${baseBranch}`,\n details: {\n branchName: command.branch,\n hasCommits\n }\n }\n } catch (error: any) {\n // If git cherry fails (branch not found, etc.), try alternative method\n try {\n // Alternative: count commits with rev-list\n const { stdout } = await execAsync(\n `git rev-list --count origin/${baseBranch}..${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n\n const commitCount = parseInt(stdout.trim(), 10)\n const hasCommits = commitCount > 0\n\n return {\n success: true,\n output: hasCommits\n ? `Branch ${command.branch} has ${commitCount} commits ahead of ${baseBranch}`\n : `Branch ${command.branch} has no commits ahead of ${baseBranch}`,\n details: {\n branchName: command.branch,\n hasCommits\n }\n }\n } catch {\n // Both methods failed - branch likely doesn't exist or isn't tracked\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: error.message || `Failed to check commits for branch ${command.branch}`\n }\n }\n }\n }\n\n /**\n * EP598: Execute main branch check - returns current branch, uncommitted files, and unpushed commits\n */\n private async executeMainBranchCheck(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Get current branch\n let currentBranch = ''\n try {\n const { stdout } = await execAsync('git branch --show-current', { cwd, timeout: options?.timeout || 10000 })\n currentBranch = stdout.trim()\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to get current branch'\n }\n }\n\n // Get uncommitted files\n let uncommittedFiles: string[] = []\n try {\n const { stdout } = await execAsync('git status --porcelain', { cwd, timeout: options?.timeout || 10000 })\n if (stdout) {\n uncommittedFiles = stdout.split('\\n').filter(line => line.trim()).map(line => {\n const parts = line.trim().split(/\\s+/)\n return parts.slice(1).join(' ')\n })\n }\n } catch {\n // Ignore errors - just means no uncommitted changes\n }\n\n // Get unpushed commits (only if on main branch)\n let localCommits: Array<{ sha: string; message: string; author: string }> = []\n if (currentBranch === 'main') {\n try {\n const { stdout } = await execAsync('git log origin/main..HEAD --format=\"%H|%s|%an\"', { cwd, timeout: options?.timeout || 10000 })\n if (stdout) {\n localCommits = stdout.split('\\n').filter(line => line.trim()).map(line => {\n const [sha, message, author] = line.split('|')\n return {\n sha: sha ? sha.substring(0, 8) : '',\n message: message || '',\n author: author || ''\n }\n })\n }\n } catch {\n // Ignore errors - might not have origin/main or no remote\n }\n }\n\n return {\n success: true,\n output: `Branch: ${currentBranch}, Uncommitted: ${uncommittedFiles.length}, Unpushed: ${localCommits.length}`,\n details: {\n currentBranch,\n uncommittedFiles,\n localCommits\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check main branch'\n }\n }\n }\n\n /**\n * EP599: Execute get_commits command\n * Returns commits for a branch with pushed/unpushed status\n */\n private async executeGetCommits(\n command: { branch: string; limit?: number; baseBranch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n const limit = command.limit || 10\n const baseBranch = command.baseBranch || 'main'\n\n try {\n // Get commits unique to this branch (not in main)\n let stdout: string\n try {\n const result = await execAsync(\n `git log ${baseBranch}..\"${command.branch}\" --pretty=format:\"%H|%an|%ae|%aI|%s\" -n ${limit} --`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n stdout = result.stdout\n } catch (error) {\n // Fallback: if comparison fails, show all commits on this branch\n try {\n const result = await execAsync(\n `git log \"${command.branch}\" --pretty=format:\"%H|%an|%ae|%aI|%s\" -n ${limit} --`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n stdout = result.stdout\n } catch (branchError) {\n // Branch doesn't exist locally\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: `Branch ${command.branch} not found locally`\n }\n }\n }\n\n if (!stdout.trim()) {\n return {\n success: true,\n output: 'No commits found',\n details: {\n commits: []\n }\n }\n }\n\n // Parse commits\n const commitLines = stdout.trim().split('\\n')\n\n // Check which commits have been pushed to remote\n let remoteShas: Set<string> = new Set()\n try {\n const { stdout: remoteCommits } = await execAsync(\n `git log \"origin/${command.branch}\" --pretty=format:\"%H\" -n ${limit} --`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n remoteShas = new Set(remoteCommits.trim().split('\\n').filter(Boolean))\n } catch {\n // Remote branch doesn't exist - all commits are local/unpushed\n }\n\n const commits = commitLines.map((line) => {\n const [sha, authorName, authorEmail, date, ...messageParts] = line.split('|')\n const message = messageParts.join('|') // Handle pipes in commit messages\n const isPushed = remoteShas.has(sha)\n\n return {\n sha,\n message,\n authorName,\n authorEmail,\n date,\n isPushed\n }\n })\n\n return {\n success: true,\n output: `Found ${commits.length} commits`,\n details: {\n commits\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to get commits'\n }\n }\n }\n\n // ========================================\n // EP599: Advanced operations for move-to-module and discard-main-changes\n // ========================================\n\n /**\n * Execute git stash operations\n */\n private async executeStash(\n command: { operation: 'push' | 'pop' | 'drop' | 'list'; message?: string; includeUntracked?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args: string[] = ['stash']\n\n switch (command.operation) {\n case 'push':\n args.push('push')\n if (command.includeUntracked) {\n args.push('--include-untracked')\n }\n if (command.message) {\n args.push('-m', command.message)\n }\n break\n case 'pop':\n args.push('pop')\n break\n case 'drop':\n args.push('drop')\n break\n case 'list':\n args.push('list')\n break\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Stash operation failed'\n }\n }\n }\n\n /**\n * Execute git reset\n */\n private async executeReset(\n command: { mode: 'soft' | 'mixed' | 'hard'; target?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['reset', `--${command.mode}`]\n if (command.target) {\n args.push(command.target)\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Reset failed'\n }\n }\n }\n\n /**\n * Execute git merge\n */\n private async executeMerge(\n command: { branch: string; strategy?: 'ours' | 'theirs'; noEdit?: boolean; abort?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n if (command.abort) {\n return await this.runGitCommand(['merge', '--abort'], cwd, options)\n }\n\n // Validate branch name\n const branchValidation = validateBranchName(command.branch)\n if (!branchValidation.valid) {\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: branchValidation.error || 'Invalid branch name'\n }\n }\n\n const args = ['merge', command.branch]\n\n if (command.strategy === 'ours') {\n args.push('--strategy=ours')\n } else if (command.strategy === 'theirs') {\n args.push('--strategy-option=theirs')\n }\n\n if (command.noEdit) {\n args.push('--no-edit')\n }\n\n const result = await this.runGitCommand(args, cwd, options)\n\n // Check for merge conflicts\n if (!result.success && result.output?.includes('CONFLICT')) {\n result.details = result.details || {}\n result.details.mergeConflicts = true\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: error.message || 'Merge failed'\n }\n }\n }\n\n /**\n * Execute git cherry-pick\n */\n private async executeCherryPick(\n command: { sha: string; abort?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n if (command.abort) {\n return await this.runGitCommand(['cherry-pick', '--abort'], cwd, options)\n }\n\n const result = await this.runGitCommand(['cherry-pick', command.sha], cwd, options)\n\n // Check for cherry-pick conflicts\n if (!result.success && result.output?.includes('CONFLICT')) {\n result.details = result.details || {}\n result.details.cherryPickConflicts = true\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: error.message || 'Cherry-pick failed'\n }\n }\n }\n\n /**\n * Execute git clean\n */\n private async executeClean(\n command: { force?: boolean; directories?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['clean']\n\n if (command.force) {\n args.push('-f')\n }\n if (command.directories) {\n args.push('-d')\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Clean failed'\n }\n }\n }\n\n /**\n * Execute git add\n */\n private async executeAdd(\n command: { files?: string[]; all?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['add']\n\n if (command.all) {\n args.push('-A')\n } else if (command.files && command.files.length > 0) {\n // Validate file paths\n const validation = validateFilePaths(command.files)\n if (!validation.valid) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: validation.error || 'Invalid file paths'\n }\n }\n args.push(...command.files)\n } else {\n args.push('-A') // Default to all\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Add failed'\n }\n }\n }\n\n /**\n * Execute git fetch\n */\n private async executeFetch(\n command: { remote?: string; branch?: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const args = ['fetch']\n\n if (command.remote) {\n args.push(command.remote)\n if (command.branch) {\n args.push(command.branch)\n }\n } else {\n args.push('origin')\n }\n\n return await this.runGitCommand(args, cwd, options)\n } catch (error: any) {\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: error.message || 'Fetch failed'\n }\n }\n }\n\n // ========================================\n // EP599-3: Composite operations\n // ========================================\n\n /**\n * Execute move_to_module - composite operation\n * Moves commits/changes to a module branch with conflict handling\n */\n private async executeMoveToModule(\n command: { targetBranch: string; commitShas?: string[]; conflictResolution?: 'ours' | 'theirs' },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n const { targetBranch, commitShas, conflictResolution } = command\n let hasStash = false\n const cherryPickedCommits: string[] = []\n\n try {\n // Step 1: Get current branch\n const { stdout: currentBranchOut } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd })\n const currentBranch = currentBranchOut.trim()\n\n // Step 2: Stash uncommitted changes\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd })\n if (statusOutput.trim()) {\n try {\n await execAsync('git add -A', { cwd })\n const { stdout: stashHash } = await execAsync('git stash create -m \"episoda-move-to-module\"', { cwd })\n if (stashHash && stashHash.trim()) {\n await execAsync(`git stash store -m \"episoda-move-to-module\" ${stashHash.trim()}`, { cwd })\n await execAsync('git reset --hard HEAD', { cwd })\n hasStash = true\n }\n } catch (stashError: any) {\n // Continue without stashing\n }\n }\n\n // Step 3: Switch to main if we have commits to move\n if (commitShas && commitShas.length > 0 && currentBranch !== 'main' && currentBranch !== 'master') {\n await execAsync('git checkout main', { cwd })\n }\n\n // Step 4: Create or checkout target branch\n let branchExists = false\n try {\n await execAsync(`git rev-parse --verify ${targetBranch}`, { cwd })\n branchExists = true\n } catch {\n branchExists = false\n }\n\n if (!branchExists) {\n await execAsync(`git checkout -b ${targetBranch}`, { cwd })\n } else {\n await execAsync(`git checkout ${targetBranch}`, { cwd })\n\n // Try to merge changes from main\n if (currentBranch === 'main' || currentBranch === 'master') {\n try {\n const mergeStrategy = conflictResolution === 'ours' ? '--strategy=ours' :\n conflictResolution === 'theirs' ? '--strategy-option=theirs' : ''\n await execAsync(`git merge ${currentBranch} ${mergeStrategy} --no-edit`, { cwd })\n } catch (mergeError: any) {\n // Check for conflicts\n const { stdout: conflictStatus } = await execAsync('git status --porcelain', { cwd })\n if (conflictStatus.includes('UU ') || conflictStatus.includes('AA ') || conflictStatus.includes('DD ')) {\n const { stdout: conflictFiles } = await execAsync('git diff --name-only --diff-filter=U', { cwd })\n const conflictedFiles = conflictFiles.trim().split('\\n').filter(Boolean)\n\n if (conflictResolution) {\n // Auto-resolve conflicts\n for (const file of conflictedFiles) {\n await execAsync(`git checkout --${conflictResolution} \"${file}\"`, { cwd })\n await execAsync(`git add \"${file}\"`, { cwd })\n }\n await execAsync('git commit --no-edit', { cwd })\n } else {\n // Abort merge and return conflict error\n await execAsync('git merge --abort', { cwd })\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: 'Merge conflicts detected',\n details: {\n hasConflicts: true,\n conflictedFiles,\n movedToBranch: targetBranch\n }\n }\n }\n }\n }\n }\n }\n\n // Step 5: Cherry-pick commits if provided\n if (commitShas && commitShas.length > 0 && (currentBranch === 'main' || currentBranch === 'master')) {\n for (const sha of commitShas) {\n try {\n // Check if commit already exists in branch\n const { stdout: logOutput } = await execAsync(\n `git log --format=%H ${targetBranch} | grep ${sha}`,\n { cwd }\n ).catch(() => ({ stdout: '' }))\n\n if (!logOutput.trim()) {\n await execAsync(`git cherry-pick ${sha}`, { cwd })\n cherryPickedCommits.push(sha)\n }\n } catch (err: any) {\n await execAsync('git cherry-pick --abort', { cwd }).catch(() => {})\n }\n }\n\n // Reset main to origin/main\n await execAsync('git checkout main', { cwd })\n await execAsync('git reset --hard origin/main', { cwd })\n await execAsync(`git checkout ${targetBranch}`, { cwd })\n }\n\n // Step 6: Apply stashed changes\n if (hasStash) {\n try {\n await execAsync('git stash pop', { cwd })\n } catch (stashError: any) {\n // Check for stash conflicts\n const { stdout: conflictStatus } = await execAsync('git status --porcelain', { cwd })\n if (conflictStatus.includes('UU ') || conflictStatus.includes('AA ')) {\n if (conflictResolution) {\n const { stdout: conflictFiles } = await execAsync('git diff --name-only --diff-filter=U', { cwd })\n const conflictedFiles = conflictFiles.trim().split('\\n').filter(Boolean)\n for (const file of conflictedFiles) {\n await execAsync(`git checkout --${conflictResolution} \"${file}\"`, { cwd })\n await execAsync(`git add \"${file}\"`, { cwd })\n }\n }\n }\n }\n }\n\n return {\n success: true,\n output: `Successfully moved to branch ${targetBranch}`,\n details: {\n movedToBranch: targetBranch,\n cherryPickedCommits,\n currentBranch: targetBranch\n }\n }\n } catch (error: any) {\n // Try to restore stash if something went wrong\n if (hasStash) {\n try {\n const { stdout: stashList } = await execAsync('git stash list', { cwd })\n if (stashList.includes('episoda-move-to-module')) {\n await execAsync('git stash pop', { cwd })\n }\n } catch (e) {\n // Ignore errors restoring stash\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Move to module failed'\n }\n }\n }\n\n /**\n * Execute discard_main_changes - composite operation\n * Discards all uncommitted files and local commits on main branch\n */\n private async executeDiscardMainChanges(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Step 1: Verify we're on main/master\n const { stdout: currentBranchOut } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd })\n const branch = currentBranchOut.trim()\n\n if (branch !== 'main' && branch !== 'master') {\n return {\n success: false,\n error: 'BRANCH_NOT_FOUND',\n output: `Cannot discard changes - not on main branch. Current branch: ${branch}`\n }\n }\n\n let discardedFiles = 0\n\n // Step 2: Stash uncommitted changes (will be dropped)\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd })\n if (statusOutput.trim()) {\n try {\n await execAsync('git stash --include-untracked', { cwd })\n discardedFiles = statusOutput.trim().split('\\n').length\n } catch (stashError: any) {\n // Continue - might be nothing to stash\n }\n }\n\n // Step 3: Fetch and reset to origin\n await execAsync('git fetch origin', { cwd })\n await execAsync(`git reset --hard origin/${branch}`, { cwd })\n\n // Step 4: Clean untracked files\n try {\n await execAsync('git clean -fd', { cwd })\n } catch (cleanError: any) {\n // Non-critical\n }\n\n // Step 5: Drop the stash\n try {\n await execAsync('git stash drop', { cwd })\n } catch (dropError: any) {\n // Stash might not exist\n }\n\n return {\n success: true,\n output: `Successfully discarded all changes and reset to origin/${branch}`,\n details: {\n currentBranch: branch,\n discardedFiles\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Discard main changes failed'\n }\n }\n }\n\n // ========================================\n // EP523: Branch sync operations\n // ========================================\n\n /**\n * EP523: Get sync status of a branch relative to main\n * Returns how many commits behind/ahead the branch is\n */\n private async executeSyncStatus(\n command: { branch: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Fetch latest from remote to get accurate counts\n try {\n await execAsync('git fetch origin', { cwd, timeout: options?.timeout || 30000 })\n } catch (fetchError: any) {\n // Network error - return what we can determine locally\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: 'Unable to fetch from remote. Check your network connection.'\n }\n }\n\n let commitsBehind = 0\n let commitsAhead = 0\n\n // Count commits the branch is BEHIND main (main has commits branch doesn't)\n try {\n const { stdout: behindOutput } = await execAsync(\n `git rev-list --count ${command.branch}..origin/main`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n commitsBehind = parseInt(behindOutput.trim(), 10) || 0\n } catch {\n // Branch might not exist or no common ancestor\n commitsBehind = 0\n }\n\n // Count commits the branch is AHEAD of main (branch has commits main doesn't)\n try {\n const { stdout: aheadOutput } = await execAsync(\n `git rev-list --count origin/main..${command.branch}`,\n { cwd, timeout: options?.timeout || 10000 }\n )\n commitsAhead = parseInt(aheadOutput.trim(), 10) || 0\n } catch {\n // Branch might not exist or no common ancestor\n commitsAhead = 0\n }\n\n const isBehind = commitsBehind > 0\n const isAhead = commitsAhead > 0\n const needsSync = isBehind\n\n return {\n success: true,\n output: isBehind\n ? `Branch ${command.branch} is ${commitsBehind} commit(s) behind main`\n : `Branch ${command.branch} is up to date with main`,\n details: {\n branchName: command.branch,\n commitsBehind,\n commitsAhead,\n isBehind,\n isAhead,\n needsSync\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check sync status'\n }\n }\n }\n\n /**\n * EP523: Sync local main branch with remote\n * Used before creating new branches to ensure we branch from latest main\n */\n private async executeSyncMain(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Get current branch to restore later if needed\n let currentBranch = ''\n try {\n const { stdout } = await execAsync('git branch --show-current', { cwd, timeout: 5000 })\n currentBranch = stdout.trim()\n } catch {\n // Ignore - might be detached HEAD\n }\n\n // Fetch latest from remote\n try {\n await execAsync('git fetch origin main', { cwd, timeout: options?.timeout || 30000 })\n } catch (fetchError: any) {\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: 'Unable to fetch from remote. Check your network connection.'\n }\n }\n\n // Check if we need to switch to main\n const needsSwitch = currentBranch !== 'main' && currentBranch !== ''\n\n if (needsSwitch) {\n // Check for uncommitted changes first\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd, timeout: 5000 })\n if (statusOutput.trim()) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n output: 'Cannot sync main: you have uncommitted changes. Commit or stash them first.',\n details: {\n uncommittedFiles: statusOutput.trim().split('\\n').map(line => line.slice(3))\n }\n }\n }\n\n // Switch to main\n await execAsync('git checkout main', { cwd, timeout: options?.timeout || 10000 })\n }\n\n // Pull latest main\n try {\n await execAsync('git pull origin main', { cwd, timeout: options?.timeout || 30000 })\n } catch (pullError: any) {\n // Check for conflicts\n if (pullError.message?.includes('CONFLICT') || pullError.stderr?.includes('CONFLICT')) {\n // Abort the merge\n await execAsync('git merge --abort', { cwd, timeout: 5000 }).catch(() => {})\n return {\n success: false,\n error: 'MERGE_CONFLICT',\n output: 'Conflict while syncing main. This is unexpected - main should not have local commits.'\n }\n }\n throw pullError\n }\n\n // Switch back to original branch if we switched\n if (needsSwitch && currentBranch) {\n await execAsync(`git checkout \"${currentBranch}\"`, { cwd, timeout: options?.timeout || 10000 })\n }\n\n return {\n success: true,\n output: 'Successfully synced main with remote',\n details: {\n currentBranch: needsSwitch ? currentBranch : 'main'\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to sync main'\n }\n }\n }\n\n /**\n * EP523: Rebase a branch onto main\n * Used when resuming work on a branch that's behind main\n */\n private async executeRebaseBranch(\n command: { branch: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Validate branch name\n const validation = validateBranchName(command.branch)\n if (!validation.valid) {\n return {\n success: false,\n error: validation.error || 'UNKNOWN_ERROR'\n }\n }\n\n // Check for uncommitted changes\n const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd, timeout: 5000 })\n if (statusOutput.trim()) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n output: 'Cannot rebase: you have uncommitted changes. Commit or stash them first.',\n details: {\n uncommittedFiles: statusOutput.trim().split('\\n').map(line => line.slice(3))\n }\n }\n }\n\n // Get current branch\n const { stdout: currentBranchOut } = await execAsync('git branch --show-current', { cwd, timeout: 5000 })\n const currentBranch = currentBranchOut.trim()\n\n // Ensure we're on the target branch\n if (currentBranch !== command.branch) {\n await execAsync(`git checkout \"${command.branch}\"`, { cwd, timeout: options?.timeout || 10000 })\n }\n\n // Fetch latest main\n await execAsync('git fetch origin main', { cwd, timeout: options?.timeout || 30000 })\n\n // Perform rebase\n try {\n await execAsync('git rebase origin/main', { cwd, timeout: options?.timeout || 60000 })\n } catch (rebaseError: any) {\n const errorOutput = (rebaseError.stderr || '') + (rebaseError.stdout || '')\n\n // Check for conflicts\n if (errorOutput.includes('CONFLICT') || errorOutput.includes('could not apply')) {\n // Get conflicting files\n let conflictFiles: string[] = []\n try {\n const { stdout: conflictOutput } = await execAsync(\n 'git diff --name-only --diff-filter=U',\n { cwd, timeout: 5000 }\n )\n conflictFiles = conflictOutput.trim().split('\\n').filter(Boolean)\n } catch {\n // Ignore - try alternate method\n try {\n const { stdout: statusOut } = await execAsync('git status --porcelain', { cwd, timeout: 5000 })\n conflictFiles = statusOut\n .trim()\n .split('\\n')\n .filter(line => line.startsWith('UU ') || line.startsWith('AA ') || line.startsWith('DD '))\n .map(line => line.slice(3))\n } catch {\n // Couldn't get conflict files\n }\n }\n\n return {\n success: false,\n error: 'REBASE_CONFLICT',\n output: `Rebase conflict in ${conflictFiles.length} file(s). Resolve conflicts then use rebase_continue, or use rebase_abort to cancel.`,\n details: {\n inRebase: true,\n rebaseConflicts: conflictFiles,\n hasConflicts: true,\n conflictedFiles: conflictFiles\n }\n }\n }\n\n throw rebaseError\n }\n\n return {\n success: true,\n output: `Successfully rebased ${command.branch} onto main`,\n details: {\n branchName: command.branch,\n inRebase: false\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Rebase failed'\n }\n }\n }\n\n /**\n * EP523: Abort an in-progress rebase\n * Returns to the state before rebase was started\n */\n private async executeRebaseAbort(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n await execAsync('git rebase --abort', { cwd, timeout: options?.timeout || 10000 })\n\n return {\n success: true,\n output: 'Rebase aborted. Your branch has been restored to its previous state.',\n details: {\n inRebase: false\n }\n }\n } catch (error: any) {\n // Check if there's no rebase in progress\n if (error.message?.includes('No rebase in progress') || error.stderr?.includes('No rebase in progress')) {\n return {\n success: true,\n output: 'No rebase in progress.',\n details: {\n inRebase: false\n }\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to abort rebase'\n }\n }\n }\n\n /**\n * EP523: Continue a paused rebase after conflicts are resolved\n */\n private async executeRebaseContinue(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Stage all resolved files\n await execAsync('git add -A', { cwd, timeout: 5000 })\n\n // Continue the rebase\n await execAsync('git rebase --continue', { cwd, timeout: options?.timeout || 60000 })\n\n return {\n success: true,\n output: 'Rebase continued successfully.',\n details: {\n inRebase: false\n }\n }\n } catch (error: any) {\n const errorOutput = (error.stderr || '') + (error.stdout || '')\n\n // Check if there are still conflicts\n if (errorOutput.includes('CONFLICT') || errorOutput.includes('could not apply')) {\n // Get remaining conflict files\n let conflictFiles: string[] = []\n try {\n const { stdout: conflictOutput } = await execAsync(\n 'git diff --name-only --diff-filter=U',\n { cwd, timeout: 5000 }\n )\n conflictFiles = conflictOutput.trim().split('\\n').filter(Boolean)\n } catch {\n // Ignore\n }\n\n return {\n success: false,\n error: 'REBASE_CONFLICT',\n output: 'More conflicts encountered. Resolve them and try again.',\n details: {\n inRebase: true,\n rebaseConflicts: conflictFiles,\n hasConflicts: true,\n conflictedFiles: conflictFiles\n }\n }\n }\n\n // Check if there's no rebase in progress\n if (errorOutput.includes('No rebase in progress')) {\n return {\n success: true,\n output: 'No rebase in progress.',\n details: {\n inRebase: false\n }\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to continue rebase'\n }\n }\n }\n\n /**\n * EP523: Check if a rebase is currently in progress\n */\n private async executeRebaseStatus(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Check for rebase-merge directory (indicates rebase in progress)\n let inRebase = false\n let rebaseConflicts: string[] = []\n\n try {\n const { stdout: gitDir } = await execAsync('git rev-parse --git-dir', { cwd, timeout: 5000 })\n const gitDirPath = gitDir.trim()\n\n // Check for rebase directories\n const fs = await import('fs').then(m => m.promises)\n const rebaseMergePath = `${gitDirPath}/rebase-merge`\n const rebaseApplyPath = `${gitDirPath}/rebase-apply`\n\n try {\n await fs.access(rebaseMergePath)\n inRebase = true\n } catch {\n try {\n await fs.access(rebaseApplyPath)\n inRebase = true\n } catch {\n inRebase = false\n }\n }\n } catch {\n // If we can't determine, check via status\n try {\n const { stdout: statusOutput } = await execAsync('git status', { cwd, timeout: 5000 })\n inRebase = statusOutput.includes('rebase in progress') ||\n statusOutput.includes('interactive rebase in progress') ||\n statusOutput.includes('You are currently rebasing')\n } catch {\n inRebase = false\n }\n }\n\n // If in rebase, get conflict files\n if (inRebase) {\n try {\n const { stdout: conflictOutput } = await execAsync(\n 'git diff --name-only --diff-filter=U',\n { cwd, timeout: 5000 }\n )\n rebaseConflicts = conflictOutput.trim().split('\\n').filter(Boolean)\n } catch {\n // No conflicts or couldn't get them\n }\n }\n\n return {\n success: true,\n output: inRebase\n ? `Rebase in progress with ${rebaseConflicts.length} conflicting file(s)`\n : 'No rebase in progress',\n details: {\n inRebase,\n rebaseConflicts,\n hasConflicts: rebaseConflicts.length > 0\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to check rebase status'\n }\n }\n }\n\n /**\n * Run a git command and return structured result\n */\n private async runGitCommand(\n args: string[],\n cwd: string,\n options?: ExecutionOptions & { env?: NodeJS.ProcessEnv }\n ): Promise<ExecutionResult> {\n try {\n // Sanitize arguments\n const sanitizedArgs = sanitizeArgs(args)\n\n // Build command\n const command = ['git', ...sanitizedArgs].join(' ')\n\n // Execute with timeout\n const timeout = options?.timeout || 30000 // 30 second default\n const execOptions = {\n cwd,\n timeout,\n env: options?.env || process.env,\n maxBuffer: 1024 * 1024 * 10 // 10MB buffer\n }\n\n const { stdout, stderr } = await execAsync(command, execOptions)\n\n // Combine output\n const output = (stdout + stderr).trim()\n\n // Extract additional details\n const details: ExecutionResult['details'] = {}\n\n // Try to extract branch name\n const branchName = extractBranchName(output)\n if (branchName) {\n details.branchName = branchName\n }\n\n // Check for detached HEAD\n if (isDetachedHead(output)) {\n details.branchName = 'HEAD (detached)'\n }\n\n return {\n success: true,\n output,\n details: Object.keys(details).length > 0 ? details : undefined\n }\n } catch (error: any) {\n // Parse error\n const stderr = error.stderr || ''\n const stdout = error.stdout || ''\n const exitCode = error.code || 1\n\n // Determine error code\n const errorCode = parseGitError(stderr, stdout, exitCode)\n\n // Extract additional details based on error type\n const details: ExecutionResult['details'] = {\n exitCode\n }\n\n // Parse conflicts if merge conflict\n if (errorCode === 'MERGE_CONFLICT') {\n const conflicts = parseMergeConflicts(stdout + stderr)\n if (conflicts.length > 0) {\n details.conflictingFiles = conflicts\n }\n }\n\n // Parse status for uncommitted changes\n if (errorCode === 'UNCOMMITTED_CHANGES') {\n try {\n const statusResult = await this.executeStatus(cwd, options)\n if (statusResult.details?.uncommittedFiles) {\n details.uncommittedFiles = statusResult.details.uncommittedFiles\n }\n } catch {\n // Ignore errors when getting status\n }\n }\n\n return {\n success: false,\n error: errorCode,\n output: (stdout + stderr).trim(),\n details\n }\n }\n }\n\n /**\n * Validate that git is installed\n */\n private async validateGitInstalled(): Promise<boolean> {\n try {\n await execAsync('git --version', { timeout: 5000 })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Check if directory is a git repository\n */\n private async isGitRepository(cwd: string): Promise<boolean> {\n try {\n await execAsync('git rev-parse --git-dir', { cwd, timeout: 5000 })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Detect the git working directory (repository root)\n * @returns Path to the git repository root\n */\n private async detectWorkingDirectory(startPath?: string): Promise<string | null> {\n try {\n const { stdout } = await execAsync('git rev-parse --show-toplevel', {\n cwd: startPath || process.cwd(),\n timeout: 5000\n })\n return stdout.trim()\n } catch {\n return null\n }\n }\n}\n","/**\n * Version management for @episoda/core\n *\n * EP812: Updated to be bundle-safe. When bundled into CLI by tsup,\n * the package.json path lookup fails because __dirname changes.\n * Now tries package.json first, falls back to hardcoded version.\n *\n * IMPORTANT: Keep FALLBACK_VERSION in sync with package.json version!\n * This should be updated when running `npm version` in the core package.\n */\n\nimport { readFileSync, existsSync } from 'fs'\nimport { join } from 'path'\n\n// Fallback version for bundled usage (update with package.json!)\nconst FALLBACK_VERSION = '0.1.11'\n\nfunction getVersion(): string {\n try {\n // Try to read from package.json (works when running from source)\n const packageJsonPath = join(__dirname, '..', 'package.json')\n if (existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))\n return packageJson.version\n }\n } catch {\n // Bundled or path doesn't exist - use fallback\n }\n return FALLBACK_VERSION\n}\n\nexport const VERSION: string = getVersion()\n","/**\n * Episoda WebSocket Client\n *\n * EP589-9: Implemented with comprehensive error handling and reconnection logic\n *\n * Provides reliable WebSocket connection to episoda.dev CLI gateway with:\n * - Automatic reconnection with exponential backoff\n * - Heartbeat/ping-pong to keep connection alive\n * - Event-driven architecture for handling server commands\n * - Graceful error handling and recovery\n */\n\nimport WS from 'ws'\nimport https from 'https'\nimport { ServerMessage, ClientMessage, ConnectionStatus } from './command-protocol'\nimport { VERSION } from './version'\n\n// EP801: Create HTTPS agent that forces IPv4 to avoid IPv6 timeout issues\n// Many networks have IPv6 configured but not fully routable, causing connection failures\nconst ipv4Agent = new https.Agent({ family: 4 })\n\n// EP701: Client-side events emitted by EpisodaClient\nexport type DisconnectEvent = {\n type: 'disconnected'\n code: number\n reason: string\n willReconnect: boolean\n}\n\nexport type ClientEvent = ServerMessage | DisconnectEvent\n\nexport type EventHandler = (event: ClientEvent) => void | Promise<void>\n\n// EP605: Reconnection configuration for long-term stability\nconst INITIAL_RECONNECT_DELAY = 1000 // 1 second\nconst MAX_RECONNECT_DELAY = 60000 // 60 seconds standard max\nconst IDLE_RECONNECT_DELAY = 600000 // 10 minutes when idle (no commands for 1+ hour)\nconst MAX_RETRY_DURATION = 6 * 60 * 60 * 1000 // 6 hours maximum retry duration\nconst IDLE_THRESHOLD = 60 * 60 * 1000 // 1 hour - after this, use slower retry\n\n// EP648: Rate limit and rapid reconnect prevention\nconst RATE_LIMIT_BACKOFF = 60000 // 60 seconds when rate limited\nconst RAPID_CLOSE_THRESHOLD = 2000 // If connection closes within 2s, it's likely an error\nconst RAPID_CLOSE_BACKOFF = 30000 // 30 seconds backoff for rapid close scenarios\n\n// EP605: Client-side heartbeat to detect dead connections\nconst CLIENT_HEARTBEAT_INTERVAL = 45000 // 45 seconds (offset from server's 30s to avoid collision)\nconst CLIENT_HEARTBEAT_TIMEOUT = 15000 // 15 seconds to wait for pong\n\n// EP606: Connection timeout for initial WebSocket connection\nconst CONNECTION_TIMEOUT = 15000 // 15 seconds to establish connection\n\n/**\n * WebSocket client for connecting to episoda.dev/cli gateway\n *\n * DESIGN PRINCIPLES:\n * - Reverse WebSocket: Client connects TO server (bypasses NAT/firewall)\n * - Automatic reconnection with exponential backoff\n * - Server-initiated heartbeat via WebSocket ping/pong (30s interval)\n * - Event-driven architecture for handling server commands\n */\nexport class EpisodaClient {\n private ws?: WS\n private eventHandlers: Map<string, EventHandler[]> = new Map()\n private reconnectAttempts = 0\n private reconnectTimeout?: NodeJS.Timeout\n private url = ''\n private token = ''\n private machineId?: string\n private hostname?: string\n private osPlatform?: string\n private osArch?: string\n private daemonPid?: number\n private isConnected = false\n private isDisconnecting = false\n private isGracefulShutdown = false // Track if shutdown was graceful (server-initiated)\n // EP605: Client-side heartbeat for connection health monitoring\n private heartbeatTimer?: NodeJS.Timeout\n private heartbeatTimeoutTimer?: NodeJS.Timeout\n // EP605: Activity and retry duration tracking\n private lastCommandTime = Date.now() // Track last command for idle detection\n private firstDisconnectTime?: number // Track when reconnection attempts started\n private isIntentionalDisconnect = false // Prevent reconnection after intentional disconnect\n // EP648: Rate limit and rapid reconnect tracking\n private rateLimitBackoffUntil?: number // Timestamp until which we should wait (rate limited)\n private lastConnectAttemptTime = 0 // Track when we last attempted to connect\n private lastErrorCode?: string // Track the last error code received\n\n /**\n * Connect to episoda.dev WebSocket gateway\n * @param url - WebSocket URL (wss://episoda.dev/cli)\n * @param token - OAuth access token\n * @param machineId - Optional machine identifier for multi-machine support\n * @param deviceInfo - Optional device information (hostname, OS, daemonPid)\n */\n async connect(\n url: string,\n token: string,\n machineId?: string,\n deviceInfo?: { hostname?: string; osPlatform?: string; osArch?: string; daemonPid?: number }\n ): Promise<void> {\n this.url = url\n this.token = token\n this.machineId = machineId\n this.hostname = deviceInfo?.hostname\n this.osPlatform = deviceInfo?.osPlatform\n this.osArch = deviceInfo?.osArch\n this.daemonPid = deviceInfo?.daemonPid\n this.isDisconnecting = false\n this.isGracefulShutdown = false // Reset graceful shutdown flag on new connection\n this.isIntentionalDisconnect = false // Allow reconnection on fresh connect\n this.lastConnectAttemptTime = Date.now() // EP648: Track when this connection attempt started\n this.lastErrorCode = undefined // EP648: Clear last error code\n\n return new Promise((resolve, reject) => {\n // EP606: Connection timeout to prevent hanging indefinitely\n const connectionTimeout = setTimeout(() => {\n if (this.ws) {\n this.ws.terminate()\n }\n reject(new Error(`Connection timeout after ${CONNECTION_TIMEOUT / 1000}s - server may be unreachable`))\n }, CONNECTION_TIMEOUT)\n\n try {\n // EP801: Use IPv4 agent to avoid IPv6 timeout issues on networks with broken IPv6\n this.ws = new WS(url, { agent: ipv4Agent })\n\n // Connection opened\n this.ws.on('open', () => {\n clearTimeout(connectionTimeout) // EP606: Clear timeout on successful connection\n console.log('[EpisodaClient] WebSocket connected')\n this.isConnected = true\n this.reconnectAttempts = 0\n this.firstDisconnectTime = undefined // EP605: Reset retry duration tracking\n this.lastCommandTime = Date.now() // EP605: Reset activity timer\n\n // Send auth message (K722: includes machineId, EP596: includes device info, EP601: includes daemonPid)\n this.send({\n type: 'auth',\n token,\n version: VERSION,\n machineId,\n hostname: this.hostname,\n osPlatform: this.osPlatform,\n osArch: this.osArch,\n daemonPid: this.daemonPid\n })\n\n // EP605: Start client-side heartbeat\n this.startHeartbeat()\n\n resolve()\n })\n\n // EP605: Handle pong response from server (for client-initiated pings)\n this.ws.on('pong', () => {\n // Clear the timeout - connection is alive\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n this.heartbeatTimeoutTimer = undefined\n }\n })\n\n // Message received\n this.ws.on('message', (data: WS.Data) => {\n try {\n const message = JSON.parse(data.toString()) as ServerMessage\n this.handleMessage(message)\n } catch (error) {\n console.error('[EpisodaClient] Failed to parse message:', error)\n }\n })\n\n // EP603: Log ping events for debugging connection health\n this.ws.on('ping', () => {\n console.log('[EpisodaClient] Received ping from server')\n })\n\n // Connection closed\n this.ws.on('close', (code: number, reason: Buffer) => {\n console.log(`[EpisodaClient] WebSocket closed: ${code} ${reason.toString()}`)\n this.isConnected = false\n\n const willReconnect = !this.isDisconnecting\n\n // EP701: Emit 'disconnected' event so daemon can clean up connection\n this.emit({\n type: 'disconnected',\n code,\n reason: reason.toString(),\n willReconnect\n })\n\n // Attempt reconnection if not intentional disconnect\n if (willReconnect) {\n this.scheduleReconnect()\n }\n })\n\n // Error occurred\n this.ws.on('error', (error: Error) => {\n console.error('[EpisodaClient] WebSocket error:', error)\n\n if (!this.isConnected) {\n // Connection failed, reject the promise\n clearTimeout(connectionTimeout) // EP606: Clear timeout on error\n reject(error)\n }\n })\n\n } catch (error) {\n clearTimeout(connectionTimeout) // EP606: Clear timeout on exception\n reject(error)\n }\n })\n }\n\n /**\n * Disconnect from the server\n * @param intentional - If true, prevents automatic reconnection (user-initiated disconnect)\n */\n async disconnect(intentional = true): Promise<void> {\n this.isDisconnecting = true\n this.isIntentionalDisconnect = intentional // EP605: Prevent reconnection if user intentionally disconnected\n\n // EP605: Clear all timers\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = undefined\n }\n\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\n }\n\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n this.heartbeatTimeoutTimer = undefined\n }\n\n if (this.ws) {\n this.ws.close()\n this.ws = undefined\n }\n\n this.isConnected = false\n }\n\n /**\n * Register an event handler\n * @param event - Event type ('command', 'ping', 'error', 'auth_success')\n * @param handler - Handler function\n */\n on(event: string, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, [])\n }\n this.eventHandlers.get(event)!.push(handler)\n }\n\n /**\n * Send a message to the server\n * @param message - Client message to send\n */\n async send(message: ClientMessage): Promise<void> {\n if (!this.ws || !this.isConnected) {\n throw new Error('WebSocket not connected')\n }\n\n return new Promise((resolve, reject) => {\n this.ws!.send(JSON.stringify(message), (error) => {\n if (error) {\n console.error('[EpisodaClient] Failed to send message:', error)\n reject(error)\n } else {\n resolve()\n }\n })\n })\n }\n\n /**\n * Get current connection status\n */\n getStatus(): ConnectionStatus {\n return {\n connected: this.isConnected\n }\n }\n\n /**\n * EP605: Update last command time to reset idle detection\n * Call this when a command is received/executed\n */\n updateActivity(): void {\n this.lastCommandTime = Date.now()\n }\n\n /**\n * EP701: Emit a client-side event to registered handlers\n * Used for events like 'disconnected' that originate from the client, not server\n */\n private emit(event: ClientEvent): void {\n const handlers = this.eventHandlers.get(event.type) || []\n handlers.forEach(handler => {\n try {\n handler(event)\n } catch (error) {\n console.error(`[EpisodaClient] Handler error for ${event.type}:`, error)\n }\n })\n }\n\n /**\n * Handle incoming message from server\n */\n private handleMessage(message: ServerMessage): void {\n // Special handling for graceful shutdown messages from server\n if (message.type === 'shutdown') {\n console.log('[EpisodaClient] Received graceful shutdown message from server')\n this.isGracefulShutdown = true\n }\n\n // EP648: Handle error messages, especially rate limiting and rapid reconnect\n if (message.type === 'error') {\n const errorMessage = message as ServerMessage & { code?: string; retryAfter?: number }\n this.lastErrorCode = errorMessage.code\n\n if (errorMessage.code === 'RATE_LIMITED' || errorMessage.code === 'TOO_SOON') {\n // Use server-provided retryAfter or default to 60 seconds for rate limit, 5 seconds for too soon\n const defaultRetry = errorMessage.code === 'RATE_LIMITED' ? 60 : 5\n const retryAfterMs = (errorMessage.retryAfter || defaultRetry) * 1000\n this.rateLimitBackoffUntil = Date.now() + retryAfterMs\n console.log(`[EpisodaClient] ${errorMessage.code}: will retry after ${retryAfterMs / 1000}s`)\n }\n }\n\n const handlers = this.eventHandlers.get(message.type) || []\n\n // Execute all handlers for this message type\n handlers.forEach(handler => {\n try {\n handler(message)\n } catch (error) {\n console.error(`[EpisodaClient] Handler error for ${message.type}:`, error)\n }\n })\n }\n\n /**\n * Schedule reconnection with exponential backoff and randomization\n *\n * EP605: Conservative reconnection with multiple safeguards:\n * - 6-hour maximum retry duration to prevent indefinite retrying\n * - Activity-based backoff (10 min delay if idle for 1+ hour)\n * - No reconnection after intentional disconnect\n * - Randomization to prevent thundering herd\n *\n * EP648: Additional protections against reconnection loops:\n * - Rate limit awareness: respect server's RATE_LIMITED response\n * - Rapid close detection: if connection closes within 2s, apply longer backoff\n */\n private scheduleReconnect(): void {\n // EP605: Don't reconnect if user intentionally disconnected\n if (this.isIntentionalDisconnect) {\n console.log('[EpisodaClient] Intentional disconnect - not reconnecting')\n return\n }\n\n // EP605: Clear heartbeat timers before reconnection attempt\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = undefined\n }\n\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n this.heartbeatTimeoutTimer = undefined\n }\n\n // EP605: Track when reconnection attempts started\n if (!this.firstDisconnectTime) {\n this.firstDisconnectTime = Date.now()\n }\n\n // EP605: Check 6-hour maximum retry duration\n const retryDuration = Date.now() - this.firstDisconnectTime\n if (retryDuration >= MAX_RETRY_DURATION) {\n console.error(`[EpisodaClient] Maximum retry duration (6 hours) exceeded, giving up. Please restart the CLI.`)\n return\n }\n\n // EP605: Log periodically to show we're still trying\n if (this.reconnectAttempts > 0 && this.reconnectAttempts % 10 === 0) {\n const hoursRemaining = ((MAX_RETRY_DURATION - retryDuration) / (60 * 60 * 1000)).toFixed(1)\n console.log(`[EpisodaClient] Still attempting to reconnect (attempt ${this.reconnectAttempts}, ${hoursRemaining}h remaining)...`)\n }\n\n // EP648: Check if we're still in rate limit backoff period\n if (this.rateLimitBackoffUntil && Date.now() < this.rateLimitBackoffUntil) {\n const waitTime = this.rateLimitBackoffUntil - Date.now()\n console.log(`[EpisodaClient] Rate limited, waiting ${Math.round(waitTime / 1000)}s before retry`)\n this.reconnectAttempts++\n this.reconnectTimeout = setTimeout(() => {\n this.rateLimitBackoffUntil = undefined // Clear rate limit after waiting\n this.scheduleReconnect()\n }, waitTime)\n return\n }\n\n // EP648: Detect rapid close (connection closed within 2 seconds of opening)\n // This indicates an error condition like rate limiting, auth failure, etc.\n const timeSinceConnect = Date.now() - this.lastConnectAttemptTime\n const wasRapidClose = timeSinceConnect < RAPID_CLOSE_THRESHOLD && this.lastConnectAttemptTime > 0\n\n // EP605: Check if connection is idle (no commands for 1+ hour)\n const timeSinceLastCommand = Date.now() - this.lastCommandTime\n const isIdle = timeSinceLastCommand >= IDLE_THRESHOLD\n\n // Calculate base delay based on shutdown type and conditions\n let baseDelay: number\n if (this.isGracefulShutdown && this.reconnectAttempts < 3) {\n // Graceful shutdown: try reconnecting quickly (500ms, 1s, 2s)\n baseDelay = 500 * Math.pow(2, this.reconnectAttempts)\n } else if (wasRapidClose) {\n // EP648: Connection failed almost immediately - likely rate limited or auth error\n // Apply longer backoff to prevent spam\n baseDelay = Math.max(RAPID_CLOSE_BACKOFF, INITIAL_RECONNECT_DELAY * Math.pow(2, Math.min(this.reconnectAttempts, 6)))\n console.log(`[EpisodaClient] Rapid close detected (${timeSinceConnect}ms), applying ${baseDelay / 1000}s backoff`)\n } else if (isIdle) {\n // EP605: Use slower retry for idle connections (10 minutes)\n baseDelay = IDLE_RECONNECT_DELAY\n } else {\n // EP605: Use capped exponential backoff\n // After 6 attempts, we hit max delay and stay there\n const cappedAttempts = Math.min(this.reconnectAttempts, 6)\n baseDelay = INITIAL_RECONNECT_DELAY * Math.pow(2, cappedAttempts)\n }\n\n // Add randomization (±25%) to prevent thundering herd\n // When server restarts, all clients shouldn't hit it at the exact same time\n const jitter = baseDelay * 0.25 * (Math.random() * 2 - 1) // Random between -25% and +25%\n const maxDelay = isIdle ? IDLE_RECONNECT_DELAY : MAX_RECONNECT_DELAY\n const delay = Math.min(baseDelay + jitter, maxDelay)\n\n this.reconnectAttempts++\n const shutdownType = this.isGracefulShutdown ? 'graceful' : 'ungraceful'\n const idleStatus = isIdle ? ', idle' : ''\n const rapidStatus = wasRapidClose ? ', rapid-close' : ''\n\n // EP605: Only log first few attempts and then periodically\n if (this.reconnectAttempts <= 5 || this.reconnectAttempts % 10 === 0) {\n console.log(`[EpisodaClient] Reconnecting in ${Math.round(delay / 1000)}s (attempt ${this.reconnectAttempts}, ${shutdownType}${idleStatus}${rapidStatus})`)\n }\n\n this.reconnectTimeout = setTimeout(() => {\n console.log('[EpisodaClient] Attempting reconnection...')\n this.connect(this.url, this.token, this.machineId, {\n hostname: this.hostname,\n osPlatform: this.osPlatform,\n osArch: this.osArch,\n daemonPid: this.daemonPid\n }).then(() => {\n // EP605: Reset counters after successful reconnection\n console.log('[EpisodaClient] Reconnection successful, resetting retry counter')\n this.reconnectAttempts = 0\n this.isGracefulShutdown = false\n this.firstDisconnectTime = undefined\n this.rateLimitBackoffUntil = undefined // EP648: Clear rate limit on success\n }).catch(error => {\n console.error('[EpisodaClient] Reconnection failed:', error)\n // scheduleReconnect will be called again from the 'close' event\n })\n }, delay)\n }\n\n /**\n * EP605: Start client-side heartbeat to detect dead connections\n *\n * Sends ping every 45 seconds and expects pong within 15 seconds.\n * If no pong received, terminates connection to trigger reconnection.\n */\n private startHeartbeat(): void {\n // Clear any existing heartbeat\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n }\n\n this.heartbeatTimer = setInterval(() => {\n if (!this.ws || this.ws.readyState !== WS.OPEN) {\n return\n }\n\n // Send ping\n try {\n this.ws.ping()\n\n // Clear any existing timeout before setting new one (prevents timer leak)\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer)\n }\n\n // Set timeout for pong response\n this.heartbeatTimeoutTimer = setTimeout(() => {\n console.log('[EpisodaClient] Heartbeat timeout - no pong received, terminating connection')\n if (this.ws) {\n this.ws.terminate() // Force close to trigger reconnection\n }\n }, CLIENT_HEARTBEAT_TIMEOUT)\n } catch (error) {\n console.error('[EpisodaClient] Error sending heartbeat ping:', error)\n }\n }, CLIENT_HEARTBEAT_INTERVAL)\n }\n}\n","/**\n * Authentication utilities\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as os from 'os'\nimport { execSync } from 'child_process'\nimport { EpisodaConfig } from './command-protocol'\n\nconst DEFAULT_CONFIG_FILE = 'config.json'\n\n/**\n * Get the config directory path\n * Supports EPISODA_CONFIG_DIR env var for running multiple environments\n */\nexport function getConfigDir(): string {\n return process.env.EPISODA_CONFIG_DIR || path.join(os.homedir(), '.episoda')\n}\n\n/**\n * Get the full path to the config file\n */\nexport function getConfigPath(configPath?: string): string {\n if (configPath) {\n return configPath\n }\n return path.join(getConfigDir(), DEFAULT_CONFIG_FILE)\n}\n\n/**\n * Ensure the config directory exists\n *\n * EP798: Also excludes the directory from iCloud/Dropbox sync on macOS\n * to prevent machine-specific files (machine-id, daemon.sock) from syncing.\n */\nfunction ensureConfigDir(configPath: string): void {\n const dir = path.dirname(configPath)\n const isNew = !fs.existsSync(dir)\n\n if (isNew) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 })\n }\n\n // EP798: Exclude from cloud sync on macOS\n // Only do this once when directory is created (or if .nosync doesn't exist)\n if (process.platform === 'darwin') {\n const nosyncPath = path.join(dir, '.nosync')\n if (isNew || !fs.existsSync(nosyncPath)) {\n try {\n // Create .nosync file (respected by some cloud services)\n fs.writeFileSync(nosyncPath, '', { mode: 0o600 })\n // Set iCloud-specific exclusion attribute\n execSync(`xattr -w com.apple.fileprovider.ignore 1 \"${dir}\"`, {\n stdio: 'ignore',\n timeout: 5000\n })\n } catch {\n // Ignore errors - xattr may not be available or dir may already be excluded\n }\n }\n }\n}\n\n/**\n * Load configuration from .episoda/config.json\n */\nexport async function loadConfig(configPath?: string): Promise<EpisodaConfig | null> {\n const fullPath = getConfigPath(configPath)\n\n if (!fs.existsSync(fullPath)) {\n return null\n }\n\n try {\n const content = fs.readFileSync(fullPath, 'utf8')\n const config = JSON.parse(content) as EpisodaConfig\n return config\n } catch (error) {\n console.error('Error loading config:', error)\n return null\n }\n}\n\n/**\n * Save configuration to .episoda/config.json\n */\nexport async function saveConfig(config: EpisodaConfig, configPath?: string): Promise<void> {\n const fullPath = getConfigPath(configPath)\n ensureConfigDir(fullPath)\n\n try {\n const content = JSON.stringify(config, null, 2)\n fs.writeFileSync(fullPath, content, { mode: 0o600 })\n } catch (error) {\n throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Validate an access token\n */\nexport async function validateToken(token: string): Promise<boolean> {\n // For now, just check if token exists and is non-empty\n // In the future, we can make an API call to validate\n return Boolean(token && token.length > 0)\n}\n","/**\n * Error handling utilities\n */\n\nimport { ErrorCode } from './command-protocol'\n\n/**\n * Custom error class for Episoda operations\n */\nexport class EpisodaError extends Error {\n constructor(\n public code: ErrorCode,\n message: string,\n public details?: Record<string, any>\n ) {\n super(message)\n this.name = 'EpisodaError'\n }\n}\n\n// Note: parseGitError is exported from git-parser.ts\n\n/**\n * Create a user-friendly error message from error code\n * (For CLI use - MCP will format differently)\n */\nexport function formatErrorMessage(code: ErrorCode, details?: Record<string, any>): string {\n const messages: Record<ErrorCode, string> = {\n 'GIT_NOT_INSTALLED': 'Git is not installed or not in PATH',\n 'NOT_GIT_REPO': 'Not a git repository',\n 'MERGE_CONFLICT': 'Merge conflict detected',\n 'REBASE_CONFLICT': 'Rebase conflict detected - resolve conflicts or abort rebase',\n 'UNCOMMITTED_CHANGES': 'Uncommitted changes would be overwritten',\n 'NETWORK_ERROR': 'Network error occurred',\n 'AUTH_FAILURE': 'Authentication failed',\n 'BRANCH_NOT_FOUND': 'Branch not found',\n 'BRANCH_ALREADY_EXISTS': 'Branch already exists',\n 'PUSH_REJECTED': 'Push rejected by remote',\n 'COMMAND_TIMEOUT': 'Command timed out',\n 'UNKNOWN_ERROR': 'Unknown error occurred'\n }\n\n let message = messages[code] || `Error: ${code}`\n\n // Add details if provided\n if (details) {\n if (details.uncommittedFiles && details.uncommittedFiles.length > 0) {\n message += `\\nUncommitted files: ${details.uncommittedFiles.join(', ')}`\n }\n if (details.conflictingFiles && details.conflictingFiles.length > 0) {\n message += `\\nConflicting files: ${details.conflictingFiles.join(', ')}`\n }\n if (details.branchName) {\n message += `\\nBranch: ${details.branchName}`\n }\n }\n\n return message\n}\n","/**\n * @episoda/core\n *\n * Reusable business logic for Episoda local development orchestration.\n * Used by both episoda CLI (current) and @episoda/mcp-local-dev (future).\n *\n * DESIGN PRINCIPLES:\n * - Interface-agnostic: No CLI or MCP-specific code\n * - Structured data: Return objects, not formatted strings\n * - Error codes: Return codes, not messages\n * - Reusable: 80% code reuse target for MCP conversion\n */\n\n// Core types (LOCKED for Phase 2)\nexport * from './command-protocol'\n\n// Business logic classes\nexport { GitExecutor } from './git-executor'\nexport { EpisodaClient, type DisconnectEvent, type ClientEvent, type EventHandler } from './websocket-client'\n\n// Utilities\nexport * from './auth'\nexport * from './errors'\nexport * from './git-validator'\nexport * from './git-parser'\n\n// Package version - single source of truth from package.json\nexport { VERSION } from './version'\n","/**\n * Episoda CLI\n *\n * Local development workflow orchestration for episoda.dev\n */\n\nimport { program } from 'commander'\nimport { VERSION } from '@episoda/core'\nimport { devCommand } from './commands/dev'\nimport { authCommand } from './commands/auth'\nimport { statusCommand } from './commands/status'\nimport { stopCommand } from './commands/stop'\nimport { status } from './output'\n\nprogram\n .name('episoda')\n .description('Episoda local development workflow orchestration')\n .version(VERSION)\n\n// Auth command (K722)\nprogram\n .command('auth')\n .description('Authenticate to Episoda via OAuth and configure CLI')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (options) => {\n try {\n await authCommand(options)\n } catch (error) {\n status.error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Dev command\nprogram\n .command('dev')\n .description('Start dev server with Episoda orchestration')\n .option('--auto-restart', 'Auto-restart dev server if it crashes')\n .option('--connection-only', 'Connection-only mode (don\\'t start dev server)')\n .allowUnknownOption()\n .action(async (options, cmd) => {\n try {\n // Extract command after 'dev' as the dev server command\n // e.g., 'episoda dev -- npm run dev' -> [\"npm\", \"run\", \"dev\"]\n const args = process.argv.slice(process.argv.indexOf('dev') + 1)\n\n // Remove '--' separator if present\n const separatorIndex = args.indexOf('--')\n const command = separatorIndex >= 0 ? args.slice(separatorIndex + 1) : []\n\n // Filter out our own options\n const filteredCommand = command.filter(arg => arg !== '--auto-restart' && arg !== '--connection-only')\n\n await devCommand({\n command: options.connectionOnly ? [] : (filteredCommand.length > 0 ? filteredCommand : undefined),\n autoRestart: options.autoRestart\n })\n } catch (error) {\n status.error(`Dev command failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Status command\n// EP805: Added --verify flag for explicit connection health check\nprogram\n .command('status')\n .description('Show connection status')\n .option('--verify', 'Verify connection is healthy (not just connected)')\n .action(async (options) => {\n try {\n await statusCommand({ verify: options.verify })\n } catch (error) {\n status.error(`Status check failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Stop command (K722)\nprogram\n .command('stop')\n .description('Stop the Episoda daemon')\n .option('--force', 'Force kill daemon without graceful shutdown')\n .action(async (options) => {\n try {\n await stopCommand(options)\n } catch (error) {\n status.error(`Stop failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Disconnect command (alias for stop)\nprogram\n .command('disconnect')\n .description('Disconnect from episoda.dev (alias for stop)')\n .action(async () => {\n try {\n await stopCommand()\n } catch (error) {\n status.error(`Disconnect failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram.parse()\n","/**\n * `episoda dev` command\n *\n * K722 Architecture:\n * - Ensures daemon is running\n * - Adds project to daemon for WebSocket connection\n * - Optionally wraps user's dev server command\n * - Daemon handles WebSocket persistence across terminal sessions\n */\n\nimport { loadConfig, saveConfig } from '@episoda/core'\nimport { resolveDevCommand } from '../framework-detector'\nimport { status, printFrameworkDetection } from '../output'\nimport { isDaemonRunning, startDaemon, killAllEpisodaProcesses } from '../daemon/daemon-manager'\nimport { addProject, isDaemonReachable } from '../ipc/ipc-client'\nimport { spawn, ChildProcess, execSync } from 'child_process'\nimport * as path from 'path'\nimport { isPortInUse, getServerPort } from '../utils/port-check'\n\n// EP734: Connection retry configuration (polling removed - now event-driven)\nconst CONNECTION_MAX_RETRIES = 3 // Retry up to 3 times on failure\n\n/**\n * EP593: Find the git repository root directory\n * This ensures we always use the same project path regardless of where episoda dev is run\n */\nfunction findGitRoot(startDir: string): string | null {\n try {\n const result = execSync('git rev-parse --show-toplevel', {\n cwd: startDir,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe']\n })\n return result.trim()\n } catch {\n return null\n }\n}\n\nexport interface DevCommandOptions {\n command?: string[] // User-provided command (e.g., [\"npm\", \"run\", \"dev\"])\n autoRestart?: boolean // Auto-restart dev server if it crashes\n cwd?: string // Working directory\n}\n\n/**\n * Execute the `episoda dev` command\n * @param options - Command options\n */\nexport async function devCommand(options: DevCommandOptions = {}): Promise<void> {\n try {\n // K722: Load existing config (created by /api/start OAuth flow)\n const config = await loadConfig()\n if (!config || !config.access_token || !config.project_id) {\n status.error('No authentication found. Please run:')\n status.info('')\n status.info(' episoda auth')\n status.info('')\n status.info('This will authenticate with episoda.dev and configure the CLI.')\n process.exit(1)\n }\n\n // EP734: Clean up any stale episoda processes before starting\n // This ensures only one daemon runs per device\n const killedCount = killAllEpisodaProcesses()\n if (killedCount > 0) {\n status.info(`Cleaned up ${killedCount} stale process${killedCount > 1 ? 'es' : ''}`)\n }\n\n // EP734: Determine project path using cached settings (avoids network call)\n // Priority: 1. Cached settings (if fresh), 2. Git root detection, 3. Current working directory\n const serverUrl = config.api_url || process.env.EPISODA_API_URL || 'https://episoda.dev'\n let projectPath: string\n\n // EP734: Check if we have fresh cached settings (< 24 hours old)\n const SETTINGS_CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours in ms\n const cachedSettings = config.project_settings\n const isCacheFresh = cachedSettings?.cached_at &&\n (Date.now() - cachedSettings.cached_at) < SETTINGS_CACHE_TTL\n\n if (isCacheFresh && cachedSettings?.local_project_path) {\n // Use cached project path\n projectPath = cachedSettings.local_project_path\n status.debug(`Using cached project path: ${projectPath}`)\n } else {\n // No cache or stale - detect git root locally (no network call)\n const detectedRoot = findGitRoot(options.cwd || process.cwd())\n projectPath = detectedRoot || path.resolve(options.cwd || process.cwd())\n\n if (detectedRoot) {\n status.debug(`Detected project root: ${projectPath}`)\n } else {\n status.warning(`Could not detect git root, using: ${projectPath}`)\n }\n\n // Cache the detected path locally\n const updatedConfig = {\n ...config,\n project_settings: {\n ...cachedSettings,\n local_project_path: projectPath,\n cached_at: Date.now()\n }\n }\n await saveConfig(updatedConfig)\n status.debug('Cached project settings locally')\n\n // Background: Save to server (don't await - fire and forget)\n const settingsUrl = `${serverUrl}/api/projects/${config.project_id}/settings`\n fetch(settingsUrl, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ local_project_path: projectPath })\n }).catch(() => {}) // Ignore errors - server sync is best-effort\n }\n\n // K722: Ensure daemon is running\n let daemonPid = isDaemonRunning()\n if (!daemonPid) {\n status.info('Starting Episoda daemon...')\n try {\n daemonPid = await startDaemon()\n status.success(`Daemon started (PID: ${daemonPid})`)\n } catch (error) {\n status.error(`Failed to start daemon: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n } else {\n status.debug(`Daemon already running (PID: ${daemonPid})`)\n }\n\n // Verify daemon is reachable via IPC\n const reachable = await isDaemonReachable()\n if (!reachable) {\n status.error('Daemon is running but not responding. Try: episoda stop && episoda dev')\n process.exit(1)\n }\n\n // EP734: Add project to daemon - now blocking (no more polling)\n // The addProject IPC call waits for WebSocket connection to complete\n status.info(`Connecting project to Episoda...`)\n\n let connected = false\n let lastError: string | undefined\n\n for (let retry = 0; retry < CONNECTION_MAX_RETRIES && !connected; retry++) {\n if (retry > 0) {\n status.info(`Retrying connection (attempt ${retry + 1}/${CONNECTION_MAX_RETRIES})...`)\n await new Promise(resolve => setTimeout(resolve, 1000))\n }\n\n try {\n // EP734: Blocking call - waits for connection to complete\n const result = await addProject(config.project_id, projectPath)\n\n if (result.connected) {\n connected = true\n } else {\n lastError = result.error || 'Connection failed'\n }\n } catch (error) {\n lastError = error instanceof Error ? error.message : String(error)\n }\n }\n\n if (!connected) {\n status.error(`Failed to connect: ${lastError}`)\n status.info('Check server status at https://episoda.dev/api/system/health')\n process.exit(1)\n }\n\n status.success('Connected to Episoda')\n\n // Determine if we should start a dev server or just connect\n let command: string[] | undefined\n let detection: any\n let connectionOnlyMode = false\n\n // Check if user explicitly requested connection-only mode\n if (options.command && options.command.length === 0) {\n connectionOnlyMode = true\n status.debug('Connection-only mode (explicit)')\n } else if (!options.command) {\n // Auto-detect: Check if server is already running\n const serverPort = getServerPort()\n const portInUse = await isPortInUse(serverPort)\n\n if (portInUse) {\n connectionOnlyMode = true\n status.info(`Server already running on port ${serverPort}`)\n status.info('Connecting to existing server...')\n }\n }\n\n // Resolve dev command if not in connection-only mode\n if (!connectionOnlyMode) {\n try {\n const result = await resolveDevCommand(options.command || null, projectPath)\n command = result.command\n detection = result.detection\n\n // Show framework detection if auto-detected\n if (detection && !options.command) {\n printFrameworkDetection(detection.framework, detection.command, detection.confidence)\n }\n } catch (error) {\n // No command detected\n status.debug('No dev command detected')\n command = undefined\n }\n }\n\n // If dev command provided, wrap it\n if (command && command.length > 0) {\n await runDevServer(command, projectPath, options.autoRestart ?? false)\n } else {\n // Connection-only mode\n status.info('')\n status.info('Connected! Git operations will be executed by Episoda.')\n status.info('Press Ctrl+C to disconnect.')\n status.info('')\n\n // Keep process alive\n await new Promise(() => {})\n }\n } catch (error) {\n status.error(`Failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n}\n\n/**\n * Run dev server process\n */\nasync function runDevServer(\n command: string[],\n cwd: string,\n autoRestart: boolean\n): Promise<void> {\n let devProcess: ChildProcess | undefined\n let restartCount = 0\n let shuttingDown = false\n\n const startServer = () => {\n status.info(`Starting dev server: ${command.join(' ')}`)\n\n devProcess = spawn(command[0], command.slice(1), {\n cwd,\n stdio: ['inherit', 'inherit', 'inherit'],\n shell: true\n })\n\n devProcess.on('exit', (code, signal) => {\n if (shuttingDown) {\n status.info('Dev server stopped')\n return\n }\n\n if (code === 0) {\n status.success('Dev server exited successfully')\n } else {\n status.error(`Dev server exited with code ${code}${signal ? ` (signal: ${signal})` : ''}`)\n }\n\n // Auto-restart if enabled\n if (autoRestart && !shuttingDown) {\n restartCount++\n status.info(`Auto-restarting dev server (attempt ${restartCount})...`)\n setTimeout(() => {\n startServer()\n }, 2000)\n }\n })\n\n devProcess.on('error', (error) => {\n status.error(`Dev server error: ${error.message}`)\n })\n }\n\n // Setup shutdown handler\n const shutdownHandler = async (signal: string) => {\n if (shuttingDown) return\n shuttingDown = true\n\n status.info(`\\nReceived ${signal}, shutting down...`)\n\n if (devProcess) {\n devProcess.kill('SIGTERM')\n }\n\n // Note: Daemon keeps running in background\n status.info('Dev server stopped. Daemon continues running in background.')\n process.exit(0)\n }\n\n process.on('SIGTERM', () => shutdownHandler('SIGTERM'))\n process.on('SIGINT', () => shutdownHandler('SIGINT'))\n\n startServer()\n\n // Keep process alive\n await new Promise(() => {})\n}\n","/**\n * Framework Detection\n *\n * Detects the framework/stack based on project files and suggests\n * appropriate dev server command.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nexport interface FrameworkDetection {\n framework: string\n command: string[]\n confidence: 'high' | 'medium' | 'low'\n detectedFrom: string\n}\n\n/**\n * Detect framework from project files\n * @param cwd - Working directory to search (defaults to process.cwd())\n * @returns Detection result or null if no framework detected\n */\nexport async function detectFramework(cwd: string = process.cwd()): Promise<FrameworkDetection | null> {\n // Check for package.json (Node.js projects)\n const packageJsonPath = path.join(cwd, 'package.json')\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n\n // Check scripts first\n const scripts = packageJson.scripts || {}\n\n // Next.js detection\n if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {\n if (scripts.dev) {\n return {\n framework: 'Next.js',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (next dependency + dev script)'\n }\n }\n return {\n framework: 'Next.js',\n command: ['npx', 'next', 'dev'],\n confidence: 'medium',\n detectedFrom: 'package.json (next dependency)'\n }\n }\n\n // React (Create React App, Vite, etc.)\n if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {\n if (scripts.dev) {\n return {\n framework: 'React',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (react + dev script)'\n }\n }\n if (scripts.start) {\n return {\n framework: 'React',\n command: ['npm', 'start'],\n confidence: 'high',\n detectedFrom: 'package.json (react + start script)'\n }\n }\n }\n\n // Express\n if (packageJson.dependencies?.express) {\n if (scripts.dev) {\n return {\n framework: 'Express',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (express + dev script)'\n }\n }\n if (scripts.start) {\n return {\n framework: 'Express',\n command: ['npm', 'start'],\n confidence: 'medium',\n detectedFrom: 'package.json (express + start script)'\n }\n }\n }\n\n // Vue\n if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {\n if (scripts.dev) {\n return {\n framework: 'Vue',\n command: ['npm', 'run', 'dev'],\n confidence: 'high',\n detectedFrom: 'package.json (vue + dev script)'\n }\n }\n if (scripts.serve) {\n return {\n framework: 'Vue',\n command: ['npm', 'run', 'serve'],\n confidence: 'high',\n detectedFrom: 'package.json (vue + serve script)'\n }\n }\n }\n\n // Generic Node.js (fallback)\n if (scripts.dev) {\n return {\n framework: 'Node.js',\n command: ['npm', 'run', 'dev'],\n confidence: 'medium',\n detectedFrom: 'package.json (dev script)'\n }\n }\n if (scripts.start) {\n return {\n framework: 'Node.js',\n command: ['npm', 'start'],\n confidence: 'low',\n detectedFrom: 'package.json (start script)'\n }\n }\n }\n\n // Check for requirements.txt (Python projects)\n const requirementsPath = path.join(cwd, 'requirements.txt')\n if (fs.existsSync(requirementsPath)) {\n const requirements = fs.readFileSync(requirementsPath, 'utf-8')\n\n // Django\n if (requirements.includes('Django') || requirements.includes('django')) {\n const managePy = path.join(cwd, 'manage.py')\n if (fs.existsSync(managePy)) {\n return {\n framework: 'Django',\n command: ['python', 'manage.py', 'runserver'],\n confidence: 'high',\n detectedFrom: 'requirements.txt (Django) + manage.py'\n }\n }\n return {\n framework: 'Django',\n command: ['python', 'manage.py', 'runserver'],\n confidence: 'medium',\n detectedFrom: 'requirements.txt (Django)'\n }\n }\n\n // Flask\n if (requirements.includes('Flask') || requirements.includes('flask')) {\n // Look for common Flask app files\n const appFiles = ['app.py', 'application.py', 'wsgi.py']\n for (const file of appFiles) {\n if (fs.existsSync(path.join(cwd, file))) {\n return {\n framework: 'Flask',\n command: ['flask', 'run'],\n confidence: 'high',\n detectedFrom: `requirements.txt (Flask) + ${file}`\n }\n }\n }\n return {\n framework: 'Flask',\n command: ['flask', 'run'],\n confidence: 'medium',\n detectedFrom: 'requirements.txt (Flask)'\n }\n }\n\n // FastAPI / Uvicorn\n if (requirements.includes('fastapi') || requirements.includes('uvicorn')) {\n return {\n framework: 'FastAPI',\n command: ['uvicorn', 'main:app', '--reload'],\n confidence: 'medium',\n detectedFrom: 'requirements.txt (fastapi/uvicorn)'\n }\n }\n }\n\n // Check for Gemfile (Ruby projects)\n const gemfilePath = path.join(cwd, 'Gemfile')\n if (fs.existsSync(gemfilePath)) {\n const gemfile = fs.readFileSync(gemfilePath, 'utf-8')\n\n // Rails\n if (gemfile.includes('rails')) {\n return {\n framework: 'Rails',\n command: ['rails', 'server'],\n confidence: 'high',\n detectedFrom: 'Gemfile (rails)'\n }\n }\n\n // Sinatra\n if (gemfile.includes('sinatra')) {\n return {\n framework: 'Sinatra',\n command: ['ruby', 'app.rb'],\n confidence: 'medium',\n detectedFrom: 'Gemfile (sinatra)'\n }\n }\n }\n\n // Check for go.mod (Go projects)\n const goModPath = path.join(cwd, 'go.mod')\n if (fs.existsSync(goModPath)) {\n return {\n framework: 'Go',\n command: ['go', 'run', '.'],\n confidence: 'medium',\n detectedFrom: 'go.mod'\n }\n }\n\n // Check for Cargo.toml (Rust projects)\n const cargoTomlPath = path.join(cwd, 'Cargo.toml')\n if (fs.existsSync(cargoTomlPath)) {\n return {\n framework: 'Rust',\n command: ['cargo', 'run'],\n confidence: 'medium',\n detectedFrom: 'Cargo.toml'\n }\n }\n\n // No framework detected\n return null\n}\n\n/**\n * Suggest command based on user-provided command or auto-detection\n * @param providedCommand - Command provided by user (e.g., [\"npm\", \"run\", \"dev\"])\n * @param cwd - Working directory\n * @returns Final command to use\n */\nexport async function resolveDevCommand(\n providedCommand: string[] | null,\n cwd: string = process.cwd()\n): Promise<{ command: string[]; detection: FrameworkDetection | null }> {\n // If user provided command, use it as-is\n if (providedCommand && providedCommand.length > 0) {\n return { command: providedCommand, detection: null }\n }\n\n // Auto-detect framework\n const detection = await detectFramework(cwd)\n if (detection) {\n return { command: detection.command, detection }\n }\n\n // No detection - default to npm run dev\n return {\n command: ['npm', 'run', 'dev'],\n detection: {\n framework: 'Unknown',\n command: ['npm', 'run', 'dev'],\n confidence: 'low',\n detectedFrom: 'default fallback'\n }\n }\n}\n","/**\n * Terminal Output Formatting (CLI-specific)\n *\n * This module handles terminal output formatting.\n * CLI-specific - not exported from core/index.ts\n */\n\nimport chalk from 'chalk'\nimport ora, { Ora } from 'ora'\nimport { ErrorCode, ExecutionResult } from '@episoda/core'\n\n/**\n * Format a git execution result for terminal output\n */\nexport function formatResult(result: ExecutionResult): string {\n if (result.success) {\n return chalk.green('✓ ' + (result.output || 'Success'))\n }\n\n let message = chalk.red('✗ Failed')\n if (result.error) {\n message += `: ${formatError(result.error, result.details)}`\n }\n return message\n}\n\n/**\n * Format an error code with details for terminal output\n */\nexport function formatError(code: ErrorCode, details?: Record<string, any>): string {\n const errorMessages: Record<ErrorCode, string> = {\n GIT_NOT_INSTALLED: 'Git is not installed',\n NOT_GIT_REPO: 'Not a git repository',\n MERGE_CONFLICT: 'Merge conflict detected',\n REBASE_CONFLICT: 'Rebase conflict - resolve conflicts or abort rebase',\n UNCOMMITTED_CHANGES: 'You have uncommitted changes',\n NETWORK_ERROR: 'Network error',\n AUTH_FAILURE: 'Authentication failed',\n BRANCH_NOT_FOUND: 'Branch not found',\n BRANCH_ALREADY_EXISTS: 'Branch already exists',\n PUSH_REJECTED: 'Push rejected',\n COMMAND_TIMEOUT: 'Command timed out',\n UNKNOWN_ERROR: 'Unknown error'\n }\n\n let message = chalk.red(errorMessages[code] || code)\n\n if (details) {\n if (details.uncommittedFiles && details.uncommittedFiles.length > 0) {\n message += chalk.yellow('\\n Uncommitted files: ' + details.uncommittedFiles.join(', '))\n }\n if (details.conflictingFiles && details.conflictingFiles.length > 0) {\n message += chalk.yellow('\\n Conflicting files: ' + details.conflictingFiles.join(', '))\n }\n if (details.branchName) {\n message += chalk.cyan('\\n Branch: ' + details.branchName)\n }\n }\n\n return message\n}\n\n/**\n * Create a spinner for long-running operations\n */\nexport function createSpinner(text: string): Ora {\n return ora(text)\n}\n\n/**\n * Print status messages\n */\nexport const status = {\n success: (message: string) => console.log(chalk.green('✓'), message),\n error: (message: string) => console.log(chalk.red('✗'), message),\n warning: (message: string) => console.log(chalk.yellow('⚠'), message),\n info: (message: string) => console.log(chalk.blue('ℹ'), message),\n debug: (message: string) => {\n if (process.env.DEBUG) {\n console.log(chalk.gray('⚙'), chalk.gray(message))\n }\n },\n // EP606: Progress indicator that overwrites the current line\n progress: (message: string) => {\n process.stdout.write(`\\r${chalk.blue('⏳')} ${message}`)\n }\n}\n\n/**\n * Print a header with framework detection info\n */\nexport function printFrameworkDetection(framework: string, command: string[], confidence: string) {\n console.log(chalk.bold('\\n🔍 Framework Detection'))\n console.log(chalk.cyan(' Framework:'), framework)\n console.log(chalk.cyan(' Command:'), command.join(' '))\n console.log(chalk.cyan(' Confidence:'), confidence)\n console.log()\n}\n\n/**\n * Print connection status\n */\nexport function printConnectionStatus(connected: boolean, projectId?: string) {\n if (connected) {\n status.success(`Connected to episoda.dev${projectId ? ` (${projectId})` : ''}`)\n } else {\n status.warning('Not connected to episoda.dev')\n }\n}\n\n/**\n * Print dev server status\n */\nexport function printDevServerStatus(running: boolean, pid?: number) {\n if (running) {\n status.success(`Dev server running${pid ? ` (PID: ${pid})` : ''}`)\n } else {\n status.warning('Dev server stopped')\n }\n}\n\n/**\n * Print git command execution\n */\nexport function printGitCommand(command: string) {\n status.info(chalk.gray(`Executing: ${command}`))\n}\n","/**\n * Daemon lifecycle management\n *\n * Manages the Episoda daemon process lifecycle:\n * - Spawning daemon in detached mode\n * - Checking daemon status\n * - Stopping daemon gracefully\n * - PID file management\n *\n * Ensures only one daemon runs per user.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { spawn, execSync } from 'child_process'\nimport { getConfigDir } from '@episoda/core'\n\n/**\n * EP734: Kill all stale Episoda processes\n *\n * Finds and kills any existing episoda-related processes to ensure\n * a clean slate before starting a new daemon. This prevents:\n * - Multiple daemon processes running simultaneously\n * - Stale `episoda dev` foreground processes from previous sessions\n * - Orphaned node processes running daemon-process.js\n *\n * @returns Number of processes killed\n */\nexport function killAllEpisodaProcesses(): number {\n const currentPid = process.pid\n let killedCount = 0\n\n // EP734: Windows not supported for process cleanup - skip with warning\n if (process.platform === 'win32') {\n console.warn('[Cleanup] Process cleanup not supported on Windows - skipping')\n return 0\n }\n\n try {\n // Find all episoda-related processes using ps (Unix/macOS only)\n // Patterns: 'episoda dev', 'daemon-process.js', 'episoda status', 'ws-server.js'\n // EP797: Added ws-server.js to catch orphaned WebSocket server processes\n const psOutput = execSync(\n 'ps aux | grep -E \"(episoda (dev|status|stop)|daemon-process\\\\.js|node ws-server\\\\.js)\" | grep -v grep || true',\n { encoding: 'utf-8', timeout: 5000 }\n )\n\n const lines = psOutput.trim().split('\\n').filter(line => line.length > 0)\n\n for (const line of lines) {\n // Parse PID from ps aux output (second column)\n const parts = line.trim().split(/\\s+/)\n if (parts.length < 2) continue\n\n const pid = parseInt(parts[1], 10)\n if (isNaN(pid) || pid === currentPid) continue\n\n try {\n process.kill(pid, 'SIGTERM')\n killedCount++\n console.log(`[Cleanup] Killed stale process PID ${pid}`)\n } catch (err) {\n // Process might have already exited, ignore\n }\n }\n\n // Give processes a moment to terminate\n if (killedCount > 0) {\n execSync('sleep 0.5', { timeout: 2000 })\n }\n\n // Clean up stale files\n const configDir = getConfigDir()\n const pidPath = path.join(configDir, 'daemon.pid')\n const sockPath = path.join(configDir, 'daemon.sock')\n\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n if (fs.existsSync(sockPath)) {\n fs.unlinkSync(sockPath)\n }\n\n } catch (error) {\n // Non-fatal - best effort cleanup\n console.warn('[Cleanup] Error during process cleanup:', error instanceof Error ? error.message : error)\n }\n\n return killedCount\n}\n\n/**\n * Get path to daemon PID file\n */\nexport function getPidFilePath(): string {\n return path.join(getConfigDir(), 'daemon.pid')\n}\n\n/**\n * Check if daemon is running\n *\n * Reads PID from file and checks if process exists.\n *\n * @returns PID if running, null if not\n */\nexport function isDaemonRunning(): number | null {\n const pidPath = getPidFilePath()\n\n try {\n if (!fs.existsSync(pidPath)) {\n return null\n }\n\n const pidStr = fs.readFileSync(pidPath, 'utf-8').trim()\n const pid = parseInt(pidStr, 10)\n\n if (isNaN(pid)) {\n // Invalid PID, clean up file\n fs.unlinkSync(pidPath)\n return null\n }\n\n // Check if process exists\n try {\n // Sending signal 0 checks existence without killing\n process.kill(pid, 0)\n return pid\n } catch (error) {\n // Process doesn't exist, clean up stale PID file\n fs.unlinkSync(pidPath)\n return null\n }\n } catch (error) {\n console.error('Error checking daemon status:', error)\n return null\n }\n}\n\n/**\n * Start the daemon process\n *\n * Spawns daemon in detached mode. Daemon survives terminal close.\n *\n * @throws Error if daemon already running or spawn fails\n */\nexport async function startDaemon(): Promise<number> {\n // Check if already running\n const existingPid = isDaemonRunning()\n if (existingPid) {\n throw new Error(`Daemon already running (PID: ${existingPid})`)\n }\n\n // Ensure config directory exists\n const configDir = getConfigDir()\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true })\n }\n\n // Path to daemon entry point\n // When built: dist/daemon/daemon-process.js (tsup preserves directory structure)\n // __dirname is dist/ when running from bundled index.js\n const daemonScript = path.join(__dirname, 'daemon', 'daemon-process.js')\n\n if (!fs.existsSync(daemonScript)) {\n throw new Error(`Daemon script not found: ${daemonScript}. Make sure CLI is built.`)\n }\n\n // Spawn daemon as detached process\n const child = spawn('node', [daemonScript], {\n detached: true, // Run independently of parent\n stdio: 'ignore', // Don't inherit stdio (prevents hanging)\n env: {\n ...process.env,\n EPISODA_DAEMON_MODE: '1', // Signal to daemon it's running in daemon mode\n },\n })\n\n // Detach from parent process\n child.unref()\n\n const pid = child.pid!\n\n // Save PID to file\n const pidPath = getPidFilePath()\n fs.writeFileSync(pidPath, pid.toString(), 'utf-8')\n\n // Give daemon a moment to start\n await new Promise(resolve => setTimeout(resolve, 500))\n\n // Verify daemon actually started\n const runningPid = isDaemonRunning()\n if (!runningPid) {\n throw new Error('Daemon failed to start')\n }\n\n return pid\n}\n\n/**\n * Stop the daemon process\n *\n * Sends SIGTERM for graceful shutdown. If daemon doesn't stop\n * within timeout, sends SIGKILL.\n *\n * @param timeout Milliseconds to wait before SIGKILL (default: 5000)\n * @returns true if stopped, false if wasn't running\n */\nexport async function stopDaemon(timeout: number = 5000): Promise<boolean> {\n const pid = isDaemonRunning()\n if (!pid) {\n // Clean up PID file just in case\n const pidPath = getPidFilePath()\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n return false\n }\n\n try {\n // Send SIGTERM for graceful shutdown\n process.kill(pid, 'SIGTERM')\n\n // Wait for process to exit\n const startTime = Date.now()\n while (Date.now() - startTime < timeout) {\n try {\n process.kill(pid, 0) // Check if still alive\n await new Promise(resolve => setTimeout(resolve, 100))\n } catch (error) {\n // Process exited\n const pidPath = getPidFilePath()\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n return true\n }\n }\n\n // Timeout reached, force kill\n console.warn(`Daemon didn't stop gracefully, forcing shutdown (PID: ${pid})`)\n process.kill(pid, 'SIGKILL')\n\n // Clean up PID file\n const pidPath = getPidFilePath()\n if (fs.existsSync(pidPath)) {\n fs.unlinkSync(pidPath)\n }\n\n return true\n } catch (error) {\n console.error('Error stopping daemon:', error)\n return false\n }\n}\n\n/**\n * Restart the daemon\n *\n * Stops existing daemon and starts new one.\n *\n * @returns PID of new daemon\n */\nexport async function restartDaemon(): Promise<number> {\n await stopDaemon()\n return startDaemon()\n}\n\n/**\n * Get daemon status information\n *\n * @returns Status object with running state and PID\n */\nexport function getDaemonStatus(): { running: boolean; pid: number | null } {\n const pid = isDaemonRunning()\n return {\n running: pid !== null,\n pid,\n }\n}\n","/**\n * IPC Client - Used by CLI commands\n *\n * Sends commands to daemon via Unix domain socket.\n * Handles request/response communication.\n *\n * Socket location: ~/.episoda/daemon.sock\n */\n\nimport * as net from 'net'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport { getConfigDir } from '@episoda/core'\n\nconst getSocketPath = () => path.join(getConfigDir(), 'daemon.sock')\nconst DEFAULT_TIMEOUT = 15000 // 15 seconds (EP606: increased for device registration)\n\nexport interface IPCRequest {\n id: string\n command: string\n params?: any\n}\n\nexport interface IPCResponse {\n id: string\n success: boolean\n data?: any\n error?: string\n}\n\n/**\n * Send command to daemon\n *\n * Connects to daemon socket, sends command, waits for response.\n *\n * @param command Command name\n * @param params Command parameters\n * @param timeout Timeout in milliseconds (default: 15000)\n * @returns Response data\n * @throws Error if daemon not running or command fails\n */\nexport async function sendCommand(\n command: string,\n params?: any,\n timeout: number = DEFAULT_TIMEOUT\n): Promise<any> {\n return new Promise((resolve, reject) => {\n const socket = net.createConnection(getSocketPath())\n const requestId = crypto.randomUUID()\n let buffer = ''\n let timeoutHandle: NodeJS.Timeout\n\n // Set timeout\n timeoutHandle = setTimeout(() => {\n socket.destroy()\n reject(new Error(`Command timed out after ${timeout}ms`))\n }, timeout)\n\n socket.on('connect', () => {\n // Send request\n const request: IPCRequest = {\n id: requestId,\n command,\n params,\n }\n\n socket.write(JSON.stringify(request) + '\\n')\n })\n\n socket.on('data', (chunk) => {\n buffer += chunk.toString()\n\n // Check for complete response (delimited by newline)\n const newlineIndex = buffer.indexOf('\\n')\n if (newlineIndex === -1) return\n\n // Extract response\n const message = buffer.slice(0, newlineIndex)\n\n try {\n const response = JSON.parse(message) as IPCResponse\n\n // Verify response ID matches\n if (response.id !== requestId) {\n reject(new Error('Response ID mismatch'))\n return\n }\n\n clearTimeout(timeoutHandle)\n socket.end()\n\n if (response.success) {\n resolve(response.data)\n } else {\n reject(new Error(response.error || 'Command failed'))\n }\n } catch (error) {\n clearTimeout(timeoutHandle)\n socket.end()\n reject(new Error('Invalid response from daemon'))\n }\n })\n\n socket.on('error', (error) => {\n clearTimeout(timeoutHandle)\n\n // Check for common errors\n if ((error as any).code === 'ENOENT' || (error as any).code === 'ECONNREFUSED') {\n reject(new Error('Daemon is not running. Start it with: episoda dev'))\n } else {\n reject(error)\n }\n })\n\n socket.on('timeout', () => {\n clearTimeout(timeoutHandle)\n socket.destroy()\n reject(new Error('Connection timeout'))\n })\n })\n}\n\n/**\n * Check if daemon is reachable via IPC\n *\n * @returns true if daemon is running and responding\n */\nexport async function isDaemonReachable(): Promise<boolean> {\n try {\n await sendCommand('ping', {}, 1000)\n return true\n } catch (error) {\n return false\n }\n}\n\n/**\n * Add project to daemon\n *\n * EP734: Now blocking - waits for WebSocket connection to complete\n *\n * @param projectId Supabase project ID\n * @param projectPath Absolute path to project\n * @returns Connection result with success/error info\n */\nexport async function addProject(projectId: string, projectPath: string): Promise<{\n success: boolean\n connected: boolean\n error?: string\n}> {\n // EP734: Increased timeout to 30s to allow for WebSocket connection\n return await sendCommand('add-project', { projectId, projectPath }, 30000)\n}\n\n/**\n * Remove project from daemon\n *\n * @param projectPath Absolute path to project\n */\nexport async function removeProject(projectPath: string): Promise<void> {\n await sendCommand('remove-project', { projectPath })\n}\n\n/**\n * Get daemon status\n *\n * EP738: Added hostname, platform, arch for status command (HTTP server removed)\n *\n * @returns Status information including connected projects and device info\n */\nexport async function getStatus(): Promise<{\n running: boolean\n machineId: string\n deviceId?: string\n hostname: string\n platform: string\n arch: string\n projects: Array<{\n id: string\n path: string\n name: string\n connected: boolean\n }>\n}> {\n return await sendCommand('status')\n}\n\n/**\n * Request daemon to connect to a project's WebSocket\n *\n * @param projectPath Absolute path to project\n */\nexport async function connectProject(projectPath: string): Promise<void> {\n await sendCommand('connect-project', { projectPath })\n}\n\n/**\n * Request daemon to disconnect from a project's WebSocket\n *\n * @param projectPath Absolute path to project\n */\nexport async function disconnectProject(projectPath: string): Promise<void> {\n await sendCommand('disconnect-project', { projectPath })\n}\n\n/**\n * Request daemon shutdown\n */\nexport async function shutdownDaemon(): Promise<void> {\n await sendCommand('shutdown', {}, 2000)\n}\n\n/**\n * EP805: Verify connection health\n *\n * Checks if connections are actually healthy (in both connections Map and liveConnections Set).\n * Useful for detecting stale connections where WebSocket died but entry persists.\n *\n * @returns Health status with per-project details\n */\nexport async function verifyHealth(): Promise<{\n totalProjects: number\n healthyConnections: number\n staleConnections: number\n projects: Array<{\n id: string\n path: string\n name: string\n inConnectionsMap: boolean\n inLiveConnections: boolean\n isHealthy: boolean\n }>\n}> {\n return await sendCommand('verify-health')\n}\n\n// EP734: Removed getConnectionStatus - no longer needed with blocking add-project\n","/**\n * Port availability checker\n */\n\nimport * as net from 'net'\n\n/**\n * Check if a port is in use\n * @param port Port number to check\n * @returns true if port is in use, false otherwise\n */\nexport async function isPortInUse(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer()\n\n server.once('error', (err: any) => {\n if (err.code === 'EADDRINUSE') {\n resolve(true)\n } else {\n resolve(false)\n }\n })\n\n server.once('listening', () => {\n server.close()\n resolve(false)\n })\n\n // Don't specify host - bind to all interfaces to detect any listener\n // This matches how Next.js and other dev servers bind\n server.listen(port)\n })\n}\n\n/**\n * Get the server port from config or default\n * @returns Port number\n */\nexport function getServerPort(): number {\n // Check environment variable\n if (process.env.PORT) {\n return parseInt(process.env.PORT, 10)\n }\n\n // Default to 3000 (Next.js default)\n return 3000\n}\n","/**\n * `episoda auth` command (K722)\n *\n * Combines OAuth device flow with automatic configuration:\n * 1. Initiates OAuth device flow (gets device_code and user_code)\n * 2. Opens browser for user to authorize and select project\n * 3. Monitors SSE stream for authorization completion\n * 4. Exchanges device_code for access_token\n * 5. Saves complete config to ~/.episoda/config.json\n * 6. EP548: Installs git credential helper for seamless GitHub auth\n *\n * This replaces the old two-step flow (episoda init + setup token).\n * Now users just run `episoda auth` and select their project in the browser.\n */\n\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { spawn, execSync } from 'child_process'\nimport { saveConfig, VERSION, getConfigPath } from '@episoda/core'\nimport { status } from '../output'\nimport { getMachineId } from '../daemon/machine-id'\nimport { generateCredentialHelperScript } from '../git-helpers/git-credential-helper'\n\ninterface DeviceAuthorizationResponse {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n expires_in: number\n interval: number\n}\n\ninterface TokenResponse {\n access_token: string\n token_type: 'Bearer'\n expires_in: number\n refresh_token: string\n user_id: string\n workspace_id: string\n workspace_uid: string\n project_id: string\n project_uid: string\n}\n\ninterface SSEMessage {\n status: 'pending' | 'authorized' | 'denied' | 'expired'\n message?: string\n}\n\nexport interface AuthCommandOptions {\n apiUrl?: string\n}\n\n/**\n * Auth command - OAuth device flow with browser-based project selection\n */\nexport async function authCommand(options: AuthCommandOptions = {}): Promise<void> {\n const apiUrl = options.apiUrl || process.env.EPISODA_API_URL || 'https://episoda.dev'\n\n status.info('Initializing Episoda CLI...')\n status.info('')\n\n // EP812: Get machine ID early so it can be passed through the entire OAuth flow\n const machineId = await getMachineId()\n\n // Step 1: Initiate device flow\n status.info('Step 1/3: Requesting authorization code...')\n const deviceAuth = await initiateDeviceFlow(apiUrl, machineId)\n\n status.success(`✓ Authorization code received: ${deviceAuth.user_code}`)\n status.info('')\n\n // Step 2: Open browser and monitor for authorization\n status.info('Step 2/3: Opening browser for authorization...')\n status.info(` Visit: ${deviceAuth.verification_uri_complete}`)\n status.info(` User code: ${deviceAuth.user_code}`)\n status.info('')\n\n // Open browser\n openBrowser(deviceAuth.verification_uri_complete)\n\n status.info('Waiting for authorization...')\n const authorized = await monitorAuthorization(apiUrl, deviceAuth.device_code, deviceAuth.expires_in)\n\n if (!authorized) {\n throw new Error('Authorization failed or timed out')\n }\n\n status.success('✓ Authorization successful!')\n status.info('')\n\n // Step 3: Exchange device code for access token\n status.info('Step 3/3: Exchanging authorization for access token...')\n\n // EP609: machineId already obtained in Step 1 (EP812 moved it earlier)\n const tokenResponse = await exchangeDeviceCode(apiUrl, deviceAuth.device_code, machineId)\n\n status.success('✓ Access token received')\n status.info(` Project: ${tokenResponse.project_uid}`)\n status.info(` Workspace: ${tokenResponse.workspace_uid}`)\n status.info('')\n\n // Save configuration\n await saveConfig({\n project_id: tokenResponse.project_id,\n user_id: tokenResponse.user_id,\n workspace_id: tokenResponse.workspace_id,\n access_token: tokenResponse.access_token,\n api_url: apiUrl,\n cli_version: VERSION,\n })\n\n status.success(`✓ Configuration saved to ${getConfigPath()}`)\n status.info('')\n\n // EP548: Install git credential helper\n status.info('Installing git credential helper...')\n const credentialHelperInstalled = await installGitCredentialHelper(apiUrl)\n\n if (credentialHelperInstalled) {\n status.success('✓ Git credential helper installed')\n status.info(' Git operations will now use Episoda authentication automatically.')\n } else {\n status.warning('⚠ Git credential helper could not be installed automatically.')\n status.info(' You can still use git with manual token setup.')\n }\n status.info('')\n\n status.success('CLI initialized successfully!')\n status.info('')\n status.info('Next steps:')\n status.info(' • Start daemon: episoda dev')\n status.info(' • Check status: episoda status')\n status.info(' • Git operations will use Episoda auth automatically')\n status.info('')\n}\n\n/**\n * Step 1: Initiate OAuth device flow\n * EP812: Now includes machine_id so it can be stored with the device authorization\n * and used to set a browser pairing cookie when the user authorizes.\n */\nasync function initiateDeviceFlow(apiUrl: string, machineId: string): Promise<DeviceAuthorizationResponse> {\n const response = await fetch(`${apiUrl}/api/oauth/code`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n machine_id: machineId, // EP812: For cookie-based device pairing\n }),\n })\n\n if (!response.ok) {\n const error = await response.json() as { error?: string; error_description?: string }\n throw new Error(error.error_description || error.error || 'Failed to initiate device flow')\n }\n\n return await response.json() as DeviceAuthorizationResponse\n}\n\n/**\n * Step 2: Monitor authorization via SSE stream\n */\nasync function monitorAuthorization(\n apiUrl: string,\n deviceCode: string,\n expiresIn: number\n): Promise<boolean> {\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n status.error('Authorization timed out')\n resolve(false)\n }, expiresIn * 1000) // Convert seconds to milliseconds\n\n // Start SSE stream\n const url = `${apiUrl}/api/oauth/authorize-stream?device_code=${deviceCode}`\n\n // Use curl to handle SSE stream (simpler than implementing SSE parser in Node)\n const curlProcess = spawn('curl', ['-N', url])\n\n let buffer = ''\n\n curlProcess.stdout.on('data', (chunk: Buffer) => {\n buffer += chunk.toString()\n\n // Process complete SSE messages\n const messages = buffer.split('\\n\\n')\n buffer = messages.pop() || '' // Keep incomplete message in buffer\n\n for (const message of messages) {\n if (!message.trim()) continue\n\n // Parse SSE message\n const lines = message.split('\\n')\n let eventType = ''\n let data = ''\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n eventType = line.substring(7)\n } else if (line.startsWith('data: ')) {\n data = line.substring(6)\n }\n }\n\n if (!eventType) continue\n\n try {\n // Handle based on event type\n if (eventType === 'authorized') {\n clearTimeout(timeout)\n curlProcess.kill()\n resolve(true)\n return\n } else if (eventType === 'denied') {\n clearTimeout(timeout)\n curlProcess.kill()\n status.error('Authorization denied by user')\n resolve(false)\n return\n } else if (eventType === 'expired') {\n clearTimeout(timeout)\n curlProcess.kill()\n status.error('Authorization code expired')\n resolve(false)\n return\n } else if (eventType === 'error') {\n const errorData = JSON.parse(data) as { error?: string; error_description?: string }\n clearTimeout(timeout)\n curlProcess.kill()\n status.error(`Authorization error: ${errorData.error_description || errorData.error || 'Unknown error'}`)\n resolve(false)\n return\n }\n // For 'pending' events, continue waiting\n } catch (error) {\n // Ignore parse errors, continue monitoring\n }\n }\n })\n\n curlProcess.on('error', (error) => {\n clearTimeout(timeout)\n status.error(`Failed to monitor authorization: ${error.message}`)\n resolve(false)\n })\n\n curlProcess.on('close', (code) => {\n clearTimeout(timeout)\n if (code !== 0 && code !== null) {\n status.error(`Authorization monitoring failed with code ${code}`)\n resolve(false)\n }\n })\n })\n}\n\n/**\n * Step 3: Exchange device code for access token\n * EP609: Now includes machine_id to associate token with device\n */\nasync function exchangeDeviceCode(apiUrl: string, deviceCode: string, machineId: string): Promise<TokenResponse> {\n const response = await fetch(`${apiUrl}/api/oauth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n machine_id: machineId,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json() as { error?: string; error_description?: string }\n throw new Error(error.error_description || error.error || 'Failed to exchange device code')\n }\n\n const tokenResponse = await response.json() as TokenResponse\n\n // Validate that we got all required fields\n if (!tokenResponse.access_token || !tokenResponse.user_id || !tokenResponse.workspace_id || !tokenResponse.project_id) {\n throw new Error('Incomplete token response from server')\n }\n\n return tokenResponse\n}\n\n/**\n * Open browser to verification URL\n */\nfunction openBrowser(url: string): void {\n const platform = os.platform()\n\n let command: string\n let args: string[]\n\n switch (platform) {\n case 'darwin':\n command = 'open'\n args = [url]\n break\n case 'win32':\n command = 'cmd'\n args = ['/c', 'start', url]\n break\n default: // Linux and others\n command = 'xdg-open'\n args = [url]\n break\n }\n\n try {\n spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n }).unref()\n } catch (error) {\n status.warning(`Could not open browser automatically. Please visit: ${url}`)\n }\n}\n\n/**\n * EP548: Install git credential helper\n *\n * This function:\n * 1. Creates ~/.episoda/bin directory\n * 2. Writes the credential helper script\n * 3. Makes it executable\n * 4. Adds ~/.episoda/bin to PATH if needed\n * 5. Configures git to use the credential helper\n *\n * The credential helper is added to git's credential helper chain,\n * so existing credential helpers are preserved.\n */\nasync function installGitCredentialHelper(apiUrl: string): Promise<boolean> {\n try {\n const homeDir = os.homedir()\n const episodaBinDir = path.join(homeDir, '.episoda', 'bin')\n const helperPath = path.join(episodaBinDir, 'git-credential-episoda')\n\n // 1. Create bin directory\n fs.mkdirSync(episodaBinDir, { recursive: true })\n\n // 2. Write credential helper script\n const scriptContent = generateCredentialHelperScript(apiUrl)\n fs.writeFileSync(helperPath, scriptContent, { mode: 0o755 })\n\n // 3. Verify it's executable\n try {\n fs.accessSync(helperPath, fs.constants.X_OK)\n } catch {\n // On Windows, this might fail but the script should still work\n }\n\n // 4. Configure git to use the credential helper\n // Check if already configured (use --get-all to check all helpers in chain)\n try {\n const allHelpers = execSync('git config --global --get-all credential.helper', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe']\n }).trim().split('\\n')\n\n // If this exact helper path is already configured, we're done\n // This prevents duplicate entries if user runs `episoda auth` multiple times\n if (allHelpers.some(h => h.includes(helperPath) || h.includes('git-credential-episoda'))) {\n return true\n }\n } catch {\n // No credential helper configured yet, that's fine\n }\n\n // Add episoda credential helper to the chain\n // Using --add ensures we don't overwrite existing helpers\n execSync(`git config --global --add credential.helper \"${helperPath}\"`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe']\n })\n\n // 5. Add to PATH by updating shell profile if needed\n updateShellProfile(episodaBinDir)\n\n return true\n } catch (error: any) {\n // Log error but don't fail the auth flow\n status.warning(`Could not install credential helper: ${error.message}`)\n return false\n }\n}\n\n/**\n * Update shell profile to include ~/.episoda/bin in PATH\n */\nfunction updateShellProfile(binDir: string): void {\n const platform = os.platform()\n\n if (platform === 'win32') {\n // Windows uses a different mechanism (system PATH)\n // Skip for now - the full path in git config should work\n return\n }\n\n // For Unix-like systems, check common shell profiles\n const homeDir = os.homedir()\n const profiles = [\n path.join(homeDir, '.bashrc'),\n path.join(homeDir, '.zshrc'),\n path.join(homeDir, '.profile'),\n ]\n\n const exportLine = `export PATH=\"${binDir}:$PATH\" # Added by episoda auth`\n\n for (const profile of profiles) {\n try {\n if (fs.existsSync(profile)) {\n const content = fs.readFileSync(profile, 'utf8')\n\n // Check if already added\n if (content.includes('.episoda/bin')) {\n continue\n }\n\n // Append export line\n fs.appendFileSync(profile, `\\n# Episoda CLI\\n${exportLine}\\n`)\n }\n } catch {\n // Ignore errors - PATH update is optional\n }\n }\n}\n","/**\n * Machine ID generation and management\n *\n * EP812: Changed to use UUID format for consistency across the system.\n * The machine ID is now a proper UUID that can be used directly as the\n * database local_machine.id, eliminating the need for separate TEXT and UUID IDs.\n *\n * Format: Standard UUID v4 (e.g., \"550e8400-e29b-41d4-a716-446655440000\")\n *\n * EP731: Derives UUID deterministically from hardware UUID to prevent duplicate\n * device registrations when ~/.episoda syncs between devices via iCloud/Dropbox.\n *\n * Properties:\n * - Stable across daemon restarts\n * - Unique per PHYSICAL machine (derived from hardware UUID)\n * - Standard UUID format (works as database PK)\n * - Survives OS reboots\n * - Cannot sync between devices (hardware-based)\n *\n * Migration: Existing TEXT machine IDs (e.g., \"hostname-a3f2b1c4\") are\n * automatically migrated to UUID format on first read.\n */\n\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport { execSync } from 'child_process'\nimport { getConfigDir } from '@episoda/core'\n\n/**\n * Check if a string is a valid UUID format\n */\nfunction isValidUUID(str: string): boolean {\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n return uuidRegex.test(str)\n}\n\n/**\n * Get or generate machine ID\n *\n * EP812: Now returns a UUID format machine ID that can be used directly\n * as the database local_machine.id.\n *\n * Reads from config directory's machine-id if exists, otherwise generates new one\n * and saves it for future use. Migrates old TEXT format IDs to UUID.\n *\n * @returns Machine ID as UUID string\n */\nexport async function getMachineId(): Promise<string> {\n const machineIdPath = path.join(getConfigDir(), 'machine-id')\n\n // Try to read existing machine ID\n try {\n if (fs.existsSync(machineIdPath)) {\n const existingId = fs.readFileSync(machineIdPath, 'utf-8').trim()\n if (existingId) {\n // EP812: Check if already UUID format\n if (isValidUUID(existingId)) {\n return existingId\n }\n // EP812: Migrate old TEXT format to UUID\n // Generate new UUID based on hardware to maintain device uniqueness\n console.log('[MachineId] Migrating legacy machine ID to UUID format...')\n const newUUID = generateMachineId()\n fs.writeFileSync(machineIdPath, newUUID, 'utf-8')\n console.log(`[MachineId] Migrated: ${existingId} → ${newUUID}`)\n return newUUID\n }\n }\n } catch (error) {\n // File doesn't exist or can't be read, generate new one\n }\n\n // Generate new machine ID (UUID format)\n const machineId = generateMachineId()\n\n // Save to file\n try {\n const dir = path.dirname(machineIdPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n fs.writeFileSync(machineIdPath, machineId, 'utf-8')\n } catch (error) {\n console.error('Warning: Could not save machine ID to disk:', error)\n // Continue anyway - machine ID will be regenerated next time\n }\n\n return machineId\n}\n\n/**\n * EP731: Get hardware UUID for the current machine\n *\n * Uses platform-specific methods to get a hardware identifier that is:\n * - Unique per physical machine\n * - Stable across reboots\n * - Cannot sync between devices\n *\n * Falls back to random UUID if hardware UUID cannot be obtained.\n *\n * @returns Hardware UUID string\n */\nfunction getHardwareUUID(): string {\n try {\n if (process.platform === 'darwin') {\n // macOS: Get IOPlatformUUID from ioreg\n const output = execSync(\n 'ioreg -d2 -c IOPlatformExpertDevice | awk -F\\\\\" \\'/IOPlatformUUID/{print $(NF-1)}\\'',\n { encoding: 'utf-8', timeout: 5000 }\n ).trim()\n if (output && output.length > 0) {\n return output\n }\n } else if (process.platform === 'linux') {\n // Linux: Read /etc/machine-id\n if (fs.existsSync('/etc/machine-id')) {\n const machineId = fs.readFileSync('/etc/machine-id', 'utf-8').trim()\n if (machineId && machineId.length > 0) {\n return machineId\n }\n }\n // Fallback: Try /var/lib/dbus/machine-id\n if (fs.existsSync('/var/lib/dbus/machine-id')) {\n const dbusId = fs.readFileSync('/var/lib/dbus/machine-id', 'utf-8').trim()\n if (dbusId && dbusId.length > 0) {\n return dbusId\n }\n }\n } else if (process.platform === 'win32') {\n // Windows: Get UUID from wmic\n const output = execSync('wmic csproduct get uuid', {\n encoding: 'utf-8',\n timeout: 5000\n })\n const lines = output.trim().split('\\n')\n if (lines.length >= 2) {\n const uuid = lines[1].trim()\n if (uuid && uuid.length > 0 && uuid !== 'UUID') {\n return uuid\n }\n }\n }\n } catch (error) {\n // Hardware UUID retrieval failed, will fall back to random\n console.warn('Could not get hardware UUID, using random fallback:', error)\n }\n\n // Fallback: Generate random UUID\n return crypto.randomUUID()\n}\n\n/**\n * Generate a new machine ID as UUID\n *\n * EP812: Now generates a proper UUID that can be used as database PK.\n * EP731: Derives UUID deterministically from hardware UUID to ensure uniqueness\n * per physical machine, even if ~/.episoda syncs between devices.\n *\n * Format: Standard UUID v4\n * Example: \"550e8400-e29b-41d4-a716-446655440000\"\n *\n * @returns Generated machine ID as UUID\n */\nfunction generateMachineId(): string {\n const hwUUID = getHardwareUUID()\n\n // If hardware UUID is already a valid UUID, use it directly\n if (isValidUUID(hwUUID)) {\n return hwUUID.toLowerCase()\n }\n\n // Otherwise, derive a deterministic UUID from the hardware identifier\n // This ensures the same physical machine always gets the same UUID\n const hash = crypto.createHash('sha256').update(hwUUID).digest('hex')\n\n // Format as UUID v4 (but deterministic based on hardware)\n // Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\n // where y is 8, 9, a, or b\n const uuid = [\n hash.slice(0, 8),\n hash.slice(8, 12),\n '4' + hash.slice(13, 16), // Version 4\n ((parseInt(hash.slice(16, 17), 16) & 0x3) | 0x8).toString(16) + hash.slice(17, 20), // Variant\n hash.slice(20, 32)\n ].join('-')\n\n return uuid.toLowerCase()\n}\n\n/**\n * Reset machine ID (force regeneration)\n *\n * Deletes the stored machine ID file, forcing a new ID to be generated\n * on next getMachineId() call.\n *\n * Use case: Developer explicitly wants to change their machine identity\n */\nexport function resetMachineId(): void {\n const machineIdPath = path.join(getConfigDir(), 'machine-id')\n try {\n if (fs.existsSync(machineIdPath)) {\n fs.unlinkSync(machineIdPath)\n }\n } catch (error) {\n throw new Error(`Failed to reset machine ID: ${error}`)\n }\n}\n","/**\n * EP548/EP612: Git Credential Helper Script Generator\n *\n * This module generates the git credential helper script that is installed\n * during `episoda auth`. The script is called by git when credentials are needed.\n *\n * The generated script:\n * 1. Detects the environment (local vs cloud)\n * 2. Calls GET /api/git/credentials with appropriate auth\n * 3. Returns credentials in git credential protocol format\n * 4. Caches tokens locally (5 min TTL) to avoid API calls on every git operation\n *\n * EP612: Removed jq dependency - uses pure bash JSON parsing\n */\n\n/**\n * Generate the credential helper script content\n *\n * The script needs to:\n * - Be standalone (no external dependencies - just bash and curl)\n * - Work with curl (available on all platforms)\n * - Handle both local (OAuth) and cloud (machine ID) auth\n * - Cache tokens to avoid hitting API on every git operation\n */\nexport function generateCredentialHelperScript(apiUrl: string): string {\n // The script uses bash because it's universally available\n // No jq dependency - uses pure bash for JSON parsing\n return `#!/bin/bash\n#\n# Episoda Git Credential Helper\n# EP548/EP612: Unified git authentication for all environments\n#\n# This script is called by git when credentials are needed.\n# It calls the Episoda API to get a fresh GitHub token.\n#\n# Installation: episoda auth\n# Location: ~/.episoda/bin/git-credential-episoda (local)\n# /usr/local/bin/git-credential-episoda (cloud VM)\n#\n# Git credential protocol:\n# - git calls: git-credential-episoda get\n# - input on stdin: protocol=https\\\\nhost=github.com\\\\n\n# - output on stdout: username=x-access-token\\\\npassword=TOKEN\\\\n\n#\n# Dependencies: bash, curl (no jq required)\n#\n\nset -euo pipefail\n\nEPISODA_DIR=\"\\${HOME}/.episoda\"\nCONFIG_FILE=\"\\${EPISODA_DIR}/config.json\"\nCACHE_FILE=\"\\${EPISODA_DIR}/git-token-cache.json\"\nAPI_URL=\"${apiUrl}\"\n\n# Cache TTL in seconds (5 minutes)\nCACHE_TTL=300\n\n# Log function (to stderr so git doesn't see it)\nlog() {\n if [[ \"\\${GIT_CREDENTIAL_EPISODA_DEBUG:-}\" == \"1\" ]]; then\n echo \"[episoda-git] \\$(date '+%H:%M:%S') \\$*\" >&2\n fi\n}\n\n# Error log (always shown)\nerror() {\n echo \"[episoda-git] ERROR: \\$*\" >&2\n}\n\n# Pure bash JSON value extraction (no jq needed)\n# Usage: json_get '{\"foo\":\"bar\"}' \"foo\" -> \"bar\"\n# Handles simple flat JSON and nested paths like \"credentials.username\"\njson_get() {\n local json=\"\\$1\"\n local key=\"\\$2\"\n local value=\"\"\n\n # Handle nested keys (e.g., \"credentials.username\")\n if [[ \"\\$key\" == *.* ]]; then\n local outer=\"\\${key%%.*}\"\n local inner=\"\\${key#*.}\"\n # Extract outer object first, then inner key\n # Match \"outer\":{...} and extract the {...} part\n local nested\n nested=\\$(echo \"\\$json\" | sed -n 's/.*\"'\\$outer'\"[[:space:]]*:[[:space:]]*{\\\\([^}]*\\\\)}.*/\\\\1/p')\n if [[ -n \"\\$nested\" ]]; then\n json_get \"{\\$nested}\" \"\\$inner\"\n return\n fi\n return\n fi\n\n # Simple key extraction: \"key\":\"value\" or \"key\": \"value\"\n # Handle both quoted strings and unquoted values\n value=\\$(echo \"\\$json\" | sed -n 's/.*\"'\\$key'\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/p')\n\n if [[ -n \"\\$value\" ]]; then\n echo \"\\$value\"\n fi\n}\n\n# Check for required dependencies\ncheck_dependencies() {\n if ! command -v curl >/dev/null 2>&1; then\n error \"curl is required but not installed\"\n return 1\n fi\n return 0\n}\n\n# Parse git credential input from stdin\nparse_input() {\n while IFS= read -r line; do\n [[ -z \"\\$line\" ]] && break\n case \"\\$line\" in\n protocol=*) PROTOCOL=\"\\${line#protocol=}\" ;;\n host=*) HOST=\"\\${line#host=}\" ;;\n path=*) PATH_=\"\\${line#path=}\" ;;\n esac\n done\n}\n\n# Parse ISO 8601 date to unix timestamp (cross-platform)\nparse_iso_date() {\n local iso_date=\"\\$1\"\n # Try GNU date first (Linux)\n if date -d \"\\$iso_date\" +%s 2>/dev/null; then\n return\n fi\n # Try BSD date (macOS) - strip timezone for parsing\n local clean_date=\"\\${iso_date%+*}\" # Remove +00:00\n clean_date=\"\\${clean_date%Z}\" # Remove Z\n clean_date=\"\\${clean_date%.*}\" # Remove .milliseconds\n if date -jf \"%Y-%m-%dT%H:%M:%S\" \"\\$clean_date\" +%s 2>/dev/null; then\n return\n fi\n # Fallback: return 0 (expired)\n echo \"0\"\n}\n\n# Check if cached token is still valid\nget_cached_token() {\n if [[ ! -f \"\\$CACHE_FILE\" ]]; then\n log \"No cache file\"\n return 1\n fi\n\n # Read cache file\n local cache_content\n cache_content=\\$(cat \"\\$CACHE_FILE\" 2>/dev/null) || return 1\n\n # Parse cache file using pure bash\n local expires_at\n expires_at=\\$(json_get \"\\$cache_content\" \"expires_at\")\n\n if [[ -z \"\\$expires_at\" ]]; then\n log \"No expires_at in cache\"\n return 1\n fi\n\n # Check if expired (with 60 second buffer)\n local now expires_ts buffer\n now=\\$(date +%s)\n expires_ts=\\$(parse_iso_date \"\\$expires_at\")\n buffer=60\n\n if [[ \\$((expires_ts - buffer)) -le \\$now ]]; then\n log \"Cache expired (expires: \\$expires_at)\"\n return 1\n fi\n\n # Return cached token\n CACHED_TOKEN=\\$(json_get \"\\$cache_content\" \"password\")\n CACHED_USER=\\$(json_get \"\\$cache_content\" \"username\")\n\n if [[ -n \"\\$CACHED_TOKEN\" && -n \"\\$CACHED_USER\" ]]; then\n log \"Using cached token (expires: \\$expires_at)\"\n return 0\n fi\n\n log \"Invalid cache content\"\n return 1\n}\n\n# Save token to cache\nsave_to_cache() {\n local username=\"\\$1\"\n local password=\"\\$2\"\n local expires_at=\"\\$3\"\n\n mkdir -p \"\\$EPISODA_DIR\"\n cat > \"\\$CACHE_FILE\" <<CACHE_EOF\n{\"username\":\"\\$username\",\"password\":\"\\$password\",\"expires_at\":\"\\$expires_at\",\"cached_at\":\"\\$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")\"}\nCACHE_EOF\n chmod 600 \"\\$CACHE_FILE\"\n log \"Token cached until \\$expires_at\"\n}\n\n# Get credentials from Episoda API\nfetch_credentials() {\n local api_url=\"\\${EPISODA_API_URL:-\\${API_URL}}\"\n local response=\"\"\n local http_code=\"\"\n\n # Detect environment - check multiple ways to identify a cloud VM\n local machine_id=\"\"\n\n # Check FLY_MACHINE_ID (set by Fly.io on all machines)\n if [[ -n \"\\${FLY_MACHINE_ID:-}\" ]]; then\n machine_id=\"\\$FLY_MACHINE_ID\"\n log \"Cloud environment detected via FLY_MACHINE_ID: \\$machine_id\"\n # Legacy: check /app/.machine_id file\n elif [[ -f \"/app/.machine_id\" ]]; then\n machine_id=\\$(cat /app/.machine_id)\n log \"Cloud environment detected via /app/.machine_id: \\$machine_id\"\n fi\n\n if [[ -n \"\\$machine_id\" ]]; then\n # Cloud VM: use machine ID header\n log \"Fetching credentials for machine: \\$machine_id\"\n response=\\$(curl -s -w \"\\\\n%{http_code}\" --max-time 10 \"\\${api_url}/api/git/credentials\" \\\\\n -H \"X-Machine-ID: \\$machine_id\" \\\\\n -H \"Content-Type: application/json\" 2>&1) || {\n error \"curl failed: \\$response\"\n return 1\n }\n else\n # Local: use OAuth token from config\n if [[ ! -f \"\\$CONFIG_FILE\" ]]; then\n error \"No config found at \\$CONFIG_FILE. Run 'episoda auth' first.\"\n return 1\n fi\n\n # Parse config using pure bash\n local config_content\n config_content=\\$(cat \"\\$CONFIG_FILE\" 2>/dev/null) || {\n error \"Cannot read config file\"\n return 1\n }\n\n local access_token project_id\n access_token=\\$(json_get \"\\$config_content\" \"access_token\")\n project_id=\\$(json_get \"\\$config_content\" \"project_id\")\n\n if [[ -z \"\\$access_token\" ]]; then\n error \"No access token in config. Run 'episoda auth' to authenticate.\"\n return 1\n fi\n\n log \"Local environment (project: \\$project_id)\"\n response=\\$(curl -s -w \"\\\\n%{http_code}\" --max-time 10 \"\\${api_url}/api/git/credentials\" \\\\\n -H \"Authorization: Bearer \\$access_token\" \\\\\n -H \"X-Project-ID: \\$project_id\" \\\\\n -H \"Content-Type: application/json\" 2>&1) || {\n error \"curl failed: \\$response\"\n return 1\n }\n fi\n\n # Split response and HTTP code\n http_code=\\$(echo \"\\$response\" | tail -n1)\n response=\\$(echo \"\\$response\" | sed '\\$d')\n\n # Check HTTP status\n if [[ \"\\$http_code\" != \"200\" ]]; then\n error \"API returned HTTP \\$http_code\"\n log \"Response: \\$response\"\n return 1\n fi\n\n # Parse response using pure bash\n CRED_USERNAME=\\$(json_get \"\\$response\" \"credentials.username\")\n CRED_PASSWORD=\\$(json_get \"\\$response\" \"credentials.password\")\n CRED_EXPIRES=\\$(json_get \"\\$response\" \"credentials.expires_at\")\n\n if [[ -z \"\\$CRED_USERNAME\" || -z \"\\$CRED_PASSWORD\" ]]; then\n error \"Invalid credentials in response\"\n log \"Response: \\$response\"\n return 1\n fi\n\n # Cache the token\n save_to_cache \"\\$CRED_USERNAME\" \"\\$CRED_PASSWORD\" \"\\$CRED_EXPIRES\"\n\n log \"Credentials fetched successfully\"\n return 0\n}\n\n# Main\nmain() {\n local command=\"\\${1:-}\"\n\n # Check dependencies before processing\n if ! check_dependencies; then\n exit 0 # Exit gracefully so git tries other helpers\n fi\n\n case \"\\$command\" in\n get)\n parse_input\n\n # Only handle github.com\n if [[ \"\\${HOST:-}\" != \"github.com\" ]]; then\n log \"Not handling host: \\${HOST:-unknown}\"\n exit 0\n fi\n\n # Try cache first\n if get_cached_token; then\n echo \"username=\\$CACHED_USER\"\n echo \"password=\\$CACHED_TOKEN\"\n exit 0\n fi\n\n # Fetch fresh credentials\n if fetch_credentials; then\n echo \"username=\\$CRED_USERNAME\"\n echo \"password=\\$CRED_PASSWORD\"\n exit 0\n fi\n\n # Failed - let git try other credential helpers\n log \"Failed to get credentials, falling back to other helpers\"\n exit 0\n ;;\n\n store|erase)\n # We don't store or erase credentials\n exit 0\n ;;\n\n *)\n # Unknown command\n exit 0\n ;;\n esac\n}\n\nmain \"\\$@\"\n`\n}\n\n/**\n * Get the content of the credential helper for embedding in the CLI\n */\nexport const CREDENTIAL_HELPER_SCRIPT = generateCredentialHelperScript('https://episoda.dev')\n","/**\n * `episoda status` command\n *\n * Shows the current CLI connection status and configuration details.\n * EP734: Simplified to only check local daemon state (removed server comparison).\n * EP738: Now uses IPC instead of HTTP (localhost:3002 server removed).\n * EP805: Added --verify flag for explicit connection health check.\n */\n\nimport { loadConfig, getConfigPath } from '@episoda/core'\nimport { getStatus, verifyHealth } from '../ipc/ipc-client'\nimport { status } from '../output'\n\nexport interface StatusOptions {\n verify?: boolean\n}\n\nexport async function statusCommand(options: StatusOptions = {}): Promise<void> {\n status.info('Checking CLI status...')\n status.info('')\n\n // Check if config exists\n const config = await loadConfig()\n\n if (!config) {\n status.error('✗ CLI not initialized')\n status.info('')\n status.info('Run the following to initialize:')\n status.info(' episoda init --project <project_id> --token <setup_token>')\n status.info('')\n status.info('Get a setup token from: Settings > Local Development')\n return\n }\n\n // Show config details\n status.success('✓ CLI initialized')\n status.info('')\n status.info('Configuration:')\n status.info(` Project ID: ${config.project_id}`)\n status.info(` API URL: ${config.api_url}`)\n status.info(` CLI Version: ${config.cli_version}`)\n status.info(` Config file: ${getConfigPath()}`)\n status.info('')\n\n if (!config.access_token || config.access_token === '') {\n status.warning('⚠ Not authenticated')\n status.info(' Run \"episoda auth\" to authenticate.')\n return\n }\n\n // EP738: Query daemon via IPC instead of HTTP\n let daemonStatus: Awaited<ReturnType<typeof getStatus>> | null = null\n try {\n daemonStatus = await getStatus()\n } catch {\n // Daemon not running or IPC failed\n }\n\n if (!daemonStatus) {\n status.warning('⚠ Daemon not running')\n status.info(' Run \"episoda dev\" to start the daemon.')\n return\n }\n\n // Check if any project is connected\n const connectedProject = daemonStatus.projects.find(p => p.connected)\n\n if (connectedProject) {\n status.success('✓ Connected to server')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info(` Project: ${connectedProject.name}`)\n } else if (daemonStatus.projects.length > 0) {\n status.warning('⚠ Daemon running but not connected')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info(' Run \"episoda dev\" in a project directory to connect.')\n } else {\n status.warning('⚠ Daemon running but no projects registered')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(' Run \"episoda dev\" in a project directory to connect.')\n }\n\n // EP805: Verify connection health if --verify flag is set\n if (options.verify) {\n status.info('')\n status.info('Connection Health Check:')\n\n try {\n const health = await verifyHealth()\n\n if (health.staleConnections > 0) {\n status.warning(` ⚠ ${health.staleConnections} stale connection(s) detected`)\n for (const project of health.projects) {\n if (project.inConnectionsMap && !project.inLiveConnections) {\n status.warning(` - ${project.name}: in Map but WebSocket dead`)\n }\n }\n status.info(' Stale connections will be cleaned up on next connect attempt.')\n } else if (health.healthyConnections > 0) {\n status.success(` ✓ All ${health.healthyConnections} connection(s) healthy`)\n } else {\n status.info(' No active connections to verify.')\n }\n } catch (error) {\n status.error(` ✗ Health check failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n}\n","/**\n * `episoda stop` command\n *\n * K722: Stop the Episoda daemon\n * - Sends shutdown signal to daemon via IPC\n * - Waits for daemon to stop gracefully\n * - Falls back to forceful shutdown if needed\n */\n\nimport { status } from '../output'\nimport { isDaemonRunning, stopDaemon } from '../daemon/daemon-manager'\nimport { shutdownDaemon, isDaemonReachable } from '../ipc/ipc-client'\n\nexport interface StopCommandOptions {\n force?: boolean // Force kill without graceful shutdown\n}\n\n/**\n * Execute the `episoda stop` command\n * @param options - Command options\n */\nexport async function stopCommand(options: StopCommandOptions = {}): Promise<void> {\n try {\n // Check if daemon is running\n const pid = isDaemonRunning()\n if (!pid) {\n status.info('Daemon is not running')\n return\n }\n\n status.info(`Stopping daemon (PID: ${pid})...`)\n\n if (options.force) {\n // Force kill\n const stopped = await stopDaemon(1000) // 1 second timeout before SIGKILL\n if (stopped) {\n status.success('Daemon force-stopped')\n } else {\n status.error('Failed to stop daemon')\n process.exit(1)\n }\n } else {\n // Graceful shutdown via IPC\n try {\n // Check if daemon is reachable\n const reachable = await isDaemonReachable()\n if (reachable) {\n // Send shutdown command\n await shutdownDaemon()\n status.success('Daemon stopped')\n } else {\n // Daemon not reachable, force stop\n status.warning('Daemon not responding, forcing shutdown...')\n const stopped = await stopDaemon(1000)\n if (stopped) {\n status.success('Daemon stopped')\n } else {\n status.error('Failed to stop daemon')\n process.exit(1)\n }\n }\n } catch (error) {\n // IPC failed, try direct stop\n status.warning('Failed to communicate with daemon, trying direct shutdown...')\n const stopped = await stopDaemon(5000)\n if (stopped) {\n status.success('Daemon stopped')\n } else {\n status.error(`Failed to stop daemon: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n }\n }\n } catch (error) {\n status.error(`Failed to stop daemon: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n}\n"],"mappings":"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O,SAAS,+BAA+B,QAAwB;AAGrE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAyBE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["exports","exports","status","fs","exports","exports","resolve","exports","exports","getConfigDir","getConfigPath","loadConfig","saveConfig","fs","path","os","exports","exports","import_core","import_core","chalk","fs","path","resolve","pidPath","path","import_core","resolve","import_child_process","path","net","resolve","resolve","fs","path","import_child_process","import_core","fs","path","crypto","import_child_process","import_core","resolve","platform","import_core"]}