Windows 實作筆記 · 相對路徑示例

讓 Claude Code 讀到 .agents/skills

Claude Code 會掃 .claude/skills,不會替你尋找 .agents/skills。這篇用 Junction 或 Symbolic Link 補上這段路徑, 再以 @AGENTS.md 共用專案規則。最後真正要驗收的是 slash command、 auto-invocation 與 /context

環境:Windows / PowerShell 預設:逐 Skill Link 驗收:slash / auto / context

短答案

.claude/skills 已有內容時,不要整棵替換。只替需要共用的 Skill 建立 Link:

.agents/skills/                     # 可攜 Skill 的來源
.claude/skills/<portable-skill>     # 指向來源的 Junction / symlink
.claude/skills/<claude-only>        # Claude 專屬實體 Skill
AGENTS.md                           # 共用專案規則
CLAUDE.md                           # @AGENTS.md + Claude-only 補充

只有兩邊內容完全同質時,才考慮整棵 Skills Junction。規則檔優先用 @AGENTS.md;Hard Link 留給固定在 Windows/NTFS、而且兩個檔名必須永遠 同內容的環境。

Claude Code 沒有掃 .agents/skills

Agent Skills 規格定義 SKILL.md 的結構,各個 client 仍可決定安裝位置。 許多工具採用 .agents/skills;Claude Code 的專案路徑則是 .claude/skills/<skill-name>/SKILL.md。檔案可讀,不等於原生 discovery 會找到它。

來源

內容留一份

.agents/skills 保存可攜版本,避免兩份 SKILL.md 漂移。

轉接

補原生路徑

Link 把同一份內容露出在 .claude/skills,不靠模型臨時搜尋。

驗收

回到 Claude 測

檢查 slash autocomplete、auto-invocation 與 /context,不只看檔案存在。

mapping 已經存在,但目標是實體目錄

以下以專案根目錄內的 Link bootstrap 為例。腳本可用相對路徑定義 .claude\skills → .agents\skills,並讓 CLAUDE.mdGEMINI.md 共用來源 AGENTS.md。若目標已經是實體目錄,就不能直接覆蓋。

PowerShell · mapping 示意
$directoryLinks = [ordered]@{
    ".claude\skills" = ".agents\skills"
}

$instructionLinks = [ordered]@{
    "AGENTS.md" = "AGENTS.md"
    "CLAUDE.md" = "AGENTS.md"
    "GEMINI.md" = "AGENTS.md"
}

發布文件可使用的中性情境

  • 所有路徑都以目前專案根目錄為基準。
  • .agents\skills.claude\skills 目前都是實體目錄。
  • 兩邊可能各有獨有 Skill,也可能存在同名但內容不同的檔案。
  • 完成內容比對前,不刪除、不搬移,也不覆蓋任何既有目錄。
  1. bootstrap 從目前專案根目錄解析來源與目標。
  2. 建立 Link 前,先檢查目標是否已存在以及它的 LinkType
  3. 目標若是實體目錄,腳本應停止或跳過,交由人工先比較內容。

Link 方案沒有壞。真正的前置工作,是先拆清楚兩邊哪些 Skill 可共用、哪些只能留給 Claude。直接刪除 .claude\skills 可能把 Claude-only Skill 一併移除。

動手前,先確認 .claude\skills 是什麼

  • 以下命令都在專案根目錄執行。
  • .agents\skills 必須存在,每個 Skill 目錄內要有 SKILL.md
  • 查清楚 .claude\skills 是實體目錄、Junction,還是 Symbolic Link。
  • Hard Link 的來源與目標必須位於同一個 volume。
  • Junction 可跨本機 volume,但不能指向映射的網路磁碟。
PowerShell · 唯讀盤點
$repoRoot = (Resolve-Path ".").Path
$agentsSkills = Join-Path $repoRoot ".agents\skills"
$claudeSkills = Join-Path $repoRoot ".claude\skills"

