From f50520c93f97b625e348c294fb0f12ad9ab5fa47 Mon Sep 17 00:00:00 2001 From: Shirone Date: Mon, 12 Jan 2026 19:09:25 +0100 Subject: [PATCH] feat(delete): enhance branch deletion handling and validation - Introduced a flag to track if a branch was successfully deleted, improving response clarity. - Updated the response structure to include the new branchDeleted flag. - Enhanced projectPath validation in init-script to ensure it is a non-empty string before processing. --- apps/server/src/routes/worktree/routes/delete.ts | 6 +++++- .../src/routes/worktree/routes/init-script.ts | 14 ++++++++++++-- .../board-view/dialogs/backlog-plan-dialog.tsx | 8 ++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/server/src/routes/worktree/routes/delete.ts b/apps/server/src/routes/worktree/routes/delete.ts index 9d8f9d27..6814add9 100644 --- a/apps/server/src/routes/worktree/routes/delete.ts +++ b/apps/server/src/routes/worktree/routes/delete.ts @@ -57,6 +57,7 @@ export function createDeleteHandler() { } // Optionally delete the branch + let branchDeleted = false; if (deleteBranch && branchName && branchName !== 'main' && branchName !== 'master') { // Validate branch name to prevent command injection if (!isValidBranchName(branchName)) { @@ -64,8 +65,10 @@ export function createDeleteHandler() { } else { try { await execGitCommand(['branch', '-D', branchName], projectPath); + branchDeleted = true; } catch { // Branch deletion failed, not critical + logger.warn(`Failed to delete branch: ${branchName}`); } } } @@ -74,7 +77,8 @@ export function createDeleteHandler() { success: true, deleted: { worktreePath, - branch: deleteBranch ? branchName : null, + branch: branchDeleted ? branchName : null, + branchDeleted, }, }); } catch (error) { diff --git a/apps/server/src/routes/worktree/routes/init-script.ts b/apps/server/src/routes/worktree/routes/init-script.ts index bb04d706..e11dfd53 100644 --- a/apps/server/src/routes/worktree/routes/init-script.ts +++ b/apps/server/src/routes/worktree/routes/init-script.ts @@ -36,9 +36,10 @@ function getInitScriptPath(projectPath: string): string { export function createGetInitScriptHandler() { return async (req: Request, res: Response): Promise => { try { - const projectPath = req.query.projectPath as string; + const rawProjectPath = req.query.projectPath; - if (!projectPath) { + // Validate projectPath is a non-empty string (not an array or undefined) + if (!rawProjectPath || typeof rawProjectPath !== 'string') { res.status(400).json({ success: false, error: 'projectPath query parameter is required', @@ -46,6 +47,15 @@ export function createGetInitScriptHandler() { return; } + const projectPath = rawProjectPath.trim(); + if (!projectPath) { + res.status(400).json({ + success: false, + error: 'projectPath cannot be empty', + }); + return; + } + const scriptPath = getInitScriptPath(projectPath); try { diff --git a/apps/ui/src/components/views/board-view/dialogs/backlog-plan-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/backlog-plan-dialog.tsx index ee78f153..c9fcd311 100644 --- a/apps/ui/src/components/views/board-view/dialogs/backlog-plan-dialog.tsx +++ b/apps/ui/src/components/views/board-view/dialogs/backlog-plan-dialog.tsx @@ -63,7 +63,7 @@ interface BacklogPlanDialogProps { setPendingPlanResult: (result: BacklogPlanResult | null) => void; isGeneratingPlan: boolean; setIsGeneratingPlan: (generating: boolean) => void; - // Branch to use for created features (defaults to main if not provided) + // Branch to use for created features (defaults to 'main' when applying) currentBranch?: string; } @@ -170,7 +170,11 @@ export function BacklogPlanDialog({ }) || [], }; - const result = await api.backlogPlan.apply(projectPath, filteredPlanResult, currentBranch); + const result = await api.backlogPlan.apply( + projectPath, + filteredPlanResult, + currentBranch ?? 'main' + ); if (result.success) { toast.success(`Applied ${result.appliedChanges?.length || 0} changes`); setPendingPlanResult(null);