跳至主要內容
EMil Wu
EN

#14

實戰 Tips 2:把你的 Skill 打包成 Plugin 發布

Practical Tips 2: Packaging Your Skills as Plugins for Distribution

實戰心得 10 分鐘閱讀
把 Skill 打包成 Plugin:從拼圖到禮盒的封裝過程 把 Skill 打包成 Plugin:從拼圖到禮盒的封裝過程
把你的 Skill 打包成 Plugin,讓任何人都能一行指令安裝

上一篇我們聊了 Agent 的「已知」陷阱,這篇來談另一個實戰場景:你寫了一個好用的 Skill 或 Command,想分享給同事、社群、甚至自己的另一台電腦,該怎麼做?

前幾天 Claude Code 的原始碼外洩,我從裡面拆解了 /insights 指令重新包裝成 Skill 和 Command [1],做完之後自然想:這能不能讓別人也能一鍵安裝?

答案是可以的,Claude Code 有完整的 Plugin 系統 [3],但從「本地能跑」到「別人也能裝」,中間的路比想像中多彎。


Plugin 是什麼?跟 Skill 差在哪?

先釐清一個常見的混淆:Skill 是能力,Plugin 是封裝。

Skill 放在 .claude/skills/ 目錄下,只有你自己的專案能用,Command 放在 ~/.claude/commands/,你自己的所有專案能用,但不管是哪一種,你都沒辦法輕鬆地讓另一個人「安裝」它。

Plugin 是 Claude Code 的標準封裝格式,它把 Skill、Command、Agent、Hook 打包在一起,加上 manifest(plugin.json)和安裝邏輯,讓使用者可以用一行指令安裝。

Skill / CommandPlugin
作用範圍單一專案或個人全域任何人可安裝
安裝方式手動複製檔案/plugin install 一行搞定
環境設定寫死在檔案裡userConfig + 模板自動引導
版本管理沒有plugin.json 內建版號
分享機制複製貼上Plugin Marketplace(GitHub repo)

打包流程:四個 Phase

四個 Phase:分析依賴 → 設計結構 → 建置改寫 → 驗證 四個 Phase:分析依賴 → 設計結構 → 建置改寫 → 驗證
打包流程四階段:Analyze → Design → Build → Verify

把一個 Skill 打包成 Plugin,不是把檔案換個目錄就好,以下是我實際走過的完整流程。

Phase 1 — 分析依賴

在動手之前,先搞清楚你的 Skill 依賴了什麼,依賴可以分成幾類:

分類說明處理方式
CORESkill 本體的邏輯和檔案直接打包
CONTEXT環境相關(專案名、API key、路徑)轉成模板或 userConfig
FRAMEWORKClaude Code 本身提供的能力(tool、sub-agent)不需處理,runtime 自帶
EXTERNAL外部工具(Node.js、git、gh CLI)在 README 標示需求
PHANTOM程式碼裡引用但實際不存在的依賴移除

最容易被忽略的是 CONTEXT 依賴,也就是你的 Skill 裡寫死了哪些只有在你的環境才成立的東西?

CONTEXT 又可以細分:

  • CONTEXT-SIMPLE:1-3 個簡單的 key-value(例如專案名稱、預設天數)→ 放進 plugin.jsonuserConfig
  • CONTEXT-COMPLEX:結構化或重複性的資料(例如多個 channel 的設定)→ 做成模板檔案,用 SessionStart hook 引導使用者填入

Phase 2 — 設計結構

Plugin 的目錄結構長這樣:

my-plugin/
├── .claude-plugin/
│   └── plugin.json          ← manifest(名稱、版本、設定項)
├── skills/                  ← Skill 檔案
│   └── my-skill/
│       └── SKILL.md
├── commands/                ← Command 檔案
│   └── my-command.md
├── hooks/
│   └── hooks.json           ← 自動化 hook(可選)
├── scripts/                 ← 輔助腳本(可選)
└── README.md

plugin.json 是核心,定義了 Plugin 的 metadata 和使用者需要填的設定項:

{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "做某件事的 Plugin",
  "author": "your-name",
  "userConfig": {
    "PROJECT_NAME": {
      "description": "預設分析的專案名稱",
      "required": false,
      "default": ""
    }
  }
}

Phase 3 — 建置與路徑改寫

這一步最容易出錯,Plugin 安裝後的運行環境跟你本地開發時不同,所有路徑都要改寫:

  • Skill 裡引用的檔案路徑 → 改用 ${CLAUDE_PLUGIN_ROOT} 作為基底
  • 需要寫入的資料路徑 → 改用 ${CLAUDE_PLUGIN_DATA}(Plugin 專屬的可寫目錄)
  • 絕對路徑 → 全部消除,改成相對路徑或變數
  • ../ 路徑穿越 → 不允許,Plugin 不能存取自己目錄以外的東西

如果你的 Skill 需要在第一次執行時做初始化(例如 npm install、編譯 TypeScript),用 SessionStart hook 處理:

{
  "hooks": {
    "SessionStart": [{
      "type": "command",
      "command": "cd ${CLAUDE_PLUGIN_ROOT}/scripts && npm install --silent && npx tsc"
    }]
  }
}

Phase 4 — 驗證

這是整個流程中最重要也最容易被跳過的一步,不要自己驗證自己的 Plugin,而是用一個全新的 Sub-agent,假裝自己是第一次看到這個 Plugin 的使用者,從頭模擬安裝流程。

驗證要抓的問題分三級:

BLOCKER(必須修):

  • 缺少必要檔案(plugin.json、SKILL.md)
  • 路徑引用到不存在的檔案
  • 絕對路徑或路徑穿越
  • CONTEXT 變數沒有替換成模板

