feat: add auto-generated titles for features

- Add POST /features/generate-title endpoint using Claude Haiku
- Generate concise titles (5-10 words) from feature descriptions
- Display titles in kanban cards with loading state
- Add optional title field to add/edit feature dialogs
- Auto-generate titles when description provided but title empty
- Add 'Pull & Resolve Conflicts' action to worktree dropdown
- Show running agents count in board header (X / Y format)
- Update Feature interface to include title and titleGenerating fields
This commit is contained in:
Cody Seibert
2025-12-19 23:36:29 -05:00
parent 36e007e647
commit fcb2e904eb
16 changed files with 307 additions and 8 deletions

View File

@@ -20,6 +20,7 @@ import {
Square,
Globe,
MessageSquare,
GitMerge,
} from "lucide-react";
import { cn } from "@/lib/utils";
import type { WorktreeInfo, DevServerInfo, PRInfo } from "../types";
@@ -42,6 +43,7 @@ interface WorktreeActionsDropdownProps {
onCommit: (worktree: WorktreeInfo) => void;
onCreatePR: (worktree: WorktreeInfo) => void;
onAddressPRComments: (worktree: WorktreeInfo, prInfo: PRInfo) => void;
onResolveConflicts: (worktree: WorktreeInfo) => void;
onDeleteWorktree: (worktree: WorktreeInfo) => void;
onStartDevServer: (worktree: WorktreeInfo) => void;
onStopDevServer: (worktree: WorktreeInfo) => void;
@@ -66,6 +68,7 @@ export function WorktreeActionsDropdown({
onCommit,
onCreatePR,
onAddressPRComments,
onResolveConflicts,
onDeleteWorktree,
onStartDevServer,
onStopDevServer,
@@ -160,6 +163,15 @@ export function WorktreeActionsDropdown({
</span>
)}
</DropdownMenuItem>
{!worktree.isMain && (
<DropdownMenuItem
onClick={() => onResolveConflicts(worktree)}
className="text-xs text-purple-500 focus:text-purple-600"
>
<GitMerge className="w-3.5 h-3.5 mr-2" />
Pull & Resolve Conflicts
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => onOpenInEditor(worktree)}

View File

@@ -45,6 +45,7 @@ interface WorktreeTabProps {
onCommit: (worktree: WorktreeInfo) => void;
onCreatePR: (worktree: WorktreeInfo) => void;
onAddressPRComments: (worktree: WorktreeInfo, prInfo: PRInfo) => void;
onResolveConflicts: (worktree: WorktreeInfo) => void;
onDeleteWorktree: (worktree: WorktreeInfo) => void;
onStartDevServer: (worktree: WorktreeInfo) => void;
onStopDevServer: (worktree: WorktreeInfo) => void;
@@ -84,6 +85,7 @@ export function WorktreeTab({
onCommit,
onCreatePR,
onAddressPRComments,
onResolveConflicts,
onDeleteWorktree,
onStartDevServer,
onStopDevServer,
@@ -343,6 +345,7 @@ export function WorktreeTab({
onCommit={onCommit}
onCreatePR={onCreatePR}
onAddressPRComments={onAddressPRComments}
onResolveConflicts={onResolveConflicts}
onDeleteWorktree={onDeleteWorktree}
onStartDevServer={onStartDevServer}
onStopDevServer={onStopDevServer}

View File

@@ -67,6 +67,7 @@ export interface WorktreePanelProps {
onCreatePR: (worktree: WorktreeInfo) => void;
onCreateBranch: (worktree: WorktreeInfo) => void;
onAddressPRComments: (worktree: WorktreeInfo, prInfo: PRInfo) => void;
onResolveConflicts: (worktree: WorktreeInfo) => void;
onRemovedWorktrees?: (removedWorktrees: Array<{ path: string; branch: string }>) => void;
runningFeatureIds?: string[];
features?: FeatureInfo[];

View File

@@ -21,6 +21,7 @@ export function WorktreePanel({
onCreatePR,
onCreateBranch,
onAddressPRComments,
onResolveConflicts,
onRemovedWorktrees,
runningFeatureIds = [],
features = [],
@@ -148,6 +149,7 @@ export function WorktreePanel({
onCommit={onCommit}
onCreatePR={onCreatePR}
onAddressPRComments={onAddressPRComments}
onResolveConflicts={onResolveConflicts}
onDeleteWorktree={onDeleteWorktree}
onStartDevServer={handleStartDevServer}
onStopDevServer={handleStopDevServer}