diff --git a/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx b/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx index 8a986f7..6d254a9 100644 --- a/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx +++ b/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx @@ -15,6 +15,8 @@ describe('AIMCPClientInstallPanel', () => { displayName: 'Claude Code', installed: false, matchesCurrent: false, + clientDetected: false, + clientCommand: 'claude', message: '未检测到 Claude Code 用户级 GoNavi MCP 配置', }, { @@ -22,6 +24,9 @@ describe('AIMCPClientInstallPanel', () => { displayName: 'Codex', installed: true, matchesCurrent: false, + clientDetected: true, + clientCommand: 'codex', + clientPath: 'C:/Users/mock/AppData/Roaming/npm/codex.cmd', message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新', configPath: '~/.codex/config.toml', command: 'gonavi-mcp-server', @@ -34,6 +39,9 @@ describe('AIMCPClientInstallPanel', () => { displayName: 'Codex', installed: true, matchesCurrent: false, + clientDetected: true, + clientCommand: 'codex', + clientPath: 'C:/Users/mock/AppData/Roaming/npm/codex.cmd', message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新', configPath: '~/.codex/config.toml', command: 'gonavi-mcp-server', @@ -60,31 +68,46 @@ describe('AIMCPClientInstallPanel', () => { expect(markup).toContain('第 2 步:确认状态后写入'); expect(markup).toContain('未接入'); expect(markup).toContain('需更新'); + expect(markup).toContain('命令已检测'); expect(markup).toContain('复制配置路径'); expect(markup).toContain('复制启动命令'); expect(markup).toContain('更新 Codex 配置'); + expect(markup).toContain('本机命令状态:已检测到 codex'); expect(markup).toContain('不会下载 Claude Code / Codex'); }); - it('shows an already-connected label when the selected client matches the current GoNavi path', () => { + it('shows an already-connected label and supports prewriting config when the client command is not detected locally', () => { const markup = renderToStaticMarkup( { />, ); - expect(markup).toContain('已接入'); - expect(markup).toContain('已接入 Claude Code'); + expect(markup).toContain('预写入 Claude Code 配置'); + expect(markup).toContain('未检测命令'); + expect(markup).toContain('未检测到本机 claude 命令'); }); }); diff --git a/frontend/src/components/ai/AIMCPClientInstallPanel.tsx b/frontend/src/components/ai/AIMCPClientInstallPanel.tsx index 30d9fcc..92374d4 100644 --- a/frontend/src/components/ai/AIMCPClientInstallPanel.tsx +++ b/frontend/src/components/ai/AIMCPClientInstallPanel.tsx @@ -55,6 +55,29 @@ const getStatusTone = (status: AIMCPClientInstallStatus | undefined, darkMode: b }; }; +const resolveClientCommandName = (status: AIMCPClientInstallStatus | undefined) => { + const command = String(status?.clientCommand || '').trim(); + if (command) { + return command; + } + return status?.client === 'codex' ? 'codex' : 'claude'; +}; + +const getClientDetectionTone = (status: AIMCPClientInstallStatus | undefined, darkMode: boolean) => { + if (status?.clientDetected) { + return { + label: '命令已检测', + color: '#16a34a', + bg: darkMode ? 'rgba(34,197,94,0.18)' : 'rgba(34,197,94,0.12)', + }; + } + return { + label: '未检测命令', + color: '#d97706', + bg: darkMode ? 'rgba(245,158,11,0.18)' : 'rgba(245,158,11,0.12)', + }; +}; + const getStatusSummary = (status: AIMCPClientInstallStatus | undefined) => { const label = status?.displayName || '这个客户端'; const messageText = String(status?.message || ''); @@ -80,6 +103,15 @@ const getClientCardDescription = (status: AIMCPClientInstallStatus | undefined) return '还没有写入 GoNavi MCP 配置。'; }; +const getClientDetectionSummary = (status: AIMCPClientInstallStatus | undefined) => { + const label = status?.displayName || '这个客户端'; + const commandName = resolveClientCommandName(status); + if (status?.clientDetected) { + return `已检测到本机 ${commandName} 命令,写入后可直接重启 ${label} 验证。`; + } + return `未检测到本机 ${commandName} 命令;如果你只装了桌面端或 CLI 还没加入 PATH,也可以先写配置,稍后再重启 ${label}。`; +}; + const resolveActionLabel = (status: AIMCPClientInstallStatus | undefined) => { const label = status?.displayName || '客户端'; if (status?.matchesCurrent) { @@ -88,6 +120,9 @@ const resolveActionLabel = (status: AIMCPClientInstallStatus | undefined) => { if (status?.installed) { return `更新 ${label} 配置`; } + if (status?.clientDetected === false) { + return `预写入 ${label} 配置`; + } return `写入 ${label} 配置`; }; @@ -149,7 +184,7 @@ const AIMCPClientInstallPanel: React.FC = ({
第 1 步:选择目标客户端
- 每次只处理一个外部客户端,先看状态,再决定是否写入。 + 每次只处理一个外部客户端,先看 GoNavi MCP 配置状态,再看本机有没有检测到对应命令。
@@ -157,6 +192,7 @@ const AIMCPClientInstallPanel: React.FC = ({ const client = status.client === 'codex' ? 'codex' : 'claude-code'; const active = selectedClient === client; const tone = getStatusTone(status, darkMode); + const detectionTone = getClientDetectionTone(status, darkMode); return ( ); @@ -261,6 +318,16 @@ const AIMCPClientInstallPanel: React.FC = ({
{getStatusSummary(selectedStatus)}
+
+ 本机命令状态:{selectedStatus?.clientDetected + ? `已检测到 ${resolveClientCommandName(selectedStatus)}` + : `未检测到 ${resolveClientCommandName(selectedStatus)},仍可先写配置`} +
+ {selectedStatus?.clientPath && ( +
+ 命令路径:{selectedStatus.clientPath} +
+ )}
检测结果:{selectedStatus?.message || '未检测到安装状态'}
@@ -307,7 +374,7 @@ const AIMCPClientInstallPanel: React.FC = ({
- 写入后重启对应客户端即可生效;如果当前路径已经一致,按钮会自动禁用,避免重复写入。 + 命令未检测到时也可以先写配置;后续装好 CLI 或把命令加入 PATH 后,重启对应客户端即可生效。当前路径已经一致时按钮会自动禁用,避免重复写入。