Claude Code Skill 怎麼建立封面:SKILL.md、觸發描述與分層參考檔

Claude Code 的 skill 是一個資料夾,裡面的 SKILL.md 寫著某一類工作的做法。開頭的 description 說明它做什麼、什麼時候用,平常只有這段描述留在 context。被叫用時才載入全文,更細的資料拆成參考檔,需要時才讀。

這篇用假案例示範:假設你在做一個記帳網站,叫做豪享記帳,前端用 Next.js、後端用 Go。內容包括 SKILL.md 的位置與欄位、觸發描述的寫法、參考檔與腳本,以及舊的自訂欄位怎麼改成官方寫法。還不熟 CLAUDE.md 的話,可以先讀上一章 CLAUDE.md 與記憶。

Claude Code 的 skill 是什麼?什麼時候該寫一個?

skill 是一份寫給 Claude 的工作手冊。Claude 判斷用得到時會自己載入,你也可以在訊息開頭輸入 /名稱 直接叫用。

官方文件建議兩種情況寫成 skill:同一段指示、檢查清單或多步驟流程,一直重複貼進對話;或是 CLAUDE.md 裡的某一段,已經從「事實」長成「流程」。

skill 的內文只在用到時才載入,所以很長的參考資料,平常幾乎不佔 context。以豪享記帳來說,下面這些工作都適合寫成 skill:

  • 新增後端 API:寫規格、handler、單元測試的固定順序。
  • 修改資料表:migration 的命名、回滾與備份步驟。
  • 發版前檢查:跑測試、整理變更、寫更新紀錄。
  • API 慣例:金額用整數「分」儲存,錯誤格式與分頁怎麼寫。

SKILL.md 要放在哪裡?

每個 skill 是一個資料夾,裡面一定要有 SKILL.md。資料夾放在哪一層,決定這個 skill 在哪些專案可以用。

skill 的存放位置

層級路徑適用範圍
個人~/.claude/skills/<名稱>/SKILL.md你在這台電腦的所有專案
專案.claude/skills/<名稱>/SKILL.md這個 repo,可以提交給團隊共用
外掛<外掛>/skills/<名稱>/SKILL.md啟用外掛的地方,用 /外掛名:技能名 叫用
企業組織的 managed 設定目錄組織內的所有使用者

資料來源:Claude Code 官方文件 Skills

同名時,企業層級優先於個人,個人優先於專案。外掛的 skill 有自己的命名空間,不會和其他 skill 撞名。

豪享記帳的規則跟著程式碼走,所以放在專案的 .claude/skills/,和程式碼一起用 git 管理:

text
hao-ledger/
├── .claude/
│   └── skills/
│       ├── add-endpoint/
│       │   └── SKILL.md
│       └── release-check/
│           └── SKILL.md
├── api/            # Go 後端
└── web/            # Next.js 前端

在對話中修改 SKILL.md 會立刻生效,不用重開 Claude Code。如果對話開始時還沒有 skills 資料夾,中途才新建,就要執行 /reload-skills。

SKILL.md 的 frontmatter 有哪些欄位?

frontmatter 是檔案最上面、兩條 --- 之間的 YAML。第一行一定要是 ---,Claude Code 才會解析;不然整份檔案都會被當成內文。

所有欄位都是選填,官方只建議一定要寫 description。欄位名稱要和文件完全一致,連字號也不能少;不認得的欄位會被直接忽略,不會報錯。

先看一個最小的例子:

markdown
---
name: add-endpoint
description: 在豪享記帳的 Go 後端新增 API,包含規格、handler 與單元測試。使用者要新增 API 或修改回應欄位時使用。
---
# 新增後端 API
照下面的順序做,前一步完成才做下一步:
1. 先在 `api/openapi.yaml` 寫好規格,使用者確認後才寫程式。
2. handler 放 `internal/handler/`,商業邏輯放 `internal/service/`。
3. 金額一律用整數「分」儲存,不用浮點數。
4. 每支 API 都要有單元測試,改完執行 `go test ./...`。

常用的 frontmatter 欄位

