import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { getMentorContext, getProjectNotes, type MentorContext, type ProjectNote } from "../../lib/commands"; interface Props { projectId: string; projectName: string; onClose: () => void; } export function MentorPanel({ projectId, projectName, onClose }: Props) { const [copied, setCopied] = useState(false); const { data: ctx, isLoading, isError, error } = useQuery({ queryKey: ["mentorContext", projectId], queryFn: () => getMentorContext(projectId), staleTime: 30000, retry: false, }); const { data: notes = [] } = useQuery({ queryKey: ["projectNotes", projectId], queryFn: () => getProjectNotes(projectId), staleTime: 30000, retry: false, }); const handleCopy = () => { if (!ctx) return; navigator.clipboard.writeText(ctx.mentor_markdown).catch(() => {}); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return (
{/* Header */}

项目导师

{projectName}

{/* Body */}
{isLoading && (
加载中…
)} {isError && (
{String(error)}
)} {ctx && (
{/* 本地健康 */}

本地健康

{ctx.health.tech_stack && (
技术栈 {ctx.health.tech_stack}
)} {ctx.health.last_commit && (
最近提交 {ctx.health.last_commit}
)}
{/* 蓝图状态 */}

蓝图状态

{ctx.blueprint_summary ? ( ctx.blueprint_summary.module_count === 0 ? (

蓝图存在,但暂无模块数据。

) : (
{[ { label: "模块", value: `${ctx.blueprint_summary.done_count}/${ctx.blueprint_summary.module_count}` }, { label: "任务", value: `${ctx.blueprint_summary.done_tasks}/${ctx.blueprint_summary.total_tasks}` }, { label: "可派发", value: String(ctx.blueprint_summary.dispatchable) }, { label: "进行中", value: String(ctx.blueprint_summary.in_progress) }, { label: "已规划", value: String(ctx.blueprint_summary.planned) }, ].map(({ label, value }) => (
{label} {value}
))}
) ) : (

该项目尚未接入蓝图(无 .blueprint/manifest.yaml)。

)}
{/* 近期错误日志 */}

近期错误日志

{ctx.recent_errors.length === 0 ? (

无错误记录 ✅

) : (
    {ctx.recent_errors.map((e, i) => (
  • {e.level === "error" ? "❌" : "⚠️"} {e.ts.slice(0, 19)} [{e.category}] {e.message}
  • ))}
)}
{/* AI 笔记 */}

AI 笔记

{notes.length === 0 ? (

暂无笔记。Claude 通过 MCP 分析项目后会在此留下洞察。

) : (
    {notes.map((note) => (
  • {note.source === "mcp" ? "Claude · MCP" : "手动"} {note.created_at.slice(0, 16)}

    {note.content}

  • ))}
)}
)}
{/* Footer */} {ctx && (

生成导师报告后粘贴给 Claude,即可获得质量分析和成长建议

)}
); } // ── 辅助组件 ────────────────────────────────────────────────────────────────── function HealthRow({ ok, label, ok_text, fail_text, warn, }: { ok: boolean; label: string; ok_text: string; fail_text: string; warn?: boolean; }) { const icon = ok ? "✅" : warn ? "⚠️" : "❌"; const textColor = ok ? "text-green-700" : warn ? "text-yellow-700" : "text-red-700"; return (
{label} {icon} {ok ? ok_text : fail_text}
); }