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,
getCurrentWorktree,
setCurrentWorktree,
getWorktrees,
setWorktrees,
} = useAppStore();
const shortcuts = useKeyboardShortcutsConfig();
const {
@@ -215,7 +217,7 @@ export function BoardView() {
// Branch suggestions for the branch autocomplete
const [branchSuggestions, setBranchSuggestions] = useState<string[]>([]);
// Fetch branches when project changes
// Fetch branches when project changes or worktrees are created/modified
useEffect(() => {
const fetchBranches = async () => {
if (!currentProject) {
@@ -244,7 +246,7 @@ export function BoardView() {
};
fetchBranches();
}, [currentProject]);
}, [currentProject, worktreeRefreshKey]);
// Custom collision detection that prioritizes columns over cards
const collisionDetectionStrategy = useCallback(
@@ -339,16 +341,21 @@ export function BoardView() {
});
// Use drag and drop hook
// Get current worktree path and branch for filtering features
const currentWorktreePath = currentProject ? getCurrentWorktree(currentProject.path) : null;
// Get current worktree info (path and branch) for filtering features
const currentWorktreeInfo = currentProject ? getCurrentWorktree(currentProject.path) : null;
const currentWorktreePath = currentWorktreeInfo?.path ?? null;
const currentWorktreeBranch = currentWorktreeInfo?.branch ?? null;
const worktreesByProject = useAppStore((s) => s.worktreesByProject);
const worktrees = useMemo(
() => (currentProject ? (worktreesByProject[currentProject.path] ?? EMPTY_WORKTREES) : EMPTY_WORKTREES),
[currentProject, worktreesByProject]
);
const currentWorktreeBranch = currentWorktreePath
? worktrees.find(w => w.path === currentWorktreePath)?.branch || null
: null;
// Get the branch for the currently selected worktree (for defaulting new features)
// 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({
features: hookFeatures,
@@ -533,6 +540,7 @@ export function BoardView() {
categorySuggestions={categorySuggestions}
branchSuggestions={branchSuggestions}
defaultSkipTests={defaultSkipTests}
defaultBranch={selectedWorktreeBranch}
isMaximized={isMaximized}
showProfilesOnly={showProfilesOnly}
aiProfiles={aiProfiles}
@@ -603,10 +611,24 @@ export function BoardView() {
open={showCreateWorktreeDialog}
onOpenChange={setShowCreateWorktreeDialog}
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);
// 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
useEffect(() => {
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]);
const handleSelectWorktree = async (worktree: WorktreeInfo) => {
// Simply select the worktree in the UI
setCurrentWorktree(projectPath, worktree.isMain ? null : worktree.path);
// Simply select the worktree in the UI with both path and branch
setCurrentWorktree(projectPath, worktree.isMain ? null : worktree.path, worktree.branch);
};
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
// currentWorktree is null for main, or the worktree path for others
const selectedWorktree = currentWorktree
? worktrees.find((w) => w.path === currentWorktree)
// currentWorktree.path is null for main, or the worktree path for others
const currentWorktreePath = currentWorktree?.path ?? null;
const selectedWorktree = currentWorktreePath
? worktrees.find((w) => w.path === currentWorktreePath)
: worktrees.find((w) => w.isMain);
// Render a worktree tab with branch selector (for main) and actions dropdown
const renderWorktreeTab = (worktree: WorktreeInfo) => {
// 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
? currentWorktree === null || currentWorktree === undefined
: worktree.path === currentWorktree;
? currentWorktree === null || currentWorktree === undefined || currentWorktree.path === null
: worktree.path === currentWorktreePath;
const isRunning = hasRunningFeatures(worktree);

View File

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

View File

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

View File

@@ -43,7 +43,12 @@ export function useBoardColumnFeatures({
// Determine the effective worktree path and branch for filtering
// If currentWorktreePath is null, we're on the main worktree
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) => {
// If feature has a running agent, always show it in "in_progress"
@@ -62,6 +67,11 @@ export function useBoardColumnFeatures({
} else if (f.worktreePath) {
// Has worktreePath - match by path
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 {
// Has branchName but no worktreePath - match by branch name
matchesWorktree = featureBranch === effectiveBranch;
@@ -76,10 +86,12 @@ export function useBoardColumnFeatures({
// Otherwise, use the feature's status (fallback to backlog for unknown statuses)
const status = f.status as ColumnId;
// Backlog items are always visible (they have no worktree assigned)
// For other statuses, filter by worktree
// Filter all items by worktree, including backlog
// This ensures backlog items with a branch assigned only show in that branch
if (status === "backlog") {
map.backlog.push(f);
if (matchesWorktree) {
map.backlog.push(f);
}
} else if (map[status]) {
// Only show if matches current worktree or has no worktree assigned
if (matchesWorktree) {

View File

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