fixing some bugs

This commit is contained in:
Cody Seibert
2025-12-16 12:54:53 -05:00
parent d103d0aa45
commit 064a395c4c
6 changed files with 79 additions and 33 deletions

View File

@@ -67,6 +67,8 @@ export function BoardView() {
setSpecCreatingForProject, setSpecCreatingForProject,
getCurrentWorktree, getCurrentWorktree,
setCurrentWorktree, setCurrentWorktree,
getWorktrees,
setWorktrees,
} = useAppStore(); } = useAppStore();
const shortcuts = useKeyboardShortcutsConfig(); const shortcuts = useKeyboardShortcutsConfig();
const { const {
@@ -215,7 +217,7 @@ export function BoardView() {
// Branch suggestions for the branch autocomplete // Branch suggestions for the branch autocomplete
const [branchSuggestions, setBranchSuggestions] = useState<string[]>([]); const [branchSuggestions, setBranchSuggestions] = useState<string[]>([]);
// Fetch branches when project changes // Fetch branches when project changes or worktrees are created/modified
useEffect(() => { useEffect(() => {
const fetchBranches = async () => { const fetchBranches = async () => {
if (!currentProject) { if (!currentProject) {
@@ -244,7 +246,7 @@ export function BoardView() {
}; };
fetchBranches(); fetchBranches();
}, [currentProject]); }, [currentProject, worktreeRefreshKey]);
// Custom collision detection that prioritizes columns over cards // Custom collision detection that prioritizes columns over cards
const collisionDetectionStrategy = useCallback( const collisionDetectionStrategy = useCallback(
@@ -339,16 +341,21 @@ export function BoardView() {
}); });
// Use drag and drop hook // Use drag and drop hook
// Get current worktree path and branch for filtering features // Get current worktree info (path and branch) for filtering features
const currentWorktreePath = currentProject ? getCurrentWorktree(currentProject.path) : null; const currentWorktreeInfo = currentProject ? getCurrentWorktree(currentProject.path) : null;
const currentWorktreePath = currentWorktreeInfo?.path ?? null;
const currentWorktreeBranch = currentWorktreeInfo?.branch ?? null;
const worktreesByProject = useAppStore((s) => s.worktreesByProject); const worktreesByProject = useAppStore((s) => s.worktreesByProject);
const worktrees = useMemo( const worktrees = useMemo(
() => (currentProject ? (worktreesByProject[currentProject.path] ?? EMPTY_WORKTREES) : EMPTY_WORKTREES), () => (currentProject ? (worktreesByProject[currentProject.path] ?? EMPTY_WORKTREES) : EMPTY_WORKTREES),
[currentProject, worktreesByProject] [currentProject, worktreesByProject]
); );
const currentWorktreeBranch = currentWorktreePath
? worktrees.find(w => w.path === currentWorktreePath)?.branch || null // Get the branch for the currently selected worktree (for defaulting new features)
: null; // Use the branch from currentWorktreeInfo, or fall back to main worktree's branch
const selectedWorktreeBranch = currentWorktreeBranch
|| worktrees.find(w => w.isMain)?.branch
|| "main";
const { activeFeature, handleDragStart, handleDragEnd } = useBoardDragDrop({ const { activeFeature, handleDragStart, handleDragEnd } = useBoardDragDrop({
features: hookFeatures, features: hookFeatures,
@@ -533,6 +540,7 @@ export function BoardView() {
categorySuggestions={categorySuggestions} categorySuggestions={categorySuggestions}
branchSuggestions={branchSuggestions} branchSuggestions={branchSuggestions}
defaultSkipTests={defaultSkipTests} defaultSkipTests={defaultSkipTests}
defaultBranch={selectedWorktreeBranch}
isMaximized={isMaximized} isMaximized={isMaximized}
showProfilesOnly={showProfilesOnly} showProfilesOnly={showProfilesOnly}
aiProfiles={aiProfiles} aiProfiles={aiProfiles}
@@ -603,10 +611,24 @@ export function BoardView() {
open={showCreateWorktreeDialog} open={showCreateWorktreeDialog}
onOpenChange={setShowCreateWorktreeDialog} onOpenChange={setShowCreateWorktreeDialog}
projectPath={currentProject.path} projectPath={currentProject.path}
onCreated={(worktreePath) => { onCreated={(newWorktree) => {
// Add the new worktree to the store immediately to avoid race condition
// when deriving currentWorktreeBranch for filtering
const currentWorktrees = getWorktrees(currentProject.path);
const newWorktreeInfo = {
path: newWorktree.path,
branch: newWorktree.branch,
isMain: false,
isCurrent: false,
hasWorktree: true,
};
setWorktrees(currentProject.path, [...currentWorktrees, newWorktreeInfo]);
// Now set the current worktree with both path and branch
setCurrentWorktree(currentProject.path, newWorktree.path, newWorktree.branch);
// Trigger refresh to get full worktree details (hasChanges, etc.)
setWorktreeRefreshKey((k) => k + 1); setWorktreeRefreshKey((k) => k + 1);
// Auto-select the newly created worktree
setCurrentWorktree(currentProject.path, worktreePath);
}} }}
/> />

View File

@@ -198,13 +198,15 @@ export function WorktreeSelector({
// Initialize selection to main if not set // Initialize selection to main if not set
useEffect(() => { useEffect(() => {
if (worktrees.length > 0 && currentWorktree === undefined) { if (worktrees.length > 0 && currentWorktree === undefined) {
setCurrentWorktree(projectPath, null); // null = main worktree const mainWorktree = worktrees.find(w => w.isMain);
const mainBranch = mainWorktree?.branch || "main";
setCurrentWorktree(projectPath, null, mainBranch); // null = main worktree
} }
}, [worktrees, currentWorktree, projectPath, setCurrentWorktree]); }, [worktrees, currentWorktree, projectPath, setCurrentWorktree]);
const handleSelectWorktree = async (worktree: WorktreeInfo) => { const handleSelectWorktree = async (worktree: WorktreeInfo) => {
// Simply select the worktree in the UI // Simply select the worktree in the UI with both path and branch
setCurrentWorktree(projectPath, worktree.isMain ? null : worktree.path); setCurrentWorktree(projectPath, worktree.isMain ? null : worktree.path, worktree.branch);
}; };
const handleStartDevServer = async (worktree: WorktreeInfo) => { const handleStartDevServer = async (worktree: WorktreeInfo) => {
@@ -454,19 +456,20 @@ export function WorktreeSelector({
}; };
// The "selected" worktree is based on UI state, not git's current branch // The "selected" worktree is based on UI state, not git's current branch
// currentWorktree is null for main, or the worktree path for others // currentWorktree.path is null for main, or the worktree path for others
const selectedWorktree = currentWorktree const currentWorktreePath = currentWorktree?.path ?? null;
? worktrees.find((w) => w.path === currentWorktree) const selectedWorktree = currentWorktreePath
? worktrees.find((w) => w.path === currentWorktreePath)
: worktrees.find((w) => w.isMain); : worktrees.find((w) => w.isMain);
// Render a worktree tab with branch selector (for main) and actions dropdown // Render a worktree tab with branch selector (for main) and actions dropdown
const renderWorktreeTab = (worktree: WorktreeInfo) => { const renderWorktreeTab = (worktree: WorktreeInfo) => {
// Selection is based on UI state, not git's current branch // Selection is based on UI state, not git's current branch
// Default to main selected if currentWorktree is null or undefined // Default to main selected if currentWorktree is null/undefined or path is null
const isSelected = worktree.isMain const isSelected = worktree.isMain
? currentWorktree === null || currentWorktree === undefined ? currentWorktree === null || currentWorktree === undefined || currentWorktree.path === null
: worktree.path === currentWorktree; : worktree.path === currentWorktreePath;
const isRunning = hasRunningFeatures(worktree); const isRunning = hasRunningFeatures(worktree);

View File

@@ -69,6 +69,7 @@ interface AddFeatureDialogProps {
categorySuggestions: string[]; categorySuggestions: string[];
branchSuggestions: string[]; branchSuggestions: string[];
defaultSkipTests: boolean; defaultSkipTests: boolean;
defaultBranch?: string;
isMaximized: boolean; isMaximized: boolean;
showProfilesOnly: boolean; showProfilesOnly: boolean;
aiProfiles: AIProfile[]; aiProfiles: AIProfile[];
@@ -81,6 +82,7 @@ export function AddFeatureDialog({
categorySuggestions, categorySuggestions,
branchSuggestions, branchSuggestions,
defaultSkipTests, defaultSkipTests,
defaultBranch = "main",
isMaximized, isMaximized,
showProfilesOnly, showProfilesOnly,
aiProfiles, aiProfiles,
@@ -109,15 +111,16 @@ export function AddFeatureDialog({
// Get enhancement model from store // Get enhancement model from store
const { enhancementModel } = useAppStore(); const { enhancementModel } = useAppStore();
// Sync skipTests default when dialog opens // Sync defaults when dialog opens
useEffect(() => { useEffect(() => {
if (open) { if (open) {
setNewFeature((prev) => ({ setNewFeature((prev) => ({
...prev, ...prev,
skipTests: defaultSkipTests, skipTests: defaultSkipTests,
branchName: defaultBranch,
})); }));
} }
}, [open, defaultSkipTests]); }, [open, defaultSkipTests, defaultBranch]);
const handleAdd = () => { const handleAdd = () => {
if (!newFeature.description.trim()) { if (!newFeature.description.trim()) {
@@ -155,7 +158,7 @@ export function AddFeatureDialog({
model: "opus", model: "opus",
priority: 2, priority: 2,
thinkingLevel: "none", thinkingLevel: "none",
branchName: "main", branchName: defaultBranch,
}); });
setNewFeaturePreviewMap(new Map()); setNewFeaturePreviewMap(new Map());
setShowAdvancedOptions(false); setShowAdvancedOptions(false);

View File

@@ -16,11 +16,16 @@ import { GitBranch, Loader2 } from "lucide-react";
import { getElectronAPI } from "@/lib/electron"; import { getElectronAPI } from "@/lib/electron";
import { toast } from "sonner"; import { toast } from "sonner";
interface CreatedWorktreeInfo {
path: string;
branch: string;
}
interface CreateWorktreeDialogProps { interface CreateWorktreeDialogProps {
open: boolean; open: boolean;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
projectPath: string; projectPath: string;
onCreated: (worktreePath: string) => void; onCreated: (worktree: CreatedWorktreeInfo) => void;
} }
export function CreateWorktreeDialog({ export function CreateWorktreeDialog({
@@ -68,7 +73,7 @@ export function CreateWorktreeDialog({
: "Using existing branch", : "Using existing branch",
} }
); );
onCreated(result.worktree.path); onCreated({ path: result.worktree.path, branch: result.worktree.branch });
onOpenChange(false); onOpenChange(false);
setBranchName(""); setBranchName("");
} else { } else {

View File

@@ -43,7 +43,12 @@ export function useBoardColumnFeatures({
// Determine the effective worktree path and branch for filtering // Determine the effective worktree path and branch for filtering
// If currentWorktreePath is null, we're on the main worktree // If currentWorktreePath is null, we're on the main worktree
const effectiveWorktreePath = currentWorktreePath || projectPath; const effectiveWorktreePath = currentWorktreePath || projectPath;
const effectiveBranch = currentWorktreeBranch || "main"; // Use the branch name from the selected worktree
// If we're selecting main (currentWorktreePath is null), currentWorktreeBranch
// should contain the main branch's actual name, defaulting to "main"
// If we're selecting a non-main worktree but can't find it, currentWorktreeBranch is null
// In that case, we can't do branch-based filtering, so we'll handle it specially below
const effectiveBranch = currentWorktreeBranch;
filteredFeatures.forEach((f) => { filteredFeatures.forEach((f) => {
// If feature has a running agent, always show it in "in_progress" // If feature has a running agent, always show it in "in_progress"
@@ -62,6 +67,11 @@ export function useBoardColumnFeatures({
} else if (f.worktreePath) { } else if (f.worktreePath) {
// Has worktreePath - match by path // Has worktreePath - match by path
matchesWorktree = f.worktreePath === effectiveWorktreePath; matchesWorktree = f.worktreePath === effectiveWorktreePath;
} else if (effectiveBranch === null) {
// We're selecting a non-main worktree but can't determine its branch yet
// (worktrees haven't loaded). Don't show branch-only features until we know.
// This prevents showing wrong features during loading.
matchesWorktree = false;
} else { } else {
// Has branchName but no worktreePath - match by branch name // Has branchName but no worktreePath - match by branch name
matchesWorktree = featureBranch === effectiveBranch; matchesWorktree = featureBranch === effectiveBranch;
@@ -76,10 +86,12 @@ export function useBoardColumnFeatures({
// Otherwise, use the feature's status (fallback to backlog for unknown statuses) // Otherwise, use the feature's status (fallback to backlog for unknown statuses)
const status = f.status as ColumnId; const status = f.status as ColumnId;
// Backlog items are always visible (they have no worktree assigned) // Filter all items by worktree, including backlog
// For other statuses, filter by worktree // This ensures backlog items with a branch assigned only show in that branch
if (status === "backlog") { if (status === "backlog") {
if (matchesWorktree) {
map.backlog.push(f); map.backlog.push(f);
}
} else if (map[status]) { } else if (map[status]) {
// Only show if matches current worktree or has no worktree assigned // Only show if matches current worktree or has no worktree assigned
if (matchesWorktree) { if (matchesWorktree) {

View File

@@ -399,7 +399,8 @@ export interface AppState {
useWorktrees: boolean; // Whether to use git worktree isolation for features (default: false) useWorktrees: boolean; // Whether to use git worktree isolation for features (default: false)
// User-managed Worktrees (per-project) // User-managed Worktrees (per-project)
currentWorktreeByProject: Record<string, string | null>; // projectPath -> worktreePath or null for main // projectPath -> { path: worktreePath or null for main, branch: branch name }
currentWorktreeByProject: Record<string, { path: string | null; branch: string }>;
worktreesByProject: Record< worktreesByProject: Record<
string, string,
Array<{ Array<{
@@ -582,7 +583,7 @@ export interface AppActions {
// Worktree Settings actions // Worktree Settings actions
setUseWorktrees: (enabled: boolean) => void; setUseWorktrees: (enabled: boolean) => void;
setCurrentWorktree: (projectPath: string, worktreePath: string | null) => void; setCurrentWorktree: (projectPath: string, worktreePath: string | null, branch: string) => void;
setWorktrees: ( setWorktrees: (
projectPath: string, projectPath: string,
worktrees: Array<{ worktrees: Array<{
@@ -593,7 +594,7 @@ export interface AppActions {
changedFilesCount?: number; changedFilesCount?: number;
}> }>
) => void; ) => void;
getCurrentWorktree: (projectPath: string) => string | null; getCurrentWorktree: (projectPath: string) => { path: string | null; branch: string } | null;
getWorktrees: (projectPath: string) => Array<{ getWorktrees: (projectPath: string) => Array<{
path: string; path: string;
branch: string; branch: string;
@@ -1344,12 +1345,12 @@ export const useAppStore = create<AppState & AppActions>()(
// Worktree Settings actions // Worktree Settings actions
setUseWorktrees: (enabled) => set({ useWorktrees: enabled }), setUseWorktrees: (enabled) => set({ useWorktrees: enabled }),
setCurrentWorktree: (projectPath, worktreePath) => { setCurrentWorktree: (projectPath, worktreePath, branch) => {
const current = get().currentWorktreeByProject; const current = get().currentWorktreeByProject;
set({ set({
currentWorktreeByProject: { currentWorktreeByProject: {
...current, ...current,
[projectPath]: worktreePath, [projectPath]: { path: worktreePath, branch },
}, },
}); });
}, },