feat(skills): 一次處理多個 CLI,並修掉闇影劍在 #30 找到的問題 #31

Merged
admin merged 2 commits from develop into master 2026-07-31 08:55:00 +00:00
2 changed files with 86 additions and 36 deletions
Showing only changes of commit 93757b3036 - Show all commits
+46 -20
View File
@@ -1,7 +1,7 @@
---
name: plugins-install
description: 一次把 JSC 的三個 pluginjsc-codejsc-docjsc-persona)安裝或更新到目前的 AI 助理,支援 Claude Code、Codex、GitHub Copilot CLI、Antigravity 四家原生 plugin CLIOpenCode 則以複製 skills 目錄處理;每個 plugin 先判斷已安裝或未安裝,未安裝就安裝、已安裝就更新到最新,最後以表格回報每個 plugin結果與版本。當使用者說要安裝所有 jsc plugin、一次更新全部 skill 套件、把 codedocpersona 都裝起來、換新機器要把 plugin 都補齊、或問怎麼一次更新所有 plugin 時觸發。不適用於:移除 plugin(用 /jsc-generic:plugins-uninstall)、只處理單一 plugin(直接照該 plugin README 的安裝章節)、安裝非 JSC 的第三方 plugin。
argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,doc,persona] [--host <gitea 主機>] [--clone-dir <目錄>] [--yes]"
description: 一次把 JSC 的三個 pluginjsc-codejsc-docjsc-persona)安裝或更新到一個或多個 AI 助理,可同時處理 Claude Code、Codex、GitHub Copilot CLI、Antigravity 四家原生 plugin CLIOpenCode複製 skills 目錄);沒指定時偵測本機裝了哪些 CLI 並讓使用者多選,每個 plugin 先判斷已安裝或未安裝,未安裝就安裝、已安裝就更新到最新,最後以「助理 × plugin」的表格回報結果與版本。當使用者說要安裝所有 jsc plugin、一次更新全部 skill 套件、把 codedocpersona 都裝起來、要同時更新好幾個 CLI、換新機器要把 plugin 都補齊、或問怎麼一次更新所有 plugin 時觸發。不適用於:移除 plugin(用 /jsc-generic:plugins-uninstall)、只處理單一 plugin(直接照該 plugin README 的安裝章節)、安裝非 JSC 的第三方 plugin。
argument-hint: "[--assistant <助理清單,逗號分隔,或 all>] [--plugins code,doc,persona] [--host <gitea 主機>] [--clone-dir <目錄>] [--yes]"
---
# plugins-install — 一次安裝/更新所有 JSC plugin
@@ -10,7 +10,7 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
| 階段 | 動作 |
| --- | --- |
| A. 前置設定 | 決定要操作哪助理(`--assistant`/偵測目前執行環境/詢問)→ 決定 gitea 主機 → 決定 plugin 清單(預設 code、doc、persona |
| A. 前置設定 | 決定要操作哪助理(`--assistant` 可帶多個或 all/偵測本機有哪些 CLI/多個就讓使用者多選)→ 決定 gitea 主機 → 決定 plugin 清單(預設 code、doc、persona |
| B. 現況盤點 | 對每個 plugin 查詢 marketplace 與 plugin 是否已存在,決定「安裝」或「更新」 |
| C. 安裝/更新 | 依助理的原生指令逐一執行;Antigravity 與 OpenCode 走 clone/複製路徑 |
| D. 回報 | 以表格列出每個 plugin 的動作、結果與版本,並提醒重啟工作階段 |
@@ -35,7 +35,7 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
## 參數
- `--assistant claude|codex|copilot|agy|opencode`:指定要操作的助理。**省略時**依階段 A1 判斷
- `--assistant <清單>`:要操作的助理,**可以多個**,以逗號分隔(`claude,codex,copilot,agy,opencode`),或用 `all` 代表本機找得到的全部。**省略時**依階段 A1 判斷(只有一個就直接用,多個就讓使用者多選)
- `--plugins code,doc,persona`:要處理的 plugin(以逗號分隔,用 repo 短名)。**省略時預設三個全做**。
- `--host <gitea 主機>`gitea 主機,省略時預設 `gitea.jsc.idv.tw`
- `--clone-dir <目錄>`AntigravityOpenCode 的本機 clone 根目錄,**省略時預設 `~/plugins`**。
@@ -65,14 +65,20 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
## 階段 A:前置設定
### A1. 決定助理
### A1. 決定助理(可以一次多個)
`--assistant` 收的是**清單**,不是單一值:`--assistant claude,codex,copilot`、或 `--assistant all`
最終得到的是一組助理,後面每個階段都對**這組的每一個**各跑一遍。
依序判斷,**第一個成立的就採用**:
1. 有帶 `--assistant`直接採用
2. 目前正在執行本 skill 的助理可自我辨識(例如 Claude Code`/plugin` 指令可用)→ 採用自己。
3. 逐一檢查哪些 CLI 存在(`command -v claude codex copilot agy opencode`)→ 只有一個就採用;**有多個就列出來問使用者**
4. 一個都沒有 → 回報「找不到任何支援的助理 CLI」並停止
1. 有帶 `--assistant`照它。`all` 代表「本機找得到的全部」(等同下面第 3 點的偵測結果)
2. 沒帶 → 逐一檢查哪些 CLI 存在(`command -v claude codex copilot agy opencode`):
- 找到 **1 個** → 直接用它
- 找到 **多個** → 列出來讓使用者**多選**(預設全選)。帶 `--yes` 時不問,直接全做
3. 一個都沒有 → 回報「找不到任何支援的助理 CLI」並停止。
> 目前正在執行本 skill 的那個助理,如果也在清單裡,**放到最後處理**——更新它自己會需要重啟工作階段。
### A2. 決定 gitea 主機與 clone 根目錄
@@ -95,9 +101,11 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
| Codex | `codex plugin marketplace list``codex plugin list` | 同上 |
| GitHub Copilot CLI | `copilot plugin marketplace list``copilot plugin list` | 同上 |
| Antigravity | `agy plugin list`,並看 `<clone-dir>/<repo>` 是否存在 | 目錄在且已安裝 → 更新;否則安裝 |
| OpenCode | 看 `~/.config/opencode/skills/` 底下是否已有該 plugin 的 skill 目錄 | 有 → 更新(覆蓋複製);沒有 → 安裝 |
| OpenCode | 看 `~/.config/opencode/skills/` 底下是否已有該 plugin 的 skill 目錄 | 有 → 更新(重新複製;注意不是覆蓋,見階段 C);沒有 → 安裝 |
指令不存在或子指令不被支援(舊版 CLI)時,**不要中斷整批**:記下該 plugin 為「跳過(CLI 不支援)」,繼續下一個,最後在階段 D 一起回報
盤點的迴圈是**助理 × plugin**:階段 A1 選定的每個助理,都要對每個 plugin 各判定一次,結果分開記
指令不存在或子指令不被支援(舊版 CLI)時,**不要中斷整批**:記下那一格為「跳過(CLI 不支援)」,繼續下一個,最後在階段 D 一起回報。同一個助理連續失敗(例如 CLI 存在但每個子指令都不支援)就整個助理標記為跳過,換下一個助理,不要卡住整批。
---
@@ -145,40 +153,58 @@ copilot plugin update <token>
> `agy plugin install <url>` 目前只支援 github.comgitea 一律走「clone + 本地路徑」。`agy` 沒有 update 子指令,更新=`git pull` 後重裝。
```bash
# 安裝
# 取得或更新本機 clone(目錄已存在就 pull,不要無條件 clone)
if [ -d "<clone-dir>/<repo>/.git" ]; then
git -C <clone-dir>/<repo> pull --ff-only
else
git clone <url> <clone-dir>/<repo>
fi
# 安裝
agy plugin install <clone-dir>/<repo>
# 更新
git -C <clone-dir>/<repo> pull --ff-only
# 更新agy 沒有 update 子指令,只能重裝)
agy plugin uninstall <plugin>
agy plugin install <clone-dir>/<repo>
```
- **不可無條件 `git clone`**:clone 目錄已經存在(很常見——開發者自己就 clone 在那裡)時,`git clone` 會以 `fatal: destination path already exists` 中止。階段 B 的判定只看「有沒有裝進 agy」,所以「目錄在、但 agy 沒裝」這個狀態會落進安裝分支,必須靠上面的 `if` 擋掉。
- `git pull --ff-only` 失敗(本機有未提交變更或分支分岔)→ **停在該 plugin**,回報現況讓使用者裁示,不得 `reset --hard``clean`,其餘 plugin 照常繼續。
- **先確認 clone 在哪個分支**:`git -C <clone-dir>/<repo> branch --show-current`。若不是發佈分支(`master`),代表要裝進去的是未合併的內容——先告訴使用者,由他決定要換分支還是照裝。預設的 `~/plugins` 很可能就是開發者自己的工作區。
### OpenCode
> OpenCode 的 plugin 是 npm 套件,不適用 skill 包;改用目錄安裝。**只會帶入 `skills/``scripts/` 與 `hooks/` 不會進去**——`jsc-persona` 依賴 `scripts/persona.mjs`,以此方式安裝等於只有說明書、沒有工具,安裝前要先告知使用者。
```bash
git clone <url> <clone-dir>/<repo> # 已存在則 git -C <clone-dir>/<repo> pull --ff-only
# 取得或更新本機 clone(同 Antigravity,不要無條件 clone
if [ -d "<clone-dir>/<repo>/.git" ]; then
git -C <clone-dir>/<repo> pull --ff-only
else
git clone <url> <clone-dir>/<repo>
fi
mkdir -p ~/.config/opencode/skills
cp -r <clone-dir>/<repo>/skills/* ~/.config/opencode/skills/
```
> **Windows PowerShell**`cp -r A B` → `Copy-Item A B -Recurse -Force`、`~` → `$HOME`。
- **`cp -r` 不是覆蓋,是合併**:同名檔案會更新,但**上游已經刪掉的檔案會原地留著**。所以 skill 改名或移除之後,OpenCode 端會同時留著新舊兩份。要乾淨更新就先刪該 plugin 帶入的目錄再複製一次(刪法見 `/jsc-generic:plugins-uninstall` 的 OpenCode 段)。回報時不要講「已覆蓋」,講「已複製,舊檔可能殘留」。
---
## 階段 D:回報
以表格回報,一個 plugin 一列:
以表格回報,**一個「助理 × plugin一列**
| plugin | 動作 | 結果 | 版本 |
| --- | --- | --- | --- |
| `jsc-code` | 安裝/更新/跳過 | ✅ 成功/⚠ 需處理/❌ 失敗 | 例 `0.0.2` |
| 助理 | plugin | 動作 | 結果 | 版本 |
| --- | --- | --- | --- | --- |
| Claude Code | `jsc-code` | 安裝/更新/跳過 | ✅ 成功/⚠ 需處理/❌ 失敗 | 例 `0.0.2` |
| Codex | `jsc-code` | 更新 | ✅ 成功 | 未知 |
- 版本取自該 plugin 的 `plugin.json`;拿不到就寫「未知」
只處理一個助理時可以省掉「助理」欄。**處理多個時一定要有**,否則使用者看不出哪一格出問題
- 版本取自該 plugin 的 `plugin.json`。**只有 Antigravity 與 OpenCode 拿得到**(它們有本機 clone 可讀);ClaudeCodexCopilot 把 plugin 放在各自 CLI 自管的快取目錄,除非該 CLI 的 `plugin list` 印得出版本,否則一律寫「未知」,不要去猜。
- 有任何一列不是 ✅ → 在表格下方逐項說明原因與建議動作。
- 最後固定提醒:**安裝或更新後要重啟工作階段**才會生效;`jsc-persona` 在 Claude Code 還要用 `/hooks` 確認六個 hook 都在。
+40 -16
View File
@@ -1,7 +1,7 @@
---
name: plugins-uninstall
description: 一次把 JSC 的四個 pluginjsc-codejsc-docjsc-personajsc-generic)從目前的 AI 助理移除,支援 Claude Code、Codex、GitHub Copilot CLI、Antigravity 四家原生 plugin CLIOpenCode刪除先前複製進去的 skill 目錄;移除順序固定把 jsc-generic 放到最後(本 skill 就住在裡面,移除後即失效),並在動手前列出將被移除的項目與會受影響的本機資料讓使用者確認。當使用者說要移除所有 jsc plugin、把 skill 套件整組解除安裝、清掉 codedocpersonageneric、重灌前先卸載、或問怎麼一次移除全部 plugin 時觸發。不適用於:安裝或更新(用 /jsc-generic:plugins-install)、只移除單一 plugin(直接照該 plugin README 的移除章節)、刪除人格資料或 Gitea 上的存取庫。
argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,doc,persona,generic] [--keep-marketplace] [--keep-clone] [--yes]"
description: 一次把 JSC 的四個 pluginjsc-codejsc-docjsc-personajsc-generic)從一個或多個 AI 助理移除,可同時處理 Claude Code、Codex、GitHub Copilot CLI、Antigravity 四家原生 plugin CLIOpenCode刪除先前複製進去的 skill 目錄);沒指定時偵測本機裝了哪些 CLI 並讓使用者多選;移除順序固定把 jsc-generic 放到最後(本 skill 就住在裡面,移除後即失效),並在動手前列出將被移除的項目與會受影響的本機資料讓使用者確認。當使用者說要移除所有 jsc plugin、把 skill 套件整組解除安裝、清掉 codedocpersonageneric、重灌前先卸載、要同時從好幾個 CLI 移除、或問怎麼一次移除全部 plugin 時觸發。不適用於:安裝或更新(用 /jsc-generic:plugins-install)、只移除單一 plugin(直接照該 plugin README 的移除章節)、刪除人格資料或 Gitea 上的存取庫。
argument-hint: "[--assistant <助理清單,逗號分隔,或 all>] [--plugins code,doc,persona,generic] [--keep-marketplace] [--keep-clone] [--yes]"
---
# plugins-uninstall — 一次移除所有 JSC plugin
@@ -10,7 +10,7 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
| 階段 | 動作 |
| --- | --- |
| A. 前置設定 | 決定要操作哪助理(`--assistant`/偵測目前執行環境/詢問)→ 決定 plugin 清單(預設四個全移) |
| A. 前置設定 | 決定要操作哪助理(`--assistant` 可帶多個或 all/偵測本機有哪些 CLI/多個就讓使用者多選)→ 決定 plugin 清單(預設四個全移) |
| B. 盤點與確認 | 列出實際已安裝的項目、會一併移除的 marketplace 與本機 clone、以及**不會**被碰的資料,請使用者確認 |
| C. 移除 | 依 `code``doc``persona``generic` 的順序逐一移除 |
| D. 回報 | 表格回報每個 plugin 的結果,並列出刻意保留的殘留物 |
@@ -30,12 +30,13 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
- **移除是不可逆的動作**:階段 B 的確認是**必要決策**,除非帶 `--yes`,否則一定要問過才動手。
- **絕不刪除使用者資料**`~/.claude/personas/`(人格倉庫)、`~/.roles/``~/.memory/`(角色與記憶)一律不動,Gitea 上的存取庫也不動。要清這些請使用者自己來。
- **順序不可調換**`jsc-generic` 一定最後移除。它是本 skill 的所在地,移除後本 skill 隨之失效,後面的步驟會執行不到。
- **多個助理時的順序**:外層先跑完一個助理的四個 plugin,再換下一個助理。**目前正在執行本 skill 的那個助理排到最後**——不然它自己的 `jsc-generic` 一沒,剩下的助理就處理不到了。
---
## 參數
- `--assistant claude|codex|copilot|agy|opencode`:指定要操作的助理。**省略時**依階段 A1 判斷
- `--assistant <清單>`:要操作的助理,**可以多個**,以逗號分隔(`claude,codex,copilot,agy,opencode`),或用 `all` 代表本機找得到的全部。**省略時**依階段 A1 判斷(只有一個就直接用,多個就讓使用者多選,且不預設全選)
- `--plugins code,doc,persona,generic`:要移除的 plugin(以逗號分隔,用 repo 短名)。**省略時預設四個全移**。
- `--keep-marketplace`:只移除 plugin,保留 marketplace 登錄(之後要重裝比較快)。
- `--keep-clone`:保留 AntigravityOpenCode 用的本機 clone 目錄(**預設就是保留**,此旗標僅用於明示)。要刪除本機 clone 必須由使用者在階段 B 明確同意。
@@ -63,20 +64,27 @@ argument-hint: "[--assistant claude|codex|copilot|agy|opencode] [--plugins code,
| `persona` | `persona-anime``persona-chat``persona-create``persona-icon``persona-invite``persona-memory``persona-relation``persona-sleep``persona-status``persona-sync``persona-therapist``persona-transfer` |
| `generic` | `role``plugins-install``plugins-uninstall``spec-action-params``spec-doc-funcs-handoff``spec-dockerfile``spec-execution``spec-gitea``spec-git-safety``spec-output``spec-plugin-version``spec-project-board``spec-time-log` |
> OpenCode 是**逐一刪除上表的 skill 目錄**,不可用萬用字元一次掃掉整個 `skills/`——那裡可能還有別處裝進去的 skill
> 上表是**寫下來當天的快照**,plugin 之後新增 skill 它不會自己更新。所以 OpenCode 的刪除**優先從本機 clone 的 `skills/` 推導清單**(見階段 C 的 OpenCode 段),clone 不在時才退回這張表,並在回報裡註明「清單可能不完整」
> 兩種做法都**不可用萬用字元一次掃掉整個 `skills/`**——那裡可能還有別處裝進去的 skill。
---
## 階段 A:前置設定
### A1. 決定助理
### A1. 決定助理(可以一次多個)
`--assistant` 收的是**清單**,不是單一值:`--assistant claude,codex,copilot`、或 `--assistant all`
最終得到的是一組助理,階段 B 到 D 都對**這組的每一個**各跑一遍。
依序判斷,**第一個成立的就採用**:
1. 有帶 `--assistant`直接採用
2. 目前正在執行本 skill 的助理可自我辨識 → 採用自己。
3. 逐一檢查哪些 CLI 存在(`command -v claude codex copilot agy opencode`)→ 只有一個就採用;**有多個就列出來問使用者**
4. 一個都沒有 → 回報「找不到任何支援的助理 CLI」並停止
1. 有帶 `--assistant`照它。`all` 代表「本機找得到的全部」(等同下面第 2 點的偵測結果)
2. 沒帶 → 逐一檢查哪些 CLI 存在(`command -v claude codex copilot agy opencode`):
- 找到 **1 個** → 直接用它
- 找到 **多個** → 列出來讓使用者**多選**(移除是不可逆的,**預設不全選**,要他自己勾)
3. 一個都沒有 → 回報「找不到任何支援的助理 CLI」並停止。
> 目前正在執行本 skill 的那個助理,如果也在清單裡,**放到最後處理**——移掉它自己之後,本 skill 就不存在了。
### A2. 決定 plugin 清單
@@ -135,12 +143,25 @@ agy plugin uninstall <plugin>
### OpenCode
逐一刪除該 plugin 帶入的 skill 目錄(照上表,不用萬用字元)
逐一刪除該 plugin 帶入的 skill 目錄。**最可靠的做法是從本機 clone 推導清單**,而不是照抄上表——上表是快照,plugin 新增 skill 之後就會漏
```bash
rm -rf ~/.config/opencode/skills/{<該 plugin 的 skill 目錄,以逗號分隔>}
# clone 還在:從來源目錄推導要刪哪些(唯一不會漏的做法)
for s in <clone-dir>/<repo>/skills/*/; do
rm -rf "$HOME/.config/opencode/skills/$(basename "$s")"
done
```
clone 已經不在時才退回上表,且**一個一行分開刪**:
```bash
rm -rf ~/.config/opencode/skills/docker
rm -rf ~/.config/opencode/skills/funcs
# …照上表逐行
```
> **不要用 `{a,b,c}` 這種 brace expansion**。它有三種會靜默失效的情況:逗號後有空格(`{a, b}`)不展開、只有一個元素(`{a}`)不展開、以及在 `dash`/`sh` 底下完全不支援。三種都是「什麼都沒刪,但 `-f` 讓結束碼還是 0」,回報會變成假的 ✅。
> **Windows PowerShell**`rm -rf X` → `Remove-Item X -Recurse -Force`、`~` → `$HOME`。
### `jsc-generic` 的收尾
@@ -155,11 +176,14 @@ rm -rf ~/.config/opencode/skills/{<該 plugin 的 skill 目錄,以逗號分隔
## 階段 D:回報
以表格回報,一個 plugin 一列:
以表格回報,**一個「助理 × plugin一列**
| plugin | 動作 | 結果 | 備註 |
| --- | --- | --- | --- |
| `jsc-code` | 移除/跳過(未安裝) | ✅ 成功/⚠ 需處理/❌ 失敗 | 例:marketplace 已保留 |
| 助理 | plugin | 動作 | 結果 | 備註 |
| --- | --- | --- | --- | --- |
| Claude Code | `jsc-code` | 移除/跳過(未安裝) | ✅ 成功/⚠ 需處理/❌ 失敗 | 例:marketplace 已保留 |
| Codex | `jsc-code` | 跳過(未安裝) | ✅ | — |
只處理一個助理時可以省掉「助理」欄。**處理多個時一定要有**,否則使用者看不出哪一格出問題。
表格下方固定列出: