episoda 0.2.33 → 0.2.34
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/daemon/daemon-process.js +335 -175
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +660 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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/daemon/worktree-manager.ts","../src/api/machine-settings.ts","../src/utils/bootstrap.ts","../src/commands/auth.ts","../src/daemon/machine-id.ts","../src/git-helpers/git-credential-helper.ts","../src/commands/connect.ts","../src/commands/status.ts","../src/utils/update-checker.ts","../src/commands/stop.ts","../src/commands/clone.ts","../src/daemon/project-tracker.ts","../src/commands/checkout.ts","../src/utils/http.ts","../src/utils/env-setup.ts","../src/commands/release.ts","../src/commands/list.ts","../src/commands/update.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 // EP831: Find branch by prefix pattern\n case 'find_branch_by_prefix':\n return await this.executeFindBranchByPrefix(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 // EP944: Worktree operations\n case 'worktree_add':\n return await this.executeWorktreeAdd(command, cwd, options)\n case 'worktree_remove':\n return await this.executeWorktreeRemove(command, cwd, options)\n case 'worktree_list':\n return await this.executeWorktreeList(cwd, options)\n case 'worktree_prune':\n return await this.executeWorktreePrune(cwd, options)\n case 'clone_bare':\n return await this.executeCloneBare(command, options)\n case 'project_info':\n return await this.executeProjectInfo(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 // EP971: Check if this is a bare repo (git status doesn't work in bare repos)\n try {\n const isBareResult = await execAsync('git rev-parse --is-bare-repository', { cwd, timeout: 5000 })\n if (isBareResult.stdout.trim() === 'true') {\n // In bare repo, get current branch from HEAD ref\n const headResult = await execAsync('git symbolic-ref --short HEAD', { cwd, timeout: 5000 })\n const branchName = headResult.stdout.trim()\n return {\n success: true,\n output: `## ${branchName}`,\n details: {\n uncommittedFiles: [], // No working tree in bare repo\n branchName,\n currentBranch: branchName\n }\n }\n }\n } catch {\n // Not a bare repo or failed to check, continue with normal status\n }\n\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\n /**\n * EP831: Find branch by prefix pattern\n * Searches local and remote branches for one matching the prefix\n */\n private async executeFindBranchByPrefix(\n command: { prefix: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const { stdout } = await execAsync(\n 'git branch -a',\n { cwd, timeout: options?.timeout || 10000 }\n )\n\n const prefix = command.prefix\n const branches = stdout.split('\\n')\n .map(line => line.replace(/^[\\s*]*/, '').replace('remotes/origin/', '').trim())\n .filter(branch => branch && !branch.includes('->'))\n\n const matchingBranch = branches.find(branch => branch.startsWith(prefix))\n\n return {\n success: true,\n output: matchingBranch || '',\n details: {\n branchName: matchingBranch || undefined,\n branchExists: !!matchingBranch\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to find branch'\n }\n }\n }\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 // EP944: Worktree operations\n // ========================================\n\n /**\n * EP944: Add a new worktree for a branch\n * Creates a new working tree at the specified path\n */\n private async executeWorktreeAdd(\n command: { path: string; branch: string; create?: boolean; startPoint?: 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 if path already exists\n const fs = await import('fs').then(m => m.promises)\n try {\n await fs.access(command.path)\n return {\n success: false,\n error: 'WORKTREE_EXISTS',\n output: `Worktree already exists at path: ${command.path}`\n }\n } catch {\n // Path doesn't exist, which is what we want\n }\n\n // Fetch latest refs from remote to ensure branch exists\n // This is especially important for branches created after the initial clone\n try {\n await this.runGitCommand(['fetch', '--all', '--prune'], cwd, options)\n } catch {\n // Fetch failure is non-fatal - branch may be local or fetch may not be needed\n }\n\n // Build command\n const args = ['worktree', 'add']\n if (command.create) {\n args.push('-b', command.branch, command.path)\n // EP996: Add start point for new branch to ensure fresh base\n if (command.startPoint) {\n args.push(command.startPoint)\n }\n } else {\n args.push(command.path, command.branch)\n }\n\n const result = await this.runGitCommand(args, cwd, options)\n\n if (result.success) {\n return {\n success: true,\n output: `Created worktree at ${command.path} for branch ${command.branch}`,\n details: {\n worktreePath: command.path,\n branchName: command.branch\n }\n }\n }\n\n // Check for specific error conditions\n if (result.output?.includes('already checked out')) {\n return {\n success: false,\n error: 'BRANCH_IN_USE',\n output: `Branch '${command.branch}' is already checked out in another worktree`\n }\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to add worktree'\n }\n }\n }\n\n /**\n * EP944: Remove a worktree\n * Removes the working tree at the specified path\n */\n private async executeWorktreeRemove(\n command: { path: string; force?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Check if path exists\n const fs = await import('fs').then(m => m.promises)\n try {\n await fs.access(command.path)\n } catch {\n return {\n success: false,\n error: 'WORKTREE_NOT_FOUND',\n output: `Worktree not found at path: ${command.path}`\n }\n }\n\n // Check for uncommitted changes (unless force)\n if (!command.force) {\n try {\n const { stdout } = await execAsync('git status --porcelain', {\n cwd: command.path,\n timeout: options?.timeout || 10000\n })\n if (stdout.trim()) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n output: 'Worktree has uncommitted changes. Use force to remove anyway.',\n details: {\n uncommittedFiles: stdout.trim().split('\\n').map(line => line.slice(3))\n }\n }\n }\n } catch {\n // If we can't check status, continue with removal\n }\n }\n\n // Build command\n const args = ['worktree', 'remove']\n if (command.force) {\n args.push('--force')\n }\n args.push(command.path)\n\n const result = await this.runGitCommand(args, cwd, options)\n\n if (result.success) {\n return {\n success: true,\n output: `Removed worktree at ${command.path}`,\n details: {\n worktreePath: command.path\n }\n }\n }\n\n // Check for locked worktree\n if (result.output?.includes('locked')) {\n return {\n success: false,\n error: 'WORKTREE_LOCKED',\n output: `Worktree at ${command.path} is locked`\n }\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to remove worktree'\n }\n }\n }\n\n /**\n * EP944: List all worktrees\n * Returns information about all worktrees in the repository\n */\n private async executeWorktreeList(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const { stdout } = await execAsync('git worktree list --porcelain', {\n cwd,\n timeout: options?.timeout || 10000\n })\n\n const worktrees: Array<{\n path: string\n branch: string\n commit: string\n locked?: boolean\n prunable?: boolean\n }> = []\n\n // Parse porcelain output\n // Format:\n // worktree /path/to/worktree\n // HEAD abc123...\n // branch refs/heads/branch-name\n // (blank line)\n const lines = stdout.trim().split('\\n')\n let current: Partial<{\n path: string\n branch: string\n commit: string\n locked: boolean\n prunable: boolean\n }> = {}\n\n for (const line of lines) {\n if (line.startsWith('worktree ')) {\n current.path = line.slice(9)\n } else if (line.startsWith('HEAD ')) {\n current.commit = line.slice(5)\n } else if (line.startsWith('branch ')) {\n // Extract branch name from refs/heads/branch-name\n const refPath = line.slice(7)\n current.branch = refPath.replace('refs/heads/', '')\n } else if (line === 'locked') {\n current.locked = true\n } else if (line === 'prunable') {\n current.prunable = true\n } else if (line.startsWith('detached')) {\n current.branch = 'HEAD (detached)'\n } else if (line === '' && current.path) {\n // End of entry\n worktrees.push({\n path: current.path,\n branch: current.branch || 'unknown',\n commit: current.commit || '',\n locked: current.locked,\n prunable: current.prunable\n })\n current = {}\n }\n }\n\n // Don't forget the last entry if output doesn't end with blank line\n if (current.path) {\n worktrees.push({\n path: current.path,\n branch: current.branch || 'unknown',\n commit: current.commit || '',\n locked: current.locked,\n prunable: current.prunable\n })\n }\n\n return {\n success: true,\n output: `Found ${worktrees.length} worktree(s)`,\n details: {\n worktrees\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to list worktrees'\n }\n }\n }\n\n /**\n * EP944: Prune stale worktrees\n * Removes worktree administrative files for worktrees whose directories are missing\n */\n private async executeWorktreePrune(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // First get list of prunable worktrees\n const listResult = await this.executeWorktreeList(cwd, options)\n const prunableCount = listResult.details?.worktrees?.filter(w => w.prunable).length || 0\n\n // Run prune\n const result = await this.runGitCommand(['worktree', 'prune'], cwd, options)\n\n if (result.success) {\n return {\n success: true,\n output: prunableCount > 0\n ? `Pruned ${prunableCount} stale worktree(s)`\n : 'No stale worktrees to prune',\n details: {\n prunedCount: prunableCount\n }\n }\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to prune worktrees'\n }\n }\n }\n\n /**\n * EP944: Clone a repository as a bare repository\n * Used for worktree-based development setup\n */\n private async executeCloneBare(\n command: { url: string; path: string },\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const fs = await import('fs').then(m => m.promises)\n const path = await import('path')\n\n // Check if path already exists\n try {\n await fs.access(command.path)\n return {\n success: false,\n error: 'BRANCH_ALREADY_EXISTS', // Reusing for path exists\n output: `Directory already exists at path: ${command.path}`\n }\n } catch {\n // Path doesn't exist, which is what we want\n }\n\n // Create parent directory if needed\n const parentDir = path.dirname(command.path)\n try {\n await fs.mkdir(parentDir, { recursive: true })\n } catch {\n // Directory might already exist\n }\n\n // Clone as bare repository\n const { stdout, stderr } = await execAsync(\n `git clone --bare \"${command.url}\" \"${command.path}\"`,\n { timeout: options?.timeout || 120000 } // 2 minutes for clone\n )\n\n return {\n success: true,\n output: `Cloned bare repository to ${command.path}`,\n details: {\n worktreePath: command.path\n }\n }\n } catch (error: any) {\n // Check for auth errors\n if (error.message?.includes('Authentication') || error.message?.includes('Permission denied')) {\n return {\n success: false,\n error: 'AUTH_FAILURE',\n output: 'Authentication failed. Please check your credentials.'\n }\n }\n\n // Check for network errors\n if (error.message?.includes('Could not resolve') || error.message?.includes('unable to access')) {\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: 'Network error. Please check your connection.'\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to clone repository'\n }\n }\n }\n\n /**\n * EP944: Get project info including worktree mode\n * Returns information about the project configuration\n */\n private async executeProjectInfo(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const fs = await import('fs').then(m => m.promises)\n const path = await import('path')\n\n // EP971: All projects use worktree architecture\n // Walk up to find the project root with .bare and .episoda directories\n let currentPath = cwd\n let projectPath = cwd\n let bareRepoPath: string | undefined\n\n for (let i = 0; i < 10; i++) {\n const bareDir = path.join(currentPath, '.bare')\n const episodaDir = path.join(currentPath, '.episoda')\n\n try {\n await fs.access(bareDir)\n await fs.access(episodaDir)\n\n // Found project root\n projectPath = currentPath\n bareRepoPath = bareDir\n break\n } catch {\n // Not found at this level, check parent\n const parentPath = path.dirname(currentPath)\n if (parentPath === currentPath) {\n break // Reached filesystem root\n }\n currentPath = parentPath\n }\n }\n\n return {\n success: true,\n output: bareRepoPath ? 'Episoda project' : 'Git repository',\n details: {\n projectPath,\n bareRepoPath\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to get project info'\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\n// EP846-7: Aligned to 20s (was 45s) - server is 15s, so intervals don't collide\nconst CLIENT_HEARTBEAT_INTERVAL = 20000 // 20 seconds (offset from server's 15s)\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 (15s 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 // EP816: Close any existing WebSocket before creating a new one\n // This prevents orphaned connections when connect() is called multiple times\n if (this.ws) {\n try {\n this.ws.removeAllListeners() // Prevent close handler from triggering reconnect\n this.ws.terminate()\n } catch {\n // Ignore errors during cleanup\n }\n this.ws = undefined\n }\n\n // Clear any pending reconnect timer to prevent race conditions\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = undefined\n }\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 * EP812: Register a one-time event handler (removes itself after first call)\n * @param event - Event type\n * @param handler - Handler function\n */\n once(event: string, handler: EventHandler): void {\n const onceHandler: EventHandler = (message) => {\n this.off(event, onceHandler)\n handler(message)\n }\n this.on(event, onceHandler)\n }\n\n /**\n * EP812: Remove an event handler\n * @param event - Event type\n * @param handler - Handler function to remove\n */\n off(event: string, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event)\n if (handlers) {\n const index = handlers.indexOf(handler)\n if (index !== -1) {\n handlers.splice(index, 1)\n }\n }\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 simplified retry logic\n *\n * EP843: Simplified from complex exponential backoff to fast-fail approach\n *\n * Strategy:\n * - For graceful shutdown (server restart): Quick retry (500ms, 1s, 2s) up to 3 attempts\n * - For other disconnects: 1 retry after 1 second, then stop\n * - Always respect rate limits from server\n * - Surface errors quickly so user can take action\n *\n * This replaces the previous 6-hour retry with exponential backoff,\n * which masked problems and delayed error visibility.\n */\n private scheduleReconnect(): void {\n // Don't reconnect if user intentionally disconnected\n if (this.isIntentionalDisconnect) {\n console.log('[EpisodaClient] Intentional disconnect - not reconnecting')\n return\n }\n\n // Don't schedule if reconnection is already pending\n if (this.reconnectTimeout) {\n console.log('[EpisodaClient] Reconnection already scheduled, skipping duplicate')\n return\n }\n\n // 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 // 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\n this.scheduleReconnect()\n }, waitTime)\n return\n }\n\n // EP843: Simplified retry logic\n let delay: number\n let shouldRetry = true\n\n if (this.isGracefulShutdown) {\n // Graceful shutdown (server restart): Quick retry up to 7 attempts\n // EP843: Increased from 3 to 7 to cover longer server restart windows (deploys, migrations)\n // Retry schedule: 500ms, 1s, 2s, 4s, 5s, 5s, 5s = ~22.5s total coverage\n if (this.reconnectAttempts >= 7) {\n console.error('[EpisodaClient] Server restart reconnection failed after 7 attempts. Run \"episoda dev\" to reconnect.')\n shouldRetry = false\n } else {\n // Exponential backoff capped at 5s: 500ms, 1s, 2s, 4s, 5s, 5s, 5s\n delay = Math.min(500 * Math.pow(2, this.reconnectAttempts), 5000)\n console.log(`[EpisodaClient] Server restarting, reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1}/7)`)\n }\n } else {\n // EP956: Non-graceful disconnects (code 1006 etc): 5 retries with exponential backoff\n // Previously only 1 retry, which was too aggressive at failing\n // Retry schedule: 1s, 2s, 4s, 8s, 16s = ~31s total coverage\n const MAX_NON_GRACEFUL_RETRIES = 5\n if (this.reconnectAttempts >= MAX_NON_GRACEFUL_RETRIES) {\n // EP843: Improved error message with more context\n console.error(`[EpisodaClient] Connection lost. Reconnection failed after ${MAX_NON_GRACEFUL_RETRIES} attempts. Check server status or restart with \"episoda dev\".`)\n shouldRetry = false\n } else {\n // Exponential backoff: 1s, 2s, 4s, 8s, 16s\n delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 16000)\n console.log(`[EpisodaClient] Connection lost, retrying in ${delay / 1000}s... (attempt ${this.reconnectAttempts + 1}/${MAX_NON_GRACEFUL_RETRIES})`)\n }\n }\n\n if (!shouldRetry) {\n // Emit disconnected event so daemon can handle it\n this.emit({\n type: 'disconnected',\n code: 1006,\n reason: 'Reconnection attempts exhausted',\n willReconnect: false\n })\n return\n }\n\n this.reconnectAttempts++\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 console.log('[EpisodaClient] Reconnection successful')\n this.reconnectAttempts = 0\n this.isGracefulShutdown = false\n this.firstDisconnectTime = undefined\n this.rateLimitBackoffUntil = undefined\n }).catch(error => {\n console.error('[EpisodaClient] Reconnection failed:', error.message)\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 // EP944: Worktree error messages\n 'WORKTREE_EXISTS': 'Worktree already exists at this path',\n 'WORKTREE_NOT_FOUND': 'Worktree not found at this path',\n 'WORKTREE_LOCKED': 'Worktree is locked',\n 'BRANCH_IN_USE': 'Branch is already checked out in another worktree',\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 { connectCommand } from './commands/connect'\nimport { statusCommand } from './commands/status'\nimport { stopCommand } from './commands/stop'\nimport { cloneCommand } from './commands/clone'\nimport { checkoutCommand } from './commands/checkout'\nimport { releaseCommand } from './commands/release'\nimport { listCommand } from './commands/list'\nimport { updateCommand } from './commands/update'\n// EP959: Removed migrateCommand - all projects use worktree architecture\n// EP904: Removed tunnel CLI commands - tunnels are now managed exclusively by daemon\nimport { status } from './output'\n\nprogram\n .name('episoda')\n .description('Episoda CLI - local development with git worktree isolation')\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// Connect command (EP591) - Browser-initiated authentication\nprogram\n .command('connect')\n .description('Connect using a pre-authorized code from the browser')\n .requiredOption('--code <code>', 'Connection code from browser')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (options) => {\n try {\n await connectCommand(options)\n } catch (error) {\n status.error(`Connection failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Dev command (requires project from `episoda clone`)\nprogram\n .command('dev')\n .description('Connect to Episoda and start dev server')\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\n// EP846-6: Now verifies with server by default; --local skips server check\n// EP992: Added --skip-update-check flag to skip version check\nprogram\n .command('status')\n .description('Show connection status')\n .option('--verify', 'Verify connection is healthy (not just connected)')\n .option('--local', 'Only check local daemon state (faster, but may be stale)')\n .option('--skip-update-check', 'Skip CLI version update check (faster)')\n .action(async (options) => {\n try {\n await statusCommand({\n verify: options.verify,\n local: options.local,\n skipUpdateCheck: options.skipUpdateCheck\n })\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\n// EP904: Tunnel CLI commands removed - tunnels are managed exclusively by daemon\n// Tunnels automatically start when module enters doing/review state (module_state_changed event)\n// Tunnels automatically stop when module leaves active zone\n\n// EP989: Update command for manual version checking and updates\nprogram\n .command('update')\n .description('Check for updates and optionally install them')\n .option('-y, --yes', 'Auto-update without prompting')\n .option('--check', 'Just check for updates, do not install')\n .option('--restart', 'Restart daemon after update if it was running')\n .action(async (options) => {\n try {\n await updateCommand(options)\n } catch (error) {\n status.error(`Update failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// EP944: Clone command for multi-module development\nprogram\n .command('clone')\n .description('Clone a project for multi-module development')\n .argument('<workspace/project>', 'Workspace and project slug (e.g., my-team/my-project)')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (slug, options) => {\n try {\n await cloneCommand(slug, options)\n } catch (error) {\n status.error(`Clone failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('checkout')\n .description('Create a worktree for a module')\n .argument('<moduleUid>', 'Module UID (e.g., EP100)')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .option('--create', 'Create branch if it doesn\\'t exist')\n .action(async (moduleUid, options) => {\n try {\n await checkoutCommand(moduleUid, options)\n } catch (error) {\n status.error(`Checkout failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('release')\n .description('Remove a worktree for a module (preserves branch)')\n .argument('<moduleUid>', 'Module UID (e.g., EP100)')\n .option('--force', 'Force release even with uncommitted changes')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (moduleUid, options) => {\n try {\n await releaseCommand(moduleUid, options)\n } catch (error) {\n status.error(`Release failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('list')\n .description('List cloned projects and worktrees')\n .argument('[subcommand]', 'Subcommand: \"worktrees\" or \"wt\" to list worktrees')\n .option('--verbose', 'Show detailed information')\n .action(async (subcommand, options) => {\n try {\n await listCommand(subcommand, options)\n } catch (error) {\n status.error(`List failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// EP959: Removed 'migrate' command - use 'episoda clone' for new projects\n\n// EP932: Dev server management commands\nimport { restartDevServer, getDevServerStatus } from './ipc/ipc-client'\n\nprogram\n .command('dev:restart')\n .description('Restart the dev server for a module')\n .argument('[moduleUid]', 'Module UID (required)')\n .action(async (moduleUid) => {\n try {\n if (!moduleUid) {\n status.error('Module UID is required. Usage: episoda dev:restart <moduleUid>')\n process.exit(1)\n }\n\n status.info(`Restarting dev server for ${moduleUid}...`)\n const result = await restartDevServer(moduleUid)\n\n if (result.success) {\n status.success(`Dev server restarted for ${moduleUid}`)\n } else {\n status.error(`Failed to restart: ${result.error}`)\n process.exit(1)\n }\n } catch (error) {\n status.error(`Dev restart failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('dev:status')\n .description('Show status of running dev servers')\n .action(async () => {\n try {\n const result = await getDevServerStatus()\n\n if (!result.success) {\n status.error('Failed to get dev server status')\n process.exit(1)\n }\n\n if (result.servers.length === 0) {\n status.info('No dev servers running')\n return\n }\n\n console.log('\\nDev Servers:')\n console.log('─'.repeat(80))\n\n for (const server of result.servers) {\n const uptimeFormatted = formatUptime(server.uptime)\n const autoRestart = server.autoRestartEnabled ? 'enabled' : 'disabled'\n const restartInfo = server.restartCount > 0\n ? ` (${server.restartCount} restarts)`\n : ''\n\n console.log(` ${server.moduleUid}`)\n console.log(` Port: ${server.port} PID: ${server.pid || 'N/A'} Uptime: ${uptimeFormatted}${restartInfo}`)\n console.log(` Auto-restart: ${autoRestart}`)\n if (server.logFile) {\n console.log(` Log: ${server.logFile}`)\n }\n console.log()\n }\n } catch (error) {\n status.error(`Status check failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n/**\n * Format uptime in seconds to human-readable string\n */\nfunction formatUptime(seconds: number): string {\n if (seconds < 60) return `${seconds}s`\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`\n const hours = Math.floor(seconds / 3600)\n const mins = Math.floor((seconds % 3600) / 60)\n return `${hours}h ${mins}m`\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 } from '@episoda/core'\nimport { resolveDevCommand } from '../framework-detector'\nimport { status, printFrameworkDetection } from '../output'\nimport { isDaemonRunning, startDaemon, killAllEpisodaProcesses } from '../daemon/daemon-manager'\nimport { addProject, isDaemonReachable, verifyHealth } from '../ipc/ipc-client'\nimport { spawn, ChildProcess, execSync } from 'child_process'\nimport * as path from 'path'\nimport * as fs from 'fs'\nimport { isPortInUse, getServerPort } from '../utils/port-check'\nimport { isWorktreeProject } from '../daemon/worktree-manager'\nimport { fetchProjectPath, syncProjectPath } from '../api/machine-settings'\nimport { extractBootstrapScripts } from '../utils/bootstrap'\n// EP959: Removed migrateCommand import - all projects use worktree architecture\n// EP960: Removed saveConfig - no longer caching paths locally\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 // EP812: Check if daemon is already running and healthy before killing\n // Only restart if unhealthy - this avoids rate limiting on reconnect\n let needsRestart = false\n const existingPid = isDaemonRunning()\n\n if (existingPid) {\n // Daemon is running - check if it's healthy\n const reachable = await isDaemonReachable()\n if (reachable) {\n try {\n const health = await verifyHealth()\n if (health.healthyConnections > 0) {\n status.debug(`Daemon already running and healthy (PID: ${existingPid})`)\n // Skip cleanup and reuse existing daemon\n } else if (health.staleConnections > 0) {\n status.info('Daemon has stale connections, restarting...')\n needsRestart = true\n } else {\n // No connections but daemon is reachable - it will connect when we add project\n status.debug(`Daemon running but no connections (PID: ${existingPid})`)\n }\n } catch {\n // Health check failed - restart\n status.debug('Health check failed, restarting daemon...')\n needsRestart = true\n }\n } else {\n // Daemon running but not reachable via IPC - restart\n status.debug('Daemon not reachable via IPC, restarting...')\n needsRestart = true\n }\n } else {\n // No daemon running - will start fresh\n needsRestart = false\n }\n\n // Only kill processes if we determined a restart is needed\n if (needsRestart) {\n const killedCount = killAllEpisodaProcesses()\n if (killedCount > 0) {\n status.info(`Cleaned up ${killedCount} stale process${killedCount > 1 ? 'es' : ''}`)\n // EP812: Wait for server rate limit window after killing connected processes\n await new Promise(resolve => setTimeout(resolve, 2000))\n }\n }\n\n // EP960: Server-synced project path resolution\n // Priority: 1. Server-stored path (if valid), 2. Local git detection, 3. Current working directory\n // This replaces the 24-hour local cache that caused stale path issues\n let projectPath: string\n\n // Try to get path from server first\n const serverPath = await fetchProjectPath(config, config.project_id)\n\n if (serverPath && fs.existsSync(serverPath)) {\n // Server path is valid - use it\n projectPath = serverPath\n status.debug(`Using server-synced project path: ${projectPath}`)\n } else {\n // Server path missing or invalid - detect locally\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 // Sync detected path to server (background, non-blocking)\n syncProjectPath(config, config.project_id, projectPath)\n .then(synced => {\n if (synced) {\n status.debug('Project path synced to server')\n }\n })\n .catch(() => {}) // Ignore errors - sync is best-effort\n }\n\n // EP971: Validate this is an Episoda project (has .bare/ directory)\n const isWorktree = await isWorktreeProject(projectPath)\n\n if (!isWorktree) {\n status.error('Not an Episoda project.')\n status.info('')\n status.info('Use `episoda clone {workspace}/{project}` to set up a project.')\n status.info('')\n process.exit(1)\n }\n\n // EP984: Ensure bootstrap scripts exist (for pre-EP964 installations or new machines)\n const bareRepoPath = path.join(projectPath, '.bare')\n const scriptsExtracted = await extractBootstrapScripts(bareRepoPath, projectPath)\n if (scriptsExtracted) {\n status.success('✓ Bootstrap scripts extracted')\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 } else if (!shuttingDown) {\n // EP813: Dev server failed but daemon is still connected\n // Fall back to connection-only mode messaging\n status.info('')\n status.info('Dev server stopped, but daemon remains connected.')\n status.info('Git operations will still be executed by Episoda.')\n status.info('Press Ctrl+C to disconnect.')\n status.info('')\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 * EP986: Install command detection result\n */\nexport interface InstallCommand {\n command: string[]\n description: string\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 * EP986: Detect and return the appropriate dependency install command\n *\n * Detection priority (lock files first for exact versions):\n * 1. bun.lockb → bun install\n * 2. pnpm-lock.yaml → pnpm install\n * 3. yarn.lock → yarn install\n * 4. package-lock.json → npm ci (exact versions)\n * 5. package.json → npm install (fallback)\n * 6. Pipfile.lock → pipenv install\n * 7. poetry.lock → poetry install\n * 8. requirements.txt → pip install -r requirements.txt\n * 9. Gemfile.lock / Gemfile → bundle install\n * 10. go.sum / go.mod → go mod download\n * 11. Cargo.lock / Cargo.toml → cargo build\n *\n * @param cwd - Working directory to search\n * @returns Install command info or null if no package manager detected\n */\nexport function getInstallCommand(cwd: string): InstallCommand | null {\n // Node.js - check lock files first for specific package manager\n // Bun first (fastest runtime)\n if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {\n return {\n command: ['bun', 'install'],\n description: 'Installing dependencies with bun',\n detectedFrom: 'bun.lockb'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\n return {\n command: ['pnpm', 'install'],\n description: 'Installing dependencies with pnpm',\n detectedFrom: 'pnpm-lock.yaml'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\n return {\n command: ['yarn', 'install'],\n description: 'Installing dependencies with yarn',\n detectedFrom: 'yarn.lock'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {\n return {\n command: ['npm', 'ci'],\n description: 'Installing dependencies with npm ci',\n detectedFrom: 'package-lock.json'\n }\n }\n\n // Fallback to npm install if only package.json exists\n if (fs.existsSync(path.join(cwd, 'package.json'))) {\n return {\n command: ['npm', 'install'],\n description: 'Installing dependencies with npm',\n detectedFrom: 'package.json'\n }\n }\n\n // Python - check lock files first\n if (fs.existsSync(path.join(cwd, 'Pipfile.lock')) || fs.existsSync(path.join(cwd, 'Pipfile'))) {\n return {\n command: ['pipenv', 'install'],\n description: 'Installing dependencies with pipenv',\n detectedFrom: fs.existsSync(path.join(cwd, 'Pipfile.lock')) ? 'Pipfile.lock' : 'Pipfile'\n }\n }\n\n // Poetry detection: check lock file first, then pyproject.toml\n if (fs.existsSync(path.join(cwd, 'poetry.lock'))) {\n return {\n command: ['poetry', 'install'],\n description: 'Installing dependencies with poetry',\n detectedFrom: 'poetry.lock'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'pyproject.toml'))) {\n const pyprojectPath = path.join(cwd, 'pyproject.toml')\n const content = fs.readFileSync(pyprojectPath, 'utf-8')\n if (content.includes('[tool.poetry]')) {\n return {\n command: ['poetry', 'install'],\n description: 'Installing dependencies with poetry',\n detectedFrom: 'pyproject.toml'\n }\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'requirements.txt'))) {\n return {\n command: ['pip', 'install', '-r', 'requirements.txt'],\n description: 'Installing dependencies with pip',\n detectedFrom: 'requirements.txt'\n }\n }\n\n // Ruby\n if (fs.existsSync(path.join(cwd, 'Gemfile.lock')) || fs.existsSync(path.join(cwd, 'Gemfile'))) {\n return {\n command: ['bundle', 'install'],\n description: 'Installing dependencies with bundler',\n detectedFrom: fs.existsSync(path.join(cwd, 'Gemfile.lock')) ? 'Gemfile.lock' : 'Gemfile'\n }\n }\n\n // Go\n if (fs.existsSync(path.join(cwd, 'go.sum')) || fs.existsSync(path.join(cwd, 'go.mod'))) {\n return {\n command: ['go', 'mod', 'download'],\n description: 'Downloading Go modules',\n detectedFrom: fs.existsSync(path.join(cwd, 'go.sum')) ? 'go.sum' : 'go.mod'\n }\n }\n\n // Rust\n if (fs.existsSync(path.join(cwd, 'Cargo.lock')) || fs.existsSync(path.join(cwd, 'Cargo.toml'))) {\n return {\n command: ['cargo', 'build'],\n description: 'Building Rust project (downloads dependencies)',\n detectedFrom: fs.existsSync(path.join(cwd, 'Cargo.lock')) ? 'Cargo.lock' : 'Cargo.toml'\n }\n }\n\n // No package manager 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 // EP944: Worktree errors\n WORKTREE_EXISTS: 'Worktree already exists at this path',\n WORKTREE_NOT_FOUND: 'Worktree not found',\n WORKTREE_LOCKED: 'Worktree is locked',\n BRANCH_IN_USE: 'Branch is already checked out in another worktree',\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 // EP813: Debug - log daemon output to file for troubleshooting\n const logPath = path.join(configDir, 'daemon.log')\n const logFd = fs.openSync(logPath, 'a')\n\n // Spawn daemon as detached process\n const child = spawn('node', [daemonScript], {\n detached: true, // Run independently of parent\n stdio: ['ignore', logFd, logFd], // EP813: Redirect stdout/stderr to log file\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/**\n * EP846-6: Verify connection with server\n *\n * Queries the server's /api/cli/status endpoint to verify the connection\n * is actually alive from the server's perspective. This is the authoritative\n * check - local daemon state can be stale.\n *\n * @returns Server verification result\n */\nexport async function verifyServerConnection(): Promise<{\n verified: boolean\n error?: string\n localConnected: boolean\n serverConnected: boolean\n machineMatch?: boolean\n machineId?: string\n serverMachineId?: string | null\n serverError?: string | null\n actuallyConnected: boolean\n}> {\n return await sendCommand('verify-server-connection', {}, 10000) // 10s timeout for network call\n}\n\n// EP734: Removed getConnectionStatus - no longer needed with blocking add-project\n\n/**\n * EP823: Get tunnel status from daemon\n *\n * Returns all running tunnels managed by the daemon's TunnelManager.\n * This is the authoritative source of tunnel state since the daemon\n * holds the actual cloudflared processes.\n *\n * @returns List of running tunnels with their info\n */\nexport async function getTunnelStatus(): Promise<{\n tunnels: Array<{\n moduleUid: string\n url: string\n port: number\n status: string\n pid?: number\n startedAt: string // Date serialized as ISO string\n error?: string\n }>\n}> {\n return await sendCommand('tunnel-status')\n}\n\n/**\n * EP823: Stop a tunnel via daemon\n *\n * Stops a tunnel by module UID, including:\n * - Killing the cloudflared process\n * - Stopping the associated dev server\n * - Clearing the tunnel URL from the API\n *\n * @param moduleUid The module UID of the tunnel to stop\n * @returns Success status\n */\nexport async function stopTunnel(moduleUid: string): Promise<{\n success: boolean\n error?: string\n}> {\n return await sendCommand('tunnel-stop', { moduleUid })\n}\n\n/**\n * EP932: Restart dev server for a module\n *\n * Restarts the dev server with the enhanced auto-restart logic:\n * - Stops the current server gracefully\n * - Kills any hung processes on the port\n * - Starts a fresh server with auto-restart enabled\n *\n * @param moduleUid The module UID of the dev server to restart\n * @returns Success status with error message if failed\n */\nexport async function restartDevServer(moduleUid: string): Promise<{\n success: boolean\n error?: string\n}> {\n return await sendCommand('dev-server-restart', { moduleUid })\n}\n\n/**\n * EP932: Get status of all running dev servers\n *\n * Returns information about all dev servers managed by the daemon:\n * - Module UID\n * - Port\n * - PID\n * - Uptime\n * - Restart count\n * - Log file location\n *\n * @returns List of running dev servers with their status\n */\nexport async function getDevServerStatus(): Promise<{\n success: boolean\n servers: Array<{\n moduleUid: string\n port: number\n pid: number | undefined\n startedAt: string // ISO date string\n uptime: number // seconds\n restartCount: number\n lastRestartAt: string | null // ISO date string\n autoRestartEnabled: boolean\n logFile: string | null\n }>\n}> {\n return await sendCommand('dev-server-status')\n}\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 * Worktree Manager\n *\n * EP944: Manages git worktrees for multi-module development.\n * Each module gets its own worktree, enabling parallel development.\n *\n * Directory Structure:\n * ~/episoda/{workspace}/{project}/\n * ├── .bare/ # Bare git clone\n * ├── .episoda/\n * │ └── config.json # Project worktree config\n * ├── EP100/ # Module worktree\n * └── EP101/ # Another module worktree\n *\n * EP995 EVALUATION: Local worktrees[] array vs Server-side state\n * ---------------------------------------------------------------\n * With EP995, the server now stores:\n * - module.worktree_path - filesystem path on checkout_machine_id\n * - module.worktree_status - pending/setup/ready/error\n * - module.worktree_error - error message if setup failed\n *\n * The daemon does reconciliation on connect (reconcileWorktrees), making\n * the local worktrees[] array mostly redundant.\n *\n * RECOMMENDATION: Keep local worktrees[] for now because:\n * 1. It provides instant local lookup without network call\n * 2. Acts as fallback if server unreachable during worktree operations\n * 3. Stores setupStatus for local progress tracking during setup\n * 4. No breaking change - gradual migration is safer\n *\n * FUTURE: Consider removing in a follow-up module when:\n * - EP978 progress infrastructure is complete (UI can show server-side status)\n * - Offline support requirements are clarified\n * - Migration path for existing installations is defined\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { GitExecutor, ExecutionResult } from '@episoda/core'\n\n/**\n * EP957: Validate module UID to prevent path traversal attacks\n *\n * UIDs are server-generated in EPxxx format, but this provides defense-in-depth\n * in case a malformed UID ever reaches the worktree system.\n *\n * @param moduleUid - The module UID to validate\n * @returns true if valid, false if potentially dangerous\n */\nexport function validateModuleUid(moduleUid: string): boolean {\n // Reject empty, null, or whitespace-only\n if (!moduleUid || typeof moduleUid !== 'string' || !moduleUid.trim()) {\n return false\n }\n\n // Reject path traversal attempts\n if (moduleUid.includes('/') || moduleUid.includes('\\\\') || moduleUid.includes('..')) {\n return false\n }\n\n // Reject null bytes (could truncate paths in some systems)\n if (moduleUid.includes('\\0')) {\n return false\n }\n\n // Reject if it would resolve to a hidden directory\n if (moduleUid.startsWith('.')) {\n return false\n }\n\n return true\n}\n\n/**\n * Information about an active worktree\n */\nexport interface WorktreeInfo {\n moduleUid: string // Module UID (e.g., EP100)\n branchName: string // Git branch name\n worktreePath: string // Absolute path to worktree\n createdAt: string // ISO timestamp\n lastAccessed: string // ISO timestamp of last access\n // EP959-11: Setup status tracking\n setupStatus?: 'pending' | 'running' | 'ready' | 'error'\n setupStartedAt?: string // ISO timestamp\n setupCompletedAt?: string // ISO timestamp\n setupError?: string // Error message if setup failed\n}\n\n/**\n * Worktree project configuration\n * Stored in {projectRoot}/.episoda/config.json\n * EP971: All projects use worktree architecture\n */\nexport interface WorktreeProjectConfig {\n projectId: string\n workspaceSlug: string\n projectSlug: string\n bareRepoPath: string // Path to .bare/ directory\n createdAt: string\n worktrees: WorktreeInfo[]\n}\n\n/**\n * Result of a worktree operation\n */\nexport interface WorktreeOperationResult {\n success: boolean\n error?: string\n worktreePath?: string\n worktreeInfo?: WorktreeInfo\n}\n\n/**\n * Manages worktrees for a single project\n */\nexport class WorktreeManager {\n private projectRoot: string\n private bareRepoPath: string\n private configPath: string\n private gitExecutor: GitExecutor\n\n constructor(projectRoot: string) {\n this.projectRoot = projectRoot\n this.bareRepoPath = path.join(projectRoot, '.bare')\n this.configPath = path.join(projectRoot, '.episoda', 'config.json')\n this.gitExecutor = new GitExecutor()\n }\n\n /**\n * Initialize worktree manager from existing project root\n * EP971: All projects use worktree architecture\n * @returns true if valid project, false otherwise\n */\n async initialize(): Promise<boolean> {\n // Check if .bare directory exists\n if (!fs.existsSync(this.bareRepoPath)) {\n return false\n }\n\n // Check if config exists\n if (!fs.existsSync(this.configPath)) {\n return false\n }\n\n try {\n const config = this.readConfig()\n return config !== null\n } catch {\n return false\n }\n }\n\n /**\n * Create a new worktree project from scratch\n */\n static async createProject(\n projectRoot: string,\n repoUrl: string,\n projectId: string,\n workspaceSlug: string,\n projectSlug: string\n ): Promise<WorktreeManager> {\n const manager = new WorktreeManager(projectRoot)\n\n // Create project directory structure\n const episodaDir = path.join(projectRoot, '.episoda')\n fs.mkdirSync(episodaDir, { recursive: true })\n\n // Clone as bare repository\n const cloneResult = await manager.gitExecutor.execute({\n action: 'clone_bare',\n url: repoUrl,\n path: manager.bareRepoPath\n })\n\n if (!cloneResult.success) {\n throw new Error(`Failed to clone repository: ${cloneResult.output}`)\n }\n\n // Create initial config (EP971: all projects use worktree architecture)\n const config: WorktreeProjectConfig = {\n projectId,\n workspaceSlug,\n projectSlug,\n bareRepoPath: manager.bareRepoPath,\n createdAt: new Date().toISOString(),\n worktrees: []\n }\n\n manager.writeConfig(config)\n\n return manager\n }\n\n /**\n * Create a worktree for a module\n * The entire operation is locked to prevent race conditions\n */\n async createWorktree(\n moduleUid: string,\n branchName: string,\n createBranch: boolean = false\n ): Promise<WorktreeOperationResult> {\n // EP957: Validate module UID to prevent path traversal\n if (!validateModuleUid(moduleUid)) {\n return {\n success: false,\n error: `Invalid module UID: \"${moduleUid}\" - contains disallowed characters`\n }\n }\n\n const worktreePath = path.join(this.projectRoot, moduleUid)\n\n // Acquire lock for the entire check-and-create operation\n const lockAcquired = await this.acquireLock()\n if (!lockAcquired) {\n return {\n success: false,\n error: 'Could not acquire lock for worktree creation'\n }\n }\n\n try {\n // Check if worktree already exists (inside lock to prevent race)\n const existing = this.getWorktreeByModuleUid(moduleUid)\n if (existing) {\n return {\n success: true,\n worktreePath: existing.worktreePath,\n worktreeInfo: existing\n }\n }\n\n // EP945-11: Fetch latest refs from origin before creating worktree\n // This ensures new worktrees are based on current main, not stale local refs\n const fetchResult = await this.gitExecutor.execute({\n action: 'fetch',\n remote: 'origin'\n }, { cwd: this.bareRepoPath })\n\n // EP996: Fail if fetch fails when creating a new branch\n // Without a fresh fetch, the worktree would be based on stale refs\n if (!fetchResult.success && createBranch) {\n console.error('[worktree-manager] Failed to fetch from origin:', fetchResult.output)\n return {\n success: false,\n error: 'Failed to fetch latest refs from origin. Cannot create worktree with stale refs.'\n }\n }\n\n // Create worktree via git\n // EP996: Use origin/main as start point when creating a new branch\n // This ensures the branch is based on the latest remote state, not stale local HEAD\n const result = await this.gitExecutor.execute({\n action: 'worktree_add',\n path: worktreePath,\n branch: branchName,\n create: createBranch,\n startPoint: createBranch ? 'origin/main' : undefined\n }, { cwd: this.bareRepoPath })\n\n if (!result.success) {\n return {\n success: false,\n error: result.output || 'Failed to create worktree'\n }\n }\n\n // Record worktree in config\n const worktreeInfo: WorktreeInfo = {\n moduleUid,\n branchName,\n worktreePath,\n createdAt: new Date().toISOString(),\n lastAccessed: new Date().toISOString()\n }\n\n const config = this.readConfig()\n if (config) {\n config.worktrees.push(worktreeInfo)\n this.writeConfig(config)\n }\n\n return {\n success: true,\n worktreePath,\n worktreeInfo\n }\n } finally {\n this.releaseLock()\n }\n }\n\n /**\n * Remove a worktree for a module\n * P1-2: Wrapped entire operation in lock to prevent race with createWorktree\n */\n async removeWorktree(\n moduleUid: string,\n force: boolean = false\n ): Promise<WorktreeOperationResult> {\n // EP957: Validate module UID to prevent path traversal\n if (!validateModuleUid(moduleUid)) {\n return {\n success: false,\n error: `Invalid module UID: \"${moduleUid}\" - contains disallowed characters`\n }\n }\n\n // Acquire lock for the entire check-and-remove operation\n const lockAcquired = await this.acquireLock()\n if (!lockAcquired) {\n return {\n success: false,\n error: 'Could not acquire lock for worktree removal'\n }\n }\n\n try {\n const existing = this.getWorktreeByModuleUid(moduleUid)\n if (!existing) {\n return {\n success: false,\n error: `No worktree found for module ${moduleUid}`\n }\n }\n\n // Remove worktree via git\n const result = await this.gitExecutor.execute({\n action: 'worktree_remove',\n path: existing.worktreePath,\n force\n }, { cwd: this.bareRepoPath })\n\n if (!result.success) {\n return {\n success: false,\n error: result.output || 'Failed to remove worktree'\n }\n }\n\n // Remove from config (already inside lock, so just read-modify-write)\n const config = this.readConfig()\n if (config) {\n config.worktrees = config.worktrees.filter(w => w.moduleUid !== moduleUid)\n this.writeConfig(config)\n }\n\n return {\n success: true,\n worktreePath: existing.worktreePath\n }\n } finally {\n this.releaseLock()\n }\n }\n\n /**\n * Get worktree path for a module\n */\n getWorktreePath(moduleUid: string): string | null {\n // EP957: Validate module UID for defense-in-depth\n if (!validateModuleUid(moduleUid)) {\n return null\n }\n const worktree = this.getWorktreeByModuleUid(moduleUid)\n return worktree?.worktreePath || null\n }\n\n /**\n * Get worktree info by module UID\n */\n getWorktreeByModuleUid(moduleUid: string): WorktreeInfo | null {\n const config = this.readConfig()\n return config?.worktrees.find(w => w.moduleUid === moduleUid) || null\n }\n\n /**\n * Get worktree info by branch name\n */\n getWorktreeByBranch(branchName: string): WorktreeInfo | null {\n const config = this.readConfig()\n return config?.worktrees.find(w => w.branchName === branchName) || null\n }\n\n /**\n * List all active worktrees\n */\n listWorktrees(): WorktreeInfo[] {\n const config = this.readConfig()\n return config?.worktrees || []\n }\n\n /**\n * EP957: Audit worktrees against known active module UIDs\n *\n * Compares local worktrees against a list of module UIDs that should be active.\n * Used by daemon startup to detect orphaned worktrees from crashed sessions.\n *\n * @param activeModuleUids - UIDs of modules currently in doing/review state\n * @returns Object with orphaned and valid worktree lists\n */\n auditWorktrees(activeModuleUids: string[]): {\n orphaned: WorktreeInfo[]\n valid: WorktreeInfo[]\n } {\n const allWorktrees = this.listWorktrees()\n const activeSet = new Set(activeModuleUids)\n\n const orphaned = allWorktrees.filter(w => !activeSet.has(w.moduleUid))\n const valid = allWorktrees.filter(w => activeSet.has(w.moduleUid))\n\n return { orphaned, valid }\n }\n\n /**\n * Update last accessed timestamp for a worktree\n */\n async touchWorktree(moduleUid: string): Promise<void> {\n await this.updateConfigSafe(config => {\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (worktree) {\n worktree.lastAccessed = new Date().toISOString()\n }\n return config\n })\n }\n\n /**\n * Prune stale worktrees (directories that no longer exist)\n */\n async pruneStaleWorktrees(): Promise<number> {\n // First, run git worktree prune\n await this.gitExecutor.execute({\n action: 'worktree_prune'\n }, { cwd: this.bareRepoPath })\n\n // Then sync our config with reality (with locking)\n let prunedCount = 0\n await this.updateConfigSafe(config => {\n const initialCount = config.worktrees.length\n config.worktrees = config.worktrees.filter(w => fs.existsSync(w.worktreePath))\n prunedCount = initialCount - config.worktrees.length\n return config\n })\n\n return prunedCount\n }\n\n /**\n * Validate all worktrees and sync with git\n */\n async validateWorktrees(): Promise<{\n valid: WorktreeInfo[]\n stale: WorktreeInfo[]\n orphaned: string[]\n }> {\n const config = this.readConfig()\n const valid: WorktreeInfo[] = []\n const stale: WorktreeInfo[] = []\n const orphaned: string[] = []\n\n // Get actual worktrees from git\n const listResult = await this.gitExecutor.execute({\n action: 'worktree_list'\n }, { cwd: this.bareRepoPath })\n\n const actualWorktrees = new Set(\n listResult.details?.worktrees?.map(w => w.path) || []\n )\n\n // Check config worktrees\n for (const worktree of config?.worktrees || []) {\n if (actualWorktrees.has(worktree.worktreePath)) {\n valid.push(worktree)\n actualWorktrees.delete(worktree.worktreePath)\n } else {\n stale.push(worktree)\n }\n }\n\n // Any remaining are orphaned (exist in git but not in config)\n // Filter out the bare repo itself\n for (const wpath of actualWorktrees) {\n if (wpath !== this.bareRepoPath) {\n orphaned.push(wpath)\n }\n }\n\n return { valid, stale, orphaned }\n }\n\n /**\n * Get project configuration\n */\n getConfig(): WorktreeProjectConfig | null {\n return this.readConfig()\n }\n\n /**\n * Get the bare repo path\n */\n getBareRepoPath(): string {\n return this.bareRepoPath\n }\n\n /**\n * Get the project root path\n */\n getProjectRoot(): string {\n return this.projectRoot\n }\n\n // ============================================================\n // Private methods\n // ============================================================\n\n private lockPath: string = ''\n\n private getLockPath(): string {\n if (!this.lockPath) {\n this.lockPath = this.configPath + '.lock'\n }\n return this.lockPath\n }\n\n /**\n * Check if a process is still running\n */\n private isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0) // Signal 0 = check if process exists without killing\n return true\n } catch {\n return false // Process doesn't exist or no permission\n }\n }\n\n /**\n * Acquire a file lock with timeout\n * Uses atomic file creation to ensure only one process holds the lock\n * P1-1: Added PID verification before removing stale locks to prevent race conditions\n */\n private async acquireLock(timeoutMs: number = 5000): Promise<boolean> {\n const lockPath = this.getLockPath()\n const startTime = Date.now()\n const retryInterval = 50\n\n while (Date.now() - startTime < timeoutMs) {\n try {\n // wx flag = create exclusive (fails if file exists)\n fs.writeFileSync(lockPath, String(process.pid), { flag: 'wx' })\n return true\n } catch (err: any) {\n if (err.code === 'EEXIST') {\n // Lock exists, check if it's stale (older than 30 seconds)\n try {\n const stats = fs.statSync(lockPath)\n const lockAge = Date.now() - stats.mtimeMs\n if (lockAge > 30000) {\n // P1-1: Verify the lock holder process is actually dead before removing\n // This prevents race conditions where multiple processes detect stale lock\n try {\n const lockContent = fs.readFileSync(lockPath, 'utf-8').trim()\n const lockPid = parseInt(lockContent, 10)\n if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {\n // Process still running despite old lock - don't remove, just wait\n // The lock holder might be doing a long operation\n await new Promise(resolve => setTimeout(resolve, retryInterval))\n continue\n }\n } catch {\n // Can't read PID, proceed with removal attempt\n }\n // Stale lock from dead process, remove it\n try {\n fs.unlinkSync(lockPath)\n } catch {\n // Another process may have removed it - that's fine, retry\n }\n continue\n }\n } catch {\n // Lock file disappeared, retry\n continue\n }\n // Wait and retry\n await new Promise(resolve => setTimeout(resolve, retryInterval))\n continue\n }\n throw err\n }\n }\n return false\n }\n\n /**\n * Release the file lock\n */\n private releaseLock(): void {\n try {\n fs.unlinkSync(this.getLockPath())\n } catch {\n // Ignore errors (lock may not exist)\n }\n }\n\n private readConfig(): WorktreeProjectConfig | null {\n try {\n if (!fs.existsSync(this.configPath)) {\n return null\n }\n const content = fs.readFileSync(this.configPath, 'utf-8')\n return JSON.parse(content) as WorktreeProjectConfig\n } catch (error) {\n console.error('[WorktreeManager] Failed to read config:', error)\n return null\n }\n }\n\n private writeConfig(config: WorktreeProjectConfig): void {\n try {\n const dir = path.dirname(this.configPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8')\n } catch (error) {\n console.error('[WorktreeManager] Failed to write config:', error)\n throw error\n }\n }\n\n /**\n * Read-modify-write with file locking for safe concurrent access\n */\n private async updateConfigSafe(\n updater: (config: WorktreeProjectConfig) => WorktreeProjectConfig\n ): Promise<boolean> {\n const lockAcquired = await this.acquireLock()\n if (!lockAcquired) {\n console.error('[WorktreeManager] Failed to acquire lock for config update')\n return false\n }\n\n try {\n const config = this.readConfig()\n if (!config) {\n return false\n }\n const updated = updater(config)\n this.writeConfig(updated)\n return true\n } finally {\n this.releaseLock()\n }\n }\n\n // EP959-11: Worktree setup methods\n\n /**\n * Update the setup status of a worktree\n */\n async updateWorktreeStatus(\n moduleUid: string,\n status: 'pending' | 'running' | 'ready' | 'error',\n error?: string\n ): Promise<boolean> {\n return this.updateConfigSafe((config) => {\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (worktree) {\n worktree.setupStatus = status\n if (status === 'running') {\n worktree.setupStartedAt = new Date().toISOString()\n } else if (status === 'ready' || status === 'error') {\n worktree.setupCompletedAt = new Date().toISOString()\n }\n if (error) {\n worktree.setupError = error\n }\n }\n return config\n })\n }\n\n /**\n * Get worktree info including setup status\n */\n getWorktreeStatus(moduleUid: string): WorktreeInfo | null {\n const config = this.readConfig()\n if (!config) return null\n return config.worktrees.find(w => w.moduleUid === moduleUid) || null\n }\n\n /**\n * Copy files from main worktree to module worktree\n *\n * @deprecated EP964: This function is deprecated. Use worktree_env_vars for\n * environment variables or worktree_setup_script for other file operations.\n * This function now returns success (no-op) when main/ worktree doesn't exist.\n */\n async copyFilesFromMain(moduleUid: string, files: string[]): Promise<{ success: boolean; error?: string }> {\n // EP964: Always log deprecation warning\n console.warn(`[WorktreeManager] EP964: copyFilesFromMain is DEPRECATED.`)\n console.warn(`[WorktreeManager] EP964: Use worktree_env_vars for .env or worktree_setup_script for other files.`)\n\n const config = this.readConfig()\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (!worktree) {\n return { success: false, error: `Worktree not found for ${moduleUid}` }\n }\n\n // EP964: Return success (no-op) when main/ doesn't exist\n const mainWorktree = config.worktrees.find(w => w.moduleUid === 'main')\n if (!mainWorktree) {\n console.warn(`[WorktreeManager] EP964: No 'main' worktree - skipping file copy (this is expected).`)\n return { success: true } // No-op success instead of failure\n }\n\n // Backward compatibility: still copy if main/ exists\n try {\n for (const file of files) {\n const srcPath = path.join(mainWorktree.worktreePath, file)\n const destPath = path.join(worktree.worktreePath, file)\n\n if (fs.existsSync(srcPath)) {\n const destDir = path.dirname(destPath)\n if (!fs.existsSync(destDir)) {\n fs.mkdirSync(destDir, { recursive: true })\n }\n fs.copyFileSync(srcPath, destPath)\n console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`)\n } else {\n console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`)\n }\n }\n return { success: true }\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n\n /**\n * Run worktree setup script\n * EP959-M1: Enhanced logging with working directory, timeout info, and script preview\n */\n async runSetupScript(moduleUid: string, script: string): Promise<{ success: boolean; error?: string }> {\n const config = this.readConfig()\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (!worktree) {\n return { success: false, error: `Worktree not found for ${moduleUid}` }\n }\n\n // EP959-M1: Enhanced logging - show script preview (truncate if very long)\n const TIMEOUT_MINUTES = 10\n const scriptPreview = script.length > 200 ? script.slice(0, 200) + '...' : script\n console.log(`[WorktreeManager] EP959: Running setup script for ${moduleUid}`)\n console.log(`[WorktreeManager] EP959: Working directory: ${worktree.worktreePath}`)\n console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`)\n console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`)\n\n try {\n const { execSync } = require('child_process')\n\n execSync(script, {\n cwd: worktree.worktreePath,\n stdio: 'inherit',\n timeout: TIMEOUT_MINUTES * 60 * 1000,\n env: { ...process.env, NODE_ENV: 'development' }\n })\n\n console.log(`[WorktreeManager] EP959: Setup script completed successfully for ${moduleUid}`)\n return { success: true }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[WorktreeManager] EP959: Setup script failed for ${moduleUid}:`, errorMessage)\n return { success: false, error: errorMessage }\n }\n }\n\n /**\n * Run worktree cleanup script before removal\n * EP959-m3: Execute cleanup script when worktree is being released\n */\n async runCleanupScript(moduleUid: string, script: string): Promise<{ success: boolean; error?: string }> {\n const config = this.readConfig()\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (!worktree) {\n return { success: false, error: `Worktree not found for ${moduleUid}` }\n }\n\n const TIMEOUT_MINUTES = 5\n const scriptPreview = script.length > 200 ? script.slice(0, 200) + '...' : script\n console.log(`[WorktreeManager] EP959: Running cleanup script for ${moduleUid}`)\n console.log(`[WorktreeManager] EP959: Working directory: ${worktree.worktreePath}`)\n console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`)\n console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`)\n\n try {\n const { execSync } = require('child_process')\n\n execSync(script, {\n cwd: worktree.worktreePath,\n stdio: 'inherit',\n timeout: TIMEOUT_MINUTES * 60 * 1000,\n env: { ...process.env, NODE_ENV: 'development' }\n })\n\n console.log(`[WorktreeManager] EP959: Cleanup script completed successfully for ${moduleUid}`)\n return { success: true }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n // Cleanup failures are logged but don't block worktree removal\n console.warn(`[WorktreeManager] EP959: Cleanup script failed for ${moduleUid} (non-blocking):`, errorMessage)\n return { success: false, error: errorMessage }\n }\n }\n}\n\n/**\n * Get the default episoda root directory\n * Can be overridden with EPISODA_ROOT environment variable\n */\nexport function getEpisodaRoot(): string {\n return process.env.EPISODA_ROOT || path.join(require('os').homedir(), 'episoda')\n}\n\n/**\n * Get the project path for a workspace/project combination\n */\nexport function getProjectPath(workspaceSlug: string, projectSlug: string): string {\n return path.join(getEpisodaRoot(), workspaceSlug, projectSlug)\n}\n\n/**\n * Check if a path is a valid worktree project\n */\nexport async function isWorktreeProject(projectRoot: string): Promise<boolean> {\n const manager = new WorktreeManager(projectRoot)\n return manager.initialize()\n}\n\n/**\n * Find the project root by looking for .bare directory\n * Searches current directory and walks up to 5 levels looking for a valid worktree project.\n *\n * @param startPath - Directory to start searching from\n * @returns Absolute path to project root, or null if not found\n */\nexport async function findProjectRoot(startPath: string): Promise<string | null> {\n let current = path.resolve(startPath)\n const episodaRoot = getEpisodaRoot()\n\n // Must be under ~/episoda to be a worktree project\n if (!current.startsWith(episodaRoot)) {\n return null\n }\n\n // Walk up to find .bare directory (support up to 10 levels deep)\n for (let i = 0; i < 10; i++) {\n const bareDir = path.join(current, '.bare')\n const episodaDir = path.join(current, '.episoda')\n\n if (fs.existsSync(bareDir) && fs.existsSync(episodaDir)) {\n // Verify it's a valid worktree project\n if (await isWorktreeProject(current)) {\n return current\n }\n }\n\n // Move up one directory\n const parent = path.dirname(current)\n if (parent === current) {\n // Reached filesystem root\n break\n }\n current = parent\n }\n\n return null\n}\n","/**\n * EP960: Machine Settings API Client\n *\n * Functions for CLI to fetch and sync machine settings with the server.\n * Enables server-synced project paths and EPISODA_ROOT.\n */\n\nimport type { EpisodaConfig } from '@episoda/core'\n\ninterface ProjectPathResponse {\n success: boolean\n data?: {\n path: string | null\n }\n error?: string\n}\n\ninterface MachineSettingsResponse {\n success: boolean\n machine?: {\n id: string\n episoda_root: string\n project_paths: Record<string, string>\n }\n error?: string\n}\n\n/**\n * Fetch project path from server for a specific machine+project combination\n *\n * @param config - Episoda config with auth and device info\n * @param projectId - Project UUID\n * @returns The stored project path or null if not set\n */\nexport async function fetchProjectPath(\n config: EpisodaConfig,\n projectId: string\n): Promise<string | null> {\n if (!config.device_id || !config.access_token) {\n return null\n }\n\n const serverUrl = config.api_url || 'https://episoda.dev'\n const url = `${serverUrl}/api/dev/machines/${config.device_id}/project-path/${projectId}`\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n // 404 means no path stored yet, not an error\n if (response.status === 404) {\n return null\n }\n // Log non-404 errors for debugging\n console.debug(`[MachineSettings] fetchProjectPath failed: ${response.status} ${response.statusText}`)\n return null\n }\n\n const data: ProjectPathResponse = await response.json()\n return data.data?.path || null\n } catch (error) {\n // Network error - return null to trigger local detection\n console.debug(`[MachineSettings] fetchProjectPath network error:`, error)\n return null\n }\n}\n\n/**\n * Sync a project path to the server\n *\n * @param config - Episoda config with auth and device info\n * @param projectId - Project UUID\n * @param path - Absolute path to the project on this machine\n * @returns True if sync succeeded\n */\nexport async function syncProjectPath(\n config: EpisodaConfig,\n projectId: string,\n path: string\n): Promise<boolean> {\n if (!config.device_id || !config.access_token) {\n return false\n }\n\n const serverUrl = config.api_url || 'https://episoda.dev'\n const url = `${serverUrl}/api/dev/machines/${config.device_id}/project-path/${projectId}`\n\n try {\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ path })\n })\n\n if (!response.ok) {\n console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`)\n }\n return response.ok\n } catch (error) {\n // Network error - sync failed but don't block operation\n console.debug(`[MachineSettings] syncProjectPath network error:`, error)\n return false\n }\n}\n\n/**\n * Fetch machine settings including episoda_root\n *\n * @param config - Episoda config with auth and device info\n * @returns Machine settings or null if fetch failed\n */\nexport async function fetchMachineSettings(\n config: EpisodaConfig\n): Promise<{ episodaRoot: string; projectPaths: Record<string, string> } | null> {\n if (!config.device_id || !config.access_token) {\n return null\n }\n\n const serverUrl = config.api_url || 'https://episoda.dev'\n const url = `${serverUrl}/api/account/machines/${config.device_id}`\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n console.debug(`[MachineSettings] fetchMachineSettings failed: ${response.status} ${response.statusText}`)\n return null\n }\n\n const data: MachineSettingsResponse = await response.json()\n if (!data.machine) {\n return null\n }\n\n return {\n episodaRoot: data.machine.episoda_root || 'episoda',\n projectPaths: data.machine.project_paths || {}\n }\n } catch (error) {\n console.debug(`[MachineSettings] fetchMachineSettings network error:`, error)\n return null\n }\n}\n\n/**\n * Fetch only the episoda_root setting for this machine\n *\n * @param config - Episoda config with auth and device info\n * @returns The episoda_root value or default 'episoda'\n */\nexport async function fetchEpisodaRoot(config: EpisodaConfig): Promise<string> {\n const settings = await fetchMachineSettings(config)\n return settings?.episodaRoot || 'episoda'\n}\n","/**\n * Bootstrap utilities for CLI setup (EP984)\n *\n * Provides functions to extract bootstrap scripts (api-helper.sh) from\n * the bare repo to .episoda/scripts/, enabling API access before any\n * worktree is checked out.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\n\n/**\n * Extract bootstrap scripts from bare repo to .episoda/scripts/\n *\n * This enables agents to use API helpers immediately after clone,\n * before any worktree is checked out. Also called by `episoda dev`\n * to ensure scripts exist for existing installations.\n *\n * @param bareRepoPath - Path to the .bare/ git repository\n * @param projectPath - Path to the project root (contains .episoda/)\n * @returns true if scripts were extracted, false if already existed or failed\n */\nexport async function extractBootstrapScripts(\n bareRepoPath: string,\n projectPath: string\n): Promise<boolean> {\n const scriptsDir = path.join(projectPath, '.episoda', 'scripts')\n const scriptPath = path.join(scriptsDir, 'api-helper.sh')\n\n // Check if already exists (skip extraction)\n if (fs.existsSync(scriptPath)) {\n return false\n }\n\n // Create scripts directory\n fs.mkdirSync(scriptsDir, { recursive: true })\n\n try {\n // Use git show to extract file content from bare repo\n const scriptContent = execSync(\n `git --git-dir=\"${bareRepoPath}\" show main:scripts/api-helper.sh`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }\n )\n\n fs.writeFileSync(scriptPath, scriptContent, { mode: 0o755 })\n return true\n } catch (error) {\n // Non-fatal - the script might not exist in all repos\n console.log('[bootstrap] Could not extract api-helper.sh:', error)\n return false\n }\n}\n\n/**\n * Check if bootstrap scripts exist\n */\nexport function hasBootstrapScripts(projectPath: string): boolean {\n const scriptPath = path.join(projectPath, '.episoda', 'scripts', 'api-helper.sh')\n return fs.existsSync(scriptPath)\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, 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 // EP904: Now includes refresh_token and expires_at for token refresh support\n // EP951: Removed cli_version - status now reads VERSION at runtime\n // EP956: Added workspace_slug and project_slug for worktree path resolution\n await saveConfig({\n project_id: tokenResponse.project_id,\n user_id: tokenResponse.user_id,\n workspace_id: tokenResponse.workspace_id,\n workspace_slug: tokenResponse.workspace_uid, // EP956: For worktree paths\n project_slug: tokenResponse.project_uid, // EP956: For worktree paths\n access_token: tokenResponse.access_token,\n refresh_token: tokenResponse.refresh_token,\n expires_at: Date.now() + (tokenResponse.expires_in * 1000), // Convert to Unix timestamp\n api_url: apiUrl,\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 connect` command (EP591)\n *\n * Simplified authentication for browser-initiated flows.\n * Unlike `episoda auth`, this command:\n * - Does NOT open a browser\n * - Does NOT generate codes\n * - Just exchanges a pre-authorized user_code for tokens\n *\n * This enables the one-liner install flow:\n * 1. User generates code in browser (code is pre-authorized)\n * 2. User runs: curl https://episoda.dev/i/CODE | bash\n * 3. Install script runs: episoda connect --code CODE\n * 4. This command exchanges code for tokens and saves config\n */\n\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\nimport { saveConfig, getConfigPath } from '@episoda/core'\nimport { status } from '../output'\nimport { getMachineId } from '../daemon/machine-id'\nimport { generateCredentialHelperScript } from '../git-helpers/git-credential-helper'\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 TokenErrorResponse {\n error: string\n error_description?: string\n}\n\nexport interface ConnectCommandOptions {\n code: string\n apiUrl?: string\n}\n\n/**\n * Connect command - Exchange pre-authorized code for tokens\n */\nexport async function connectCommand(options: ConnectCommandOptions): Promise<void> {\n const { code } = options\n const apiUrl = options.apiUrl || process.env.EPISODA_API_URL || 'https://episoda.dev'\n\n if (!code) {\n throw new Error('Connection code is required. Usage: episoda connect --code <code>')\n }\n\n status.info('Connecting to Episoda...')\n status.info('')\n\n // Get machine ID to associate with the token\n const machineId = await getMachineId()\n\n // Exchange code for tokens\n status.info('Exchanging connection code...')\n const tokenResponse = await exchangeUserCode(apiUrl, code, machineId)\n\n status.success('✓ Connected successfully!')\n status.info(` Project: ${tokenResponse.project_uid}`)\n status.info(` Workspace: ${tokenResponse.workspace_uid}`)\n status.info('')\n\n // Save configuration\n // EP951: Removed cli_version - status now reads VERSION at runtime\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 })\n\n status.success(`✓ Configuration saved to ${getConfigPath()}`)\n status.info('')\n\n // 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 } else {\n status.warning('⚠ Git credential helper could not be installed automatically.')\n }\n status.info('')\n\n status.success('Ready!')\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('')\n}\n\n/**\n * Exchange user_code for access token\n * EP591: Uses user_code (not device_code) for browser-initiated flows\n */\nasync function exchangeUserCode(apiUrl: string, userCode: 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 user_code: userCode, // EP591: Use user_code instead of device_code\n machine_id: machineId,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json() as TokenErrorResponse\n\n // Provide user-friendly error messages\n if (error.error === 'invalid_grant') {\n throw new Error('Connection code not found or already used. Please generate a new code.')\n } else if (error.error === 'expired_token') {\n throw new Error('Connection code has expired. Please generate a new code.')\n } else if (error.error === 'authorization_pending') {\n throw new Error('Connection code not yet authorized. Please complete authorization in the browser first.')\n }\n\n throw new Error(error.error_description || error.error || 'Failed to connect')\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 response from server')\n }\n\n return tokenResponse\n}\n\n/**\n * Install git credential helper\n * Copied from auth.ts - installs helper for seamless GitHub auth\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 // Create bin directory\n fs.mkdirSync(episodaBinDir, { recursive: true })\n\n // Write credential helper script\n const scriptContent = generateCredentialHelperScript(apiUrl)\n fs.writeFileSync(helperPath, scriptContent, { mode: 0o755 })\n\n // Configure git to use the credential helper\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 already configured, we're done\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 execSync(`git config --global --add credential.helper \"${helperPath}\"`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe']\n })\n\n // Update shell profile\n updateShellProfile(episodaBinDir)\n\n return true\n } catch (error: any) {\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 return\n }\n\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`\n\n for (const profile of profiles) {\n try {\n if (fs.existsSync(profile)) {\n const content = fs.readFileSync(profile, 'utf8')\n if (content.includes('.episoda/bin')) {\n continue\n }\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 * `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 * EP846-6: Now verifies connection with server by default (local state can be stale).\n * EP951: Display VERSION from @episoda/core instead of stale config.cli_version.\n * EP992: Added version update check to status output.\n */\n\nimport { loadConfig, getConfigPath, VERSION } from '@episoda/core'\nimport { getStatus, verifyHealth, verifyServerConnection } from '../ipc/ipc-client'\nimport { checkForUpdates, performBackgroundUpdate } from '../utils/update-checker'\nimport { status } from '../output'\n\nexport interface StatusOptions {\n verify?: boolean\n local?: boolean // EP846-6: Skip server verification (fast, but may be stale)\n skipUpdateCheck?: boolean // EP992: Skip version update check for faster output\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\n // EP992: Check for CLI updates unless --skip-update-check is set\n if (!options.skipUpdateCheck) {\n const updateResult = await checkForUpdates(VERSION)\n if (updateResult.updateAvailable) {\n status.info(` CLI Version: ${VERSION} ⬆ updating to ${updateResult.latestVersion}...`)\n performBackgroundUpdate()\n } else {\n status.info(` CLI Version: ${VERSION} ✓`)\n }\n } else {\n status.info(` CLI Version: ${VERSION}`)\n }\n\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 (local state)\n const connectedProject = daemonStatus.projects.find(p => p.connected)\n\n // EP846-6: Verify with server by default (unless --local flag is set)\n // This is the authoritative check - local state can be stale\n if (!options.local && connectedProject) {\n try {\n const serverCheck = await verifyServerConnection()\n\n if (serverCheck.verified) {\n if (serverCheck.actuallyConnected) {\n // Both local and server agree - truly connected\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 (serverCheck.localConnected && !serverCheck.serverConnected) {\n // Local thinks connected, server says no - STALE\n status.error('✗ Not connected to server')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info('')\n status.warning(' Local daemon thinks connected, but server disagrees.')\n if (serverCheck.serverError) {\n status.info(` Server error: ${serverCheck.serverError}`)\n }\n status.info(' Run \"episoda dev\" to reconnect.')\n } else if (!serverCheck.machineMatch && serverCheck.serverConnected) {\n // Server sees a different machine connected\n status.warning('⚠ Different machine connected')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` This machine: ${serverCheck.machineId}`)\n status.info(` Connected machine: ${serverCheck.serverMachineId}`)\n status.info(' Run \"episoda dev\" to connect this machine.')\n } else {\n // Not connected\n status.warning('⚠ Not connected to server')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(' Run \"episoda dev\" to connect.')\n }\n } else {\n // Verification failed - fall back to local state with warning\n status.warning('⚠ Could not verify with server')\n if (serverCheck.error) {\n status.info(` Error: ${serverCheck.error}`)\n }\n status.info(` Local state: ${connectedProject ? 'connected' : 'not connected'}`)\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n }\n } catch (error) {\n // Server verification failed - show local state with warning\n status.warning('⚠ Could not verify with server')\n status.info(` Error: ${error instanceof Error ? error.message : String(error)}`)\n status.info('')\n status.info('Local state (may be stale):')\n if (connectedProject) {\n status.info(` ✓ Daemon thinks connected to: ${connectedProject.name}`)\n }\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n }\n } else if (connectedProject) {\n // --local flag set, show local state only\n status.success('✓ Connected (local state)')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info(` Project: ${connectedProject.name}`)\n status.info('')\n status.info(' Note: Use without --local to verify with server.')\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 * CLI Auto-Update Checker\n * EP783: Check for updates on daemon startup and auto-update in background\n *\n * This module provides non-blocking update checking and background updates\n * to ensure users always have the latest CLI version with correct git hooks.\n */\n\nimport { spawn, execSync } from 'child_process'\nimport * as semver from 'semver'\n\nconst PACKAGE_NAME = 'episoda'\nconst NPM_REGISTRY = 'https://registry.npmjs.org'\n\nexport interface UpdateCheckResult {\n currentVersion: string\n latestVersion: string\n updateAvailable: boolean\n /** EP989: True if check failed due to network issues (offline, timeout, etc.) */\n offline?: boolean\n}\n\n/**\n * Check npm registry for latest version\n * Non-blocking - fails silently on network errors\n */\nexport async function checkForUpdates(currentVersion: string): Promise<UpdateCheckResult> {\n try {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 5000) // 5s timeout\n\n const response = await fetch(`${NPM_REGISTRY}/${PACKAGE_NAME}/latest`, {\n signal: controller.signal\n })\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n return { currentVersion, latestVersion: currentVersion, updateAvailable: false }\n }\n\n const data = await response.json() as { version: string }\n const latestVersion = data.version\n\n return {\n currentVersion,\n latestVersion,\n updateAvailable: semver.gt(latestVersion, currentVersion)\n }\n } catch (error) {\n // EP989: Detect network errors and report offline status\n // This includes timeouts (AbortError), DNS failures, connection refused, etc.\n return { currentVersion, latestVersion: currentVersion, updateAvailable: false, offline: true }\n }\n}\n\n/**\n * Run npm update in background (detached process)\n * The update runs independently of the daemon process\n */\nexport function performBackgroundUpdate(): void {\n try {\n const child = spawn('npm', ['update', '-g', PACKAGE_NAME], {\n detached: true,\n stdio: 'ignore',\n // Use shell on Windows for proper npm execution\n shell: process.platform === 'win32'\n })\n child.unref() // Allow parent to exit independently\n } catch (error) {\n // Silently ignore spawn errors\n }\n}\n\n/**\n * EP989: Get the currently installed version of the CLI from npm\n * Used for version verification after update\n */\nexport function getInstalledVersion(): string | null {\n try {\n const output = execSync(`npm list -g ${PACKAGE_NAME} --json`, {\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout: 10000\n }).toString()\n\n const data = JSON.parse(output)\n // npm list output structure: { dependencies: { episoda: { version: \"x.x.x\" } } }\n return data?.dependencies?.[PACKAGE_NAME]?.version || null\n } catch {\n // Package might not be installed globally or npm command failed\n return null\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","/**\n * `episoda clone` command (EP944)\n *\n * Clones a project for multi-module development using worktree architecture.\n *\n * Usage:\n * episoda clone {workspace}/{project}\n *\n * This creates:\n * ~/episoda/{workspace}/{project}/\n * ├── .bare/ # Bare git clone\n * └── .episoda/\n * └── config.json # Project config\n *\n * After cloning, use `episoda checkout {module}` to create worktrees.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { addProject } from '../daemon/project-tracker'\nimport {\n WorktreeManager,\n getEpisodaRoot,\n getProjectPath\n} from '../daemon/worktree-manager'\nimport { extractBootstrapScripts } from '../utils/bootstrap'\n\n/**\n * API response for project details\n */\ninterface ProjectDetailsResponse {\n id: string\n uid: string\n name: string\n slug: string\n workspace_slug: string\n repo_url: string | null\n default_branch: string\n}\n\nexport interface CloneCommandOptions {\n apiUrl?: string\n}\n\n/**\n * Clone command - creates an Episoda project\n */\nexport async function cloneCommand(\n slugArg: string,\n options: CloneCommandOptions = {}\n): Promise<void> {\n // Step 1: Parse and validate the slug\n const slugParts = slugArg.split('/')\n if (slugParts.length !== 2 || !slugParts[0] || !slugParts[1]) {\n throw new Error(\n 'Invalid format. Usage: episoda clone {workspace}/{project}\\n' +\n 'Example: episoda clone my-team/my-project'\n )\n }\n\n const [workspaceSlug, projectSlug] = slugParts\n\n status.info(`Cloning ${workspaceSlug}/${projectSlug}...`)\n status.info('')\n\n // Step 2: Load auth config\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error(\n 'Not authenticated. Please run `episoda auth` first.'\n )\n }\n\n const apiUrl = options.apiUrl || config.api_url || 'https://episoda.dev'\n\n // Step 3: Check if project already exists locally\n const projectPath = getProjectPath(workspaceSlug, projectSlug)\n if (fs.existsSync(projectPath)) {\n const bareRepoPath = path.join(projectPath, '.bare')\n if (fs.existsSync(bareRepoPath)) {\n status.warning(`Project already cloned at ${projectPath}`)\n status.info('')\n status.info('Next steps:')\n status.info(` • cd ${projectPath}`)\n status.info(' • episoda checkout {module}')\n return\n }\n throw new Error(\n `Directory exists but is not an Episoda project: ${projectPath}\\n` +\n 'Please remove it manually or use a different location.'\n )\n }\n\n // Step 4: Fetch project details from API\n status.info('Fetching project details...')\n const projectDetails = await fetchProjectDetails(\n apiUrl,\n workspaceSlug,\n projectSlug,\n config.access_token\n )\n\n if (!projectDetails.repo_url) {\n throw new Error(\n `Project \"${projectSlug}\" has no repository configured.\\n` +\n 'Please configure a repository in the project settings on episoda.dev.'\n )\n }\n\n // Validate repo URL before cloning (security check)\n validateRepoUrl(projectDetails.repo_url)\n\n status.success(`✓ Found project: ${projectDetails.name}`)\n status.info(` Repository: ${projectDetails.repo_url}`)\n status.info('')\n\n // Step 5: Create project directory\n status.info('Creating project directory...')\n const episodaRoot = getEpisodaRoot()\n fs.mkdirSync(projectPath, { recursive: true })\n status.success(`✓ Created ${projectPath}`)\n\n // Step 6: Clone bare repository\n status.info('Cloning repository (bare)...')\n try {\n const worktreeManager = await WorktreeManager.createProject(\n projectPath,\n projectDetails.repo_url,\n projectDetails.id,\n workspaceSlug,\n projectSlug\n )\n\n status.success('✓ Repository cloned')\n status.info('')\n\n // Step 7: Extract api-helper.sh to .episoda/scripts/ (EP964)\n const bareRepoPath = worktreeManager.getBareRepoPath()\n await extractBootstrapScripts(bareRepoPath, projectPath)\n status.success('✓ Bootstrap scripts extracted')\n status.info('')\n\n // Step 8: Register with project tracker (EP971: all projects use worktree architecture)\n addProject(projectDetails.id, projectPath, {\n bareRepoPath\n })\n status.success('✓ Project registered with daemon')\n status.info('')\n\n // Success message\n status.success('Project cloned successfully!')\n status.info('')\n status.info('Project structure:')\n status.info(` ${projectPath}/`)\n status.info(' ├── .bare/ # Git repository')\n status.info(' └── .episoda/')\n status.info(' ├── config.json # Project config')\n status.info(' └── scripts/')\n status.info(' └── api-helper.sh # API authentication')\n status.info('')\n status.info('Next steps:')\n status.info(` 1. cd ${projectPath}`)\n status.info(' 2. source .episoda/scripts/api-helper.sh # Load API helpers')\n status.info(' 3. episoda checkout {moduleUid} # e.g., episoda checkout EP100')\n status.info('')\n\n } catch (error) {\n // Cleanup on failure\n if (fs.existsSync(projectPath)) {\n try {\n fs.rmSync(projectPath, { recursive: true, force: true })\n } catch {\n // Ignore cleanup errors\n }\n }\n throw error\n }\n}\n\n/**\n * Validate that a repository URL is safe to clone\n * Prevents command injection and other security issues\n */\nfunction validateRepoUrl(url: string): void {\n // Check for command injection characters\n const dangerousChars = /[;|&$`\\\\<>(){}[\\]!#*?~'\"]/\n if (dangerousChars.test(url)) {\n throw new Error(\n 'Repository URL contains invalid characters.\\n' +\n 'Please check the repository configuration on episoda.dev.'\n )\n }\n\n // Try to parse as URL\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n // Allow SSH-style URLs (git@github.com:owner/repo.git)\n if (/^[\\w.-]+@[\\w.-]+:[\\w./-]+$/.test(url)) {\n return\n }\n throw new Error(\n 'Repository URL is not a valid URL format.\\n' +\n `Received: ${url}`\n )\n }\n\n // Only allow safe protocols\n const allowedProtocols = ['https:', 'http:', 'git:', 'ssh:']\n if (!allowedProtocols.includes(parsed.protocol)) {\n throw new Error(\n `Repository URL uses unsupported protocol: ${parsed.protocol}\\n` +\n 'Allowed protocols: https, http, git, ssh'\n )\n }\n\n // Ensure hostname is present and reasonable\n if (!parsed.hostname || parsed.hostname.length < 3) {\n throw new Error(\n 'Repository URL has invalid hostname.\\n' +\n `Received: ${url}`\n )\n }\n}\n\n/**\n * Fetch project details from the API\n */\nasync function fetchProjectDetails(\n apiUrl: string,\n workspaceSlug: string,\n projectSlug: string,\n accessToken: string\n): Promise<ProjectDetailsResponse> {\n const url = `${apiUrl}/api/projects/by-slug/${workspaceSlug}/${projectSlug}`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (response.status === 404) {\n throw new Error(\n `Project \"${workspaceSlug}/${projectSlug}\" not found.\\n` +\n 'Check the workspace and project slugs are correct.'\n )\n }\n\n if (response.status === 401) {\n throw new Error(\n 'Authentication expired. Please run `episoda auth` again.'\n )\n }\n\n if (!response.ok) {\n const errorBody = await response.text()\n throw new Error(\n `Failed to fetch project details: ${response.status}\\n${errorBody}`\n )\n }\n\n const data = await response.json() as ProjectDetailsResponse\n\n // Validate required fields\n if (!data.id || !data.slug) {\n throw new Error('Invalid project response from server')\n }\n\n return data\n}\n\n","/**\n * Project tracking for daemon\n *\n * Manages ~/.episoda/projects.json which tracks all projects\n * the daemon is monitoring.\n *\n * Format:\n * {\n * \"projects\": [\n * {\n * \"id\": \"proj_abc123\",\n * \"path\": \"/Users/alan/Dev/my-project\",\n * \"name\": \"my-project\",\n * \"added_at\": \"2025-01-18T10:30:00.000Z\",\n * \"last_active\": \"2025-01-18T12:45:00.000Z\"\n * }\n * ]\n * }\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { getConfigDir } from '@episoda/core'\n\nexport interface TrackedProject {\n id: string // Supabase project ID\n path: string // Absolute path to project directory\n name: string // Project name (from directory)\n added_at: string // ISO timestamp when first added\n last_active: string // ISO timestamp of last activity\n // EP971: All projects use worktree architecture\n bareRepoPath?: string // Path to .bare/ directory\n}\n\ninterface ProjectsData {\n projects: TrackedProject[]\n}\n\n/**\n * Get path to projects.json file\n */\nfunction getProjectsFilePath(): string {\n return path.join(getConfigDir(), 'projects.json')\n}\n\n/**\n * Read projects from file\n *\n * @returns Projects data, or empty list if file doesn't exist\n */\nfunction readProjects(): ProjectsData {\n const projectsPath = getProjectsFilePath()\n\n try {\n if (!fs.existsSync(projectsPath)) {\n return { projects: [] }\n }\n\n const content = fs.readFileSync(projectsPath, 'utf-8')\n const data = JSON.parse(content) as ProjectsData\n\n // Validate structure\n if (!data.projects || !Array.isArray(data.projects)) {\n console.warn('Invalid projects.json structure, resetting')\n return { projects: [] }\n }\n\n return data\n } catch (error) {\n console.error('Error reading projects.json:', error)\n return { projects: [] }\n }\n}\n\n/**\n * Write projects to file\n */\nfunction writeProjects(data: ProjectsData): void {\n const projectsPath = getProjectsFilePath()\n\n try {\n // Ensure directory exists\n const dir = path.dirname(projectsPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n fs.writeFileSync(projectsPath, JSON.stringify(data, null, 2), 'utf-8')\n } catch (error) {\n throw new Error(`Failed to write projects.json: ${error}`)\n }\n}\n\n/**\n * Options for adding a project\n * EP971: All projects use worktree architecture\n */\nexport interface AddProjectOptions {\n bareRepoPath?: string // Path to .bare/ directory\n}\n\n/**\n * Add or update a project\n *\n * EP593: Now enforces one entry per projectId to prevent duplicate connections.\n * When adding a project:\n * - If same path exists, update last_active\n * - If same projectId exists with different path, REPLACE the old entry\n * (user wants git operations in the new directory)\n * - Otherwise, add new project\n *\n * EP971: All projects use worktree architecture.\n *\n * @param projectId Supabase project ID\n * @param projectPath Absolute path to project\n * @param options Optional settings (bareRepoPath)\n * @returns The tracked project\n */\nexport function addProject(\n projectId: string,\n projectPath: string,\n options?: AddProjectOptions\n): TrackedProject {\n const data = readProjects()\n const now = new Date().toISOString()\n\n // Check if project already exists by path (exact match)\n const existingByPath = data.projects.find(p => p.path === projectPath)\n\n if (existingByPath) {\n // Update existing project\n existingByPath.id = projectId // Update ID in case it changed\n existingByPath.last_active = now\n // EP971: Update bare repo path if specified\n if (options?.bareRepoPath) {\n existingByPath.bareRepoPath = options.bareRepoPath\n }\n writeProjects(data)\n return existingByPath\n }\n\n // EP593: Check if project exists by ID (different path)\n // This prevents multiple entries for the same project from different directories\n const existingByIdIndex = data.projects.findIndex(p => p.id === projectId)\n\n if (existingByIdIndex !== -1) {\n const existingById = data.projects[existingByIdIndex]\n console.log(`[ProjectTracker] Replacing project entry: ${existingById.path} -> ${projectPath}`)\n\n // Remove the old entry\n data.projects.splice(existingByIdIndex, 1)\n }\n\n // Add new project\n const projectName = path.basename(projectPath)\n const newProject: TrackedProject = {\n id: projectId,\n path: projectPath,\n name: projectName,\n added_at: now,\n last_active: now,\n // EP971: Bare repo path for worktree architecture\n bareRepoPath: options?.bareRepoPath,\n }\n\n data.projects.push(newProject)\n writeProjects(data)\n\n return newProject\n}\n\n/**\n * Remove a project\n *\n * @param projectPath Absolute path to project\n * @returns true if removed, false if not found\n */\nexport function removeProject(projectPath: string): boolean {\n const data = readProjects()\n const initialLength = data.projects.length\n\n data.projects = data.projects.filter(p => p.path !== projectPath)\n\n if (data.projects.length < initialLength) {\n writeProjects(data)\n return true\n }\n\n return false\n}\n\n/**\n * Get a project by path\n *\n * @param projectPath Absolute path to project\n * @returns Project if found, null otherwise\n */\nexport function getProject(projectPath: string): TrackedProject | null {\n const data = readProjects()\n return data.projects.find(p => p.path === projectPath) || null\n}\n\n/**\n * Get a project by ID\n *\n * @param projectId Supabase project ID\n * @returns Project if found, null otherwise\n */\nexport function getProjectById(projectId: string): TrackedProject | null {\n const data = readProjects()\n return data.projects.find(p => p.id === projectId) || null\n}\n\n/**\n * Get all tracked projects\n *\n * @returns Array of tracked projects\n */\nexport function getAllProjects(): TrackedProject[] {\n const data = readProjects()\n return data.projects\n}\n\n/**\n * Update last active timestamp for a project\n *\n * @param projectPath Absolute path to project\n */\nexport function touchProject(projectPath: string): void {\n const data = readProjects()\n const project = data.projects.find(p => p.path === projectPath)\n\n if (project) {\n project.last_active = new Date().toISOString()\n writeProjects(data)\n }\n}\n\n/**\n * Clean up stale projects\n *\n * Removes projects that:\n * - Haven't been active in N days\n * - Directory no longer exists\n *\n * @param maxAgeDays Maximum age in days (default: 30)\n * @returns Number of projects removed\n */\nexport function cleanupStaleProjects(maxAgeDays: number = 30): number {\n const data = readProjects()\n const now = Date.now()\n const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000\n\n const initialLength = data.projects.length\n\n data.projects = data.projects.filter(project => {\n // Check if directory still exists\n if (!fs.existsSync(project.path)) {\n return false\n }\n\n // Check if too old\n const lastActive = new Date(project.last_active).getTime()\n const age = now - lastActive\n\n return age < maxAgeMs\n })\n\n const removedCount = initialLength - data.projects.length\n\n if (removedCount > 0) {\n writeProjects(data)\n }\n\n return removedCount\n}\n\n/**\n * Clear all projects\n *\n * USE WITH CAUTION - removes all tracked projects\n */\nexport function clearAllProjects(): void {\n writeProjects({ projects: [] })\n}\n","/**\n * `episoda checkout` command (EP944)\n *\n * Creates a worktree for a module, enabling isolated development.\n *\n * Usage:\n * episoda checkout {moduleUid}\n * episoda checkout EP100\n *\n * Creates:\n * ~/episoda/{workspace}/{project}/{moduleUid}/\n *\n * The module is checked out on its feature branch. If the branch\n * doesn't exist yet, it's created from the default branch.\n */\n\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchWithRetry } from '../utils/http'\nimport { setupWorktreeEnv } from '../utils/env-setup'\nimport {\n WorktreeManager,\n findProjectRoot\n} from '../daemon/worktree-manager'\n\n/**\n * API response for module details\n */\ninterface ModuleDetailsResponse {\n id: string\n uid: string\n name: string\n branch_name: string | null\n state: string\n project_id: string\n}\n\nexport interface CheckoutCommandOptions {\n apiUrl?: string\n create?: boolean // Create branch if it doesn't exist\n}\n\n/**\n * Checkout command - creates a worktree for a module\n */\nexport async function checkoutCommand(\n moduleUid: string,\n options: CheckoutCommandOptions = {}\n): Promise<void> {\n // Step 1: Validate module UID format\n if (!moduleUid || !moduleUid.match(/^EP\\d+$/)) {\n throw new Error(\n 'Invalid module UID format. Expected format: EP###\\n' +\n 'Example: episoda checkout EP100'\n )\n }\n\n status.info(`Checking out ${moduleUid}...`)\n status.info('')\n\n // Step 2: Find project root (must be in a worktree project)\n const projectRoot = await findProjectRoot(process.cwd())\n if (!projectRoot) {\n throw new Error(\n 'Not in a worktree project.\\n' +\n 'Run this command from within a project cloned with `episoda clone`.\\n' +\n 'Or cd to ~/episoda/{workspace}/{project}/'\n )\n }\n\n // Step 3: Load auth config\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error(\n 'Not authenticated. Please run `episoda auth` first.'\n )\n }\n\n const apiUrl = options.apiUrl || config.api_url || 'https://episoda.dev'\n\n // Step 4: Initialize worktree manager\n const worktreeManager = new WorktreeManager(projectRoot)\n const initialized = await worktreeManager.initialize()\n if (!initialized) {\n throw new Error(\n `Invalid worktree project at ${projectRoot}.\\n` +\n 'The .bare directory or .episoda/config.json may be missing.'\n )\n }\n\n // Check if worktree already exists\n const existing = worktreeManager.getWorktreeByModuleUid(moduleUid)\n if (existing) {\n status.success(`Module ${moduleUid} is already checked out.`)\n status.info('')\n status.info(`Path: ${existing.worktreePath}`)\n status.info(`Branch: ${existing.branchName}`)\n status.info('')\n status.info(`cd ${existing.worktreePath}`)\n return\n }\n\n // Step 5: Fetch module details from API\n status.info('Fetching module details...')\n const worktreeConfig = worktreeManager.getConfig()\n if (!worktreeConfig) {\n throw new Error('Could not read worktree project config')\n }\n\n const moduleDetails = await fetchModuleDetails(\n apiUrl,\n worktreeConfig.projectId,\n moduleUid,\n config.access_token\n )\n\n // Determine branch name\n const branchName = moduleDetails.branch_name || `feature/${moduleUid.toLowerCase()}`\n\n // P1-5: Check if generated branch already exists (local or remote)\n // This prevents conflicts when the branch was created elsewhere\n let createBranch = !moduleDetails.branch_name || options.create\n if (createBranch && !moduleDetails.branch_name) {\n // We're about to generate a new branch - check if it already exists\n const { GitExecutor } = await import('@episoda/core')\n const gitExecutor = new GitExecutor()\n const branchCheck = await gitExecutor.execute(\n { action: 'branch_exists', branch: branchName },\n { cwd: worktreeManager.getBareRepoPath() }\n )\n if (branchCheck.success && branchCheck.details?.branchExists) {\n status.info(`Branch ${branchName} already exists, will use existing`)\n createBranch = false\n }\n }\n\n status.success(`✓ Found module: ${moduleDetails.name || moduleUid}`)\n status.info(` Branch: ${branchName}${createBranch ? ' (will be created)' : ''}`)\n status.info('')\n\n // Step 6: Create the worktree\n status.info('Creating worktree...')\n const result = await worktreeManager.createWorktree(\n moduleUid,\n branchName,\n createBranch\n )\n\n if (!result.success) {\n throw new Error(`Failed to create worktree: ${result.error}`)\n }\n\n status.success('✓ Worktree created')\n status.info('')\n\n // Step 6b: Fetch and write environment variables (EP988)\n try {\n status.info('Configuring environment...')\n const envWritten = await setupWorktreeEnv(result.worktreePath!, apiUrl, config.access_token)\n if (envWritten) {\n status.success('✓ Environment configured')\n }\n status.info('')\n } catch (error) {\n // Non-fatal - warn but continue\n status.warning('Could not configure environment variables')\n if (error instanceof Error) {\n status.warning(` ${error.message}`)\n }\n status.info('')\n }\n\n // Step 7: Update server with checkout status (optional, non-blocking)\n // If we generated the branch name, persist it to the server\n const branchWasGenerated = !moduleDetails.branch_name\n try {\n await updateModuleCheckout(\n apiUrl,\n moduleDetails.id,\n config.access_token,\n branchWasGenerated ? branchName : undefined\n )\n } catch (error) {\n // Don't fail the command if server update fails\n status.warning('Could not update server with checkout status')\n }\n\n // Success message\n status.success(`Module ${moduleUid} checked out successfully!`)\n status.info('')\n status.info(`Path: ${result.worktreePath}`)\n status.info(`Branch: ${branchName}`)\n status.info('')\n status.info('Next step:')\n status.info(` cd ${result.worktreePath}`)\n status.info('')\n}\n\n/**\n * Fetch module details from the API\n */\nasync function fetchModuleDetails(\n apiUrl: string,\n projectId: string,\n moduleUid: string,\n accessToken: string\n): Promise<ModuleDetailsResponse> {\n const url = `${apiUrl}/api/modules/by-uid/${moduleUid}?project_id=${projectId}`\n\n const response = await fetchWithRetry(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (response.status === 404) {\n throw new Error(\n `Module \"${moduleUid}\" not found in this project.\\n` +\n 'Check the module UID is correct.'\n )\n }\n\n if (response.status === 401) {\n throw new Error(\n 'Authentication expired. Please run `episoda auth` again.'\n )\n }\n\n if (!response.ok) {\n const errorBody = await response.text()\n throw new Error(\n `Failed to fetch module details: ${response.status}\\n${errorBody}`\n )\n }\n\n const data = await response.json() as ModuleDetailsResponse\n\n // Validate required fields\n if (!data.id || !data.uid) {\n throw new Error('Invalid module response from server')\n }\n\n return data\n}\n\n/**\n * Update server with checkout status\n * This is a best-effort operation - failures don't block checkout\n * If branchName is provided, it will be persisted to the module record\n */\nasync function updateModuleCheckout(\n apiUrl: string,\n moduleId: string,\n accessToken: string,\n branchName?: string\n): Promise<void> {\n const url = `${apiUrl}/api/modules/${moduleId}/checkout`\n\n const body: Record<string, unknown> = {\n checked_out: true,\n checkout_type: 'worktree'\n }\n\n // Include branch name if it was generated and needs to be persisted\n if (branchName) {\n body.branch_name = branchName\n }\n\n await fetchWithRetry(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n // Don't check response - this is optional\n}\n","/**\n * HTTP utilities for CLI commands\n *\n * EP944: Shared fetch utilities with retry logic for API resilience\n */\n\n/**\n * Retry a fetch request with exponential backoff\n * Does not retry on 401/404 (permanent failures)\n *\n * @param url - The URL to fetch\n * @param options - Standard fetch RequestInit options\n * @param maxRetries - Maximum number of retry attempts (default: 3)\n * @returns The fetch Response\n * @throws Error if all retries fail\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n maxRetries: number = 3\n): Promise<Response> {\n let lastError: Error | null = null\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const response = await fetch(url, options)\n\n // Don't retry on permanent failures\n if (response.status === 401 || response.status === 404) {\n return response\n }\n\n // Success or client error - don't retry\n if (response.ok || (response.status >= 400 && response.status < 500)) {\n return response\n }\n\n // Server error - retry\n lastError = new Error(`Server error: ${response.status}`)\n } catch (error) {\n // Network error - retry\n lastError = error instanceof Error ? error : new Error('Network error')\n }\n\n // Wait before retrying (exponential backoff: 1s, 2s, 4s)\n if (attempt < maxRetries - 1) {\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000))\n }\n }\n\n throw lastError || new Error('Request failed after retries')\n}\n","/**\n * EP988: Environment Variable Setup Utility\n *\n * Shared utility for fetching and writing environment variables to worktrees.\n * Used by both CLI checkout and daemon checkout_module handler.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\n/**\n * Fetch environment variables from the server\n *\n * @param apiUrl - Base API URL (e.g., https://episoda.dev)\n * @param accessToken - OAuth access token\n * @returns Key-value map of env vars, or empty object on error\n */\nexport async function fetchEnvVars(\n apiUrl: string,\n accessToken: string\n): Promise<Record<string, string>> {\n try {\n const url = `${apiUrl}/api/cli/env-vars`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n console.warn(`[env-setup] Failed to fetch env vars: ${response.status}`)\n return {}\n }\n\n const data = await response.json() as { env_vars?: Record<string, string> }\n const envVars = data.env_vars || {}\n\n console.log(`[env-setup] Fetched ${Object.keys(envVars).length} env vars from server`)\n return envVars\n } catch (error) {\n console.warn('[env-setup] Error fetching env vars:', error instanceof Error ? error.message : error)\n return {}\n }\n}\n\n/**\n * Write environment variables to .env file in a directory\n *\n * @param targetPath - Directory path to write .env file to\n * @param envVars - Key-value map of environment variables\n */\nexport function writeEnvFile(\n targetPath: string,\n envVars: Record<string, string>\n): void {\n if (Object.keys(envVars).length === 0) {\n return\n }\n\n const envContent = Object.entries(envVars)\n .map(([key, value]) => {\n // Quote values containing spaces, quotes, newlines, or shell special chars\n if (/[\\s'\"#$`\\\\]/.test(value) || value.includes('\\n')) {\n // Escape backslashes, quotes, and newlines for .env compatibility\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n return `${key}=\"${escaped}\"`\n }\n return `${key}=${value}`\n })\n .join('\\n') + '\\n'\n\n const envPath = path.join(targetPath, '.env')\n fs.writeFileSync(envPath, envContent, { mode: 0o600 })\n console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`)\n}\n\n/**\n * Complete env setup for a worktree: fetch from server and write to .env\n *\n * @param worktreePath - Path to the worktree directory\n * @param apiUrl - Base API URL\n * @param accessToken - OAuth access token\n * @returns true if env vars were written, false otherwise\n */\nexport async function setupWorktreeEnv(\n worktreePath: string,\n apiUrl: string,\n accessToken: string\n): Promise<boolean> {\n const envVars = await fetchEnvVars(apiUrl, accessToken)\n\n if (Object.keys(envVars).length === 0) {\n return false\n }\n\n writeEnvFile(worktreePath, envVars)\n return true\n}\n","/**\n * `episoda release` command (EP944)\n *\n * Removes a worktree for a module, freeing up disk space.\n *\n * Usage:\n * episoda release {moduleUid}\n * episoda release EP100\n *\n * Removes:\n * ~/episoda/{workspace}/{project}/{moduleUid}/\n *\n * The branch is preserved in the repository - only the worktree\n * (working directory) is removed. Use --force to release even\n * with uncommitted changes.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { loadConfig, GitExecutor } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchWithRetry } from '../utils/http'\nimport {\n WorktreeManager,\n findProjectRoot\n} from '../daemon/worktree-manager'\n\nexport interface ReleaseCommandOptions {\n force?: boolean // Force release even with uncommitted changes\n apiUrl?: string\n}\n\n/**\n * Release command - removes a worktree for a module\n */\nexport async function releaseCommand(\n moduleUid: string,\n options: ReleaseCommandOptions = {}\n): Promise<void> {\n // Step 1: Validate module UID format\n if (!moduleUid || !moduleUid.match(/^EP\\d+$/)) {\n throw new Error(\n 'Invalid module UID format. Expected format: EP###\\n' +\n 'Example: episoda release EP100'\n )\n }\n\n status.info(`Releasing ${moduleUid}...`)\n status.info('')\n\n // Step 2: Find project root\n const projectRoot = await findProjectRoot(process.cwd())\n if (!projectRoot) {\n throw new Error(\n 'Not in a worktree project.\\n' +\n 'Run this command from within a project cloned with `episoda clone`.'\n )\n }\n\n // Step 3: Initialize worktree manager\n const worktreeManager = new WorktreeManager(projectRoot)\n const initialized = await worktreeManager.initialize()\n if (!initialized) {\n throw new Error(\n `Invalid worktree project at ${projectRoot}.`\n )\n }\n\n // Step 4: Check if worktree exists\n const existing = worktreeManager.getWorktreeByModuleUid(moduleUid)\n if (!existing) {\n status.warning(`Module ${moduleUid} is not checked out.`)\n status.info('')\n status.info('Available worktrees:')\n const worktrees = worktreeManager.listWorktrees()\n if (worktrees.length === 0) {\n status.info(' (none)')\n } else {\n for (const wt of worktrees) {\n status.info(` ${wt.moduleUid} → ${wt.branchName}`)\n }\n }\n return\n }\n\n // Step 5: Check for uncommitted changes (unless --force)\n if (!options.force) {\n const hasChanges = await checkUncommittedChanges(existing.worktreePath)\n if (hasChanges) {\n throw new Error(\n `Module ${moduleUid} has uncommitted changes.\\n` +\n 'Commit or stash your changes first, or use --force to discard them.'\n )\n }\n }\n\n // Step 6: Check if we're inside the worktree being released\n const currentPath = path.resolve(process.cwd())\n if (currentPath.startsWith(existing.worktreePath)) {\n status.warning('You are inside the worktree being released.')\n status.info(`Please cd to ${projectRoot} first.`)\n throw new Error('Cannot release worktree while inside it')\n }\n\n // EP959-m3: Run cleanup script before removing worktree (if configured)\n const config = await loadConfig()\n const cleanupScript = config?.project_settings?.worktree_cleanup_script\n if (cleanupScript) {\n status.info('Running cleanup script...')\n const cleanupResult = await worktreeManager.runCleanupScript(moduleUid, cleanupScript)\n if (!cleanupResult.success) {\n // Cleanup failure is a warning, not a blocking error\n status.warning(`Cleanup script failed: ${cleanupResult.error}`)\n status.info('Continuing with worktree removal...')\n } else {\n status.success('✓ Cleanup script completed')\n }\n }\n\n // Step 7: Remove the worktree\n status.info('Removing worktree...')\n const result = await worktreeManager.removeWorktree(moduleUid, options.force)\n\n if (!result.success) {\n throw new Error(`Failed to remove worktree: ${result.error}`)\n }\n\n status.success('✓ Worktree removed')\n status.info('')\n\n // Step 8: Update server (optional, non-blocking)\n // EP959-m3: Reuse config loaded earlier for cleanup script check\n try {\n if (config?.access_token) {\n const worktreeConfig = worktreeManager.getConfig()\n if (worktreeConfig) {\n await updateModuleRelease(\n config.api_url || 'https://episoda.dev',\n worktreeConfig.projectId,\n worktreeConfig.workspaceSlug,\n moduleUid,\n config.access_token\n )\n }\n }\n } catch (error) {\n // Don't fail the command if server update fails\n }\n\n // Success message\n status.success(`Module ${moduleUid} released successfully!`)\n status.info('')\n status.info(`Branch \"${existing.branchName}\" is preserved in the repository.`)\n status.info('')\n status.info('To check out again:')\n status.info(` episoda checkout ${moduleUid}`)\n status.info('')\n}\n\n/**\n * Check if a worktree has uncommitted changes\n */\nasync function checkUncommittedChanges(worktreePath: string): Promise<boolean> {\n const gitExecutor = new GitExecutor()\n\n const result = await gitExecutor.execute(\n { action: 'status' },\n { cwd: worktreePath }\n )\n\n if (!result.success) {\n // Assume has changes if we can't check\n return true\n }\n\n // Check for uncommitted files\n const uncommitted = result.details?.uncommittedFiles || []\n return uncommitted.length > 0\n}\n\n/**\n * Update server with release status\n * EP944: Uses existing checkout/release endpoint which supports UIDs\n */\nasync function updateModuleRelease(\n apiUrl: string,\n projectId: string,\n workspaceSlug: string,\n moduleUid: string,\n accessToken: string\n): Promise<void> {\n // Use the existing checkout/release endpoint which supports UIDs via resolveModuleId\n const url = `${apiUrl}/api/modules/${moduleUid}/checkout/release`\n\n await fetchWithRetry(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'X-Project-Id': projectId,\n 'X-Workspace-Slug': workspaceSlug\n }\n })\n}\n","/**\n * `episoda list` command (EP944)\n *\n * Lists all cloned projects and their worktrees.\n *\n * Usage:\n * episoda list # List all projects\n * episoda list worktrees # List worktrees for current project\n *\n * Shows:\n * - Cloned projects under ~/episoda/\n * - Active worktrees for each project\n * - Uncommitted change status\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport chalk from 'chalk'\nimport { GitExecutor } from '@episoda/core'\nimport { status } from '../output'\nimport {\n WorktreeManager,\n getEpisodaRoot,\n isWorktreeProject,\n findProjectRoot,\n WorktreeInfo\n} from '../daemon/worktree-manager'\n\nexport interface ListCommandOptions {\n verbose?: boolean\n}\n\n/**\n * List command - shows cloned projects and worktrees\n */\nexport async function listCommand(\n subcommand?: string,\n options: ListCommandOptions = {}\n): Promise<void> {\n if (subcommand === 'worktrees' || subcommand === 'wt') {\n await listWorktrees(options)\n } else {\n await listProjects(options)\n }\n}\n\n/**\n * List all cloned projects\n */\nasync function listProjects(options: ListCommandOptions): Promise<void> {\n const episodaRoot = getEpisodaRoot()\n\n if (!fs.existsSync(episodaRoot)) {\n status.info('No projects cloned yet.')\n status.info('')\n status.info('Clone a project with:')\n status.info(' episoda clone {workspace}/{project}')\n return\n }\n\n // Scan for projects (workspace/project structure)\n const projects: Array<{\n workspaceSlug: string\n projectSlug: string\n projectPath: string\n worktreeCount: number\n }> = []\n\n // List workspaces\n const workspaces = fs.readdirSync(episodaRoot, { withFileTypes: true })\n .filter(d => d.isDirectory() && !d.name.startsWith('.'))\n\n for (const workspace of workspaces) {\n const workspacePath = path.join(episodaRoot, workspace.name)\n\n // List projects in workspace\n const projectDirs = fs.readdirSync(workspacePath, { withFileTypes: true })\n .filter(d => d.isDirectory() && !d.name.startsWith('.'))\n\n for (const projectDir of projectDirs) {\n const projectPath = path.join(workspacePath, projectDir.name)\n\n // Check if it's a valid worktree project\n if (await isWorktreeProject(projectPath)) {\n const manager = new WorktreeManager(projectPath)\n await manager.initialize()\n const worktrees = manager.listWorktrees()\n\n projects.push({\n workspaceSlug: workspace.name,\n projectSlug: projectDir.name,\n projectPath,\n worktreeCount: worktrees.length\n })\n }\n }\n }\n\n if (projects.length === 0) {\n status.info('No worktree projects found.')\n status.info('')\n status.info('Clone a project with:')\n status.info(' episoda clone {workspace}/{project}')\n return\n }\n\n console.log('')\n console.log(chalk.bold('Cloned Projects'))\n console.log('─'.repeat(60))\n\n for (const project of projects) {\n const worktreeLabel = project.worktreeCount === 0\n ? chalk.gray('(no worktrees)')\n : project.worktreeCount === 1\n ? chalk.cyan('1 worktree')\n : chalk.cyan(`${project.worktreeCount} worktrees`)\n\n console.log('')\n console.log(` ${chalk.bold(project.workspaceSlug)}/${chalk.bold(project.projectSlug)} ${worktreeLabel}`)\n console.log(chalk.gray(` ${project.projectPath}`))\n }\n\n console.log('')\n console.log(chalk.gray('─'.repeat(60)))\n console.log(`${projects.length} project${projects.length === 1 ? '' : 's'}`)\n console.log('')\n\n if (options.verbose) {\n status.info('Commands:')\n status.info(' episoda list worktrees # Show worktrees for current project')\n status.info(' episoda checkout EP### # Create a new worktree')\n }\n}\n\n/**\n * List worktrees for the current project\n */\nasync function listWorktrees(options: ListCommandOptions): Promise<void> {\n const projectRoot = await findProjectRoot(process.cwd())\n if (!projectRoot) {\n throw new Error(\n 'Not in a worktree project.\\n' +\n 'Run this from within a project cloned with `episoda clone`.'\n )\n }\n\n const manager = new WorktreeManager(projectRoot)\n const initialized = await manager.initialize()\n if (!initialized) {\n throw new Error(`Invalid worktree project at ${projectRoot}`)\n }\n\n const config = manager.getConfig()\n const worktrees = manager.listWorktrees()\n\n console.log('')\n console.log(chalk.bold(`Worktrees for ${config?.workspaceSlug}/${config?.projectSlug}`))\n console.log('─'.repeat(60))\n\n if (worktrees.length === 0) {\n console.log('')\n console.log(chalk.gray(' No worktrees checked out'))\n console.log('')\n status.info('Create a worktree with:')\n status.info(' episoda checkout {moduleUid}')\n return\n }\n\n const gitExecutor = new GitExecutor()\n\n for (const wt of worktrees) {\n console.log('')\n\n // Check for uncommitted changes\n let statusIndicator = chalk.green('●')\n let statusNote = ''\n\n try {\n const result = await gitExecutor.execute(\n { action: 'status' },\n { cwd: wt.worktreePath }\n )\n\n if (result.success) {\n const uncommitted = result.details?.uncommittedFiles || []\n if (uncommitted.length > 0) {\n statusIndicator = chalk.yellow('●')\n statusNote = chalk.yellow(` (${uncommitted.length} uncommitted)`)\n }\n }\n } catch {\n statusIndicator = chalk.gray('○')\n }\n\n console.log(` ${statusIndicator} ${chalk.bold(wt.moduleUid)}${statusNote}`)\n console.log(chalk.gray(` Branch: ${wt.branchName}`))\n console.log(chalk.gray(` Path: ${wt.worktreePath}`))\n\n if (options.verbose) {\n console.log(chalk.gray(` Created: ${new Date(wt.createdAt).toLocaleDateString()}`))\n console.log(chalk.gray(` Last accessed: ${new Date(wt.lastAccessed).toLocaleDateString()}`))\n }\n }\n\n console.log('')\n console.log(chalk.gray('─'.repeat(60)))\n console.log(`${worktrees.length} worktree${worktrees.length === 1 ? '' : 's'}`)\n console.log('')\n console.log(chalk.gray('● clean ') + chalk.yellow('● uncommitted changes'))\n console.log('')\n}\n\n","/**\n * `episoda update` command\n *\n * EP989: Manual version checking and updates for the Episoda CLI.\n *\n * Usage:\n * episoda update - Check and prompt for update interactively\n * episoda update -y - Auto-update without prompting\n * episoda update --check - Just check, don't update\n * episoda update --restart - After update, restart daemon if running\n */\n\nimport { execSync, spawn } from 'child_process'\nimport { VERSION } from '@episoda/core'\nimport { checkForUpdates, getInstalledVersion } from '../utils/update-checker'\nimport { getStatus } from '../ipc/ipc-client'\nimport { status } from '../output'\n\nexport interface UpdateOptions {\n yes?: boolean // -y: Auto-update without prompting\n check?: boolean // --check: Just check, don't update\n restart?: boolean // --restart: Restart daemon after update\n}\n\n/**\n * Check if daemon is currently running\n */\nasync function isDaemonRunning(): Promise<boolean> {\n try {\n const daemonStatus = await getStatus()\n return !!daemonStatus\n } catch {\n return false\n }\n}\n\n/**\n * Stop the daemon gracefully\n */\nfunction stopDaemon(): boolean {\n try {\n execSync('episoda stop', { stdio: 'pipe' })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Start the daemon in detached mode\n */\nfunction startDaemon(): boolean {\n try {\n // Start daemon in detached mode using async spawn\n // This allows the daemon to run independently of this process\n const child = spawn('episoda', ['dev'], {\n detached: true,\n stdio: 'ignore',\n shell: process.platform === 'win32'\n })\n child.unref() // Allow parent to exit independently\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Perform the npm update synchronously\n * Returns true if update was successful\n */\nfunction performUpdate(): { success: boolean; error?: string } {\n try {\n // Use execSync for synchronous update so we can restart daemon after\n execSync('npm update -g episoda', {\n stdio: 'pipe',\n timeout: 120000 // 2 minute timeout\n })\n return { success: true }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n // Check for common permission errors\n if (message.includes('EACCES') || message.includes('permission denied')) {\n return {\n success: false,\n error: 'Permission denied. Try running with sudo:\\n sudo npm update -g episoda'\n }\n }\n\n return { success: false, error: message }\n }\n}\n\nexport async function updateCommand(options: UpdateOptions = {}): Promise<void> {\n const currentVersion = VERSION\n\n // Check for updates\n status.info(`Current version: ${currentVersion}`)\n status.info('Checking for updates...')\n\n const result = await checkForUpdates(currentVersion)\n\n // EP989: Handle offline/network error case separately\n if (result.offline) {\n status.warning('⚠ Could not check for updates (offline or network error)')\n status.info(` Current version: ${currentVersion}`)\n status.info('')\n status.info('Check your internet connection and try again.')\n return\n }\n\n if (!result.updateAvailable) {\n status.success(`✓ Already up to date (${currentVersion})`)\n return\n }\n\n status.info(`Update available: ${result.currentVersion} → ${result.latestVersion}`)\n\n // If --check flag, just show info and exit\n if (options.check) {\n status.info('')\n status.info('Run \"episoda update\" to install the update.')\n return\n }\n\n // If not -y flag, prompt for confirmation (but we can't do interactive prompts easily)\n // For non-interactive use, we'll proceed if -y is set, otherwise show instructions\n if (!options.yes) {\n status.info('')\n status.info('To update, run:')\n status.info(' episoda update -y')\n status.info('')\n status.info('Or with daemon restart:')\n status.info(' episoda update -y --restart')\n return\n }\n\n // Check if daemon is running (for restart logic)\n const daemonWasRunning = options.restart ? await isDaemonRunning() : false\n\n // Stop daemon before update if --restart flag is set and daemon is running\n if (options.restart && daemonWasRunning) {\n status.info('Stopping daemon before update...')\n if (!stopDaemon()) {\n status.warning('Could not stop daemon gracefully, continuing with update...')\n }\n }\n\n // Perform the update\n status.info('Updating...')\n const updateResult = performUpdate()\n\n if (!updateResult.success) {\n status.error(`✗ Update failed: ${updateResult.error}`)\n\n // If we stopped the daemon, try to restart it even though update failed\n if (options.restart && daemonWasRunning) {\n status.info('Restarting daemon...')\n startDaemon()\n }\n\n process.exit(1)\n }\n\n // EP989: Verify the update actually installed the new version\n const installedVersion = getInstalledVersion()\n if (installedVersion && installedVersion !== result.latestVersion) {\n status.warning(`⚠ Update completed but version mismatch detected`)\n status.info(` Expected: ${result.latestVersion}`)\n status.info(` Installed: ${installedVersion}`)\n status.info('')\n status.info('Try running manually: npm update -g episoda')\n } else if (installedVersion) {\n status.success(`✓ Updated to ${installedVersion}`)\n } else {\n // Could not verify, but npm command succeeded\n status.success(`✓ Updated to ${result.latestVersion}`)\n }\n\n // Restart daemon if requested and it was running\n if (options.restart && daemonWasRunning) {\n status.info('Restarting daemon...')\n\n // Small delay to ensure update is complete\n await new Promise(resolve => setTimeout(resolve, 1000))\n\n if (startDaemon()) {\n status.success('✓ Daemon restarted')\n status.info('')\n status.info('Run \"episoda status\" to verify connection.')\n } else {\n status.warning('Could not restart daemon automatically.')\n status.info('Run \"episoda dev\" to start the daemon.')\n }\n } else if (options.restart) {\n // --restart was set but daemon wasn't running\n status.info('Daemon was not running, skipping restart.')\n } else {\n // No restart requested, remind user if daemon might need restart\n const daemonRunning = await isDaemonRunning()\n if (daemonRunning) {\n status.info('')\n status.info('Note: Restart the daemon to use the new version:')\n status.info(' episoda stop && episoda dev')\n }\n }\n}\n"],"mappings":"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","names":["exports","exports","status","GitExecutor","fs","path","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","fs","net","resolve","fs","path","import_core","resolve","status","execSync","path","fs","path","import_child_process","reachable","resolve","fs","path","import_child_process","import_core","fs","path","crypto","import_child_process","import_core","resolve","platform","os","fs","path","import_child_process","import_core","installGitCredentialHelper","updateShellProfile","platform","import_core","import_child_process","fs","path","import_core","fs","path","import_core","addProject","addProject","import_core","resolve","fs","path","GitExecutor","path","import_core","fs","path","import_chalk","import_core","chalk","import_child_process","import_core","isDaemonRunning","stopDaemon","startDaemon","resolve"]}
|
|
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/daemon/worktree-manager.ts","../src/api/machine-settings.ts","../src/utils/bootstrap.ts","../src/commands/auth.ts","../src/daemon/machine-id.ts","../src/git-helpers/git-credential-helper.ts","../src/commands/connect.ts","../src/commands/status.ts","../src/utils/update-checker.ts","../src/commands/stop.ts","../src/commands/clone.ts","../src/daemon/project-tracker.ts","../src/commands/checkout.ts","../src/utils/http.ts","../src/utils/env-setup.ts","../src/commands/release.ts","../src/commands/list.ts","../src/commands/update.ts","../src/commands/run.ts","../src/utils/env-cache.ts","../src/commands/shell.ts","../src/commands/env.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 // EP831: Find branch by prefix pattern\n case 'find_branch_by_prefix':\n return await this.executeFindBranchByPrefix(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 // EP944: Worktree operations\n case 'worktree_add':\n return await this.executeWorktreeAdd(command, cwd, options)\n case 'worktree_remove':\n return await this.executeWorktreeRemove(command, cwd, options)\n case 'worktree_list':\n return await this.executeWorktreeList(cwd, options)\n case 'worktree_prune':\n return await this.executeWorktreePrune(cwd, options)\n case 'clone_bare':\n return await this.executeCloneBare(command, options)\n case 'project_info':\n return await this.executeProjectInfo(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 // EP971: Check if this is a bare repo (git status doesn't work in bare repos)\n try {\n const isBareResult = await execAsync('git rev-parse --is-bare-repository', { cwd, timeout: 5000 })\n if (isBareResult.stdout.trim() === 'true') {\n // In bare repo, get current branch from HEAD ref\n const headResult = await execAsync('git symbolic-ref --short HEAD', { cwd, timeout: 5000 })\n const branchName = headResult.stdout.trim()\n return {\n success: true,\n output: `## ${branchName}`,\n details: {\n uncommittedFiles: [], // No working tree in bare repo\n branchName,\n currentBranch: branchName\n }\n }\n }\n } catch {\n // Not a bare repo or failed to check, continue with normal status\n }\n\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\n /**\n * EP831: Find branch by prefix pattern\n * Searches local and remote branches for one matching the prefix\n */\n private async executeFindBranchByPrefix(\n command: { prefix: string },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const { stdout } = await execAsync(\n 'git branch -a',\n { cwd, timeout: options?.timeout || 10000 }\n )\n\n const prefix = command.prefix\n const branches = stdout.split('\\n')\n .map(line => line.replace(/^[\\s*]*/, '').replace('remotes/origin/', '').trim())\n .filter(branch => branch && !branch.includes('->'))\n\n const matchingBranch = branches.find(branch => branch.startsWith(prefix))\n\n return {\n success: true,\n output: matchingBranch || '',\n details: {\n branchName: matchingBranch || undefined,\n branchExists: !!matchingBranch\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to find branch'\n }\n }\n }\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 // EP944: Worktree operations\n // ========================================\n\n /**\n * EP944: Add a new worktree for a branch\n * Creates a new working tree at the specified path\n */\n private async executeWorktreeAdd(\n command: { path: string; branch: string; create?: boolean; startPoint?: 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 if path already exists\n const fs = await import('fs').then(m => m.promises)\n try {\n await fs.access(command.path)\n return {\n success: false,\n error: 'WORKTREE_EXISTS',\n output: `Worktree already exists at path: ${command.path}`\n }\n } catch {\n // Path doesn't exist, which is what we want\n }\n\n // Fetch latest refs from remote to ensure branch exists\n // This is especially important for branches created after the initial clone\n try {\n await this.runGitCommand(['fetch', '--all', '--prune'], cwd, options)\n } catch {\n // Fetch failure is non-fatal - branch may be local or fetch may not be needed\n }\n\n // Build command\n const args = ['worktree', 'add']\n if (command.create) {\n args.push('-b', command.branch, command.path)\n // EP996: Add start point for new branch to ensure fresh base\n if (command.startPoint) {\n args.push(command.startPoint)\n }\n } else {\n args.push(command.path, command.branch)\n }\n\n const result = await this.runGitCommand(args, cwd, options)\n\n if (result.success) {\n return {\n success: true,\n output: `Created worktree at ${command.path} for branch ${command.branch}`,\n details: {\n worktreePath: command.path,\n branchName: command.branch\n }\n }\n }\n\n // Check for specific error conditions\n if (result.output?.includes('already checked out')) {\n return {\n success: false,\n error: 'BRANCH_IN_USE',\n output: `Branch '${command.branch}' is already checked out in another worktree`\n }\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to add worktree'\n }\n }\n }\n\n /**\n * EP944: Remove a worktree\n * Removes the working tree at the specified path\n */\n private async executeWorktreeRemove(\n command: { path: string; force?: boolean },\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // Check if path exists\n const fs = await import('fs').then(m => m.promises)\n try {\n await fs.access(command.path)\n } catch {\n return {\n success: false,\n error: 'WORKTREE_NOT_FOUND',\n output: `Worktree not found at path: ${command.path}`\n }\n }\n\n // Check for uncommitted changes (unless force)\n if (!command.force) {\n try {\n const { stdout } = await execAsync('git status --porcelain', {\n cwd: command.path,\n timeout: options?.timeout || 10000\n })\n if (stdout.trim()) {\n return {\n success: false,\n error: 'UNCOMMITTED_CHANGES',\n output: 'Worktree has uncommitted changes. Use force to remove anyway.',\n details: {\n uncommittedFiles: stdout.trim().split('\\n').map(line => line.slice(3))\n }\n }\n }\n } catch {\n // If we can't check status, continue with removal\n }\n }\n\n // Build command\n const args = ['worktree', 'remove']\n if (command.force) {\n args.push('--force')\n }\n args.push(command.path)\n\n const result = await this.runGitCommand(args, cwd, options)\n\n if (result.success) {\n return {\n success: true,\n output: `Removed worktree at ${command.path}`,\n details: {\n worktreePath: command.path\n }\n }\n }\n\n // Check for locked worktree\n if (result.output?.includes('locked')) {\n return {\n success: false,\n error: 'WORKTREE_LOCKED',\n output: `Worktree at ${command.path} is locked`\n }\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to remove worktree'\n }\n }\n }\n\n /**\n * EP944: List all worktrees\n * Returns information about all worktrees in the repository\n */\n private async executeWorktreeList(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const { stdout } = await execAsync('git worktree list --porcelain', {\n cwd,\n timeout: options?.timeout || 10000\n })\n\n const worktrees: Array<{\n path: string\n branch: string\n commit: string\n locked?: boolean\n prunable?: boolean\n }> = []\n\n // Parse porcelain output\n // Format:\n // worktree /path/to/worktree\n // HEAD abc123...\n // branch refs/heads/branch-name\n // (blank line)\n const lines = stdout.trim().split('\\n')\n let current: Partial<{\n path: string\n branch: string\n commit: string\n locked: boolean\n prunable: boolean\n }> = {}\n\n for (const line of lines) {\n if (line.startsWith('worktree ')) {\n current.path = line.slice(9)\n } else if (line.startsWith('HEAD ')) {\n current.commit = line.slice(5)\n } else if (line.startsWith('branch ')) {\n // Extract branch name from refs/heads/branch-name\n const refPath = line.slice(7)\n current.branch = refPath.replace('refs/heads/', '')\n } else if (line === 'locked') {\n current.locked = true\n } else if (line === 'prunable') {\n current.prunable = true\n } else if (line.startsWith('detached')) {\n current.branch = 'HEAD (detached)'\n } else if (line === '' && current.path) {\n // End of entry\n worktrees.push({\n path: current.path,\n branch: current.branch || 'unknown',\n commit: current.commit || '',\n locked: current.locked,\n prunable: current.prunable\n })\n current = {}\n }\n }\n\n // Don't forget the last entry if output doesn't end with blank line\n if (current.path) {\n worktrees.push({\n path: current.path,\n branch: current.branch || 'unknown',\n commit: current.commit || '',\n locked: current.locked,\n prunable: current.prunable\n })\n }\n\n return {\n success: true,\n output: `Found ${worktrees.length} worktree(s)`,\n details: {\n worktrees\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to list worktrees'\n }\n }\n }\n\n /**\n * EP944: Prune stale worktrees\n * Removes worktree administrative files for worktrees whose directories are missing\n */\n private async executeWorktreePrune(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n // First get list of prunable worktrees\n const listResult = await this.executeWorktreeList(cwd, options)\n const prunableCount = listResult.details?.worktrees?.filter(w => w.prunable).length || 0\n\n // Run prune\n const result = await this.runGitCommand(['worktree', 'prune'], cwd, options)\n\n if (result.success) {\n return {\n success: true,\n output: prunableCount > 0\n ? `Pruned ${prunableCount} stale worktree(s)`\n : 'No stale worktrees to prune',\n details: {\n prunedCount: prunableCount\n }\n }\n }\n\n return result\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to prune worktrees'\n }\n }\n }\n\n /**\n * EP944: Clone a repository as a bare repository\n * Used for worktree-based development setup\n */\n private async executeCloneBare(\n command: { url: string; path: string },\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const fs = await import('fs').then(m => m.promises)\n const path = await import('path')\n\n // Check if path already exists\n try {\n await fs.access(command.path)\n return {\n success: false,\n error: 'BRANCH_ALREADY_EXISTS', // Reusing for path exists\n output: `Directory already exists at path: ${command.path}`\n }\n } catch {\n // Path doesn't exist, which is what we want\n }\n\n // Create parent directory if needed\n const parentDir = path.dirname(command.path)\n try {\n await fs.mkdir(parentDir, { recursive: true })\n } catch {\n // Directory might already exist\n }\n\n // Clone as bare repository\n const { stdout, stderr } = await execAsync(\n `git clone --bare \"${command.url}\" \"${command.path}\"`,\n { timeout: options?.timeout || 120000 } // 2 minutes for clone\n )\n\n return {\n success: true,\n output: `Cloned bare repository to ${command.path}`,\n details: {\n worktreePath: command.path\n }\n }\n } catch (error: any) {\n // Check for auth errors\n if (error.message?.includes('Authentication') || error.message?.includes('Permission denied')) {\n return {\n success: false,\n error: 'AUTH_FAILURE',\n output: 'Authentication failed. Please check your credentials.'\n }\n }\n\n // Check for network errors\n if (error.message?.includes('Could not resolve') || error.message?.includes('unable to access')) {\n return {\n success: false,\n error: 'NETWORK_ERROR',\n output: 'Network error. Please check your connection.'\n }\n }\n\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to clone repository'\n }\n }\n }\n\n /**\n * EP944: Get project info including worktree mode\n * Returns information about the project configuration\n */\n private async executeProjectInfo(\n cwd: string,\n options?: ExecutionOptions\n ): Promise<ExecutionResult> {\n try {\n const fs = await import('fs').then(m => m.promises)\n const path = await import('path')\n\n // EP971: All projects use worktree architecture\n // Walk up to find the project root with .bare and .episoda directories\n let currentPath = cwd\n let projectPath = cwd\n let bareRepoPath: string | undefined\n\n for (let i = 0; i < 10; i++) {\n const bareDir = path.join(currentPath, '.bare')\n const episodaDir = path.join(currentPath, '.episoda')\n\n try {\n await fs.access(bareDir)\n await fs.access(episodaDir)\n\n // Found project root\n projectPath = currentPath\n bareRepoPath = bareDir\n break\n } catch {\n // Not found at this level, check parent\n const parentPath = path.dirname(currentPath)\n if (parentPath === currentPath) {\n break // Reached filesystem root\n }\n currentPath = parentPath\n }\n }\n\n return {\n success: true,\n output: bareRepoPath ? 'Episoda project' : 'Git repository',\n details: {\n projectPath,\n bareRepoPath\n }\n }\n } catch (error: any) {\n return {\n success: false,\n error: 'UNKNOWN_ERROR',\n output: error.message || 'Failed to get project info'\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\n// EP846-7: Aligned to 20s (was 45s) - server is 15s, so intervals don't collide\nconst CLIENT_HEARTBEAT_INTERVAL = 20000 // 20 seconds (offset from server's 15s)\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 (15s 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 // EP816: Close any existing WebSocket before creating a new one\n // This prevents orphaned connections when connect() is called multiple times\n if (this.ws) {\n try {\n this.ws.removeAllListeners() // Prevent close handler from triggering reconnect\n this.ws.terminate()\n } catch {\n // Ignore errors during cleanup\n }\n this.ws = undefined\n }\n\n // Clear any pending reconnect timer to prevent race conditions\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = undefined\n }\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 * EP812: Register a one-time event handler (removes itself after first call)\n * @param event - Event type\n * @param handler - Handler function\n */\n once(event: string, handler: EventHandler): void {\n const onceHandler: EventHandler = (message) => {\n this.off(event, onceHandler)\n handler(message)\n }\n this.on(event, onceHandler)\n }\n\n /**\n * EP812: Remove an event handler\n * @param event - Event type\n * @param handler - Handler function to remove\n */\n off(event: string, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event)\n if (handlers) {\n const index = handlers.indexOf(handler)\n if (index !== -1) {\n handlers.splice(index, 1)\n }\n }\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 simplified retry logic\n *\n * EP843: Simplified from complex exponential backoff to fast-fail approach\n *\n * Strategy:\n * - For graceful shutdown (server restart): Quick retry (500ms, 1s, 2s) up to 3 attempts\n * - For other disconnects: 1 retry after 1 second, then stop\n * - Always respect rate limits from server\n * - Surface errors quickly so user can take action\n *\n * This replaces the previous 6-hour retry with exponential backoff,\n * which masked problems and delayed error visibility.\n */\n private scheduleReconnect(): void {\n // Don't reconnect if user intentionally disconnected\n if (this.isIntentionalDisconnect) {\n console.log('[EpisodaClient] Intentional disconnect - not reconnecting')\n return\n }\n\n // Don't schedule if reconnection is already pending\n if (this.reconnectTimeout) {\n console.log('[EpisodaClient] Reconnection already scheduled, skipping duplicate')\n return\n }\n\n // 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 // 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\n this.scheduleReconnect()\n }, waitTime)\n return\n }\n\n // EP843: Simplified retry logic\n let delay: number\n let shouldRetry = true\n\n if (this.isGracefulShutdown) {\n // Graceful shutdown (server restart): Quick retry up to 7 attempts\n // EP843: Increased from 3 to 7 to cover longer server restart windows (deploys, migrations)\n // Retry schedule: 500ms, 1s, 2s, 4s, 5s, 5s, 5s = ~22.5s total coverage\n if (this.reconnectAttempts >= 7) {\n console.error('[EpisodaClient] Server restart reconnection failed after 7 attempts. Run \"episoda dev\" to reconnect.')\n shouldRetry = false\n } else {\n // Exponential backoff capped at 5s: 500ms, 1s, 2s, 4s, 5s, 5s, 5s\n delay = Math.min(500 * Math.pow(2, this.reconnectAttempts), 5000)\n console.log(`[EpisodaClient] Server restarting, reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1}/7)`)\n }\n } else {\n // EP956: Non-graceful disconnects (code 1006 etc): 5 retries with exponential backoff\n // Previously only 1 retry, which was too aggressive at failing\n // Retry schedule: 1s, 2s, 4s, 8s, 16s = ~31s total coverage\n const MAX_NON_GRACEFUL_RETRIES = 5\n if (this.reconnectAttempts >= MAX_NON_GRACEFUL_RETRIES) {\n // EP843: Improved error message with more context\n console.error(`[EpisodaClient] Connection lost. Reconnection failed after ${MAX_NON_GRACEFUL_RETRIES} attempts. Check server status or restart with \"episoda dev\".`)\n shouldRetry = false\n } else {\n // Exponential backoff: 1s, 2s, 4s, 8s, 16s\n delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 16000)\n console.log(`[EpisodaClient] Connection lost, retrying in ${delay / 1000}s... (attempt ${this.reconnectAttempts + 1}/${MAX_NON_GRACEFUL_RETRIES})`)\n }\n }\n\n if (!shouldRetry) {\n // Emit disconnected event so daemon can handle it\n this.emit({\n type: 'disconnected',\n code: 1006,\n reason: 'Reconnection attempts exhausted',\n willReconnect: false\n })\n return\n }\n\n this.reconnectAttempts++\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 console.log('[EpisodaClient] Reconnection successful')\n this.reconnectAttempts = 0\n this.isGracefulShutdown = false\n this.firstDisconnectTime = undefined\n this.rateLimitBackoffUntil = undefined\n }).catch(error => {\n console.error('[EpisodaClient] Reconnection failed:', error.message)\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 // EP944: Worktree error messages\n 'WORKTREE_EXISTS': 'Worktree already exists at this path',\n 'WORKTREE_NOT_FOUND': 'Worktree not found at this path',\n 'WORKTREE_LOCKED': 'Worktree is locked',\n 'BRANCH_IN_USE': 'Branch is already checked out in another worktree',\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 { connectCommand } from './commands/connect'\nimport { statusCommand } from './commands/status'\nimport { stopCommand } from './commands/stop'\nimport { cloneCommand } from './commands/clone'\nimport { checkoutCommand } from './commands/checkout'\nimport { releaseCommand } from './commands/release'\nimport { listCommand } from './commands/list'\nimport { updateCommand } from './commands/update'\n// EP998: Runtime environment injection commands\nimport { runCommand } from './commands/run'\nimport { shellCommand } from './commands/shell'\nimport { envListCommand, envSetCommand, envRemoveCommand, envPullCommand } from './commands/env'\n// EP959: Removed migrateCommand - all projects use worktree architecture\n// EP904: Removed tunnel CLI commands - tunnels are now managed exclusively by daemon\nimport { status } from './output'\n\nprogram\n .name('episoda')\n .description('Episoda CLI - local development with git worktree isolation')\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// Connect command (EP591) - Browser-initiated authentication\nprogram\n .command('connect')\n .description('Connect using a pre-authorized code from the browser')\n .requiredOption('--code <code>', 'Connection code from browser')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (options) => {\n try {\n await connectCommand(options)\n } catch (error) {\n status.error(`Connection failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// Dev command (requires project from `episoda clone`)\nprogram\n .command('dev')\n .description('Connect to Episoda and start dev server')\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\n// EP846-6: Now verifies with server by default; --local skips server check\n// EP992: Added --skip-update-check flag to skip version check\nprogram\n .command('status')\n .description('Show connection status')\n .option('--verify', 'Verify connection is healthy (not just connected)')\n .option('--local', 'Only check local daemon state (faster, but may be stale)')\n .option('--skip-update-check', 'Skip CLI version update check (faster)')\n .action(async (options) => {\n try {\n await statusCommand({\n verify: options.verify,\n local: options.local,\n skipUpdateCheck: options.skipUpdateCheck\n })\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\n// EP904: Tunnel CLI commands removed - tunnels are managed exclusively by daemon\n// Tunnels automatically start when module enters doing/review state (module_state_changed event)\n// Tunnels automatically stop when module leaves active zone\n\n// EP989: Update command for manual version checking and updates\nprogram\n .command('update')\n .description('Check for updates and optionally install them')\n .option('-y, --yes', 'Auto-update without prompting')\n .option('--check', 'Just check for updates, do not install')\n .option('--restart', 'Restart daemon after update if it was running')\n .action(async (options) => {\n try {\n await updateCommand(options)\n } catch (error) {\n status.error(`Update failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// EP944: Clone command for multi-module development\nprogram\n .command('clone')\n .description('Clone a project for multi-module development')\n .argument('<workspace/project>', 'Workspace and project slug (e.g., my-team/my-project)')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (slug, options) => {\n try {\n await cloneCommand(slug, options)\n } catch (error) {\n status.error(`Clone failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('checkout')\n .description('Create a worktree for a module')\n .argument('<moduleUid>', 'Module UID (e.g., EP100)')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .option('--create', 'Create branch if it doesn\\'t exist')\n .action(async (moduleUid, options) => {\n try {\n await checkoutCommand(moduleUid, options)\n } catch (error) {\n status.error(`Checkout failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('release')\n .description('Remove a worktree for a module (preserves branch)')\n .argument('<moduleUid>', 'Module UID (e.g., EP100)')\n .option('--force', 'Force release even with uncommitted changes')\n .option('--api-url <url>', 'API URL (default: https://episoda.dev)')\n .action(async (moduleUid, options) => {\n try {\n await releaseCommand(moduleUid, options)\n } catch (error) {\n status.error(`Release failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('list')\n .description('List cloned projects and worktrees')\n .argument('[subcommand]', 'Subcommand: \"worktrees\" or \"wt\" to list worktrees')\n .option('--verbose', 'Show detailed information')\n .action(async (subcommand, options) => {\n try {\n await listCommand(subcommand, options)\n } catch (error) {\n status.error(`List failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// EP959: Removed 'migrate' command - use 'episoda clone' for new projects\n\n// EP932: Dev server management commands\nimport { restartDevServer, getDevServerStatus } from './ipc/ipc-client'\n\nprogram\n .command('dev:restart')\n .description('Restart the dev server for a module')\n .argument('[moduleUid]', 'Module UID (required)')\n .action(async (moduleUid) => {\n try {\n if (!moduleUid) {\n status.error('Module UID is required. Usage: episoda dev:restart <moduleUid>')\n process.exit(1)\n }\n\n status.info(`Restarting dev server for ${moduleUid}...`)\n const result = await restartDevServer(moduleUid)\n\n if (result.success) {\n status.success(`Dev server restarted for ${moduleUid}`)\n } else {\n status.error(`Failed to restart: ${result.error}`)\n process.exit(1)\n }\n } catch (error) {\n status.error(`Dev restart failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('dev:status')\n .description('Show status of running dev servers')\n .action(async () => {\n try {\n const result = await getDevServerStatus()\n\n if (!result.success) {\n status.error('Failed to get dev server status')\n process.exit(1)\n }\n\n if (result.servers.length === 0) {\n status.info('No dev servers running')\n return\n }\n\n console.log('\\nDev Servers:')\n console.log('─'.repeat(80))\n\n for (const server of result.servers) {\n const uptimeFormatted = formatUptime(server.uptime)\n const autoRestart = server.autoRestartEnabled ? 'enabled' : 'disabled'\n const restartInfo = server.restartCount > 0\n ? ` (${server.restartCount} restarts)`\n : ''\n\n console.log(` ${server.moduleUid}`)\n console.log(` Port: ${server.port} PID: ${server.pid || 'N/A'} Uptime: ${uptimeFormatted}${restartInfo}`)\n console.log(` Auto-restart: ${autoRestart}`)\n if (server.logFile) {\n console.log(` Log: ${server.logFile}`)\n }\n console.log()\n }\n } catch (error) {\n status.error(`Status check failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n/**\n * Format uptime in seconds to human-readable string\n */\nfunction formatUptime(seconds: number): string {\n if (seconds < 60) return `${seconds}s`\n if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`\n const hours = Math.floor(seconds / 3600)\n const mins = Math.floor((seconds % 3600) / 60)\n return `${hours}h ${mins}m`\n}\n\n// EP998: Runtime environment injection commands\nprogram\n .command('run')\n .description('Run a command with environment variables injected from the database')\n .argument('<command...>', 'Command to run with injected env vars')\n .option('--no-cache', 'Force fresh fetch from server')\n .option('--cache-ttl <seconds>', 'Cache TTL in seconds (default: 60)', parseInt)\n .option('--offline', 'Use cached values only, no server call')\n .option('--verbose', 'Show fetched variable names')\n .allowUnknownOption()\n .action(async (commandArgs, options) => {\n try {\n // Handle -- separator if present in process.argv\n const args = process.argv.slice(process.argv.indexOf('run') + 1)\n const separatorIndex = args.indexOf('--')\n\n let finalArgs: string[]\n if (separatorIndex >= 0) {\n // Everything after -- is the command\n finalArgs = args.slice(separatorIndex + 1)\n } else {\n // Filter out our options from the command args\n finalArgs = commandArgs.filter((arg: string) =>\n !arg.startsWith('--no-cache') &&\n !arg.startsWith('--cache-ttl') &&\n !arg.startsWith('--offline') &&\n !arg.startsWith('--verbose')\n )\n }\n\n await runCommand(finalArgs, {\n noCache: options.cache === false,\n cacheTtl: options.cacheTtl,\n offline: options.offline,\n verbose: options.verbose\n })\n } catch (error) {\n status.error(`Run failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nprogram\n .command('shell')\n .description('Open an interactive shell with environment variables injected')\n .argument('[shell]', 'Shell to use (default: $SHELL)')\n .option('--print', 'Print export statements instead of opening shell')\n .option('--no-cache', 'Force fresh fetch from server')\n .option('--cache-ttl <seconds>', 'Cache TTL in seconds (default: 60)', parseInt)\n .option('--offline', 'Use cached values only')\n .action(async (shell, options) => {\n try {\n await shellCommand(shell, {\n print: options.print,\n noCache: options.cache === false,\n cacheTtl: options.cacheTtl,\n offline: options.offline\n })\n } catch (error) {\n status.error(`Shell failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\n// EP998: Environment variable management commands\nconst envCmd = program\n .command('env')\n .description('Manage environment variables')\n\nenvCmd\n .command('list')\n .description('List environment variables')\n .option('--show-values', 'Show value previews (masked)')\n .action(async (options) => {\n try {\n await envListCommand({ showValues: options.showValues })\n } catch (error) {\n status.error(`Env list failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nenvCmd\n .command('set')\n .description('Set an environment variable')\n .argument('<key=value>', 'KEY=value pair (omit value to prompt)')\n .option('-e, --environment <env>', 'Environment: all, dev, preview, prod (default: dev)')\n .action(async (keyValue, options) => {\n try {\n await envSetCommand(keyValue, { environment: options.environment })\n } catch (error) {\n status.error(`Env set failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nenvCmd\n .command('remove')\n .description('Remove an environment variable')\n .argument('<key>', 'Variable key to remove')\n .action(async (key) => {\n try {\n await envRemoveCommand(key)\n } catch (error) {\n status.error(`Env remove failed: ${error instanceof Error ? error.message : String(error)}`)\n process.exit(1)\n }\n })\n\nenvCmd\n .command('pull')\n .description('Generate .env file from database (explicit file generation)')\n .option('-f, --file <filename>', 'Output filename (default: .env)')\n .option('--stdout', 'Print to stdout instead of file')\n .action(async (options) => {\n try {\n await envPullCommand({ file: options.file, stdout: options.stdout })\n } catch (error) {\n status.error(`Env pull 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 } from '@episoda/core'\nimport { resolveDevCommand } from '../framework-detector'\nimport { status, printFrameworkDetection } from '../output'\nimport { isDaemonRunning, startDaemon, killAllEpisodaProcesses } from '../daemon/daemon-manager'\nimport { addProject, isDaemonReachable, verifyHealth } from '../ipc/ipc-client'\nimport { spawn, ChildProcess, execSync } from 'child_process'\nimport * as path from 'path'\nimport * as fs from 'fs'\nimport { isPortInUse, getServerPort } from '../utils/port-check'\nimport { isWorktreeProject } from '../daemon/worktree-manager'\nimport { fetchProjectPath, syncProjectPath } from '../api/machine-settings'\nimport { extractBootstrapScripts } from '../utils/bootstrap'\n// EP959: Removed migrateCommand import - all projects use worktree architecture\n// EP960: Removed saveConfig - no longer caching paths locally\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 // EP812: Check if daemon is already running and healthy before killing\n // Only restart if unhealthy - this avoids rate limiting on reconnect\n let needsRestart = false\n const existingPid = isDaemonRunning()\n\n if (existingPid) {\n // Daemon is running - check if it's healthy\n const reachable = await isDaemonReachable()\n if (reachable) {\n try {\n const health = await verifyHealth()\n if (health.healthyConnections > 0) {\n status.debug(`Daemon already running and healthy (PID: ${existingPid})`)\n // Skip cleanup and reuse existing daemon\n } else if (health.staleConnections > 0) {\n status.info('Daemon has stale connections, restarting...')\n needsRestart = true\n } else {\n // No connections but daemon is reachable - it will connect when we add project\n status.debug(`Daemon running but no connections (PID: ${existingPid})`)\n }\n } catch {\n // Health check failed - restart\n status.debug('Health check failed, restarting daemon...')\n needsRestart = true\n }\n } else {\n // Daemon running but not reachable via IPC - restart\n status.debug('Daemon not reachable via IPC, restarting...')\n needsRestart = true\n }\n } else {\n // No daemon running - will start fresh\n needsRestart = false\n }\n\n // Only kill processes if we determined a restart is needed\n if (needsRestart) {\n const killedCount = killAllEpisodaProcesses()\n if (killedCount > 0) {\n status.info(`Cleaned up ${killedCount} stale process${killedCount > 1 ? 'es' : ''}`)\n // EP812: Wait for server rate limit window after killing connected processes\n await new Promise(resolve => setTimeout(resolve, 2000))\n }\n }\n\n // EP960: Server-synced project path resolution\n // Priority: 1. Server-stored path (if valid), 2. Local git detection, 3. Current working directory\n // This replaces the 24-hour local cache that caused stale path issues\n let projectPath: string\n\n // Try to get path from server first\n const serverPath = await fetchProjectPath(config, config.project_id)\n\n if (serverPath && fs.existsSync(serverPath)) {\n // Server path is valid - use it\n projectPath = serverPath\n status.debug(`Using server-synced project path: ${projectPath}`)\n } else {\n // Server path missing or invalid - detect locally\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 // Sync detected path to server (background, non-blocking)\n syncProjectPath(config, config.project_id, projectPath)\n .then(synced => {\n if (synced) {\n status.debug('Project path synced to server')\n }\n })\n .catch(() => {}) // Ignore errors - sync is best-effort\n }\n\n // EP971: Validate this is an Episoda project (has .bare/ directory)\n const isWorktree = await isWorktreeProject(projectPath)\n\n if (!isWorktree) {\n status.error('Not an Episoda project.')\n status.info('')\n status.info('Use `episoda clone {workspace}/{project}` to set up a project.')\n status.info('')\n process.exit(1)\n }\n\n // EP984: Ensure bootstrap scripts exist (for pre-EP964 installations or new machines)\n const bareRepoPath = path.join(projectPath, '.bare')\n const scriptsExtracted = await extractBootstrapScripts(bareRepoPath, projectPath)\n if (scriptsExtracted) {\n status.success('✓ Bootstrap scripts extracted')\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 } else if (!shuttingDown) {\n // EP813: Dev server failed but daemon is still connected\n // Fall back to connection-only mode messaging\n status.info('')\n status.info('Dev server stopped, but daemon remains connected.')\n status.info('Git operations will still be executed by Episoda.')\n status.info('Press Ctrl+C to disconnect.')\n status.info('')\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 * EP986: Install command detection result\n */\nexport interface InstallCommand {\n command: string[]\n description: string\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 * EP986: Detect and return the appropriate dependency install command\n *\n * Detection priority (lock files first for exact versions):\n * 1. bun.lockb → bun install\n * 2. pnpm-lock.yaml → pnpm install\n * 3. yarn.lock → yarn install\n * 4. package-lock.json → npm ci (exact versions)\n * 5. package.json → npm install (fallback)\n * 6. Pipfile.lock → pipenv install\n * 7. poetry.lock → poetry install\n * 8. requirements.txt → pip install -r requirements.txt\n * 9. Gemfile.lock / Gemfile → bundle install\n * 10. go.sum / go.mod → go mod download\n * 11. Cargo.lock / Cargo.toml → cargo build\n *\n * @param cwd - Working directory to search\n * @returns Install command info or null if no package manager detected\n */\nexport function getInstallCommand(cwd: string): InstallCommand | null {\n // Node.js - check lock files first for specific package manager\n // Bun first (fastest runtime)\n if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {\n return {\n command: ['bun', 'install'],\n description: 'Installing dependencies with bun',\n detectedFrom: 'bun.lockb'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {\n return {\n command: ['pnpm', 'install'],\n description: 'Installing dependencies with pnpm',\n detectedFrom: 'pnpm-lock.yaml'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {\n return {\n command: ['yarn', 'install'],\n description: 'Installing dependencies with yarn',\n detectedFrom: 'yarn.lock'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {\n return {\n command: ['npm', 'ci'],\n description: 'Installing dependencies with npm ci',\n detectedFrom: 'package-lock.json'\n }\n }\n\n // Fallback to npm install if only package.json exists\n if (fs.existsSync(path.join(cwd, 'package.json'))) {\n return {\n command: ['npm', 'install'],\n description: 'Installing dependencies with npm',\n detectedFrom: 'package.json'\n }\n }\n\n // Python - check lock files first\n if (fs.existsSync(path.join(cwd, 'Pipfile.lock')) || fs.existsSync(path.join(cwd, 'Pipfile'))) {\n return {\n command: ['pipenv', 'install'],\n description: 'Installing dependencies with pipenv',\n detectedFrom: fs.existsSync(path.join(cwd, 'Pipfile.lock')) ? 'Pipfile.lock' : 'Pipfile'\n }\n }\n\n // Poetry detection: check lock file first, then pyproject.toml\n if (fs.existsSync(path.join(cwd, 'poetry.lock'))) {\n return {\n command: ['poetry', 'install'],\n description: 'Installing dependencies with poetry',\n detectedFrom: 'poetry.lock'\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'pyproject.toml'))) {\n const pyprojectPath = path.join(cwd, 'pyproject.toml')\n const content = fs.readFileSync(pyprojectPath, 'utf-8')\n if (content.includes('[tool.poetry]')) {\n return {\n command: ['poetry', 'install'],\n description: 'Installing dependencies with poetry',\n detectedFrom: 'pyproject.toml'\n }\n }\n }\n\n if (fs.existsSync(path.join(cwd, 'requirements.txt'))) {\n return {\n command: ['pip', 'install', '-r', 'requirements.txt'],\n description: 'Installing dependencies with pip',\n detectedFrom: 'requirements.txt'\n }\n }\n\n // Ruby\n if (fs.existsSync(path.join(cwd, 'Gemfile.lock')) || fs.existsSync(path.join(cwd, 'Gemfile'))) {\n return {\n command: ['bundle', 'install'],\n description: 'Installing dependencies with bundler',\n detectedFrom: fs.existsSync(path.join(cwd, 'Gemfile.lock')) ? 'Gemfile.lock' : 'Gemfile'\n }\n }\n\n // Go\n if (fs.existsSync(path.join(cwd, 'go.sum')) || fs.existsSync(path.join(cwd, 'go.mod'))) {\n return {\n command: ['go', 'mod', 'download'],\n description: 'Downloading Go modules',\n detectedFrom: fs.existsSync(path.join(cwd, 'go.sum')) ? 'go.sum' : 'go.mod'\n }\n }\n\n // Rust\n if (fs.existsSync(path.join(cwd, 'Cargo.lock')) || fs.existsSync(path.join(cwd, 'Cargo.toml'))) {\n return {\n command: ['cargo', 'build'],\n description: 'Building Rust project (downloads dependencies)',\n detectedFrom: fs.existsSync(path.join(cwd, 'Cargo.lock')) ? 'Cargo.lock' : 'Cargo.toml'\n }\n }\n\n // No package manager 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 // EP944: Worktree errors\n WORKTREE_EXISTS: 'Worktree already exists at this path',\n WORKTREE_NOT_FOUND: 'Worktree not found',\n WORKTREE_LOCKED: 'Worktree is locked',\n BRANCH_IN_USE: 'Branch is already checked out in another worktree',\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 // EP813: Debug - log daemon output to file for troubleshooting\n const logPath = path.join(configDir, 'daemon.log')\n const logFd = fs.openSync(logPath, 'a')\n\n // Spawn daemon as detached process\n const child = spawn('node', [daemonScript], {\n detached: true, // Run independently of parent\n stdio: ['ignore', logFd, logFd], // EP813: Redirect stdout/stderr to log file\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/**\n * EP846-6: Verify connection with server\n *\n * Queries the server's /api/cli/status endpoint to verify the connection\n * is actually alive from the server's perspective. This is the authoritative\n * check - local daemon state can be stale.\n *\n * @returns Server verification result\n */\nexport async function verifyServerConnection(): Promise<{\n verified: boolean\n error?: string\n localConnected: boolean\n serverConnected: boolean\n machineMatch?: boolean\n machineId?: string\n serverMachineId?: string | null\n serverError?: string | null\n actuallyConnected: boolean\n}> {\n return await sendCommand('verify-server-connection', {}, 10000) // 10s timeout for network call\n}\n\n// EP734: Removed getConnectionStatus - no longer needed with blocking add-project\n\n/**\n * EP823: Get tunnel status from daemon\n *\n * Returns all running tunnels managed by the daemon's TunnelManager.\n * This is the authoritative source of tunnel state since the daemon\n * holds the actual cloudflared processes.\n *\n * @returns List of running tunnels with their info\n */\nexport async function getTunnelStatus(): Promise<{\n tunnels: Array<{\n moduleUid: string\n url: string\n port: number\n status: string\n pid?: number\n startedAt: string // Date serialized as ISO string\n error?: string\n }>\n}> {\n return await sendCommand('tunnel-status')\n}\n\n/**\n * EP823: Stop a tunnel via daemon\n *\n * Stops a tunnel by module UID, including:\n * - Killing the cloudflared process\n * - Stopping the associated dev server\n * - Clearing the tunnel URL from the API\n *\n * @param moduleUid The module UID of the tunnel to stop\n * @returns Success status\n */\nexport async function stopTunnel(moduleUid: string): Promise<{\n success: boolean\n error?: string\n}> {\n return await sendCommand('tunnel-stop', { moduleUid })\n}\n\n/**\n * EP932: Restart dev server for a module\n *\n * Restarts the dev server with the enhanced auto-restart logic:\n * - Stops the current server gracefully\n * - Kills any hung processes on the port\n * - Starts a fresh server with auto-restart enabled\n *\n * @param moduleUid The module UID of the dev server to restart\n * @returns Success status with error message if failed\n */\nexport async function restartDevServer(moduleUid: string): Promise<{\n success: boolean\n error?: string\n}> {\n return await sendCommand('dev-server-restart', { moduleUid })\n}\n\n/**\n * EP932: Get status of all running dev servers\n *\n * Returns information about all dev servers managed by the daemon:\n * - Module UID\n * - Port\n * - PID\n * - Uptime\n * - Restart count\n * - Log file location\n *\n * @returns List of running dev servers with their status\n */\nexport async function getDevServerStatus(): Promise<{\n success: boolean\n servers: Array<{\n moduleUid: string\n port: number\n pid: number | undefined\n startedAt: string // ISO date string\n uptime: number // seconds\n restartCount: number\n lastRestartAt: string | null // ISO date string\n autoRestartEnabled: boolean\n logFile: string | null\n }>\n}> {\n return await sendCommand('dev-server-status')\n}\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 * Worktree Manager\n *\n * EP944: Manages git worktrees for multi-module development.\n * Each module gets its own worktree, enabling parallel development.\n *\n * Directory Structure:\n * ~/episoda/{workspace}/{project}/\n * ├── .bare/ # Bare git clone\n * ├── .episoda/\n * │ └── config.json # Project worktree config\n * ├── EP100/ # Module worktree\n * └── EP101/ # Another module worktree\n *\n * EP995 EVALUATION: Local worktrees[] array vs Server-side state\n * ---------------------------------------------------------------\n * With EP995, the server now stores:\n * - module.worktree_path - filesystem path on checkout_machine_id\n * - module.worktree_status - pending/setup/ready/error\n * - module.worktree_error - error message if setup failed\n *\n * The daemon does reconciliation on connect (reconcileWorktrees), making\n * the local worktrees[] array mostly redundant.\n *\n * RECOMMENDATION: Keep local worktrees[] for now because:\n * 1. It provides instant local lookup without network call\n * 2. Acts as fallback if server unreachable during worktree operations\n * 3. Stores setupStatus for local progress tracking during setup\n * 4. No breaking change - gradual migration is safer\n *\n * FUTURE: Consider removing in a follow-up module when:\n * - EP978 progress infrastructure is complete (UI can show server-side status)\n * - Offline support requirements are clarified\n * - Migration path for existing installations is defined\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { GitExecutor, ExecutionResult } from '@episoda/core'\n\n/**\n * EP957: Validate module UID to prevent path traversal attacks\n *\n * UIDs are server-generated in EPxxx format, but this provides defense-in-depth\n * in case a malformed UID ever reaches the worktree system.\n *\n * @param moduleUid - The module UID to validate\n * @returns true if valid, false if potentially dangerous\n */\nexport function validateModuleUid(moduleUid: string): boolean {\n // Reject empty, null, or whitespace-only\n if (!moduleUid || typeof moduleUid !== 'string' || !moduleUid.trim()) {\n return false\n }\n\n // Reject path traversal attempts\n if (moduleUid.includes('/') || moduleUid.includes('\\\\') || moduleUid.includes('..')) {\n return false\n }\n\n // Reject null bytes (could truncate paths in some systems)\n if (moduleUid.includes('\\0')) {\n return false\n }\n\n // Reject if it would resolve to a hidden directory\n if (moduleUid.startsWith('.')) {\n return false\n }\n\n return true\n}\n\n/**\n * Information about an active worktree\n */\nexport interface WorktreeInfo {\n moduleUid: string // Module UID (e.g., EP100)\n branchName: string // Git branch name\n worktreePath: string // Absolute path to worktree\n createdAt: string // ISO timestamp\n lastAccessed: string // ISO timestamp of last access\n // EP959-11: Setup status tracking\n setupStatus?: 'pending' | 'running' | 'ready' | 'error'\n setupStartedAt?: string // ISO timestamp\n setupCompletedAt?: string // ISO timestamp\n setupError?: string // Error message if setup failed\n}\n\n/**\n * Worktree project configuration\n * Stored in {projectRoot}/.episoda/config.json\n * EP971: All projects use worktree architecture\n */\nexport interface WorktreeProjectConfig {\n projectId: string\n workspaceSlug: string\n projectSlug: string\n bareRepoPath: string // Path to .bare/ directory\n createdAt: string\n worktrees: WorktreeInfo[]\n}\n\n/**\n * Result of a worktree operation\n */\nexport interface WorktreeOperationResult {\n success: boolean\n error?: string\n worktreePath?: string\n worktreeInfo?: WorktreeInfo\n}\n\n/**\n * Manages worktrees for a single project\n */\nexport class WorktreeManager {\n private projectRoot: string\n private bareRepoPath: string\n private configPath: string\n private gitExecutor: GitExecutor\n\n constructor(projectRoot: string) {\n this.projectRoot = projectRoot\n this.bareRepoPath = path.join(projectRoot, '.bare')\n this.configPath = path.join(projectRoot, '.episoda', 'config.json')\n this.gitExecutor = new GitExecutor()\n }\n\n /**\n * Initialize worktree manager from existing project root\n * EP971: All projects use worktree architecture\n * @returns true if valid project, false otherwise\n */\n async initialize(): Promise<boolean> {\n // Check if .bare directory exists\n if (!fs.existsSync(this.bareRepoPath)) {\n return false\n }\n\n // Check if config exists\n if (!fs.existsSync(this.configPath)) {\n return false\n }\n\n try {\n const config = this.readConfig()\n return config !== null\n } catch {\n return false\n }\n }\n\n /**\n * Create a new worktree project from scratch\n */\n static async createProject(\n projectRoot: string,\n repoUrl: string,\n projectId: string,\n workspaceSlug: string,\n projectSlug: string\n ): Promise<WorktreeManager> {\n const manager = new WorktreeManager(projectRoot)\n\n // Create project directory structure\n const episodaDir = path.join(projectRoot, '.episoda')\n fs.mkdirSync(episodaDir, { recursive: true })\n\n // Clone as bare repository\n const cloneResult = await manager.gitExecutor.execute({\n action: 'clone_bare',\n url: repoUrl,\n path: manager.bareRepoPath\n })\n\n if (!cloneResult.success) {\n throw new Error(`Failed to clone repository: ${cloneResult.output}`)\n }\n\n // Create initial config (EP971: all projects use worktree architecture)\n const config: WorktreeProjectConfig = {\n projectId,\n workspaceSlug,\n projectSlug,\n bareRepoPath: manager.bareRepoPath,\n createdAt: new Date().toISOString(),\n worktrees: []\n }\n\n manager.writeConfig(config)\n\n return manager\n }\n\n /**\n * Create a worktree for a module\n * The entire operation is locked to prevent race conditions\n */\n async createWorktree(\n moduleUid: string,\n branchName: string,\n createBranch: boolean = false\n ): Promise<WorktreeOperationResult> {\n // EP957: Validate module UID to prevent path traversal\n if (!validateModuleUid(moduleUid)) {\n return {\n success: false,\n error: `Invalid module UID: \"${moduleUid}\" - contains disallowed characters`\n }\n }\n\n const worktreePath = path.join(this.projectRoot, moduleUid)\n\n // Acquire lock for the entire check-and-create operation\n const lockAcquired = await this.acquireLock()\n if (!lockAcquired) {\n return {\n success: false,\n error: 'Could not acquire lock for worktree creation'\n }\n }\n\n try {\n // Check if worktree already exists (inside lock to prevent race)\n const existing = this.getWorktreeByModuleUid(moduleUid)\n if (existing) {\n return {\n success: true,\n worktreePath: existing.worktreePath,\n worktreeInfo: existing\n }\n }\n\n // EP945-11: Fetch latest refs from origin before creating worktree\n // This ensures new worktrees are based on current main, not stale local refs\n const fetchResult = await this.gitExecutor.execute({\n action: 'fetch',\n remote: 'origin'\n }, { cwd: this.bareRepoPath })\n\n // EP996: Fail if fetch fails when creating a new branch\n // Without a fresh fetch, the worktree would be based on stale refs\n if (!fetchResult.success && createBranch) {\n console.error('[worktree-manager] Failed to fetch from origin:', fetchResult.output)\n return {\n success: false,\n error: 'Failed to fetch latest refs from origin. Cannot create worktree with stale refs.'\n }\n }\n\n // Create worktree via git\n // EP996: Use origin/main as start point when creating a new branch\n // This ensures the branch is based on the latest remote state, not stale local HEAD\n const result = await this.gitExecutor.execute({\n action: 'worktree_add',\n path: worktreePath,\n branch: branchName,\n create: createBranch,\n startPoint: createBranch ? 'origin/main' : undefined\n }, { cwd: this.bareRepoPath })\n\n if (!result.success) {\n return {\n success: false,\n error: result.output || 'Failed to create worktree'\n }\n }\n\n // Record worktree in config\n const worktreeInfo: WorktreeInfo = {\n moduleUid,\n branchName,\n worktreePath,\n createdAt: new Date().toISOString(),\n lastAccessed: new Date().toISOString()\n }\n\n const config = this.readConfig()\n if (config) {\n config.worktrees.push(worktreeInfo)\n this.writeConfig(config)\n }\n\n return {\n success: true,\n worktreePath,\n worktreeInfo\n }\n } finally {\n this.releaseLock()\n }\n }\n\n /**\n * Remove a worktree for a module\n * P1-2: Wrapped entire operation in lock to prevent race with createWorktree\n */\n async removeWorktree(\n moduleUid: string,\n force: boolean = false\n ): Promise<WorktreeOperationResult> {\n // EP957: Validate module UID to prevent path traversal\n if (!validateModuleUid(moduleUid)) {\n return {\n success: false,\n error: `Invalid module UID: \"${moduleUid}\" - contains disallowed characters`\n }\n }\n\n // Acquire lock for the entire check-and-remove operation\n const lockAcquired = await this.acquireLock()\n if (!lockAcquired) {\n return {\n success: false,\n error: 'Could not acquire lock for worktree removal'\n }\n }\n\n try {\n const existing = this.getWorktreeByModuleUid(moduleUid)\n if (!existing) {\n return {\n success: false,\n error: `No worktree found for module ${moduleUid}`\n }\n }\n\n // Remove worktree via git\n const result = await this.gitExecutor.execute({\n action: 'worktree_remove',\n path: existing.worktreePath,\n force\n }, { cwd: this.bareRepoPath })\n\n if (!result.success) {\n return {\n success: false,\n error: result.output || 'Failed to remove worktree'\n }\n }\n\n // Remove from config (already inside lock, so just read-modify-write)\n const config = this.readConfig()\n if (config) {\n config.worktrees = config.worktrees.filter(w => w.moduleUid !== moduleUid)\n this.writeConfig(config)\n }\n\n return {\n success: true,\n worktreePath: existing.worktreePath\n }\n } finally {\n this.releaseLock()\n }\n }\n\n /**\n * Get worktree path for a module\n */\n getWorktreePath(moduleUid: string): string | null {\n // EP957: Validate module UID for defense-in-depth\n if (!validateModuleUid(moduleUid)) {\n return null\n }\n const worktree = this.getWorktreeByModuleUid(moduleUid)\n return worktree?.worktreePath || null\n }\n\n /**\n * Get worktree info by module UID\n */\n getWorktreeByModuleUid(moduleUid: string): WorktreeInfo | null {\n const config = this.readConfig()\n return config?.worktrees.find(w => w.moduleUid === moduleUid) || null\n }\n\n /**\n * Get worktree info by branch name\n */\n getWorktreeByBranch(branchName: string): WorktreeInfo | null {\n const config = this.readConfig()\n return config?.worktrees.find(w => w.branchName === branchName) || null\n }\n\n /**\n * List all active worktrees\n */\n listWorktrees(): WorktreeInfo[] {\n const config = this.readConfig()\n return config?.worktrees || []\n }\n\n /**\n * EP957: Audit worktrees against known active module UIDs\n *\n * Compares local worktrees against a list of module UIDs that should be active.\n * Used by daemon startup to detect orphaned worktrees from crashed sessions.\n *\n * @param activeModuleUids - UIDs of modules currently in doing/review state\n * @returns Object with orphaned and valid worktree lists\n */\n auditWorktrees(activeModuleUids: string[]): {\n orphaned: WorktreeInfo[]\n valid: WorktreeInfo[]\n } {\n const allWorktrees = this.listWorktrees()\n const activeSet = new Set(activeModuleUids)\n\n const orphaned = allWorktrees.filter(w => !activeSet.has(w.moduleUid))\n const valid = allWorktrees.filter(w => activeSet.has(w.moduleUid))\n\n return { orphaned, valid }\n }\n\n /**\n * Update last accessed timestamp for a worktree\n */\n async touchWorktree(moduleUid: string): Promise<void> {\n await this.updateConfigSafe(config => {\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (worktree) {\n worktree.lastAccessed = new Date().toISOString()\n }\n return config\n })\n }\n\n /**\n * Prune stale worktrees (directories that no longer exist)\n */\n async pruneStaleWorktrees(): Promise<number> {\n // First, run git worktree prune\n await this.gitExecutor.execute({\n action: 'worktree_prune'\n }, { cwd: this.bareRepoPath })\n\n // Then sync our config with reality (with locking)\n let prunedCount = 0\n await this.updateConfigSafe(config => {\n const initialCount = config.worktrees.length\n config.worktrees = config.worktrees.filter(w => fs.existsSync(w.worktreePath))\n prunedCount = initialCount - config.worktrees.length\n return config\n })\n\n return prunedCount\n }\n\n /**\n * Validate all worktrees and sync with git\n */\n async validateWorktrees(): Promise<{\n valid: WorktreeInfo[]\n stale: WorktreeInfo[]\n orphaned: string[]\n }> {\n const config = this.readConfig()\n const valid: WorktreeInfo[] = []\n const stale: WorktreeInfo[] = []\n const orphaned: string[] = []\n\n // Get actual worktrees from git\n const listResult = await this.gitExecutor.execute({\n action: 'worktree_list'\n }, { cwd: this.bareRepoPath })\n\n const actualWorktrees = new Set(\n listResult.details?.worktrees?.map(w => w.path) || []\n )\n\n // Check config worktrees\n for (const worktree of config?.worktrees || []) {\n if (actualWorktrees.has(worktree.worktreePath)) {\n valid.push(worktree)\n actualWorktrees.delete(worktree.worktreePath)\n } else {\n stale.push(worktree)\n }\n }\n\n // Any remaining are orphaned (exist in git but not in config)\n // Filter out the bare repo itself\n for (const wpath of actualWorktrees) {\n if (wpath !== this.bareRepoPath) {\n orphaned.push(wpath)\n }\n }\n\n return { valid, stale, orphaned }\n }\n\n /**\n * Get project configuration\n */\n getConfig(): WorktreeProjectConfig | null {\n return this.readConfig()\n }\n\n /**\n * Get the bare repo path\n */\n getBareRepoPath(): string {\n return this.bareRepoPath\n }\n\n /**\n * Get the project root path\n */\n getProjectRoot(): string {\n return this.projectRoot\n }\n\n // ============================================================\n // Private methods\n // ============================================================\n\n private lockPath: string = ''\n\n private getLockPath(): string {\n if (!this.lockPath) {\n this.lockPath = this.configPath + '.lock'\n }\n return this.lockPath\n }\n\n /**\n * Check if a process is still running\n */\n private isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0) // Signal 0 = check if process exists without killing\n return true\n } catch {\n return false // Process doesn't exist or no permission\n }\n }\n\n /**\n * Acquire a file lock with timeout\n * Uses atomic file creation to ensure only one process holds the lock\n * P1-1: Added PID verification before removing stale locks to prevent race conditions\n */\n private async acquireLock(timeoutMs: number = 5000): Promise<boolean> {\n const lockPath = this.getLockPath()\n const startTime = Date.now()\n const retryInterval = 50\n\n while (Date.now() - startTime < timeoutMs) {\n try {\n // wx flag = create exclusive (fails if file exists)\n fs.writeFileSync(lockPath, String(process.pid), { flag: 'wx' })\n return true\n } catch (err: any) {\n if (err.code === 'EEXIST') {\n // Lock exists, check if it's stale (older than 30 seconds)\n try {\n const stats = fs.statSync(lockPath)\n const lockAge = Date.now() - stats.mtimeMs\n if (lockAge > 30000) {\n // P1-1: Verify the lock holder process is actually dead before removing\n // This prevents race conditions where multiple processes detect stale lock\n try {\n const lockContent = fs.readFileSync(lockPath, 'utf-8').trim()\n const lockPid = parseInt(lockContent, 10)\n if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {\n // Process still running despite old lock - don't remove, just wait\n // The lock holder might be doing a long operation\n await new Promise(resolve => setTimeout(resolve, retryInterval))\n continue\n }\n } catch {\n // Can't read PID, proceed with removal attempt\n }\n // Stale lock from dead process, remove it\n try {\n fs.unlinkSync(lockPath)\n } catch {\n // Another process may have removed it - that's fine, retry\n }\n continue\n }\n } catch {\n // Lock file disappeared, retry\n continue\n }\n // Wait and retry\n await new Promise(resolve => setTimeout(resolve, retryInterval))\n continue\n }\n throw err\n }\n }\n return false\n }\n\n /**\n * Release the file lock\n */\n private releaseLock(): void {\n try {\n fs.unlinkSync(this.getLockPath())\n } catch {\n // Ignore errors (lock may not exist)\n }\n }\n\n private readConfig(): WorktreeProjectConfig | null {\n try {\n if (!fs.existsSync(this.configPath)) {\n return null\n }\n const content = fs.readFileSync(this.configPath, 'utf-8')\n return JSON.parse(content) as WorktreeProjectConfig\n } catch (error) {\n console.error('[WorktreeManager] Failed to read config:', error)\n return null\n }\n }\n\n private writeConfig(config: WorktreeProjectConfig): void {\n try {\n const dir = path.dirname(this.configPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8')\n } catch (error) {\n console.error('[WorktreeManager] Failed to write config:', error)\n throw error\n }\n }\n\n /**\n * Read-modify-write with file locking for safe concurrent access\n */\n private async updateConfigSafe(\n updater: (config: WorktreeProjectConfig) => WorktreeProjectConfig\n ): Promise<boolean> {\n const lockAcquired = await this.acquireLock()\n if (!lockAcquired) {\n console.error('[WorktreeManager] Failed to acquire lock for config update')\n return false\n }\n\n try {\n const config = this.readConfig()\n if (!config) {\n return false\n }\n const updated = updater(config)\n this.writeConfig(updated)\n return true\n } finally {\n this.releaseLock()\n }\n }\n\n // EP959-11: Worktree setup methods\n\n /**\n * Update the setup status of a worktree\n */\n async updateWorktreeStatus(\n moduleUid: string,\n status: 'pending' | 'running' | 'ready' | 'error',\n error?: string\n ): Promise<boolean> {\n return this.updateConfigSafe((config) => {\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (worktree) {\n worktree.setupStatus = status\n if (status === 'running') {\n worktree.setupStartedAt = new Date().toISOString()\n } else if (status === 'ready' || status === 'error') {\n worktree.setupCompletedAt = new Date().toISOString()\n }\n if (error) {\n worktree.setupError = error\n }\n }\n return config\n })\n }\n\n /**\n * Get worktree info including setup status\n */\n getWorktreeStatus(moduleUid: string): WorktreeInfo | null {\n const config = this.readConfig()\n if (!config) return null\n return config.worktrees.find(w => w.moduleUid === moduleUid) || null\n }\n\n /**\n * Copy files from main worktree to module worktree\n *\n * @deprecated EP964: This function is deprecated. Use worktree_env_vars for\n * environment variables or worktree_setup_script for other file operations.\n * This function now returns success (no-op) when main/ worktree doesn't exist.\n */\n async copyFilesFromMain(moduleUid: string, files: string[]): Promise<{ success: boolean; error?: string }> {\n // EP964: Always log deprecation warning\n console.warn(`[WorktreeManager] EP964: copyFilesFromMain is DEPRECATED.`)\n console.warn(`[WorktreeManager] EP964: Use worktree_env_vars for .env or worktree_setup_script for other files.`)\n\n const config = this.readConfig()\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (!worktree) {\n return { success: false, error: `Worktree not found for ${moduleUid}` }\n }\n\n // EP964: Return success (no-op) when main/ doesn't exist\n const mainWorktree = config.worktrees.find(w => w.moduleUid === 'main')\n if (!mainWorktree) {\n console.warn(`[WorktreeManager] EP964: No 'main' worktree - skipping file copy (this is expected).`)\n return { success: true } // No-op success instead of failure\n }\n\n // Backward compatibility: still copy if main/ exists\n try {\n for (const file of files) {\n const srcPath = path.join(mainWorktree.worktreePath, file)\n const destPath = path.join(worktree.worktreePath, file)\n\n if (fs.existsSync(srcPath)) {\n const destDir = path.dirname(destPath)\n if (!fs.existsSync(destDir)) {\n fs.mkdirSync(destDir, { recursive: true })\n }\n fs.copyFileSync(srcPath, destPath)\n console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`)\n } else {\n console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`)\n }\n }\n return { success: true }\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n\n /**\n * Run worktree setup script\n * EP959-M1: Enhanced logging with working directory, timeout info, and script preview\n */\n async runSetupScript(moduleUid: string, script: string): Promise<{ success: boolean; error?: string }> {\n const config = this.readConfig()\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (!worktree) {\n return { success: false, error: `Worktree not found for ${moduleUid}` }\n }\n\n // EP959-M1: Enhanced logging - show script preview (truncate if very long)\n const TIMEOUT_MINUTES = 10\n const scriptPreview = script.length > 200 ? script.slice(0, 200) + '...' : script\n console.log(`[WorktreeManager] EP959: Running setup script for ${moduleUid}`)\n console.log(`[WorktreeManager] EP959: Working directory: ${worktree.worktreePath}`)\n console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`)\n console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`)\n\n try {\n const { execSync } = require('child_process')\n\n execSync(script, {\n cwd: worktree.worktreePath,\n stdio: 'inherit',\n timeout: TIMEOUT_MINUTES * 60 * 1000,\n env: { ...process.env, NODE_ENV: 'development' }\n })\n\n console.log(`[WorktreeManager] EP959: Setup script completed successfully for ${moduleUid}`)\n return { success: true }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[WorktreeManager] EP959: Setup script failed for ${moduleUid}:`, errorMessage)\n return { success: false, error: errorMessage }\n }\n }\n\n /**\n * Run worktree cleanup script before removal\n * EP959-m3: Execute cleanup script when worktree is being released\n */\n async runCleanupScript(moduleUid: string, script: string): Promise<{ success: boolean; error?: string }> {\n const config = this.readConfig()\n if (!config) {\n return { success: false, error: 'Config not found' }\n }\n\n const worktree = config.worktrees.find(w => w.moduleUid === moduleUid)\n if (!worktree) {\n return { success: false, error: `Worktree not found for ${moduleUid}` }\n }\n\n const TIMEOUT_MINUTES = 5\n const scriptPreview = script.length > 200 ? script.slice(0, 200) + '...' : script\n console.log(`[WorktreeManager] EP959: Running cleanup script for ${moduleUid}`)\n console.log(`[WorktreeManager] EP959: Working directory: ${worktree.worktreePath}`)\n console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`)\n console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`)\n\n try {\n const { execSync } = require('child_process')\n\n execSync(script, {\n cwd: worktree.worktreePath,\n stdio: 'inherit',\n timeout: TIMEOUT_MINUTES * 60 * 1000,\n env: { ...process.env, NODE_ENV: 'development' }\n })\n\n console.log(`[WorktreeManager] EP959: Cleanup script completed successfully for ${moduleUid}`)\n return { success: true }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n // Cleanup failures are logged but don't block worktree removal\n console.warn(`[WorktreeManager] EP959: Cleanup script failed for ${moduleUid} (non-blocking):`, errorMessage)\n return { success: false, error: errorMessage }\n }\n }\n}\n\n/**\n * Get the default episoda root directory\n * Can be overridden with EPISODA_ROOT environment variable\n */\nexport function getEpisodaRoot(): string {\n return process.env.EPISODA_ROOT || path.join(require('os').homedir(), 'episoda')\n}\n\n/**\n * Get the project path for a workspace/project combination\n */\nexport function getProjectPath(workspaceSlug: string, projectSlug: string): string {\n return path.join(getEpisodaRoot(), workspaceSlug, projectSlug)\n}\n\n/**\n * Check if a path is a valid worktree project\n */\nexport async function isWorktreeProject(projectRoot: string): Promise<boolean> {\n const manager = new WorktreeManager(projectRoot)\n return manager.initialize()\n}\n\n/**\n * Find the project root by looking for .bare directory\n * Searches current directory and walks up to 5 levels looking for a valid worktree project.\n *\n * @param startPath - Directory to start searching from\n * @returns Absolute path to project root, or null if not found\n */\nexport async function findProjectRoot(startPath: string): Promise<string | null> {\n let current = path.resolve(startPath)\n const episodaRoot = getEpisodaRoot()\n\n // Must be under ~/episoda to be a worktree project\n if (!current.startsWith(episodaRoot)) {\n return null\n }\n\n // Walk up to find .bare directory (support up to 10 levels deep)\n for (let i = 0; i < 10; i++) {\n const bareDir = path.join(current, '.bare')\n const episodaDir = path.join(current, '.episoda')\n\n if (fs.existsSync(bareDir) && fs.existsSync(episodaDir)) {\n // Verify it's a valid worktree project\n if (await isWorktreeProject(current)) {\n return current\n }\n }\n\n // Move up one directory\n const parent = path.dirname(current)\n if (parent === current) {\n // Reached filesystem root\n break\n }\n current = parent\n }\n\n return null\n}\n","/**\n * EP960: Machine Settings API Client\n *\n * Functions for CLI to fetch and sync machine settings with the server.\n * Enables server-synced project paths and EPISODA_ROOT.\n */\n\nimport type { EpisodaConfig } from '@episoda/core'\n\ninterface ProjectPathResponse {\n success: boolean\n data?: {\n path: string | null\n }\n error?: string\n}\n\ninterface MachineSettingsResponse {\n success: boolean\n machine?: {\n id: string\n episoda_root: string\n project_paths: Record<string, string>\n }\n error?: string\n}\n\n/**\n * Fetch project path from server for a specific machine+project combination\n *\n * @param config - Episoda config with auth and device info\n * @param projectId - Project UUID\n * @returns The stored project path or null if not set\n */\nexport async function fetchProjectPath(\n config: EpisodaConfig,\n projectId: string\n): Promise<string | null> {\n if (!config.device_id || !config.access_token) {\n return null\n }\n\n const serverUrl = config.api_url || 'https://episoda.dev'\n const url = `${serverUrl}/api/dev/machines/${config.device_id}/project-path/${projectId}`\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n // 404 means no path stored yet, not an error\n if (response.status === 404) {\n return null\n }\n // Log non-404 errors for debugging\n console.debug(`[MachineSettings] fetchProjectPath failed: ${response.status} ${response.statusText}`)\n return null\n }\n\n const data: ProjectPathResponse = await response.json()\n return data.data?.path || null\n } catch (error) {\n // Network error - return null to trigger local detection\n console.debug(`[MachineSettings] fetchProjectPath network error:`, error)\n return null\n }\n}\n\n/**\n * Sync a project path to the server\n *\n * @param config - Episoda config with auth and device info\n * @param projectId - Project UUID\n * @param path - Absolute path to the project on this machine\n * @returns True if sync succeeded\n */\nexport async function syncProjectPath(\n config: EpisodaConfig,\n projectId: string,\n path: string\n): Promise<boolean> {\n if (!config.device_id || !config.access_token) {\n return false\n }\n\n const serverUrl = config.api_url || 'https://episoda.dev'\n const url = `${serverUrl}/api/dev/machines/${config.device_id}/project-path/${projectId}`\n\n try {\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ path })\n })\n\n if (!response.ok) {\n console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`)\n }\n return response.ok\n } catch (error) {\n // Network error - sync failed but don't block operation\n console.debug(`[MachineSettings] syncProjectPath network error:`, error)\n return false\n }\n}\n\n/**\n * Fetch machine settings including episoda_root\n *\n * @param config - Episoda config with auth and device info\n * @returns Machine settings or null if fetch failed\n */\nexport async function fetchMachineSettings(\n config: EpisodaConfig\n): Promise<{ episodaRoot: string; projectPaths: Record<string, string> } | null> {\n if (!config.device_id || !config.access_token) {\n return null\n }\n\n const serverUrl = config.api_url || 'https://episoda.dev'\n const url = `${serverUrl}/api/account/machines/${config.device_id}`\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n console.debug(`[MachineSettings] fetchMachineSettings failed: ${response.status} ${response.statusText}`)\n return null\n }\n\n const data: MachineSettingsResponse = await response.json()\n if (!data.machine) {\n return null\n }\n\n return {\n episodaRoot: data.machine.episoda_root || 'episoda',\n projectPaths: data.machine.project_paths || {}\n }\n } catch (error) {\n console.debug(`[MachineSettings] fetchMachineSettings network error:`, error)\n return null\n }\n}\n\n/**\n * Fetch only the episoda_root setting for this machine\n *\n * @param config - Episoda config with auth and device info\n * @returns The episoda_root value or default 'episoda'\n */\nexport async function fetchEpisodaRoot(config: EpisodaConfig): Promise<string> {\n const settings = await fetchMachineSettings(config)\n return settings?.episodaRoot || 'episoda'\n}\n","/**\n * Bootstrap utilities for CLI setup (EP984)\n *\n * Provides functions to extract bootstrap scripts (api-helper.sh) from\n * the bare repo to .episoda/scripts/, enabling API access before any\n * worktree is checked out.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\n\n/**\n * Extract bootstrap scripts from bare repo to .episoda/scripts/\n *\n * This enables agents to use API helpers immediately after clone,\n * before any worktree is checked out. Also called by `episoda dev`\n * to ensure scripts exist for existing installations.\n *\n * @param bareRepoPath - Path to the .bare/ git repository\n * @param projectPath - Path to the project root (contains .episoda/)\n * @returns true if scripts were extracted, false if already existed or failed\n */\nexport async function extractBootstrapScripts(\n bareRepoPath: string,\n projectPath: string\n): Promise<boolean> {\n const scriptsDir = path.join(projectPath, '.episoda', 'scripts')\n const scriptPath = path.join(scriptsDir, 'api-helper.sh')\n\n // Check if already exists (skip extraction)\n if (fs.existsSync(scriptPath)) {\n return false\n }\n\n // Create scripts directory\n fs.mkdirSync(scriptsDir, { recursive: true })\n\n try {\n // Use git show to extract file content from bare repo\n const scriptContent = execSync(\n `git --git-dir=\"${bareRepoPath}\" show main:scripts/api-helper.sh`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }\n )\n\n fs.writeFileSync(scriptPath, scriptContent, { mode: 0o755 })\n return true\n } catch (error) {\n // Non-fatal - the script might not exist in all repos\n console.log('[bootstrap] Could not extract api-helper.sh:', error)\n return false\n }\n}\n\n/**\n * Check if bootstrap scripts exist\n */\nexport function hasBootstrapScripts(projectPath: string): boolean {\n const scriptPath = path.join(projectPath, '.episoda', 'scripts', 'api-helper.sh')\n return fs.existsSync(scriptPath)\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, 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 // EP904: Now includes refresh_token and expires_at for token refresh support\n // EP951: Removed cli_version - status now reads VERSION at runtime\n // EP956: Added workspace_slug and project_slug for worktree path resolution\n await saveConfig({\n project_id: tokenResponse.project_id,\n user_id: tokenResponse.user_id,\n workspace_id: tokenResponse.workspace_id,\n workspace_slug: tokenResponse.workspace_uid, // EP956: For worktree paths\n project_slug: tokenResponse.project_uid, // EP956: For worktree paths\n access_token: tokenResponse.access_token,\n refresh_token: tokenResponse.refresh_token,\n expires_at: Date.now() + (tokenResponse.expires_in * 1000), // Convert to Unix timestamp\n api_url: apiUrl,\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 connect` command (EP591)\n *\n * Simplified authentication for browser-initiated flows.\n * Unlike `episoda auth`, this command:\n * - Does NOT open a browser\n * - Does NOT generate codes\n * - Just exchanges a pre-authorized user_code for tokens\n *\n * This enables the one-liner install flow:\n * 1. User generates code in browser (code is pre-authorized)\n * 2. User runs: curl https://episoda.dev/i/CODE | bash\n * 3. Install script runs: episoda connect --code CODE\n * 4. This command exchanges code for tokens and saves config\n */\n\nimport * as os from 'os'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\nimport { saveConfig, getConfigPath } from '@episoda/core'\nimport { status } from '../output'\nimport { getMachineId } from '../daemon/machine-id'\nimport { generateCredentialHelperScript } from '../git-helpers/git-credential-helper'\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 TokenErrorResponse {\n error: string\n error_description?: string\n}\n\nexport interface ConnectCommandOptions {\n code: string\n apiUrl?: string\n}\n\n/**\n * Connect command - Exchange pre-authorized code for tokens\n */\nexport async function connectCommand(options: ConnectCommandOptions): Promise<void> {\n const { code } = options\n const apiUrl = options.apiUrl || process.env.EPISODA_API_URL || 'https://episoda.dev'\n\n if (!code) {\n throw new Error('Connection code is required. Usage: episoda connect --code <code>')\n }\n\n status.info('Connecting to Episoda...')\n status.info('')\n\n // Get machine ID to associate with the token\n const machineId = await getMachineId()\n\n // Exchange code for tokens\n status.info('Exchanging connection code...')\n const tokenResponse = await exchangeUserCode(apiUrl, code, machineId)\n\n status.success('✓ Connected successfully!')\n status.info(` Project: ${tokenResponse.project_uid}`)\n status.info(` Workspace: ${tokenResponse.workspace_uid}`)\n status.info('')\n\n // Save configuration\n // EP951: Removed cli_version - status now reads VERSION at runtime\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 })\n\n status.success(`✓ Configuration saved to ${getConfigPath()}`)\n status.info('')\n\n // 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 } else {\n status.warning('⚠ Git credential helper could not be installed automatically.')\n }\n status.info('')\n\n status.success('Ready!')\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('')\n}\n\n/**\n * Exchange user_code for access token\n * EP591: Uses user_code (not device_code) for browser-initiated flows\n */\nasync function exchangeUserCode(apiUrl: string, userCode: 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 user_code: userCode, // EP591: Use user_code instead of device_code\n machine_id: machineId,\n }),\n })\n\n if (!response.ok) {\n const error = await response.json() as TokenErrorResponse\n\n // Provide user-friendly error messages\n if (error.error === 'invalid_grant') {\n throw new Error('Connection code not found or already used. Please generate a new code.')\n } else if (error.error === 'expired_token') {\n throw new Error('Connection code has expired. Please generate a new code.')\n } else if (error.error === 'authorization_pending') {\n throw new Error('Connection code not yet authorized. Please complete authorization in the browser first.')\n }\n\n throw new Error(error.error_description || error.error || 'Failed to connect')\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 response from server')\n }\n\n return tokenResponse\n}\n\n/**\n * Install git credential helper\n * Copied from auth.ts - installs helper for seamless GitHub auth\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 // Create bin directory\n fs.mkdirSync(episodaBinDir, { recursive: true })\n\n // Write credential helper script\n const scriptContent = generateCredentialHelperScript(apiUrl)\n fs.writeFileSync(helperPath, scriptContent, { mode: 0o755 })\n\n // Configure git to use the credential helper\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 already configured, we're done\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 execSync(`git config --global --add credential.helper \"${helperPath}\"`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe']\n })\n\n // Update shell profile\n updateShellProfile(episodaBinDir)\n\n return true\n } catch (error: any) {\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 return\n }\n\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`\n\n for (const profile of profiles) {\n try {\n if (fs.existsSync(profile)) {\n const content = fs.readFileSync(profile, 'utf8')\n if (content.includes('.episoda/bin')) {\n continue\n }\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 * `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 * EP846-6: Now verifies connection with server by default (local state can be stale).\n * EP951: Display VERSION from @episoda/core instead of stale config.cli_version.\n * EP992: Added version update check to status output.\n */\n\nimport { loadConfig, getConfigPath, VERSION } from '@episoda/core'\nimport { getStatus, verifyHealth, verifyServerConnection } from '../ipc/ipc-client'\nimport { checkForUpdates, performBackgroundUpdate } from '../utils/update-checker'\nimport { status } from '../output'\n\nexport interface StatusOptions {\n verify?: boolean\n local?: boolean // EP846-6: Skip server verification (fast, but may be stale)\n skipUpdateCheck?: boolean // EP992: Skip version update check for faster output\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\n // EP992: Check for CLI updates unless --skip-update-check is set\n if (!options.skipUpdateCheck) {\n const updateResult = await checkForUpdates(VERSION)\n if (updateResult.updateAvailable) {\n status.info(` CLI Version: ${VERSION} ⬆ updating to ${updateResult.latestVersion}...`)\n performBackgroundUpdate()\n } else {\n status.info(` CLI Version: ${VERSION} ✓`)\n }\n } else {\n status.info(` CLI Version: ${VERSION}`)\n }\n\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 (local state)\n const connectedProject = daemonStatus.projects.find(p => p.connected)\n\n // EP846-6: Verify with server by default (unless --local flag is set)\n // This is the authoritative check - local state can be stale\n if (!options.local && connectedProject) {\n try {\n const serverCheck = await verifyServerConnection()\n\n if (serverCheck.verified) {\n if (serverCheck.actuallyConnected) {\n // Both local and server agree - truly connected\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 (serverCheck.localConnected && !serverCheck.serverConnected) {\n // Local thinks connected, server says no - STALE\n status.error('✗ Not connected to server')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info('')\n status.warning(' Local daemon thinks connected, but server disagrees.')\n if (serverCheck.serverError) {\n status.info(` Server error: ${serverCheck.serverError}`)\n }\n status.info(' Run \"episoda dev\" to reconnect.')\n } else if (!serverCheck.machineMatch && serverCheck.serverConnected) {\n // Server sees a different machine connected\n status.warning('⚠ Different machine connected')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` This machine: ${serverCheck.machineId}`)\n status.info(` Connected machine: ${serverCheck.serverMachineId}`)\n status.info(' Run \"episoda dev\" to connect this machine.')\n } else {\n // Not connected\n status.warning('⚠ Not connected to server')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(' Run \"episoda dev\" to connect.')\n }\n } else {\n // Verification failed - fall back to local state with warning\n status.warning('⚠ Could not verify with server')\n if (serverCheck.error) {\n status.info(` Error: ${serverCheck.error}`)\n }\n status.info(` Local state: ${connectedProject ? 'connected' : 'not connected'}`)\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n }\n } catch (error) {\n // Server verification failed - show local state with warning\n status.warning('⚠ Could not verify with server')\n status.info(` Error: ${error instanceof Error ? error.message : String(error)}`)\n status.info('')\n status.info('Local state (may be stale):')\n if (connectedProject) {\n status.info(` ✓ Daemon thinks connected to: ${connectedProject.name}`)\n }\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n }\n } else if (connectedProject) {\n // --local flag set, show local state only\n status.success('✓ Connected (local state)')\n status.info(` Device: ${daemonStatus.hostname}`)\n status.info(` Platform: ${daemonStatus.platform}/${daemonStatus.arch}`)\n status.info(` Project: ${connectedProject.name}`)\n status.info('')\n status.info(' Note: Use without --local to verify with server.')\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 * CLI Auto-Update Checker\n * EP783: Check for updates on daemon startup and auto-update in background\n *\n * This module provides non-blocking update checking and background updates\n * to ensure users always have the latest CLI version with correct git hooks.\n */\n\nimport { spawn, execSync } from 'child_process'\nimport * as semver from 'semver'\n\nconst PACKAGE_NAME = 'episoda'\nconst NPM_REGISTRY = 'https://registry.npmjs.org'\n\nexport interface UpdateCheckResult {\n currentVersion: string\n latestVersion: string\n updateAvailable: boolean\n /** EP989: True if check failed due to network issues (offline, timeout, etc.) */\n offline?: boolean\n}\n\n/**\n * Check npm registry for latest version\n * Non-blocking - fails silently on network errors\n */\nexport async function checkForUpdates(currentVersion: string): Promise<UpdateCheckResult> {\n try {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 5000) // 5s timeout\n\n const response = await fetch(`${NPM_REGISTRY}/${PACKAGE_NAME}/latest`, {\n signal: controller.signal\n })\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n return { currentVersion, latestVersion: currentVersion, updateAvailable: false }\n }\n\n const data = await response.json() as { version: string }\n const latestVersion = data.version\n\n return {\n currentVersion,\n latestVersion,\n updateAvailable: semver.gt(latestVersion, currentVersion)\n }\n } catch (error) {\n // EP989: Detect network errors and report offline status\n // This includes timeouts (AbortError), DNS failures, connection refused, etc.\n return { currentVersion, latestVersion: currentVersion, updateAvailable: false, offline: true }\n }\n}\n\n/**\n * Run npm update in background (detached process)\n * The update runs independently of the daemon process\n */\nexport function performBackgroundUpdate(): void {\n try {\n const child = spawn('npm', ['update', '-g', PACKAGE_NAME], {\n detached: true,\n stdio: 'ignore',\n // Use shell on Windows for proper npm execution\n shell: process.platform === 'win32'\n })\n child.unref() // Allow parent to exit independently\n } catch (error) {\n // Silently ignore spawn errors\n }\n}\n\n/**\n * EP989: Get the currently installed version of the CLI from npm\n * Used for version verification after update\n */\nexport function getInstalledVersion(): string | null {\n try {\n const output = execSync(`npm list -g ${PACKAGE_NAME} --json`, {\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout: 10000\n }).toString()\n\n const data = JSON.parse(output)\n // npm list output structure: { dependencies: { episoda: { version: \"x.x.x\" } } }\n return data?.dependencies?.[PACKAGE_NAME]?.version || null\n } catch {\n // Package might not be installed globally or npm command failed\n return null\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","/**\n * `episoda clone` command (EP944)\n *\n * Clones a project for multi-module development using worktree architecture.\n *\n * Usage:\n * episoda clone {workspace}/{project}\n *\n * This creates:\n * ~/episoda/{workspace}/{project}/\n * ├── .bare/ # Bare git clone\n * └── .episoda/\n * └── config.json # Project config\n *\n * After cloning, use `episoda checkout {module}` to create worktrees.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { addProject } from '../daemon/project-tracker'\nimport {\n WorktreeManager,\n getEpisodaRoot,\n getProjectPath\n} from '../daemon/worktree-manager'\nimport { extractBootstrapScripts } from '../utils/bootstrap'\n\n/**\n * API response for project details\n */\ninterface ProjectDetailsResponse {\n id: string\n uid: string\n name: string\n slug: string\n workspace_slug: string\n repo_url: string | null\n default_branch: string\n}\n\nexport interface CloneCommandOptions {\n apiUrl?: string\n}\n\n/**\n * Clone command - creates an Episoda project\n */\nexport async function cloneCommand(\n slugArg: string,\n options: CloneCommandOptions = {}\n): Promise<void> {\n // Step 1: Parse and validate the slug\n const slugParts = slugArg.split('/')\n if (slugParts.length !== 2 || !slugParts[0] || !slugParts[1]) {\n throw new Error(\n 'Invalid format. Usage: episoda clone {workspace}/{project}\\n' +\n 'Example: episoda clone my-team/my-project'\n )\n }\n\n const [workspaceSlug, projectSlug] = slugParts\n\n status.info(`Cloning ${workspaceSlug}/${projectSlug}...`)\n status.info('')\n\n // Step 2: Load auth config\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error(\n 'Not authenticated. Please run `episoda auth` first.'\n )\n }\n\n const apiUrl = options.apiUrl || config.api_url || 'https://episoda.dev'\n\n // Step 3: Check if project already exists locally\n const projectPath = getProjectPath(workspaceSlug, projectSlug)\n if (fs.existsSync(projectPath)) {\n const bareRepoPath = path.join(projectPath, '.bare')\n if (fs.existsSync(bareRepoPath)) {\n status.warning(`Project already cloned at ${projectPath}`)\n status.info('')\n status.info('Next steps:')\n status.info(` • cd ${projectPath}`)\n status.info(' • episoda checkout {module}')\n return\n }\n throw new Error(\n `Directory exists but is not an Episoda project: ${projectPath}\\n` +\n 'Please remove it manually or use a different location.'\n )\n }\n\n // Step 4: Fetch project details from API\n status.info('Fetching project details...')\n const projectDetails = await fetchProjectDetails(\n apiUrl,\n workspaceSlug,\n projectSlug,\n config.access_token\n )\n\n if (!projectDetails.repo_url) {\n throw new Error(\n `Project \"${projectSlug}\" has no repository configured.\\n` +\n 'Please configure a repository in the project settings on episoda.dev.'\n )\n }\n\n // Validate repo URL before cloning (security check)\n validateRepoUrl(projectDetails.repo_url)\n\n status.success(`✓ Found project: ${projectDetails.name}`)\n status.info(` Repository: ${projectDetails.repo_url}`)\n status.info('')\n\n // Step 5: Create project directory\n status.info('Creating project directory...')\n const episodaRoot = getEpisodaRoot()\n fs.mkdirSync(projectPath, { recursive: true })\n status.success(`✓ Created ${projectPath}`)\n\n // Step 6: Clone bare repository\n status.info('Cloning repository (bare)...')\n try {\n const worktreeManager = await WorktreeManager.createProject(\n projectPath,\n projectDetails.repo_url,\n projectDetails.id,\n workspaceSlug,\n projectSlug\n )\n\n status.success('✓ Repository cloned')\n status.info('')\n\n // Step 7: Extract api-helper.sh to .episoda/scripts/ (EP964)\n const bareRepoPath = worktreeManager.getBareRepoPath()\n await extractBootstrapScripts(bareRepoPath, projectPath)\n status.success('✓ Bootstrap scripts extracted')\n status.info('')\n\n // Step 8: Register with project tracker (EP971: all projects use worktree architecture)\n addProject(projectDetails.id, projectPath, {\n bareRepoPath\n })\n status.success('✓ Project registered with daemon')\n status.info('')\n\n // Success message\n status.success('Project cloned successfully!')\n status.info('')\n status.info('Project structure:')\n status.info(` ${projectPath}/`)\n status.info(' ├── .bare/ # Git repository')\n status.info(' └── .episoda/')\n status.info(' ├── config.json # Project config')\n status.info(' └── scripts/')\n status.info(' └── api-helper.sh # API authentication')\n status.info('')\n status.info('Next steps:')\n status.info(` 1. cd ${projectPath}`)\n status.info(' 2. source .episoda/scripts/api-helper.sh # Load API helpers')\n status.info(' 3. episoda checkout {moduleUid} # e.g., episoda checkout EP100')\n status.info('')\n\n } catch (error) {\n // Cleanup on failure\n if (fs.existsSync(projectPath)) {\n try {\n fs.rmSync(projectPath, { recursive: true, force: true })\n } catch {\n // Ignore cleanup errors\n }\n }\n throw error\n }\n}\n\n/**\n * Validate that a repository URL is safe to clone\n * Prevents command injection and other security issues\n */\nfunction validateRepoUrl(url: string): void {\n // Check for command injection characters\n const dangerousChars = /[;|&$`\\\\<>(){}[\\]!#*?~'\"]/\n if (dangerousChars.test(url)) {\n throw new Error(\n 'Repository URL contains invalid characters.\\n' +\n 'Please check the repository configuration on episoda.dev.'\n )\n }\n\n // Try to parse as URL\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n // Allow SSH-style URLs (git@github.com:owner/repo.git)\n if (/^[\\w.-]+@[\\w.-]+:[\\w./-]+$/.test(url)) {\n return\n }\n throw new Error(\n 'Repository URL is not a valid URL format.\\n' +\n `Received: ${url}`\n )\n }\n\n // Only allow safe protocols\n const allowedProtocols = ['https:', 'http:', 'git:', 'ssh:']\n if (!allowedProtocols.includes(parsed.protocol)) {\n throw new Error(\n `Repository URL uses unsupported protocol: ${parsed.protocol}\\n` +\n 'Allowed protocols: https, http, git, ssh'\n )\n }\n\n // Ensure hostname is present and reasonable\n if (!parsed.hostname || parsed.hostname.length < 3) {\n throw new Error(\n 'Repository URL has invalid hostname.\\n' +\n `Received: ${url}`\n )\n }\n}\n\n/**\n * Fetch project details from the API\n */\nasync function fetchProjectDetails(\n apiUrl: string,\n workspaceSlug: string,\n projectSlug: string,\n accessToken: string\n): Promise<ProjectDetailsResponse> {\n const url = `${apiUrl}/api/projects/by-slug/${workspaceSlug}/${projectSlug}`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (response.status === 404) {\n throw new Error(\n `Project \"${workspaceSlug}/${projectSlug}\" not found.\\n` +\n 'Check the workspace and project slugs are correct.'\n )\n }\n\n if (response.status === 401) {\n throw new Error(\n 'Authentication expired. Please run `episoda auth` again.'\n )\n }\n\n if (!response.ok) {\n const errorBody = await response.text()\n throw new Error(\n `Failed to fetch project details: ${response.status}\\n${errorBody}`\n )\n }\n\n const data = await response.json() as ProjectDetailsResponse\n\n // Validate required fields\n if (!data.id || !data.slug) {\n throw new Error('Invalid project response from server')\n }\n\n return data\n}\n\n","/**\n * Project tracking for daemon\n *\n * Manages ~/.episoda/projects.json which tracks all projects\n * the daemon is monitoring.\n *\n * Format:\n * {\n * \"projects\": [\n * {\n * \"id\": \"proj_abc123\",\n * \"path\": \"/Users/alan/Dev/my-project\",\n * \"name\": \"my-project\",\n * \"added_at\": \"2025-01-18T10:30:00.000Z\",\n * \"last_active\": \"2025-01-18T12:45:00.000Z\"\n * }\n * ]\n * }\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { getConfigDir } from '@episoda/core'\n\nexport interface TrackedProject {\n id: string // Supabase project ID\n path: string // Absolute path to project directory\n name: string // Project name (from directory)\n added_at: string // ISO timestamp when first added\n last_active: string // ISO timestamp of last activity\n // EP971: All projects use worktree architecture\n bareRepoPath?: string // Path to .bare/ directory\n}\n\ninterface ProjectsData {\n projects: TrackedProject[]\n}\n\n/**\n * Get path to projects.json file\n */\nfunction getProjectsFilePath(): string {\n return path.join(getConfigDir(), 'projects.json')\n}\n\n/**\n * Read projects from file\n *\n * @returns Projects data, or empty list if file doesn't exist\n */\nfunction readProjects(): ProjectsData {\n const projectsPath = getProjectsFilePath()\n\n try {\n if (!fs.existsSync(projectsPath)) {\n return { projects: [] }\n }\n\n const content = fs.readFileSync(projectsPath, 'utf-8')\n const data = JSON.parse(content) as ProjectsData\n\n // Validate structure\n if (!data.projects || !Array.isArray(data.projects)) {\n console.warn('Invalid projects.json structure, resetting')\n return { projects: [] }\n }\n\n return data\n } catch (error) {\n console.error('Error reading projects.json:', error)\n return { projects: [] }\n }\n}\n\n/**\n * Write projects to file\n */\nfunction writeProjects(data: ProjectsData): void {\n const projectsPath = getProjectsFilePath()\n\n try {\n // Ensure directory exists\n const dir = path.dirname(projectsPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n fs.writeFileSync(projectsPath, JSON.stringify(data, null, 2), 'utf-8')\n } catch (error) {\n throw new Error(`Failed to write projects.json: ${error}`)\n }\n}\n\n/**\n * Options for adding a project\n * EP971: All projects use worktree architecture\n */\nexport interface AddProjectOptions {\n bareRepoPath?: string // Path to .bare/ directory\n}\n\n/**\n * Add or update a project\n *\n * EP593: Now enforces one entry per projectId to prevent duplicate connections.\n * When adding a project:\n * - If same path exists, update last_active\n * - If same projectId exists with different path, REPLACE the old entry\n * (user wants git operations in the new directory)\n * - Otherwise, add new project\n *\n * EP971: All projects use worktree architecture.\n *\n * @param projectId Supabase project ID\n * @param projectPath Absolute path to project\n * @param options Optional settings (bareRepoPath)\n * @returns The tracked project\n */\nexport function addProject(\n projectId: string,\n projectPath: string,\n options?: AddProjectOptions\n): TrackedProject {\n const data = readProjects()\n const now = new Date().toISOString()\n\n // Check if project already exists by path (exact match)\n const existingByPath = data.projects.find(p => p.path === projectPath)\n\n if (existingByPath) {\n // Update existing project\n existingByPath.id = projectId // Update ID in case it changed\n existingByPath.last_active = now\n // EP971: Update bare repo path if specified\n if (options?.bareRepoPath) {\n existingByPath.bareRepoPath = options.bareRepoPath\n }\n writeProjects(data)\n return existingByPath\n }\n\n // EP593: Check if project exists by ID (different path)\n // This prevents multiple entries for the same project from different directories\n const existingByIdIndex = data.projects.findIndex(p => p.id === projectId)\n\n if (existingByIdIndex !== -1) {\n const existingById = data.projects[existingByIdIndex]\n console.log(`[ProjectTracker] Replacing project entry: ${existingById.path} -> ${projectPath}`)\n\n // Remove the old entry\n data.projects.splice(existingByIdIndex, 1)\n }\n\n // Add new project\n const projectName = path.basename(projectPath)\n const newProject: TrackedProject = {\n id: projectId,\n path: projectPath,\n name: projectName,\n added_at: now,\n last_active: now,\n // EP971: Bare repo path for worktree architecture\n bareRepoPath: options?.bareRepoPath,\n }\n\n data.projects.push(newProject)\n writeProjects(data)\n\n return newProject\n}\n\n/**\n * Remove a project\n *\n * @param projectPath Absolute path to project\n * @returns true if removed, false if not found\n */\nexport function removeProject(projectPath: string): boolean {\n const data = readProjects()\n const initialLength = data.projects.length\n\n data.projects = data.projects.filter(p => p.path !== projectPath)\n\n if (data.projects.length < initialLength) {\n writeProjects(data)\n return true\n }\n\n return false\n}\n\n/**\n * Get a project by path\n *\n * @param projectPath Absolute path to project\n * @returns Project if found, null otherwise\n */\nexport function getProject(projectPath: string): TrackedProject | null {\n const data = readProjects()\n return data.projects.find(p => p.path === projectPath) || null\n}\n\n/**\n * Get a project by ID\n *\n * @param projectId Supabase project ID\n * @returns Project if found, null otherwise\n */\nexport function getProjectById(projectId: string): TrackedProject | null {\n const data = readProjects()\n return data.projects.find(p => p.id === projectId) || null\n}\n\n/**\n * Get all tracked projects\n *\n * @returns Array of tracked projects\n */\nexport function getAllProjects(): TrackedProject[] {\n const data = readProjects()\n return data.projects\n}\n\n/**\n * Update last active timestamp for a project\n *\n * @param projectPath Absolute path to project\n */\nexport function touchProject(projectPath: string): void {\n const data = readProjects()\n const project = data.projects.find(p => p.path === projectPath)\n\n if (project) {\n project.last_active = new Date().toISOString()\n writeProjects(data)\n }\n}\n\n/**\n * Clean up stale projects\n *\n * Removes projects that:\n * - Haven't been active in N days\n * - Directory no longer exists\n *\n * @param maxAgeDays Maximum age in days (default: 30)\n * @returns Number of projects removed\n */\nexport function cleanupStaleProjects(maxAgeDays: number = 30): number {\n const data = readProjects()\n const now = Date.now()\n const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000\n\n const initialLength = data.projects.length\n\n data.projects = data.projects.filter(project => {\n // Check if directory still exists\n if (!fs.existsSync(project.path)) {\n return false\n }\n\n // Check if too old\n const lastActive = new Date(project.last_active).getTime()\n const age = now - lastActive\n\n return age < maxAgeMs\n })\n\n const removedCount = initialLength - data.projects.length\n\n if (removedCount > 0) {\n writeProjects(data)\n }\n\n return removedCount\n}\n\n/**\n * Clear all projects\n *\n * USE WITH CAUTION - removes all tracked projects\n */\nexport function clearAllProjects(): void {\n writeProjects({ projects: [] })\n}\n","/**\n * `episoda checkout` command (EP944)\n *\n * Creates a worktree for a module, enabling isolated development.\n *\n * Usage:\n * episoda checkout {moduleUid}\n * episoda checkout EP100\n *\n * Creates:\n * ~/episoda/{workspace}/{project}/{moduleUid}/\n *\n * The module is checked out on its feature branch. If the branch\n * doesn't exist yet, it's created from the default branch.\n */\n\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchWithRetry } from '../utils/http'\nimport { setupWorktreeEnv } from '../utils/env-setup'\nimport {\n WorktreeManager,\n findProjectRoot\n} from '../daemon/worktree-manager'\n\n/**\n * API response for module details\n */\ninterface ModuleDetailsResponse {\n id: string\n uid: string\n name: string\n branch_name: string | null\n state: string\n project_id: string\n}\n\nexport interface CheckoutCommandOptions {\n apiUrl?: string\n create?: boolean // Create branch if it doesn't exist\n}\n\n/**\n * Checkout command - creates a worktree for a module\n */\nexport async function checkoutCommand(\n moduleUid: string,\n options: CheckoutCommandOptions = {}\n): Promise<void> {\n // Step 1: Validate module UID format\n if (!moduleUid || !moduleUid.match(/^EP\\d+$/)) {\n throw new Error(\n 'Invalid module UID format. Expected format: EP###\\n' +\n 'Example: episoda checkout EP100'\n )\n }\n\n status.info(`Checking out ${moduleUid}...`)\n status.info('')\n\n // Step 2: Find project root (must be in a worktree project)\n const projectRoot = await findProjectRoot(process.cwd())\n if (!projectRoot) {\n throw new Error(\n 'Not in a worktree project.\\n' +\n 'Run this command from within a project cloned with `episoda clone`.\\n' +\n 'Or cd to ~/episoda/{workspace}/{project}/'\n )\n }\n\n // Step 3: Load auth config\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error(\n 'Not authenticated. Please run `episoda auth` first.'\n )\n }\n\n const apiUrl = options.apiUrl || config.api_url || 'https://episoda.dev'\n\n // Step 4: Initialize worktree manager\n const worktreeManager = new WorktreeManager(projectRoot)\n const initialized = await worktreeManager.initialize()\n if (!initialized) {\n throw new Error(\n `Invalid worktree project at ${projectRoot}.\\n` +\n 'The .bare directory or .episoda/config.json may be missing.'\n )\n }\n\n // Check if worktree already exists\n const existing = worktreeManager.getWorktreeByModuleUid(moduleUid)\n if (existing) {\n status.success(`Module ${moduleUid} is already checked out.`)\n status.info('')\n status.info(`Path: ${existing.worktreePath}`)\n status.info(`Branch: ${existing.branchName}`)\n status.info('')\n status.info(`cd ${existing.worktreePath}`)\n return\n }\n\n // Step 5: Fetch module details from API\n status.info('Fetching module details...')\n const worktreeConfig = worktreeManager.getConfig()\n if (!worktreeConfig) {\n throw new Error('Could not read worktree project config')\n }\n\n const moduleDetails = await fetchModuleDetails(\n apiUrl,\n worktreeConfig.projectId,\n moduleUid,\n config.access_token\n )\n\n // Determine branch name\n const branchName = moduleDetails.branch_name || `feature/${moduleUid.toLowerCase()}`\n\n // P1-5: Check if generated branch already exists (local or remote)\n // This prevents conflicts when the branch was created elsewhere\n let createBranch = !moduleDetails.branch_name || options.create\n if (createBranch && !moduleDetails.branch_name) {\n // We're about to generate a new branch - check if it already exists\n const { GitExecutor } = await import('@episoda/core')\n const gitExecutor = new GitExecutor()\n const branchCheck = await gitExecutor.execute(\n { action: 'branch_exists', branch: branchName },\n { cwd: worktreeManager.getBareRepoPath() }\n )\n if (branchCheck.success && branchCheck.details?.branchExists) {\n status.info(`Branch ${branchName} already exists, will use existing`)\n createBranch = false\n }\n }\n\n status.success(`✓ Found module: ${moduleDetails.name || moduleUid}`)\n status.info(` Branch: ${branchName}${createBranch ? ' (will be created)' : ''}`)\n status.info('')\n\n // Step 6: Create the worktree\n status.info('Creating worktree...')\n const result = await worktreeManager.createWorktree(\n moduleUid,\n branchName,\n createBranch\n )\n\n if (!result.success) {\n throw new Error(`Failed to create worktree: ${result.error}`)\n }\n\n status.success('✓ Worktree created')\n status.info('')\n\n // Step 6b: Fetch and write environment variables (EP988)\n try {\n status.info('Configuring environment...')\n const envWritten = await setupWorktreeEnv(result.worktreePath!, apiUrl, config.access_token)\n if (envWritten) {\n status.success('✓ Environment configured')\n }\n status.info('')\n } catch (error) {\n // Non-fatal - warn but continue\n status.warning('Could not configure environment variables')\n if (error instanceof Error) {\n status.warning(` ${error.message}`)\n }\n status.info('')\n }\n\n // Step 7: Update server with checkout status (optional, non-blocking)\n // If we generated the branch name, persist it to the server\n const branchWasGenerated = !moduleDetails.branch_name\n try {\n await updateModuleCheckout(\n apiUrl,\n moduleDetails.id,\n config.access_token,\n branchWasGenerated ? branchName : undefined\n )\n } catch (error) {\n // Don't fail the command if server update fails\n status.warning('Could not update server with checkout status')\n }\n\n // Success message\n status.success(`Module ${moduleUid} checked out successfully!`)\n status.info('')\n status.info(`Path: ${result.worktreePath}`)\n status.info(`Branch: ${branchName}`)\n status.info('')\n status.info('Next step:')\n status.info(` cd ${result.worktreePath}`)\n status.info('')\n}\n\n/**\n * Fetch module details from the API\n */\nasync function fetchModuleDetails(\n apiUrl: string,\n projectId: string,\n moduleUid: string,\n accessToken: string\n): Promise<ModuleDetailsResponse> {\n const url = `${apiUrl}/api/modules/by-uid/${moduleUid}?project_id=${projectId}`\n\n const response = await fetchWithRetry(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (response.status === 404) {\n throw new Error(\n `Module \"${moduleUid}\" not found in this project.\\n` +\n 'Check the module UID is correct.'\n )\n }\n\n if (response.status === 401) {\n throw new Error(\n 'Authentication expired. Please run `episoda auth` again.'\n )\n }\n\n if (!response.ok) {\n const errorBody = await response.text()\n throw new Error(\n `Failed to fetch module details: ${response.status}\\n${errorBody}`\n )\n }\n\n const data = await response.json() as ModuleDetailsResponse\n\n // Validate required fields\n if (!data.id || !data.uid) {\n throw new Error('Invalid module response from server')\n }\n\n return data\n}\n\n/**\n * Update server with checkout status\n * This is a best-effort operation - failures don't block checkout\n * If branchName is provided, it will be persisted to the module record\n */\nasync function updateModuleCheckout(\n apiUrl: string,\n moduleId: string,\n accessToken: string,\n branchName?: string\n): Promise<void> {\n const url = `${apiUrl}/api/modules/${moduleId}/checkout`\n\n const body: Record<string, unknown> = {\n checked_out: true,\n checkout_type: 'worktree'\n }\n\n // Include branch name if it was generated and needs to be persisted\n if (branchName) {\n body.branch_name = branchName\n }\n\n await fetchWithRetry(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n // Don't check response - this is optional\n}\n","/**\n * HTTP utilities for CLI commands\n *\n * EP944: Shared fetch utilities with retry logic for API resilience\n */\n\n/**\n * Retry a fetch request with exponential backoff\n * Does not retry on 401/404 (permanent failures)\n *\n * @param url - The URL to fetch\n * @param options - Standard fetch RequestInit options\n * @param maxRetries - Maximum number of retry attempts (default: 3)\n * @returns The fetch Response\n * @throws Error if all retries fail\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n maxRetries: number = 3\n): Promise<Response> {\n let lastError: Error | null = null\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const response = await fetch(url, options)\n\n // Don't retry on permanent failures\n if (response.status === 401 || response.status === 404) {\n return response\n }\n\n // Success or client error - don't retry\n if (response.ok || (response.status >= 400 && response.status < 500)) {\n return response\n }\n\n // Server error - retry\n lastError = new Error(`Server error: ${response.status}`)\n } catch (error) {\n // Network error - retry\n lastError = error instanceof Error ? error : new Error('Network error')\n }\n\n // Wait before retrying (exponential backoff: 1s, 2s, 4s)\n if (attempt < maxRetries - 1) {\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000))\n }\n }\n\n throw lastError || new Error('Request failed after retries')\n}\n","/**\n * EP988: Environment Variable Setup Utility\n *\n * Shared utility for fetching and writing environment variables to worktrees.\n * Used by both CLI checkout and daemon checkout_module handler.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\n\n/**\n * Fetch environment variables from the server\n *\n * @param apiUrl - Base API URL (e.g., https://episoda.dev)\n * @param accessToken - OAuth access token\n * @returns Key-value map of env vars, or empty object on error\n */\nexport async function fetchEnvVars(\n apiUrl: string,\n accessToken: string\n): Promise<Record<string, string>> {\n try {\n const url = `${apiUrl}/api/cli/env-vars`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n console.warn(`[env-setup] Failed to fetch env vars: ${response.status}`)\n return {}\n }\n\n const data = await response.json() as { env_vars?: Record<string, string> }\n const envVars = data.env_vars || {}\n\n console.log(`[env-setup] Fetched ${Object.keys(envVars).length} env vars from server`)\n return envVars\n } catch (error) {\n console.warn('[env-setup] Error fetching env vars:', error instanceof Error ? error.message : error)\n return {}\n }\n}\n\n/**\n * Write environment variables to .env file in a directory\n *\n * @param targetPath - Directory path to write .env file to\n * @param envVars - Key-value map of environment variables\n */\nexport function writeEnvFile(\n targetPath: string,\n envVars: Record<string, string>\n): void {\n if (Object.keys(envVars).length === 0) {\n return\n }\n\n const envContent = Object.entries(envVars)\n .map(([key, value]) => {\n // Quote values containing spaces, quotes, newlines, or shell special chars\n if (/[\\s'\"#$`\\\\]/.test(value) || value.includes('\\n')) {\n // Escape backslashes, quotes, and newlines for .env compatibility\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n return `${key}=\"${escaped}\"`\n }\n return `${key}=${value}`\n })\n .join('\\n') + '\\n'\n\n const envPath = path.join(targetPath, '.env')\n fs.writeFileSync(envPath, envContent, { mode: 0o600 })\n console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`)\n}\n\n/**\n * Complete env setup for a worktree: fetch from server and write to .env\n *\n * @param worktreePath - Path to the worktree directory\n * @param apiUrl - Base API URL\n * @param accessToken - OAuth access token\n * @returns true if env vars were written, false otherwise\n */\nexport async function setupWorktreeEnv(\n worktreePath: string,\n apiUrl: string,\n accessToken: string\n): Promise<boolean> {\n const envVars = await fetchEnvVars(apiUrl, accessToken)\n\n if (Object.keys(envVars).length === 0) {\n return false\n }\n\n writeEnvFile(worktreePath, envVars)\n return true\n}\n","/**\n * `episoda release` command (EP944)\n *\n * Removes a worktree for a module, freeing up disk space.\n *\n * Usage:\n * episoda release {moduleUid}\n * episoda release EP100\n *\n * Removes:\n * ~/episoda/{workspace}/{project}/{moduleUid}/\n *\n * The branch is preserved in the repository - only the worktree\n * (working directory) is removed. Use --force to release even\n * with uncommitted changes.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { loadConfig, GitExecutor } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchWithRetry } from '../utils/http'\nimport {\n WorktreeManager,\n findProjectRoot\n} from '../daemon/worktree-manager'\n\nexport interface ReleaseCommandOptions {\n force?: boolean // Force release even with uncommitted changes\n apiUrl?: string\n}\n\n/**\n * Release command - removes a worktree for a module\n */\nexport async function releaseCommand(\n moduleUid: string,\n options: ReleaseCommandOptions = {}\n): Promise<void> {\n // Step 1: Validate module UID format\n if (!moduleUid || !moduleUid.match(/^EP\\d+$/)) {\n throw new Error(\n 'Invalid module UID format. Expected format: EP###\\n' +\n 'Example: episoda release EP100'\n )\n }\n\n status.info(`Releasing ${moduleUid}...`)\n status.info('')\n\n // Step 2: Find project root\n const projectRoot = await findProjectRoot(process.cwd())\n if (!projectRoot) {\n throw new Error(\n 'Not in a worktree project.\\n' +\n 'Run this command from within a project cloned with `episoda clone`.'\n )\n }\n\n // Step 3: Initialize worktree manager\n const worktreeManager = new WorktreeManager(projectRoot)\n const initialized = await worktreeManager.initialize()\n if (!initialized) {\n throw new Error(\n `Invalid worktree project at ${projectRoot}.`\n )\n }\n\n // Step 4: Check if worktree exists\n const existing = worktreeManager.getWorktreeByModuleUid(moduleUid)\n if (!existing) {\n status.warning(`Module ${moduleUid} is not checked out.`)\n status.info('')\n status.info('Available worktrees:')\n const worktrees = worktreeManager.listWorktrees()\n if (worktrees.length === 0) {\n status.info(' (none)')\n } else {\n for (const wt of worktrees) {\n status.info(` ${wt.moduleUid} → ${wt.branchName}`)\n }\n }\n return\n }\n\n // Step 5: Check for uncommitted changes (unless --force)\n if (!options.force) {\n const hasChanges = await checkUncommittedChanges(existing.worktreePath)\n if (hasChanges) {\n throw new Error(\n `Module ${moduleUid} has uncommitted changes.\\n` +\n 'Commit or stash your changes first, or use --force to discard them.'\n )\n }\n }\n\n // Step 6: Check if we're inside the worktree being released\n const currentPath = path.resolve(process.cwd())\n if (currentPath.startsWith(existing.worktreePath)) {\n status.warning('You are inside the worktree being released.')\n status.info(`Please cd to ${projectRoot} first.`)\n throw new Error('Cannot release worktree while inside it')\n }\n\n // EP959-m3: Run cleanup script before removing worktree (if configured)\n const config = await loadConfig()\n const cleanupScript = config?.project_settings?.worktree_cleanup_script\n if (cleanupScript) {\n status.info('Running cleanup script...')\n const cleanupResult = await worktreeManager.runCleanupScript(moduleUid, cleanupScript)\n if (!cleanupResult.success) {\n // Cleanup failure is a warning, not a blocking error\n status.warning(`Cleanup script failed: ${cleanupResult.error}`)\n status.info('Continuing with worktree removal...')\n } else {\n status.success('✓ Cleanup script completed')\n }\n }\n\n // Step 7: Remove the worktree\n status.info('Removing worktree...')\n const result = await worktreeManager.removeWorktree(moduleUid, options.force)\n\n if (!result.success) {\n throw new Error(`Failed to remove worktree: ${result.error}`)\n }\n\n status.success('✓ Worktree removed')\n status.info('')\n\n // Step 8: Update server (optional, non-blocking)\n // EP959-m3: Reuse config loaded earlier for cleanup script check\n try {\n if (config?.access_token) {\n const worktreeConfig = worktreeManager.getConfig()\n if (worktreeConfig) {\n await updateModuleRelease(\n config.api_url || 'https://episoda.dev',\n worktreeConfig.projectId,\n worktreeConfig.workspaceSlug,\n moduleUid,\n config.access_token\n )\n }\n }\n } catch (error) {\n // Don't fail the command if server update fails\n }\n\n // Success message\n status.success(`Module ${moduleUid} released successfully!`)\n status.info('')\n status.info(`Branch \"${existing.branchName}\" is preserved in the repository.`)\n status.info('')\n status.info('To check out again:')\n status.info(` episoda checkout ${moduleUid}`)\n status.info('')\n}\n\n/**\n * Check if a worktree has uncommitted changes\n */\nasync function checkUncommittedChanges(worktreePath: string): Promise<boolean> {\n const gitExecutor = new GitExecutor()\n\n const result = await gitExecutor.execute(\n { action: 'status' },\n { cwd: worktreePath }\n )\n\n if (!result.success) {\n // Assume has changes if we can't check\n return true\n }\n\n // Check for uncommitted files\n const uncommitted = result.details?.uncommittedFiles || []\n return uncommitted.length > 0\n}\n\n/**\n * Update server with release status\n * EP944: Uses existing checkout/release endpoint which supports UIDs\n */\nasync function updateModuleRelease(\n apiUrl: string,\n projectId: string,\n workspaceSlug: string,\n moduleUid: string,\n accessToken: string\n): Promise<void> {\n // Use the existing checkout/release endpoint which supports UIDs via resolveModuleId\n const url = `${apiUrl}/api/modules/${moduleUid}/checkout/release`\n\n await fetchWithRetry(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n 'X-Project-Id': projectId,\n 'X-Workspace-Slug': workspaceSlug\n }\n })\n}\n","/**\n * `episoda list` command (EP944)\n *\n * Lists all cloned projects and their worktrees.\n *\n * Usage:\n * episoda list # List all projects\n * episoda list worktrees # List worktrees for current project\n *\n * Shows:\n * - Cloned projects under ~/episoda/\n * - Active worktrees for each project\n * - Uncommitted change status\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport chalk from 'chalk'\nimport { GitExecutor } from '@episoda/core'\nimport { status } from '../output'\nimport {\n WorktreeManager,\n getEpisodaRoot,\n isWorktreeProject,\n findProjectRoot,\n WorktreeInfo\n} from '../daemon/worktree-manager'\n\nexport interface ListCommandOptions {\n verbose?: boolean\n}\n\n/**\n * List command - shows cloned projects and worktrees\n */\nexport async function listCommand(\n subcommand?: string,\n options: ListCommandOptions = {}\n): Promise<void> {\n if (subcommand === 'worktrees' || subcommand === 'wt') {\n await listWorktrees(options)\n } else {\n await listProjects(options)\n }\n}\n\n/**\n * List all cloned projects\n */\nasync function listProjects(options: ListCommandOptions): Promise<void> {\n const episodaRoot = getEpisodaRoot()\n\n if (!fs.existsSync(episodaRoot)) {\n status.info('No projects cloned yet.')\n status.info('')\n status.info('Clone a project with:')\n status.info(' episoda clone {workspace}/{project}')\n return\n }\n\n // Scan for projects (workspace/project structure)\n const projects: Array<{\n workspaceSlug: string\n projectSlug: string\n projectPath: string\n worktreeCount: number\n }> = []\n\n // List workspaces\n const workspaces = fs.readdirSync(episodaRoot, { withFileTypes: true })\n .filter(d => d.isDirectory() && !d.name.startsWith('.'))\n\n for (const workspace of workspaces) {\n const workspacePath = path.join(episodaRoot, workspace.name)\n\n // List projects in workspace\n const projectDirs = fs.readdirSync(workspacePath, { withFileTypes: true })\n .filter(d => d.isDirectory() && !d.name.startsWith('.'))\n\n for (const projectDir of projectDirs) {\n const projectPath = path.join(workspacePath, projectDir.name)\n\n // Check if it's a valid worktree project\n if (await isWorktreeProject(projectPath)) {\n const manager = new WorktreeManager(projectPath)\n await manager.initialize()\n const worktrees = manager.listWorktrees()\n\n projects.push({\n workspaceSlug: workspace.name,\n projectSlug: projectDir.name,\n projectPath,\n worktreeCount: worktrees.length\n })\n }\n }\n }\n\n if (projects.length === 0) {\n status.info('No worktree projects found.')\n status.info('')\n status.info('Clone a project with:')\n status.info(' episoda clone {workspace}/{project}')\n return\n }\n\n console.log('')\n console.log(chalk.bold('Cloned Projects'))\n console.log('─'.repeat(60))\n\n for (const project of projects) {\n const worktreeLabel = project.worktreeCount === 0\n ? chalk.gray('(no worktrees)')\n : project.worktreeCount === 1\n ? chalk.cyan('1 worktree')\n : chalk.cyan(`${project.worktreeCount} worktrees`)\n\n console.log('')\n console.log(` ${chalk.bold(project.workspaceSlug)}/${chalk.bold(project.projectSlug)} ${worktreeLabel}`)\n console.log(chalk.gray(` ${project.projectPath}`))\n }\n\n console.log('')\n console.log(chalk.gray('─'.repeat(60)))\n console.log(`${projects.length} project${projects.length === 1 ? '' : 's'}`)\n console.log('')\n\n if (options.verbose) {\n status.info('Commands:')\n status.info(' episoda list worktrees # Show worktrees for current project')\n status.info(' episoda checkout EP### # Create a new worktree')\n }\n}\n\n/**\n * List worktrees for the current project\n */\nasync function listWorktrees(options: ListCommandOptions): Promise<void> {\n const projectRoot = await findProjectRoot(process.cwd())\n if (!projectRoot) {\n throw new Error(\n 'Not in a worktree project.\\n' +\n 'Run this from within a project cloned with `episoda clone`.'\n )\n }\n\n const manager = new WorktreeManager(projectRoot)\n const initialized = await manager.initialize()\n if (!initialized) {\n throw new Error(`Invalid worktree project at ${projectRoot}`)\n }\n\n const config = manager.getConfig()\n const worktrees = manager.listWorktrees()\n\n console.log('')\n console.log(chalk.bold(`Worktrees for ${config?.workspaceSlug}/${config?.projectSlug}`))\n console.log('─'.repeat(60))\n\n if (worktrees.length === 0) {\n console.log('')\n console.log(chalk.gray(' No worktrees checked out'))\n console.log('')\n status.info('Create a worktree with:')\n status.info(' episoda checkout {moduleUid}')\n return\n }\n\n const gitExecutor = new GitExecutor()\n\n for (const wt of worktrees) {\n console.log('')\n\n // Check for uncommitted changes\n let statusIndicator = chalk.green('●')\n let statusNote = ''\n\n try {\n const result = await gitExecutor.execute(\n { action: 'status' },\n { cwd: wt.worktreePath }\n )\n\n if (result.success) {\n const uncommitted = result.details?.uncommittedFiles || []\n if (uncommitted.length > 0) {\n statusIndicator = chalk.yellow('●')\n statusNote = chalk.yellow(` (${uncommitted.length} uncommitted)`)\n }\n }\n } catch {\n statusIndicator = chalk.gray('○')\n }\n\n console.log(` ${statusIndicator} ${chalk.bold(wt.moduleUid)}${statusNote}`)\n console.log(chalk.gray(` Branch: ${wt.branchName}`))\n console.log(chalk.gray(` Path: ${wt.worktreePath}`))\n\n if (options.verbose) {\n console.log(chalk.gray(` Created: ${new Date(wt.createdAt).toLocaleDateString()}`))\n console.log(chalk.gray(` Last accessed: ${new Date(wt.lastAccessed).toLocaleDateString()}`))\n }\n }\n\n console.log('')\n console.log(chalk.gray('─'.repeat(60)))\n console.log(`${worktrees.length} worktree${worktrees.length === 1 ? '' : 's'}`)\n console.log('')\n console.log(chalk.gray('● clean ') + chalk.yellow('● uncommitted changes'))\n console.log('')\n}\n\n","/**\n * `episoda update` command\n *\n * EP989: Manual version checking and updates for the Episoda CLI.\n *\n * Usage:\n * episoda update - Check and prompt for update interactively\n * episoda update -y - Auto-update without prompting\n * episoda update --check - Just check, don't update\n * episoda update --restart - After update, restart daemon if running\n */\n\nimport { execSync, spawn } from 'child_process'\nimport { VERSION } from '@episoda/core'\nimport { checkForUpdates, getInstalledVersion } from '../utils/update-checker'\nimport { getStatus } from '../ipc/ipc-client'\nimport { status } from '../output'\n\nexport interface UpdateOptions {\n yes?: boolean // -y: Auto-update without prompting\n check?: boolean // --check: Just check, don't update\n restart?: boolean // --restart: Restart daemon after update\n}\n\n/**\n * Check if daemon is currently running\n */\nasync function isDaemonRunning(): Promise<boolean> {\n try {\n const daemonStatus = await getStatus()\n return !!daemonStatus\n } catch {\n return false\n }\n}\n\n/**\n * Stop the daemon gracefully\n */\nfunction stopDaemon(): boolean {\n try {\n execSync('episoda stop', { stdio: 'pipe' })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Start the daemon in detached mode\n */\nfunction startDaemon(): boolean {\n try {\n // Start daemon in detached mode using async spawn\n // This allows the daemon to run independently of this process\n const child = spawn('episoda', ['dev'], {\n detached: true,\n stdio: 'ignore',\n shell: process.platform === 'win32'\n })\n child.unref() // Allow parent to exit independently\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Perform the npm update synchronously\n * Returns true if update was successful\n */\nfunction performUpdate(): { success: boolean; error?: string } {\n try {\n // Use execSync for synchronous update so we can restart daemon after\n execSync('npm update -g episoda', {\n stdio: 'pipe',\n timeout: 120000 // 2 minute timeout\n })\n return { success: true }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n // Check for common permission errors\n if (message.includes('EACCES') || message.includes('permission denied')) {\n return {\n success: false,\n error: 'Permission denied. Try running with sudo:\\n sudo npm update -g episoda'\n }\n }\n\n return { success: false, error: message }\n }\n}\n\nexport async function updateCommand(options: UpdateOptions = {}): Promise<void> {\n const currentVersion = VERSION\n\n // Check for updates\n status.info(`Current version: ${currentVersion}`)\n status.info('Checking for updates...')\n\n const result = await checkForUpdates(currentVersion)\n\n // EP989: Handle offline/network error case separately\n if (result.offline) {\n status.warning('⚠ Could not check for updates (offline or network error)')\n status.info(` Current version: ${currentVersion}`)\n status.info('')\n status.info('Check your internet connection and try again.')\n return\n }\n\n if (!result.updateAvailable) {\n status.success(`✓ Already up to date (${currentVersion})`)\n return\n }\n\n status.info(`Update available: ${result.currentVersion} → ${result.latestVersion}`)\n\n // If --check flag, just show info and exit\n if (options.check) {\n status.info('')\n status.info('Run \"episoda update\" to install the update.')\n return\n }\n\n // If not -y flag, prompt for confirmation (but we can't do interactive prompts easily)\n // For non-interactive use, we'll proceed if -y is set, otherwise show instructions\n if (!options.yes) {\n status.info('')\n status.info('To update, run:')\n status.info(' episoda update -y')\n status.info('')\n status.info('Or with daemon restart:')\n status.info(' episoda update -y --restart')\n return\n }\n\n // Check if daemon is running (for restart logic)\n const daemonWasRunning = options.restart ? await isDaemonRunning() : false\n\n // Stop daemon before update if --restart flag is set and daemon is running\n if (options.restart && daemonWasRunning) {\n status.info('Stopping daemon before update...')\n if (!stopDaemon()) {\n status.warning('Could not stop daemon gracefully, continuing with update...')\n }\n }\n\n // Perform the update\n status.info('Updating...')\n const updateResult = performUpdate()\n\n if (!updateResult.success) {\n status.error(`✗ Update failed: ${updateResult.error}`)\n\n // If we stopped the daemon, try to restart it even though update failed\n if (options.restart && daemonWasRunning) {\n status.info('Restarting daemon...')\n startDaemon()\n }\n\n process.exit(1)\n }\n\n // EP989: Verify the update actually installed the new version\n const installedVersion = getInstalledVersion()\n if (installedVersion && installedVersion !== result.latestVersion) {\n status.warning(`⚠ Update completed but version mismatch detected`)\n status.info(` Expected: ${result.latestVersion}`)\n status.info(` Installed: ${installedVersion}`)\n status.info('')\n status.info('Try running manually: npm update -g episoda')\n } else if (installedVersion) {\n status.success(`✓ Updated to ${installedVersion}`)\n } else {\n // Could not verify, but npm command succeeded\n status.success(`✓ Updated to ${result.latestVersion}`)\n }\n\n // Restart daemon if requested and it was running\n if (options.restart && daemonWasRunning) {\n status.info('Restarting daemon...')\n\n // Small delay to ensure update is complete\n await new Promise(resolve => setTimeout(resolve, 1000))\n\n if (startDaemon()) {\n status.success('✓ Daemon restarted')\n status.info('')\n status.info('Run \"episoda status\" to verify connection.')\n } else {\n status.warning('Could not restart daemon automatically.')\n status.info('Run \"episoda dev\" to start the daemon.')\n }\n } else if (options.restart) {\n // --restart was set but daemon wasn't running\n status.info('Daemon was not running, skipping restart.')\n } else {\n // No restart requested, remind user if daemon might need restart\n const daemonRunning = await isDaemonRunning()\n if (daemonRunning) {\n status.info('')\n status.info('Note: Restart the daemon to use the new version:')\n status.info(' episoda stop && episoda dev')\n }\n }\n}\n","/**\n * EP998: `episoda run` command\n *\n * Runs a command with environment variables injected from the database.\n * Secrets are fetched at runtime and never written to disk.\n *\n * Usage:\n * episoda run npm run dev\n * episoda run -- npm run dev --port 3001\n * episoda run python manage.py runserver\n *\n * Pattern inspired by:\n * - Doppler: `doppler run -- <command>`\n * - Railway: `railway run <command>`\n */\n\nimport { spawn, ChildProcess } from 'child_process'\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchEnvVarsWithCache, EnvCacheOptions } from '../utils/env-cache'\n\nexport interface RunCommandOptions {\n noCache?: boolean\n cacheTtl?: number\n offline?: boolean\n verbose?: boolean\n}\n\n/**\n * Run a command with injected environment variables\n */\nexport async function runCommand(\n args: string[],\n options: RunCommandOptions = {}\n): Promise<void> {\n // Validate we have a command to run\n if (args.length === 0) {\n throw new Error(\n 'No command specified.\\n\\n' +\n 'Usage:\\n' +\n ' episoda run <command> [args...]\\n' +\n ' episoda run -- <command> [args...]\\n\\n' +\n 'Examples:\\n' +\n ' episoda run npm run dev\\n' +\n ' episoda run -- npm run dev --port 3001\\n' +\n ' episoda run python manage.py runserver'\n )\n }\n\n // Load auth config\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error(\n 'Not authenticated. Please run `episoda auth` first.'\n )\n }\n\n const apiUrl = config.api_url || 'https://episoda.dev'\n\n // Fetch environment variables\n const cacheOptions: EnvCacheOptions = {\n noCache: options.noCache,\n cacheTtl: options.cacheTtl,\n offline: options.offline,\n projectId: config.project_id\n }\n\n if (options.verbose) {\n status.info('Fetching environment variables...')\n }\n\n const { envVars, fromCache, cacheAge } = await fetchEnvVarsWithCache(\n apiUrl,\n config.access_token,\n cacheOptions\n )\n\n const varCount = Object.keys(envVars).length\n\n if (varCount === 0) {\n status.warning('No environment variables found. Command will run with system env only.')\n } else if (options.verbose) {\n if (fromCache) {\n status.info(`Using ${varCount} cached env vars (${Math.round(cacheAge! / 1000)}s old)`)\n } else {\n status.info(`Fetched ${varCount} env vars from server`)\n }\n // Show variable names (not values) in verbose mode\n status.info(`Variables: ${Object.keys(envVars).join(', ')}`)\n }\n\n // Merge with current process env (fetched vars take precedence)\n const mergedEnv = {\n ...process.env,\n ...envVars\n }\n\n // Extract command and arguments\n const [command, ...commandArgs] = args\n\n if (options.verbose) {\n status.info(`Running: ${command} ${commandArgs.join(' ')}`)\n status.info('')\n }\n\n // Spawn child process\n const child: ChildProcess = spawn(command, commandArgs, {\n stdio: 'inherit',\n env: mergedEnv,\n shell: process.platform === 'win32' // Use shell on Windows for .cmd/.bat files\n })\n\n // Forward signals to child process\n const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGHUP']\n const signalHandlers: Map<NodeJS.Signals, () => void> = new Map()\n\n for (const signal of signals) {\n const handler = () => {\n if (child.pid) {\n child.kill(signal)\n }\n }\n signalHandlers.set(signal, handler)\n process.on(signal, handler)\n }\n\n // Handle child process exit\n child.on('error', (error) => {\n // Clean up signal handlers\n for (const [signal, handler] of signalHandlers) {\n process.removeListener(signal, handler)\n }\n\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n status.error(`Command not found: ${command}`)\n process.exit(127)\n } else {\n status.error(`Failed to start command: ${error.message}`)\n process.exit(1)\n }\n })\n\n child.on('exit', (code, signal) => {\n // Clean up signal handlers\n for (const [sig, handler] of signalHandlers) {\n process.removeListener(sig, handler)\n }\n\n if (signal) {\n // Process was killed by signal\n process.exit(128 + (signalToNumber(signal) || 0))\n } else {\n process.exit(code ?? 1)\n }\n })\n}\n\n/**\n * Convert signal name to number for exit code calculation\n */\nfunction signalToNumber(signal: string): number | undefined {\n const signalMap: Record<string, number> = {\n SIGHUP: 1,\n SIGINT: 2,\n SIGQUIT: 3,\n SIGTERM: 15\n }\n return signalMap[signal]\n}\n","/**\n * EP998: Environment Variable Caching\n *\n * Provides caching for fetched environment variables to reduce API calls\n * and enable offline fallback.\n *\n * Cache location: ~/.episoda/cache/env-vars-{projectId}.json\n * Default TTL: 60 seconds\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as os from 'os'\nimport { fetchEnvVars } from './env-setup'\n\nexport interface EnvCacheOptions {\n noCache?: boolean // Force fresh fetch, ignore cache\n cacheTtl?: number // Cache TTL in seconds (default: 60)\n offline?: boolean // Use cache only, don't fetch\n projectId?: string // Project ID for cache file naming\n}\n\nexport interface EnvCacheResult {\n envVars: Record<string, string>\n fromCache: boolean\n cacheAge?: number // Age of cache in milliseconds (if from cache)\n}\n\ninterface CacheData {\n vars: Record<string, string>\n fetchedAt: number // Unix timestamp in milliseconds\n projectId: string\n}\n\nconst DEFAULT_CACHE_TTL = 60 // seconds\nconst CACHE_DIR = path.join(os.homedir(), '.episoda', 'cache')\n\n/**\n * Get the cache file path for a project\n */\nfunction getCacheFilePath(projectId: string): string {\n return path.join(CACHE_DIR, `env-vars-${projectId}.json`)\n}\n\n/**\n * Ensure cache directory exists\n */\nfunction ensureCacheDir(): void {\n if (!fs.existsSync(CACHE_DIR)) {\n fs.mkdirSync(CACHE_DIR, { recursive: true, mode: 0o700 })\n }\n}\n\n/**\n * Read cached environment variables\n */\nfunction readCache(projectId: string): CacheData | null {\n try {\n const cacheFile = getCacheFilePath(projectId)\n if (!fs.existsSync(cacheFile)) {\n return null\n }\n\n const content = fs.readFileSync(cacheFile, 'utf-8')\n const data = JSON.parse(content) as CacheData\n\n // Validate cache data structure\n if (!data.vars || typeof data.vars !== 'object' || !data.fetchedAt) {\n return null\n }\n\n return data\n } catch {\n return null\n }\n}\n\n/**\n * Write environment variables to cache\n */\nfunction writeCache(projectId: string, vars: Record<string, string>): void {\n try {\n ensureCacheDir()\n const cacheFile = getCacheFilePath(projectId)\n const data: CacheData = {\n vars,\n fetchedAt: Date.now(),\n projectId\n }\n fs.writeFileSync(cacheFile, JSON.stringify(data, null, 2), { mode: 0o600 })\n } catch (error) {\n // Silently fail on cache write errors\n console.warn('[env-cache] Failed to write cache:', error instanceof Error ? error.message : error)\n }\n}\n\n/**\n * Check if cache is still valid (within TTL)\n */\nfunction isCacheValid(cache: CacheData, ttlSeconds: number): boolean {\n const ageMs = Date.now() - cache.fetchedAt\n return ageMs < ttlSeconds * 1000\n}\n\n/**\n * Fetch environment variables with caching support\n *\n * @param apiUrl - Base API URL\n * @param accessToken - OAuth access token\n * @param options - Cache options\n * @returns Environment variables and cache status\n */\nexport async function fetchEnvVarsWithCache(\n apiUrl: string,\n accessToken: string,\n options: EnvCacheOptions = {}\n): Promise<EnvCacheResult> {\n const {\n noCache = false,\n cacheTtl = DEFAULT_CACHE_TTL,\n offline = false,\n projectId = 'default'\n } = options\n\n // Offline mode: only use cache\n if (offline) {\n const cache = readCache(projectId)\n if (cache && Object.keys(cache.vars).length > 0) {\n return {\n envVars: cache.vars,\n fromCache: true,\n cacheAge: Date.now() - cache.fetchedAt\n }\n }\n throw new Error(\n 'Offline mode requires cached env vars, but no cache found.\\n' +\n 'Run without --offline first to populate the cache.'\n )\n }\n\n // Check cache (unless noCache is set)\n if (!noCache) {\n const cache = readCache(projectId)\n if (cache && isCacheValid(cache, cacheTtl)) {\n return {\n envVars: cache.vars,\n fromCache: true,\n cacheAge: Date.now() - cache.fetchedAt\n }\n }\n }\n\n // Fetch from server\n try {\n const envVars = await fetchEnvVars(apiUrl, accessToken)\n\n // Update cache (even if fetch returned empty, to record the attempt)\n if (Object.keys(envVars).length > 0) {\n writeCache(projectId, envVars)\n }\n\n return {\n envVars,\n fromCache: false\n }\n } catch (error) {\n // On fetch error, try to use stale cache as fallback\n const cache = readCache(projectId)\n if (cache && Object.keys(cache.vars).length > 0) {\n const cacheAge = Date.now() - cache.fetchedAt\n console.warn(\n `[env-cache] Failed to fetch env vars, using stale cache (${Math.round(cacheAge / 1000)}s old)`\n )\n return {\n envVars: cache.vars,\n fromCache: true,\n cacheAge\n }\n }\n\n // No cache available, rethrow error\n throw new Error(\n `Failed to fetch environment variables: ${error instanceof Error ? error.message : error}\\n` +\n 'No cached values available as fallback.'\n )\n }\n}\n\n/**\n * Clear the env var cache for a project\n */\nexport function clearEnvCache(projectId: string): boolean {\n try {\n const cacheFile = getCacheFilePath(projectId)\n if (fs.existsSync(cacheFile)) {\n fs.unlinkSync(cacheFile)\n return true\n }\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Get cache info for a project\n */\nexport function getCacheInfo(projectId: string): { exists: boolean; age?: number; varCount?: number } {\n const cache = readCache(projectId)\n if (!cache) {\n return { exists: false }\n }\n return {\n exists: true,\n age: Date.now() - cache.fetchedAt,\n varCount: Object.keys(cache.vars).length\n }\n}\n","/**\n * EP998: `episoda shell` command\n *\n * Opens an interactive shell with environment variables injected.\n * Pattern inspired by Railway's `railway shell` command.\n *\n * Usage:\n * episoda shell # Opens $SHELL with env vars\n * episoda shell bash # Opens bash specifically\n * episoda shell --print # Prints export statements for eval\n */\n\nimport { spawn } from 'child_process'\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchEnvVarsWithCache, EnvCacheOptions } from '../utils/env-cache'\n\nexport interface ShellCommandOptions {\n print?: boolean // Print export statements instead of opening shell\n noCache?: boolean\n cacheTtl?: number\n offline?: boolean\n}\n\n/**\n * Open an interactive shell with injected environment variables\n */\nexport async function shellCommand(\n shell?: string,\n options: ShellCommandOptions = {}\n): Promise<void> {\n // Load auth config\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error(\n 'Not authenticated. Please run `episoda auth` first.'\n )\n }\n\n const apiUrl = config.api_url || 'https://episoda.dev'\n\n // Fetch environment variables\n const cacheOptions: EnvCacheOptions = {\n noCache: options.noCache,\n cacheTtl: options.cacheTtl,\n offline: options.offline,\n projectId: config.project_id\n }\n\n status.info('Fetching environment variables...')\n\n const { envVars, fromCache, cacheAge } = await fetchEnvVarsWithCache(\n apiUrl,\n config.access_token,\n cacheOptions\n )\n\n const varCount = Object.keys(envVars).length\n\n if (varCount === 0) {\n status.warning('No environment variables found.')\n } else if (fromCache) {\n status.info(`Using ${varCount} cached env vars (${Math.round(cacheAge! / 1000)}s old)`)\n } else {\n status.info(`Fetched ${varCount} env vars from server`)\n }\n\n // Print mode: output export statements for eval\n if (options.print) {\n printExports(envVars)\n return\n }\n\n // Determine which shell to use\n const targetShell = shell || process.env.SHELL || '/bin/bash'\n\n status.info(`Opening ${targetShell} with ${varCount} environment variables...`)\n status.info('Type \"exit\" to return to your normal shell.')\n status.info('')\n\n // Merge with current process env (fetched vars take precedence)\n const mergedEnv = {\n ...process.env,\n ...envVars,\n // Set a marker so users know they're in an episoda shell\n EPISODA_SHELL: '1'\n }\n\n // Spawn interactive shell\n const child = spawn(targetShell, [], {\n stdio: 'inherit',\n env: mergedEnv\n })\n\n // Handle child process exit\n child.on('error', (error) => {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n status.error(`Shell not found: ${targetShell}`)\n process.exit(127)\n } else {\n status.error(`Failed to start shell: ${error.message}`)\n process.exit(1)\n }\n })\n\n child.on('exit', (code) => {\n process.exit(code ?? 0)\n })\n}\n\n/**\n * Print export statements for sourcing in current shell\n *\n * Usage: eval $(episoda shell --print)\n */\nfunction printExports(envVars: Record<string, string>): void {\n for (const [key, value] of Object.entries(envVars)) {\n // Escape single quotes in value for shell safety\n const escapedValue = value.replace(/'/g, \"'\\\\''\")\n console.log(`export ${key}='${escapedValue}'`)\n }\n}\n","/**\n * EP998: `episoda env` commands\n *\n * Manage environment variables from the command line.\n *\n * Usage:\n * episoda env list # List env var keys\n * episoda env list --show-values # List with values (requires confirmation)\n * episoda env set KEY=value # Add/update env var\n * episoda env set KEY # Prompts for value (hidden)\n * episoda env remove KEY # Remove env var\n * episoda env pull # Generate .env file from DB\n * episoda env pull --stdout # Print to stdout\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as readline from 'readline'\nimport { loadConfig } from '@episoda/core'\nimport { status } from '../output'\nimport { fetchEnvVars, writeEnvFile } from '../utils/env-setup'\n\nexport interface EnvListOptions {\n showValues?: boolean\n}\n\nexport interface EnvSetOptions {\n environment?: 'all' | 'dev' | 'preview' | 'prod'\n}\n\nexport interface EnvPullOptions {\n file?: string // Custom filename (default: .env)\n stdout?: boolean // Print to stdout instead of file\n}\n\n/**\n * List environment variables\n */\nexport async function envListCommand(options: EnvListOptions = {}): Promise<void> {\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error('Not authenticated. Please run `episoda auth` first.')\n }\n\n const apiUrl = config.api_url || 'https://episoda.dev'\n\n // Fetch env vars list from project endpoint (shows keys only by default)\n const url = `${apiUrl}/api/projects/${config.project_id}/env-vars`\n const response = await fetch(url, {\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n throw new Error(`Failed to list env vars: ${response.status} ${response.statusText}`)\n }\n\n const data = await response.json() as {\n success: boolean\n env_vars: Array<{\n id: string\n key: string\n environment: string\n value_preview?: string\n is_sealed: boolean\n }>\n }\n\n if (!data.success || !data.env_vars) {\n throw new Error('Failed to parse env vars response')\n }\n\n if (data.env_vars.length === 0) {\n status.info('No environment variables configured.')\n status.info('Add one with: episoda env set KEY=value')\n return\n }\n\n console.log('\\nEnvironment Variables:')\n console.log('─'.repeat(60))\n\n for (const envVar of data.env_vars) {\n const envBadge = envVar.environment === 'all' ? '' : ` [${envVar.environment}]`\n const sealed = envVar.is_sealed ? ' (sealed)' : ''\n\n if (options.showValues) {\n // For show-values, we need to fetch the actual values\n const preview = envVar.value_preview || '••••••••'\n console.log(` ${envVar.key}${envBadge}${sealed} = ${preview}`)\n } else {\n console.log(` ${envVar.key}${envBadge}${sealed}`)\n }\n }\n\n console.log('')\n console.log(`Total: ${data.env_vars.length} variable(s)`)\n\n if (!options.showValues) {\n console.log('\\nTip: Use --show-values to see value previews')\n }\n}\n\n/**\n * Set (create or update) an environment variable\n */\nexport async function envSetCommand(\n keyValue: string,\n options: EnvSetOptions = {}\n): Promise<void> {\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error('Not authenticated. Please run `episoda auth` first.')\n }\n\n const apiUrl = config.api_url || 'https://episoda.dev'\n\n // Parse KEY=value or just KEY\n let key: string\n let value: string\n\n const eqIndex = keyValue.indexOf('=')\n if (eqIndex === -1) {\n // No '=', prompt for value\n key = keyValue\n value = await promptForValue(key)\n } else {\n key = keyValue.slice(0, eqIndex)\n value = keyValue.slice(eqIndex + 1)\n }\n\n // Validate key format\n if (!/^[A-Z][A-Z0-9_]*$/.test(key)) {\n throw new Error(\n `Invalid key format: ${key}\\n` +\n 'Keys must be uppercase with underscores (e.g., API_KEY, DATABASE_URL)'\n )\n }\n\n // Create the env var\n const url = `${apiUrl}/api/projects/${config.project_id}/env-vars`\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n key,\n value,\n environment: options.environment || 'dev'\n })\n })\n\n const data = await response.json() as { success: boolean; message?: string; error?: { message: string } }\n\n if (!response.ok) {\n if (response.status === 409) {\n // Duplicate key - try to update instead\n status.info(`${key} already exists, updating...`)\n await updateEnvVar(apiUrl, config.access_token, config.project_id, key, value)\n return\n }\n throw new Error(data.error?.message || `Failed to set env var: ${response.status}`)\n }\n\n status.success(`Set ${key} successfully`)\n}\n\n/**\n * Update an existing env var (helper for envSetCommand)\n */\nasync function updateEnvVar(\n apiUrl: string,\n accessToken: string,\n projectId: string,\n key: string,\n value: string\n): Promise<void> {\n // First, get the env var ID by key\n const listUrl = `${apiUrl}/api/projects/${projectId}/env-vars`\n const listResponse = await fetch(listUrl, {\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!listResponse.ok) {\n throw new Error('Failed to find existing env var for update')\n }\n\n const listData = await listResponse.json() as {\n env_vars: Array<{ id: string; key: string }>\n }\n\n const existingVar = listData.env_vars?.find(v => v.key === key)\n if (!existingVar) {\n throw new Error(`Env var ${key} not found for update`)\n }\n\n // Update the env var\n const updateUrl = `${apiUrl}/api/projects/${projectId}/env-vars/${existingVar.id}`\n const updateResponse = await fetch(updateUrl, {\n method: 'PATCH',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({ value })\n })\n\n if (!updateResponse.ok) {\n throw new Error(`Failed to update env var: ${updateResponse.status}`)\n }\n\n status.success(`Updated ${key} successfully`)\n}\n\n/**\n * Remove an environment variable\n */\nexport async function envRemoveCommand(key: string): Promise<void> {\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error('Not authenticated. Please run `episoda auth` first.')\n }\n\n const apiUrl = config.api_url || 'https://episoda.dev'\n\n // First, get the env var ID by key\n const listUrl = `${apiUrl}/api/projects/${config.project_id}/env-vars`\n const listResponse = await fetch(listUrl, {\n headers: {\n 'Authorization': `Bearer ${config.access_token}`,\n 'Content-Type': 'application/json'\n }\n })\n\n if (!listResponse.ok) {\n throw new Error('Failed to list env vars')\n }\n\n const listData = await listResponse.json() as {\n env_vars: Array<{ id: string; key: string }>\n }\n\n const existingVar = listData.env_vars?.find(v => v.key === key)\n if (!existingVar) {\n throw new Error(`Env var ${key} not found`)\n }\n\n // Delete the env var\n const deleteUrl = `${apiUrl}/api/projects/${config.project_id}/env-vars/${existingVar.id}`\n const deleteResponse = await fetch(deleteUrl, {\n method: 'DELETE',\n headers: {\n 'Authorization': `Bearer ${config.access_token}`\n }\n })\n\n if (!deleteResponse.ok) {\n throw new Error(`Failed to remove env var: ${deleteResponse.status}`)\n }\n\n status.success(`Removed ${key}`)\n}\n\n/**\n * Pull environment variables and write to .env file\n */\nexport async function envPullCommand(options: EnvPullOptions = {}): Promise<void> {\n const config = await loadConfig()\n if (!config || !config.access_token) {\n throw new Error('Not authenticated. Please run `episoda auth` first.')\n }\n\n const apiUrl = config.api_url || 'https://episoda.dev'\n\n status.info('Fetching environment variables...')\n const envVars = await fetchEnvVars(apiUrl, config.access_token)\n\n if (Object.keys(envVars).length === 0) {\n status.warning('No environment variables found.')\n return\n }\n\n // Generate file content with header\n const header = [\n '# AUTO-GENERATED by episoda env pull',\n '# Do not edit - changes will be overwritten',\n `# Source of truth: ${apiUrl}/settings/env-vars`,\n `# Generated: ${new Date().toISOString()}`,\n '#',\n '# To update: episoda env set KEY=value',\n '# To refresh: episoda env pull',\n ''\n ].join('\\n')\n\n const envContent = Object.entries(envVars)\n .map(([key, value]) => {\n // Quote values containing spaces, quotes, newlines, or shell special chars\n if (/[\\s'\"#$`\\\\]/.test(value) || value.includes('\\n')) {\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n return `${key}=\"${escaped}\"`\n }\n return `${key}=${value}`\n })\n .join('\\n') + '\\n'\n\n const fullContent = header + envContent\n\n // Output to stdout if requested\n if (options.stdout) {\n console.log(fullContent)\n return\n }\n\n // Write to file\n const filename = options.file || '.env'\n const filepath = path.resolve(process.cwd(), filename)\n\n fs.writeFileSync(filepath, fullContent, { mode: 0o600 })\n status.success(`Wrote ${Object.keys(envVars).length} env vars to ${filename}`)\n}\n\n/**\n * Prompt for a value with hidden input\n */\nasync function promptForValue(key: string): Promise<string> {\n // Non-TTY mode: use readline question (value will be visible)\n if (!process.stdin.isTTY) {\n return new Promise((resolve, reject) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout\n })\n\n rl.question(`Enter value for ${key}: `, (answer) => {\n rl.close()\n resolve(answer)\n })\n\n rl.on('close', () => {\n // Handle Ctrl+D or pipe closed\n reject(new Error('Input closed'))\n })\n })\n }\n\n // TTY mode: use raw mode for hidden input\n return new Promise((resolve, reject) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout\n })\n\n let value = ''\n let resolved = false\n\n const cleanup = () => {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false)\n }\n process.stdin.removeListener('data', dataHandler)\n rl.close()\n }\n\n const dataHandler = (char: Buffer) => {\n const str = char.toString()\n\n if (str === '\\n' || str === '\\r' || str === '\\r\\n') {\n resolved = true\n cleanup()\n console.log('') // New line after hidden input\n resolve(value)\n } else if (str === '\\u0003') {\n // Ctrl+C\n cleanup()\n reject(new Error('Cancelled'))\n } else if (str === '\\u007F' || str === '\\b') {\n // Backspace\n value = value.slice(0, -1)\n } else {\n value += str\n }\n }\n\n // Hide input by disabling terminal echo\n process.stdin.setRawMode(true)\n process.stdout.write(`Enter value for ${key}: `)\n process.stdin.on('data', dataHandler)\n })\n}\n"],"mappings":"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","names":["exports","exports","status","GitExecutor","fs","path","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","fs","net","resolve","fs","path","import_core","resolve","status","execSync","path","fs","path","import_child_process","reachable","resolve","fs","path","import_child_process","import_core","fs","path","crypto","import_child_process","import_core","resolve","platform","os","fs","path","import_child_process","import_core","installGitCredentialHelper","updateShellProfile","platform","import_core","import_child_process","fs","path","import_core","fs","path","import_core","addProject","addProject","import_core","resolve","fs","path","GitExecutor","path","import_core","fs","path","import_chalk","import_core","chalk","import_child_process","import_core","isDaemonRunning","stopDaemon","startDaemon","resolve","import_child_process","import_core","fs","path","os","import_child_process","import_core","fs","path","import_core","resolve"]}
|