mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +08:00
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
This commit is contained in:
@@ -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[];
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "轮",
|
||||
|
||||
@@ -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<Set<string>>(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 (
|
||||
<Card className="border-border">
|
||||
<button
|
||||
type="button"
|
||||
className="w-full flex items-center gap-2 p-3 text-left hover:bg-muted/50 transition-colors"
|
||||
onClick={() => toggleSection(id)}
|
||||
>
|
||||
<span className="text-muted-foreground">{icon}</span>
|
||||
<span className="text-sm font-medium text-foreground flex-1">{title}</span>
|
||||
{badge}
|
||||
{isExpanded(id) ? (
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
{isExpanded(id) && (
|
||||
<CardContent className="px-3 pb-3 pt-0">
|
||||
{children}
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{/* Solution Section */}
|
||||
{data.solution && (
|
||||
<Section
|
||||
id="solution"
|
||||
icon={<Sparkles className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.solution' })}
|
||||
badge={
|
||||
<div className="flex items-center gap-1">
|
||||
{data.solution.source_cli.map(cli => (
|
||||
<Badge key={cli} variant="outline" className="text-[10px] px-1.5 py-0">{cli}</Badge>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-medium text-foreground text-sm">{data.solution.name}</h4>
|
||||
<p className="text-xs text-muted-foreground">{data.solution.summary}</p>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant="success" className="text-[10px]">
|
||||
{Math.round(data.solution.feasibility * 100)}% {formatMessage({ id: 'liteTasks.multiCli.feasible' })}
|
||||
</Badge>
|
||||
<Badge variant="warning" className="text-[10px]">{data.solution.effort}</Badge>
|
||||
<Badge variant={data.solution.risk === 'high' ? 'destructive' : data.solution.risk === 'low' ? 'success' : 'warning'} className="text-[10px]">
|
||||
{data.solution.risk} {formatMessage({ id: 'liteTasks.multiCli.risk' })}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Implementation Plan Section */}
|
||||
{data.implementation_plan && (
|
||||
<Section
|
||||
id="plan"
|
||||
icon={<Route className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.implementationPlan' })}
|
||||
badge={<Badge variant="secondary" className="text-[10px]">{data.implementation_plan.tasks?.length || 0} tasks</Badge>}
|
||||
>
|
||||
<div className="space-y-3">
|
||||
{/* Approach */}
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{formatMessage({ id: 'liteTasks.multiCli.context.approach' })}:</span>{' '}
|
||||
{data.implementation_plan.approach}
|
||||
</div>
|
||||
|
||||
{/* Execution Flow */}
|
||||
{data.implementation_plan.execution_flow && (
|
||||
<div className="p-2 bg-muted/50 rounded-md">
|
||||
<code className="text-xs font-mono text-foreground">{data.implementation_plan.execution_flow}</code>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tasks */}
|
||||
{data.implementation_plan.tasks && data.implementation_plan.tasks.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<div className="text-xs font-medium text-foreground">{formatMessage({ id: 'liteTasks.multiCli.context.tasks' })}</div>
|
||||
<div className="space-y-1.5">
|
||||
{data.implementation_plan.tasks.map((task, idx) => (
|
||||
<div key={task.id || idx} className="flex items-start gap-2 p-2 bg-muted/30 rounded-md">
|
||||
<Badge variant="outline" className="text-[10px] font-mono shrink-0">{task.id}</Badge>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs font-medium text-foreground">{task.name}</div>
|
||||
{task.key_point && (
|
||||
<div className="text-[10px] text-muted-foreground mt-0.5">{task.key_point}</div>
|
||||
)}
|
||||
{task.files && task.files.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-1">
|
||||
{task.files.map((f, i) => (
|
||||
<Badge key={i} variant="secondary" className="text-[9px] font-mono">
|
||||
{f.action === 'create' ? '+' : f.action === 'delete' ? '-' : '~'} {f.file}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Milestones */}
|
||||
{data.implementation_plan.milestones && data.implementation_plan.milestones.length > 0 && (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center gap-2 text-xs font-medium text-foreground">
|
||||
<Flag className="h-3 w-3" />
|
||||
{formatMessage({ id: 'liteTasks.multiCli.context.milestones' })}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{data.implementation_plan.milestones.map((milestone, i) => (
|
||||
<Badge key={i} variant="info" className="text-[10px]">{milestone}</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Dependencies Section */}
|
||||
{data.dependencies && (data.dependencies.internal?.length > 0 || data.dependencies.external?.length > 0) && (
|
||||
<Section
|
||||
id="deps"
|
||||
icon={<Link2 className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.dependencies' })}
|
||||
badge={<Badge variant="secondary" className="text-[10px]">{(data.dependencies.internal?.length || 0) + (data.dependencies.external?.length || 0)}</Badge>}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
{data.dependencies.internal && data.dependencies.internal.length > 0 && (
|
||||
<div>
|
||||
<div className="text-xs font-medium text-foreground mb-1">{formatMessage({ id: 'liteTasks.multiCli.context.internal' })}</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{data.dependencies.internal.map((dep, i) => (
|
||||
<Badge key={i} variant="outline" className="text-[10px] font-mono">{dep}</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{data.dependencies.external && data.dependencies.external.length > 0 && (
|
||||
<div>
|
||||
<div className="text-xs font-medium text-foreground mb-1">{formatMessage({ id: 'liteTasks.multiCli.context.external' })}</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{data.dependencies.external.map((dep, i) => (
|
||||
<Badge key={i} variant="secondary" className="text-[10px] font-mono">{dep}</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Consensus Section */}
|
||||
{data.consensus && data.consensus.agreements?.length > 0 && (
|
||||
<Section
|
||||
id="consensus"
|
||||
icon={<ShieldCheck className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.consensus' })}
|
||||
badge={<Badge variant="success" className="text-[10px]">{data.consensus.agreements.length}</Badge>}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<ul className="space-y-1">
|
||||
{data.consensus.agreements.map((agreement, i) => (
|
||||
<li key={i} className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||
<CheckCircle2 className="h-3 w-3 text-success shrink-0 mt-0.5" />
|
||||
<span>{agreement}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{data.consensus.resolved_conflicts && (
|
||||
<div className="mt-2 p-2 bg-success/10 border border-success/20 rounded-md text-xs text-muted-foreground">
|
||||
<span className="font-medium text-success">{formatMessage({ id: 'liteTasks.multiCli.context.resolvedConflicts' })}:</span>{' '}
|
||||
{data.consensus.resolved_conflicts}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Technical Concerns Section */}
|
||||
{data.technical_concerns && data.technical_concerns.length > 0 && (
|
||||
<Section
|
||||
id="concerns"
|
||||
icon={<AlertOctagon className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.technicalConcerns' })}
|
||||
badge={<Badge variant="warning" className="text-[10px]">{data.technical_concerns.length}</Badge>}
|
||||
>
|
||||
<ul className="space-y-1">
|
||||
{data.technical_concerns.map((concern, i) => (
|
||||
<li key={i} className="flex items-start gap-2 text-xs text-muted-foreground">
|
||||
<AlertCircle className="h-3 w-3 text-warning shrink-0 mt-0.5" />
|
||||
<span>{concern}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Constraints Section */}
|
||||
{data.constraints && data.constraints.length > 0 && (
|
||||
<Section
|
||||
id="constraints"
|
||||
icon={<Settings2 className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.constraints' })}
|
||||
badge={<Badge variant="secondary" className="text-[10px]">{data.constraints.length}</Badge>}
|
||||
>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{data.constraints.map((constraint, i) => (
|
||||
<Badge key={i} variant="outline" className="text-[10px]">{constraint}</Badge>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Session Info */}
|
||||
{(data.task_description || data.session_id) && (
|
||||
<Section
|
||||
id="info"
|
||||
icon={<Package className="h-4 w-4" />}
|
||||
title={formatMessage({ id: 'liteTasks.multiCli.context.sessionInfo' })}
|
||||
defaultExpanded={false}
|
||||
>
|
||||
<div className="space-y-2 text-xs">
|
||||
{data.task_description && (
|
||||
<div className="text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{formatMessage({ id: 'liteTasks.contextPanel.taskDescription' })}:</span>{' '}
|
||||
{data.task_description}
|
||||
</div>
|
||||
)}
|
||||
{data.session_id && (
|
||||
<div className="text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{formatMessage({ id: 'liteTasks.contextPanel.sessionId' })}:</span>{' '}
|
||||
<span className="font-mono bg-muted/50 px-1.5 py-0.5 rounded">{data.session_id}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type MultiCliExpandedTab = 'tasks' | 'discussion' | 'context';
|
||||
|
||||
/**
|
||||
@@ -882,7 +1174,12 @@ function ExpandedMultiCliPanel({
|
||||
</div>
|
||||
)}
|
||||
{!contextLoading && !contextError && contextData && (
|
||||
// Detect multi-cli-plan context by checking for solution field
|
||||
(contextData.context as MultiCliContextPackage)?.solution ? (
|
||||
<MultiCliContextContent data={contextData.context as MultiCliContextPackage} />
|
||||
) : (
|
||||
<LiteContextContent contextData={contextData} session={session} />
|
||||
)
|
||||
)}
|
||||
{!contextLoading && !contextError && !contextData && !session.path && (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-center">
|
||||
|
||||
Reference in New Issue
Block a user