欄位用途豪享記帳的例子
name/ 選單顯示的名稱,省略時用資料夾名稱add-endpoint
description做什麼、什麼時候用;Claude 靠它決定要不要載入新增 Go 後端 API
when_to_use補充觸發情境,例如使用者會說的話「交易列表要能依分類篩選」
argument-hint輸入指令時,提示要帶什麼參數[資源名稱]
disable-model-invocation設成 true,只有你能叫用發版前檢查
user-invocable設成 false,只有 Claude 能叫用API 慣例
allowed-tools叫用的那一輪,列出的工具免詢問Bash(go test *)
paths只在處理符合的檔案時自動載入api/**/*.go
context、agent設 context: fork,交給子代理執行大範圍搜尋程式碼
model、effort這個 skill 執行時用的模型與思考強度複雜重構用較高強度

資料來源:Claude Code 官方文件 Skills

description 加上 when_to_use,在清單裡最多顯示 1,536 個字元。超過的部分會被截掉。

誰可以叫用 skill?disable-model-invocation 與 user-invocable

預設是你和 Claude 都能叫用。兩個欄位可以改變這件事:

叫用權限怎麼設

設定你能叫用Claude 能叫用描述放進 context
預設可以可以是
disable-model-invocation: true可以不行否
user-invocable: false不行可以是

資料來源:Claude Code 官方文件 Skills

會動到正式環境的流程,例如豪享記帳的發版,適合設成只有你能叫用。加上 disable-model-invocation: true 後,Claude 不會自己執行它。發版檢查的完整範例放在後面的腳本段落。

背景知識則相反,例如豪享記帳的 API 慣例。設成 user-invocable: false 後,它不會出現在 / 選單,Claude 仍會在需要時載入。

yaml
---
name: api-conventions
description: 豪享記帳的 API 慣例:金額單位、錯誤格式、分頁與日期格式。撰寫或審查 API 時使用。
user-invocable: false
---

手動叫用時,/名稱 要放在訊息開頭才會直接執行。寫在句子中間,只代表你允許 Claude 自己判斷要不要執行。

description 與 when_to_use 怎麼寫,Claude 才會叫到?

Claude 每一輪都看得到 skill 清單,裡面有每個 skill 的名稱和描述。它靠描述判斷要不要載入,所以描述寫得準不準,決定 skill 會不會被叫到。

寫描述時把握四件事:

  • 第一句寫主要用途:描述太長會被截斷,最重要的用途放在最前面。
  • 做什麼和什麼時候用都要寫:官方撰寫指南要求兩者都寫,並用第三人稱。不要寫「我可以幫你…」「你可以用它…」。
  • 放使用者會說的詞:官方排查「skill 沒被叫到」的第一步,就是檢查描述裡有沒有使用者自然會說的關鍵字。
  • 寫清楚範圍:太常被誤叫,就把描述寫得更具體;完全不想被自動叫用,就設 disable-model-invocation: true。

觸發描述的改寫對照

模糊的寫法具體的寫法
後端相關工作在豪享記帳的 Go 後端新增 API,包含規格、handler 與單元測試
我可以幫你寫測試為豪享記帳的 Go 服務撰寫表格驅動測試。使用者要補測試或修失敗的測試時使用
處理資料庫新增或修改豪享記帳的資料表 migration,包含命名、回滾與備份步驟

when_to_use 用來補觸發情境,例如使用者會說的話或範例需求。它會接在 description 後面一起顯示,也算進 1,536 字元的上限。

yaml
---
name: add-endpoint
description: 在豪享記帳的 Go 後端新增 API,包含規格、handler 與單元測試。
when_to_use: 使用者說「加一支 API」「交易列表要能依分類篩選」「要能匯出 CSV」時使用。只改前端畫面時不用。
---

paths 可以再縮小自動載入的範圍。例如豪享記帳的 Go 程式風格 skill,可以設成 paths: "api/**/*.go"。這樣只有在處理後端的 Go 檔時,Claude 才會自動載入它。

skill 太多時,描述會被拿掉

skill 清單有總預算,約為模型 context 視窗的 1%。超過時,Claude Code 會從最少用的 skill 開始拿掉描述,只留名稱。

