@@ -47,13 +47,16 @@ export async function listFiles(dirPath: string, recursive: boolean, limit: numb
4747 const rgPath = await getRipgrepPath ( )
4848
4949 if ( ! recursive ) {
50- // For non-recursive, use the existing approach
50+ // For non-recursive, include top-level files plus top-level symlinked files
5151 const files = await listFilesWithRipgrep ( rgPath , dirPath , false , limit )
52+ const symlinkFiles = await listTopLevelSymlinkFiles ( dirPath )
53+ const mergedFiles = [ ...files , ...symlinkFiles ]
54+
5255 const ignoreInstance = await createIgnoreInstance ( dirPath )
53- // Calculate remaining limit for directories
54- const remainingLimit = Math . max ( 0 , limit - files . length )
56+ // Calculate remaining limit for directories after accounting for files
57+ const remainingLimit = Math . max ( 0 , limit - mergedFiles . length )
5558 const directories = await listFilteredDirectories ( dirPath , false , ignoreInstance , remainingLimit )
56- return formatAndCombineResults ( files , directories , limit )
59+ return formatAndCombineResults ( mergedFiles , directories , limit )
5760 }
5861
5962 // For recursive mode, use the original approach but ensure first-level directories are included
@@ -212,15 +215,47 @@ async function listFilesWithRipgrep(
212215 const absolutePath = path . resolve ( dirPath )
213216 return relativePaths . map ( ( relativePath ) => path . resolve ( absolutePath , relativePath ) )
214217}
218+ /**
219+ * List top-level symlinked files in a directory (non-recursive).
220+ * We include the symlink path itself if it points to a file, or if it's a broken link.
221+ */
222+ async function listTopLevelSymlinkFiles ( dirPath : string ) : Promise < string [ ] > {
223+ const absolutePath = path . resolve ( dirPath )
224+ try {
225+ const entries = await fs . promises . readdir ( absolutePath , { withFileTypes : true } )
226+ const results : string [ ] = [ ]
227+ for ( const entry of entries ) {
228+ if ( entry . isSymbolicLink ( ) ) {
229+ const symlinkPath = path . join ( absolutePath , entry . name )
230+ try {
231+ // stat follows the symlink
232+ const targetStat = await fs . promises . stat ( symlinkPath )
233+ if ( targetStat . isFile ( ) ) {
234+ results . push ( symlinkPath )
235+ }
236+ } catch {
237+ // Broken symlink - still surface the symlink path so it is visible to the user
238+ results . push ( symlinkPath )
239+ }
240+ }
241+ }
242+ return results
243+ } catch {
244+ return [ ]
245+ }
246+ }
215247
216248/**
217249 * Build appropriate ripgrep arguments based on whether we're doing a recursive search
218250 */
219251function buildRipgrepArgs ( dirPath : string , recursive : boolean ) : string [ ] {
220252 // Base arguments to list files
221- const args = [ "--files" , "--hidden" , "--follow" ]
253+ // Note: do NOT follow symlinks in non-recursive mode so that symlinked files themselves are listed.
254+ // In recursive mode we follow symlinks to traverse into linked directories when appropriate.
255+ const args = [ "--files" , "--hidden" ]
222256
223257 if ( recursive ) {
258+ args . push ( "--follow" )
224259 return [ ...args , ...buildRecursiveArgs ( dirPath ) , dirPath ]
225260 } else {
226261 return [ ...args , ...buildNonRecursiveArgs ( ) , dirPath ]
0 commit comments