From 9aa4dd1f6f9b49e2012baf0cccaf3953cbf511ac Mon Sep 17 00:00:00 2001 From: catlog22 Date: Sat, 14 Feb 2026 23:41:28 +0800 Subject: [PATCH] feat: Add specialized context rendering for multi-cli-plan sessions - Add MultiCliContextPackage type for multi-cli-plan context-package.json - Create MultiCliContextContent component with sections for: - Selected solution (feasibility, effort, risk, CLI sources) - Implementation plan (approach, tasks with files, execution flow, milestones) - Dependencies (internal and external) - CLI consensus (agreements, resolved conflicts) - Technical concerns and constraints - Update ExpandedMultiCliPanel to detect and use appropriate context renderer - Add i18n translations for new context labels --- ccw/frontend/src/lib/api.ts | 36 +++ ccw/frontend/src/locales/en/lite-tasks.json | 17 +- ccw/frontend/src/locales/zh/lite-tasks.json | 17 +- ccw/frontend/src/pages/LiteTasksPage.tsx | 301 +++++++++++++++++++- 4 files changed, 367 insertions(+), 4 deletions(-) diff --git a/ccw/frontend/src/lib/api.ts b/ccw/frontend/src/lib/api.ts index 5d188a1b..94261fa5 100644 --- a/ccw/frontend/src/lib/api.ts +++ b/ccw/frontend/src/lib/api.ts @@ -2244,6 +2244,42 @@ export interface RoundSynthesis { user_feedback_incorporated?: string; } +// Multi-cli-plan context-package.json structure +export interface MultiCliContextPackage { + solution?: { + name: string; + source_cli: string[]; + feasibility: number; + effort: string; + risk: string; + summary: string; + }; + implementation_plan?: { + approach: string; + tasks: Array<{ + id: string; + name: string; + depends_on: string[]; + files: SolutionFileAction[]; + key_point: string | null; + }>; + execution_flow: string; + milestones: string[]; + }; + dependencies?: { + internal: string[]; + external: string[]; + }; + technical_concerns?: string[]; + consensus?: { + agreements: string[]; + resolved_conflicts?: string; + }; + constraints?: string[]; + task_description?: string; + session_id?: string; +} + export interface LiteTasksResponse { litePlan?: LiteTaskSession[]; liteFix?: LiteTaskSession[]; diff --git a/ccw/frontend/src/locales/en/lite-tasks.json b/ccw/frontend/src/locales/en/lite-tasks.json index 3f12fc12..373a452e 100644 --- a/ccw/frontend/src/locales/en/lite-tasks.json +++ b/ccw/frontend/src/locales/en/lite-tasks.json @@ -36,7 +36,22 @@ "agreements": "Agreements", "disagreements": "Disagreements", "resolution": "Resolution", - "clarificationQuestions": "Clarification Questions" + "clarificationQuestions": "Clarification Questions", + "context": { + "solution": "Selected Solution", + "implementationPlan": "Implementation Plan", + "approach": "Approach", + "tasks": "Tasks", + "milestones": "Milestones", + "dependencies": "Dependencies", + "internal": "Internal", + "external": "External", + "consensus": "CLI Consensus", + "resolvedConflicts": "Resolved Conflicts", + "technicalConcerns": "Technical Concerns", + "constraints": "Constraints", + "sessionInfo": "Session Info" + } }, "createdAt": "Created", "rounds": "rounds", diff --git a/ccw/frontend/src/locales/zh/lite-tasks.json b/ccw/frontend/src/locales/zh/lite-tasks.json index cdab33cd..963c99f0 100644 --- a/ccw/frontend/src/locales/zh/lite-tasks.json +++ b/ccw/frontend/src/locales/zh/lite-tasks.json @@ -36,7 +36,22 @@ "agreements": "共识", "disagreements": "分歧", "resolution": "解决方式", - "clarificationQuestions": "澄清问题" + "clarificationQuestions": "澄清问题", + "context": { + "solution": "选定方案", + "implementationPlan": "实现计划", + "approach": "实现路径", + "tasks": "任务列表", + "milestones": "里程碑", + "dependencies": "依赖项", + "internal": "内部依赖", + "external": "外部依赖", + "consensus": "CLI 共识", + "resolvedConflicts": "已解决分歧", + "technicalConcerns": "技术关注点", + "constraints": "约束条件", + "sessionInfo": "会话信息" + } }, "createdAt": "创建时间", "rounds": "轮", diff --git a/ccw/frontend/src/pages/LiteTasksPage.tsx b/ccw/frontend/src/pages/LiteTasksPage.tsx index 24a0c666..ef77761d 100644 --- a/ccw/frontend/src/pages/LiteTasksPage.tsx +++ b/ccw/frontend/src/pages/LiteTasksPage.tsx @@ -39,6 +39,12 @@ import { Timer, Sparkles, CheckCheck, + Route, + Flag, + AlertOctagon, + Link2, + ShieldCheck, + Settings2, } from 'lucide-react'; import { useLiteTasks } from '@/hooks/useLiteTasks'; import { Button } from '@/components/ui/Button'; @@ -46,7 +52,7 @@ import { Badge } from '@/components/ui/Badge'; import { Card, CardContent } from '@/components/ui/Card'; import { TabsNavigation } from '@/components/ui/TabsNavigation'; import { TaskDrawer } from '@/components/shared/TaskDrawer'; -import { fetchLiteSessionContext, type LiteTask, type LiteTaskSession, type LiteSessionContext, type RoundSynthesis } from '@/lib/api'; +import { fetchLiteSessionContext, type LiteTask, type LiteTaskSession, type LiteSessionContext, type RoundSynthesis, type MultiCliContextPackage } from '@/lib/api'; import { LiteContextContent } from '@/components/lite-tasks/LiteContextContent'; import { useNavigate } from 'react-router-dom'; @@ -540,6 +546,292 @@ function RoundDetailCard({ round, isLast }: { round: RoundSynthesis; isLast: boo ); } +/** + * MultiCliContextContent - Display context-package.json for multi-cli-plan sessions + */ +function MultiCliContextContent({ data }: { data: MultiCliContextPackage }) { + const { formatMessage } = useIntl(); + const [expandedSections, setExpandedSections] = React.useState>(new Set(['solution', 'plan'])); + + const toggleSection = (section: string) => { + setExpandedSections(prev => { + const next = new Set(prev); + if (next.has(section)) { + next.delete(section); + } else { + next.add(section); + } + return next; + }); + }; + + const isExpanded = (section: string) => expandedSections.has(section); + + // Section wrapper component + const Section = ({ id, icon, title, badge, children, defaultExpanded = true }: { + id: string; + icon: React.ReactNode; + title: string; + badge?: React.ReactNode; + children: React.ReactNode; + defaultExpanded?: boolean; + }) => { + // Initialize expanded state based on defaultExpanded if not already in set + React.useEffect(() => { + if (defaultExpanded && !expandedSections.has(id)) { + setExpandedSections(prev => new Set(prev).add(id)); + } + }, [id, defaultExpanded]); + + return ( + + + {isExpanded(id) && ( + + {children} + + )} + + ); + }; + + return ( +
+ {/* Solution Section */} + {data.solution && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.solution' })} + badge={ +
+ {data.solution.source_cli.map(cli => ( + {cli} + ))} +
+ } + > +
+

