-
Notifications
You must be signed in to change notification settings - Fork 560
feat: Use feature title for auto-generated worktree branch names #604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,23 +117,51 @@ export function useBoardActions({ | |
| }) => { | ||
| const workMode = featureData.workMode || 'current'; | ||
|
|
||
| // For auto worktree mode, we need a title for the branch name. | ||
| // If no title provided, generate one from the description first. | ||
| let titleForBranch = featureData.title; | ||
| let titleWasGenerated = false; | ||
|
|
||
| if (workMode === 'auto' && !featureData.title.trim() && featureData.description.trim()) { | ||
| // Generate title first so we can use it for the branch name | ||
| const api = getElectronAPI(); | ||
| if (api?.features?.generateTitle) { | ||
| try { | ||
| const result = await api.features.generateTitle(featureData.description); | ||
| if (result.success && result.title) { | ||
| titleForBranch = result.title; | ||
| titleWasGenerated = true; | ||
| } | ||
| } catch (error) { | ||
| logger.error('Error generating title for branch name:', error); | ||
| } | ||
| } | ||
| // If title generation failed, fall back to first part of description | ||
| if (!titleForBranch.trim()) { | ||
| titleForBranch = featureData.description.substring(0, 60); | ||
| } | ||
| } | ||
|
|
||
| // Determine final branch name based on work mode: | ||
| // - 'current': No branch name, work on current branch (no worktree) | ||
| // - 'auto': Auto-generate branch name based on current branch | ||
| // - 'auto': Auto-generate branch name based on feature title | ||
| // - 'custom': Use the provided branch name | ||
| let finalBranchName: string | undefined; | ||
|
|
||
| if (workMode === 'current') { | ||
| // No worktree isolation - work directly on current branch | ||
| finalBranchName = undefined; | ||
| } else if (workMode === 'auto') { | ||
| // Auto-generate a branch name based on primary branch (main/master) and timestamp | ||
| // Always use primary branch to avoid nested feature/feature/... paths | ||
| const baseBranch = | ||
| (currentProject?.path ? getPrimaryWorktreeBranch(currentProject.path) : null) || 'main'; | ||
| // Auto-generate a branch name based on feature title and timestamp | ||
| // Create a slug from the title: lowercase, replace non-alphanumeric with hyphens | ||
| const titleSlug = | ||
| titleForBranch | ||
| .toLowerCase() | ||
| .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric sequences with hyphens | ||
| .replace(/^-|-$/g, '') // Remove leading/trailing hyphens | ||
| .substring(0, 50) || 'untitled'; // Fallback if slug is empty (e.g., title was only special chars) | ||
| const timestamp = Date.now(); | ||
| const randomSuffix = Math.random().toString(36).substring(2, 6); | ||
| finalBranchName = `feature/${baseBranch}-${timestamp}-${randomSuffix}`; | ||
| finalBranchName = `feature/${titleSlug}-${timestamp}`; | ||
|
Comment on lines
125
to
164
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for generating a branch name in 'auto' mode, including title generation and slugification, is duplicated between |
||
| } else { | ||
| // Custom mode - use provided branch name | ||
| finalBranchName = featureData.branchName || undefined; | ||
|
|
@@ -176,12 +204,13 @@ export function useBoardActions({ | |
| } | ||
| } | ||
|
|
||
| // Check if we need to generate a title | ||
| const needsTitleGeneration = !featureData.title.trim() && featureData.description.trim(); | ||
| // Check if we need to generate a title (only if we didn't already generate it for the branch name) | ||
| const needsTitleGeneration = | ||
| !titleWasGenerated && !featureData.title.trim() && featureData.description.trim(); | ||
|
|
||
| const newFeatureData = { | ||
| ...featureData, | ||
| title: featureData.title, | ||
| title: titleWasGenerated ? titleForBranch : featureData.title, | ||
| titleGenerating: needsTitleGeneration, | ||
| status: 'backlog' as const, | ||
| branchName: finalBranchName, | ||
|
|
@@ -247,7 +276,6 @@ export function useBoardActions({ | |
| currentProject, | ||
| onWorktreeCreated, | ||
| onWorktreeAutoSelect, | ||
| getPrimaryWorktreeBranch, | ||
| features, | ||
| ] | ||
| ); | ||
|
|
@@ -278,19 +306,47 @@ export function useBoardActions({ | |
| ) => { | ||
| const workMode = updates.workMode || 'current'; | ||
|
|
||
| // For auto worktree mode, we need a title for the branch name. | ||
| // If no title provided, generate one from the description first. | ||
| let titleForBranch = updates.title; | ||
| let titleWasGenerated = false; | ||
|
|
||
| if (workMode === 'auto' && !updates.title.trim() && updates.description.trim()) { | ||
| // Generate title first so we can use it for the branch name | ||
| const api = getElectronAPI(); | ||
| if (api?.features?.generateTitle) { | ||
| try { | ||
| const result = await api.features.generateTitle(updates.description); | ||
| if (result.success && result.title) { | ||
| titleForBranch = result.title; | ||
| titleWasGenerated = true; | ||
| } | ||
| } catch (error) { | ||
| logger.error('Error generating title for branch name:', error); | ||
| } | ||
| } | ||
| // If title generation failed, fall back to first part of description | ||
| if (!titleForBranch.trim()) { | ||
| titleForBranch = updates.description.substring(0, 60); | ||
| } | ||
| } | ||
|
|
||
| // Determine final branch name based on work mode | ||
| let finalBranchName: string | undefined; | ||
|
|
||
| if (workMode === 'current') { | ||
| finalBranchName = undefined; | ||
| } else if (workMode === 'auto') { | ||
| // Auto-generate a branch name based on primary branch (main/master) and timestamp | ||
| // Always use primary branch to avoid nested feature/feature/... paths | ||
| const baseBranch = | ||
| (currentProject?.path ? getPrimaryWorktreeBranch(currentProject.path) : null) || 'main'; | ||
| // Auto-generate a branch name based on feature title and timestamp | ||
| // Create a slug from the title: lowercase, replace non-alphanumeric with hyphens | ||
| const titleSlug = | ||
| titleForBranch | ||
| .toLowerCase() | ||
| .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric sequences with hyphens | ||
| .replace(/^-|-$/g, '') // Remove leading/trailing hyphens | ||
| .substring(0, 50) || 'untitled'; // Fallback if slug is empty (e.g., title was only special chars) | ||
| const timestamp = Date.now(); | ||
| const randomSuffix = Math.random().toString(36).substring(2, 6); | ||
| finalBranchName = `feature/${baseBranch}-${timestamp}-${randomSuffix}`; | ||
| finalBranchName = `feature/${titleSlug}-${timestamp}`; | ||
| } else { | ||
| finalBranchName = updates.branchName || undefined; | ||
| } | ||
|
|
@@ -332,7 +388,7 @@ export function useBoardActions({ | |
|
|
||
| const finalUpdates = { | ||
| ...restUpdates, | ||
| title: updates.title, | ||
| title: titleWasGenerated ? titleForBranch : updates.title, | ||
| branchName: finalBranchName, | ||
| }; | ||
|
Comment on lines
389
to
393
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's an inconsistency here compared to As a result, if a user updates a feature to use 'auto' worktree mode without a title, and the synchronous title generation fails, the feature's title will remain empty, even though a branch is created. For consistency, you should consider adding the background title generation logic here as well, similar to what's done in |
||
|
|
||
|
|
@@ -395,7 +451,6 @@ export function useBoardActions({ | |
| setEditingFeature, | ||
| currentProject, | ||
| onWorktreeCreated, | ||
| getPrimaryWorktreeBranch, | ||
| features, | ||
| ] | ||
| ); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.