Feature: worktree view customization and stability fixes (#805)

* Changes from feature/worktree-view-customization

* Feature: Git sync, set-tracking, and push divergence handling (#796)

* Add quick-add feature with improved workflows (#802)

* Changes from feature/quick-add

* feat: Clarify system prompt and improve error handling across services. Address PR Feedback

* feat: Improve PR description parsing and refactor event handling

* feat: Add context options to pipeline orchestrator initialization

* fix: Deduplicate React and handle CJS interop for use-sync-external-store

Resolve "Cannot read properties of null (reading 'useState')" errors by
deduplicating React/react-dom and ensuring use-sync-external-store is
bundled together with React to prevent CJS packages from resolving to
different React instances.

* Changes from feature/worktree-view-customization

* refactor: Remove unused worktree swap and highlight props

* refactor: Consolidate feature completion logic and improve thinking level defaults

* feat: Increase max turn limit to 10000

- Update DEFAULT_MAX_TURNS from 1000 to 10000 in settings-helpers.ts and agent-executor.ts
- Update MAX_ALLOWED_TURNS from 2000 to 10000 in settings-helpers.ts
- Update UI clamping logic from 2000 to 10000 in app-store.ts
- Update fallback values from 1000 to 10000 in use-settings-sync.ts
- Update default value from 1000 to 10000 in DEFAULT_GLOBAL_SETTINGS
- Update documentation to reflect new range: 1-10000

Allows agents to perform up to 10000 turns for complex feature execution.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: Add model resolution, improve session handling, and enhance UI stability

* refactor: Remove unused sync and tracking branch props from worktree components

* feat: Add PR number update functionality to worktrees. Address pr feedback

* feat: Optimize Gemini CLI startup and add tool result tracking

* refactor: Improve error handling and simplify worktree task cleanup

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-02-23 20:31:25 -08:00
committed by GitHub
parent e7504b247f
commit 0330c70261
72 changed files with 3667 additions and 1173 deletions

View File

@@ -36,6 +36,7 @@ import {
DEFAULT_COPILOT_MODEL,
DEFAULT_MAX_CONCURRENCY,
DEFAULT_GLOBAL_SETTINGS,
getThinkingLevelsForModel,
} from '@automaker/types';
// Import types from modular type files
@@ -371,9 +372,9 @@ const initialState: AppState = {
defaultPlanningMode: 'skip' as PlanningMode,
defaultRequirePlanApproval: false,
defaultFeatureModel: DEFAULT_GLOBAL_SETTINGS.defaultFeatureModel,
defaultThinkingLevel: DEFAULT_GLOBAL_SETTINGS.defaultThinkingLevel ?? 'none',
defaultThinkingLevel: DEFAULT_GLOBAL_SETTINGS.defaultThinkingLevel ?? 'adaptive',
defaultReasoningEffort: DEFAULT_GLOBAL_SETTINGS.defaultReasoningEffort ?? 'none',
defaultMaxTurns: DEFAULT_GLOBAL_SETTINGS.defaultMaxTurns ?? 1000,
defaultMaxTurns: DEFAULT_GLOBAL_SETTINGS.defaultMaxTurns ?? 10000,
pendingPlanApproval: null,
claudeRefreshInterval: 60,
claudeUsage: null,
@@ -396,6 +397,10 @@ const initialState: AppState = {
autoDismissInitScriptIndicatorByProject: {},
useWorktreesByProject: {},
worktreeCopyFilesByProject: {},
pinnedWorktreesCountByProject: {},
pinnedWorktreeBranchesByProject: {},
worktreeDropdownThresholdByProject: {},
alwaysUseWorktreeDropdownByProject: {},
worktreePanelCollapsed: false,
lastProjectDir: '',
recentFolders: [],
@@ -2453,7 +2458,20 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
setDefaultFeatureModel: (entry) => set({ defaultFeatureModel: entry }),
setDefaultThinkingLevel: async (level) => {
set({ defaultThinkingLevel: level });
const currentModel = get().defaultFeatureModel;
const modelId = currentModel.model;
const availableLevels = getThinkingLevelsForModel(modelId);
// Also update defaultFeatureModel's thinkingLevel if compatible
if (availableLevels.includes(level)) {
set({
defaultThinkingLevel: level,
defaultFeatureModel: { ...currentModel, thinkingLevel: level },
});
} else {
set({ defaultThinkingLevel: level });
}
// Sync to server
try {
const httpApi = getHttpApiClient();
@@ -2478,7 +2496,7 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
// Guard against NaN/Infinity before flooring and clamping
const safeValue = Number.isFinite(maxTurns) ? maxTurns : 1;
// Clamp to valid range
const clamped = Math.max(1, Math.min(2000, Math.floor(safeValue)));
const clamped = Math.max(1, Math.min(10000, Math.floor(safeValue)));
set({ defaultMaxTurns: clamped });
// Sync to server
try {
@@ -2641,6 +2659,65 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
})),
getWorktreeCopyFiles: (projectPath) => get().worktreeCopyFilesByProject[projectPath] ?? [],
// Worktree Display Settings actions
setPinnedWorktreesCount: (projectPath, count) =>
set((state) => ({
pinnedWorktreesCountByProject: {
...state.pinnedWorktreesCountByProject,
[projectPath]: count,
},
})),
getPinnedWorktreesCount: (projectPath) => get().pinnedWorktreesCountByProject[projectPath] ?? 0,
setPinnedWorktreeBranches: (projectPath, branches) =>
set((state) => ({
pinnedWorktreeBranchesByProject: {
...state.pinnedWorktreeBranchesByProject,
[projectPath]: branches,
},
})),
getPinnedWorktreeBranches: (projectPath) =>
get().pinnedWorktreeBranchesByProject[projectPath] ?? [],
swapPinnedWorktreeBranch: (projectPath, slotIndex, newBranch) =>
set((state) => {
const src = state.pinnedWorktreeBranchesByProject[projectPath] ?? [];
// Pre-fill up to slotIndex to prevent sparse holes
const current: string[] = Array.from(
{ length: Math.max(src.length, slotIndex + 1) },
(_, i) => src[i] ?? ''
);
// If the new branch is already in another slot, swap them (only when newBranch is non-empty)
const existingIndex = newBranch !== '' ? current.indexOf(newBranch) : -1;
if (existingIndex !== -1 && existingIndex !== slotIndex) {
// Swap: put the old branch from this slot into the other slot
current[existingIndex] = current[slotIndex];
}
current[slotIndex] = newBranch;
return {
pinnedWorktreeBranchesByProject: {
...state.pinnedWorktreeBranchesByProject,
[projectPath]: current,
},
};
}),
setWorktreeDropdownThreshold: (projectPath, threshold) =>
set((state) => ({
worktreeDropdownThresholdByProject: {
...state.worktreeDropdownThresholdByProject,
[projectPath]: threshold,
},
})),
getWorktreeDropdownThreshold: (projectPath) =>
get().worktreeDropdownThresholdByProject[projectPath] ?? 3,
setAlwaysUseWorktreeDropdown: (projectPath, always) =>
set((state) => ({
alwaysUseWorktreeDropdownByProject: {
...state.alwaysUseWorktreeDropdownByProject,
[projectPath]: always,
},
})),
getAlwaysUseWorktreeDropdown: (projectPath) =>
get().alwaysUseWorktreeDropdownByProject[projectPath] ?? true,
// UI State actions
setWorktreePanelCollapsed: (collapsed) => set({ worktreePanelCollapsed: collapsed }),
setLastProjectDir: (dir) => set({ lastProjectDir: dir }),

View File

@@ -370,6 +370,17 @@ export interface AppState {
// List of relative file paths to copy from project root into new worktrees
worktreeCopyFilesByProject: Record<string, string[]>;
// Worktree Display Settings (per-project, keyed by project path)
// Number of worktrees always visible (pinned) without expanding a dropdown (default: 1)
pinnedWorktreesCountByProject: Record<string, number>;
// Explicit list of branch names assigned to pinned slots (ordered)
// When set, these branches are shown in the pinned slots instead of using default ordering
pinnedWorktreeBranchesByProject: Record<string, string[]>;
// Minimum number of worktrees before the list collapses into a dropdown (default: 3)
worktreeDropdownThresholdByProject: Record<string, number>;
// Always use dropdown layout regardless of worktree count (default: false)
alwaysUseWorktreeDropdownByProject: Record<string, boolean>;
// UI State (previously in localStorage, now synced via API)
/** Whether worktree panel is collapsed in board view */
worktreePanelCollapsed: boolean;
@@ -814,6 +825,17 @@ export interface AppActions {
setWorktreeCopyFiles: (projectPath: string, files: string[]) => void;
getWorktreeCopyFiles: (projectPath: string) => string[];
// Worktree Display Settings actions (per-project)
setPinnedWorktreesCount: (projectPath: string, count: number) => void;
getPinnedWorktreesCount: (projectPath: string) => number;
setPinnedWorktreeBranches: (projectPath: string, branches: string[]) => void;
getPinnedWorktreeBranches: (projectPath: string) => string[];
swapPinnedWorktreeBranch: (projectPath: string, slotIndex: number, newBranch: string) => void;
setWorktreeDropdownThreshold: (projectPath: string, threshold: number) => void;
getWorktreeDropdownThreshold: (projectPath: string) => number;
setAlwaysUseWorktreeDropdown: (projectPath: string, always: boolean) => void;
getAlwaysUseWorktreeDropdown: (projectPath: string) => boolean;
// UI State actions (previously in localStorage, now synced via API)
setWorktreePanelCollapsed: (collapsed: boolean) => void;
setLastProjectDir: (dir: string) => void;