利用符号链接(symlink)解决多AI工具间Skill文件重复管理的痛点,真正实现工作流的统一与高效。
最近我开发了一款开源小工具,名为Kitestring。
它解决的问题虽然不大,却很实用:当你同时使用Claude Code、Codex、Copilot CLI、Gemini CLI时,不想再把同一份Skill反复复制到多个目录里。每个AI工具都有自己读取Skill的固定路径。为了让同一套工作流在不同工具中都能畅通运行,最直觉的办法就是复制文件夹。Claude Code需要一份,Codex需要一份,Copilot CLI需要一份,Gemini CLI可能也需要一份。但只要你真正开始持续使用并频繁修改Skill,复制文件夹很快就会变得混乱不堪、难以维护。
Kitestring做的事情非常简单:只保留一份真实的Skill源文件,然后通过符号链接(symlink)将其分发到各个AI工具会读取的目录里。就像放风筝一样——Skill可以出现在不同工具的目录中,但线始终握在你手里,真正需要维护的文件永远只有一份。
为什么我需要这个工具
前段时间,我写了一篇关于如何利用Claude Marketplace管理Skills的文章。当时的出发点很明确:随着电脑里Skill文件越来越多,希望借助Anthropic官方提供的能力统一管理它们。这个初衷至今依然成立。但继续使用一段时间后,我发现Claude Marketplace只能解决Claude Code生态内的Skill管理问题。如今我的日常使用场景早已超出Claude Code——还会用到Codex、Copilot CLI、Gemini CLI,并且也在尝试其他Agent工具。这些工具各自有着自己的目录约定,并不会主动读取Claude Code存放Skill的路径。
同时,我也针对工作或创作场景编写了不少Skill,例如article2ticktick、mp-article-writor、logging-session。这些Skill一开始都是为了解决自己真实的工作流问题,并非为了上架某个市场而写。它们里面包含我的路径约定、工作流习惯,甚至一些不适合公开的个人信息。为了管理它们而创建开源项目并上架Claude Marketplace,显然并不合适。
还有一些开源项目也没有主动适配Claude Marketplace,比如我下载过的khazix-skills、magazine-web-ppt。我完全可以通过提交Issue甚至PR来帮助项目适配Claude Marketplace,但最终是否接入,并不取决于我。
我希望自己能决定Skill放在哪里,也希望多个AI工具都能读取同一份Skill。
直接复制不行吗?
直接复制一份,是最简单的解决办法。Copilot CLI从~/.copilot/skills/路径读取Skill,那就复制一份到那里;Codex读取~/.codex/skills/,就再复制一份。但随之而来的问题是数据不同步。
比如在使用mp-article-writor时,我经常根据真实的写作过程调整规则。今天发现文章开头容易显得空洞,就加一条禁止教科书式开头的规则;明天觉得标题太像产品说明书,就补充一条标题风格的规则;后天发现自检报告仍然带有明显的“AI味”,于是继续修改审读标准。
如果把这个Skill复制到好几个工具目录里,那每修改一次,就得回想自己曾经复制到过哪些地方。只要漏掉一个目录,那个工具读到的就是旧版本。再过几周,连自己都很难分清哪个才是最新的。从GitHub下载的开源项目也是同理——作者更新后可以快速拉取最新改动,但仍然需要手动一份份复制到其他工具目录中。目录越多,复制越容易遗漏。
还有一个更棘手的问题:Skill的迭代需求,最容易出现在日常使用过程中。当在某个项目里调用Agent,发现某条规则不够准确时,最理想的方式是直接让当前Agent基于当下上下文帮自己修改Skill。但如果Skill的真实存放路径和当前项目路径完全分离,就需要先总结问题,再切换到保存Skill的目录里去修改。这一步看似不大,却会明显打断工作流。我真正想要的状态是:无论在哪个工具、哪个项目中修改Skill,最后改到的都应该是同一份源文件。
symlink像风筝线,真实文件只有一份
更合适的方案是使用符号链接(symlink)。你可以把symlink近似理解成Windows上的“创建快捷方式”,或macOS上的“制作替身”。它看起来像一个文件夹,但真实内容存放在别处。和普通快捷方式相比,symlink对很多命令行工具更友好。大多数软件会直接把它当作真正的文件夹来处理。
这样一来,真实文件夹只需保留一份,放在自己真正想维护的位置。其他工具目录里放置的不是复制品,而是指向源文件夹的symlink。你只需要修改mp-article-writor的源文件,Claude Code、Codex、Copilot CLI顺着symlink读到的就是最新内容。不用再回想自己复制过哪些目录,也不用担心某个工具还在使用上周的旧版本。
symlink带来的另一个好处,是能把所有Skill统一保存在一个本地文件夹中,同时让不同工具读取同一份内容。这样既能保持文件管理的清晰性,也方便随时迭代自己的工作流。
问题在于,symlink本身不够顺手。它通常需要通过命令行创建:
ln -s 真实文件夹路径 目标文件夹路径
如果同时使用Claude Code、Copilot CLI、Codex、Gemini CLI,每个工具又有用户级路径和项目级路径,就需要反复执行类似命令。路径需要记忆,目录需要查找,创建完还要确认是否生效。
因此我做了Kitestring。它的目标不是发明新的Skill格式,也不是重新做一个Skill市场。它只做一件事:帮你把已有Skill的源文件和各个工具目录之间的symlink管起来。
Kitestring能把已有Skill找出来
很多人真正需要这个工具时,本地早就已经有一堆Skill了。它们可能存在于Claude Code或Codex的目录里,可能来自Claude Marketplace,也可能是从某个GitHub仓库clone下来的文件夹。所以Kitestring支持多种导入方式,尽量让你不用从零开始整理。如果你已经在使用symlink管理Skill,别担心——导入时会自动识别并标识,同时也会追溯真正的Skill源文件一并导入。
第一种方式:从工具的用户级默认读取路径导入。
Kitestring内置了Claude Code、Copilot CLI、Gemini CLI、Codex的默认路径,也支持通用的Agent Folder。比如Claude Code默认路径是:
~/.claude/skills/
Codex默认路径是:
~/.codex/skills/
如果这些目录里已经有Skill,Kitestring会尝试识别并纳入管理。对于从Claude Marketplace下载的Skill,也做了专门识别。适配Claude Marketplace的Skill项目,路径往往是类似这样的层级:
~/skills/article2ticktick/skills/article2ticktick
它和一般的Skill文件夹路径不同。所以Kitestring默认会扫描~/.claude/plugins/marketplaces,在有限深度内递归查找SKILL.md,同时跳过~/.claude/plugins/cache/这类缓存目录。只要Skill位于Kitestring已配置的扫描路径下,并且目录内有SKILL.md,Kitestring就会尽量把它识别出来,而无需用户先理解每个平台的目录习惯。
第二种方式:从本地文件夹导入。
Kitestring可以直接从本地文件夹中扫描Skill。无论这个文件夹里只有一个Skill,还是包含多个Skill,它都会递归查找SKILL.md,读取里面的name和description,并把真实文件夹记录为Skill源目录。比如你自己有一个独立的skills文件夹,用来保存自己创建的Skill以及从GitHub下载的开源Skill,就可以用这种方式批量导入。
第三种方式:创建项目并导入。
一个独立开发项目、一个Obsidian仓库,都可以视作Kitestring里的“项目”。在同一个项目中,我们可能会使用多种Agent工具。比如我自己就经常同时使用Claude Code、Codex和Copilot CLI。对于某些常用Skill,每个工具都应该能读到。通过项目导入后,Kitestring可以从项目维度查看工具和Skill的关系。你可以看到这个项目下有哪些Skill,也可以看到它们是否已经分发到对应工具路径中。
这个能力很适合探索新工具。比如已经在某个项目里用Claude Code打磨出一组常用Skill,现在想试试Codex,就不用手动复制目录。只要在项目视图里把这些Skill分发到Codex的项目级路径即可。
Kitestring只做本地管理
导入Skill之后,Kitestring会记录它的源路径、来源类型、Git信息和分发记录。你可以看到一个Skill来自本地文件夹,还是来自GitHub。你也可以看到它当前是否位于Git仓库中,以及它已经被分发到了哪些工具路径。
这里有一个重要边界:Kitestring是本地Skill管理工具。导入意味着在Kitestring中创建Skill记录。原文件仍然保存在原来的位置,Kitestring不会默认把它复制到自己的项目目录里。直接从GitHub URL导入时除外——这种方式下,Kitestring会把仓库clone到:
~/.kitestring/repos/
一开始我不想把自动更新做得太激进。Skill里经常包含工作流、提示词、路径约定和个人习惯,它不是缓存文件,不能被随便覆盖。如果Skill来自GitHub,或者本地目录本身就是一个Git仓库,Kitestring可以尝试拉取更新。但目前我采用的是比较保守的策略:只处理干净工作区里的fast-forward更新。如果存在未提交文件、未跟踪文件,Kitestring会拒绝拉取。如果分支已经分叉,Kitestring也不会强行合并或覆盖。对于Skill来说,我更希望更新是可控的,而不是自动替你做危险的决定。
分发就是创建symlink
Kitestring里的“分发”,本质上就是在指定路径创建symlink。最常做的动作很简单:选中一个Skill,在对应工具路径上点击分发按钮,Kitestring就会创建symlink,并记录这条分发关系。
比如把mp-article-writor分发到Claude Code,也分发到Codex。Kitestring会在对应目录下创建symlink。取消分发时,它只会移除目标路径里的symlink,不会删除Skill源文件。如果你需要自定义路径,也可以手动输入目标目录。这个功能对不完全遵循默认路径的工具,或者个人自定义配置来说,非常有用。
以前遇到Skill没生效时,常常要在几个目录之间来回检查——到底是Claude Code没读到,还是Codex目录里没有,还是复制的是旧版本,还是名字写错了。Kitestring至少把这件事变成了一个可以被看见的状态。你能看到一个Skill分发到了哪里,也能看到某个工具路径下是否已经存在对应symlink。
项目级分发适合多Agent工具项目
一个项目里可能同时使用Claude Code和Codex。它们各自有项目级Skill路径,比如.claude/skills/和.codex/skills/。如果一个项目级Skill对这两个工具都有用,Kitestring可以把它分发到对应路径。在项目视图里,你可以看到这个项目下的所有Skill,以及它们在各个工具路径里的状态。你可以对单个Skill分发,也可以按某个工具列,把当前项目内尚未分发的Skill逐个分发过去。
这个功能的价值在于,它把“这个项目使用哪些Skill”和“这些Skill是否已经被各个Agent工具读到”放在同一个视图里。 对于使用者来说,这比手动记路径可靠得多。
现在可以下载试用
Kitestring v0.1.0现在已经发布,目前仍处于Early Preview阶段。这个版本还不完美——配置格式、交互细节、工具兼容范围,都可能在后续版本里继续调整。首个公开版本提供macOS、Windows和Linux的安装包。
如果你已经在使用Claude Code、Codex、Copilot CLI、Gemini CLI,并且本地已经积累了越来越多的Skills,Kitestring现在就可以帮你处理这些工具的默认路径导入、Skill识别和symlink分发。
它现在做的事情很明确:把散落在不同目录里的Skill,重新变成一份源文件和几根清楚的线。
GitHub地址:balabalabalading/Kitestring[1]
Release下载地址:Kitestring Releases[2]
如果你也被Skill的路径和分发问题烦过,可以先试试Kitestring。先把这根风筝线牵起来。
- https://github.com/balabalabalading/Kitestring ↩
- https://github.com/balabalabalading/Kitestring/releases ↩
