import { Effect } from 'effect'; import { Worktree, MergeConfig } from '../types/index.js'; import { GitError, FileSystemError, ProcessError } from '../types/errors.js'; /** * WorktreeService - Git worktree management with Effect-based error handling * * All public methods return Effect types for type-safe, composable error handling. * See CLAUDE.md for complete examples and patterns. * * ## Effect-ts Resources * - Effect Type: https://effect.website/docs/effect/effect-type * - Error Management: https://effect.website/docs/error-management/error-handling * - Effect Execution: https://effect.website/docs/guides/running-effects * - Tagged Errors: https://effect.website/docs/error-management/expected-errors#tagged-errors * * ## Key Concepts * - All operations return `Effect.Effect` where T is success type, E is error type * - Execute Effects with `Effect.runPromise()` or `Effect.match()` for type-safe handling * - Use error discrimination via `error._tag` property for TypeScript type narrowing * - Compose Effects with `Effect.flatMap()`, `Effect.all()`, `Effect.catchTag()`, etc. * * @example * ```typescript * // See individual method JSDoc for specific usage examples * const service = new WorktreeService(); * ``` */ export declare class WorktreeService { private rootPath; private gitRootPath; constructor(rootPath?: string); private getGitRepositoryRoot; isGitRepository(): boolean; getGitRootPath(): string; /** * SYNCHRONOUS HELPER: Resolves a branch name to its proper git reference. * * This method remains synchronous as it's called within Effect.gen contexts * but doesn't need to be wrapped in Effect itself. It's used by createWorktreeEffect() * to resolve branch references before creating worktrees. * * Handles multiple remotes and throws AmbiguousBranchError when disambiguation is needed. * * Priority order: * 1. Local branch exists -> return as-is * 2. Single remote branch -> return remote/branch * 3. Multiple remote branches -> throw AmbiguousBranchError * 4. No branches found -> return original (let git handle error) * * @private * @param {string} branchName - Branch name to resolve * @returns {string} Resolved branch reference * @throws {AmbiguousBranchError} When branch exists in multiple remotes */ private resolveBranchReference; /** * SYNCHRONOUS HELPER: Gets all git remotes for this repository. * * This method remains synchronous as it's a simple utility used by resolveBranchReference(). * No need for Effect version since it's a pure read operation with no complex error handling. * * @private * @returns {string[]} Array of remote names, empty array on error */ private getAllRemotes; /** * SYNCHRONOUS HELPER: Copies Claude Code session data between worktrees. * * This method remains synchronous and is wrapped in Effect.try when called from * createWorktreeEffect() (line ~676). This provides proper error handling while * keeping the implementation simple. * * @private * @param {string} sourceWorktreePath - Source worktree path * @param {string} targetWorktreePath - Target worktree path * @throws {Error} When copy operation fails */ private copyClaudeSessionData; /** * Effect-based hasClaudeDirectoryInBranch operation * Checks if a .claude directory exists in the worktree for the specified branch * * @param {string} branchName - Name of the branch to check * @returns {Effect.Effect} Effect containing true if .claude directory exists, false otherwise * * @example * ```typescript * // Check if branch has .claude directory * const hasClaudeDir = await Effect.runPromise( * effect * ); * * // Or use Effect.match for error handling * const result = await Effect.runPromise( * Effect.match(effect, { * onFailure: (error: GitError) => ({ * type: 'error' as const, * message: error.stderr * }), * onSuccess: (hasDir: boolean) => ({ * type: 'success' as const, * data: hasDir * }) * }) * ); * ``` * * @throws {GitError} When git operations fail */ hasClaudeDirectoryInBranchEffect(branchName: string): Effect.Effect; /** * Effect-based copyClaudeDirectoryFromBaseBranch operation * Copies .claude directory from base branch worktree to target worktree * * @param {string} worktreePath - Path of the target worktree * @param {string} baseBranch - Name of the base branch to copy from * @returns {Effect.Effect} Effect that completes successfully or fails with error * * @example * ```typescript * // Copy .claude directory from main branch * await Effect.runPromise( * effect * ); * * // With error handling * const result = await Effect.runPromise( * Effect.catchAll( * effect, * (error) => { * console.warn('Could not copy .claude directory:', error); * return Effect.succeed(undefined); // Continue despite error * } * ) * ); * ``` * * @throws {GitError} When base worktree cannot be found * @throws {FileSystemError} When copying the directory fails */ private copyClaudeDirectoryFromBaseBranchEffect; /** * Effect-based getDefaultBranch operation * Returns Effect that may fail with GitError * * @returns {Effect.Effect} Effect containing default branch name or GitError * * @example * ```typescript * // Use Effect.match for type-safe error handling * const result = await Effect.runPromise( * Effect.match(effect, { * onFailure: (error: GitError) => ({ * type: 'error' as const, * message: `Failed to get default branch: ${error.stderr}` * }), * onSuccess: (branch: string) => ({ * type: 'success' as const, * data: branch * }) * }) * ); * * if (result.type === 'success') { * console.log(`Default branch is: ${result.data}`); * } * ``` * * @throws {GitError} When git symbolic-ref command fails and fallback detection also fails */ getDefaultBranchEffect(): Effect.Effect; /** * Effect-based getAllBranches operation * Returns Effect that succeeds with array of branches (empty on failure for non-critical operation) * * @returns {Effect.Effect} Effect containing array of branch names * * @example * ```typescript * // Execute in async context - this operation returns empty array on failure * const branches = await Effect.runPromise( * effect * ); * console.log(`Found ${branches.length} branches`); * * // Or use Effect.match for explicit error handling * const result = await Effect.runPromise( * Effect.match(effect, { * onFailure: (error: GitError) => ({ * type: 'error' as const, * message: error.stderr * }), * onSuccess: (branches: string[]) => ({ * type: 'success' as const, * data: branches * }) * }) * ); * ``` * * @throws {GitError} When git branch command fails (but falls back to empty array) */ getAllBranchesEffect(): Effect.Effect; /** * Effect-based getBranchesWithRemotes operation * Returns local and remote branches separately so callers can distinguish them. * Remote branches keep their `/` prefix (e.g. `origin/main`). * * @returns {Effect.Effect<{local: string[]; remote: string[]}, GitError, never>} */ getBranchesWithRemotesEffect(): Effect.Effect<{ local: string[]; remote: string[]; }, GitError, never>; /** * Effect-based getCurrentBranch operation * Returns Effect that may fail with GitError * * @returns {Effect.Effect} Effect containing current branch name or GitError * * @example * ```typescript * // Use Effect.match for type-safe error handling * const result = await Effect.runPromise( * Effect.match(effect, { * onFailure: (error: GitError) => ({ * type: 'error' as const, * message: `Failed to get current branch: ${error.stderr}` * }), * onSuccess: (branch: string) => ({ * type: 'success' as const, * data: branch * }) * }) * ); * * if (result.type === 'success') { * console.log(`Current branch: ${result.data}`); * } * ``` * * @throws {GitError} When git rev-parse command fails */ getCurrentBranchEffect(): Effect.Effect; /** * Effect-based getWorktrees operation * Returns Effect that may fail with GitError * * @returns {Effect.Effect} Effect containing array of worktrees or GitError * * @example * ```typescript * // Execute in async context * const worktrees = await Effect.runPromise( * effect * ); * * // Or use Effect.match for type-safe error handling * const result = await Effect.runPromise( * Effect.match(effect, { * onFailure: (error: GitError) => ({ * type: 'error' as const, * message: `Git error: ${error.stderr}` * }), * onSuccess: (worktrees: Worktree[]) => ({ * type: 'success' as const, * data: worktrees * }) * }) * ); * * if (result.type === 'error') { * console.error(result.message); * } else { * console.log(`Found ${result.data.length} worktrees`); * } * ``` * * @throws {GitError} When git worktree list command fails */ getWorktreesEffect(): Effect.Effect; /** * Effect-based createWorktree operation * May fail with GitError or FileSystemError * * @param {string} worktreePath - Path where the new worktree will be created * @param {string} branch - Name of the branch for the new worktree * @param {string} baseBranch - Base branch to create the new branch from * @param {boolean} copySessionData - Whether to copy Claude session data (default: false) * @param {boolean} copyClaudeDirectory - Whether to copy .claude directory (default: false) * @returns {Effect.Effect} Effect containing created worktree or error * * @example * ```typescript * // Create new worktree with Effect.match for error handling * const result = await Effect.runPromise( * Effect.match( * effect, * { * onFailure: (error: GitError | FileSystemError) => { * switch (error._tag) { * case 'GitError': * return {type: 'error' as const, msg: `Git failed: ${error.stderr}`}; * case 'FileSystemError': * return {type: 'error' as const, msg: `FS failed: ${error.cause}`}; * } * }, * onSuccess: (worktree: Worktree) => ({ * type: 'success' as const, * data: worktree * }) * } * ) * ); * * if (result.type === 'error') { * console.error(result.msg); * } else { * console.log(`Created worktree at ${result.data.path}`); * } * ``` * * @throws {GitError} When git worktree add command fails * @throws {FileSystemError} When session data copy fails */ createWorktreeEffect(worktreePath: string, branch: string, baseBranch: string, copySessionData?: boolean, copyClaudeDirectory?: boolean): Effect.Effect; /** * Effect-based deleteWorktree operation * May fail with GitError * * @param {string} worktreePath - Path of the worktree to delete * @param {{deleteBranch?: boolean}} options - Options for deletion (default: deleteBranch = true) * @returns {Effect.Effect} Effect that completes successfully or fails with GitError * * @example * ```typescript * // Delete worktree with Effect.catchTag for specific error handling * await Effect.runPromise( * Effect.catchTag( * effect, * 'GitError', * (error) => { * console.error(`Failed to delete worktree: ${error.stderr}`); * return Effect.succeed(undefined); // Continue despite error * } * ) * ); * ``` * * @throws {GitError} When git worktree remove command fails or worktree not found */ deleteWorktreeEffect(worktreePath: string, options?: { deleteBranch?: boolean; }): Effect.Effect; /** * Effect-based mergeWorktree operation * May fail with GitError * * @param {string} sourceBranch - Branch to merge from * @param {string} targetBranch - Branch to merge into * @param {'merge' | 'rebase'} operation - The merge operation to perform (default: 'merge') * @param {MergeConfig} mergeConfig - Optional configuration for merge/rebase arguments * @returns {Effect.Effect} Effect that completes successfully or fails with GitError * * @throws {GitError} When git merge/rebase command fails or worktrees not found */ mergeWorktreeEffect(sourceBranch: string, targetBranch: string, operation?: 'merge' | 'rebase', mergeConfig?: MergeConfig): Effect.Effect; }