From eb19053936808c4fa03bb942888973d464a89cb2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Nov 2025 05:55:39 +0000 Subject: [PATCH] feat: add file deletion feature to menu actions Implements file deletion functionality with confirmation dialog. Users can now delete Claude configuration files directly from the interactive menu using the 'x' key shortcut. Changes: - Add delete file action to menu with 'x' key binding - Require confirmation before deleting files - Update README with new keyboard shortcut - Add tests for delete functionality - Add TypeScript as dev dependency for build compatibility Resolves user request to incorporate file deletion feature to cleanup redundant tools/agents alongside existing edit/view options. --- README.md | 1 + package.json | 1 + src/components/FileList/MenuActions.test.tsx | 8 ++++++- .../FileList/MenuActions/hooks/useMenu.ts | 24 +++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b827e3..e4c0a36 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ ccexp --path ~/workspace # Specific directory - **d** - Copy file to current directory (in menu) - **e** - Edit file with $EDITOR (in menu) - **o** - Open file in default application (in menu) +- **x** - Delete file (in menu, requires confirmation) ## Development diff --git a/package.json b/package.json index 5e7c49a..5516208 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "lefthook": "^1.12.2", "publint": "^0.3.12", "tsdown": "^0.12.9", + "typescript": "^5.9.3", "vitest": "^3.2.4" }, "overrides": { diff --git a/src/components/FileList/MenuActions.test.tsx b/src/components/FileList/MenuActions.test.tsx index 2e121c1..56c2137 100644 --- a/src/components/FileList/MenuActions.test.tsx +++ b/src/components/FileList/MenuActions.test.tsx @@ -54,6 +54,7 @@ if (import.meta.vitest) { expect(lastFrame()).toContain('[D] Copy to Current Directory'); expect(lastFrame()).toContain('[E] Edit File'); expect(lastFrame()).toContain('[O] Open File'); + expect(lastFrame()).toContain('[X] Delete File'); }, ); }); @@ -157,13 +158,14 @@ if (import.meta.vitest) { , ); - // Verify 6 actions are present + // Verify 7 actions are present expect(lastFrame()).toContain('[C] Copy Content'); expect(lastFrame()).toContain('[P] Copy Path (Absolute)'); expect(lastFrame()).toContain('[R] Copy Path (Relative)'); expect(lastFrame()).toContain('[D] Copy to Current Directory'); expect(lastFrame()).toContain('[E] Edit File'); expect(lastFrame()).toContain('[O] Open File'); + expect(lastFrame()).toContain('[X] Delete File'); }, ); }); @@ -225,6 +227,7 @@ if (import.meta.vitest) { output?.indexOf('[D] Copy to Current Directory') ?? -1; const editFileIndex = output?.indexOf('[E] Edit File') ?? -1; const openFileIndex = output?.indexOf('[O] Open File') ?? -1; + const deleteFileIndex = output?.indexOf('[X] Delete File') ?? -1; expect(copyContentIndex).toBeGreaterThan(-1); expect(copyAbsoluteIndex).toBeGreaterThan(copyContentIndex); @@ -232,6 +235,7 @@ if (import.meta.vitest) { expect(copyDirIndex).toBeGreaterThan(copyRelativeIndex); expect(editFileIndex).toBeGreaterThan(copyDirIndex); expect(openFileIndex).toBeGreaterThan(editFileIndex); + expect(deleteFileIndex).toBeGreaterThan(openFileIndex); }, ); }); @@ -378,6 +382,7 @@ if (import.meta.vitest) { expect(output).toContain('Copy to Current Directory'); expect(output).toContain('Edit File'); expect(output).toContain('Open File'); + expect(output).toContain('Delete File'); }, ); }); @@ -410,6 +415,7 @@ if (import.meta.vitest) { expect(output).toContain('[D]'); expect(output).toContain('[E]'); expect(output).toContain('[O]'); + expect(output).toContain('[X]'); }, ); }); diff --git a/src/components/FileList/MenuActions/hooks/useMenu.ts b/src/components/FileList/MenuActions/hooks/useMenu.ts index df882a9..8b75f42 100644 --- a/src/components/FileList/MenuActions/hooks/useMenu.ts +++ b/src/components/FileList/MenuActions/hooks/useMenu.ts @@ -227,6 +227,30 @@ export const useMenu = ({ file, onClose }: UseMenuProps) => { return '✅ File opened'; }, }, + { + key: 'x', + label: 'Delete File', + description: 'Delete file (requires confirmation)', + action: async () => { + const fs = await import('node:fs/promises'); + + // The actual deletion operation + const performDelete = async () => { + await fs.unlink(file.path); + return `✅ File deleted: ${basename(file.path)}`; + }; + + // Always require confirmation for deletion + setConfirmMessage( + `Are you sure you want to delete "${basename(file.path)}"? This action cannot be undone.`, + ); + setIsConfirming(true); + setPendingAction(() => performDelete); + + // Return early, action will be executed after confirmation + return ''; + }, + }, ], [file.path, file.type, copyToClipboard, openFile, editFile], );