Skip to content

Commit 281e7f9

Browse files
7418claude
andcommitted
fix: skills/MCP/completion system overhaul — separate skill kinds, fix MCP remote servers, deduplicate completion processing
Skills system: - Replace `CommandBadge.isSkill: boolean` with `kind: SkillKind` (agent_skill | slash_command | sdk_command | codepilot_command) - handleSubmit dispatches by kind: agent_skill retains skill name in prompt, slash/sdk commands pass through to SDK, codepilot commands expand via COMMAND_PROMPTS - Popover groups commands into Commands / Slash Commands / Agent Skills - API `/api/skills` returns `kind` field; `displayOverride` persisted through full chain (frontend → API → DB) - Forward SDK `system:init` slash_commands/skills metadata to frontend via SSE MCP management: - Fix remote server (SSE/HTTP) creation: validate `url` instead of `command` - Fix dual-config read/write asymmetry: PUT routes servers by `_source` to correct file (~/.claude.json vs ~/.claude/settings.json) - DELETE checks both config files; POST checks both for name collision - McpManager preserves `_source` tags on edit/rename; JSON editor only manages settings.json servers Completion processing: - Extract core logic into `onboarding-processor.ts` and `checkin-processor.ts` (shared by route handlers and server-side detection) - Remove frontend `detectAssistantCompletion` from ChatView — server-side path in `finally` block is now the single source of truth - Replace hard-coded `fetch("http://localhost:PORT/...")` with direct function imports - Both processors are internally idempotent (check state before processing) Other: - `settingSources` always includes 'user' so SDK loads ~/.claude/skills/ - Add 24 unit tests for SkillKind dispatch logic - Add onboarding-completion extraction tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 789b453 commit 281e7f9

File tree

24 files changed

+1970
-463
lines changed

24 files changed

