@@ -245,6 +245,85 @@ class FileSystemTreeViewSet(viewsets.ModelViewSet): |
| 245 | 245 | response_data['output'] = '' |
| 246 | 246 | response_data['success'] = True |
| 247 | 247 | |
| 248 | + elif cmd == 'tree': |
| 249 | + # Parse options |
| 250 | + show_all = '-a' in parts |
| 251 | + max_depth = 3 # Default depth |
| 252 | + |
| 253 | + # Check for -L option |
| 254 | + if '-L' in parts: |
| 255 | + try: |
| 256 | + depth_index = parts.index('-L') + 1 |
| 257 | + if depth_index < len(parts): |
| 258 | + max_depth = int(parts[depth_index]) |
| 259 | + max_depth = min(max(max_depth, 1), 5) # Limit between 1-5 |
| 260 | + except (ValueError, IndexError): |
| 261 | + pass |
| 262 | + |
| 263 | + try: |
| 264 | + current_dir = DirectoryNode.objects.get( |
| 265 | + tree=tree, |
| 266 | + path=tree.player_location |
| 267 | + ) |
| 268 | + |
| 269 | + # Build tree output |
| 270 | + output_lines = [tree.player_location] |
| 271 | + |
| 272 | + def build_tree_output(node, prefix="", is_last=True, depth=0): |
| 273 | + if depth >= max_depth: |
| 274 | + return |
| 275 | + |
| 276 | + children = list(node.get_contents().order_by('name')) |
| 277 | + |
| 278 | + for i, child in enumerate(children): |
| 279 | + is_last_child = i == len(children) - 1 |
| 280 | + |
| 281 | + # Determine if this directory contains the mole |
| 282 | + has_mole_in_subtree = False |
| 283 | + if child.path == tree.mole_location: |
| 284 | + has_mole_in_subtree = True |
| 285 | + else: |
| 286 | + # Check if mole is in any subdirectory |
| 287 | + all_descendants = DirectoryNode.objects.filter( |
| 288 | + tree=tree, |
| 289 | + path__startswith=child.path + '/' |
| 290 | + ) |
| 291 | + if any(d.path == tree.mole_location for d in all_descendants): |
| 292 | + has_mole_in_subtree = True |
| 293 | + |
| 294 | + # Build the tree branch characters |
| 295 | + connector = "└── " if is_last_child else "├── " |
| 296 | + |
| 297 | + # Format the directory name |
| 298 | + if has_mole_in_subtree: |
| 299 | + # Use X to indicate mole presence |
| 300 | + dir_display = f"[X] {child.name}" |
| 301 | + else: |
| 302 | + dir_display = child.name |
| 303 | + |
| 304 | + output_lines.append(f"{prefix}{connector}{dir_display}") |
| 305 | + |
| 306 | + # Recurse into subdirectories |
| 307 | + if depth + 1 < max_depth: |
| 308 | + extension = " " if is_last_child else "│ " |
| 309 | + build_tree_output(child, prefix + extension, is_last_child, depth + 1) |
| 310 | + |
| 311 | + build_tree_output(current_dir) |
| 312 | + |
| 313 | + # Add directory count at the end |
| 314 | + total_shown = len(output_lines) - 1 |
| 315 | + if total_shown == 0: |
| 316 | + output_lines.append("\n0 directories") |
| 317 | + else: |
| 318 | + dir_text = "directory" if total_shown == 1 else "directories" |
| 319 | + output_lines.append(f"\n{total_shown} {dir_text}") |
| 320 | + |
| 321 | + response_data['output'] = '\n'.join(output_lines) |
| 322 | + response_data['success'] = True |
| 323 | + |
| 324 | + except DirectoryNode.DoesNotExist: |
| 325 | + response_data['output'] = "tree: cannot access directory" |
| 326 | + |
| 248 | 327 | elif cmd == 'killall' and len(parts) > 1 and parts[1] == 'moles': |
| 249 | 328 | if tree.check_win_condition(): |
| 250 | 329 | tree.is_completed = True |
@@ -273,6 +352,7 @@ dirs - Display directory stack |
| 273 | 352 | ls [-la] - List directory contents |
| 274 | 353 | pwd - Print working directory |
| 275 | 354 | echo <text> - Display text (supports $HOME, $PWD, $OLDPWD) |
| 355 | +tree [-L depth] - Display directory tree (use -L to limit depth) |
| 276 | 356 | killall moles - Eliminate moles (when in the same directory) |
| 277 | 357 | help - Show this help message |
| 278 | 358 | |