List contents of a workspace-relative directory.
(
path: Annotated[str, ParamMeta(description="Workspace-relative directory path")]=".",
*,
recursive: Annotated[bool, ParamMeta(description="Traverse subdirectories")] = False,
max_depth: Annotated[int, ParamMeta(description="Maximum depth when recursive=True")] = 3,
include_hidden: Annotated[bool, ParamMeta(description="Include entries starting with '.'")] = False,
limit: Annotated[int, ParamMeta(description="Maximum entries to return")] = 500,
_context: Dict[str, Any] | None = None,
)
| 167 | |
| 168 | |
| 169 | def list_directory( |
| 170 | path: Annotated[str, ParamMeta(description="Workspace-relative directory path")]=".", |
| 171 | *, |
| 172 | recursive: Annotated[bool, ParamMeta(description="Traverse subdirectories")] = False, |
| 173 | max_depth: Annotated[int, ParamMeta(description="Maximum depth when recursive=True")] = 3, |
| 174 | include_hidden: Annotated[bool, ParamMeta(description="Include entries starting with '.'")] = False, |
| 175 | limit: Annotated[int, ParamMeta(description="Maximum entries to return")] = 500, |
| 176 | _context: Dict[str, Any] | None = None, |
| 177 | ) -> Dict[str, Any]: |
| 178 | """List contents of a workspace-relative directory.""" |
| 179 | |
| 180 | ctx = FileToolContext(_context) |
| 181 | target = ctx.resolve_under_workspace(path) |
| 182 | if not target.exists(): |
| 183 | raise FileNotFoundError(f"Directory not found: {path}") |
| 184 | if not target.is_dir(): |
| 185 | raise NotADirectoryError(f"Path is not a directory: {path}") |
| 186 | |
| 187 | if limit <= 0: |
| 188 | raise ValueError("limit must be positive") |
| 189 | if recursive and max_depth < 1: |
| 190 | raise ValueError("max_depth must be >= 1 when recursive") |
| 191 | |
| 192 | entries: List[Dict[str, Any]] = [] |
| 193 | stack: List[tuple[Path, int]] = [(target, 0)] |
| 194 | base_relative = ctx.to_workspace_relative(target) or "." |
| 195 | |
| 196 | while stack and len(entries) < limit: |
| 197 | current, depth = stack.pop() |
| 198 | try: |
| 199 | children = sorted(current.iterdir(), key=lambda p: p.name.lower()) |
| 200 | except (FileNotFoundError, PermissionError): |
| 201 | continue |
| 202 | for child in children: |
| 203 | rel = child.relative_to(target) |
| 204 | if not include_hidden and _path_is_hidden(rel): |
| 205 | continue |
| 206 | stat_size = None |
| 207 | modified = None |
| 208 | try: |
| 209 | stat = child.stat() |
| 210 | modified = stat.st_mtime |
| 211 | if child.is_file(): |
| 212 | stat_size = stat.st_size |
| 213 | except (FileNotFoundError, PermissionError, OSError): |
| 214 | pass |
| 215 | entry = { |
| 216 | "name": child.name, |
| 217 | "relative_path": rel.as_posix(), |
| 218 | "absolute_path": str(child), |
| 219 | "type": "directory" if child.is_dir() else "file", |
| 220 | "size": stat_size, |
| 221 | "modified_ts": modified, |
| 222 | "depth": depth, |
| 223 | } |
| 224 | entries.append(entry) |
| 225 | if len(entries) >= limit: |
| 226 | break |
nothing calls this directly
no test coverage detected