Fix concurrency limits and remote branch fetching issues (#788)

* Changes from fix/bug-fixes

* feat: Refactor worktree iteration and improve error logging across services

* feat: Extract URL/port patterns to module level and fix abort condition

* fix: Improve IPv6 loopback handling, select component layout, and terminal UI

* feat: Add thinking level defaults and adjust list row padding

* Update apps/ui/src/store/app-store.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* feat: Add worktree-aware terminal creation and split options, fix npm security issues from audit

* feat: Add tracked remote detection to pull dialog flow

* feat: Add merge state tracking to git operations

* feat: Improve merge detection and add post-merge action preferences

* Update apps/ui/src/components/views/board-view/dialogs/git-pull-dialog.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update apps/ui/src/components/views/board-view/dialogs/git-pull-dialog.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: Pass merge detection info to stash reapplication and handle merge state consistently

* fix: Call onPulled callback in merge handlers and add validation checks

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
gsxdsm
2026-02-20 13:48:22 -08:00
committed by GitHub
parent 7df2182818
commit 0a5540c9a2
70 changed files with 4525 additions and 857 deletions

View File

@@ -298,6 +298,7 @@ const initialState: AppState = {
enableDependencyBlocking: true,
skipVerificationInAutoMode: false,
enableAiCommitMessages: true,
mergePostAction: null,
planUseSelectedWorktreeBranch: true,
addFeatureUseSelectedWorktreeBranch: false,
useWorktrees: true,
@@ -362,6 +363,8 @@ const initialState: AppState = {
defaultPlanningMode: 'skip' as PlanningMode,
defaultRequirePlanApproval: false,
defaultFeatureModel: DEFAULT_GLOBAL_SETTINGS.defaultFeatureModel,
defaultThinkingLevel: DEFAULT_GLOBAL_SETTINGS.defaultThinkingLevel ?? 'none',
defaultReasoningEffort: DEFAULT_GLOBAL_SETTINGS.defaultReasoningEffort ?? 'none',
pendingPlanApproval: null,
claudeRefreshInterval: 60,
claudeUsage: null,
@@ -1117,6 +1120,16 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
logger.error('Failed to sync enableAiCommitMessages:', error);
}
},
setMergePostAction: async (action) => {
set({ mergePostAction: action });
// Sync to server
try {
const httpApi = getHttpApiClient();
await httpApi.put('/api/settings', { mergePostAction: action });
} catch (error) {
logger.error('Failed to sync mergePostAction:', error);
}
},
setPlanUseSelectedWorktreeBranch: async (enabled) => {
set({ planUseSelectedWorktreeBranch: enabled });
// Sync to server
@@ -2313,6 +2326,28 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
setDefaultRequirePlanApproval: (require) => set({ defaultRequirePlanApproval: require }),
setDefaultFeatureModel: (entry) => set({ defaultFeatureModel: entry }),
setDefaultThinkingLevel: async (level) => {
set({ defaultThinkingLevel: level });
// Sync to server
try {
const httpApi = getHttpApiClient();
await httpApi.put('/api/settings', { defaultThinkingLevel: level });
} catch (error) {
logger.error('Failed to sync defaultThinkingLevel:', error);
}
},
setDefaultReasoningEffort: async (effort) => {
set({ defaultReasoningEffort: effort });
// Sync to server
try {
const httpApi = getHttpApiClient();
await httpApi.put('/api/settings', { defaultReasoningEffort: effort });
} catch (error) {
logger.error('Failed to sync defaultReasoningEffort:', error);
}
},
// Plan Approval actions
setPendingPlanApproval: (approval) => set({ pendingPlanApproval: approval }),

View File

@@ -21,6 +21,8 @@ import type {
ClaudeApiProfile,
ClaudeCompatibleProvider,
SidebarStyle,
ThinkingLevel,
ReasoningEffort,
} from '@automaker/types';
import type {
@@ -127,6 +129,7 @@ export interface AppState {
enableDependencyBlocking: boolean; // When true, show blocked badges and warnings for features with incomplete dependencies (default: true)
skipVerificationInAutoMode: boolean; // When true, auto-mode grabs features even if dependencies are not verified (only checks they're not running)
enableAiCommitMessages: boolean; // When true, auto-generate commit messages using AI when opening commit dialog
mergePostAction: 'commit' | 'manual' | null; // User's preferred action after a clean merge (null = ask every time)
planUseSelectedWorktreeBranch: boolean; // When true, Plan dialog creates features on the currently selected worktree branch
addFeatureUseSelectedWorktreeBranch: boolean; // When true, Add Feature dialog defaults to custom mode with selected worktree branch
@@ -175,6 +178,10 @@ export interface AppState {
phaseModels: PhaseModelConfig;
favoriteModels: string[];
// Default thinking/reasoning levels for two-stage model selector primary button
defaultThinkingLevel: ThinkingLevel;
defaultReasoningEffort: ReasoningEffort;
// Cursor CLI Settings (global)
enabledCursorModels: CursorModelId[]; // Which Cursor models are available in feature modal
cursorDefaultModel: CursorModelId; // Default Cursor model selection
@@ -488,6 +495,7 @@ export interface AppActions {
setEnableDependencyBlocking: (enabled: boolean) => void;
setSkipVerificationInAutoMode: (enabled: boolean) => Promise<void>;
setEnableAiCommitMessages: (enabled: boolean) => Promise<void>;
setMergePostAction: (action: 'commit' | 'manual' | null) => Promise<void>;
setPlanUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
setAddFeatureUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
@@ -548,6 +556,8 @@ export interface AppActions {
setPhaseModels: (models: Partial<PhaseModelConfig>) => Promise<void>;
resetPhaseModels: () => Promise<void>;
toggleFavoriteModel: (modelId: string) => void;
setDefaultThinkingLevel: (level: ThinkingLevel) => void;
setDefaultReasoningEffort: (effort: ReasoningEffort) => void;
// Cursor CLI Settings actions
setEnabledCursorModels: (models: CursorModelId[]) => void;

View File

@@ -35,6 +35,8 @@ interface UICacheState {
cachedWorktreePanelCollapsed: boolean;
/** Collapsed nav sections */
cachedCollapsedNavSections: Record<string, boolean>;
/** Selected worktree per project (path + branch) for instant restore on PWA reload */
cachedCurrentWorktreeByProject: Record<string, { path: string | null; branch: string }>;
}
interface UICacheActions {
@@ -52,19 +54,29 @@ export const useUICacheStore = create<UICacheState & UICacheActions>()(
cachedSidebarStyle: 'unified',
cachedWorktreePanelCollapsed: false,
cachedCollapsedNavSections: {},
cachedCurrentWorktreeByProject: {},
updateFromAppStore: (state) => set(state),
}),
{
name: STORE_NAME,
version: 1,
version: 2,
partialize: (state) => ({
cachedProjectId: state.cachedProjectId,
cachedSidebarOpen: state.cachedSidebarOpen,
cachedSidebarStyle: state.cachedSidebarStyle,
cachedWorktreePanelCollapsed: state.cachedWorktreePanelCollapsed,
cachedCollapsedNavSections: state.cachedCollapsedNavSections,
cachedCurrentWorktreeByProject: state.cachedCurrentWorktreeByProject,
}),
migrate: (persistedState: unknown, version: number) => {
const state = persistedState as Record<string, unknown>;
if (version < 2) {
// Migration from v1: add cachedCurrentWorktreeByProject
state.cachedCurrentWorktreeByProject = {};
}
return state as unknown as UICacheState & UICacheActions;
},
}
)
);
@@ -82,6 +94,7 @@ export function syncUICache(appState: {
sidebarStyle?: 'unified' | 'discord';
worktreePanelCollapsed?: boolean;
collapsedNavSections?: Record<string, boolean>;
currentWorktreeByProject?: Record<string, { path: string | null; branch: string }>;
}): void {
const update: Partial<UICacheState> = {};
@@ -100,6 +113,9 @@ export function syncUICache(appState: {
if ('collapsedNavSections' in appState) {
update.cachedCollapsedNavSections = appState.collapsedNavSections;
}
if ('currentWorktreeByProject' in appState) {
update.cachedCurrentWorktreeByProject = appState.currentWorktreeByProject;
}
if (Object.keys(update).length > 0) {
useUICacheStore.getState().updateFromAppStore(update);
@@ -142,6 +158,15 @@ export function restoreFromUICache(
collapsedNavSections: cache.cachedCollapsedNavSections,
};
// Restore last selected worktree per project so the board doesn't
// reset to main branch after PWA memory eviction or tab discard.
if (
cache.cachedCurrentWorktreeByProject &&
Object.keys(cache.cachedCurrentWorktreeByProject).length > 0
) {
stateUpdate.currentWorktreeByProject = cache.cachedCurrentWorktreeByProject;
}
// Restore the project context when the project object is available.
// When projects are not yet loaded (empty array), currentProject remains
// null and will be properly set later by hydrateStoreFromSettings().