completly remove sandbox related code as the downstream libraries do not work with it on various os

This commit is contained in:
webdevcody
2026-01-07 08:54:14 -05:00
parent 4d4025ca06
commit 1316ead8c8
34 changed files with 589 additions and 1230 deletions

View File

@@ -89,6 +89,7 @@ export function BoardView() {
setWorktrees,
useWorktrees,
enableDependencyBlocking,
skipVerificationInAutoMode,
isPrimaryWorktreeBranch,
getPrimaryWorktreeBranch,
setPipelineConfig,
@@ -732,10 +733,17 @@ export function BoardView() {
}, []);
useEffect(() => {
logger.info(
'[AutoMode] Effect triggered - isRunning:',
autoMode.isRunning,
'hasProject:',
!!currentProject
);
if (!autoMode.isRunning || !currentProject) {
return;
}
logger.info('[AutoMode] Starting auto mode polling loop for project:', currentProject.path);
let isChecking = false;
let isActive = true; // Track if this effect is still active
@@ -755,6 +763,14 @@ export function BoardView() {
try {
// Double-check auto mode is still running before proceeding
if (!isActive || !autoModeRunningRef.current || !currentProject) {
logger.debug(
'[AutoMode] Skipping check - isActive:',
isActive,
'autoModeRunning:',
autoModeRunningRef.current,
'hasProject:',
!!currentProject
);
return;
}
@@ -762,6 +778,12 @@ export function BoardView() {
// Use ref to get the latest running tasks without causing effect re-runs
const currentRunning = runningAutoTasksRef.current.length + pendingFeaturesRef.current.size;
const availableSlots = maxConcurrency - currentRunning;
logger.debug(
'[AutoMode] Checking features - running:',
currentRunning,
'available slots:',
availableSlots
);
// No available slots, skip check
if (availableSlots <= 0) {
@@ -769,10 +791,12 @@ export function BoardView() {
}
// Filter backlog features by the currently selected worktree branch
// This logic mirrors use-board-column-features.ts for consistency
// This logic mirrors use-board-column-features.ts for consistency.
// HOWEVER: auto mode should still run even if the user is viewing a non-primary worktree,
// so we fall back to "all backlog features" when none are visible in the current view.
// Use ref to get the latest features without causing effect re-runs
const currentFeatures = hookFeaturesRef.current;
const backlogFeatures = currentFeatures.filter((f) => {
const backlogFeaturesInView = currentFeatures.filter((f) => {
if (f.status !== 'backlog') return false;
const featureBranch = f.branchName;
@@ -796,7 +820,25 @@ export function BoardView() {
return featureBranch === currentWorktreeBranch;
});
const backlogFeatures =
backlogFeaturesInView.length > 0
? backlogFeaturesInView
: currentFeatures.filter((f) => f.status === 'backlog');
logger.debug(
'[AutoMode] Features - total:',
currentFeatures.length,
'backlog in view:',
backlogFeaturesInView.length,
'backlog total:',
backlogFeatures.length
);
if (backlogFeatures.length === 0) {
logger.debug(
'[AutoMode] No backlog features found, statuses:',
currentFeatures.map((f) => f.status).join(', ')
);
return;
}
@@ -806,12 +848,25 @@ export function BoardView() {
);
// Filter out features with blocking dependencies if dependency blocking is enabled
const eligibleFeatures = enableDependencyBlocking
? sortedBacklog.filter((f) => {
const blockingDeps = getBlockingDependencies(f, currentFeatures);
return blockingDeps.length === 0;
})
: sortedBacklog;
// NOTE: skipVerificationInAutoMode means "ignore unmet dependency verification" so we
// should NOT exclude blocked features in that mode.
const eligibleFeatures =
enableDependencyBlocking && !skipVerificationInAutoMode
? sortedBacklog.filter((f) => {
const blockingDeps = getBlockingDependencies(f, currentFeatures);
if (blockingDeps.length > 0) {
logger.debug('[AutoMode] Feature', f.id, 'blocked by deps:', blockingDeps);
}
return blockingDeps.length === 0;
})
: sortedBacklog;
logger.debug(
'[AutoMode] Eligible features after dep check:',
eligibleFeatures.length,
'dependency blocking enabled:',
enableDependencyBlocking
);
// Start features up to available slots
const featuresToStart = eligibleFeatures.slice(0, availableSlots);
@@ -820,6 +875,13 @@ export function BoardView() {
return;
}
logger.info(
'[AutoMode] Starting',
featuresToStart.length,
'features:',
featuresToStart.map((f) => f.id).join(', ')
);
for (const feature of featuresToStart) {
// Check again before starting each feature
if (!isActive || !autoModeRunningRef.current || !currentProject) {
@@ -827,8 +889,9 @@ export function BoardView() {
}
// Simplified: No worktree creation on client - server derives workDir from feature.branchName
// If feature has no branchName and primary worktree is selected, assign primary branch
if (currentWorktreePath === null && !feature.branchName) {
// If feature has no branchName, assign it to the primary branch so it can run consistently
// even when the user is viewing a non-primary worktree.
if (!feature.branchName) {
const primaryBranch =
(currentProject.path ? getPrimaryWorktreeBranch(currentProject.path) : null) ||
'main';
@@ -878,6 +941,7 @@ export function BoardView() {
getPrimaryWorktreeBranch,
isPrimaryWorktreeBranch,
enableDependencyBlocking,
skipVerificationInAutoMode,
persistFeatureUpdate,
]);

View File

@@ -1,13 +1,15 @@
import { useState } from 'react';
import { HotkeyButton } from '@/components/ui/hotkey-button';
import { Button } from '@/components/ui/button';
import { Slider } from '@/components/ui/slider';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Plus, Bot, Wand2 } from 'lucide-react';
import { Plus, Bot, Wand2, Settings2 } from 'lucide-react';
import { KeyboardShortcut } from '@/hooks/use-keyboard-shortcuts';
import { ClaudeUsagePopover } from '@/components/claude-usage-popover';
import { useAppStore } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import { AutoModeSettingsDialog } from './dialogs/auto-mode-settings-dialog';
interface BoardHeaderProps {
projectName: string;
@@ -38,8 +40,11 @@ export function BoardHeader({
addFeatureShortcut,
isMounted,
}: BoardHeaderProps) {
const [showAutoModeSettings, setShowAutoModeSettings] = useState(false);
const apiKeys = useAppStore((state) => state.apiKeys);
const claudeAuthStatus = useSetupStore((state) => state.claudeAuthStatus);
const skipVerificationInAutoMode = useAppStore((state) => state.skipVerificationInAutoMode);
const setSkipVerificationInAutoMode = useAppStore((state) => state.setSkipVerificationInAutoMode);
// Hide usage tracking when using API key (only show for Claude Code CLI users)
// Check both user-entered API key and environment variable ANTHROPIC_API_KEY
@@ -97,9 +102,25 @@ export function BoardHeader({
onCheckedChange={onAutoModeToggle}
data-testid="auto-mode-toggle"
/>
<button
onClick={() => setShowAutoModeSettings(true)}
className="p-1 rounded hover:bg-accent/50 transition-colors"
title="Auto Mode Settings"
data-testid="auto-mode-settings-button"
>
<Settings2 className="w-4 h-4 text-muted-foreground" />
</button>
</div>
)}
{/* Auto Mode Settings Dialog */}
<AutoModeSettingsDialog
open={showAutoModeSettings}
onOpenChange={setShowAutoModeSettings}
skipVerificationInAutoMode={skipVerificationInAutoMode}
onSkipVerificationChange={setSkipVerificationInAutoMode}
/>
<Button
size="sm"
variant="outline"

View File

@@ -5,118 +5,44 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp
import { AlertCircle, Lock, Hand, Sparkles } from 'lucide-react';
import { getBlockingDependencies } from '@automaker/dependency-resolver';
interface CardBadgeProps {
children: React.ReactNode;
className?: string;
'data-testid'?: string;
title?: string;
}
/**
* Shared badge component matching the "Just Finished" badge style
* Used for priority badges and other card badges
*/
function CardBadge({ children, className, 'data-testid': dataTestId, title }: CardBadgeProps) {
return (
<div
className={cn(
'inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium',
className
)}
data-testid={dataTestId}
title={title}
>
{children}
</div>
);
}
/** Uniform badge style for all card badges */
const uniformBadgeClass =
'inline-flex items-center justify-center w-6 h-6 rounded-md border-[1.5px]';
interface CardBadgesProps {
feature: Feature;
}
/**
* CardBadges - Shows error badges below the card header
* Note: Blocked/Lock badges are now shown in PriorityBadges for visual consistency
*/
export function CardBadges({ feature }: CardBadgesProps) {
const { enableDependencyBlocking, features } = useAppStore();
// Calculate blocking dependencies (if feature is in backlog and has incomplete dependencies)
const blockingDependencies = useMemo(() => {
if (!enableDependencyBlocking || feature.status !== 'backlog') {
return [];
}
return getBlockingDependencies(feature, features);
}, [enableDependencyBlocking, feature, features]);
// Status badges row (error, blocked)
const showStatusBadges =
feature.error ||
(blockingDependencies.length > 0 &&
!feature.error &&
!feature.skipTests &&
feature.status === 'backlog');
if (!showStatusBadges) {
if (!feature.error) {
return null;
}
return (
<div className="flex flex-wrap items-center gap-1.5 px-3 pt-1.5 min-h-[24px]">
{/* Error badge */}
{feature.error && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
'inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[10px] font-medium',
'bg-[var(--status-error-bg)] border-[var(--status-error)]/40 text-[var(--status-error)]'
)}
data-testid={`error-badge-${feature.id}`}
>
<AlertCircle className="w-3 h-3" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs max-w-[250px]">
<p>{feature.error}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{/* Blocked badge */}
{blockingDependencies.length > 0 &&
!feature.error &&
!feature.skipTests &&
feature.status === 'backlog' && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
'inline-flex items-center gap-1 rounded-full border-2 px-1.5 py-0.5 text-[10px] font-bold',
'bg-orange-500/20 border-orange-500/50 text-orange-500'
)}
data-testid={`blocked-badge-${feature.id}`}
>
<Lock className="w-3 h-3" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs max-w-[250px]">
<p className="font-medium mb-1">
Blocked by {blockingDependencies.length} incomplete{' '}
{blockingDependencies.length === 1 ? 'dependency' : 'dependencies'}
</p>
<p className="text-muted-foreground">
{blockingDependencies
.map((depId) => {
const dep = features.find((f) => f.id === depId);
return dep?.description || depId;
})
.join(', ')}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
uniformBadgeClass,
'bg-[var(--status-error-bg)] border-[var(--status-error)]/40 text-[var(--status-error)]'
)}
data-testid={`error-badge-${feature.id}`}
>
<AlertCircle className="w-3.5 h-3.5" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs max-w-[250px]">
<p>{feature.error}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}
@@ -126,8 +52,17 @@ interface PriorityBadgesProps {
}
export function PriorityBadges({ feature }: PriorityBadgesProps) {
const { enableDependencyBlocking, features } = useAppStore();
const [currentTime, setCurrentTime] = useState(() => Date.now());
// Calculate blocking dependencies (if feature is in backlog and has incomplete dependencies)
const blockingDependencies = useMemo(() => {
if (!enableDependencyBlocking || feature.status !== 'backlog') {
return [];
}
return getBlockingDependencies(feature, features);
}, [enableDependencyBlocking, feature, features]);
const isJustFinished = useMemo(() => {
if (!feature.justFinishedAt || feature.status !== 'waiting_approval' || feature.error) {
return false;
@@ -161,25 +96,27 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
};
}, [feature.justFinishedAt, feature.status, currentTime]);
const showPriorityBadges =
feature.priority ||
(feature.skipTests && !feature.error && feature.status === 'backlog') ||
isJustFinished;
const isBlocked =
blockingDependencies.length > 0 && !feature.error && feature.status === 'backlog';
const showManualVerification =
feature.skipTests && !feature.error && feature.status === 'backlog';
if (!showPriorityBadges) {
const showBadges = feature.priority || showManualVerification || isBlocked || isJustFinished;
if (!showBadges) {
return null;
}
return (
<div className="absolute top-2 left-2 flex items-center gap-1.5">
<div className="absolute top-2 left-2 flex items-center gap-1">
{/* Priority badge */}
{feature.priority && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<CardBadge
<div
className={cn(
'bg-opacity-90 border rounded-[6px] px-1.5 py-0.5 flex items-center justify-center border-[1.5px] w-5 h-5', // badge style from example
uniformBadgeClass,
feature.priority === 1 &&
'bg-[var(--status-error-bg)] border-[var(--status-error)]/40 text-[var(--status-error)]',
feature.priority === 2 &&
@@ -189,14 +126,10 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
)}
data-testid={`priority-badge-${feature.id}`}
>
{feature.priority === 1 ? (
<span className="font-bold text-xs flex items-center gap-0.5">H</span>
) : feature.priority === 2 ? (
<span className="font-bold text-xs flex items-center gap-0.5">M</span>
) : (
<span className="font-bold text-xs flex items-center gap-0.5">L</span>
)}
</CardBadge>
<span className="font-bold text-xs">
{feature.priority === 1 ? 'H' : feature.priority === 2 ? 'M' : 'L'}
</span>
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs">
<p>
@@ -210,17 +143,21 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
</Tooltip>
</TooltipProvider>
)}
{/* Manual verification badge */}
{feature.skipTests && !feature.error && feature.status === 'backlog' && (
{showManualVerification && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<CardBadge
className="bg-[var(--status-warning-bg)] border-[var(--status-warning)]/40 text-[var(--status-warning)]"
<div
className={cn(
uniformBadgeClass,
'bg-[var(--status-warning-bg)] border-[var(--status-warning)]/40 text-[var(--status-warning)]'
)}
data-testid={`skip-tests-badge-${feature.id}`}
>
<Hand className="w-3 h-3" />
</CardBadge>
<Hand className="w-3.5 h-3.5" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs">
<p>Manual verification required</p>
@@ -229,15 +166,59 @@ export function PriorityBadges({ feature }: PriorityBadgesProps) {
</TooltipProvider>
)}
{/* Blocked badge */}
{isBlocked && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
uniformBadgeClass,
'bg-orange-500/20 border-orange-500/50 text-orange-500'
)}
data-testid={`blocked-badge-${feature.id}`}
>
<Lock className="w-3.5 h-3.5" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs max-w-[250px]">
<p className="font-medium mb-1">
Blocked by {blockingDependencies.length} incomplete{' '}
{blockingDependencies.length === 1 ? 'dependency' : 'dependencies'}
</p>
<p className="text-muted-foreground">
{blockingDependencies
.map((depId) => {
const dep = features.find((f) => f.id === depId);
return dep?.description || depId;
})
.join(', ')}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{/* Just Finished badge */}
{isJustFinished && (
<CardBadge
className="bg-[var(--status-success-bg)] border-[var(--status-success)]/40 text-[var(--status-success)] animate-pulse"
data-testid={`just-finished-badge-${feature.id}`}
title="Agent just finished working on this feature"
>
<Sparkles className="w-3 h-3" />
</CardBadge>
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
uniformBadgeClass,
'bg-[var(--status-success-bg)] border-[var(--status-success)]/40 text-[var(--status-success)] animate-pulse'
)}
data-testid={`just-finished-badge-${feature.id}`}
>
<Sparkles className="w-3.5 h-3.5" />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="text-xs">
<p>Agent just finished working on this feature</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
);

View File

@@ -0,0 +1,68 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { FastForward, Settings2 } from 'lucide-react';
interface AutoModeSettingsDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
skipVerificationInAutoMode: boolean;
onSkipVerificationChange: (value: boolean) => void;
}
export function AutoModeSettingsDialog({
open,
onOpenChange,
skipVerificationInAutoMode,
onSkipVerificationChange,
}: AutoModeSettingsDialogProps) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md" data-testid="auto-mode-settings-dialog">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Settings2 className="w-5 h-5" />
Auto Mode Settings
</DialogTitle>
<DialogDescription>
Configure how auto mode handles feature execution and dependencies.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
{/* Skip Verification Setting */}
<div className="flex items-start space-x-3 p-3 rounded-lg bg-secondary/50">
<div className="flex-1 space-y-1">
<div className="flex items-center justify-between">
<Label
htmlFor="skip-verification-toggle"
className="text-sm font-medium cursor-pointer flex items-center gap-2"
>
<FastForward className="w-4 h-4 text-brand-500" />
Skip verification requirement
</Label>
<Switch
id="skip-verification-toggle"
checked={skipVerificationInAutoMode}
onCheckedChange={onSkipVerificationChange}
data-testid="skip-verification-toggle"
/>
</div>
<p className="text-xs text-muted-foreground leading-relaxed">
When enabled, auto mode will grab features even if their dependencies are not
verified, as long as they are not currently running. This allows faster pipeline
execution without waiting for manual verification.
</p>
</div>
</div>
</div>
</DialogContent>
</Dialog>
);
}

View File

@@ -79,6 +79,7 @@ export function useBoardActions({
moveFeature,
useWorktrees,
enableDependencyBlocking,
skipVerificationInAutoMode,
isPrimaryWorktreeBranch,
getPrimaryWorktreeBranch,
} = useAppStore();
@@ -805,12 +806,14 @@ export function useBoardActions({
// Sort by priority (lower number = higher priority, priority 1 is highest)
// Features with blocking dependencies are sorted to the end
const sortedBacklog = [...backlogFeatures].sort((a, b) => {
const aBlocked = enableDependencyBlocking
? getBlockingDependencies(a, features).length > 0
: false;
const bBlocked = enableDependencyBlocking
? getBlockingDependencies(b, features).length > 0
: false;
const aBlocked =
enableDependencyBlocking && !skipVerificationInAutoMode
? getBlockingDependencies(a, features).length > 0
: false;
const bBlocked =
enableDependencyBlocking && !skipVerificationInAutoMode
? getBlockingDependencies(b, features).length > 0
: false;
// Blocked features go to the end
if (aBlocked && !bBlocked) return 1;
@@ -822,14 +825,14 @@ export function useBoardActions({
// Find the first feature without blocking dependencies
const featureToStart = sortedBacklog.find((f) => {
if (!enableDependencyBlocking) return true;
if (!enableDependencyBlocking || skipVerificationInAutoMode) return true;
return getBlockingDependencies(f, features).length === 0;
});
if (!featureToStart) {
toast.info('No eligible features', {
description:
'All backlog features have unmet dependencies. Complete their dependencies first.',
'All backlog features have unmet dependencies. Complete their dependencies first (or enable "Skip verification requirement" in Auto Mode settings).',
});
return;
}
@@ -846,6 +849,7 @@ export function useBoardActions({
isPrimaryWorktreeBranch,
getPrimaryWorktreeBranch,
enableDependencyBlocking,
skipVerificationInAutoMode,
]);
const handleArchiveAllVerified = useCallback(async () => {

View File

@@ -31,6 +31,8 @@ export function SettingsView() {
setDefaultSkipTests,
enableDependencyBlocking,
setEnableDependencyBlocking,
skipVerificationInAutoMode,
setSkipVerificationInAutoMode,
useWorktrees,
setUseWorktrees,
showProfilesOnly,
@@ -48,10 +50,6 @@ export function SettingsView() {
aiProfiles,
autoLoadClaudeMd,
setAutoLoadClaudeMd,
enableSandboxMode,
setEnableSandboxMode,
skipSandboxWarning,
setSkipSandboxWarning,
promptCustomization,
setPromptCustomization,
} = useAppStore();
@@ -130,6 +128,7 @@ export function SettingsView() {
showProfilesOnly={showProfilesOnly}
defaultSkipTests={defaultSkipTests}
enableDependencyBlocking={enableDependencyBlocking}
skipVerificationInAutoMode={skipVerificationInAutoMode}
useWorktrees={useWorktrees}
defaultPlanningMode={defaultPlanningMode}
defaultRequirePlanApproval={defaultRequirePlanApproval}
@@ -138,6 +137,7 @@ export function SettingsView() {
onShowProfilesOnlyChange={setShowProfilesOnly}
onDefaultSkipTestsChange={setDefaultSkipTests}
onEnableDependencyBlockingChange={setEnableDependencyBlocking}
onSkipVerificationInAutoModeChange={setSkipVerificationInAutoMode}
onUseWorktreesChange={setUseWorktrees}
onDefaultPlanningModeChange={setDefaultPlanningMode}
onDefaultRequirePlanApprovalChange={setDefaultRequirePlanApproval}
@@ -149,8 +149,6 @@ export function SettingsView() {
<DangerZoneSection
project={settingsProject}
onDeleteClick={() => setShowDeleteDialog(true)}
skipSandboxWarning={skipSandboxWarning}
onResetSandboxWarning={() => setSkipSandboxWarning(false)}
/>
);
default:

View File

@@ -1,13 +1,11 @@
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import { FileCode, Shield } from 'lucide-react';
import { FileCode } from 'lucide-react';
import { cn } from '@/lib/utils';
interface ClaudeMdSettingsProps {
autoLoadClaudeMd: boolean;
onAutoLoadClaudeMdChange: (enabled: boolean) => void;
enableSandboxMode: boolean;
onEnableSandboxModeChange: (enabled: boolean) => void;
}
/**
@@ -15,23 +13,18 @@ interface ClaudeMdSettingsProps {
*
* UI controls for Claude Agent SDK settings including:
* - Auto-loading of project instructions from .claude/CLAUDE.md files
* - Sandbox mode for isolated bash command execution
*
* Usage:
* ```tsx
* <ClaudeMdSettings
* autoLoadClaudeMd={autoLoadClaudeMd}
* onAutoLoadClaudeMdChange={setAutoLoadClaudeMd}
* enableSandboxMode={enableSandboxMode}
* onEnableSandboxModeChange={setEnableSandboxMode}
* />
* ```
*/
export function ClaudeMdSettings({
autoLoadClaudeMd,
onAutoLoadClaudeMdChange,
enableSandboxMode,
onEnableSandboxModeChange,
}: ClaudeMdSettingsProps) {
return (
<div
@@ -83,32 +76,6 @@ export function ClaudeMdSettings({
</p>
</div>
</div>
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3 mt-2">
<Checkbox
id="enable-sandbox-mode"
checked={enableSandboxMode}
onCheckedChange={(checked) => onEnableSandboxModeChange(checked === true)}
className="mt-1"
data-testid="enable-sandbox-mode-checkbox"
/>
<div className="space-y-1.5">
<Label
htmlFor="enable-sandbox-mode"
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
>
<Shield className="w-4 h-4 text-brand-500" />
Enable Sandbox Mode
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
Run bash commands in an isolated sandbox environment for additional security.
<span className="block mt-1 text-warning/80">
Note: On some systems, enabling sandbox mode may cause the agent to hang without
responding. If you experience issues, try disabling this option.
</span>
</p>
</div>
</div>
</div>
</div>
);

View File

@@ -1,21 +1,14 @@
import { Button } from '@/components/ui/button';
import { Trash2, Folder, AlertTriangle, Shield, RotateCcw } from 'lucide-react';
import { Trash2, Folder, AlertTriangle } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { Project } from '../shared/types';
interface DangerZoneSectionProps {
project: Project | null;
onDeleteClick: () => void;
skipSandboxWarning: boolean;
onResetSandboxWarning: () => void;
}
export function DangerZoneSection({
project,
onDeleteClick,
skipSandboxWarning,
onResetSandboxWarning,
}: DangerZoneSectionProps) {
export function DangerZoneSection({ project, onDeleteClick }: DangerZoneSectionProps) {
return (
<div
className={cn(
@@ -37,36 +30,6 @@ export function DangerZoneSection({
</p>
</div>
<div className="p-6 space-y-4">
{/* Sandbox Warning Reset */}
{skipSandboxWarning && (
<div className="flex items-center justify-between gap-4 p-4 rounded-xl bg-destructive/5 border border-destructive/10">
<div className="flex items-center gap-3.5 min-w-0">
<div className="w-11 h-11 rounded-xl bg-gradient-to-br from-destructive/15 to-destructive/10 border border-destructive/20 flex items-center justify-center shrink-0">
<Shield className="w-5 h-5 text-destructive" />
</div>
<div className="min-w-0">
<p className="font-medium text-foreground">Sandbox Warning Disabled</p>
<p className="text-xs text-muted-foreground/70 mt-0.5">
The sandbox environment warning is hidden on startup
</p>
</div>
</div>
<Button
variant="outline"
onClick={onResetSandboxWarning}
data-testid="reset-sandbox-warning-button"
className={cn(
'shrink-0 gap-2',
'transition-all duration-200 ease-out',
'hover:scale-[1.02] active:scale-[0.98]'
)}
>
<RotateCcw className="w-4 h-4" />
Reset
</Button>
</div>
)}
{/* Project Delete */}
{project && (
<div className="flex items-center justify-between gap-4 p-4 rounded-xl bg-destructive/5 border border-destructive/10">
@@ -97,7 +60,7 @@ export function DangerZoneSection({
)}
{/* Empty state when nothing to show */}
{!skipSandboxWarning && !project && (
{!project && (
<p className="text-sm text-muted-foreground/60 text-center py-4">
No danger zone actions available.
</p>

View File

@@ -12,6 +12,7 @@ import {
ScrollText,
ShieldCheck,
User,
FastForward,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import {
@@ -29,6 +30,7 @@ interface FeatureDefaultsSectionProps {
showProfilesOnly: boolean;
defaultSkipTests: boolean;
enableDependencyBlocking: boolean;
skipVerificationInAutoMode: boolean;
useWorktrees: boolean;
defaultPlanningMode: PlanningMode;
defaultRequirePlanApproval: boolean;
@@ -37,6 +39,7 @@ interface FeatureDefaultsSectionProps {
onShowProfilesOnlyChange: (value: boolean) => void;
onDefaultSkipTestsChange: (value: boolean) => void;
onEnableDependencyBlockingChange: (value: boolean) => void;
onSkipVerificationInAutoModeChange: (value: boolean) => void;
onUseWorktreesChange: (value: boolean) => void;
onDefaultPlanningModeChange: (value: PlanningMode) => void;
onDefaultRequirePlanApprovalChange: (value: boolean) => void;
@@ -47,6 +50,7 @@ export function FeatureDefaultsSection({
showProfilesOnly,
defaultSkipTests,
enableDependencyBlocking,
skipVerificationInAutoMode,
useWorktrees,
defaultPlanningMode,
defaultRequirePlanApproval,
@@ -55,6 +59,7 @@ export function FeatureDefaultsSection({
onShowProfilesOnlyChange,
onDefaultSkipTestsChange,
onEnableDependencyBlockingChange,
onSkipVerificationInAutoModeChange,
onUseWorktreesChange,
onDefaultPlanningModeChange,
onDefaultRequirePlanApprovalChange,
@@ -309,6 +314,34 @@ export function FeatureDefaultsSection({
{/* Separator */}
<div className="border-t border-border/30" />
{/* Skip Verification in Auto Mode Setting */}
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
<Checkbox
id="skip-verification-auto-mode"
checked={skipVerificationInAutoMode}
onCheckedChange={(checked) => onSkipVerificationInAutoModeChange(checked === true)}
className="mt-1"
data-testid="skip-verification-auto-mode-checkbox"
/>
<div className="space-y-1.5">
<Label
htmlFor="skip-verification-auto-mode"
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
>
<FastForward className="w-4 h-4 text-brand-500" />
Skip verification in auto mode
</Label>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
When enabled, auto mode will grab features even if their dependencies are not
verified, as long as they are not currently running. This allows faster pipeline
execution without waiting for manual verification.
</p>
</div>
</div>
{/* Separator */}
<div className="border-t border-border/30" />
{/* Worktree Isolation Setting */}
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
<Checkbox