mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-24 00:13:07 +00:00
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:
@@ -28,7 +28,11 @@ import { cn } from '@/lib/utils';
|
||||
import { modelSupportsThinking } from '@/lib/utils';
|
||||
import { useAppStore, ThinkingLevel, FeatureImage, PlanningMode, Feature } from '@/store/app-store';
|
||||
import type { ReasoningEffort, PhaseModelEntry, AgentModel } from '@automaker/types';
|
||||
import { supportsReasoningEffort, isAdaptiveThinkingModel } from '@automaker/types';
|
||||
import {
|
||||
supportsReasoningEffort,
|
||||
isAdaptiveThinkingModel,
|
||||
getThinkingLevelsForModel,
|
||||
} from '@automaker/types';
|
||||
import {
|
||||
PrioritySelector,
|
||||
WorkModeSelector,
|
||||
@@ -211,6 +215,7 @@ export function AddFeatureDialog({
|
||||
defaultRequirePlanApproval,
|
||||
useWorktrees,
|
||||
defaultFeatureModel,
|
||||
defaultThinkingLevel,
|
||||
currentProject,
|
||||
} = useAppStore();
|
||||
|
||||
@@ -240,7 +245,22 @@ export function AddFeatureDialog({
|
||||
);
|
||||
setPlanningMode(defaultPlanningMode);
|
||||
setRequirePlanApproval(defaultRequirePlanApproval);
|
||||
setModelEntry(effectiveDefaultFeatureModel);
|
||||
|
||||
// Apply defaultThinkingLevel from settings to the model entry.
|
||||
// This ensures the "Quick-Select Defaults" thinking level setting is respected
|
||||
// even when the user doesn't change the model in the dropdown.
|
||||
const modelId =
|
||||
typeof effectiveDefaultFeatureModel.model === 'string'
|
||||
? effectiveDefaultFeatureModel.model
|
||||
: '';
|
||||
const availableLevels = getThinkingLevelsForModel(modelId);
|
||||
const effectiveThinkingLevel = availableLevels.includes(defaultThinkingLevel)
|
||||
? defaultThinkingLevel
|
||||
: availableLevels[0];
|
||||
setModelEntry({
|
||||
...effectiveDefaultFeatureModel,
|
||||
thinkingLevel: effectiveThinkingLevel,
|
||||
});
|
||||
|
||||
// Initialize description history (empty for new feature)
|
||||
setDescriptionHistory([]);
|
||||
@@ -269,6 +289,7 @@ export function AddFeatureDialog({
|
||||
defaultPlanningMode,
|
||||
defaultRequirePlanApproval,
|
||||
effectiveDefaultFeatureModel,
|
||||
defaultThinkingLevel,
|
||||
useWorktrees,
|
||||
selectedNonMainWorktreeBranch,
|
||||
forceCurrentBranchMode,
|
||||
@@ -394,7 +415,19 @@ export function AddFeatureDialog({
|
||||
// When a non-main worktree is selected, use its branch name for custom mode
|
||||
setBranchName(selectedNonMainWorktreeBranch || '');
|
||||
setPriority(2);
|
||||
setModelEntry(effectiveDefaultFeatureModel);
|
||||
// Apply defaultThinkingLevel to the model entry (same logic as dialog open)
|
||||
const resetModelId =
|
||||
typeof effectiveDefaultFeatureModel.model === 'string'
|
||||
? effectiveDefaultFeatureModel.model
|
||||
: '';
|
||||
const resetAvailableLevels = getThinkingLevelsForModel(resetModelId);
|
||||
const resetThinkingLevel = resetAvailableLevels.includes(defaultThinkingLevel)
|
||||
? defaultThinkingLevel
|
||||
: resetAvailableLevels[0];
|
||||
setModelEntry({
|
||||
...effectiveDefaultFeatureModel,
|
||||
thinkingLevel: resetThinkingLevel,
|
||||
});
|
||||
setWorkMode(
|
||||
getDefaultWorkMode(useWorktrees, selectedNonMainWorktreeBranch, forceCurrentBranchMode)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { GitPullRequest } from 'lucide-react';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
interface WorktreeInfo {
|
||||
path: string;
|
||||
branch: string;
|
||||
isMain: boolean;
|
||||
pr?: {
|
||||
number: number;
|
||||
url: string;
|
||||
title: string;
|
||||
state: string;
|
||||
createdAt: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ChangePRNumberDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
worktree: WorktreeInfo | null;
|
||||
projectPath: string | null;
|
||||
onChanged: () => void;
|
||||
}
|
||||
|
||||
export function ChangePRNumberDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
worktree,
|
||||
projectPath,
|
||||
onChanged,
|
||||
}: ChangePRNumberDialogProps) {
|
||||
const [prNumberInput, setPrNumberInput] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Initialize with current PR number when dialog opens
|
||||
useEffect(() => {
|
||||
if (open && worktree?.pr?.number) {
|
||||
setPrNumberInput(String(worktree.pr.number));
|
||||
} else if (open) {
|
||||
setPrNumberInput('');
|
||||
}
|
||||
setError(null);
|
||||
}, [open, worktree]);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!worktree) return;
|
||||
|
||||
const trimmed = prNumberInput.trim();
|
||||
if (!/^\d+$/.test(trimmed)) {
|
||||
setError('Please enter a valid positive PR number');
|
||||
return;
|
||||
}
|
||||
const prNumber = Number(trimmed);
|
||||
if (prNumber <= 0) {
|
||||
setError('Please enter a valid positive PR number');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api?.worktree?.updatePRNumber) {
|
||||
setError('Worktree API not available');
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await api.worktree.updatePRNumber(
|
||||
worktree.path,
|
||||
prNumber,
|
||||
projectPath || undefined
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
const prInfo = result.result?.prInfo;
|
||||
toast.success('PR tracking updated', {
|
||||
description: prInfo?.title
|
||||
? `Now tracking PR #${prNumber}: ${prInfo.title}`
|
||||
: `Now tracking PR #${prNumber}`,
|
||||
});
|
||||
onOpenChange(false);
|
||||
onChanged();
|
||||
} else {
|
||||
setError(result.error || 'Failed to update PR number');
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to update PR number');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [worktree, prNumberInput, projectPath, onOpenChange, onChanged]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !isLoading) {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}
|
||||
},
|
||||
[isLoading, handleSubmit]
|
||||
);
|
||||
|
||||
if (!worktree) return null;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (!isLoading) {
|
||||
onOpenChange(isOpen);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent className="sm:max-w-[400px]" onKeyDown={handleKeyDown}>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<GitPullRequest className="w-5 h-5" />
|
||||
Change Tracked PR Number
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Update which pull request number is tracked for{' '}
|
||||
<code className="font-mono bg-muted px-1 rounded">{worktree.branch}</code>.
|
||||
{worktree.pr && (
|
||||
<span className="block mt-1 text-xs">
|
||||
Currently tracking PR #{worktree.pr.number}
|
||||
</span>
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-2 space-y-3">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pr-number">Pull Request Number</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-sm">#</span>
|
||||
<Input
|
||||
id="pr-number"
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
placeholder="e.g. 42"
|
||||
value={prNumberInput}
|
||||
onChange={(e) => {
|
||||
setPrNumberInput(e.target.value);
|
||||
setError(null);
|
||||
}}
|
||||
disabled={isLoading}
|
||||
autoFocus
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Enter the GitHub PR number to associate with this worktree. The PR info will be
|
||||
fetched from GitHub if available.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{error && <p className="text-sm text-destructive">{error}</p>}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isLoading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSubmit} disabled={isLoading || !prNumberInput.trim()}>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Spinner size="xs" className="mr-2" />
|
||||
Updating...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<GitPullRequest className="w-4 h-4 mr-2" />
|
||||
Update PR
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { toast } from 'sonner';
|
||||
import { useWorktreeBranches } from '@/hooks/queries';
|
||||
import { ModelOverrideTrigger, useModelOverride } from '@/components/shared';
|
||||
import { resolveModelString } from '@automaker/model-resolver';
|
||||
|
||||
interface RemoteInfo {
|
||||
name: string;
|
||||
@@ -313,7 +314,7 @@ export function CreatePRDialog({
|
||||
const result = await api.worktree.generatePRDescription(
|
||||
worktree.path,
|
||||
branchNameForApi,
|
||||
prDescriptionModelOverride.effectiveModel,
|
||||
resolveModelString(prDescriptionModelOverride.effectiveModel),
|
||||
prDescriptionModelOverride.effectiveModelEntry.thinkingLevel,
|
||||
prDescriptionModelOverride.effectiveModelEntry.providerId
|
||||
);
|
||||
@@ -501,7 +502,7 @@ export function CreatePRDialog({
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-[550px] flex flex-col">
|
||||
<DialogContent className="sm:max-w-[550px] max-h-[85vh] flex flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<GitPullRequest className="w-5 h-5" />
|
||||
|
||||
@@ -25,6 +25,7 @@ export { ViewStashesDialog } from './view-stashes-dialog';
|
||||
export { StashApplyConflictDialog } from './stash-apply-conflict-dialog';
|
||||
export { CherryPickDialog } from './cherry-pick-dialog';
|
||||
export { GitPullDialog } from './git-pull-dialog';
|
||||
export { ChangePRNumberDialog } from './change-pr-number-dialog';
|
||||
export {
|
||||
BranchConflictDialog,
|
||||
type BranchConflictData,
|
||||
|
||||
Reference in New Issue
Block a user