Skill 管理
本机的 skill 由 agent CLI 统一管理。不要手动在 .claude/skills/ 下建目录或拷文件 —— 那样会绕过链接机制与三路合并,之后无法更新。
心智模型
~/.agents/skills/<id> ← 本体(唯一实体,skills.sh 下载到这里)
↑ junction ↑ junction
~/.claude/skills/<id> ./.claude/skills/<id>
(全局启用) (项目启用)
- 启用 = 建一个 junction 链接,不拷贝。同一个 skill 在 10 个项目启用也只占一份磁盘。
- 因为两个作用域指向同一实体,不存在"项目版本覆盖全局版本",改任何一处就是改本体。
- 卸载 = 删链接,本体库永不受影响。
~/.agents/.skill-lock.json是 skills.sh 的文件,本工具只读不写。
三个数据文件
| 文件 | 谁维护 | 作用 |
|---|---|---|
~/.agents/.skill-lock.json |
skills.sh | 上游来源总账,本工具只读 |
~/.agents/.merge-state.json |
本工具 | base 快照与合并历史 |
~/.agents/.skill-scan.json |
用户手工 | 扫描策略(include/exclude glob) |
~/.agents/.scan-cache.json |
本工具 | 上次扫描结果,避免每次全盘扫 |
常用命令
工具在项目根目录,用 bun run src/index.ts 调用(或已装好的 agent 命令)。
查看有哪些 skill
agent list # 表格,给人看(默认隐藏已失联的)
agent list --json # JSON,给你(AI)解析用,优先用这个
agent list --all # 连已失联的记录一起显示
JSON 每项形如:
{ "id": "codegraph", "tracked": true, "status": "up-to-date", "checkedAt": "2026-08-05T11:12:25Z",
"conflicts": 0, "enabledGlobal": false, "enabledProject": true, "orphaned": false }
表格里 v 表示已启用、- 表示未启用。"项目"列只在当前目录确实有 .claude/skills 时才出现 —— 它指的是你执行命令所在的目录,不在项目里时那一列全是 -,纯占宽度。JSON 输出不受此影响,enabledProject 始终存在。
tracked 与 status 是两件事,别混:
tracked: true= lock 里有上游记录,能执行 update。与"有没有新版本"无关。status= 上次检查的结论,回答要不要 update:
| status | 含义 | update 会怎样 |
|---|---|---|
unknown |
有上游但从未检查过 | 不知道,先跑 agent check |
up-to-date |
上次检查时两边都没变 | 提示"已是最新",什么都不做 |
local-only |
只有你改过,上游没动 | 提示"上游无变化,保留本地修改" |
behind |
上游有新内容 | 快进到最新 |
diverged |
上游有新内容且你也改过 | 三路合并 |
conflicted |
上次合并留了冲突未解 | 需先手工处理冲突标记 |
no-upstream |
没有上游 | 无法 update |
status 来自上次 check 或 update,可能过时——checkedAt 交代新鲜度。要知道上游此刻的真实状态,跑 agent check。
list 还会提示有多少 skill 散落在本体库外(读扫描缓存,不动文件)。
检查上游有无新版本
agent check # 检查全部有上游的(联网,每个约 2 秒)
agent check codegraph # 只检查指定的
agent check --json
只看不动:拉上游、比四象限、把结论写进 state,本体库一个字节都不碰。之后 agent list 的状态列就是实时的了。
为什么需要单独一个命令:判断"上游有没有新版本"本地无从推导。lockFolderHash 是 skills.sh 安装时算的,不随上游变化;.base/ 快照只记录上次同步态。必须联网拉一次对比。做成独立命令是为了让 list 保持只读且毫秒级。
启用(安装到作用域)
用户说"把 X 装到这个项目":
agent enable codegraph -p # 装到当前项目
agent enable codegraph ast-grep -p # 一次装多个
agent enable codegraph -g # 装到全局
给出 ID 时是非交互的,直接执行。 不带 ID 会进入 TUI 多选界面 —— 你(AI)不要走这条路,会卡在交互上。
-p / --project 与 -g / --global 二选一;都不传时默认项目作用域(非交互路径)。若用户没说清装到哪,问一句再执行。
卸载(停用,保留本体)
用户说"把 X 从项目里移除"、"停用 X":
agent disable codegraph -p
agent disable codegraph ast-grep -g
只删本工具建的链接,本体库不动,之后还能再启用。遇到外部工具建的链接或手写的真实目录会拒绝删除并提示,这是有意的保护。
彻底删除(从本体库抹掉)
用户说"删掉 X"、"不要 X 了"、"清理这些 skill":
agent remove # TUI 多选
agent remove old-skill another-one # 直接指定,仍会要确认
agent remove old-skill --force # 跳过确认
agent remove old-skill --no-sync # 不通知 skills.sh,只打印命令
与 disable 的区别:disable 只摘链接,remove 是把本体库里的目录删掉,不可再启用。用户说"移除/停用"时优先问清是哪个意思。
执行顺序(顺序有讲究,不能颠倒):
- 摘掉作用域链接 —— 必须先做。实测删掉本体后 junction 仍然存在但变成悬空链接(
lstat能读到、readlink还指向原处,但内容 ENOENT),Claude Code 扫到会出错 - 删除本体目录与
.base/快照 - 清掉
.merge-state.json里的条目 - 通知 skills.sh(
npx skills remove <ids> -g -y)—— 不同步的话它还以为装着,而且.skill-lock.json的残留记录会在下次syncFromLock时被重新投影,在ls里显示成"已失联"
安全性:删除前自动建 git 检查点(本体库已 git 化的话),可 git revert 找回。若本体库还没纳入 git,会警告"删除不可恢复"并建议先跑 agent repo。遇到非本工具建立的作用域链接会中止该项删除,避免留下悬空链接。
更新(三路合并)
agent update codegraph # 更新单个
agent update # TUI 多选,AI 不要用
更新按四象限判定:
| 本地改过 | 上游变了 | 行为 |
|---|---|---|
| 否 | 否 | 无需更新 |
| 否 | 是 | 快进到上游最新 |
| 是 | 否 | 保留本地,什么都不做 |
| 是 | 是 | 逐文件三路合并 |
第四象限里,上游改 SKILL.md、你改 references/usage.md 会自动合并、零冲突;只有同一文件同一处两边都改才需要人介入。
有冲突时:文件里留下 <<<<<<< 标记,命令会列出冲突文件路径,且不推进基线(下次仍能识别)。此时告知用户哪些文件冲突,可代为编辑解决冲突标记。
首次更新某个已装 skill 会提示"首次接管,已用当前内容建立基线" —— 这次判不出本地修改,第二次起完整可用。
诊断
agent doctor
区分作用域目录下三类东西:本工具纳管的链接、外部工具建的链接、真实目录副本。用户抱怨"skill 状态不对"时先跑这个。
扫描与收编(解决"skill 散落各处")
agent scan # 按配置全盘扫,报告分布
agent scan L:/Documents/GitHub # 只扫指定位置
agent scan --reuse # 用上次结果重新判定,不重扫磁盘(改完配置后用)
agent scan --json # JSON 输出
agent scan --normalize # 预演收编,不动任何文件
agent scan --normalize --apply # 真正执行
判定 skill 的条件只有一条:目录下直接含 SKILL.md。
"是否算用户的 skill"由配置决定,不是代码猜的。策略在 ~/.agents/.skill-scan.json:
{
"roots": ["C:/Users/boer", "L:/Documents/GitHub"],
"include": ["**/.claude/skills/*", "**/.agents/skills/*", "**/.cursor/skills/*"],
"exclude": ["**/node_modules/**", "**/.trae-cn/builtin/**", "**/bundled-skills/**"]
}
用 agent config 查看位置与当前内容。这个文件由用户手工编辑 —— 实测这台机器全盘有 2191 处 SKILL.md,其中 1985 处是 Trae/Hermes 内置资源与包缓存,只有 201 处是用户的。范围判断是用户偏好,代码判断不了。用户说"这些也要管"或"别扫那里"时,改这个文件再跑 --reuse。
归一化做什么:把本体库外的 skill 复制进 ~/.agents/skills/,原位置替换为指向它的 junction。四种结果:
| 情况 | 结果 | 说明 |
|---|---|---|
| 本体库没有 | adopted |
复制进去,原位置换链接 |
| 本体库有、内容一致 | linked |
直接换链接 |
| 本体库有、内容不同 | diverged |
一个文件都不动,报出让人决定 |
| 指向别处的链接 | external |
别的工具的资产,不碰 |
diverged 是关键保护:同名不等于同内容,静默覆盖会真丢数据。遇到时用 agent diff 逐个处理(见下节)。
默认是预演。 不加 --apply 绝不动文件。执行前建议先让用户看预演结果。
比对与决定(处理 diverged)
agent diff --list # 只列出差异清单与涉及的文件,不动任何东西
agent diff # 逐个处理
agent diff find-skills # 只处理这个 id
每处会显示本体库与项目两侧路径、哪些文件内容不同、哪些是单边独有,然后给四个选项:
| 选项 | 行为 |
|---|---|
| 打开 VS Code 对照编辑 | code --diff 逐文件开左右对照;关窗后重新比对,两边一致则自动收编换链接 |
| 保留本体库这份 | 项目那份换成链接,其内容丢弃 |
| 用项目这份覆盖本体库 | 本体库被替换,项目换成链接 |
| 跳过 | 留着下次再说 |
同一个 id 可能有多处待决(实测 find-skills 在三个项目各一份,加本体库共四个版本互不相同)。工具会提示"另有 N 处待决",并在每轮实时重算差异 —— 处理完一处后本体库内容已变,后面几处对照的是新内容。若某处恰好已与新的本体库一致,会直接收编不再询问。
编辑器不可用(找不到 code 命令)时会提示,其余三个选项仍然可用。
安装新 skill(本体库还没有的)
本工具不负责从网上下载。本体库为空或用户要装一个本地没有的 skill 时,用 skills.sh 的 CLI 下载到 ~/.agents/skills/,再用 agent enable 启用。skill 目录也可以手动放到 ~/.agents/skills/<id>/(含 SKILL.md),本工具会自动纳管,只是没有上游因而不可更新。
边界
- 目前只支持 Claude Code(
.claude/skills/)作为启用目标 - 不写
.skill-lock.json - 不删非本工具建立的东西
list只读不动文件;所有副作用都在enable/disable/update/scan --apply