{data.solution.name}

+

{data.solution.summary}

+
+ + {Math.round(data.solution.feasibility * 100)}% {formatMessage({ id: 'liteTasks.multiCli.feasible' })} + + {data.solution.effort} + + {data.solution.risk} {formatMessage({ id: 'liteTasks.multiCli.risk' })} + +
+
+
+ )} + + {/* Implementation Plan Section */} + {data.implementation_plan && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.implementationPlan' })} + badge={{data.implementation_plan.tasks?.length || 0} tasks} + > +
+ {/* Approach */} +
+ {formatMessage({ id: 'liteTasks.multiCli.context.approach' })}:{' '} + {data.implementation_plan.approach} +
+ + {/* Execution Flow */} + {data.implementation_plan.execution_flow && ( +
+ {data.implementation_plan.execution_flow} +
+ )} + + {/* Tasks */} + {data.implementation_plan.tasks && data.implementation_plan.tasks.length > 0 && ( +
+
{formatMessage({ id: 'liteTasks.multiCli.context.tasks' })}
+
+ {data.implementation_plan.tasks.map((task, idx) => ( +
+ {task.id} +
+
{task.name}
+ {task.key_point && ( +
{task.key_point}
+ )} + {task.files && task.files.length > 0 && ( +
+ {task.files.map((f, i) => ( + + {f.action === 'create' ? '+' : f.action === 'delete' ? '-' : '~'} {f.file} + + ))} +
+ )} +
+
+ ))} +
+
+ )} + + {/* Milestones */} + {data.implementation_plan.milestones && data.implementation_plan.milestones.length > 0 && ( +
+
+ + {formatMessage({ id: 'liteTasks.multiCli.context.milestones' })} +
+
+ {data.implementation_plan.milestones.map((milestone, i) => ( + {milestone} + ))} +
+
+ )} +
+
+ )} + + {/* Dependencies Section */} + {data.dependencies && (data.dependencies.internal?.length > 0 || data.dependencies.external?.length > 0) && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.dependencies' })} + badge={{(data.dependencies.internal?.length || 0) + (data.dependencies.external?.length || 0)}} + > +
+ {data.dependencies.internal && data.dependencies.internal.length > 0 && ( +
+
{formatMessage({ id: 'liteTasks.multiCli.context.internal' })}
+
+ {data.dependencies.internal.map((dep, i) => ( + {dep} + ))} +
+
+ )} + {data.dependencies.external && data.dependencies.external.length > 0 && ( +
+
{formatMessage({ id: 'liteTasks.multiCli.context.external' })}
+
+ {data.dependencies.external.map((dep, i) => ( + {dep} + ))} +
+
+ )} +
+
+ )} + + {/* Consensus Section */} + {data.consensus && data.consensus.agreements?.length > 0 && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.consensus' })} + badge={{data.consensus.agreements.length}} + > +
+
    + {data.consensus.agreements.map((agreement, i) => ( +
  • + + {agreement} +
  • + ))} +
+ {data.consensus.resolved_conflicts && ( +
+ {formatMessage({ id: 'liteTasks.multiCli.context.resolvedConflicts' })}:{' '} + {data.consensus.resolved_conflicts} +
+ )} +
+
+ )} + + {/* Technical Concerns Section */} + {data.technical_concerns && data.technical_concerns.length > 0 && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.technicalConcerns' })} + badge={{data.technical_concerns.length}} + > +
    + {data.technical_concerns.map((concern, i) => ( +
  • + + {concern} +
  • + ))} +
+
+ )} + + {/* Constraints Section */} + {data.constraints && data.constraints.length > 0 && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.constraints' })} + badge={{data.constraints.length}} + > +
+ {data.constraints.map((constraint, i) => ( + {constraint} + ))} +
+
+ )} + + {/* Session Info */} + {(data.task_description || data.session_id) && ( +
} + title={formatMessage({ id: 'liteTasks.multiCli.context.sessionInfo' })} + defaultExpanded={false} + > +
+ {data.task_description && ( +
+ {formatMessage({ id: 'liteTasks.contextPanel.taskDescription' })}:{' '} + {data.task_description} +
+ )} + {data.session_id && ( +
+ {formatMessage({ id: 'liteTasks.contextPanel.sessionId' })}:{' '} + {data.session_id} +
+ )} +
+
+ )} +
+ ); +} + type MultiCliExpandedTab = 'tasks' | 'discussion' | 'context'; /** @@ -882,7 +1174,12 @@ function ExpandedMultiCliPanel({ )} {!contextLoading && !contextError && contextData && ( - + // Detect multi-cli-plan context by checking for solution field + (contextData.context as MultiCliContextPackage)?.solution ? ( + + ) : ( + + ) )} {!contextLoading && !contextError && !contextData && !session.path && (