+1970
-463
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Skills / MCP / Completion 系统修复
2+
3+
**状态:** 已完成
4+
**日期:** 2026-03-10
5+
**关联调研:** `docs/research/skills-agent-sdk-review-2026-03-10.md`
6+
7+
## 问题概述
8+
9+
三个子系统存在多处 bug,部分为 P0(功能不可用)级别:
10+
11+
### 1. Skills 系统:Agent Skills 与 Slash Commands 混淆
12+
13+
**根因:** `CommandBadge.isSkill: boolean` 只区分"内置 vs 非内置",无法区分 agent_skill / slash_command / sdk_command / codepilot_command 四种执行语义。
14+
15+
**症状:**
16+
- Agent Skill 的 SKILL.md 被展开为用户消息发送,绕过 SDK 原生加载
17+
- 用户补充上下文后 `/command` 本体丢失
18+
- Popover 中无法区分不同类型的命令
19+
20+
### 2. MCP 管理:远程服务器创建失败 + 双文件读写不一致
21+
22+
**根因:**
23+
- POST 校验要求 `command` 字段,但 SSE/HTTP 服务器用 `url` 字段
24+
- GET 合并读取两个配置文件但 PUT 只写 settings.json
25+
- DELETE 只检查 settings.json
26+
27+
### 3. Completion 处理:前端+后端双重执行 + 硬编码 localhost
28+
29+
**根因:**
30+
- ChatView.tsx 和 route.ts 都处理 onboarding/checkin completion
31+
- 后端通过 `fetch("http://localhost:${PORT}")` 回调自身 API
32+
33+
## 修改清单
34+
35+
### Phase 1: SkillKind 类型系统
36+
37+
| 文件 | 改动 |
38+
|------|------|
39+
| `src/types/index.ts` | 新增 `SkillKind` 类型;`MCPServerConfig.command` 改为可选 |
40+
| `src/app/api/skills/route.ts` | `SkillFile` 增加 `kind` 字段,各扫描函数返回正确 kind |
41+
| `src/app/api/skills/[name]/route.ts` | GET/PUT 响应包含 `kind` |
42+
43+
### Phase 2: MessageInput 核心修复
44+
45+
| 文件 | 改动 |
46+
|------|------|
47+
| `src/components/chat/MessageInput.tsx` | `isSkill``kind: SkillKind``handleSubmit` 按 kind 分流;Popover 分三组 |
48+
49+
分流逻辑:
50+
- `agent_skill`: 发送 `Use the {name} skill. User context: {text}`,displayOverride 显示 `/name`
51+
- `slash_command` / `sdk_command`: 发送 `/{command} {text}` 原样给 SDK
52+
- `codepilot_command`: 展开 COMMAND_PROMPTS,displayOverride 显示 `/command`
53+
54+
### Phase 3: displayOverride 持久化链路
55+
56+
| 文件 | 改动 |
57+
|------|------|
58+
| `src/app/chat/page.tsx` | `sendFirstMessage` 接受 displayOverride 参数 |
59+
| `src/app/api/chat/route.ts` | 提取 displayOverride,DB 存储用 displayOverride |
60+
| `src/components/chat/ChatView.tsx` | 传递 displayOverride 到 startStream |
61+
| `src/lib/stream-session-manager.ts` | StartStreamParams 增加 displayOverride |
62+
63+
### Phase 4: MCP 双文件管理
64+
65+
| 文件 | 改动 |
66+
|------|------|
67+
| `src/app/api/plugins/mcp/route.ts` | POST 支持 SSE/HTTP;PUT 按 `_source` 分流写入;POST 检查双文件同名 |
68+
| `src/app/api/plugins/mcp/[name]/route.ts` | DELETE 检查双文件 |
69+
| `src/components/plugins/McpManager.tsx` | `MCPServerWithSource` 类型;保留 `_source` 标记 |
70+
| `src/components/plugins/McpServerEditor.tsx` | 远程服务器不生成空 command |
71+
72+
### Phase 5: SDK Init 元数据转发
73+
74+
| 文件 | 改动 |
75+
|------|------|
76+
| `src/lib/claude-client.ts` | system:init 转发 slash_commands/skills |
77+
| `src/hooks/useSSEStream.ts` | 新增 `onInitMeta` 回调 |
78+
| `src/lib/provider-resolver.ts` | `settingSources` 始终包含 `'user'` |
79+
80+
### Phase 6: Completion 去重 + 去 localhost
81+
82+
| 文件 | 改动 |
83+
|------|------|
84+
| `src/lib/onboarding-processor.ts` | **新增** — 从 onboarding route 提取核心逻辑 |
85+
| `src/lib/checkin-processor.ts` | **新增** — 从 checkin route 提取核心逻辑 |
86+
| `src/app/api/workspace/onboarding/route.ts` | 委托给 processOnboarding() |
87+
| `src/app/api/workspace/checkin/route.ts` | 委托给 processCheckin() |
88+
| `src/app/api/chat/route.ts` | processCompletionServerSide 直接调用处理函数,不走 HTTP |
89+
| `src/components/chat/ChatView.tsx` | 删除 detectAssistantCompletion — 仅保留服务端路径 |
90+
91+
## 架构决策
92+
93+
1. **Completion 单路径**:仅在服务端 `collectStreamResponse``finally` 块中处理,前端不再参与。理由:服务端路径在所有场景下都会执行(包括页面刷新、解析失败),前端路径是多余的且引入竞态。
94+
95+
2. **处理函数直接导入**`processOnboarding/processCheckin` 作为 lib 函数导出,route handler 和 completion detector 都直接调用,消除 HTTP 回调。
96+
97+
3. **幂等保护**`processOnboarding` 检查 `state.onboardingComplete``processCheckin` 检查 `state.lastCheckInDate === today`,即使意外多次调用也不会产生副作用。
98+
99+
## 测试
100+
101+
- 289 个单元测试全部通过
102+
- TypeScript 编译无错误
103+
- CDP 验证:Popover 三组分类正确,MCP 远程服务器创建/删除正常,Console 无 error

0 commit comments

Comments
 (0)