agent-syncer 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTENT-REPO.md +526 -0
- package/README.md +513 -47
- package/bin/agent-sync.js +149 -17
- package/lib/commands/doctor.js +314 -21
- package/lib/commands/init.js +332 -0
- package/lib/commands/link.js +364 -103
- package/lib/commands/list.js +214 -0
- package/lib/commands/status.js +203 -19
- package/lib/commands/sync.js +533 -0
- package/lib/config.js +309 -96
- package/lib/gitignore.js +34 -17
- package/lib/install.js +239 -0
- package/lib/manifest.js +420 -0
- package/lib/merge.js +1110 -0
- package/lib/prompt.js +364 -1
- package/lib/prune.js +80 -0
- package/lib/record.js +380 -0
- package/lib/source.js +240 -0
- package/lib/stale.js +130 -0
- package/lib/target.js +152 -13
- package/package.json +3 -2
package/CONTENT-REPO.md
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
# 内容仓库格式
|
|
2
|
+
|
|
3
|
+
`agent-syncer` 是分发器,**内容**放在另一处——一个独立的内容仓库。这份文档讲的就是那个仓库:
|
|
4
|
+
目录怎么摆、每个目录干什么、各类文件的格式约定,以及从零建一个的完整步骤。
|
|
5
|
+
|
|
6
|
+
面向两类人:
|
|
7
|
+
|
|
8
|
+
- **要建自己部门内容仓库的人** —— 读第 0 节和第 1 节,照着搭一次就能跑通
|
|
9
|
+
- **往现有仓库里加内容的人** —— 直接看第 5 节(格式约定)和第 8 节(常见坑)
|
|
10
|
+
|
|
11
|
+
下文示例里的内容仓库叫 `dept-content`,只是个名字——你的仓库叫什么都行,格式完全一样。
|
|
12
|
+
|
|
13
|
+
> **一条铁律**:内容仓库只描述**这是什么**,**不描述写到哪**。
|
|
14
|
+
> 落到 `.claude/skills` 还是 `.trae/skills`,由 `agent-syncer` 内部的映射表决定。
|
|
15
|
+
> 一旦在内容里写死目标工具的路径,跨工具立刻作废。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 0. 从零建一个:最小可用
|
|
20
|
+
|
|
21
|
+
三步,两分钟。
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
mkdir my-content && cd my-content
|
|
25
|
+
git init -b main
|
|
26
|
+
|
|
27
|
+
# 1. 仓库身份证。schemaVersion 必填,版本不对 agent-syncer 会直接拒绝加载
|
|
28
|
+
cat > dept.json <<'EOF'
|
|
29
|
+
{ "name": "my-dept", "version": "1.0.0", "schemaVersion": 1 }
|
|
30
|
+
EOF
|
|
31
|
+
|
|
32
|
+
# 2. 放一条内容(技能是目录形态,目录名就是 id)
|
|
33
|
+
mkdir -p skills/code-style
|
|
34
|
+
cat > skills/code-style/SKILL.md <<'EOF'
|
|
35
|
+
---
|
|
36
|
+
name: code-style
|
|
37
|
+
description: 本技能应在用户编写或评审代码时使用。提供部门编码规范。
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
# 编码规范
|
|
41
|
+
|
|
42
|
+
(这里写你的规范正文)
|
|
43
|
+
EOF
|
|
44
|
+
|
|
45
|
+
# 3. 换行符保护 —— 见第 8 节,这条不能省
|
|
46
|
+
printf '* text=auto\n*.sh text eol=lf\n*.md text eol=lf\n*.json text eol=lf\n' > .gitattributes
|
|
47
|
+
|
|
48
|
+
git add -A && git commit -m "init"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**最少只需要 `dept.json` + 一个内容目录。** 连 `bundles/` 都可以暂时不要——
|
|
52
|
+
不写模板,直接在项目里用「自定义」挑条目:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 在一个测试项目里
|
|
56
|
+
mkdir -p .claude
|
|
57
|
+
cat > agents.json <<'EOF'
|
|
58
|
+
{
|
|
59
|
+
"content": "/path/to/my-content",
|
|
60
|
+
"include": ["skill:code-style"],
|
|
61
|
+
"links": ["claude"]
|
|
62
|
+
}
|
|
63
|
+
EOF
|
|
64
|
+
|
|
65
|
+
npx agent-syncer sync
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
应当看到 `✅ 自定义 include(未使用模板):1 个条目`,随后建好 `.claude/skills` 链接。
|
|
69
|
+
(`include` 不是命令行参数,它写在 `agents.json` 里——命令行只支持 `--bundle`。)
|
|
70
|
+
|
|
71
|
+
等条目多了、有「整包引用」的需求时,再加 `bundles/`。
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 1. 目录结构总览
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
my-content/
|
|
79
|
+
├── dept.json 仓库身份证:name / version / schemaVersion
|
|
80
|
+
├── bundles/ 模板:一套可整体引用的资产组合
|
|
81
|
+
│ ├── common.json
|
|
82
|
+
│ └── java-backend.json
|
|
83
|
+
├── skills/<id>/SKILL.md 技能 → 目录形态,可再带 references/ scripts/ assets/
|
|
84
|
+
├── rules/<id>.md 规则 → 单文件
|
|
85
|
+
├── commands/<id>.md 斜杠命令 → 单文件
|
|
86
|
+
├── agents/<id>.md 子代理 → 单文件
|
|
87
|
+
├── hooks/<id>.json hook 片段 → 顶层键就是事件名
|
|
88
|
+
├── mcp/<id>.json 单个 MCP server 的定义
|
|
89
|
+
├── scripts/ 被 hooks / mcp 按路径引用的可执行文件
|
|
90
|
+
├── .gitattributes 换行符保护(必加)
|
|
91
|
+
└── .gitignore
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 各目录的作用
|
|
95
|
+
|
|
96
|
+
| 目录 | 形态 | 作用 | 在项目里的落点 |
|
|
97
|
+
| --- | --- | --- | --- |
|
|
98
|
+
| `skills/` | `<id>/SKILL.md` 目录 | 按需触发的参考资料。模型根据 frontmatter 的 `description` 判断何时加载 | `.agents/skills/<id>/` |
|
|
99
|
+
| `rules/` | `<id>.md` | **始终生效**的规则,无 frontmatter,直接进上下文 | `.agents/rules/<id>.md` |
|
|
100
|
+
| `commands/` | `<id>.md` | 用户显式调用的斜杠命令。**文件名即命令名** | `.agents/commands/<id>.md` |
|
|
101
|
+
| `agents/` | `<id>.md` | 子代理定义 | `.agents/agents/<id>.md` |
|
|
102
|
+
| `hooks/` | `<id>.json` | 事件钩子片段,要**合并**进 `.claude/settings.json` | `.agents/hooks/<id>.json` |
|
|
103
|
+
| `mcp/` | `<id>.json` | MCP server 定义,要**合并**进 MCP 配置 | `.agents/mcp/<id>.json` |
|
|
104
|
+
| `scripts/` | 任意可执行文件 | 被上面两类按路径引用(**选了它们才同步**) | `.agents/scripts/` |
|
|
105
|
+
| `bundles/` | `<名字>.json` | 模板选择器,不落进项目 | —— |
|
|
106
|
+
|
|
107
|
+
**前四类靠目录链接分发**,内容只存一份;**后三类(hooks / mcp / scripts)不是链接**——
|
|
108
|
+
`hooks/` 和 `mcp/` 要合并进各工具自己的配置文件(那里面还有用户自己的东西,不能整份覆盖),
|
|
109
|
+
`scripts/` 是被 hook 命令按路径引用的真实文件(**本轮选了 hook / mcp 才会同步过去**)。
|
|
110
|
+
|
|
111
|
+
**合并只在本部门支持的工具有意义**:
|
|
112
|
+
|
|
113
|
+
| 内容 | Claude Code | Trae | Codex |
|
|
114
|
+
| --- | --- | --- | --- |
|
|
115
|
+
| `hooks/` | `.claude/settings.json` 的 `hooks` 段 | —— | —— |
|
|
116
|
+
| `mcp/` | 项目根 `.mcp.json` | `.trae/mcp.json` ⚠️ 未实证 | —— 暂不支持 |
|
|
117
|
+
|
|
118
|
+
> **Codex 的 MCP 暂时不做**:它的配置是 TOML,而且项目级配置只在**受信任项目**里加载。
|
|
119
|
+
> 用 Codex 的项目里,`mcp/` 的内容会被同步下来但不会写进它的配置——`status` 和 `doctor`
|
|
120
|
+
> 会明确告诉你这一点,不会让你以为配好了。
|
|
121
|
+
>
|
|
122
|
+
> **Trae 那一路没有实证**(写这份文档的机器上没装 Trae),路径和变量名都来自网络资料。
|
|
123
|
+
> `doctor` 会把它标成「未实证」,不会报成「一切正常」。
|
|
124
|
+
|
|
125
|
+
**这七类目录都必须提交到版本库。** 项目里的 `.gitignore` 托管段会为它们开白名单,
|
|
126
|
+
漏一个的后果是别人克隆下来只剩空壳。
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 2. 条目的命名规则
|
|
131
|
+
|
|
132
|
+
| 类型 | 类型名 | 内容仓库里的路径 | id 从哪来 |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| 技能 | `skill` | `skills/<id>/` | 目录名 |
|
|
135
|
+
| 规则 | `rule` | `rules/<id>.md` | 文件名(去 `.md`) |
|
|
136
|
+
| 命令 | `command` | `commands/<id>.md` | 文件名(去 `.md`) |
|
|
137
|
+
| 子代理 | `agent` | `agents/<id>.md` | 文件名(去 `.md`) |
|
|
138
|
+
| Hook | `hook` | `hooks/<id>.json` | 文件名(去 `.json`) |
|
|
139
|
+
| MCP | `mcp` | `mcp/<id>.json` | 文件名(去 `.json`) |
|
|
140
|
+
| (支撑) | — | `scripts/**` | 不参与挑选,有 hook / mcp 时整目录同步 |
|
|
141
|
+
|
|
142
|
+
注意**类型名是单数**(`skill:code-style`),而目录名是复数(`skills/`)。
|
|
143
|
+
两套词汇故意分开:目录结构可以改,条目 id 是对外契约,得稳定。
|
|
144
|
+
|
|
145
|
+
`scripts/` 是唯一的例外——它不参与 `include` / `exclude` 的挑选。因为 hook 和 MCP
|
|
146
|
+
都按路径引用脚本,逐个挑拣很容易漏,漏了就是运行时静默失败。
|
|
147
|
+
|
|
148
|
+
但要分清「**不挑拣**」和「**无条件**」:
|
|
149
|
+
|
|
150
|
+
- 本轮选了 hook 或 mcp → `scripts/` **整个搬过去**,哪怕某个脚本这一轮没被引用到
|
|
151
|
+
- 本轮一个 hook / mcp 都没选 → **根本不同步**。那时候 `.agents/` 里没有任何东西
|
|
152
|
+
会去引用它们,搬过去只是把用不上的文件塞进项目——而它们是要提交进版本库的
|
|
153
|
+
|
|
154
|
+
这条判据看的是**本次选择**,不是「实际装成了几个」:被 `protect` 锁住的 hook
|
|
155
|
+
仍然躺在 `.agents/hooks/` 里,它引用的脚本还得留着。
|
|
156
|
+
|
|
157
|
+
**加内容不需要改任何清单。** 丢一个文件进目录,它就在那儿了——
|
|
158
|
+
这是这套结构最主要的好处:清单与文件天然不会不同步。只有「归到哪个模板」需要手工声明。
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 3. `dept.json`:仓库身份证
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"name": "dept",
|
|
167
|
+
"version": "2.0.0",
|
|
168
|
+
"schemaVersion": 1,
|
|
169
|
+
"description": "示例:本部门的 AI 资产。"
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
| 字段 | 必填 | 用途 |
|
|
174
|
+
| --- | --- | --- |
|
|
175
|
+
| `schemaVersion` | **是** | 格式版本。当前只支持 `1`,不匹配直接拒绝加载并提示升级 agent-syncer |
|
|
176
|
+
| `name` | 建议 | `sync` 输出里显示 |
|
|
177
|
+
| `version` | 建议 | `sync` 输出里显示 |
|
|
178
|
+
| `description` | 否 | 目前未被使用 |
|
|
179
|
+
|
|
180
|
+
> **`version` 目前纯粹是给人看的。** 「要不要更新」由**内容哈希**判断,不是版本号——
|
|
181
|
+
> 改了文件没升版本号照样会同步过去,升了版本号但内容没变则报「已是最新」。
|
|
182
|
+
> 好处是没有「版本号忘了升」这类静默失败;代价是不能靠版本号回滚。
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 4. `bundles/`:模板
|
|
187
|
+
|
|
188
|
+
模板是**唯一需要手工声明的分组**。一个 bundle 就是一套可整体引用的资产组合。
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
// bundles/java-backend.json
|
|
192
|
+
{
|
|
193
|
+
"title": "Java 后端",
|
|
194
|
+
"description": "后端服务模板:在通用基座之上追加接口约定、建服务命令,以及配套的 MCP server。",
|
|
195
|
+
"bundle": ["common"],
|
|
196
|
+
"include": ["skill:api-conventions", "command:new-service", "mcp:*"],
|
|
197
|
+
"exclude": ["hook:legacy-hooks"]
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| 字段 | 用途 |
|
|
202
|
+
| --- | --- |
|
|
203
|
+
| `title` / `description` | 不带 `--bundle` 跑 `sync` 时会列出来,给人看 |
|
|
204
|
+
| `bundle` | 引用其它模板。单个名字(`"common"`)或名字数组都行;被引用的模板各自完整解析后取并集 |
|
|
205
|
+
| `include` | 这个模板包含什么 |
|
|
206
|
+
| `exclude` | 从**本模板**已收集的集合里剔掉什么,最后生效 |
|
|
207
|
+
|
|
208
|
+
`include` / `exclude` 都必须是数组(写成裸字符串会报错)。
|
|
209
|
+
|
|
210
|
+
**`bundle` / `include` / `exclude` 三个字段的名字和含义,与项目根 `agents.json` 里的一模一样。**
|
|
211
|
+
每个模板就是一个小号的 `agents.json`——不用记两套规则,也不用猜「模板里的 bundle 和
|
|
212
|
+
项目里的 bundle 有什么区别」,它们本来就是同一个东西。
|
|
213
|
+
|
|
214
|
+
### 条目引用语法
|
|
215
|
+
|
|
216
|
+
两种写法,`include` 和 `exclude` 通用:
|
|
217
|
+
|
|
218
|
+
| 写法 | 含义 |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| `skill:code-style` | 单个条目 |
|
|
221
|
+
| `skill:*` | 该类型下的**全部**条目(展开时按名字排序) |
|
|
222
|
+
|
|
223
|
+
> **`@其它模板` 这种写法已经移除。** 以前写 `"include": ["@common"]`,现在写
|
|
224
|
+
> `"bundle": ["common"]`。`include` / `exclude` 里再出现 `@名字` 会**直接报错**并给出新写法,
|
|
225
|
+
> 不会静默忽略——静默忽略会让「引用没生效」变成一个查不出来的哑谜。
|
|
226
|
+
> `bundles/*.json` 和项目根 `agents.json` 走的是同一套语法,一处改了两处都生效。
|
|
227
|
+
|
|
228
|
+
### 基座 + 扩展,而不是平行副本
|
|
229
|
+
|
|
230
|
+
`java-backend` 和 `frontend` 都以 `"bundle": ["common"]` 起头:
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
common skills ×3 rules ×2 command agent hook
|
|
234
|
+
java-backend common + 接口约定 + 建服务命令 + MCP ×2
|
|
235
|
+
frontend common + 前端约定
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
使用者只写一个模板名,就同时拿到通用那套——不需要知道还得再装 `common`。
|
|
239
|
+
好处是**一份内容只存一次**,改通用规则时两个模板同时生效。
|
|
240
|
+
|
|
241
|
+
### 模板解析的三步流水线
|
|
242
|
+
|
|
243
|
+
模板内部和项目级用的是**同一套**,每一步都在上一步的结果上做增删:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
1. 本文件 bundle 引用的每个模板各自完整解析 → 取并集
|
|
247
|
+
2. 叠加本文件的 include
|
|
248
|
+
3. 最后过本文件的 exclude —— 它能砍掉上面任何一步进来的东西
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
第 1 步是**递归**的:被引用的模板会连同它自己的 `bundle` / `include` / `exclude` 一起解析完,
|
|
252
|
+
拿到的永远是对方**处理干净之后**的结果。第 3 步能砍掉第 1 步带进来的一切,
|
|
253
|
+
所以「基座里有个东西我这份模板不要」直接 `exclude` 掉就行。
|
|
254
|
+
|
|
255
|
+
### 模板可以叠加
|
|
256
|
+
|
|
257
|
+
项目里可以写多个模板,取**并集**(项目根的 `bundle` 字段,写法和模板里的一样):
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{ "bundle": ["common", "frontend"] }
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**多个模板各自解析完再取并集**,不是把 include 倒进一个池子。所以 A 模板的 `exclude`
|
|
264
|
+
砍不到 B 模板的内容——否则结果会取决于组合顺序和谁的 `exclude` 更宽,没法推理。
|
|
265
|
+
模板内部写 `"bundle": ["a", "b"]` 时同理。
|
|
266
|
+
|
|
267
|
+
### 已知限制:不能整块排掉一个模板
|
|
268
|
+
|
|
269
|
+
以前 `exclude: ["@common"]` 能一口气把整个模板排掉,**这个能力随 `@` 写法一起没了**。
|
|
270
|
+
现在 `exclude` 只认 `类型:名字` / `类型:*`,要排掉一整个模板只有两条路:
|
|
271
|
+
|
|
272
|
+
- **逐条列**:`"exclude": ["skill:a", "rule:b", ...]`。对方模板加一条新内容,
|
|
273
|
+
你这边不会自动跟上——这是优点也是缺点
|
|
274
|
+
- **把那个模板拆小**:把不想要的部分挪到另一个模板里,使用者只引用他要的那几个
|
|
275
|
+
|
|
276
|
+
工具**不会**为此发明新语法(比如 `exclude` 里写模板名)。`exclude` 里的 `@名字`
|
|
277
|
+
是语法错误,会当场报错而不是被当成模板名处理。项目级的 `exclude` 也一样。
|
|
278
|
+
|
|
279
|
+
### 项目级的增删
|
|
280
|
+
|
|
281
|
+
使用方还能在自己项目里叠加一层:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
模板并集 → + 项目 include → − 项目 exclude
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`exclude` 最后过一遍,能砍掉上面任何一步加进来的东西。所以**作者不用为个别项目的
|
|
288
|
+
口味去改模板**——有人不想要那个 hook,他自己 exclude 掉就行。
|
|
289
|
+
|
|
290
|
+
> 模板内部是同一套流水线的一个实例,只是 `exclude` 更严格:模板里 exclude
|
|
291
|
+
> 一个不存在的条目是**错误**(模板应当自洽);项目里则是**告警后跳过**
|
|
292
|
+
> (项目配置叠在模板之上,模板一变动就炸掉所有人的配置,代价太大)。
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 5. 各类文件的格式约定
|
|
297
|
+
|
|
298
|
+
### skills —— `skills/<id>/SKILL.md`
|
|
299
|
+
|
|
300
|
+
frontmatter 必填 `name` 与 `description`,可选 `allowed-tools`。
|
|
301
|
+
|
|
302
|
+
```markdown
|
|
303
|
+
---
|
|
304
|
+
name: code-style
|
|
305
|
+
description: 本技能应在用户编写、修改或评审代码时使用,尤其是询问"代码规范"、"命名约定"时。提供部门统一的编码规范参考。
|
|
306
|
+
version: 1.0.0
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
# 部门编码规范
|
|
310
|
+
|
|
311
|
+
正文……
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`description` 是**唯一的触发依据**——写清楚「什么时候该用」,比写清楚「这是什么」重要得多。
|
|
315
|
+
把用户可能说出口的原话("这个写法对不对"、"提交规范")写进去,命中率高得多。
|
|
316
|
+
|
|
317
|
+
目录内可以再放 `references/`、`scripts/`、`assets/`,会跟着技能一起同步:
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
skills/review-checklist/
|
|
321
|
+
├── SKILL.md 速查版
|
|
322
|
+
└── references/checklist.md 完整版,SKILL.md 正文里引用它
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### rules —— `rules/<id>.md`
|
|
326
|
+
|
|
327
|
+
**纯 markdown,不带 frontmatter。**
|
|
328
|
+
|
|
329
|
+
> Cursor 那套 `alwaysApply` / `globs` 对 Claude Code 无效,不要写。
|
|
330
|
+
> `paths` 作用域是 Claude Code 可能支持的写法,但**尚未实测**——
|
|
331
|
+
> 在验证之前一律按全局规则写。一条静默不生效的规则比没有规则更危险。
|
|
332
|
+
|
|
333
|
+
规则是**始终进上下文**的,所以只放真正的红线,别把参考资料塞进来——那属于 skills。
|
|
334
|
+
|
|
335
|
+
### commands —— `commands/<id>.md`
|
|
336
|
+
|
|
337
|
+
**文件名即命令名**(`dept-check.md` → `/dept-check`)。frontmatter 用 `description`,
|
|
338
|
+
可选 `argument-hint` 与 `allowed-tools`。
|
|
339
|
+
|
|
340
|
+
```markdown
|
|
341
|
+
---
|
|
342
|
+
description: 对当前改动做一次部门规范自查
|
|
343
|
+
argument-hint: [文件或目录路径,默认当前改动]
|
|
344
|
+
allowed-tools: [Read, Glob, Grep, Bash]
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
正文里用 `$ARGUMENTS` 取参数。
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### agents —— `agents/<id>.md`
|
|
351
|
+
|
|
352
|
+
frontmatter 必填 `name` 与 `description`,可指定 `model`。
|
|
353
|
+
`description` 决定**什么时候该把任务派给它**,同样要写清触发场景。
|
|
354
|
+
|
|
355
|
+
### hooks —— `hooks/<id>.json`
|
|
356
|
+
|
|
357
|
+
**顶层键就是 hook 事件名**,内容会被原样合并进 `.claude/settings.json` 的 `hooks` 段:
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"SessionStart": [
|
|
362
|
+
{ "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PROJECT_DIR}/.agents/scripts/session-context.sh\"", "timeout": 10 } ] }
|
|
363
|
+
],
|
|
364
|
+
"PostToolUse": [
|
|
365
|
+
{ "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PROJECT_DIR}/.agents/scripts/post-write-hint.sh\"" } ] }
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
两条硬要求:
|
|
371
|
+
|
|
372
|
+
1. **不能出现 `description` 之类的非事件键**——合并进 `settings.json` 会变成脏数据。
|
|
373
|
+
文件级说明写文档,不写在这里。
|
|
374
|
+
2. **脚本路径用 `${CLAUDE_PROJECT_DIR}/.agents/scripts/...`**。
|
|
375
|
+
插件时代的 `${CLAUDE_PLUGIN_ROOT}` 在项目环境下不存在,写了会**静默失败**。
|
|
376
|
+
|
|
377
|
+
> hook 只有 Claude Code 一个目标,所以上面这条写 `CLAUDE_PROJECT_DIR` 是对的。
|
|
378
|
+
> 但如果你写的是 `${workspaceFolder}`,工具也会**替你翻译**过去,不必改。
|
|
379
|
+
> 两套写法都认——见下一节。
|
|
380
|
+
|
|
381
|
+
Windows 上 hook 通过 `sh` 执行,需要 Git Bash 在 `PATH` 里。
|
|
382
|
+
`doctor` 会替你查这一条(`PATH` 里没有 `sh` 的话,hook 是**静默不跑**的)。
|
|
383
|
+
|
|
384
|
+
### mcp —— `mcp/<id>.json`
|
|
385
|
+
|
|
386
|
+
**单个 server 的定义**,**文件名即 server 名**。格式就是该 server 自身的配置,
|
|
387
|
+
**不要套 `mcpServers` 包装**——那是配置文件里的层级,不是这里的。
|
|
388
|
+
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
"type": "http",
|
|
392
|
+
"url": "https://mcp.example.com/dept-wiki/api",
|
|
393
|
+
"headers": { "Authorization": "Bearer ${DEPT_WIKI_TOKEN}" }
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**密钥一律走环境变量占位符**,不要写死 token。
|
|
398
|
+
|
|
399
|
+
#### 三条会「静默不生效」的坑,工具会替你拦下来
|
|
400
|
+
|
|
401
|
+
| 写法 | 后果 |
|
|
402
|
+
| --- | --- |
|
|
403
|
+
| 套了 `mcpServers` 包装 | 会写出一个名叫 `mcpServers` 的 server。**工具直接报错** |
|
|
404
|
+
| **有 `url` 却没有 `type`** | 会被当成 stdio server 读,然后**跳过**——一声不吭。**工具直接报错**,不替你猜 `type` 是什么 |
|
|
405
|
+
| 顶层不是对象 | 报错 |
|
|
406
|
+
|
|
407
|
+
工具**不会**替你补 `type: "stdio"`:Claude 缺 `type` 本来就按 stdio 读,多写一个键
|
|
408
|
+
反而可能在别的工具那里出问题。
|
|
409
|
+
|
|
410
|
+
#### 引用 `scripts/` 里的脚本
|
|
411
|
+
|
|
412
|
+
想引用 `.agents/scripts/` 下的东西时,项目根**写哪个都认**:
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{ "command": "node", "args": ["${CLAUDE_PROJECT_DIR}/.agents/scripts/serve.js"] }
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
写 `${CLAUDE_PROJECT_DIR}` 或 `${workspaceFolder}` 都行,合并时按目标工具翻译成
|
|
419
|
+
它自己的写法。别的 `${VAR}`(比如 `${DEPT_WIKI_TOKEN}`)**原样保留**——
|
|
420
|
+
那些由工具自己在运行时展开,不归本工具管。
|
|
421
|
+
|
|
422
|
+
### scripts
|
|
423
|
+
|
|
424
|
+
被 hooks / mcp 引用的可执行文件。不参与条目挑选——本轮选了 hook / mcp 就整目录
|
|
425
|
+
同步(挑漏一个是运行时静默失败),一个都没选就不同步。
|
|
426
|
+
|
|
427
|
+
`.gitattributes` 里强制 `*.sh` 用 LF——**这条不能删**。见第 8 节。
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## 6. 内容怎么落到各工具
|
|
432
|
+
|
|
433
|
+
这一节是 `agent-syncer` 的事,放在这里是为了说明「为什么内容里不能写目标路径」。
|
|
434
|
+
|
|
435
|
+
| 内容 | Claude Code | Trae | Codex CLI |
|
|
436
|
+
| --- | --- | --- | --- |
|
|
437
|
+
| skills | `.claude/skills` | `.trae/skills` | `.codex/skills` |
|
|
438
|
+
| rules | `.claude/rules` | `.trae/rules` | 合并进 `AGENTS.md` |
|
|
439
|
+
| commands | `.claude/commands` | `.trae/commands` | —— 未见项目级机制 |
|
|
440
|
+
| agents | `.claude/agents` | —— | —— |
|
|
441
|
+
| hooks | `.claude/settings.json` 的 `hooks` 段 | —— | —— |
|
|
442
|
+
| mcp | 项目根 `.mcp.json` | `.trae/mcp.json` ⚠️ 未实证 | —— 暂不支持 |
|
|
443
|
+
|
|
444
|
+
> 后两类是**合并**进去的,不是链接:那些文件里还有使用者自己的 server 和 hook,
|
|
445
|
+
> 整份覆盖等于把它们删了。所以本工具记下自己写了什么,而且**现场对不上就不动**——
|
|
446
|
+
> 用户改过的那一份会原样保留。
|
|
447
|
+
|
|
448
|
+
前四类在项目里是**目录链接**(Windows 上用 `junction`,不需要管理员权限),
|
|
449
|
+
都指向 `.agents/` 下那一份内容。所以内容永远只有一份,不会出现「改了 Claude 的、
|
|
450
|
+
忘了改 Trae 的」。
|
|
451
|
+
|
|
452
|
+
> **Codex 有规则机制,只是没有「规则目录」。** 规则写在 `AGENTS.md` 里,
|
|
453
|
+
> 优先级是 `AGENTS.override.md` > `AGENTS.md` > `project_doc_fallback_filenames`,
|
|
454
|
+
> 且按目录作用域生效。它不支持 `@import`,所以 rules 只能合并、不能链接。
|
|
455
|
+
> 注意默认有 `project_doc_max_bytes = 32768` 的大小上限。
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## 7. 项目侧怎么引用
|
|
460
|
+
|
|
461
|
+
内容仓库这边只管内容。使用方在自己项目根放一份 `agents.json`:
|
|
462
|
+
|
|
463
|
+
```json
|
|
464
|
+
{
|
|
465
|
+
"content": "https://git.example.com/team/dept-content.git",
|
|
466
|
+
"ref": "main",
|
|
467
|
+
"bundle": ["java-backend"],
|
|
468
|
+
"include": ["mcp:dept-wiki"],
|
|
469
|
+
"exclude": ["hook:dept-hooks"],
|
|
470
|
+
"links": ["claude"]
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
- `content` 可以是 git 地址,也可以是本地路径(维护内容仓库时用本地路径方便得多)
|
|
475
|
+
- `ref` 可以是分支 / 标签 / 提交 SHA,省略取默认分支。**用标签可以把项目钉在某个版本上**
|
|
476
|
+
- `links` 里写**工具名**,不是目录——选用一个工具就是它的全部类型一起适配
|
|
477
|
+
|
|
478
|
+
然后 `npx agent-syncer sync` 一条命令就够了:拉取 → 装进 `.agents/` → 建链接 → 维护 `.gitignore`。
|
|
479
|
+
|
|
480
|
+
写 `include` 时想知道 id 叫什么,用 `list` 查——**维护内容仓库的人不必自己记**:
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npx agent-syncer list --from=. # 六类条目 + 模板
|
|
484
|
+
npx agent-syncer list --from=. --kind=skill # 只看一类
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
完整字段说明见 [`README.md`](README.md),命令行用法见 `agent-syncer --help`。
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## 8. 常见坑
|
|
492
|
+
|
|
493
|
+
| 现象 | 原因 / 对策 |
|
|
494
|
+
| --- | --- |
|
|
495
|
+
| `agents.json 不是合法的 JSON:Bad escaped character` | Windows 路径直接写进去会被当成转义符。**用正斜杠**:`"C:/project/my-content"`。反斜杠得写成 `\\` |
|
|
496
|
+
| Git Bash 里脚本报 `\r: command not found` | 检出成了 CRLF。**`.gitattributes` 强制 `*.sh text eol=lf`,这条不能删** |
|
|
497
|
+
| 换了台机器,换行符又乱了 | `core.autocrlf` 是**本机配置**,不会随克隆传过去。只有 `.gitattributes` 进仓库才管用 |
|
|
498
|
+
| 规则写了一点效果都没有 | 检查是不是带了 Cursor 的 `alwaysApply` / `globs` frontmatter。那些对 Claude Code 无效 |
|
|
499
|
+
| hook 静默不执行 | 多半是脚本路径写了 `${CLAUDE_PLUGIN_ROOT}`。项目环境用 `${CLAUDE_PROJECT_DIR}` |
|
|
500
|
+
| 合并后 `settings.json` 出现脏数据 | `hooks/*.json` 里混进了 `description` 之类的非事件键 |
|
|
501
|
+
| 别人克隆下来 `.agents/` 是空的 | 项目里 `.gitignore` 的托管段缺了对应目录的白名单。跑一次 `agent-syncer link` 会补齐 |
|
|
502
|
+
| 加了内容但项目里没变 | `include` 里没列它;或者 `exclude` 把它砍了(`sync` 会打印被排除的条目) |
|
|
503
|
+
| 报错说 `"@common" 这种写法已经移除` | 老写法。模板之间的组合改用顶层字段 `"bundle": ["common"]`——`include` / `exclude` 不再接受 `@名字` |
|
|
504
|
+
| 想排掉一整个模板,发现 `exclude` 不认模板名 | `@` 移除后没这个能力了,只能逐条 exclude 或把模板拆小。见第 4 节「已知限制」 |
|
|
505
|
+
| `schemaVersion` 不匹配被拒 | 内容仓库格式版本高于当前 CLI。升级 agent-syncer |
|
|
506
|
+
| MCP 配好了却用不上 | 三种可能:①选了 Codex(它的 MCP 本工具暂不支持);②Trae,而那条路未实证;③**Claude Code 要你逐条批准才会连**——批准记录存在各人本机的 `~/.claude.json` 里,不随仓库共享。`doctor` 会告诉你是哪一种 |
|
|
507
|
+
| `sync` 报「有 N 条本工具没动」 | 那些是把使用者自己写的东西当成了本工具的。工具**保留现场的、不覆盖**,只报一声 |
|
|
508
|
+
| `sync` 报「有 N 条以前合并进工具配置的、现在不需要了」 | 模板收窄了。默认只报不删,确认后加 `--prune` 摘掉 |
|
|
509
|
+
| 合并产物没进版本库 | `doctor` 会查。常见原因是使用者自己的 `.gitignore` 里写了 `.mcp.json`(怕带密钥)——`.gitignore` 对**已跟踪**的文件无效,但没跟踪过就会被挡住 |
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## 9. 最小检查清单
|
|
514
|
+
|
|
515
|
+
建新内容仓库时逐条过:
|
|
516
|
+
|
|
517
|
+
- [ ] `dept.json` 存在,`schemaVersion` 是 `1`
|
|
518
|
+
- [ ] `.gitattributes` 存在,且含 `*.sh text eol=lf`
|
|
519
|
+
- [ ] `skills/<id>/SKILL.md` 的 frontmatter 有 `name` 和 `description`,`description` 写的是**触发场景**
|
|
520
|
+
- [ ] `rules/*.md` 不带 frontmatter
|
|
521
|
+
- [ ] `hooks/*.json` 的顶层键全是事件名,脚本路径用 `${CLAUDE_PROJECT_DIR}`(写 `${workspaceFolder}` 也认)
|
|
522
|
+
- [ ] `mcp/*.json` 是单个 server 定义,没有 `mcpServers` 包装,密钥走环境变量
|
|
523
|
+
- [ ] `mcp/*.json` 里**有 `url` 的都带了 `type`**——漏了它会被当成 stdio 静默跳过
|
|
524
|
+
- [ ] `bundles/*.json` 的 `include` / `exclude` 都是数组,`bundle` 是字符串或字符串数组
|
|
525
|
+
- [ ] `bundles/*.json` 和 `agents.json` 里都没写老写法 `"@名字"`(写了会直接报错)
|
|
526
|
+
- [ ] 在一个测试项目里跑通 `npx agent-syncer sync --from=<路径> --bundle=<名字> --dry-run`
|