if (-not (Test-Path -LiteralPath $agentsSkills)) {
    throw "找不到 Skill 來源目錄:$agentsSkills"
}

Get-Item -LiteralPath $agentsSkills -Force |
    Select-Object FullName, Attributes, LinkType, Target

if (Test-Path -LiteralPath $claudeSkills) {
    Get-Item -LiteralPath $claudeSkills -Force |
        Select-Object FullName, Attributes, LinkType, Target
}
這一步做什麼只讀取兩個目錄的型態與 target。
可以繼續來源存在;既有 Link 顯示 ReparsePoint
先停下來.claude\skills 是實體目錄,先比較內容。

兩種接法怎麼選

整個目錄做 Junction

適合所有 Skill 都能共用,而且 .claude\skills 尚不存在的專案。

  • 結構簡單。
  • 無法保留 Claude-only Skill。
  • 建立後仍要到 Claude Code 裡驗收。
現況 建議
兩邊 Skills 完全同質 整棵 Junction
已有 Claude-only Skill 逐 Skill Junction/symlink
跨 Windows、macOS、Linux 逐 Skill Symbolic Link + bootstrap script
Cloud session 或 remote container 提交 project Skill 或安裝 plugin;不依賴本機外部 Link

接法 A:整個 Skills 目錄做 Junction

適用條件:.claude\skills 尚不存在,且不需要 Claude-only Skills。

PowerShell · 整個目錄的 Junction
$repoRoot = (Resolve-Path ".").Path
$source = Join-Path $repoRoot ".agents\skills"
$claudeDir = Join-Path $repoRoot ".claude"
$target = Join-Path $claudeDir "skills"

if (-not (Test-Path -LiteralPath $source)) {
    throw "找不到來源:$source"
}

New-Item -ItemType Directory -Path $claudeDir -Force | Out-Null

if (Test-Path -LiteralPath $target) {
    throw "目標已存在,未建立 Link:$target"
}

New-Item `
    -ItemType Junction `
    -Path $target `
    -Target (Resolve-Path $source) |
    Select-Object FullName, LinkType, Target

看到這些才算建立成功:.claude\skills → .agents\skills, 輸出同時顯示 LinkType = Junction 與正確 target。

規則檔不必再複製一份

Hard Link

只適合固定 Windows/NTFS、同一 volume,而且兩個檔名永遠必須同內容的環境。

方案 A:薄 CLAUDE.md import

CLAUDE.md
@AGENTS.md

## Claude Code

- 把只適用 Claude Code 的補充規則放在這裡。

方案 B:以 Hard Link 共用規則檔

PowerShell · AGENTS.md and CLAUDE.md hard link
$repoRoot = (Resolve-Path ".").Path
$agentsFile = Join-Path $repoRoot "AGENTS.md"
$claudeFile = Join-Path $repoRoot "CLAUDE.md"

if (-not (Test-Path -LiteralPath $agentsFile)) {
    throw "找不到 AGENTS.md 來源檔"
}

if (Test-Path -LiteralPath $claudeFile) {
    throw "CLAUDE.md 已存在,未建立 Hard Link"
}

New-Item `
    -ItemType HardLink `
    -Path $claudeFile `
    -Target $agentsFile

Hard Link 建立後,兩個檔名指向同一個檔案實體。從 CLAUDE.md 編輯也會改到 AGENTS.md;需要 Claude-only 內容時改用 import。

驗收要看 Link 身分,也要看內容

1. Link 型態與 target

PowerShell · 檢查 Junction / Symbolic Link
Get-Item -LiteralPath ".claude\skills\my-skill" -Force |
    Select-Object FullName, Attributes, LinkType, Target

預期 Attributes 包含 ReparsePointLinkTypeJunctionSymbolicLink,且 Target 指向預期來源。

2. Entrypoint 與 SHA-256

PowerShell · 檢查 SKILL.md 與內容 hash
Test-Path -LiteralPath ".claude\skills\my-skill\SKILL.md"

