Claude Code Plan mode fix and better UI handling (#324)

* Claude Code Plan mode fix and better UI handling

Fix plan detections.

UI improvements:
- Added Plans tab for plan tasks which show copyable plans higlighting the current.
- Disable create task when no plan is detected and show a clear warning in the log view.

* fix tests
This commit is contained in:
Solomon
2025-07-23 09:33:20 +01:00
committed by GitHub
parent ca9040b97d
commit 8896fd9285
13 changed files with 554 additions and 25 deletions

View File

@@ -32,6 +32,8 @@ import {
TaskRelatedTasksContext,
TaskSelectedAttemptContext,
} from './taskDetailsContext.ts';
import { TaskPlanContext } from './TaskPlanContext.ts';
import { is_planning_executor_type } from '@/lib/utils.ts';
import type { AttemptData } from '@/lib/types.ts';
const TaskDetailsProvider: FC<{
@@ -436,6 +438,56 @@ const TaskDetailsProvider: FC<{
]
);
// Plan context value
const planValue = useMemo(() => {
const isPlanningMode =
attemptData.processes?.some(
(process) =>
process.executor_type &&
is_planning_executor_type(process.executor_type)
) ?? false;
const planCount =
attemptData.allLogs?.reduce((count, processLog) => {
const planEntries =
processLog.normalized_conversation?.entries.filter(
(entry) =>
entry.entry_type.type === 'tool_use' &&
entry.entry_type.action_type.action === 'plan_presentation'
) ?? [];
return count + planEntries.length;
}, 0) ?? 0;
const hasPlans = planCount > 0;
const latestProcessHasNoPlan = (() => {
if (!attemptData.allLogs || attemptData.allLogs.length === 0)
return false;
const latestProcessLog =
attemptData.allLogs[attemptData.allLogs.length - 1];
if (!latestProcessLog.normalized_conversation?.entries) return true;
return !latestProcessLog.normalized_conversation.entries.some(
(entry) =>
entry.entry_type.type === 'tool_use' &&
entry.entry_type.action_type.action === 'plan_presentation'
);
})();
// Can create task if not in planning mode, or if in planning mode and has plans
const canCreateTask =
!isPlanningMode ||
(isPlanningMode && hasPlans && !latestProcessHasNoPlan);
return {
isPlanningMode,
hasPlans,
planCount,
latestProcessHasNoPlan,
canCreateTask,
};
}, [attemptData.processes, attemptData.allLogs]);
return (
<TaskDetailsContext.Provider value={value}>
<TaskAttemptLoadingContext.Provider value={taskAttemptLoadingValue}>
@@ -453,7 +505,9 @@ const TaskDetailsProvider: FC<{
<TaskRelatedTasksContext.Provider
value={relatedTasksValue}
>
{children}
<TaskPlanContext.Provider value={planValue}>
{children}
</TaskPlanContext.Provider>
</TaskRelatedTasksContext.Provider>
</TaskBackgroundRefreshContext.Provider>
</TaskExecutionStateContext.Provider>

View File

@@ -0,0 +1,33 @@
import { createContext, useContext } from 'react';
interface TaskPlanContextValue {
isPlanningMode: boolean;
hasPlans: boolean;
planCount: number;
latestProcessHasNoPlan: boolean;
canCreateTask: boolean;
}
export const TaskPlanContext = createContext<TaskPlanContextValue>({
isPlanningMode: false,
hasPlans: false,
planCount: 0,
latestProcessHasNoPlan: false,
canCreateTask: true,
});
export const useTaskPlan = () => {
const context = useContext(TaskPlanContext);
if (!context) {
// Return defaults when used outside of TaskPlanProvider (e.g., on project-tasks page)
// In this case, we assume not in planning mode, so task creation should be allowed
return {
isPlanningMode: false,
hasPlans: false,
planCount: 0,
latestProcessHasNoPlan: false,
canCreateTask: true,
};
}
return context;
};

View File

@@ -8,14 +8,17 @@ import {
useState,
} from 'react';
import { TaskAttemptDataContext } from '@/components/context/taskDetailsContext.ts';
import { useTaskPlan } from '@/components/context/TaskPlanContext.ts';
import { Loader } from '@/components/ui/loader.tsx';
import { Button } from '@/components/ui/button';
import { AlertTriangle } from 'lucide-react';
import Prompt from './Prompt';
import ConversationEntry from './ConversationEntry';
import { ConversationEntryDisplayType } from '@/lib/types';
function Conversation() {
const { attemptData } = useContext(TaskAttemptDataContext);
const { attemptData, isAttemptRunning } = useContext(TaskAttemptDataContext);
const { isPlanningMode, latestProcessHasNoPlan } = useTaskPlan();
const [shouldAutoScrollLogs, setShouldAutoScrollLogs] = useState(true);
const [conversationUpdateTrigger, setConversationUpdateTrigger] = useState(0);
const [visibleCount, setVisibleCount] = useState(100);
@@ -249,6 +252,23 @@ function Conversation() {
</p>
</div>
)}
{/* Warning banner for planning mode without plan */}
{isPlanningMode && latestProcessHasNoPlan && !isAttemptRunning && (
<div className="mt-4 p-4 rounded-lg border border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-950/20">
<div className="flex items-center gap-2 mb-2">
<AlertTriangle className="h-5 w-5 text-orange-600 dark:text-orange-400" />
<p className="text-lg font-semibold text-orange-800 dark:text-orange-300">
No Plan Generated
</p>
</div>
<p className="text-orange-700 dark:text-orange-400">
The last execution attempt did not produce a plan. Task creation is
disabled until a plan is available. Try providing more specific
instructions or check the conversation for any errors.
</p>
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,258 @@
import { useContext, useMemo, useState } from 'react';
import {
FileText,
Copy,
AlertTriangle,
CheckCircle,
ChevronDown,
ChevronRight,
} from 'lucide-react';
import {
TaskAttemptDataContext,
TaskAttemptLoadingContext,
} from '@/components/context/taskDetailsContext.ts';
import { useTaskPlan } from '@/components/context/TaskPlanContext.ts';
import { Loader } from '@/components/ui/loader';
import MarkdownRenderer from '@/components/ui/markdown-renderer.tsx';
import { NormalizedEntry } from 'shared/types.ts';
interface PlanEntry {
entry: NormalizedEntry;
processId: string;
processIndex: number;
planIndex: number;
isCurrent: boolean;
}
function PlanTab() {
const { loading } = useContext(TaskAttemptLoadingContext);
const { attemptData } = useContext(TaskAttemptDataContext);
const { isPlanningMode, hasPlans, latestProcessHasNoPlan } = useTaskPlan();
const [copiedPlan, setCopiedPlan] = useState<string | null>(null);
const [expandedPlans, setExpandedPlans] = useState<Set<string>>(new Set());
// Extract all plans from all processes
const plans = useMemo(() => {
if (!attemptData.allLogs) return [];
const planEntries: PlanEntry[] = [];
let globalPlanIndex = 1;
attemptData.allLogs.forEach((processLog, processIndex) => {
if (!processLog.normalized_conversation?.entries) return;
let localPlanIndex = 1;
processLog.normalized_conversation.entries.forEach((entry) => {
if (
entry.entry_type.type === 'tool_use' &&
entry.entry_type.action_type.action === 'plan_presentation'
) {
planEntries.push({
entry,
processId: processLog.id,
processIndex,
planIndex: localPlanIndex,
isCurrent: globalPlanIndex === planEntries.length + 1, // Last plan is current
});
localPlanIndex++;
globalPlanIndex++;
}
});
});
// Mark the last plan as current
if (planEntries.length > 0) {
planEntries.forEach((plan) => {
plan.isCurrent = false;
});
planEntries[planEntries.length - 1].isCurrent = true;
}
return planEntries;
}, [attemptData.allLogs]);
const handleCopyPlan = async (planContent: string, planId: string) => {
try {
await navigator.clipboard.writeText(planContent);
setCopiedPlan(planId);
setTimeout(() => setCopiedPlan(null), 2000);
} catch (error) {
console.error('Failed to copy plan:', error);
}
};
const togglePlanExpansion = (planId: string) => {
setExpandedPlans((prev) => {
const newSet = new Set(prev);
if (newSet.has(planId)) {
newSet.delete(planId);
} else {
newSet.add(planId);
}
return newSet;
});
};
if (loading) {
return (
<div className="flex items-center justify-center h-full">
<Loader message="Loading..." size={32} />
</div>
);
}
if (!isPlanningMode) {
return (
<div className="text-center py-8 text-muted-foreground">
<FileText className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p className="text-lg font-medium mb-2">Not in planning mode</p>
<p className="text-sm">
This tab is only available when using a planning executor
</p>
</div>
);
}
if (!hasPlans && latestProcessHasNoPlan) {
return (
<div className="p-4">
<div className="text-center py-8">
<AlertTriangle className="h-12 w-12 mx-auto mb-4 text-orange-500" />
<p className="text-lg font-medium mb-2 text-orange-800 dark:text-orange-300">
No plan generated
</p>
<p className="text-sm text-muted-foreground mb-4">
The last execution attempt did not produce a plan. Task creation is
disabled until a plan is available.
</p>
</div>
</div>
);
}
if (!hasPlans) {
return (
<div className="text-center py-8 text-muted-foreground">
<FileText className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p className="text-lg font-medium mb-2">No plans available</p>
<p className="text-sm">
Plans will appear here once they are generated
</p>
</div>
);
}
return (
<div className="p-4 space-y-6 h-full flex flex-col">
<div className="flex items-center justify-between flex-shrink-0">
<h3 className="text-lg font-semibold">Plans ({plans.length})</h3>
{latestProcessHasNoPlan && (
<div className="flex items-center gap-2 text-orange-600 dark:text-orange-400 text-sm">
<AlertTriangle className="h-4 w-4" />
Last attempt produced no plan
</div>
)}
</div>
<div className="flex-1 overflow-y-auto space-y-4 min-h-0">
{plans.map((planEntry, index) => {
const planId = `${planEntry.processId}-${planEntry.planIndex}`;
const planContent =
planEntry.entry.entry_type.type === 'tool_use' &&
planEntry.entry.entry_type.action_type.action ===
'plan_presentation'
? planEntry.entry.entry_type.action_type.plan
: planEntry.entry.content;
const isExpanded = expandedPlans.has(planId);
return (
<div
key={planId}
className={`border rounded-lg transition-all ${
planEntry.isCurrent
? 'border-blue-400 bg-blue-50/50 dark:bg-blue-950/20 shadow-md'
: 'border-gray-200 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-950/20 opacity-75'
}`}
>
<div
className="flex items-center justify-between p-4 cursor-pointer hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors"
onClick={() => togglePlanExpansion(planId)}
>
<div className="flex items-center gap-2">
<button
className="flex items-center justify-center w-5 h-5 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors"
onClick={(e) => {
e.stopPropagation();
togglePlanExpansion(planId);
}}
>
{isExpanded ? (
<ChevronDown className="h-3 w-3" />
) : (
<ChevronRight className="h-3 w-3" />
)}
</button>
<span
className={`px-2 py-1 rounded text-xs font-medium ${
planEntry.isCurrent
? 'bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-200'
: 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'
}`}
>
Plan {index + 1}
</span>
{planEntry.isCurrent && (
<div className="flex items-center gap-1 text-green-600 dark:text-green-400">
<CheckCircle className="h-4 w-4" />
<span className="text-xs font-medium">Current</span>
</div>
)}
{planEntry.entry.timestamp && (
<span className="text-xs text-gray-500">
{new Date(planEntry.entry.timestamp).toLocaleString()}
</span>
)}
</div>
<button
onClick={(e) => {
e.stopPropagation();
handleCopyPlan(planContent, planId);
}}
className="flex items-center gap-1 px-2 py-1 text-xs text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors"
title="Copy plan as markdown"
>
<Copy className="h-3 w-3" />
{copiedPlan === planId ? 'Copied!' : 'Copy'}
</button>
</div>
{isExpanded && (
<div
className={`px-4 pb-4 border-t ${planEntry.isCurrent ? 'border-blue-200' : 'border-gray-200 dark:border-gray-700'}`}
>
<div
className={`mt-3 ${planEntry.isCurrent ? '' : 'opacity-80'}`}
>
<MarkdownRenderer
content={planContent}
className="whitespace-pre-wrap break-words"
/>
</div>
</div>
)}
</div>
);
})}
</div>
{plans.length > 1 && (
<div className="text-xs text-gray-500 text-center pt-4 border-t flex-shrink-0">
Previous plans are shown with reduced emphasis. Click to
expand/collapse plans.
</div>
)}
</div>
);
}
export default PlanTab;

View File

@@ -1,20 +1,30 @@
import { GitCompare, MessageSquare, Network, Cog } from 'lucide-react';
import {
GitCompare,
MessageSquare,
Network,
Cog,
FileText,
} from 'lucide-react';
import { useContext } from 'react';
import {
TaskAttemptDataContext,
TaskDiffContext,
TaskRelatedTasksContext,
} from '@/components/context/taskDetailsContext.ts';
import { useTaskPlan } from '@/components/context/TaskPlanContext.ts';
type Props = {
activeTab: 'logs' | 'diffs' | 'related' | 'processes';
setActiveTab: (tab: 'logs' | 'diffs' | 'related' | 'processes') => void;
activeTab: 'logs' | 'diffs' | 'related' | 'processes' | 'plan';
setActiveTab: (
tab: 'logs' | 'diffs' | 'related' | 'processes' | 'plan'
) => void;
};
function TabNavigation({ activeTab, setActiveTab }: Props) {
const { diff } = useContext(TaskDiffContext);
const { totalRelatedCount } = useContext(TaskRelatedTasksContext);
const { attemptData } = useContext(TaskAttemptDataContext);
const { isPlanningMode, planCount } = useTaskPlan();
return (
<div className="border-b bg-muted/30">
<div className="flex px-4">
@@ -31,6 +41,24 @@ function TabNavigation({ activeTab, setActiveTab }: Props) {
<MessageSquare className="h-4 w-4 mr-2" />
Logs
</button>
{isPlanningMode && (
<button
onClick={() => {
setActiveTab('plan');
}}
className={`flex items-center px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
activeTab === 'plan'
? 'border-primary text-primary bg-background'
: 'border-transparent text-muted-foreground hover:text-foreground hover:bg-muted/50'
}`}
>
<FileText className="h-4 w-4 mr-2" />
Plans
<span className="ml-2 px-1.5 py-0.5 text-xs bg-primary/10 text-primary rounded-full">
{planCount}
</span>
</button>
)}
<button
onClick={() => {
setActiveTab('diffs');

View File

@@ -11,6 +11,7 @@ import DiffTab from '@/components/tasks/TaskDetails/DiffTab.tsx';
import LogsTab from '@/components/tasks/TaskDetails/LogsTab.tsx';
import RelatedTasksTab from '@/components/tasks/TaskDetails/RelatedTasksTab.tsx';
import ProcessesTab from '@/components/tasks/TaskDetails/ProcessesTab.tsx';
import PlanTab from '@/components/tasks/TaskDetails/PlanTab.tsx';
import DeleteFileConfirmationDialog from '@/components/tasks/DeleteFileConfirmationDialog.tsx';
import TabNavigation from '@/components/tasks/TaskDetails/TabNavigation.tsx';
import CollapsibleToolbar from '@/components/tasks/TaskDetails/CollapsibleToolbar.tsx';
@@ -39,7 +40,7 @@ export function TaskDetailsPanel({
// Tab and collapsible state
const [activeTab, setActiveTab] = useState<
'logs' | 'diffs' | 'related' | 'processes'
'logs' | 'diffs' | 'related' | 'processes' | 'plan'
>('logs');
// Reset to logs tab when task changes
@@ -104,6 +105,8 @@ export function TaskDetailsPanel({
<RelatedTasksTab />
) : activeTab === 'processes' ? (
<ProcessesTab />
) : activeTab === 'plan' ? (
<PlanTab />
) : (
<LogsTab />
)}

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import { Globe2 } from 'lucide-react';
import { Globe2, AlertTriangle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
Dialog,
@@ -48,6 +48,12 @@ interface TaskFormDialogProps {
description: string,
status: TaskStatus
) => Promise<void>;
// Plan context for disabling task creation when no plan exists
planContext?: {
isPlanningMode: boolean;
canCreateTask: boolean;
latestProcessHasNoPlan: boolean;
};
}
export function TaskFormDialog({
@@ -59,6 +65,7 @@ export function TaskFormDialog({
onCreateTask,
onCreateAndStartTask,
onUpdateTask,
planContext,
}: TaskFormDialogProps) {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
@@ -71,6 +78,12 @@ export function TaskFormDialog({
const { config } = useConfig();
const isEditMode = Boolean(task);
// Check if task creation should be disabled based on plan context
const isPlanningModeWithoutPlan =
planContext?.isPlanningMode && !planContext?.canCreateTask;
const showPlanWarning =
planContext?.isPlanningMode && planContext?.latestProcessHasNoPlan;
useEffect(() => {
if (task) {
// Edit mode - populate with existing task data
@@ -248,6 +261,23 @@ export function TaskFormDialog({
</DialogTitle>
</DialogHeader>
<div className="space-y-4">
{/* Plan warning when in planning mode without plan */}
{showPlanWarning && (
<div className="p-4 rounded-lg border border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-950/20">
<div className="flex items-center gap-2 mb-2">
<AlertTriangle className="h-4 w-4 text-orange-600 dark:text-orange-400" />
<p className="text-sm font-semibold text-orange-800 dark:text-orange-300">
Plan Required
</p>
</div>
<p className="text-sm text-orange-700 dark:text-orange-400">
No plan was generated in the last execution attempt. Task
creation is disabled until a plan is available. Please generate
a plan first.
</p>
</div>
)}
<div>
<Label htmlFor="task-title" className="text-sm font-medium">
Title
@@ -372,19 +402,46 @@ export function TaskFormDialog({
variant="secondary"
onClick={handleSubmit}
disabled={
isSubmitting || isSubmittingAndStart || !title.trim()
isSubmitting ||
isSubmittingAndStart ||
!title.trim() ||
isPlanningModeWithoutPlan
}
className={
isPlanningModeWithoutPlan
? 'opacity-60 cursor-not-allowed'
: ''
}
title={
isPlanningModeWithoutPlan
? 'Plan required before creating task'
: undefined
}
>
{isPlanningModeWithoutPlan && (
<AlertTriangle className="h-4 w-4 mr-2" />
)}
{isSubmitting ? 'Creating...' : 'Create Task'}
</Button>
{onCreateAndStartTask && (
<Button
onClick={handleCreateAndStart}
disabled={
isSubmitting || isSubmittingAndStart || !title.trim()
isSubmitting ||
isSubmittingAndStart ||
!title.trim() ||
isPlanningModeWithoutPlan
}
className={`font-medium ${isPlanningModeWithoutPlan ? 'opacity-60 cursor-not-allowed bg-red-600 hover:bg-red-600' : ''}`}
title={
isPlanningModeWithoutPlan
? 'Plan required before creating and starting task'
: undefined
}
className="font-medium"
>
{isPlanningModeWithoutPlan && (
<AlertTriangle className="h-4 w-4 mr-2" />
)}
{isSubmittingAndStart
? 'Creating & Starting...'
: 'Create & Start'}

View File

@@ -1,6 +1,6 @@
import { Dispatch, SetStateAction, useCallback, useContext } from 'react';
import { Button } from '@/components/ui/button.tsx';
import { ArrowDown, Play, Settings2, X } from 'lucide-react';
import { ArrowDown, Play, Settings2, X, AlertTriangle } from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
@@ -13,6 +13,7 @@ import {
TaskAttemptDataContext,
TaskDetailsContext,
} from '@/components/context/taskDetailsContext.ts';
import { useTaskPlan } from '@/components/context/TaskPlanContext.ts';
import { useConfig } from '@/components/config-provider.tsx';
import BranchSelector from '@/components/tasks/BranchSelector.tsx';
import { useKeyboardShortcuts } from '@/lib/keyboard-shortcuts.ts';
@@ -58,6 +59,7 @@ function CreateAttempt({
}: Props) {
const { task, projectId } = useContext(TaskDetailsContext);
const { isAttemptRunning } = useContext(TaskAttemptDataContext);
const { isPlanningMode, canCreateTask } = useTaskPlan();
const { config } = useConfig();
const [showCreateAttemptConfirmation, setShowCreateAttemptConfirmation] =
@@ -155,6 +157,22 @@ function CreateAttempt({
</label>
</div>
{/* Plan warning when in planning mode without plan */}
{isPlanningMode && !canCreateTask && (
<div className="p-3 rounded-lg border border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-950/20">
<div className="flex items-center gap-2 mb-1">
<AlertTriangle className="h-4 w-4 text-orange-600 dark:text-orange-400" />
<p className="text-sm font-semibold text-orange-800 dark:text-orange-300">
Plan Required
</p>
</div>
<p className="text-xs text-orange-700 dark:text-orange-400">
Cannot start attempt - no plan was generated in the last
execution. Please generate a plan first.
</p>
</div>
)}
<div className="grid grid-cols-3 gap-3 items-end">
{/* Step 1: Choose Base Branch */}
<div className="space-y-1">
@@ -217,11 +235,29 @@ function CreateAttempt({
<div className="space-y-1">
<Button
onClick={handleCreateAttempt}
disabled={!createAttemptExecutor || isAttemptRunning}
disabled={
!createAttemptExecutor ||
isAttemptRunning ||
(isPlanningMode && !canCreateTask)
}
size="sm"
className="w-full text-xs gap-2"
className={`w-full text-xs gap-2 ${
isPlanningMode && !canCreateTask
? 'opacity-60 cursor-not-allowed bg-red-600 hover:bg-red-600'
: ''
}`}
title={
isPlanningMode && !canCreateTask
? 'Plan required before starting attempt'
: undefined
}
>
<Play className="h-3 w-3 mr-1.5" />
{isPlanningMode && !canCreateTask && (
<AlertTriangle className="h-3 w-3 mr-1.5" />
)}
{!(isPlanningMode && !canCreateTask) && (
<Play className="h-3 w-3 mr-1.5" />
)}
Start
</Button>
</div>

View File

@@ -63,6 +63,7 @@ import {
TaskRelatedTasksContext,
TaskSelectedAttemptContext,
} from '@/components/context/taskDetailsContext.ts';
import { useTaskPlan } from '@/components/context/TaskPlanContext.ts';
import { useConfig } from '@/components/config-provider.tsx';
import { useKeyboardShortcuts } from '@/lib/keyboard-shortcuts.ts';
import { useNavigate } from 'react-router-dom';
@@ -126,6 +127,7 @@ function CurrentAttempt({
const { executionState, fetchExecutionState } = useContext(
TaskExecutionStateContext
);
const { isPlanningMode, canCreateTask } = useTaskPlan();
const [isStartingDevServer, setIsStartingDevServer] = useState(false);
const [merging, setMerging] = useState(false);
@@ -741,7 +743,8 @@ function CurrentAttempt({
disabled={
isAttemptRunning ||
executionState?.execution_state === 'CodingAgentFailed' ||
executionState?.execution_state === 'SetupFailed'
executionState?.execution_state === 'SetupFailed' ||
(isPlanningMode && !canCreateTask)
}
size="sm"
className="bg-green-600 hover:bg-green-700 disabled:bg-gray-400 gap-1"

View File

@@ -10,6 +10,7 @@ import { TaskFormDialog } from '@/components/tasks/TaskFormDialog';
import { ProjectForm } from '@/components/projects/project-form';
import { TaskTemplateManager } from '@/components/TaskTemplateManager';
import { useKeyboardShortcuts } from '@/lib/keyboard-shortcuts';
import { useTaskPlan } from '@/components/context/TaskPlanContext';
import {
DropdownMenu,
DropdownMenuContent,
@@ -70,6 +71,10 @@ export function ProjectTasks() {
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
const [isPanelOpen, setIsPanelOpen] = useState(false);
// Plan context for task creation
const { isPlanningMode, canCreateTask, latestProcessHasNoPlan } =
useTaskPlan();
// Define task creation handler
const handleCreateNewTask = useCallback(() => {
setEditingTask(null);
@@ -540,6 +545,11 @@ export function ProjectTasks() {
onCreateAndStartTask={handleCreateAndStartTask}
onUpdateTask={handleUpdateTask}
initialTemplate={selectedTemplate}
planContext={{
isPlanningMode,
canCreateTask,
latestProcessHasNoPlan,
}}
/>
<ProjectForm