11"""File operations for manipulating the codebase."""
22
3- import os
43from typing import Any , Literal
54
65from codegen import Codebase
@@ -36,17 +35,31 @@ def view_file(codebase: Codebase, filepath: str) -> dict[str, Any]:
3635def list_directory (codebase : Codebase , dirpath : str = "./" , depth : int = 1 ) -> dict [str , Any ]:
3736 """List contents of a directory.
3837
39- TODO(CG-10729): add support for directories that only including non-SourceFiles (code files). At the moment,
40- only files and directories that have SourceFile objects are included.
41-
4238 Args:
4339 codebase: The codebase to operate on
4440 dirpath: Path to directory relative to workspace root
4541 depth: How deep to traverse the directory tree. Default is 1 (immediate children only).
4642 Use -1 for unlimited depth.
4743
44+ TODO(CG-10729): add support for directories that only including non-SourceFiles (code files). At the moment,
45+ only files and directories that have SourceFile objects are included.
46+
4847 Returns:
49- Dict containing directory contents and metadata, or error information if directory not found
48+ Dict containing directory contents and metadata in a nested structure:
49+ {
50+ "path": str,
51+ "name": str,
52+ "files": list[str],
53+ "subdirectories": [
54+ {
55+ "path": str,
56+ "name": str,
57+ "files": list[str],
58+ "subdirectories": [...],
59+ },
60+ ...
61+ ]
62+ }
5063 """
5164 try :
5265 directory = codebase .get_directory (dirpath )
@@ -56,33 +69,33 @@ def list_directory(codebase: Codebase, dirpath: str = "./", depth: int = 1) -> d
5669 if not directory :
5770 return {"error" : f"Directory not found: { dirpath } " }
5871
59- # Get immediate files
60- files = []
61- subdirs = []
62-
63- for item in directory .items .values ():
64- if isinstance (item , Directory ):
65- subdirs .append (item .name )
66- else :
67- # Get full filename with extension from filepath
68- files .append (os .path .basename (item .filepath ))
69-
70- # If depth > 1 or unlimited (-1), recursively get subdirectories
71- if depth != 1 :
72- new_depth = depth - 1 if depth > 1 else - 1
73- for item in directory .items .values ():
74- if isinstance (item , Directory ):
75- subdir_result = list_directory (codebase , os .path .join (dirpath , item .name ), depth = new_depth )
76- if "error" not in subdir_result :
77- files .extend (subdir_result ["files" ])
78- subdirs .extend (subdir_result ["subdirectories" ])
72+ def get_directory_info (dir_obj : Directory , current_depth : int ) -> dict [str , Any ]:
73+ """Helper function to get directory info recursively."""
74+ # Get direct files
75+ all_files = []
76+ for file in dir_obj .files :
77+ if file .directory == dir_obj :
78+ all_files .append (file .filepath .split ("/" )[- 1 ])
79+
80+ # Get direct subdirectories
81+ subdirs = []
82+ for subdir in dir_obj .subdirectories :
83+ # Only include direct descendants
84+ if subdir .parent == dir_obj :
85+ if current_depth != 1 :
86+ new_depth = current_depth - 1 if current_depth > 1 else - 1
87+ subdirs .append (get_directory_info (subdir , new_depth ))
88+ else :
89+ # At max depth, just include name
90+ subdirs .append (subdir .name )
91+ return {
92+ "name" : dir_obj .name ,
93+ "path" : dir_obj .dirpath ,
94+ "files" : all_files ,
95+ "subdirectories" : subdirs ,
96+ }
7997
80- return {
81- "path" : str (directory .path ), # Convert PosixPath to string
82- "name" : directory .name ,
83- "files" : files ,
84- "subdirectories" : subdirs ,
85- }
98+ return get_directory_info (directory , depth )
8699
87100
88101def edit_file (codebase : Codebase , filepath : str , content : str ) -> dict [str , Any ]:
0 commit comments