描述被拿掉的 skill,Claude 很難比對到你的需求。執行 /skill-doctor 可以看每個 skill 佔多少 context、多常被用到,再把用不到的關掉。

SKILL.md 太長怎麼辦?把細節拆成參考檔

官方建議 SKILL.md 控制在 500 行以內,詳細資料移到同一個資料夾的其他檔案。原因是 skill 被叫用後,內容會留在對話裡,之後每一輪都佔用 context;參考檔則是 Claude 讀了才算。

skill 分層載入示意圖:描述常駐、叫用時載入 SKILL.md、需要時讀參考檔、腳本只回傳輸出 skill 分四層載入:越下層,越晚才用到

以豪享記帳的新增 API 為例,資料夾可以這樣分:

text
add-endpoint/
├── SKILL.md              # 流程與鐵則,叫用時載入
├── references/
│   ├── errors.md         # 錯誤碼與錯誤訊息格式
│   ├── migration.md      # 資料表 migration 規則
│   └── testing.md        # 測試資料與 mock 的寫法
└── scripts/
    └── new_migration.sh  # 建立 migration 檔,只執行不載入

SKILL.md 裡要寫明每個參考檔裝什麼、什麼時候讀:

markdown
## 參考檔(需要時再讀)
| 檔案 | 什麼時候讀 |
|---|---|
| [references/errors.md](references/errors.md) | 要回傳錯誤時:錯誤碼與訊息格式 |
| [references/migration.md](references/migration.md) | 要新增或修改資料表時 |
| [references/testing.md](references/testing.md) | 寫單元測試時:測試資料與 mock |

參考檔怎麼拆,Claude 才讀得到?

  • 從 SKILL.md 直接連過去:參考檔都從 SKILL.md 連,只分一層。官方撰寫指南提到,參考檔再連參考檔時,Claude 可能只預覽檔案的前幾行。
  • 長的參考檔加目錄:超過 100 行的參考檔,在最上面放一段目錄。Claude 只讀一部分時,也看得到整份檔案有哪些內容。
  • 檔名看得出內容:用 migration.md、testing.md,不要用 doc2.md。路徑一律用斜線 /,不用反斜線。
  • 資料夾名稱自己定:references/ 只是慣例。官方範例也直接用 reference.md、examples.md。

重要的規則放在 SKILL.md 前段。對話被自動壓縮後,每個 skill 只保留前 5,000 個 token。所有 skill 合計的上限是 25,000 個 token。

我的做法:流程比較長的 skill,SKILL.md 只放步驟和一定要守的規則。各階段的細節放在 references/,再用一張表寫清楚每個檔案什麼時候讀。

skill 裡的腳本和參數怎麼用?

固定、容易出錯的步驟,寫成腳本放在 scripts/。腳本是被執行,內容不會載入 context,只有執行結果會佔用 context。

${CLAUDE_SKILL_DIR} 會換成 skill 自己的資料夾路徑。在 SKILL.md 裡用它指到腳本,從哪個目錄啟動都找得到:

markdown
---
name: add-endpoint
description: 在豪享記帳的 Go 後端新增 API,包含規格、handler 與單元測試。
argument-hint: "[資源名稱]"
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/new_migration.sh *) Bash(go test *)
---
這次要新增的資源:$ARGUMENTS
## 新增 migration
執行 `${CLAUDE_SKILL_DIR}/scripts/new_migration.sh <名稱>` 建立檔案,不要手動建檔。
  • $ARGUMENTS:換成指令後面打的所有文字。輸入 /add-endpoint budgets,$ARGUMENTS 就是 budgets。
  • $0、$1:依序取第 1 個、第 2 個參數,編號從 0 開始。
  • 沒放參數位置時:Claude Code 會把參數接在內文最後,寫成 ARGUMENTS: <值>。
  • allowed-tools:叫用的那一輪,列出的工具不用逐次詢問;你送出下一則訊息後就失效。

說明腳本時,寫清楚是「執行」還是「閱讀」。例如「執行 new_migration.sh 建立檔案」,Claude 會直接跑;「參考它的命名規則」,Claude 會打開來讀。

