Skip to content

feat: tool for removing directories #251

@LeeCheneler

Description

@LeeCheneler

User Story

As a user,
I want a tool for removing directories,
so that the LLM can manage the filesystem directly from within Tomo without escaping into run_command rm -rf.

Acceptance Criteria

Tool shape

  • New remove_dir tool registered alongside existing tools at src/tools/remove-dir.ts, follows existing tool patterns (zod schema, Tool interface, colocated test file, registered in src/app.tsx)
  • Arguments: path (required), recursive (optional boolean, default false)
  • Tool description explains: removes a directory, non-recursive by default (fails on non-empty), recursive: true removes the entire tree, recursive removes always prompt regardless of permission state
  • formatCall returns path for non-recursive removes and path (recursive) for recursive removes so the collapsed tool header shows the mode
  • New entry in toolsSchema in src/config/schema.ts so the tool can be enabled/disabled (default enabled: true)

Permissions

  • New permission keys added to permissionsSchema in src/config/schema.ts:
    • cwdRemoveDir (default false) — allows removing empty directories within the cwd without confirmation
    • globalRemoveDir (default false) — allows removing empty directories outside the cwd without confirmation
  • Permission check uses the existing isPathWithinCwd helper from src/tools/permissions.ts
  • Permissions only apply to non-recursive removes. Recursive removes always prompt regardless of permission state — the permission flag is not authorization for arbitrary tree removal, only for the safe single-empty-dir case.

Confirmation

  • Non-recursive removes (recursive: false): if the relevant permission is granted, skip the prompt; otherwise prompt with the resolved path (label "Remove directory?")
  • Recursive removes (recursive: true): always prompt with the resolved path and a label that visibly signals the higher blast radius (e.g. "Remove directory recursively?"). Independent of any permission state.
  • User denial returns a denied result; flow follows the same pattern as write-file.ts

Validation and behaviour

  • Errors clearly when the path does not exist
  • Errors clearly when the path is a file (suggest remove_file)
  • Non-recursive mode: errors clearly when the directory is not empty, suggesting recursive: true
  • Recursive mode: removes the entire tree
  • Errors clearly on permission/IO errors from the OS
  • On success, returns a plain ok result confirming the path was removed (and notes recursive if applicable)

Tests

  • Mock the filesystem at the boundary; do not mock internal helpers
  • Non-recursive behaviour: removes empty dir in cwd, removes empty dir outside cwd, errors on non-empty dir, errors on missing path, errors on file path
  • Recursive behaviour: removes a populated tree, errors on missing path, errors on file path
  • Permissions (non-recursive): cwd granted skips confirm, cwd ungranted prompts, denial returns denied; same set for global
  • Permissions (recursive): cwd granted still prompts, global granted still prompts — recursive always prompts regardless of permission state
  • Confirmation prompt shape: non-recursive uses standard label, recursive uses a distinct label/detail that signals tree removal

Additional Context

Pairs with #252 (remove_file). Together they cover the destructive filesystem operations the LLM currently has to do via run_command rm / rm -rf, which is awkward (shell quoting, cross-platform inconsistency) and noisy (confirmation per call).

The split between non-recursive and recursive is deliberate. Non-recursive is essentially safe — it fails on non-empty, has single-directory blast radius, and can be permission-gated for cwd-scoped trust the same way file writes are. Recursive is meaningfully destructive — entire tree, easy to point at the wrong path, hard to undo — and so always requires explicit per-call confirmation regardless of permission state. The permission flag is not authorization for "delete anything you want", only for "skip the prompt on the safe single-empty-dir case".

Implementation references:

  • src/tools/write-file.ts for the permission + confirm flow shape
  • src/tools/permissions.ts for checkFilePermission and isPathWithinCwd
  • src/config/schema.ts:19 for the permissions schema where the new keys go

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions