让 AI Skill 在多个 Agent 之间共享

怎样让 AI Skill 在多个 Agent 之间共享

我这台电脑上装了不少自己写的 Skill,WorkBuddy、Trae、Codex、Loomy、WorkBuddy 国际版,基本是同时开着用的。最早没当回事,觉得 skill 装了就是装了。后来接连踩了三个坑,才意识到这事儿不解决,越用越烦:

  • 切一次 WorkBuddy 账号登录,手头的 skill 没了;
  • 市场里装的插件型 skill,换个账号授权态就没了;
  • 换一个 Agent 客户端,又得把同一批 skill 重装一遍。

skill 被三层东西绑死了:账号、Agent、机器。装得越多,越像给自己挖了个坑。

这篇文章就讲我是怎么把这事儿理顺的——一个 skill 仓库,多个 Agent 靠「软链接」共用,换电脑、换账号都能快速恢复。

如果你还没搞清 Skill 本身该怎么写,可以先看我上一篇「怎样设计一个真正好用的 AI Skill」。本篇只解决「skill 怎么在多端共享」这一个具体问题。

一、先把问题拆开:到底被什么绑住了

三层绑定,每一层的表现和根因都不一样,得分别对付。

绑定层 表现 根因

账号绑定 市场装的 skill 切账号就没了 插件型 skill 住在 plugins/cache,带着授权态,跟登录账号走
Agent 绑定 换一个客户端,skill 一个都不在 每个客户端只读自己目录下的 skills/,彼此不互通
机器绑定 换电脑,目录全没了 目录挂在 C:\Users\用户名 下,跟这台机器绑定

我原先以为是「我装的方式不对」,后来才看明白:前两层是软件设计层面的隔离(账号态、客户端各自为政),第三层是物理位置的问题。它们其实可以用同一套思路解决,不用分别折腾。

二、关键发现:其实各家吃的是同一套标准

我逐个去翻了 WorkBuddy、Trae、Loomy、Codex、Claude Code 的配置目录,结论是:它们全都读 SKILL.md 这种文件夹,只是目录位置不同。

Agent 全局 skills 目录
WorkBuddy `.workbuddy\skills`  
WorkBuddy 国际版 `.workbuddy-ai\skills`  
CodeBuddy `.codebuddy\skills`  
Trae CN `.trae-cn\skills`  
Loomy `C:\Users\Public\Loomy\<实例id>\opencode\skills`  
Codex `.codex\skills`  
Claude Code `.claude\skills`  
跨运行时 `.agents\skills`(Codex / Gemini / Copilot CLI 也会读)

(最后那个 .agents\skills 是 Codex 等 CLI 的跨运行时路径,顺手也加上,不亏。)

既然格式一样、只是目录不同,思路就顺了:把 skill 集中放在一个「中枢目录」,再用链接挂到各家目录里。改一处,全端生效;装一次,八个客户端都能用。

三、链接:是软链接,不是快捷方式

这里得先说清一个容易混的概念,因为方案就建立在这上面。Windows 上有三种「看起来像链接」的东西:

1. Junction(目录联接)—— 我用的就是这个

文件系统层的链接,只针对目录。程序读 ~/.claude/skills/pdf/ 时,系统自动转到真源目录,程序完全无感知——它以为那就是个普通文件夹,还不需要管理员权限。

2. Symbolic Link(符号链接)

真正意义上的软链接,文件目录都能链,跨盘跨网络都行,但创建默认要管理员权限或开开发者模式。macOS / Linux 的 ln -s 就是它。

3. 快捷方式(.lnk)—— 没用

快捷方式只是个 2KB 小文件。程序读到它,不会自动跳转,除非程序专门写代码去解析它。AI 客户端不会认,所以快捷方式在这个场景里派不上用场。

一句人话总结:我们要的就是「一个文件夹,同时在八个地方出现」,八处的名字指向同一份实体文件,改哪个都是改同一份。junction 是只链目录的简化版软链接,够用且免权限,最合适。

我用的是逐 skill 联接(每个 skill 单独建一条链接),不是把整个 skills 目录整目录联接。两个原因:一是各客户端的 skills 目录本身保持真实目录,避免某些客户端的文件扫描器跳过重解析点;二是 WorkBuddy 自己的元数据文件(migration.json、lock 之类)能原样留在它自己目录里,不会污染到 Claude / Codex。

四、中枢长啥样

按这个思路,结构是这样:

C:\Users\miyi2\AI-Skills\          ← 唯一真源,所有 skill 住这里
   ├─ editaplot\SKILL.md
   ├─ youtube-transcribe\SKILL.md
   ├─ skill-maker\SKILL.md
   └─ ... (32 个)
   ├─ _plugin-vault\               ← 市场插件 skill 的固化副本(不对外分发)
   └─ _tools\
       └─ Sync-SkillsHub.ps1       ← 核心同步脚本

   每个 skill 建一条 junction(不是复制),挂到下面八个位置:
   ~/.workbuddy/skills/      ~/.workbuddy-ai/skills/
   ~/.codebuddy/skills/      ~/.trae-cn/skills/
   ~/.loomy/skills/          ~/.codex/skills/
   ~/.claude/skills/         ~/.agents/skills/

_plugin-vault 是给「账号绑定」那个坑准备的:市场插件 skill 原本住在 plugins\cache,跟着账号走。我把它们复制一份固化进保险库,脱离缓存,之后就不随登录态失效了。它们故意不参与分发,避免和已加载的插件重名打架——真要用,手动从中枢取出来改名即可。

五、同步脚本怎么写

核心就一个 PowerShell 脚本,分三段跑:

脚本逻辑 干啥

Stage 1 harvest 把各客户端里已有的 skill 收拢进中枢(移动,不是删除,可逆)
Stage 2 link 把中枢里每个 skill 用 junction 挂到八个客户端目录
Stage 3 git 可选:把中枢提交进 git,方便换机器恢复

几个关键点说一下:

  • 可重复跑。加了 skill 就再跑一次;删了 skill,脚本会自动把对应链接拆掉,不留僵尸。
  • 不重复 harvest 链接。早期版本会把已建好的 junction 又当「待回收 skill」处理一遍,日志刷 256 行,极端情况会误移文件。后来加了个判断:碰到链接就跳过。
  • 路径不写死。脚本默认从 $env:USERPROFILE 取当前用户目录,换电脑用户名不是 miyi2 也能用;换 Windows 账号时加 -UserHome "C:\Users\新用户名" 即可。
  • Loomy 特殊处理。Loomy 的 skill 目录在 C:\Users\Public 下,而且实例 id 升级后可能变;它启动时还会用模板覆盖 opencode.json 的 skills.paths,所以没法靠配置文件加目录,只能直接把链接建进那个目录。

跑起来的命令就一行:

powershell -ExecutionPolicy Bypass -File "C:\Users\miyi2\AI-Skills\_tools\Sync-SkillsHub.ps1" -IncludePluginCache -WithGit

-IncludePluginCache 固化市场插件,-WithGit 顺手做版本备份。不想带 git 就去掉后面那个开关。

六、跑完是什么样

验证结果:8 个目录各 32 条链接,一共 256 条,且都能正常读到 SKILL.md。

.workbuddy\skills      32    ← WorkBuddy
.workbuddy-ai\skills   32    ← 国际版
.codebuddy\skills      32
.trae-cn\skills        32    ← Trae CN
.loomy\skills          32    ← Loomy
.codex\skills          32    ← Codex
.claude\skills         32    ← Claude Code(多送的)
.agents\skills         32    ← 跨运行时路径

保险库里另存了 44 个市场插件 skill 的副本(pdf、westock、tencent-docs 这些)。

重要一步:跑完重启 Trae / Loomy / Codex / Claude Code。WorkBuddy 这边的路径没变,基本无感;但其他客户端要等它们启动时重新扫描 skills 目录才会认。

七、换电脑 / 换账号:三步恢复

这是「机器绑定」那层的解法。先说一个容易误解的点:junction 链接是不带走的。git 里存的是 32 个 skill 的文件本身,链接是每台电脑本地重建的。所以逻辑是「搬文件 → 新电脑跑一次脚本 → 链接自动生成」。

搬运方式 怎么做 适合

git 推私有远端(推荐) Gitee / GitHub 建个私有仓库,git remote add 再 push 长期多台机器、以后还要同步
整个目录拷贝 U 盘 / 网盘,把 AI-Skills 整目录搬过去 一次性、不想折腾 git
同机换 Windows 账号 不用搬,脚本加 -UserHome "C:\Users\新用户名" 只是换登录用户

新电脑三步:

  1. 装好各 Agent 客户端(WorkBuddy、Trae、Loomy 等各装一次,让它们生成自己的配置目录);
  2. 把 AI-Skills 放到 C:\Users\<新用户名>\AI-Skills;
  3. 双击 _tools\SyncSkillsHub.bat(或直接跑脚本)→ 8 个目录各 32 条链接自动建好,重启所有 Agent 即可。

新电脑少装了某些 Agent 也不影响。脚本照样会给它们建好目录和链接,等你哪天装了直接就能用,不会报错中断。

八、几个特别容易踩的坑

坑 1:直接往 Agent 自己的 skills 目录放真文件夹。 那里每一项都应该是链接。你手动塞个真文件夹进去,它不会同步回中枢,时间一长中枢和客户端就对不上了。记住:真文件夹只进 AI-Skills\,客户端目录里只放链接。

坑 2:以为链接能跟着 git 走。 git 不存 junction,只存文件。换电脑如果只 clone 不跑脚本,八个目录全是空的。链接必须靠脚本在本地重建,这是设计如此,不是 bug。

坑 3:市场插件 skill 跟账号走。 不固化进 _plugin-vault 的话,切账号授权态一丢就没了。已经处理,但提醒你以后从市场新装的,记得再跑一次 -IncludePluginCache。

坑 4:Loomy 实例 id 会变。 升级后 C:\Users\Public\Loomy\6d02254fbb40 那段 id 可能变,脚本里那行要跟着改。其他目录都是按用户目录拼的,不受影响。

坑 5:依赖过不去。 skill 文件能过去,但底层工具过不去。我这边有 16 个左右 skill 需要重装依赖:转录类(yt-dlp、ffmpeg)、channel-keyword-post、knowledge-base-management、code-mentor、ppt-generator、tencent-channel 等。另外 editaplot 要装 Origin、qgis-map-workflow 要装 QGIS、quarkclouddrive 和 tencent-channel 要重新登录。换电脑别指望开箱即用,这些得单独补。

九、最后说一句

我原先以为「共享 skill」是个配置问题,搞个同步盘或者软链一下就行。真做下来才发现,它其实是在同时解开三层绑定:账号态、客户端隔离、机器位置。junction 解决「一处改多处生效」,git 解决「换机器能复原」,保险库解决「市场插件不随账号消失」——三件事各管一摊,合起来才稳。

回头看,值钱的经验就几条:

  • skill 的真源只有一份,客户端目录里不要放真文件夹;
  • 链接本地重建,文件才进版本库,别指望把链接 commit 走;
  • 市场装的 skill 默认跟账号走,要用就先固化;
  • 底层依赖是另一回事,文件同步救不了「没装 ffmpeg」。

当你开始用「中枢 + 链接 + 版本库」这套思路管 skill,就不太会再被「换账号没了」「换个客户端重装」这类事烦到了。这大概才是多 Agent 玩家真正该有的状态。

附录脚本

Sync-SkillsHub.ps1

当前完整内容(我本机在跑的版本)

#Requires -Version 5.1
<#
.SYNOPSIS
  Skills Hub - one source of truth for SKILL.md folders, shared by every AI client.

.DESCRIPTION
  Stage 1  harvest : move existing skills from each client into the hub
  Stage 2  link    : junction every hub skill into each client's skills dir
  Stage 3  git     : optional versioning / cross-machine restore

  Clients covered
    WorkBuddy        .workbuddy\skills
    WorkBuddy AI     .workbuddy-ai\skills
    CodeBuddy        .codebuddy\skills
    Trae CN          .trae-cn\skills
    Loomy            <Loomy install dir>\opencode\skills  (see $targets below)
    Codex CLI        .codex\skills
    Claude Code      .claude\skills
    cross-runtime    .agents\skills   (read by Codex / Gemini / Copilot CLI)

  Safe to re-run at any time. Run again after adding or removing skills in the hub.
#>
param(
    [string]$Hub = (Join-Path $env:USERPROFILE "AI-Skills"),
    [string]$UserHome = $env:USERPROFILE,
    [switch]$IncludePluginCache,
    [switch]$WithGit
)

$ErrorActionPreference = "Stop"
$Reparse = [IO.FileAttributes]::ReparsePoint

# NOTE about Loomy:
#   1. Loomy loads skills from <Loomy install dir>\opencode\skills. The opencode.json
#      skills.paths cannot be used to add extra dirs: Loomy's template-sync mechanism
#      overwrites that file on startup. So we junction hub skills directly into that dir.
#   2. The instance id "6d02254fbb40" may change after a Loomy upgrade - update the line
#      below if it does.
#   3. Inside the Loomy runtime, $env:USERPROFILE points to the Loomy data dir, NOT the
#      real user profile. Always pass -Hub and -UserHome explicitly when run from Loomy.
$targets = @(
    "$UserHome\.workbuddy\skills",
    "$UserHome\.workbuddy-ai\skills",
    "$UserHome\.codebuddy\skills",
    "$UserHome\.trae-cn\skills",
    "C:\Users\Public\Loomy\6d02254fbb40\opencode\skills",
    "$UserHome\.codex\skills",
    "$UserHome\.claude\skills",
    "$UserHome\.agents\skills"
)

function Test-Link([string]$p) {
    if (-not (Test-Path -LiteralPath $p)) { return $false }
    return (((Get-Item -LiteralPath $p -Force).Attributes -band $Reparse) -ne 0)
}
function Remove-Link([string]$p) {
    cmd /c "rmdir `"$p`"" 2>&1 | Out-Null
}
function Test-Skill([string]$p) {
    return (Test-Path -LiteralPath (Join-Path $p "SKILL.md"))
}

Write-Host ""
Write-Host "== Skills Hub ==" -ForegroundColor Cyan
Write-Host "Hub: $Hub" -ForegroundColor Cyan
Write-Host ""

# --- Stage 1: harvest -------------------------------------------------------
if (-not (Test-Path -LiteralPath $Hub)) {
    New-Item -ItemType Directory -Path $Hub -Force | Out-Null
}

foreach ($t in $targets) {
    if (-not (Test-Path -LiteralPath $t)) { continue }
    if (Test-Link $t) { continue }
    # Safety: only harvest agent dirs under $UserHome. Agents installed outside the
    # user profile (e.g. Loomy under C:\Users\Public) must never be moved into the hub.
    if (-not $t.StartsWith($UserHome, [System.StringComparison]::OrdinalIgnoreCase)) { continue }
    # Skip junctions: those are links we created ourselves, never harvest them back.
    Get-ChildItem -LiteralPath $t -Force -Directory -ErrorAction SilentlyContinue | Where-Object {
        $_.Name -notlike ".*" -and -not (Test-Link $_.FullName) -and (Test-Skill $_.FullName)
    } | ForEach-Object {
        $dest = Join-Path $Hub $_.Name
        if (-not (Test-Path -LiteralPath $dest)) {
            Write-Host "  harvest       : $($_.Name)" -ForegroundColor Yellow
            Move-Item -LiteralPath $_.FullName -Destination $dest
        }
    }
}

if ($IncludePluginCache) {
    # Marketplace / plugin skills live inside the plugin cache and can vanish when
    # you switch accounts. Copy them into a vault so they never disappear.
    # The vault is NOT linked into any client (no SKILL.md at its top level),
    # so it can never clash with the plugin that is already loaded.
    $vault = Join-Path $Hub "_plugin-vault"
    if (-not (Test-Path -LiteralPath $vault)) {
        New-Item -ItemType Directory -Path $vault -Force | Out-Null
    }
    $cache = "$UserHome\.workbuddy\plugins\cache"
    if (Test-Path -LiteralPath $cache) {
        Get-ChildItem -LiteralPath $cache -Recurse -Filter "SKILL.md" -File -ErrorAction SilentlyContinue | ForEach-Object {
            $d = $_.Directory
            $dest = Join-Path $vault $d.Name
            if (-not (Test-Path -LiteralPath $dest)) {
                Write-Host "  vault plugin  : $($d.Name)" -ForegroundColor Yellow
                Copy-Item -LiteralPath $d.FullName -Destination $dest -Recurse
            }
        }
    }
}

# --- Stage 2: link ----------------------------------------------------------
$skills = @(Get-ChildItem -LiteralPath $Hub -Force -Directory | Where-Object {
    $_.Name -notlike ".*" -and (Test-Skill $_.FullName)
})
Write-Host ""
Write-Host "Hub holds $($skills.Count) skills. Linking into every client..." -ForegroundColor Cyan
Write-Host ""

foreach ($t in $targets) {
    if (Test-Link $t) { Remove-Link $t }
    if (-not (Test-Path -LiteralPath $t)) {
        New-Item -ItemType Directory -Path $t -Force | Out-Null
    }

    # drop junctions that no longer exist in the hub
    Get-ChildItem -LiteralPath $t -Force -Directory -ErrorAction SilentlyContinue | ForEach-Object {
        if ((Test-Link $_.FullName) -and -not (Test-Path -LiteralPath (Join-Path $Hub $_.Name))) {
            Remove-Link $_.FullName
        }
    }

    $n = 0
    foreach ($s in $skills) {
        $link = Join-Path $t $s.Name
        if (Test-Link $link) { $n++; continue }
        if (Test-Path -LiteralPath $link) {
            Write-Host "  real dir kept : $link" -ForegroundColor DarkGray
            continue
        }
        New-Item -ItemType Junction -Path $link -Target $s.FullName | Out-Null
        $n++
    }
    Write-Host ("  {0,-3} linked -> {1}" -f $n, $t) -ForegroundColor Green
}

# --- Stage 3: git -----------------------------------------------------------
if ($WithGit) {
    $gitDir = Join-Path $Hub ".git"
    if (-not (Test-Path -LiteralPath $gitDir)) {
        Push-Location $Hub
        git init -q
        git add -A
        git commit -q -m "init skills hub"
        Pop-Location
        Write-Host ""
        Write-Host "git repo created in hub." -ForegroundColor Green
    }
    else {
        Push-Location $Hub
        git add -A
        git commit -q -m "sync $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
        Pop-Location
        Write-Host ""
        Write-Host "hub committed to git." -ForegroundColor Green
    }
}

Write-Host ""
Write-Host "Done. Restart WorkBuddy / Claude Code / Codex so they rescan skills." -ForegroundColor Cyan

SyncSkillsHub.bat

作用:做环境预处理,然后调用同目录下 PowerShell 脚本 Sync-SkillsHub.ps1,用来同步 AI-Skills 相关资源,

@echo off
chcp 65001 >nul
rem NOTE: pass -Hub / -UserHome explicitly - inside the Loomy runtime the
rem USERPROFILE env var points to the Loomy data dir, not the real user profile.
powershell -ExecutionPolicy Bypass -File "%~dp0Sync-SkillsHub.ps1" -Hub "%USERPROFILE%\AI-Skills" -UserHome "%USERPROFILE%"
pause

赏

   转载规则


《让 AI Skill 在多个 Agent 之间共享》 由 Bevis23 采用 知识共享署名 4.0 国际许可协议 进行许可。
 本篇
让 AI Skill 在多个 Agent 之间共享 让 AI Skill 在多个 Agent 之间共享
从“Skill 到底是什么”讲起,拆解 SKILL.md、scripts、references、assets 三层结构,梳理一套稳定可复用的 AI Skill 创建流程,并指出常见坑位与核心原则。
2026-10-08
下一篇 
怎样设计一个真正好用的 AI Skill 怎样设计一个真正好用的 AI Skill
从“Skill 到底是什么”讲起,拆解 SKILL.md、scripts、references、assets 三层结构,梳理一套稳定可复用的 AI Skill 创建流程,并指出常见坑位与核心原则。
2026-10-06
  目录
切换