/** * Worktrees Query Hooks * * React Query hooks for fetching worktree data. */ import { useQuery } from '@tanstack/react-query'; import { getElectronAPI } from '@/lib/electron'; import { queryKeys } from '@/lib/query-keys'; import { STALE_TIMES } from '@/lib/query-client'; interface WorktreeInfo { path: string; branch: string; isMain: boolean; hasChanges?: boolean; changedFilesCount?: number; featureId?: string; linkedToBranch?: string; } interface RemovedWorktree { path: string; branch: string; } interface WorktreesResult { worktrees: WorktreeInfo[]; removedWorktrees: RemovedWorktree[]; } /** * Fetch all worktrees for a project * * @param projectPath - Path to the project * @param includeDetails - Whether to include detailed info (default: true) * @returns Query result with worktrees array and removed worktrees * * @example * ```tsx * const { data, isLoading, refetch } = useWorktrees(currentProject?.path); * const worktrees = data?.worktrees ?? []; * ``` */ export function useWorktrees(projectPath: string | undefined, includeDetails = true) { return useQuery({ queryKey: queryKeys.worktrees.all(projectPath ?? ''), queryFn: async (): Promise => { if (!projectPath) throw new Error('No project path'); const api = getElectronAPI(); const result = await api.worktree.listAll(projectPath, includeDetails); if (!result.success) { throw new Error(result.error || 'Failed to fetch worktrees'); } return { worktrees: result.worktrees ?? [], removedWorktrees: result.removedWorktrees ?? [], }; }, enabled: !!projectPath, staleTime: STALE_TIMES.WORKTREES, }); } /** * Fetch worktree info for a specific feature * * @param projectPath - Path to the project * @param featureId - ID of the feature * @returns Query result with worktree info */ export function useWorktreeInfo(projectPath: string | undefined, featureId: string | undefined) { return useQuery({ queryKey: queryKeys.worktrees.single(projectPath ?? '', featureId ?? ''), queryFn: async () => { if (!projectPath || !featureId) throw new Error('Missing project path or feature ID'); const api = getElectronAPI(); const result = await api.worktree.getInfo(projectPath, featureId); if (!result.success) { throw new Error(result.error || 'Failed to fetch worktree info'); } return result; }, enabled: !!projectPath && !!featureId, staleTime: STALE_TIMES.WORKTREES, }); } /** * Fetch worktree status for a specific feature * * @param projectPath - Path to the project * @param featureId - ID of the feature * @returns Query result with worktree status */ export function useWorktreeStatus(projectPath: string | undefined, featureId: string | undefined) { return useQuery({ queryKey: queryKeys.worktrees.status(projectPath ?? '', featureId ?? ''), queryFn: async () => { if (!projectPath || !featureId) throw new Error('Missing project path or feature ID'); const api = getElectronAPI(); const result = await api.worktree.getStatus(projectPath, featureId); if (!result.success) { throw new Error(result.error || 'Failed to fetch worktree status'); } return result; }, enabled: !!projectPath && !!featureId, staleTime: STALE_TIMES.WORKTREES, }); } /** * Fetch worktree diffs for a specific feature * * @param projectPath - Path to the project * @param featureId - ID of the feature * @returns Query result with files and diff content */ export function useWorktreeDiffs(projectPath: string | undefined, featureId: string | undefined) { return useQuery({ queryKey: queryKeys.worktrees.diffs(projectPath ?? '', featureId ?? ''), queryFn: async () => { if (!projectPath || !featureId) throw new Error('Missing project path or feature ID'); const api = getElectronAPI(); const result = await api.worktree.getDiffs(projectPath, featureId); if (!result.success) { throw new Error(result.error || 'Failed to fetch diffs'); } return { files: result.files ?? [], diff: result.diff ?? '', }; }, enabled: !!projectPath && !!featureId, staleTime: STALE_TIMES.WORKTREES, }); } interface BranchInfo { name: string; isCurrent: boolean; isRemote?: boolean; lastCommit?: string; upstream?: string; } interface BranchesResult { branches: BranchInfo[]; aheadCount: number; behindCount: number; isGitRepo: boolean; hasCommits: boolean; } /** * Fetch available branches for a worktree * * @param worktreePath - Path to the worktree * @param includeRemote - Whether to include remote branches * @returns Query result with branches, ahead/behind counts, and git repo status */ export function useWorktreeBranches(worktreePath: string | undefined, includeRemote = false) { return useQuery({ queryKey: queryKeys.worktrees.branches(worktreePath ?? ''), queryFn: async (): Promise => { if (!worktreePath) throw new Error('No worktree path'); const api = getElectronAPI(); const result = await api.worktree.listBranches(worktreePath, includeRemote); // Handle special git status codes if (result.code === 'NOT_GIT_REPO') { return { branches: [], aheadCount: 0, behindCount: 0, isGitRepo: false, hasCommits: false, }; } if (result.code === 'NO_COMMITS') { return { branches: [], aheadCount: 0, behindCount: 0, isGitRepo: true, hasCommits: false, }; } if (!result.success) { throw new Error(result.error || 'Failed to fetch branches'); } return { branches: result.result?.branches ?? [], aheadCount: result.result?.aheadCount ?? 0, behindCount: result.result?.behindCount ?? 0, isGitRepo: true, hasCommits: true, }; }, enabled: !!worktreePath, staleTime: STALE_TIMES.WORKTREES, }); } /** * Fetch init script for a project * * @param projectPath - Path to the project * @returns Query result with init script content */ export function useWorktreeInitScript(projectPath: string | undefined) { return useQuery({ queryKey: queryKeys.worktrees.initScript(projectPath ?? ''), queryFn: async () => { if (!projectPath) throw new Error('No project path'); const api = getElectronAPI(); const result = await api.worktree.getInitScript(projectPath); if (!result.success) { throw new Error(result.error || 'Failed to fetch init script'); } return { exists: result.exists ?? false, content: result.content ?? '', }; }, enabled: !!projectPath, staleTime: STALE_TIMES.SETTINGS, }); } /** * Fetch available editors * * @returns Query result with available editors */ export function useAvailableEditors() { return useQuery({ queryKey: queryKeys.worktrees.editors(), queryFn: async () => { const api = getElectronAPI(); const result = await api.worktree.getAvailableEditors(); if (!result.success) { throw new Error(result.error || 'Failed to fetch editors'); } return result.editors ?? []; }, staleTime: STALE_TIMES.CLI_STATUS, }); }