注意:專案裡的 skill 即使在沒有信任過的資料夾,allowed-tools 也會生效。拿到別人的 repo,執行 Claude Code 前先看裡面的 skill 允許了哪些工具。

先跑指令,再把結果交給 Claude

skill 內文裡,行首或空白後面接驚嘆號與反引號包住的指令,會在送給 Claude 之前先執行。指令的輸出會取代那一行,Claude 拿到的是實際資料:

markdown
---
name: release-check
description: 豪享記帳發版前的檢查:跑前後端測試、整理變更、寫更新紀錄草稿。
disable-model-invocation: true
allowed-tools: Bash(git log *) Bash(go test *) Bash(npm test)
---
## 最近 20 筆提交
!`git log --oneline -20`
## 檢查步驟
1. 執行 `go test ./...` 與 `npm test`,有失敗就停下來回報。
2. 依上面的提交,寫出這一版的更新紀錄草稿。
3. 列出改到資料表的提交,提醒使用者先備份。

注入的指令不會跳出確認,Claude Code 會先比對權限規則。除了 auto 模式,沒有被允許的指令會讓這次叫用中止。所以範例把 git log 寫進 allowed-tools。

指令執行失敗時,整個叫用也會中止,Claude 完全看不到這份 skill 的內容。

skill、CLAUDE.md 與 slash command 怎麼分工?

CLAUDE.md 放每次都要知道的事,skill 放某一類工作的做法,一定要執行的規則交給 hook。

CLAUDE.md、skill、自訂指令與 hook 的分工

放哪裡適合寫什麼什麼時候生效豪享記帳的例子
CLAUDE.md每次都要知道的事實與慣例對話開始時載入測試指令、前端在 web/、後端在 api/
skill某一類工作的流程與檢查清單用到時才載入新增 API、發版前檢查
.claude/commands/舊的自訂指令,仍然可以用輸入指令時已經寫好的 /deploy
hook每次都必須執行的規則事件發生時,由 Claude Code 執行改完 Go 檔自動跑 gofmt

資料來源:Claude Code 官方文件 Skills、Memory、Hooks

自訂指令已經併入 skill。例如 .claude/commands/deploy.md 會產生 /deploy 指令。改成 .claude/skills/deploy/SKILL.md 也一樣;兩個同時存在時,以 skill 為準。

skill 多了資料夾、參考檔與叫用控制,Claude 也能依情境自動載入,所以新的指令可以直接寫成 skill。

skill 是給 Claude 的指示,Claude 不一定每次都照做。官方建議,每次都必須成立的規則改用 hook;想讓 hook 跟著 skill,可以寫在 skill 的 hooks 欄位,從叫用起到對話結束都有效。hook 的寫法見 Claude Code Hooks 與權限。

舊的自訂欄位怎麼改成官方寫法?

我回頭對照官方文件,檢查自己寫過的 skill,發現兩個問題。這兩個問題平常都不會跳出錯誤,skill 看起來正常,觸發描述卻沒有被讀到。

  1. 自訂欄位不會被讀:Claude Code 不認得的欄位會直接忽略。寫在 applies_when、skip_when 裡的觸發情境,從來沒有參與判斷。
  2. 描述用方括號開頭,整段失效:像 description: [後端] 新增 API 這樣寫,YAML 會解析失敗。Claude Code 會用空的欄位載入 skill,/名稱 還能用,但 Claude 比對不到描述,不會自動叫用。

我自己就踩過這個坑。為了分類,我在描述開頭加了方括號標籤。結果這些 skill 在清單裡的描述,全都變成內文的第一行,Claude 也從來沒有自動叫用過。用 YAML 解析器檢查一次,才發現是解析失敗。

舊寫法

yaml
---
description: [後端] 新增 API 的流程
tier: sub
applies_when:
  - 要新增後端 API
  - 要改 API 回應欄位
skip_when:
  - 只改前端畫面
mandatory_checks:
  - 先寫 OpenAPI 規格
  - 改完跑 go test
---

新寫法

markdown
---
name: add-endpoint
description: 在豪享記帳的 Go 後端新增 API,包含規格、handler 與單元測試。
when_to_use: 使用者要新增 API 或修改 API 回應欄位時使用;只改前端畫面時不用。
---
# 新增後端 API
(流程步驟)
## 完成前檢查
- [ ] OpenAPI 規格已經寫好,使用者確認過
- [ ] `go test ./...` 全部通過

