Skip to content

Commit 0cfee90

Browse files
author
catlog22
committed
feat: Enhance multi-cli-plan support with new synthesis types and update related components
1 parent 3a9a66a commit 0cfee90

7 files changed

Lines changed: 132 additions & 43 deletions

File tree

ccw/frontend/src/components/cli-viewer/ContentArea.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function EmptyTabState() {
3232
const { formatMessage } = useIntl();
3333

3434
return (
35-
<div className="h-full flex flex-col items-center justify-center text-muted-foreground gap-4">
35+
<div className="flex-1 flex flex-col items-center justify-center text-muted-foreground gap-4">
3636
<Terminal className="h-12 w-12 opacity-30" />
3737
<div className="text-center">
3838
<p className="text-sm font-medium">
@@ -56,7 +56,7 @@ function ExecutionNotFoundState({ executionId }: { executionId: string }) {
5656
const { formatMessage } = useIntl();
5757

5858
return (
59-
<div className="h-full flex flex-col items-center justify-center text-muted-foreground gap-4">
59+
<div className="flex-1 flex flex-col items-center justify-center text-muted-foreground gap-4">
6060
<Terminal className="h-12 w-12 opacity-30" />
6161
<div className="text-center">
6262
<p className="text-sm font-medium">
@@ -113,7 +113,7 @@ function CliOutputDisplay({ execution, executionId }: { execution: CliExecutionS
113113

114114
if (!execution.output || execution.output.length === 0) {
115115
return (
116-
<div className="h-full flex flex-col items-center justify-center text-muted-foreground gap-4">
116+
<div className="flex-1 flex flex-col items-center justify-center text-muted-foreground gap-4">
117117
<Terminal className="h-12 w-12 opacity-30" />
118118
<div className="text-center">
119119
<p className="text-sm">
@@ -185,7 +185,7 @@ export function ContentArea({ paneId, className }: ContentAreaProps) {
185185
return (
186186
<div
187187
className={cn(
188-
'flex-1 overflow-hidden',
188+
'flex-1 min-h-0 flex flex-col overflow-hidden',
189189
'bg-background',
190190
className
191191
)}

ccw/frontend/src/components/cli-viewer/LayoutContainer.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// ========================================
44
// Manages allotment-based split panes for CLI viewer
55

6-
import { useCallback, useMemo, useRef, useEffect } from 'react';
6+
import { useMemo, useRef, useEffect, useCallback } from 'react';
77
import { Allotment } from 'allotment';
88
import 'allotment/dist/style.css';
99
import { cn } from '@/lib/utils';
@@ -46,13 +46,6 @@ function isPaneId(child: PaneId | AllotmentLayoutGroup): child is PaneId {
4646
function LayoutGroupRenderer({ group, minSize, onSizeChange }: LayoutGroupRendererProps) {
4747
const panes = useViewerPanes();
4848

49-
const handleChange = useCallback(
50-
(sizes: number[]) => {
51-
onSizeChange(sizes);
52-
},
53-
[onSizeChange]
54-
);
55-
5649
// Check if all panes in this group exist
5750
const validChildren = useMemo(() => {
5851
return group.children.filter(child => {
@@ -71,7 +64,7 @@ function LayoutGroupRenderer({ group, minSize, onSizeChange }: LayoutGroupRender
7164
<Allotment
7265
vertical={group.direction === 'vertical'}
7366
defaultSizes={group.sizes}
74-
onChange={handleChange}
67+
onChange={onSizeChange}
7568
className="h-full"
7669
>
7770
{validChildren.map((child, index) => (

ccw/frontend/src/components/shared/CliStreamMonitor/MonitorBody/index.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,12 @@ function MonitorBodyComponent(
104104
return (
105105
<div
106106
ref={containerRef}
107-
className={cn('flex-1 overflow-y-auto bg-background relative', className)}
107+
className={cn('flex-1 min-h-0 overflow-y-auto bg-background relative', className)}
108108
onScroll={handleScroll}
109109
>
110-
<div className="h-full">
111-
{children}
112-
{/* Anchor for scroll to bottom */}
113-
<div ref={logsEndRef} />
114-
</div>
110+
{children}
111+
{/* Anchor for scroll to bottom */}
112+
<div ref={logsEndRef} />
115113

116114
{/* Show scroll button when user is not at bottom */}
117115
{showScrollButton && isUserScrolling && (

ccw/frontend/src/components/shared/TaskDrawer.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,16 @@ export function TaskDrawer({ task, isOpen, onClose }: TaskDrawerProps) {
8282
return () => window.removeEventListener('keydown', handleEsc);
8383
}, [isOpen, onClose]);
8484

85-
if (!task || !isOpen) {
86-
return null;
87-
}
88-
8985
// Normalize task to unified flat format (handles old nested, new flat, and raw LiteTask/TaskData)
86+
// MUST be called before early return to satisfy React hooks rules
9087
const nt = React.useMemo(
91-
() => normalizeTask(task as unknown as Record<string, unknown>),
88+
() => task ? normalizeTask(task as unknown as Record<string, unknown>) : null,
9289
[task],
9390
);
91+
92+
if (!task || !isOpen || !nt) {
93+
return null;
94+
}
9495
const taskId = nt.task_id || 'N/A';
9596
const taskTitle = nt.title || 'Untitled Task';
9697
const taskDescription = nt.description;

ccw/frontend/src/lib/api.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,6 +2173,75 @@ export interface LiteTaskSession {
21732173
status?: string;
21742174
createdAt?: string;
21752175
updatedAt?: string;
2176+
// Multi-cli-plan specific fields
2177+
rounds?: RoundSynthesis[];
2178+
}
2179+
2180+
// Multi-cli-plan synthesis types
2181+
export interface SolutionFileAction {
2182+
file: string;
2183+
line: number;
2184+
action: 'modify' | 'create' | 'delete';
2185+
}
2186+
2187+
export interface SolutionTask {
2188+
id: string;
2189+
name: string;
2190+
depends_on: string[];
2191+
files: SolutionFileAction[];
2192+
key_point: string | null;
2193+
}
2194+
2195+
export interface Solution {
2196+
name: string;
2197+
source_cli: string[];
2198+
feasibility: number;
2199+
effort: string;
2200+
risk: string;
2201+
summary: string;
2202+
pros: string[];
2203+
cons: string[];
2204+
affected_files: SolutionFileAction[];
2205+
implementation_plan: {
2206+
approach: string;
2207+
tasks: SolutionTask[];
2208+
execution_flow: string;
2209+
milestones: string[];
2210+
};
2211+
dependencies: {
2212+
internal: string[];
2213+
external: string[];
2214+
};
2215+
technical_concerns: string[];
2216+
}
2217+
2218+
export interface SynthesisConvergence {
2219+
score: number;
2220+
new_insights: boolean;
2221+
recommendation: 'converged' | 'continue' | 'user_input_needed';
2222+
rationale: string;
2223+
}
2224+
2225+
export interface SynthesisCrossVerification {
2226+
agreements: string[];
2227+
disagreements: Array<{
2228+
topic: string;
2229+
gemini: string;
2230+
codex: string;
2231+
resolution: string | null;
2232+
}>;
2233+
resolution: string;
2234+
}
2235+
2236+
export interface RoundSynthesis {
2237+
round: number;
2238+
timestamp: string;
2239+
cli_executions: Record<string, { status: string; duration_ms: number; model: string }>;
2240+
solutions: Solution[];
2241+
convergence: SynthesisConvergence;
2242+
cross_verification: SynthesisCrossVerification;
2243+
clarification_questions: string[];
2244+
user_feedback_incorporated?: string;
21762245
}
21772246

21782247
export interface LiteTasksResponse {

ccw/frontend/src/pages/LiteTasksPage.tsx

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,25 @@ import {
3030
Clock,
3131
AlertCircle,
3232
FileCode,
33+
ThumbsUp,
34+
ThumbsDown,
35+
Target,
36+
GitCompare,
37+
HelpCircle,
38+
Cpu,
39+
Timer,
40+
Sparkles,
41+
Layers,
42+
CheckCheck,
43+
ArrowRight,
3344
} from 'lucide-react';
3445
import { useLiteTasks } from '@/hooks/useLiteTasks';
3546
import { Button } from '@/components/ui/Button';
3647
import { Badge } from '@/components/ui/Badge';
3748
import { Card, CardContent } from '@/components/ui/Card';
3849
import { TabsNavigation } from '@/components/ui/TabsNavigation';
3950
import { TaskDrawer } from '@/components/shared/TaskDrawer';
40-
import { fetchLiteSessionContext, type LiteTask, type LiteTaskSession, type LiteSessionContext } from '@/lib/api';
51+
import { fetchLiteSessionContext, type LiteTask, type LiteTaskSession, type LiteSessionContext, type RoundSynthesis } from '@/lib/api';
4152
import { LiteContextContent } from '@/components/lite-tasks/LiteContextContent';
4253
import { useNavigate } from 'react-router-dom';
4354

@@ -532,25 +543,36 @@ function ExpandedMultiCliPanel({
532543
{/* Discussion Tab */}
533544
{activeTab === 'discussion' && (
534545
<div className="space-y-3">
535-
<Card className="border-border">
536-
<CardContent className="p-4">
537-
<div className="flex items-center gap-2 mb-3">
538-
<MessagesSquare className="h-5 w-5 text-primary" />
539-
<h4 className="font-medium text-foreground">
540-
{formatMessage({ id: 'liteTasks.multiCli.discussionRounds' })}
541-
</h4>
542-
<Badge variant="secondary" className="text-xs">{roundCount} {formatMessage({ id: 'liteTasks.rounds' })}</Badge>
543-
</div>
544-
<p className="text-sm text-muted-foreground">
545-
{formatMessage({ id: 'liteTasks.multiCli.discussionDescription' })}
546-
</p>
547-
{goal && (
548-
<div className="mt-3 p-3 bg-muted/50 rounded-lg">
549-
<p className="text-sm text-foreground">{goal}</p>
546+
{/* Rounds Detail */}
547+
{session.rounds && session.rounds.length > 0 ? (
548+
session.rounds.map((round, idx) => (
549+
<RoundDetailCard
550+
key={round.round || idx}
551+
round={round}
552+
isLast={idx === session.rounds!.length - 1}
553+
/>
554+
))
555+
) : (
556+
<Card className="border-border">
557+
<CardContent className="p-4">
558+
<div className="flex items-center gap-2 mb-3">
559+
<MessagesSquare className="h-5 w-5 text-primary" />
560+
<h4 className="font-medium text-foreground">
561+
{formatMessage({ id: 'liteTasks.multiCli.discussionRounds' })}
562+
</h4>
563+
<Badge variant="secondary" className="text-xs">{roundCount} {formatMessage({ id: 'liteTasks.rounds' })}</Badge>
550564
</div>
551-
)}
552-
</CardContent>
553-
</Card>
565+
<p className="text-sm text-muted-foreground">
566+
{formatMessage({ id: 'liteTasks.multiCli.discussionDescription' })}
567+
</p>
568+
{goal && (
569+
<div className="mt-3 p-3 bg-muted/50 rounded-lg">
570+
<p className="text-sm text-foreground">{goal}</p>
571+
</div>
572+
)}
573+
</CardContent>
574+
</Card>
575+
)}
554576
</div>
555577
)}
556578

ccw/src/core/lite-scanner.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ interface LiteTaskDetail {
118118
explorations: unknown[];
119119
clarifications: unknown | null;
120120
diagnoses?: Diagnoses;
121+
// Multi-cli-plan specific
122+
rounds?: RoundSynthesis[];
123+
latestSynthesis?: RoundSynthesis | null;
121124
}
122125

123126
/**
@@ -923,6 +926,9 @@ export async function getLiteTaskDetail(workflowDir: string, type: string, sessi
923926
tasks: extractTasksFromSyntheses(syntheses),
924927
explorations,
925928
clarifications,
929+
// Multi-cli-plan specific fields
930+
rounds: syntheses,
931+
latestSynthesis,
926932
};
927933

928934
return detail;

0 commit comments

Comments
 (0)