WARNING(應該修):

  • 寫死的名稱或路徑(hardcoded)
  • 語言鎖定(只寫了中文說明沒有英文)
  • README 缺少安裝或使用步驟

NITPICK(可以改):

  • 風格不一致
  • 多餘的檔案

驗證跑最多三輪:第一輪找問題、修完再跑第二輪、直到乾淨或三輪用完。


發布到 Marketplace

Plugin 寫好了,怎麼讓別人安裝?

單一 Plugin Repo

最簡單的方式:把 Plugin 推到一個 GitHub repo,repo 根目錄就是 Plugin 的根目錄,使用者直接安裝:

/plugin install github:your-name/your-plugin

統一 Marketplace(推薦)

如果你有多個 Plugin,每個都開一個 repo 會讓使用者需要記住多個來源,更好的做法是建一個 統一 Marketplace repo

emilwu-plugins/
├── .claude-plugin/
│   └── marketplace.json     ← 列出所有 plugin
└── plugins/
    ├── plugin-a/
    │   └── .claude-plugin/plugin.json
    ├── plugin-b/
    │   └── .claude-plugin/plugin.json
    └── plugin-c/
        └── .claude-plugin/plugin.json

使用者只需要加入一次 Marketplace,之後所有 Plugin 都可以安裝:

# 一次性加入(每台電腦做一次)
/plugin marketplace add your-name/your-marketplace

# 安裝任何 Plugin
/plugin install plugin-a@your-marketplace
/plugin install plugin-b@your-marketplace

踩過的坑

打包過程中踩到的各種坑 打包過程中踩到的各種坑
本地跑得好好的,安裝到別台電腦就全部壞掉 — 歡迎來到打包的世界

為了避免重複程式碼,我最初用 symlink 讓兩個 Plugin 共用同一份 scripts/ 目錄。本地測試完美通過。

但 Claude Code 的 plugin cache 機制在複製 Plugin 時不會 follow symlink,而是複製一個空連結,導致安裝後找不到腳本。

解法: 放棄 symlink,實體複製,每個 Plugin 各自包含完整的 scripts,有點浪費空間,但至少不會壞。

2. Marketplace 名稱 ≠ GitHub Repo 名稱

Plugin 安裝格式是 plugin-name@marketplace-name,這裡的 marketplace-name 不是 GitHub repo 名稱,而是 marketplace.json 裡的 "name" 欄位,搞混了會得到 Plugin not found in any marketplace,而且錯誤訊息不會告訴你是哪個名字不對。

3. 每台電腦都要 Add Marketplace

/plugin marketplace add 是本地操作,不會跨裝置同步,這不是 bug,但第一次在第二台電腦上安裝時一定會被困住。

4. 從分散到統一的演進

一開始我每個 Plugin 各開一個 repo,裝第一個還好,裝到第三個的時候,使用者要記住三個不同的 repo URL。

後來全部搬到一個統一的 marketplace repo,使用者只需要 add 一次,未來新增任何 Plugin 都自動可用。如果你打算做超過一個 Plugin,一開始就用統一 Marketplace。

5. SessionStart Hook 的首次執行陷阱

SessionStart hook 在每次 Claude Code 啟動時都會執行,如果你的 hook 會做 npm installtsc,每次啟動都會多等幾秒。

解法: 在 hook script 裡加判斷,如果 node_modules/ 已經存在就跳過安裝,如果 .js 輸出檔已經存在就跳過編譯,只在第一次(或手動刪除後)才執行完整初始化。


實戰案例:/insights Plugin

上面說的所有流程,我都在打包 /insights 的時候實際走過一遍,最終產出了兩個 Plugin:

  • claude-insights-command — 跨專案分析版,支援 /insights emilwu-tw-site 7d
  • claude-insights-skill — 當前專案分析版,支援 /insights 7d

拆解原始碼和重新設計架構的過程寫在另一篇:從外洩原始碼拆解 Claude Code /insights [1]。

安裝方式:

/plugin marketplace add emilwu/emilwu-plugins
/plugin install claude-insights-command@emilwu-plugins
/plugin install claude-insights-skill@emilwu-plugins

Plugin 原始碼和 Marketplace 可以在 GitHub: emilwu-plugins [2] 找到。


我的觀察

或許 Plugin 系統最大的價值不在於「分享」,畢竟大部分人寫的 Skill 是為了解決自己的特定問題,不一定有通用性。

真正的價值在於「可攜性」。

當你把一個 Skill 打包成 Plugin,你被迫要做一件事:把所有環境依賴抽出來,把所有寫死的路徑變數化,把所有「只有我知道」的前提變成明確的設定項,這個過程本身就是一次 Context Engineering 的實踐,也就是說你在幫你的 Skill 做你應該幫你的 Agent 做的事:把隱性知識顯性化。

做完之後,不只是別人能裝,你自己換一台電腦、換一個專案,也能直接用。

或許這才是打包最值得做的原因:不是為了分享,而是為了讓你的工具真正屬於你,而不是綁定在某一台機器的某一個目錄裡。


參考資料:

[1] 從外洩原始碼拆解 Claude Code /insights — 實際拆解案例 /resources/claude-code-source-insights

[2] GitHub: emilwu-plugins — 統一 Plugin Marketplace https://github.com/emilwu/emilwu-plugins

[3] Claude Code Plugins — 官方文件 — Plugin 系統完整說明 https://code.claude.com/docs/en/plugins

支持這個系列

如果這系列文章對你有幫助,考慮請我喝杯咖啡