Get-FileHash ".agents\skills\my-skill\SKILL.md"
Get-FileHash ".claude\skills\my-skill\SKILL.md"

Test-Path 應回傳 True,兩個 SHA-256 應一致。

3. Hard Link 身分

Windows · 列出 Hard Link set
fsutil hardlink list ".\AGENTS.md"

預期清單同時包含 AGENTS.mdCLAUDE.md。內容相同的兩份副本不能當成 Hard Link 證據。

回到 Claude Code 做最後三項驗收

PowerShell · 記錄 Claude Code 版本
claude --version
  1. 如果 session 啟動時還沒有頂層 .claude\skills,建立後重啟 Claude Code。
  2. 輸入 /my-skill,確認 slash autocomplete 能看到該 Skill。
  3. 用符合 frontmatter description 的自然語言任務,確認 auto-invocation。
  4. 執行 /context,確認 Memory files 已載入 CLAUDE.md

一般 Read tool 能看到檔案,只能證明檔案可讀。slash autocomplete、 auto-invocation 與 /context 都通過,才能證明原生 discovery 已接上。

執行 Link bootstrap 前,先看它會動哪些檔案

團隊 bootstrap 可能同時處理 metadata、wrapper、專案根目錄標記與清理流程。 如果目的只是讓 Claude Code 看見一個 Skill,手動建立單一 Link 比直接執行整支腳本容易控制。

程式行為 風險 執行前檢查
任何 Reparse Point 都視為已存在 未核對正確 target 檢查 Get-Item ... Target
實體 .claude\skills 直接跳過 雙份 Skill 繼續漂移 分類 portable 與 Claude-only Skill
規則檔內容相同就顯示 Hard Link 已存在 相同副本被誤認為同一檔案 執行 fsutil hardlink list
規則內容不同時改名備份再建立 Link 目前專案的檔案配置會改變 review 差異與備份名稱
清理腳本對一般規則檔直接移除 檔案脫離 Hard Link 後仍可能被刪 核對 Hard Link set 並另留備份

AST parse 只能證明 PowerShell 語法可解析,不能證明檔案變更符合預期。 在隔離的測試專案完成建立與清理流程前,不要把語法檢查當成安全驗收。

卡住時,從這裡查

狀況 可能原因 處理順序
.claude\skills 已是實體目錄 過去人工複製,或內含 Claude-only Skill 比較差異後採逐 Skill Link
Claude 看不到 Skill 路徑、檔名、description、target 或 session 時機錯誤 依序檢查 .claude/skills/<name>/SKILL.mdTest-Path 與重啟
Symbolic Link 權限不足 未開 Developer Mode,shell 也沒有 symlink 權限 在 Windows 本機改用 Junction,或調整權限後重試
Hard Link 建立失敗 AGENTS.mdCLAUDE.md 不在同一 volume 改用 @AGENTS.md import
修改 CLAUDE.mdAGENTS.md 也變了 這是 Hard Link 的正常行為 需要 Claude-only 內容時改採 import loader
本機可用,Cloud session 看不到 外部 Junction 不會隨 clone 出現 提交 project Skill、使用 bootstrap 或 plugin

交付前逐項確認

  • .agents/skills/<name>/SKILL.md 是可攜 Skill 的唯一來源。
  • .claude/skills/<name>LinkTypeTarget 正確。
  • Claude path 的 SKILL.md 可讀,SHA-256 與來源一致。
  • /my-skill 出現在 slash autocomplete。
  • 自然語言任務能觸發該 Skill。
  • /context 顯示 CLAUDE.md
  • 使用 Hard Link 時,fsutil hardlink list 同時列出兩個檔名。
  • bootstrap 或團隊文件已記錄 clone 後如何重建 Link。

資料來源與驗證邊界

本文只使用以專案根目錄為基準的相對路徑與中性名稱,不記錄作者本機的 使用者名稱、磁碟位置、私人 repository 名稱或實際檔案數量。命令未在讀者環境 執行;實際變更前,請先在可還原的測試專案驗證。