让自建 Claude Code skills 有一个真正的"家"
Claude Code 的 skills 机制很好用——只要在 ~/.claude/skills/<name>/SKILL.md 里写好 frontmatter,它就会自动加载。但真正开始写自己的 skill 之后,你大概率会很快撞到一个很小但非常恼人的问题:这个 skill 到底应该住在哪里?
一个很小但很恼人的问题
~/.claude/skills/ 是 Claude Code 规定的加载位置,Claude Code 只认这里。于是很多人(包括最开始的我)就很自然地直接把自建 skill 写进这个目录。
问题很快就暴露了。这个目录同时承担两个角色:
- 你正在开发的 skill——需要版本管理,需要多机同步,需要和其他代码一样被
git log追踪 - 你装的别人写好的 skill——第三方、不需要版本管理、各自更新节奏不同
这两件事混在一起,你会开始遇到一些看起来很傻但很实际的麻烦:
- 要不要把
~/.claude/skills/直接变成 git 仓库?如果变,那第三方 skill 怎么办?加.gitignore一条一条排除吗? - 换了台机器,我怎么分清”我要带走的”和”那台机器上装的”?
- 我刚改的这个 skill 是在哪台机器上改的?它的权威版本在哪?
这些问题单个看都不致命,但加起来就会形成一种低级但持续的烦躁感:你对自己的工具链失去了”地图”。
仓库即真实源,加载位置只是符号链接
我现在的做法很简单:
- 所有自建 skill 放进一个独立的 git 仓库。我自己命名为
cc-skills,放在~/workspace/cc-skills ~/.claude/skills/下只放符号链接,指回仓库里对应的目录- 第三方 skill 直接放在
~/.claude/skills/下的实体目录里,完全不纳入版本管理
一个自建 skill 的”落地”就是一条命令:
ln -s ~/workspace/cc-skills/<skill-name> ~/.claude/skills/<skill-name>做完之后,Claude Code 加载的是软链指向的真实目录。而因为软链是透明的,你在 cc-skills 仓库里编辑 SKILL.md,下一次 Claude Code 启动就立即生效,没有任何同步步骤、没有”要不要复制过去”的犹豫、没有第二份会漂移的副本。
这就是”单一真实源”(single source of truth)在这里的具体含义:只有一个地方能编辑,另一个地方是指针。你永远不会问自己”我该改哪一个”,因为只有一个能改。
自建 skill 和第三方 skill 天然分层
这套做法还有一个不在最初设计目标里、但用起来很舒服的副作用:物理分层自动承担了认知分层。
随便跑一下 ls -la ~/.claude/skills/,你会看到类似这样的东西:
lrwxr-xr-x blog -> ~/workspace/cc-skills/bloglrwxr-xr-x codewise -> ~/workspace/cc-skills/codewiselrwxr-xr-x recall -> ~/workspace/cc-skills/recalldrwxr-xr-x cartographerdrwxr-xr-x ui-stylingdrwxr-xr-x unocss一眼就能分清:软链的是自己维护的,实体目录的是装的。不需要写文档记录”哪个是我的、哪个是别人的”——ls -la 本身就是文档。
这件事的意义被低估了。你每天要打交道的工具越多,区分”自己维护的”和”别人维护的”就越重要——前者你有责任修,后者你有责任更新。它们的心智模型完全不同。
新增和删除的动作链
有了这套结构,skill 的生命周期操作就变得非常短:
新增:
cd ~/workspace/cc-skillsmkdir new-skill && $EDITOR new-skill/SKILL.md # 写 frontmatter 和内容ln -s ~/workspace/cc-skills/new-skill ~/.claude/skills/new-skillgit add new-skill && git commit -m "feat: add new-skill"删除:
rm ~/.claude/skills/old-skill # 只删软链,不碰真实目录cd ~/workspace/cc-skillsgit rm -r old-skill && git commit -m "chore: remove old-skill"值得单独提一句的是 “只删软链”的安全性:rm 一个符号链接时,删除的只是那个链接本身,指向的目录原封不动。这意味着你可以很放心地临时摘掉某个 skill(比如在调试别的东西时暂时不想让它被加载),之后再 ln -s 回来,仓库里的真实内容一次都不会动。
为什么不干脆把 ~/.claude/skills 整个变成 git 仓库
这是一个合理的怀疑,值得单独回答。直接在 ~/.claude/skills/ 里 git init 看起来更简单——少一层软链,少一个仓库路径。但它会撞上几个具体问题:
第三方 skill 会被卷进版本管理。 你只有两个选择:要么让它们污染 git history(别人的更新会变成你的 commit),要么一条一条加 .gitignore。两个都很别扭,而且每装一个新的第三方 skill 就要手动维护一次 ignore 规则。
不同机器的第三方 skill 集合不一样。 你工作机上装了 cartographer,家里这台装了别的;同一个 git 仓库在两台机器上的状态会持续打架,每次 pull 都要处理冲突。
“这个 skill 是从哪来的”会变成一个模糊问题。 一旦所有目录都长一样,你很快就会分不清某个 skill 是自己写的、是抄的、还是从 marketplace 装的。几个月后回头想修改它,你得先考古来源。
把自建和第三方隔离在不同的存储层(一个在独立仓库、一个在加载目录),这几个问题就同时消失了——不是被解决,是根本不会出现。
小结:认知清晰度比技术难度更值钱
这套做法没解决任何技术难题。ln -s 是 1970 年代就有的东西,git 也不是新工具。它真正解决的是每天都要回答一次的那个小问题:我这个 skill 的权威版本在哪。
当答案从”可能在这里,也可能在那里,可能两边还不一致”变成”只有一个地方,其他全是指针”,维护成本会在不经意间下降——你会更愿意写新 skill,更愿意改旧 skill,更愿意删掉不用的 skill。
工具链的清晰度和你对它的投入之间,存在一个很隐蔽的正反馈。把基础设施理顺,不是为了让当下的某件事变得更快,而是为了让未来的每一件事都少一点摩擦。自建 skill 的”家”值得认真安排一次,之后就不用再管了。