舊寫法改新寫法對照圖:自訂欄位改成 name、description、when_to_use 與內文檢查清單 自訂欄位逐項改成官方欄位

skill 欄位的舊寫法 → 新寫法

舊寫法問題新寫法
沒寫 name,資料夾用底線命名指令沿用資料夾名稱,可以用;但 Agent Skills 標準要求小寫英文、數字與連字號寫出 name,例如 add-endpoint
description: [後端] …YAML 解析失敗,所有欄位都沒被讀到拿掉方括號,或整段加上引號
applies_when 清單自訂欄位,被忽略改寫成 when_to_use 的一句話
skip_when 清單自訂欄位,被忽略範圍寫進描述,例如「只改前端畫面時不用」
mandatory_checks 清單自訂欄位,被忽略移到內文的檢查清單;每次都必須做的改用 hook
tier、type 分類自訂欄位,被忽略刪除;要給自己的工具讀,放進 metadata
內文寫「不要自動使用」只是文字,Claude 仍可能叫用加上 disable-model-invocation: true
停用的 skill 留在資料夾描述仍然佔用清單預算刪掉資料夾,或用 skillOverrides 設成 off

資料來源:Claude Code 官方文件 Skills、Skill authoring best practices

描述裡有半形冒號加空白,或用 [、{ 開頭時,整段用雙引號包起來最保險。

skillOverrides 寫在 settings.json,不用改 SKILL.md。可以設成 off 關掉,或設成 name-only 只留名稱、不放描述:

json
{
  "skillOverrides": {
    "old-report": "off",
    "ledger-history": "name-only"
  }
}

寫好的 skill 怎麼測試?

寫完 skill,照下面的順序驗一次:

  1. 確認清單裡有它:輸入 /skills 看清單,或直接問 Claude「有哪些 skill 可以用?」。
  2. 檢查 frontmatter:用下面的指令找出解析失敗的 SKILL.md,需要 v2.1.233 以上。
  3. 用平常的說法測觸發:不打斜線,直接說「交易列表要能依分類篩選」。再看 Claude 有沒有載入 add-endpoint。
  4. 調整描述:沒被叫到,就補上使用者會說的關鍵字;太常被叫到,就把描述寫得更具體。
  5. 換新對話實測:官方撰寫指南建議,開一個沒有前情的新對話做真實任務,觀察 Claude 有沒有讀對參考檔。
  6. 定期看用量:/skill-doctor 會列出每個 skill 佔用的 context 與使用頻率。這個指令需要 v2.1.252 以上。
bash
# 找出 frontmatter 解析失敗的 SKILL.md
claude plugin validate .claude/skills
# 用除錯模式啟動,看解析錯誤
claude --debug

常見問題

Q1:Claude Code 的 skill 一定要寫 name 嗎?

不一定,沒寫 name 時,指令名稱就是資料夾名稱。要上傳到 claude.ai 或 Skills API,就照 Agent Skills 標準寫出 name。名稱只用小寫英文、數字與連字號。

Q2:Claude 用了 skill,後面卻不照做怎麼辦?

先看是哪一種情況。每次都必須成立的規則,改用 hook;需要判斷的指引,寫成整個任務都適用的句子,例如「每次修改後都跑測試」。對話被壓縮過的話,再叫用一次 skill,就能補回完整內容。

Q3:allowed-tools 可以限制 skill 只用某些工具嗎?

不行,allowed-tools 是預先允許,不是限制。列出的工具在叫用那一輪不用逐次詢問,沒列出的工具照你的權限設定處理。要在 skill 執行期間拿掉某些工具,改用 disallowed-tools。

Q4:Claude Code 的 skill 可以拿到 claude.ai 用嗎?

可以,Claude Code 的 skill 依循 Agent Skills 開放標準。上傳到 claude.ai 或 Skills API 時,只接受標準裡的 6 個欄位。when_to_use、context 這類 Claude Code 專用的欄位,上傳會直接報錯。

延伸閱讀