Skip to content

Commit 6b19372

Browse files
Sync documentation from main Wegent repository
1 parent 05e5b46 commit 6b19372

2 files changed

Lines changed: 360 additions & 0 deletions

File tree

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
---
2+
sidebar_position: 19
3+
---
4+
5+
# Global Skill Sync
6+
7+
Global Skill sync lets Backend install user-selected Skills into an online local executor device's Claude Code global Skill directory, so Project tasks can reuse those local capabilities by default.
8+
9+
The current version syncs Skills only. MCP sync is temporarily disabled: Backend returns `422` when the request body contains non-empty `mcp_ids`, and the executor rejects WebSocket payloads that contain `mcps`.
10+
11+
## API
12+
13+
Request:
14+
15+
```http
16+
POST /api/local-executor/devices/{device_id}/capabilities/sync
17+
```
18+
19+
Body:
20+
21+
```json
22+
{
23+
"skill_ids": [1336],
24+
"mode": "merge"
25+
}
26+
```
27+
28+
`device_id` must belong to the current user and be online. `skill_ids` are `Kind.id` values, and each resource must satisfy:
29+
30+
- `kind == "Skill"`
31+
- `is_active == true`
32+
- `user_id` equals the current user, or `user_id == 0` for public Skills
33+
34+
Successful response example:
35+
36+
```json
37+
{
38+
"success": true,
39+
"device_id": "c675be0c-560e-4432-9076-6731ddc6341b",
40+
"mode": "merge",
41+
"skills": [
42+
{
43+
"id": 1336,
44+
"name": "browser",
45+
"status": "synced"
46+
}
47+
],
48+
"errors": []
49+
}
50+
```
51+
52+
MCP-disabled response example:
53+
54+
```json
55+
{
56+
"detail": "MCP capability sync is temporarily disabled"
57+
}
58+
```
59+
60+
## Backend Behavior
61+
62+
Backend validates device ownership, online state, and Skill authorization. After validation, Backend resolves each Skill into an executor download reference:
63+
64+
```json
65+
{
66+
"id": 1336,
67+
"name": "browser",
68+
"namespace": "default",
69+
"is_public": false,
70+
"download_path": "/api/v1/kinds/skills/1336/download?namespace=default"
71+
}
72+
```
73+
74+
Backend does not expose local paths to the frontend and does not accept arbitrary installation directories. The sync action is delivered to the target device through the `device:sync_capabilities` event in the `/local-executor` Socket.IO namespace.
75+
76+
## Executor Behavior
77+
78+
When the local executor receives `device:sync_capabilities`, it:
79+
80+
1. Validates `mode`; only `merge` and `replace` are accepted.
81+
2. Rejects payloads containing `mcps`.
82+
3. Uses the existing `SkillDownloader` to download and install Skills by Skill ID.
83+
4. Installs Skills into `~/.claude/skills/{skill_name}`.
84+
5. Records Wegent-managed Skills in `~/.wegent-executor/capabilities.json`.
85+
6. Forces the next heartbeat to include the full capability list.
86+
87+
The manifest records only Wegent-managed Skills and does not record MCP entries:
88+
89+
```json
90+
{
91+
"version": 1,
92+
"revision": 2,
93+
"skills": {
94+
"browser": {
95+
"skill_id": 1336,
96+
"namespace": "default",
97+
"updated_at": "2026-05-27T02:29:17+00:00"
98+
}
99+
},
100+
"last_sync_at": "2026-05-27T02:29:17+00:00"
101+
}
102+
```
103+
104+
If an older manifest contains an `mcps` field, the new code drops that field when reading or writing the manifest.
105+
106+
## Heartbeat
107+
108+
Executor heartbeat reports sanitized global Skill state:
109+
110+
```json
111+
{
112+
"capabilities": {
113+
"revision": 2,
114+
"digest": "sha256:c193c44afdbf51c8a1772e62b98915c8b9ffa68b266a641bc505868370495edb",
115+
"full": true,
116+
"skills": [
117+
{
118+
"name": "browser",
119+
"skill_id": 1336,
120+
"namespace": "default",
121+
"source": "wegent"
122+
}
123+
],
124+
"last_sync_at": "2026-05-27T02:29:17+00:00"
125+
}
126+
}
127+
```
128+
129+
Backend stores the latest state in Redis. Non-full heartbeats refresh only revision and digest and do not overwrite the previous full list. Heartbeats no longer report `mcps`.
130+
131+
## Project Task Runtime
132+
133+
Project tasks enable the global Skill directory:
134+
135+
```text
136+
SKILLS_DIR=~/.claude/skills
137+
```
138+
139+
The local executor still sets a task-scoped `CLAUDE_CONFIG_DIR` to isolate Claude Code's non-sensitive configuration. Because Claude Code scans Skills from the active `CLAUDE_CONFIG_DIR/skills`, Project task startup also links the task `.claude/skills` directory to `~/.claude/skills`:
140+
141+
```text
142+
{task_config_dir}/skills -> ~/.claude/skills
143+
```
144+
145+
This lets Claude Code discover global Skills through its native lookup path without writing sensitive model configuration into global Claude Code config.
146+
147+
Non-Project tasks continue to use task-scoped `.claude/skills` directories to preserve isolation.
148+
149+
The current version does not read Claude Code global MCP configuration and does not merge global MCP entries into Project task `mcp_servers`. Task-level MCP supplied by the current Bot, Ghost, or Skill still follows the existing task-level flow.
150+
151+
## Verification
152+
153+
Verify MCP is disabled:
154+
155+
```bash
156+
curl -i -X POST "http://localhost:8000/api/local-executor/devices/${DEVICE_ID}/capabilities/sync" \
157+
-H "Authorization: Bearer ${TOKEN}" \
158+
-H "Content-Type: application/json" \
159+
-d '{
160+
"skill_ids": [],
161+
"mcp_ids": ["dingtalk/docs"],
162+
"mode": "merge"
163+
}'
164+
```
165+
166+
Expected response: `422`.
167+
168+
Verify Skill sync:
169+
170+
```bash
171+
curl -i -X POST "http://localhost:8000/api/local-executor/devices/${DEVICE_ID}/capabilities/sync" \
172+
-H "Authorization: Bearer ${TOKEN}" \
173+
-H "Content-Type: application/json" \
174+
-d '{
175+
"skill_ids": [1336],
176+
"mode": "merge"
177+
}'
178+
```
179+
180+
Expected response: `200`, and `~/.claude/skills/browser/SKILL.md` should exist on the device.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
---
2+
sidebar_position: 19
3+
---
4+
5+
# 全局 Skill 同步
6+
7+
全局 Skill 同步允许 Backend 将用户选择的 Skill 安装到在线 local executor 设备的 Claude Code 全局 Skill 目录,让 Project task 默认复用这些本机能力。
8+
9+
当前版本只同步 Skill。MCP 同步暂时禁用:请求体中只要包含非空 `mcp_ids`,Backend 会返回 `422`,executor 也会拒绝包含 `mcps` 的 WebSocket payload。
10+
11+
## API
12+
13+
请求:
14+
15+
```http
16+
POST /api/local-executor/devices/{device_id}/capabilities/sync
17+
```
18+
19+
请求体:
20+
21+
```json
22+
{
23+
"skill_ids": [1336],
24+
"mode": "merge"
25+
}
26+
```
27+
28+
`device_id` 必须属于当前用户且在线。`skill_ids` 使用 `Kind.id`,资源必须满足:
29+
30+
- `kind == "Skill"`
31+
- `is_active == true`
32+
- `user_id` 等于当前用户,或 `user_id == 0` 的公共 Skill
33+
34+
成功响应示例:
35+
36+
```json
37+
{
38+
"success": true,
39+
"device_id": "c675be0c-560e-4432-9076-6731ddc6341b",
40+
"mode": "merge",
41+
"skills": [
42+
{
43+
"id": 1336,
44+
"name": "browser",
45+
"status": "synced"
46+
}
47+
],
48+
"errors": []
49+
}
50+
```
51+
52+
MCP 禁用响应示例:
53+
54+
```json
55+
{
56+
"detail": "MCP capability sync is temporarily disabled"
57+
}
58+
```
59+
60+
## Backend 行为
61+
62+
Backend 负责设备归属、在线状态和 Skill 权限校验。通过校验后,Backend 将 Skill 解析为 executor 可下载的引用:
63+
64+
```json
65+
{
66+
"id": 1336,
67+
"name": "browser",
68+
"namespace": "default",
69+
"is_public": false,
70+
"download_path": "/api/v1/kinds/skills/1336/download?namespace=default"
71+
}
72+
```
73+
74+
Backend 不向 frontend 暴露本地路径,也不接受任意安装目录。同步动作通过 `/local-executor` Socket.IO namespace 的 `device:sync_capabilities` 事件下发给目标设备。
75+
76+
## Executor 行为
77+
78+
local executor 收到 `device:sync_capabilities` 后会:
79+
80+
1. 校验 `mode`,只接受 `merge``replace`
81+
2. 拒绝包含 `mcps` 的 payload。
82+
3. 使用现有 `SkillDownloader` 按 Skill ID 下载并安装 Skill。
83+
4. 将 Skill 安装到 `~/.claude/skills/{skill_name}`
84+
5.`~/.wegent-executor/capabilities.json` 记录 Wegent 管理的 Skill manifest。
85+
6. 触发下一次 heartbeat 上报完整能力列表。
86+
87+
manifest 只记录 Wegent 管理的 Skill,不记录 MCP:
88+
89+
```json
90+
{
91+
"version": 1,
92+
"revision": 2,
93+
"skills": {
94+
"browser": {
95+
"skill_id": 1336,
96+
"namespace": "default",
97+
"updated_at": "2026-05-27T02:29:17+00:00"
98+
}
99+
},
100+
"last_sync_at": "2026-05-27T02:29:17+00:00"
101+
}
102+
```
103+
104+
如果旧 manifest 中存在 `mcps` 字段,新代码在读写 manifest 时会丢弃该字段。
105+
106+
## Heartbeat
107+
108+
executor heartbeat 会带上脱敏后的全局 Skill 状态:
109+
110+
```json
111+
{
112+
"capabilities": {
113+
"revision": 2,
114+
"digest": "sha256:c193c44afdbf51c8a1772e62b98915c8b9ffa68b266a641bc505868370495edb",
115+
"full": true,
116+
"skills": [
117+
{
118+
"name": "browser",
119+
"skill_id": 1336,
120+
"namespace": "default",
121+
"source": "wegent"
122+
}
123+
],
124+
"last_sync_at": "2026-05-27T02:29:17+00:00"
125+
}
126+
}
127+
```
128+
129+
Backend 将最新状态保存到 Redis。非 full heartbeat 只刷新 revision 和 digest,不覆盖已有完整列表。Heartbeat 不再上报 `mcps`
130+
131+
## Project Task 运行时
132+
133+
Project task 会启用全局 Skill 目录:
134+
135+
```text
136+
SKILLS_DIR=~/.claude/skills
137+
```
138+
139+
local executor 仍会设置任务级 `CLAUDE_CONFIG_DIR` 来隔离 Claude Code 的非敏感配置。由于 Claude Code 会从当前 `CLAUDE_CONFIG_DIR/skills` 扫描 Skill,Project task 启动时会额外将任务目录下的 `.claude/skills` 链接到 `~/.claude/skills`
140+
141+
```text
142+
{task_config_dir}/skills -> ~/.claude/skills
143+
```
144+
145+
这样 Claude Code 可以按自身发现规则看到全局 Skill,同时不会把敏感模型配置写入全局 Claude Code 配置。
146+
147+
非 Project task 继续使用任务级 `.claude/skills`,保持隔离。
148+
149+
当前版本不会从 Claude Code 全局配置读取 MCP,也不会把全局 MCP 合并到 Project task 的 `mcp_servers`。任务级 Bot、Ghost 或 Skill 动态提供的 MCP 仍按原有任务链路生效。
150+
151+
## 验证
152+
153+
MCP 禁用验证:
154+
155+
```bash
156+
curl -i -X POST "http://localhost:8000/api/local-executor/devices/${DEVICE_ID}/capabilities/sync" \
157+
-H "Authorization: Bearer ${TOKEN}" \
158+
-H "Content-Type: application/json" \
159+
-d '{
160+
"skill_ids": [],
161+
"mcp_ids": ["dingtalk/docs"],
162+
"mode": "merge"
163+
}'
164+
```
165+
166+
预期返回 `422`
167+
168+
Skill 同步验证:
169+
170+
```bash
171+
curl -i -X POST "http://localhost:8000/api/local-executor/devices/${DEVICE_ID}/capabilities/sync" \
172+
-H "Authorization: Bearer ${TOKEN}" \
173+
-H "Content-Type: application/json" \
174+
-d '{
175+
"skill_ids": [1336],
176+
"mode": "merge"
177+
}'
178+
```
179+
180+
预期返回 `200`,并在设备内看到 `~/.claude/skills/browser/SKILL.md`

0 commit comments

Comments
 (0)