Skip to content

feat(tools): add remove_dir tool#277

Merged
LeeCheneler merged 2 commits intomainfrom
feat/251-remove-dir-tool
Apr 10, 2026
Merged

feat(tools): add remove_dir tool#277
LeeCheneler merged 2 commits intomainfrom
feat/251-remove-dir-tool

Conversation

@LeeCheneler
Copy link
Copy Markdown
Owner

Summary

Adds a new remove_dir tool so the model can delete directories directly without escaping into run_command rm -rf. Two modes: non-recursive (permission-gated, safe single-empty-dir case) and recursive (always prompts regardless of permissions).

GitHub Issue

Closes #251

What Changed

New tool. remove_dir takes path + optional recursive (default false). Error ordering: path-is-file → suggest remove_file; path doesn't exist → error; non-recursive + non-empty → error suggesting recursive: true. formatCall returns path or path (recursive) so the collapsed tool header reflects the mode.

Two confirmation paths. Non-recursive uses the standard permission resolution against the new cwdRemoveDir / globalRemoveDir keys and only prompts when not pre-granted. Recursive always prompts regardless of permission state — the permission flag is not authorisation for arbitrary tree removal, only for the safe single-empty-dir case. Both modes use label "Remove directory?"; recursive adds (recursive) to the detail line so the prompt header visibly signals the bigger blast radius.

Schema. cwdRemoveDir / globalRemoveDir follow the optional-boolean pattern alongside the existing read/write/remove keys. removeDir joins toolsSchema with the field-level .default({ enabled: true }) pattern we standardised in #276, so adding this tool is non-breaking for existing configs.

PathOperation extended. The union already covered "read" | "write" | "remove"; now also "removeDir". The PERMISSION_KEYS map gets a new entry — no branching changes, uniform handling across all four operations. (FileOperationPathOperation rename happened in the prep commit e0ce922.)

mock-fs upgrade. The test helper previously had no concept of an empty directory — directories were inferred from file prefixes, so isDirectory("/empty/dir") was always false unless files existed under it. That broke the non-recursive "removes empty dir" cases. Fixed by tracking explicit dirs in a Set<string> alongside the file map:

  • ensureDir now registers paths instead of being a no-op.
  • New optional second ctor arg initialDirs: string[] for test convenience.
  • isDirectory returns true if path is in dirs OR has files under it.
  • New removeDir spy enforces ENOENT (missing path) and ENOTEMPTY (non-empty non-recursive) semantics.
  • New getDirs() helper on the returned state.

Settings UI. New "Remove Directory" entry in the tools toggle list and two "Remove directories (current directory / global)" entries in the permissions toggle list.

Docs. README tools table, permissions YAML example, new "recursive always prompts" callout explaining the non-authorisation semantics of the permission flag, and CONTRIBUTING's src/ tree comment.

Notes for Reviewers

  • Two commits on this branch. First is a pure rename (FileOperationPathOperation, checkFilePermissioncheckPathPermission) to prep for the new "removeDir" operation, no behaviour change. Second is the feature itself. Happy to squash on merge either way.
  • Recursive always prompts — verify this in remove-dir.ts:66-77. The if (parsed.recursive) branch unconditionally calls context.confirm before reaching the permission check. The permission matrix test "still prompts when cwdRemoveDir is granted" covers this explicitly.
  • Error ordering matters. fileExists && !isDirectory → file → suggest remove_file (check first). Then !isDirectory → doesn't exist. If reordered, the file-path case would report "does not exist" instead.
  • stub-context.ts default permissions now include cwdRemoveDir: true for happy-path test convenience. Permission-matrix tests still pin explicit values.
  • Coverage is 100% on both remove-dir.ts and the updated mock-fs.ts.

Prep for the upcoming remove_dir tool, which will add a "removeDir"
operation to this type. Renames FileOperation → PathOperation and
checkFilePermission → checkPathPermission to reflect that the helper
covers both files and directories. Pure rename, no behaviour change.
Adds a new remove_dir tool so the model can delete directories directly
instead of escaping into run_command rm / rm -rf. The tool has two modes:

- Non-recursive (default): removes an empty directory, fails on
  non-empty. Gated by new cwdRemoveDir / globalRemoveDir permission keys
  that follow the existing optional-boolean pattern.
- Recursive: removes the entire tree. Always prompts for confirmation
  regardless of permission state — the permission flag only gates the
  safe single-empty-dir case, not arbitrary tree removal.

The mock-fs test helper now tracks explicit empty directories via a
dirs Set alongside the file map. ensureDir registers paths (was a
no-op), an optional second ctor arg accepts initial empty dirs, and a
new removeDir spy enforces ENOENT / ENOTEMPTY semantics. This lets the
non-recursive "removes empty dir" cases be tested without standing up
a real filesystem.

Closes #251
@LeeCheneler LeeCheneler merged commit 6d7901d into main Apr 10, 2026
4 checks passed
@LeeCheneler LeeCheneler deleted the feat/251-remove-dir-tool branch April 10, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: tool for removing directories

1 participant