diff --git a/.gitignore b/.gitignore index 505a3b1..2ea1e09 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ wheels/ # Virtual environments .venv + +# Tool caches +.pytest_cache/ +.ruff_cache/ diff --git a/src/grove/cli.py b/src/grove/cli.py index f494189..1607ef3 100644 --- a/src/grove/cli.py +++ b/src/grove/cli.py @@ -318,28 +318,37 @@ def delete( force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"), ) -> None: """Delete a workspace and its worktrees.""" - # Interactive fallback + # Interactive fallback — multi-select if name is None: workspaces = state.load_workspaces() if not workspaces: error("No workspaces to delete") raise typer.Exit(1) - name = _pick_one("Select workspace to delete", [ws.name for ws in workspaces]) + names = _pick_many("Select workspace(s) to delete", [ws.name for ws in workspaces]) + else: + names = [name] - ws = state.get_workspace(name) - if ws is None: - error(f"Workspace [bold]{name}[/] not found") - raise typer.Exit(1) + # Validate all names upfront + for n in names: + if state.get_workspace(n) is None: + error(f"Workspace [bold]{n}[/] not found") + raise typer.Exit(1) if not force: - confirm = typer.confirm(f"Delete workspace '{name}' and all its worktrees?") + label = ", ".join(names) + msg = f"Delete {len(names)} workspace(s) ({label}) and all their worktrees?" + confirm = typer.confirm(msg) if not confirm: info("Cancelled") return - if workspace.delete_workspace(name): - success(f"Workspace [bold]{name}[/] deleted") - else: + failed = False + for n in names: + if workspace.delete_workspace(n): + success(f"Workspace [bold]{n}[/] deleted") + else: + failed = True + if failed: raise typer.Exit(1) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 64a0494..81424e7 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -114,9 +114,7 @@ def test_auto_creates_branch(self, tmp_grove): ): ws = workspace.create_workspace("test", {"svc-api": repo_path}, "feat/new", cfg) assert ws is not None - mock_create.assert_called_once_with( - repo_path, "feat/new", start_point="origin/main" - ) + mock_create.assert_called_once_with(repo_path, "feat/new", start_point="origin/main") class TestSetupHook: