11import { executeCommand } from '../utils/process'
2- import { ensureDirectoryExists } from './file-operations'
2+ import { ensureDirectoryExists , directoryExists , removeDirectory , listDirectoryNames } from './file-operations'
33import * as db from '../db/queries'
44import type { Database } from 'bun:sqlite'
55import type { Repo , CreateRepoInput } from '../types/repo'
@@ -88,12 +88,7 @@ async function isValidGitRepo(repoPath: string): Promise<boolean> {
8888async function checkRepoNameAvailable ( name : string ) : Promise < boolean > {
8989 const reposPath = getReposPath ( )
9090 const targetPath = path . join ( reposPath , name )
91- try {
92- await executeCommand ( [ 'test' , '-e' , targetPath ] , { silent : true } )
93- return false
94- } catch {
95- return true
96- }
91+ return ! ( await directoryExists ( targetPath ) )
9792}
9893
9994async function copyRepoToWorkspace ( sourcePath : string , targetName : string ) : Promise < void > {
@@ -186,9 +181,7 @@ export async function initLocalRepo(
186181 logger . info ( `Absolute path detected: ${ normalizedInputPath } ` )
187182
188183 try {
189- const exists = await executeCommand ( [ 'test' , '-d' , normalizedInputPath ] , { silent : true } )
190- . then ( ( ) => true )
191- . catch ( ( ) => false )
184+ const exists = await directoryExists ( normalizedInputPath )
192185
193186 if ( ! exists ) {
194187 throw new Error ( `No such file or directory: '${ normalizedInputPath } '` )
@@ -296,14 +289,14 @@ export async function initLocalRepo(
296289
297290 if ( directoryCreated && ! sourceWasGitRepo ) {
298291 try {
299- await executeCommand ( [ 'rm' , '-rf' , repoLocalPath ] , getReposPath ( ) )
292+ await removeDirectory ( path . join ( getReposPath ( ) , repoLocalPath ) )
300293 logger . info ( `Rolled back directory: ${ repoLocalPath } ` )
301294 } catch ( fsError : any ) {
302295 logger . error ( `Failed to rollback directory ${ repoLocalPath } :` , fsError )
303296 }
304297 } else if ( sourceWasGitRepo ) {
305298 try {
306- await executeCommand ( [ 'rm' , '-rf' , repoLocalPath ] , getReposPath ( ) )
299+ await removeDirectory ( path . join ( getReposPath ( ) , repoLocalPath ) )
307300 logger . info ( `Cleaned up copied directory: ${ repoLocalPath } ` )
308301 } catch ( fsError : any ) {
309302 logger . error ( `Failed to clean up copied directory ${ repoLocalPath } :` , fsError )
@@ -333,9 +326,9 @@ export async function cloneRepo(
333326 }
334327
335328 await ensureDirectoryExists ( getReposPath ( ) )
336- const baseRepoExists = await executeCommand ( [ 'bash' , '-c' , `test -d ${ baseRepoDirName } && echo exists || echo missing` ] , path . resolve ( getReposPath ( ) ) )
329+ const baseRepoExists = await directoryExists ( path . join ( getReposPath ( ) , baseRepoDirName ) )
337330
338- const shouldUseWorktree = useWorktree && branch && baseRepoExists . trim ( ) === 'exists'
331+ const shouldUseWorktree = useWorktree && branch && baseRepoExists
339332
340333 const createRepoInput : CreateRepoInput = {
341334 repoUrl : normalizedRepoUrl ,
@@ -366,26 +359,24 @@ export async function cloneRepo(
366359
367360 await createWorktreeSafely ( baseRepoPath , worktreePath , branch )
368361
369- const worktreeVerified = await executeCommand ( [ 'test' , '-d' , worktreePath ] )
370- . then ( ( ) => true )
371- . catch ( ( ) => false )
362+ const worktreeVerified = await directoryExists ( worktreePath )
372363
373364 if ( ! worktreeVerified ) {
374365 throw new Error ( `Worktree directory was not created at: ${ worktreePath } ` )
375366 }
376367
377368 logger . info ( `Worktree verified at: ${ worktreePath } ` )
378369
379- } else if ( branch && baseRepoExists . trim ( ) === 'exists' && useWorktree ) {
370+ } else if ( branch && baseRepoExists && useWorktree ) {
380371 logger . info ( `Base repo exists but worktree creation failed, cloning branch separately` )
381372
382- const worktreeExists = await executeCommand ( [ 'bash' , '-c' , `test -d ${ worktreeDirName } && echo exists || echo missing` ] , path . resolve ( getReposPath ( ) ) )
383- if ( worktreeExists . trim ( ) === 'exists' ) {
373+ const worktreeExists = await directoryExists ( path . join ( getReposPath ( ) , worktreeDirName ) )
374+ if ( worktreeExists ) {
384375 logger . info ( `Workspace directory exists, removing it: ${ worktreeDirName } ` )
385376 try {
386- await executeCommand ( [ 'rm' , '-rf' , worktreeDirName ] , getReposPath ( ) )
387- const verifyRemoved = await executeCommand ( [ 'bash' , '-c' , `test -d ${ worktreeDirName } && echo exists || echo removed` ] , getReposPath ( ) )
388- if ( verifyRemoved . trim ( ) === 'exists' ) {
377+ await removeDirectory ( path . join ( getReposPath ( ) , worktreeDirName ) )
378+ const verifyRemoved = ! ( await directoryExists ( path . join ( getReposPath ( ) , worktreeDirName ) ) )
379+ if ( ! verifyRemoved ) {
389380 throw new Error ( `Failed to remove existing directory: ${ worktreeDirName } ` )
390381 }
391382 } catch ( cleanupError : any ) {
@@ -402,27 +393,32 @@ export async function cloneRepo(
402393 throw new Error ( `Workspace directory ${ worktreeDirName } already exists. Please delete it manually or contact support.` )
403394 }
404395
405- logger . info ( `Branch '${ branch } ' not found during clone, cloning default branch and creating branch locally` )
406- await executeGitWithFallback ( [ 'git' , 'clone' , normalizedRepoUrl , worktreeDirName ] , { cwd : getReposPath ( ) , env } )
407- let localBranchExists = 'missing'
408- try {
409- await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , worktreeDirName ) , 'rev-parse' , '--verify' , `refs/heads/${ branch } ` ] )
410- localBranchExists = 'exists'
411- } catch {
412- localBranchExists = 'missing'
413- }
414- if ( localBranchExists . trim ( ) === 'missing' ) {
396+ if ( branch && ( error . message . includes ( 'Remote branch' ) || error . message . includes ( 'not found' ) ) ) {
397+ logger . info ( `Branch '${ branch } ' not found, cloning default branch and creating branch locally` )
398+ await executeGitWithFallback ( [ 'git' , 'clone' , normalizedRepoUrl , worktreeDirName ] , { cwd : getReposPath ( ) , env } )
399+ let localBranchExists = 'missing'
400+ try {
401+ await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , worktreeDirName ) , 'rev-parse' , '--verify' , `refs/heads/${ branch } ` ] )
402+ localBranchExists = 'exists'
403+ } catch {
404+ localBranchExists = 'missing'
405+ }
406+
407+ if ( localBranchExists === 'missing' ) {
415408 await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , worktreeDirName ) , 'checkout' , '-b' , branch ] )
416409 } else {
417410 await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , worktreeDirName ) , 'checkout' , branch ] )
418411 }
412+ } else {
413+ throw error
414+ }
419415 }
420416 } else {
421- if ( baseRepoExists . trim ( ) === 'exists' ) {
417+ if ( baseRepoExists ) {
422418 logger . info ( `Repository directory already exists, verifying it's a valid git repo: ${ baseRepoDirName } ` )
423419 const isValidRepo = await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , baseRepoDirName ) , 'rev-parse' , '--git-dir' ] , path . resolve ( getReposPath ( ) ) ) . then ( ( ) => 'valid' ) . catch ( ( ) => 'invalid' )
424420
425- if ( isValidRepo . trim ( ) === 'valid' ) {
421+ if ( isValidRepo === 'valid' ) {
426422 logger . info ( `Valid repository found: ${ normalizedRepoUrl } ` )
427423
428424 if ( branch ) {
@@ -462,19 +458,19 @@ export async function cloneRepo(
462458 return { ...repo , cloneStatus : 'ready' }
463459 } else {
464460 logger . warn ( `Invalid repository directory found, removing and recloning: ${ baseRepoDirName } ` )
465- await executeCommand ( [ 'rm' , '-rf' , baseRepoDirName ] , getReposPath ( ) )
461+ await removeDirectory ( path . join ( getReposPath ( ) , baseRepoDirName ) )
466462 }
467463 }
468464
469465 logger . info ( `Cloning repo: ${ normalizedRepoUrl } ${ branch ? ` to branch ${ branch } ` : '' } ` )
470466
471- const worktreeExists = await executeCommand ( [ 'bash' , '-c' , `test -d ${ worktreeDirName } && echo exists || echo missing` ] , getReposPath ( ) )
472- if ( worktreeExists . trim ( ) === 'exists' ) {
467+ const worktreeExists = await directoryExists ( path . join ( getReposPath ( ) , worktreeDirName ) )
468+ if ( worktreeExists ) {
473469 logger . info ( `Workspace directory exists, removing it: ${ worktreeDirName } ` )
474470 try {
475- await executeCommand ( [ 'rm' , '-rf' , worktreeDirName ] , getReposPath ( ) )
476- const verifyRemoved = await executeCommand ( [ 'bash' , '-c' , `test -d ${ worktreeDirName } && echo exists || echo removed` ] , getReposPath ( ) )
477- if ( verifyRemoved . trim ( ) === 'exists' ) {
471+ await removeDirectory ( path . join ( getReposPath ( ) , worktreeDirName ) )
472+ const verifyRemoved = ! ( await directoryExists ( path . join ( getReposPath ( ) , worktreeDirName ) ) )
473+ if ( ! verifyRemoved ) {
478474 throw new Error ( `Failed to remove existing directory: ${ worktreeDirName } ` )
479475 }
480476 } catch ( cleanupError : any ) {
@@ -506,7 +502,7 @@ export async function cloneRepo(
506502 localBranchExists = 'missing'
507503 }
508504
509- if ( localBranchExists . trim ( ) === 'missing' ) {
505+ if ( localBranchExists === 'missing' ) {
510506 await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , worktreeDirName ) , 'checkout' , '-b' , branch ] )
511507 } else {
512508 await executeCommand ( [ 'git' , '-C' , path . resolve ( getReposPath ( ) , worktreeDirName ) , 'checkout' , branch ] )
@@ -704,10 +700,10 @@ export async function deleteRepoFiles(database: Database, repoId: number): Promi
704700
705701 // Remove the directory
706702 logger . info ( `Removing directory: ${ dirName } from ${ getReposPath ( ) } ` )
707- await executeCommand ( [ 'rm' , '-rf' , dirName ] , getReposPath ( ) )
703+ await removeDirectory ( path . join ( getReposPath ( ) , dirName ) )
708704
709- const checkExists = await executeCommand ( [ 'bash' , '-c' , `test -d ${ dirName } && echo exists || echo deleted` ] , getReposPath ( ) )
710- if ( checkExists . trim ( ) === 'exists' ) {
705+ const checkExists = await directoryExists ( path . join ( getReposPath ( ) , dirName ) )
706+ if ( checkExists ) {
711707 logger . error ( `Directory still exists after deletion: ${ dirName } ` )
712708 throw new Error ( `Failed to delete workspace directory: ${ dirName } ` )
713709 }
@@ -764,8 +760,7 @@ export async function cleanupOrphanedDirectories(database: Database): Promise<vo
764760 const reposPath = getReposPath ( )
765761 await ensureDirectoryExists ( reposPath )
766762
767- const dirResult = await executeCommand ( [ 'ls' , '-1' ] , reposPath ) . catch ( ( ) => '' )
768- const directories = dirResult . split ( '\n' ) . filter ( d => d . trim ( ) )
763+ const directories = await listDirectoryNames ( reposPath )
769764
770765 if ( directories . length === 0 ) {
771766 return
@@ -782,7 +777,7 @@ export async function cleanupOrphanedDirectories(database: Database): Promise<vo
782777 for ( const dir of orphanedDirs ) {
783778 try {
784779 logger . info ( `Removing orphaned directory: ${ dir } ` )
785- await executeCommand ( [ 'rm' , '-rf' , dir ] , reposPath )
780+ await removeDirectory ( path . join ( reposPath , dir ) )
786781 } catch ( error ) {
787782 logger . warn ( `Failed to remove orphaned directory ${ dir } :` , error )
788783 }
0 commit comments