mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
feat: enhance auto mode functionality with worktree support
- Updated auto mode handlers to support branch-specific operations, allowing for better management of features across different worktrees. - Introduced normalization of branch names to handle undefined values gracefully. - Enhanced status and response messages to reflect the current worktree context. - Updated the auto mode service to manage state and concurrency settings per worktree, improving user experience and flexibility. - Added UI elements to display current max concurrency for auto mode in both board and mobile views. This update aims to streamline the auto mode experience, making it more intuitive for users working with multiple branches and worktrees.
This commit is contained in:
@@ -88,8 +88,8 @@ const logger = createLogger('Board');
|
||||
export function BoardView() {
|
||||
const {
|
||||
currentProject,
|
||||
maxConcurrency,
|
||||
setMaxConcurrency,
|
||||
maxConcurrency: legacyMaxConcurrency,
|
||||
setMaxConcurrency: legacySetMaxConcurrency,
|
||||
defaultSkipTests,
|
||||
specCreatingForProject,
|
||||
setSpecCreatingForProject,
|
||||
@@ -261,11 +261,6 @@ export function BoardView() {
|
||||
loadPipelineConfig();
|
||||
}, [currentProject?.path, setPipelineConfig]);
|
||||
|
||||
// Auto mode hook
|
||||
const autoMode = useAutoMode();
|
||||
// Get runningTasks from the hook (scoped to current project)
|
||||
const runningAutoTasks = autoMode.runningTasks;
|
||||
|
||||
// Window state hook for compact dialog mode
|
||||
const { isMaximized } = useWindowState();
|
||||
|
||||
@@ -374,14 +369,6 @@ export function BoardView() {
|
||||
[hookFeatures, updateFeature, persistFeatureUpdate]
|
||||
);
|
||||
|
||||
// Get in-progress features for keyboard shortcuts (needed before actions hook)
|
||||
const inProgressFeaturesForShortcuts = useMemo(() => {
|
||||
return hookFeatures.filter((f) => {
|
||||
const isRunning = runningAutoTasks.includes(f.id);
|
||||
return isRunning || f.status === 'in_progress';
|
||||
});
|
||||
}, [hookFeatures, runningAutoTasks]);
|
||||
|
||||
// Get current worktree info (path) for filtering features
|
||||
// This needs to be before useBoardActions so we can pass currentWorktreeBranch
|
||||
const currentWorktreeInfo = currentProject ? getCurrentWorktree(currentProject.path) : null;
|
||||
@@ -407,6 +394,16 @@ export function BoardView() {
|
||||
}
|
||||
}, [worktrees, currentWorktreePath]);
|
||||
|
||||
// Auto mode hook - pass current worktree to get worktree-specific state
|
||||
// Must be after selectedWorktree is defined
|
||||
const autoMode = useAutoMode(selectedWorktree ?? undefined);
|
||||
// Get runningTasks from the hook (scoped to current project/worktree)
|
||||
const runningAutoTasks = autoMode.runningTasks;
|
||||
// Get worktree-specific maxConcurrency from the hook
|
||||
const maxConcurrency = autoMode.maxConcurrency;
|
||||
// Get worktree-specific setter
|
||||
const setMaxConcurrencyForWorktree = useAppStore((state) => state.setMaxConcurrencyForWorktree);
|
||||
|
||||
// Get the current branch from the selected worktree (not from store which may be stale)
|
||||
const currentWorktreeBranch = selectedWorktree?.branch ?? null;
|
||||
|
||||
@@ -415,6 +412,15 @@ export function BoardView() {
|
||||
const selectedWorktreeBranch =
|
||||
currentWorktreeBranch || worktrees.find((w) => w.isMain)?.branch || 'main';
|
||||
|
||||
// Get in-progress features for keyboard shortcuts (needed before actions hook)
|
||||
// Must be after runningAutoTasks is defined
|
||||
const inProgressFeaturesForShortcuts = useMemo(() => {
|
||||
return hookFeatures.filter((f) => {
|
||||
const isRunning = runningAutoTasks.includes(f.id);
|
||||
return isRunning || f.status === 'in_progress';
|
||||
});
|
||||
}, [hookFeatures, runningAutoTasks]);
|
||||
|
||||
// Calculate unarchived card counts per branch
|
||||
const branchCardCounts = useMemo(() => {
|
||||
// Use primary worktree branch as default for features without branchName
|
||||
@@ -512,14 +518,14 @@ export function BoardView() {
|
||||
|
||||
try {
|
||||
// Determine final branch name based on work mode:
|
||||
// - 'current': Empty string to clear branch assignment (work on main/current branch)
|
||||
// - 'current': Use selected worktree branch if available, otherwise undefined (work on main)
|
||||
// - 'auto': Auto-generate branch name based on current branch
|
||||
// - 'custom': Use the provided branch name
|
||||
let finalBranchName: string | undefined;
|
||||
|
||||
if (workMode === 'current') {
|
||||
// Empty string clears the branch assignment, moving features to main/current branch
|
||||
finalBranchName = '';
|
||||
// If a worktree is selected, use its branch; otherwise work on main (undefined = no branch assignment)
|
||||
finalBranchName = currentWorktreeBranch || undefined;
|
||||
} else if (workMode === 'auto') {
|
||||
// Auto-generate a branch name based on primary branch (main/master) and timestamp
|
||||
// Always use primary branch to avoid nested feature/feature/... paths
|
||||
@@ -605,6 +611,7 @@ export function BoardView() {
|
||||
exitSelectionMode,
|
||||
getPrimaryWorktreeBranch,
|
||||
addAndSelectWorktree,
|
||||
currentWorktreeBranch,
|
||||
setWorktreeRefreshKey,
|
||||
]
|
||||
);
|
||||
@@ -1127,7 +1134,21 @@ export function BoardView() {
|
||||
projectPath={currentProject.path}
|
||||
maxConcurrency={maxConcurrency}
|
||||
runningAgentsCount={runningAutoTasks.length}
|
||||
onConcurrencyChange={setMaxConcurrency}
|
||||
onConcurrencyChange={(newMaxConcurrency) => {
|
||||
if (currentProject && selectedWorktree) {
|
||||
const branchName = selectedWorktree.isMain ? null : selectedWorktree.branch;
|
||||
setMaxConcurrencyForWorktree(currentProject.id, branchName, newMaxConcurrency);
|
||||
// Also update backend if auto mode is running
|
||||
if (autoMode.isRunning) {
|
||||
// Restart auto mode with new concurrency (backend will handle this)
|
||||
autoMode.stop().then(() => {
|
||||
autoMode.start().catch((error) => {
|
||||
logger.error('[AutoMode] Failed to restart with new concurrency:', error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}}
|
||||
isAutoModeRunning={autoMode.isRunning}
|
||||
onAutoModeToggle={(enabled) => {
|
||||
if (enabled) {
|
||||
|
||||
@@ -182,6 +182,13 @@ export function BoardHeader({
|
||||
>
|
||||
Auto Mode
|
||||
</Label>
|
||||
<span
|
||||
className="text-[10px] font-medium text-muted-foreground bg-muted/60 px-1.5 py-0.5 rounded"
|
||||
data-testid="auto-mode-max-concurrency"
|
||||
title="Max concurrent agents"
|
||||
>
|
||||
{maxConcurrency}
|
||||
</span>
|
||||
<Switch
|
||||
id="auto-mode-toggle"
|
||||
checked={isAutoModeRunning}
|
||||
|
||||
@@ -80,6 +80,13 @@ export function HeaderMobileMenu({
|
||||
)}
|
||||
/>
|
||||
<span className="text-sm font-medium">Auto Mode</span>
|
||||
<span
|
||||
className="text-[10px] font-medium text-muted-foreground bg-muted/60 px-1.5 py-0.5 rounded"
|
||||
data-testid="mobile-auto-mode-max-concurrency"
|
||||
title="Max concurrent agents"
|
||||
>
|
||||
{maxConcurrency}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
|
||||
@@ -118,14 +118,14 @@ export function useBoardActions({
|
||||
const workMode = featureData.workMode || 'current';
|
||||
|
||||
// Determine final branch name based on work mode:
|
||||
// - 'current': No branch name, work on current branch (no worktree)
|
||||
// - 'current': Use selected worktree branch if available, otherwise undefined (work on main)
|
||||
// - 'auto': Auto-generate branch name based on current branch
|
||||
// - 'custom': Use the provided branch name
|
||||
let finalBranchName: string | undefined;
|
||||
|
||||
if (workMode === 'current') {
|
||||
// No worktree isolation - work directly on current branch
|
||||
finalBranchName = undefined;
|
||||
// If a worktree is selected, use its branch; otherwise work on main (undefined = no branch assignment)
|
||||
finalBranchName = currentWorktreeBranch || undefined;
|
||||
} else if (workMode === 'auto') {
|
||||
// Auto-generate a branch name based on primary branch (main/master) and timestamp
|
||||
// Always use primary branch to avoid nested feature/feature/... paths
|
||||
@@ -249,6 +249,7 @@ export function useBoardActions({
|
||||
onWorktreeAutoSelect,
|
||||
getPrimaryWorktreeBranch,
|
||||
features,
|
||||
currentWorktreeBranch,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -282,7 +283,8 @@ export function useBoardActions({
|
||||
let finalBranchName: string | undefined;
|
||||
|
||||
if (workMode === 'current') {
|
||||
finalBranchName = undefined;
|
||||
// If a worktree is selected, use its branch; otherwise work on main (undefined = no branch assignment)
|
||||
finalBranchName = currentWorktreeBranch || undefined;
|
||||
} else if (workMode === 'auto') {
|
||||
// Auto-generate a branch name based on primary branch (main/master) and timestamp
|
||||
// Always use primary branch to avoid nested feature/feature/... paths
|
||||
@@ -397,6 +399,7 @@ export function useBoardActions({
|
||||
onWorktreeCreated,
|
||||
getPrimaryWorktreeBranch,
|
||||
features,
|
||||
currentWorktreeBranch,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@@ -97,8 +97,25 @@ export function useBoardColumnFeatures({
|
||||
// Historically, we forced "running" features into in_progress so they never disappeared
|
||||
// during stale reload windows. With pipelines, a feature can legitimately be running while
|
||||
// its status is `pipeline_*`, so we must respect that status to render it in the right column.
|
||||
// NOTE: runningAutoTasks is already worktree-scoped, so if a feature is in runningAutoTasks,
|
||||
// it's already running for the current worktree. However, we still need to check matchesWorktree
|
||||
// to ensure the feature's branchName matches the current worktree's branch.
|
||||
if (isRunning) {
|
||||
if (!matchesWorktree) return;
|
||||
// If feature is running but doesn't match worktree, it might be a timing issue where
|
||||
// the feature was started for a different worktree. Still show it if it's running to
|
||||
// prevent disappearing features, but log a warning.
|
||||
if (!matchesWorktree) {
|
||||
// This can happen if:
|
||||
// 1. Feature was started for a different worktree (bug)
|
||||
// 2. Timing issue where branchName hasn't been set yet
|
||||
// 3. User switched worktrees while feature was starting
|
||||
// Still show it in in_progress to prevent it from disappearing
|
||||
console.debug(
|
||||
`Feature ${f.id} is running but branchName (${featureBranch}) doesn't match current worktree branch (${effectiveBranch}) - showing anyway to prevent disappearing`
|
||||
);
|
||||
map.in_progress.push(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.startsWith('pipeline_')) {
|
||||
if (!map[status]) map[status] = [];
|
||||
|
||||
@@ -196,13 +196,30 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
|
||||
|
||||
const { removeRunningTask } = useAppStore.getState();
|
||||
const projectId = currentProject.id;
|
||||
const projectPath = currentProject.path;
|
||||
|
||||
const unsubscribe = api.autoMode.onEvent((event) => {
|
||||
// Check if event is for the current project by matching projectPath
|
||||
const eventProjectPath = ('projectPath' in event && event.projectPath) as string | undefined;
|
||||
if (eventProjectPath && eventProjectPath !== projectPath) {
|
||||
// Event is for a different project, ignore it
|
||||
logger.debug(
|
||||
`Ignoring auto mode event for different project: ${eventProjectPath} (current: ${projectPath})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use event's projectPath or projectId if available, otherwise use current project
|
||||
// Board view only reacts to events for the currently selected project
|
||||
const eventProjectId = ('projectId' in event && event.projectId) || projectId;
|
||||
|
||||
if (event.type === 'auto_mode_feature_complete') {
|
||||
if (event.type === 'auto_mode_feature_start') {
|
||||
// Reload features when a feature starts to ensure status update (backlog -> in_progress) is reflected
|
||||
logger.info(
|
||||
`[BoardFeatures] Feature ${event.featureId} started for project ${projectPath}, reloading features to update status...`
|
||||
);
|
||||
loadFeatures();
|
||||
} else if (event.type === 'auto_mode_feature_complete') {
|
||||
// Reload features when a feature is completed
|
||||
logger.info('Feature completed, reloading features...');
|
||||
loadFeatures();
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
Terminal,
|
||||
SquarePlus,
|
||||
SplitSquareHorizontal,
|
||||
Zap,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -56,6 +57,8 @@ interface WorktreeActionsDropdownProps {
|
||||
gitRepoStatus: GitRepoStatus;
|
||||
/** When true, renders as a standalone button (not attached to another element) */
|
||||
standalone?: boolean;
|
||||
/** Whether auto mode is running for this worktree */
|
||||
isAutoModeRunning?: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onPull: (worktree: WorktreeInfo) => void;
|
||||
onPush: (worktree: WorktreeInfo) => void;
|
||||
@@ -73,6 +76,7 @@ interface WorktreeActionsDropdownProps {
|
||||
onOpenDevServerUrl: (worktree: WorktreeInfo) => void;
|
||||
onViewDevServerLogs: (worktree: WorktreeInfo) => void;
|
||||
onRunInitScript: (worktree: WorktreeInfo) => void;
|
||||
onToggleAutoMode?: (worktree: WorktreeInfo) => void;
|
||||
hasInitScript: boolean;
|
||||
}
|
||||
|
||||
@@ -88,6 +92,7 @@ export function WorktreeActionsDropdown({
|
||||
devServerInfo,
|
||||
gitRepoStatus,
|
||||
standalone = false,
|
||||
isAutoModeRunning = false,
|
||||
onOpenChange,
|
||||
onPull,
|
||||
onPush,
|
||||
@@ -105,6 +110,7 @@ export function WorktreeActionsDropdown({
|
||||
onOpenDevServerUrl,
|
||||
onViewDevServerLogs,
|
||||
onRunInitScript,
|
||||
onToggleAutoMode,
|
||||
hasInitScript,
|
||||
}: WorktreeActionsDropdownProps) {
|
||||
// Get available editors for the "Open In" submenu
|
||||
@@ -214,6 +220,26 @@ export function WorktreeActionsDropdown({
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
{/* Auto Mode toggle */}
|
||||
{onToggleAutoMode && (
|
||||
<>
|
||||
{isAutoModeRunning ? (
|
||||
<DropdownMenuItem onClick={() => onToggleAutoMode(worktree)} className="text-xs">
|
||||
<span className="flex items-center mr-2">
|
||||
<Zap className="w-3.5 h-3.5 text-yellow-500" />
|
||||
<span className="ml-0.5 h-2 w-2 rounded-full bg-green-500 animate-pulse" />
|
||||
</span>
|
||||
Stop Auto Mode
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={() => onToggleAutoMode(worktree)} className="text-xs">
|
||||
<Zap className="w-3.5 h-3.5 mr-2" />
|
||||
Start Auto Mode
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<TooltipWrapper showTooltip={!!gitOpsDisabledReason} tooltipContent={gitOpsDisabledReason}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => canPerformGitOps && onPull(worktree)}
|
||||
|
||||
@@ -29,6 +29,8 @@ interface WorktreeTabProps {
|
||||
aheadCount: number;
|
||||
behindCount: number;
|
||||
gitRepoStatus: GitRepoStatus;
|
||||
/** Whether auto mode is running for this worktree */
|
||||
isAutoModeRunning?: boolean;
|
||||
onSelectWorktree: (worktree: WorktreeInfo) => void;
|
||||
onBranchDropdownOpenChange: (open: boolean) => void;
|
||||
onActionsDropdownOpenChange: (open: boolean) => void;
|
||||
@@ -51,6 +53,7 @@ interface WorktreeTabProps {
|
||||
onOpenDevServerUrl: (worktree: WorktreeInfo) => void;
|
||||
onViewDevServerLogs: (worktree: WorktreeInfo) => void;
|
||||
onRunInitScript: (worktree: WorktreeInfo) => void;
|
||||
onToggleAutoMode?: (worktree: WorktreeInfo) => void;
|
||||
hasInitScript: boolean;
|
||||
}
|
||||
|
||||
@@ -75,6 +78,7 @@ export function WorktreeTab({
|
||||
aheadCount,
|
||||
behindCount,
|
||||
gitRepoStatus,
|
||||
isAutoModeRunning = false,
|
||||
onSelectWorktree,
|
||||
onBranchDropdownOpenChange,
|
||||
onActionsDropdownOpenChange,
|
||||
@@ -97,6 +101,7 @@ export function WorktreeTab({
|
||||
onOpenDevServerUrl,
|
||||
onViewDevServerLogs,
|
||||
onRunInitScript,
|
||||
onToggleAutoMode,
|
||||
hasInitScript,
|
||||
}: WorktreeTabProps) {
|
||||
let prBadge: JSX.Element | null = null;
|
||||
@@ -332,6 +337,26 @@ export function WorktreeTab({
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{isAutoModeRunning && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center justify-center h-7 px-1.5 rounded-none border-r-0',
|
||||
isSelected ? 'bg-primary text-primary-foreground' : 'bg-secondary/50'
|
||||
)}
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-green-500 animate-pulse" />
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Auto Mode Running</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<WorktreeActionsDropdown
|
||||
worktree={worktree}
|
||||
isSelected={isSelected}
|
||||
@@ -343,6 +368,7 @@ export function WorktreeTab({
|
||||
isDevServerRunning={isDevServerRunning}
|
||||
devServerInfo={devServerInfo}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunning}
|
||||
onOpenChange={onActionsDropdownOpenChange}
|
||||
onPull={onPull}
|
||||
onPush={onPush}
|
||||
@@ -360,6 +386,7 @@ export function WorktreeTab({
|
||||
onOpenDevServerUrl={onOpenDevServerUrl}
|
||||
onViewDevServerLogs={onViewDevServerLogs}
|
||||
onRunInitScript={onRunInitScript}
|
||||
onToggleAutoMode={onToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { GitBranch, Plus, RefreshCw } from 'lucide-react';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { cn, pathsEqual } from '@/lib/utils';
|
||||
import { pathsEqual } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { useIsMobile } from '@/hooks/use-media-query';
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
WorktreeActionsDropdown,
|
||||
BranchSwitchDropdown,
|
||||
} from './components';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
export function WorktreePanel({
|
||||
projectPath,
|
||||
@@ -50,7 +51,6 @@ export function WorktreePanel({
|
||||
|
||||
const {
|
||||
isStartingDevServer,
|
||||
getWorktreeKey,
|
||||
isDevServerRunning,
|
||||
getDevServerInfo,
|
||||
handleStartDevServer,
|
||||
@@ -92,6 +92,67 @@ export function WorktreePanel({
|
||||
features,
|
||||
});
|
||||
|
||||
// Auto-mode state management using the store
|
||||
// Use separate selectors to avoid creating new object references on each render
|
||||
const autoModeByWorktree = useAppStore((state) => state.autoModeByWorktree);
|
||||
const currentProject = useAppStore((state) => state.currentProject);
|
||||
|
||||
// Helper to generate worktree key for auto-mode (inlined to avoid selector issues)
|
||||
const getAutoModeWorktreeKey = useCallback(
|
||||
(projectId: string, branchName: string | null): string => {
|
||||
return `${projectId}::${branchName ?? '__main__'}`;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Helper to check if auto-mode is running for a specific worktree
|
||||
const isAutoModeRunningForWorktree = useCallback(
|
||||
(worktree: WorktreeInfo): boolean => {
|
||||
if (!currentProject) return false;
|
||||
const branchName = worktree.isMain ? null : worktree.branch;
|
||||
const key = getAutoModeWorktreeKey(currentProject.id, branchName);
|
||||
return autoModeByWorktree[key]?.isRunning ?? false;
|
||||
},
|
||||
[currentProject, autoModeByWorktree, getAutoModeWorktreeKey]
|
||||
);
|
||||
|
||||
// Handler to toggle auto-mode for a worktree
|
||||
const handleToggleAutoMode = useCallback(
|
||||
async (worktree: WorktreeInfo) => {
|
||||
if (!currentProject) return;
|
||||
|
||||
// Import the useAutoMode to get start/stop functions
|
||||
// Since useAutoMode is a hook, we'll use the API client directly
|
||||
const api = getHttpApiClient();
|
||||
const branchName = worktree.isMain ? null : worktree.branch;
|
||||
const isRunning = isAutoModeRunningForWorktree(worktree);
|
||||
|
||||
try {
|
||||
if (isRunning) {
|
||||
const result = await api.autoMode.stop(projectPath, branchName);
|
||||
if (result.success) {
|
||||
const desc = branchName ? `worktree ${branchName}` : 'main branch';
|
||||
toast.success(`Auto Mode stopped for ${desc}`);
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to stop Auto Mode');
|
||||
}
|
||||
} else {
|
||||
const result = await api.autoMode.start(projectPath, branchName);
|
||||
if (result.success) {
|
||||
const desc = branchName ? `worktree ${branchName}` : 'main branch';
|
||||
toast.success(`Auto Mode started for ${desc}`);
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to start Auto Mode');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Error toggling Auto Mode');
|
||||
console.error('Auto mode toggle error:', error);
|
||||
}
|
||||
},
|
||||
[currentProject, projectPath, isAutoModeRunningForWorktree]
|
||||
);
|
||||
|
||||
// Track whether init script exists for the project
|
||||
const [hasInitScript, setHasInitScript] = useState(false);
|
||||
|
||||
@@ -244,6 +305,7 @@ export function WorktreePanel({
|
||||
isDevServerRunning={isDevServerRunning(selectedWorktree)}
|
||||
devServerInfo={getDevServerInfo(selectedWorktree)}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunningForWorktree(selectedWorktree)}
|
||||
onOpenChange={handleActionsDropdownOpenChange(selectedWorktree)}
|
||||
onPull={handlePull}
|
||||
onPush={handlePush}
|
||||
@@ -261,6 +323,7 @@ export function WorktreePanel({
|
||||
onOpenDevServerUrl={handleOpenDevServerUrl}
|
||||
onViewDevServerLogs={handleViewDevServerLogs}
|
||||
onRunInitScript={handleRunInitScript}
|
||||
onToggleAutoMode={handleToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
)}
|
||||
@@ -328,6 +391,7 @@ export function WorktreePanel({
|
||||
aheadCount={aheadCount}
|
||||
behindCount={behindCount}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunningForWorktree(mainWorktree)}
|
||||
onSelectWorktree={handleSelectWorktree}
|
||||
onBranchDropdownOpenChange={handleBranchDropdownOpenChange(mainWorktree)}
|
||||
onActionsDropdownOpenChange={handleActionsDropdownOpenChange(mainWorktree)}
|
||||
@@ -350,6 +414,7 @@ export function WorktreePanel({
|
||||
onOpenDevServerUrl={handleOpenDevServerUrl}
|
||||
onViewDevServerLogs={handleViewDevServerLogs}
|
||||
onRunInitScript={handleRunInitScript}
|
||||
onToggleAutoMode={handleToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
)}
|
||||
@@ -388,6 +453,7 @@ export function WorktreePanel({
|
||||
aheadCount={aheadCount}
|
||||
behindCount={behindCount}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunningForWorktree(worktree)}
|
||||
onSelectWorktree={handleSelectWorktree}
|
||||
onBranchDropdownOpenChange={handleBranchDropdownOpenChange(worktree)}
|
||||
onActionsDropdownOpenChange={handleActionsDropdownOpenChange(worktree)}
|
||||
@@ -410,6 +476,7 @@ export function WorktreePanel({
|
||||
onOpenDevServerUrl={handleOpenDevServerUrl}
|
||||
onViewDevServerLogs={handleViewDevServerLogs}
|
||||
onRunInitScript={handleRunInitScript}
|
||||
onToggleAutoMode={handleToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user