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

@@ -906,6 +906,7 @@ export function useAutoMode(worktree?: WorktreeInfo) {
if (result.success) {
removeRunningTask(currentProject.id, branchName, featureId);
logger.info('Feature stopped successfully:', featureId);
addAutoModeActivity({
featureId,

View File

@@ -15,6 +15,7 @@ interface UseElectronAgentOptions {
model?: string;
thinkingLevel?: string;
onToolUse?: (toolName: string, toolInput: unknown) => void;
onToolResult?: (toolName: string, result: unknown) => void;
}
// Server-side queued prompt type
@@ -72,6 +73,7 @@ export function useElectronAgent({
model,
thinkingLevel,
onToolUse,
onToolResult,
}: UseElectronAgentOptions): UseElectronAgentResult {
const [messages, setMessages] = useState<Message[]>([]);
const [isProcessing, setIsProcessing] = useState(false);
@@ -308,6 +310,12 @@ export function useElectronAgent({
onToolUse?.(event.tool.name, event.tool.input);
break;
case 'tool_result':
// Tool completed - surface result via onToolResult callback
logger.info('Tool result:', event.tool.name);
onToolResult?.(event.tool.name, event.tool.input);
break;
case 'complete':
// Agent finished processing for THIS session
logger.info('Processing complete for session:', sessionId);
@@ -366,7 +374,7 @@ export function useElectronAgent({
unsubscribeRef.current = null;
}
};
}, [sessionId, onToolUse]);
}, [sessionId, onToolUse, onToolResult]);
// Send a message to the agent
const sendMessage = useCallback(

View File

@@ -26,6 +26,10 @@ export function useProjectSettingsLoader() {
(state) => state.setAutoDismissInitScriptIndicator
);
const setWorktreeCopyFiles = useAppStore((state) => state.setWorktreeCopyFiles);
const setProjectUseWorktrees = useAppStore((state) => state.setProjectUseWorktrees);
const setPinnedWorktreesCount = useAppStore((state) => state.setPinnedWorktreesCount);
const setWorktreeDropdownThreshold = useAppStore((state) => state.setWorktreeDropdownThreshold);
const setAlwaysUseWorktreeDropdown = useAppStore((state) => state.setAlwaysUseWorktreeDropdown);
const appliedProjectRef = useRef<{ path: string; dataUpdatedAt: number } | null>(null);
@@ -100,6 +104,24 @@ export function useProjectSettingsLoader() {
setWorktreeCopyFiles(projectPath, settings.worktreeCopyFiles);
}
// Apply useWorktrees if present
if (settings.useWorktrees !== undefined) {
setProjectUseWorktrees(projectPath, settings.useWorktrees);
}
// Apply worktree display settings if present
if (settings.pinnedWorktreesCount !== undefined) {
setPinnedWorktreesCount(projectPath, settings.pinnedWorktreesCount);
}
if (settings.worktreeDropdownThreshold !== undefined) {
setWorktreeDropdownThreshold(projectPath, settings.worktreeDropdownThreshold);
}
if (settings.alwaysUseWorktreeDropdown !== undefined) {
setAlwaysUseWorktreeDropdown(projectPath, settings.alwaysUseWorktreeDropdown);
}
// Apply activeClaudeApiProfileId and phaseModelOverrides if present
// These are stored directly on the project, so we need to update both
// currentProject AND the projects array to keep them in sync
@@ -167,5 +189,9 @@ export function useProjectSettingsLoader() {
setDefaultDeleteBranch,
setAutoDismissInitScriptIndicator,
setWorktreeCopyFiles,
setProjectUseWorktrees,
setPinnedWorktreesCount,
setWorktreeDropdownThreshold,
setAlwaysUseWorktreeDropdown,
]);
}

View File

@@ -750,6 +750,7 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
defaultRequirePlanApproval: settings.defaultRequirePlanApproval ?? false,
defaultFeatureModel: migratePhaseModelEntry(settings.defaultFeatureModel) ?? {
model: 'claude-opus',
thinkingLevel: 'adaptive',
},
muteDoneSound: settings.muteDoneSound ?? false,
disableSplashScreen: settings.disableSplashScreen ?? false,
@@ -759,7 +760,7 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
enhancementModel: settings.enhancementModel ?? 'claude-sonnet',
validationModel: settings.validationModel ?? 'claude-opus',
phaseModels: { ...DEFAULT_PHASE_MODELS, ...(settings.phaseModels ?? current.phaseModels) },
defaultThinkingLevel: settings.defaultThinkingLevel ?? 'none',
defaultThinkingLevel: settings.defaultThinkingLevel ?? 'adaptive',
defaultReasoningEffort: settings.defaultReasoningEffort ?? 'none',
enabledCursorModels: allCursorModels, // Always use ALL cursor models
cursorDefaultModel: sanitizedCursorDefaultModel,
@@ -805,7 +806,11 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
// (error boundary reloads → restores same bad path → crash again).
// The use-worktrees validation effect will re-discover valid worktrees
// from the server once they load.
currentWorktreeByProject: sanitizeWorktreeByProject(settings.currentWorktreeByProject),
currentWorktreeByProject: Object.fromEntries(
Object.entries(sanitizeWorktreeByProject(settings.currentWorktreeByProject)).filter(
([, worktree]) => worktree.path === null
)
),
// UI State
worktreePanelCollapsed: settings.worktreePanelCollapsed ?? false,
lastProjectDir: settings.lastProjectDir ?? '',

View File

@@ -75,6 +75,8 @@ const SETTINGS_FIELDS_TO_SYNC = [
'enhancementModel',
'validationModel',
'phaseModels',
'defaultThinkingLevel',
'defaultReasoningEffort',
'enabledCursorModels',
'cursorDefaultModel',
'enabledOpencodeModels',
@@ -781,9 +783,9 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
defaultRequirePlanApproval: serverSettings.defaultRequirePlanApproval,
defaultFeatureModel: serverSettings.defaultFeatureModel
? migratePhaseModelEntry(serverSettings.defaultFeatureModel)
: { model: 'claude-opus' },
: { model: 'claude-opus', thinkingLevel: 'adaptive' },
muteDoneSound: serverSettings.muteDoneSound,
defaultMaxTurns: serverSettings.defaultMaxTurns ?? 1000,
defaultMaxTurns: serverSettings.defaultMaxTurns ?? 10000,
disableSplashScreen: serverSettings.disableSplashScreen ?? false,
serverLogLevel: serverSettings.serverLogLevel ?? 'info',
enableRequestLogging: serverSettings.enableRequestLogging ?? true,
@@ -793,6 +795,8 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
...DEFAULT_PHASE_MODELS,
...(migratedPhaseModels ?? serverSettings.phaseModels),
},
defaultThinkingLevel: serverSettings.defaultThinkingLevel ?? 'adaptive',
defaultReasoningEffort: serverSettings.defaultReasoningEffort ?? 'none',
enabledCursorModels: allCursorModels, // Always use ALL cursor models
cursorDefaultModel: sanitizedCursorDefault,
enabledOpencodeModels: sanitizedEnabledOpencodeModels,