1414logger = logging .getLogger (__name__ )
1515
1616# Default directories to always ignore
17- DEFAULT_IGNORED_DIRS = [".git" , ".cursor" , ".mvn" , ". venv" ]
17+ DEFAULT_IGNORED_DIRS = [".git" , ".venv" ]
1818
1919
2020class ListFilesArgs (ActionArguments ):
@@ -32,6 +32,10 @@ class ListFilesArgs(ActionArguments):
3232 default = 100 ,
3333 description = "Maximum number of results (files and directories) to return." ,
3434 )
35+ show_hidden : bool = Field (
36+ default = False ,
37+ description = "Whether to show hidden files and directories (starting with '.'). Always excludes .git and .venv." ,
38+ )
3539
3640 model_config = ConfigDict (title = "ListFiles" )
3741
@@ -45,6 +49,8 @@ def short_summary(self) -> str:
4549 param_str += f", recursive={ self .recursive } "
4650 if self .max_results != 100 :
4751 param_str += f", max_results={ self .max_results } "
52+ if self .show_hidden :
53+ param_str += f", show_hidden={ self .show_hidden } "
4854 return f"{ self .name } ({ param_str } )"
4955
5056 @classmethod
@@ -123,32 +129,13 @@ async def execute(
123129 dir_path = list_files_args .directory .rstrip ("/" )
124130 target_dir = f"./{ dir_path } " if dir_path else "."
125131
126- # Check if git is available in the workspace
127- git_available = False
128- try :
129- git_check = await local_env .execute ("git rev-parse --is-inside-work-tree 2>/dev/null || echo 'false'" )
130- git_available = git_check .strip () == "true"
131- except Exception :
132- # If the command fails, assume git is not available
133- git_available = False
134-
135- # Legacy variables no longer needed due to clearer builders
136- # (kept minimal changes to surrounding logic)
137-
138- # Build the commands using helpers for clarity
139- if git_available :
140- dirs_command , files_command = build_git_commands (dir_path , target_dir , list_files_args .recursive , self .ignored_dirs )
141- else :
142- dirs_command , files_command = build_fs_commands (dir_path , target_dir , list_files_args .recursive , self .ignored_dirs )
132+ # Build the commands using filesystem only
133+ dirs_command , files_command = build_commands (dir_path , target_dir , list_files_args .recursive , self .ignored_dirs )
143134
144135 try :
145136 # Execute commands to get directories and files
146137 try :
147- # Execute commands to get directories and files
148- if dirs_command is not None :
149- dirs_output = await local_env .execute (dirs_command , patch = patch )
150- else :
151- dirs_output = ""
138+ dirs_output = await local_env .execute (dirs_command , patch = patch )
152139 files_output = await local_env .execute (files_command , patch = patch )
153140
154141 except Exception as e :
@@ -160,8 +147,7 @@ async def execute(
160147 )
161148 raise # Re-raise if it's a different error
162149
163- # Process directory results (helpers for clarity)
164- directories : list [str ] = []
150+ # Check for directory not found
165151 if (dirs_output and "No such file or directory" in dirs_output ) or (
166152 files_output and "No such file or directory" in files_output
167153 ):
@@ -170,25 +156,27 @@ async def execute(
170156 properties = {"fail_reason" : "directory_not_found" },
171157 )
172158
159+ # Process directory results
160+ directories : list [str ] = []
173161 for line in dirs_output .strip ().split ("\n " ):
174162 rel_path = normalize_rel (line .strip ())
175163 if not rel_path or rel_path == dir_path :
176164 continue
177165 if not list_files_args .recursive :
178166 dir_name = rel_path .replace (f"{ dir_path } /" , "" ) if dir_path else rel_path
179- if not dir_name or should_skip_dir (dir_name , self . ignored_dirs ):
167+ if not dir_name or should_skip_dir (dir_name , list_files_args . show_hidden ):
180168 continue
181169 directories .append (dir_name )
182170 else :
183- if should_skip_dir (rel_path , self . ignored_dirs ):
171+ if should_skip_dir (rel_path , list_files_args . show_hidden ):
184172 continue
185173 directories .append (rel_path )
186174
187- # Process file results (helpers for clarity)
175+ # Process file results
188176 files : list [str ] = []
189177 for line in files_output .strip ().split ("\n " ):
190178 rel_path = normalize_rel (line .strip ())
191- if not rel_path or should_skip_file (rel_path , self . ignored_dirs ):
179+ if not rel_path or should_skip_file (rel_path , list_files_args . show_hidden ):
192180 continue
193181
194182 if not list_files_args .recursive :
@@ -217,8 +205,7 @@ async def execute(
217205 "total_files" : len (files ),
218206 "max_results" : list_files_args .max_results ,
219207 "results_limited" : len (directories ) + len (files ) == list_files_args .max_results ,
220- "git_available" : git_available ,
221- "ignored_dirs" : self .ignored_dirs ,
208+ "show_hidden" : list_files_args .show_hidden ,
222209 }
223210
224211 except Exception as e :
@@ -260,10 +247,10 @@ async def execute(
260247 )
261248
262249 recursive_str = " (recursive)" if list_files_args .recursive else ""
263- gitignore_str = " (respecting .gitignore )" if git_available else ""
250+ hidden_str = " (including hidden )" if list_files_args . show_hidden else ""
264251
265252 message = (
266- f"Contents of directory '{ list_files_args .directory or '(root)' } '{ recursive_str } { gitignore_str } \n \n "
253+ f"Contents of directory '{ list_files_args .directory or '(root)' } '{ recursive_str } { hidden_str } \n \n "
267254 )
268255
269256 if result ["directories" ]:
@@ -329,30 +316,34 @@ def is_hidden_segment(segments: list[str]) -> bool:
329316 return any (seg .startswith ("." ) for seg in segments )
330317
331318
332- def should_skip_dir (rel_path : str , ignored_dirs : list [ str ] ) -> bool :
319+ def should_skip_dir (rel_path : str , show_hidden : bool ) -> bool :
333320 if not rel_path :
334321 return True
335- if rel_path in ignored_dirs or any (rel_path .startswith (f"{ d } /" ) for d in ignored_dirs ):
336- return True
337- if rel_path .startswith ("." ):
338- return True
339- parts = rel_path .split ("/" )
340- if is_hidden_segment (parts ):
322+ # Always skip .git and .venv
323+ if rel_path in [".git" , ".venv" ] or any (rel_path .startswith (f"{ d } /" ) for d in [".git" , ".venv" ]):
341324 return True
325+ # Skip hidden files/dirs unless show_hidden is True
326+ if not show_hidden :
327+ if rel_path .startswith ("." ):
328+ return True
329+ parts = rel_path .split ("/" )
330+ if is_hidden_segment (parts ):
331+ return True
342332 return False
343333
344334
345- def should_skip_file (rel_path : str , ignored_dirs : list [ str ] ) -> bool :
335+ def should_skip_file (rel_path : str , show_hidden : bool ) -> bool :
346336 if not rel_path :
347337 return True
348338 parts = rel_path .split ("/" )
349339 base = parts [- 1 ]
350- # In ignored dir
351- if any (f"/{ ignored_dir } /" in f"/{ rel_path } /" for ignored_dir in ignored_dirs ):
352- return True
353- # Hidden dir or hidden file
354- if is_hidden_segment (parts [:- 1 ]) or base .startswith ("." ):
340+ # Skip files in .git and .venv directories
341+ if any (f"/{ ignored_dir } /" in f"/{ rel_path } /" for ignored_dir in [".git" , ".venv" ]):
355342 return True
343+ # Skip hidden files/dirs unless show_hidden is True
344+ if not show_hidden :
345+ if is_hidden_segment (parts [:- 1 ]) or base .startswith ("." ):
346+ return True
356347 return False
357348
358349
@@ -382,60 +373,24 @@ def apply_limits(directories: list[str], files: list[str], max_results: int) ->
382373 }
383374
384375
385- def _ignored_prune_expr (ignored_dirs : list [str ]) -> str :
386- if not ignored_dirs :
387- return ""
388- names = " -o " .join ([f"-name { shlex .quote (d )} " for d in ignored_dirs ])
389- # Wrap in grouping for -prune usage
390- return f"\( { names } \) -prune -o"
391376
392377
393- def build_fs_commands (dir_path : str , target_dir : str , recursive : bool , ignored_dirs : list [str ]) -> tuple [str , str ]:
378+ def build_commands (dir_path : str , target_dir : str , recursive : bool , ignored_dirs : list [str ]) -> tuple [str , str ]:
379+ """Build simple find commands for directories and files."""
394380 td = shlex .quote (target_dir )
395- prune = _ignored_prune_expr (ignored_dirs )
381+
382+ # Always exclude .git and .venv directories using prune
383+ prune_expr = "\\ ( -name .git -o -name .venv \\ ) -prune -o"
384+
396385 if recursive :
397- # All directories (excluding the root itself via -mindepth 1)
398- dirs_cmd = f"find { td } -xdev -mindepth 1 -type d"
399- if prune :
400- dirs_cmd = f"find { td } -xdev { prune } -mindepth 1 -type d -print | sort"
401- else :
402- dirs_cmd = f"{ dirs_cmd } | sort"
403- files_cmd = f"find { td } -xdev -type f | sort"
386+ # All directories and files recursively
387+ dirs_cmd = f"find { td } -xdev { prune_expr } -mindepth 1 -type d -print | sort"
388+ files_cmd = f"find { td } -xdev { prune_expr } -type f -print | sort"
404389 else :
405390 # Immediate children only
406- dirs_cmd = f"find { td } -xdev -maxdepth 1 -mindepth 1 -type d"
407- if prune :
408- dirs_cmd = f"find { td } -xdev -maxdepth 1 -mindepth 1 { prune } -type d -print | sort"
409- else :
410- dirs_cmd = f"{ dirs_cmd } | sort"
411- files_cmd = f"find { td } -xdev -maxdepth 1 -type f | sort"
412- return dirs_cmd , files_cmd
413-
414-
415- def build_git_commands (dir_path : str , target_dir : str , recursive : bool , ignored_dirs : list [str ]) -> tuple [str , str ]:
416- # Files via git to respect .gitignore
417- if dir_path :
418- files_cmd = f"git ls-files --cached --others --exclude-standard -- { shlex .quote (dir_path )} /** | sort"
419- else :
420- files_cmd = "git ls-files --cached --others --exclude-standard | sort"
421-
422- # Directories using find, then filter through git check-ignore
423- td = shlex .quote (target_dir )
424- prune = _ignored_prune_expr (ignored_dirs )
425- if recursive :
426- base_find = f"find { td } -xdev -mindepth 1 -type d"
427- if prune :
428- base_find = f"find { td } -xdev { prune } -mindepth 1 -type d -print"
429- else :
430- base_find = f"find { td } -xdev -maxdepth 1 -mindepth 1 -type d"
431- if prune :
432- base_find = f"find { td } -xdev -maxdepth 1 -mindepth 1 { prune } -type d -print"
433-
434- # Keep the existing behavior: check each dir with git check-ignore
435- dirs_cmd = (
436- f"{ base_find } | while read -r dir; do git check-ignore \" $dir/\" >/dev/null 2>&1 || echo \" $dir\" ; done | sort"
437- )
438-
391+ dirs_cmd = f"find { td } -xdev -maxdepth 1 { prune_expr } -mindepth 1 -type d -print | sort"
392+ files_cmd = f"find { td } -xdev -maxdepth 1 { prune_expr } -type f -print | sort"
393+
439394 return dirs_cmd , files_cmd
440395
441396
0 commit comments