发布于 2026 年 4 月 5 日,星期日
软链接+Git管理个人 agent skill
软链接把分散在各仓库的 Agent skill 映射到统一目录,配合 .gitignore 与 hooks 实现「一处更新、多库复用」;方案覆盖 macOS/Linux/Windows 三平台符号链接差异、版本冲突回滚、CI 自动同步等实战细节,提供可复制的目录模板与一键初始化脚本,让个人技能库在 Git 流程中轻量可追踪、随拉随用。
我现在管理个人 skills 的做法很简单:Git 仓库里只留一份真文件,放在 .agent/skills;.claude/skills、.codex/skills、.cline/skills 这些目录不再各自存一份,而是全部指到这一个位置。这样迁移电脑、做版本管理、给多个 AI 工具共用同一套 skills,会轻松很多。
这套方案在 Windows 上完全能用,也不用把整个 .claude 或 .codex 目录都绑死。更稳的做法是只处理它们的 skills 子目录。别的配置继续留在原位,只有 skills 走统一入口。
先把结构想清楚
你真正维护的只有这一个目录:
D:\AI-Skills-Repo
└── .agent
└── skills
├── skillA
├── skillB
├── skillC
└── skillD
你平时看到的这些路径:
C:\Users\你的用户名\.claude\skillsC:\Users\你的用户名\.cursor\skillsC:\Users\你的用户名\.codex\skillsC:\Users\你的用户名\.copilot\skillsC:\Users\你的用户名\.cline\skillsC:\Users\你的用户名\.gemini\skillsC:\Users\你的用户名\.kiro\skills
都只是入口,不再存真文件。
这里有个很重要的前提:你要的是共享池。也就是说,只要某个 skill 进了 D:\AI-Skills-Repo\.agent\skills,上面这些工具都会看到它。你如果本来就想“一处维护,全部共用”,这正是你要的效果。如果你希望某个工具只能看到自己专属的 skill,这种做法就不适合。
先跑通一个最小版本
Windows 上做这种目录共享,我更建议用 Junction。它就是目录级别的入口映射,本地磁盘场景很稳。下面这组命令先把最小版本跑通,只接 .claude\skills,确认没问题再批量铺开。
前置条件就一条:你已经装了 Git,PowerShell 能正常运行。
$repo = "D:\AI-Skills-Repo"
$shared = Join-Path $repo ".agent\skills"
$claudeRoot = Join-Path $HOME ".claude"
$claudeSkills = Join-Path $claudeRoot "skills"
New-Item -ItemType Directory -Force -Path $shared | Out-Null
New-Item -ItemType Directory -Force -Path $claudeRoot | Out-Null
if (Test-Path $claudeSkills) {
throw ".claude\\skills 已存在,先把里面的内容迁走,再回来创建入口。"
}
New-Item -ItemType Junction -Path $claudeSkills -Target $shared | Out-Null
跑完后,你可以马上验证:
Get-Item $claudeSkills | Format-List FullName, LinkType, Target
你应该能看到 LinkType 是 Junction,目标路径指向 D:\AI-Skills-Repo\.agent\skills。这一步对了,后面只是把同样的动作复制给别的工具。
把现有 skills 收进 Git 仓库
真正麻烦的不是建入口,而是把原来散在各处的 skill 收拢。我的建议是先迁移,再做链接,不要一边迁一边改。
先建好仓库目录:
$repo = "D:\AI-Skills-Repo"
$shared = Join-Path $repo ".agent\skills"
New-Item -ItemType Directory -Force -Path $shared | Out-Null
Set-Location $repo
if (-not (Test-Path ".git")) {
git init
}
如果你原来在 .claude\skills、.codex\skills 下面已经有现成的 skill 目录,可以用这段脚本先统一搬过去:
$repo = "D:\AI-Skills-Repo"
$shared = Join-Path $repo ".agent\skills"
$tools = @(".claude", ".cursor", ".codex", ".copilot", ".cline", ".gemini", ".kiro")
New-Item -ItemType Directory -Force -Path $shared | Out-Null
foreach ($tool in $tools) {
$src = Join-Path (Join-Path $HOME $tool) "skills"
if (-not (Test-Path $src)) {
continue
}
Get-ChildItem $src -Directory | ForEach-Object {
$dest = Join-Path $shared $_.Name
if (Test-Path $dest) {
Write-Warning "跳过重名 skill:$($_.Name) 来自 $tool"
} else {
Move-Item $_.FullName $dest
}
}
}
这段脚本的意思很直接:
- 找到每个工具目录下已有的
skills - 把每个 skill 文件夹搬到
D:\AI-Skills-Repo\.agent\skills - 如果遇到重名,不覆盖,直接提醒你手动处理
搬完后,检查一下共享目录里是不是已经收齐:
Get-ChildItem $shared
如果这里已经看到了你要的 skill 列表,说明真文件已经集中好了。
一次性给 7 个工具挂入口
确认真文件已经在 Git 仓库里之后,再批量给每个工具创建 skills 入口。这里我不会直接粗暴删目录,只会在目录为空或者本来就是入口时继续,免得误删你还没迁走的东西。
$repo = "D:\AI-Skills-Repo"
$shared = Join-Path $repo ".agent\skills"
$tools = @(".claude", ".cursor", ".codex", ".copilot", ".cline", ".gemini", ".kiro")
foreach ($tool in $tools) {
$toolRoot = Join-Path $HOME $tool
$toolSkills = Join-Path $toolRoot "skills"
New-Item -ItemType Directory -Force -Path $toolRoot | Out-Null
if (Test-Path $toolSkills) {
$item = Get-Item $toolSkills -Force
if ($item.LinkType -eq "Junction") {
Remove-Item $toolSkills -Force
} elseif ((Get-ChildItem $toolSkills -Force | Measure-Object).Count -eq 0) {
Remove-Item $toolSkills -Force
} else {
Write-Warning "$toolSkills 里还有内容,先确认已经迁走,再重新执行这段脚本。"
continue
}
}
New-Item -ItemType Junction -Path $toolSkills -Target $shared | Out-Null
}
这一步做完后,.claude\skills、.codex\skills、.cline\skills 这些路径看起来还是各自独立,实际上都在读同一份目录。
你怎么确认它真的生效了
最直接的验证方式不是看链接属性,而是实际加一个测试 skill。
先在 Git 仓库里新建一个目录:
$shared = "D:\AI-Skills-Repo\.agent\skills"
New-Item -ItemType Directory -Force -Path (Join-Path $shared "demo-skill") | Out-Null
再分别检查两个工具入口:
Get-ChildItem (Join-Path (Join-Path $HOME ".claude") "skills")
Get-ChildItem (Join-Path (Join-Path $HOME ".codex") "skills")
如果这两个地方都能看到 demo-skill,说明共享已经生效。你再删掉 demo-skill,两个入口里也会一起消失。
Git 这边也顺手确认一下:
Set-Location "D:\AI-Skills-Repo"
git status
如果 git status 能看到 .agent/skills/demo-skill 之类的变更,说明以后你只需要在这一处提交就够了。
这几个坑最容易踩
不要把整个工具目录都做成入口
如果你把 .claude 整个目录都指到 .agent,很容易把别的配置也绑进去。你要共享的是 skills,那就只处理 skills 这一层。
先迁走,再创建入口
New-Item -ItemType Junction 不能覆盖一个还装着内容的真实目录。你如果一上来就建入口,很容易把旧 skill 留在原地,后面自己也分不清哪份才是真的。
重名 skill 要手动定规矩
不同工具里可能早就各自装过一个同名目录。脚本里我直接选择跳过不覆盖,因为覆盖谁都不安全。最稳的做法是你自己决定保留哪一份,必要时改名。
共享池意味着所有工具看到的是同一套东西
这套方案的好处是维护成本低,坏处也很明显:Claude 能看到的 skill,Codex、Cline、Cursor 也能看到。你如果打算做工具专属 skill,最好在命名或目录层面提前规划。
不要把密钥直接提交进 Git
如果某些 skill 目录里带了本地账号、token、环境文件,先在仓库里加 .gitignore,再提交。共享 skills 和共享敏感信息不是一回事。
你把这套结构搭好之后,后面新增 skill 的动作就只剩一个:把目录放进 D:\AI-Skills-Repo\.agent\skills,提交 Git。别的工具目录不用再碰。