What:`templates/check-contents.md` 從 markdown 表格改成一台執行環境一個 H2 區塊,H2 標題就是那一台的體檢頁頁名 `CHECK_{HASH}`,原本的七個欄位改成標題底下一層 `- {欄位名}:{值}` 的條列,頁上不留任何表格。`skills/doctor/SKILL.md` 與 `skills/setup/SKILL.md` 對目錄頁的呼叫從 `wiki-contents.sh upsert CHECK 4 "{HASH}"` 改成 `upsert CHECK 1 "CHECK_{HASH}"`,`references/behaviors.md` 的關鍵步驟、完成條件與可驗證跡象,以及 `README.md` 的兩段技能敘述都跟著對齊。
Why:一列七格的表格,欄位一多就要橫向捲動,讀的人得先數欄位再對照表頭才知道哪一格是什麼;一筆一個 H2 區塊、每一條自己帶欄位名,掃過去就讀得懂。版面換成區塊之後鍵也得跟著換:表格時代的鍵是第 4 欄的裸 `HASH`,區塊沒有欄位可指,唯一能當鍵的是 H2 標題。key-col 與 key 沿用舊值會讓腳本比對不中,同一台機器每體檢一次就在頁尾多附一個區塊,舊區塊從此再也更新不到,而頁面看起來完全正常,錯得無聲無息。頁名只由 `{短主機名}/{登入帳號}` 決定,`GITEA_HOST` 換掉、`JSC_WIKI_REPO_CHECK` 搬到別的存取庫、Gitea 對頁名的編碼有差都動不到它,拿它當鍵比拿任何含網址的值都穩。
How:範本頁首的寫入語意從「一列」改寫成「一個區塊」,並補上四個參數的說明。第三個參數 `<key>` 收 H2 標題文字,也就是 `CHECK_{HASH}`;第四個參數收的是區塊檔而不是列檔,內容為 `## CHECK_{HASH}` 那一行、一個空行,再照範本的欄位順序每欄一條條列,`HASH` 那一欄照樣要寫,標題是鍵不代表欄位可以省,否則下一個讀的人讀不到。第二個參數 `1` 定位成 `<key-col>`,只在頁面還留著舊表格、需要自動轉檔時才用得到,指舊表格裡持有 `[CHECK_{HASH}](網址)` 的第 1 欄,轉檔時取那一格的文字當 H2 標題,頁面已經是條列格式就忽略它。「體檢頁」那一條維持 `[{文字}]({絕對網址})` 的人用連結寫法,H2 標題本身不放連結也不放網址。兩支技能的結束碼分流一併校正:`wiki-contents.sh` 的結束碼 1 從「寫入失敗」改寫成「組不出頁面內容或寫入失敗」,頁上找不到本機那一個區塊不算這一碼,腳本會改成附加。實際的轉檔與 upsert 邏輯都在 `jsc-gitea/tools/wiki-contents.sh`,這個存取庫只改敘述與範本。
Who:屬於「wiki 目錄頁改條列式呈現」這個需求落在 jsc-cli 的部分,也就是 `CHECK_CONTENTS` 這一型目錄頁的去表格化。內容頁維持圖表優先,不在這一輪範圍。
96 lines
15 KiB
Markdown
96 lines
15 KiB
Markdown
# jsc-cli — CLI 偵測與技能庫部署
|
||
|
||
jsc 技能組的 CLI domain:找出已安裝的 AI CLI、列出各 CLI 可用模型並加上能力標籤,以及對每個 CLI 批次安裝、更新、解除安裝整組 jsc plugins。
|
||
|
||
## 安裝、更新、移除
|
||
|
||
Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安裝 token 為 `jsc-cli@jsc`。每個指令一行:
|
||
|
||
| CLI | 安裝 | 更新 | 移除 |
|
||
| --- | --- | --- | --- |
|
||
| claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && claude plugin install jsc-cli@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-cli@jsc` | `claude plugin uninstall jsc-cli@jsc` |
|
||
| codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && codex plugin add jsc-cli@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-cli@jsc` |
|
||
| copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && copilot plugin install jsc-cli@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-cli@jsc` | `copilot plugin uninstall jsc-cli@jsc` |
|
||
| antigravity | `git clone https://gitea.jsc.idv.tw/plugins/cli.git ~/plugins/cli && agy plugin install ~/plugins/cli` | `git -C ~/plugins/cli pull && agy plugin uninstall jsc-cli && agy plugin install ~/plugins/cli` | `agy plugin uninstall jsc-cli` |
|
||
| kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && kiro-cli plugin install jsc-cli@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-cli@jsc` | `kiro-cli plugin uninstall jsc-cli@jsc` |
|
||
|
||
> antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用 `/jsc-cli:deploy`。
|
||
|
||
> 舊入口 `plugins/jsc` 已移除,marketplace 正本移到 `plugins/meta`。marketplace 名稱仍是 `jsc`(取自 marketplace.json 的 `name` 欄位,與存取庫名無關),安裝 token 不變;已從舊入口安裝過的人先執行 `claude plugin marketplace remove jsc`,再依上表重新 add。
|
||
|
||
## 工具
|
||
|
||
| 工具 | 用途 |
|
||
| --- | --- |
|
||
| `tools/detect-clis.sh` | 列出已安裝的 AI CLI 與執行檔路徑(TSV:name / path / version;antigravity 的執行檔為 `agy`、kiro 為 `kiro-cli`) |
|
||
| `tools/deploy.sh` | 對單一 CLI 執行安裝、更新或解除安裝(`deploy.sh [-n] {mode} {cli} {domain}...`,mode 為 install / update / uninstall);印出每個指令與其結束碼,最後一行 `result` 標 ok 或 fail。`-n` 只印指令不執行。上表五個 CLI 的指令差異全部收在這支腳本裡。Codex 更新 `jsc-cli` 與 `jsc-hooks` 後會把舊版快取路徑補成指向新版的相容連結,避免正在跑的部署流程找不到 helper 腳本,也避免尚未重啟的工作階段在 Stop hook 階段找不到舊路徑。install 或 update 全數成功時,收尾轉呼叫 `jsc-hooks` 的 `restart-gate.sh require` 掛上重啟閘門,並印一行 `restart` 標出狀態檔位置;uninstall 不寫。尋找 `restart-gate.sh` 時優先用 `$JSC_HOME/current/jsc-hooks`、本地 clone 與 Kiro skills,最後才掃各 CLI 快取,避免部署收尾綁死單一 CLI 的版號路徑。狀態檔的路徑、格式與判讀全在 `restart-gate.sh`,這支腳本不自己拼——格式只留一個真實來源。站台取自 `GITEA_HOST`,本地 clone 目錄取自 `JSC_LOCAL_PLUGINS`,兩者的預設值見下表 |
|
||
| `tools/check-requires.sh` | `check-requires.sh {cli} {manifest}` 檢查 manifest 的 `jsc.requires` 最低版本。沒有宣告就通過;版本不符或缺相依 plugin 就回 `status=blocked` 與結束碼 1。`deploy.sh update` 在每個 domain 更新前呼叫它一次:結束碼 1 只印一行 `warn`,那個 domain 照樣更新——跳過會讓落後的 domain 永遠等不到相依版本,也就永遠更新不到,真正的阻擋由 `jsc-hooks` 的 `version-guard.sh` 在技能被叫用時執行;結束碼 2 以上是檢查腳本自己出錯,讀不到結論就不當成通過,印 `skip` 並跳過該 domain |
|
||
| `tools/write-guides.sh` | 產生這台機器專屬的更新指引 `$JSC_HOME/update-guide.md` 與移除指引 `$JSC_HOME/remove-guide.md`(`write-guides.sh [-n] {install\|update} {domain}...`),一輪部署跑一次。CLI 清單取自 `detect-clis.sh`,每支 CLI 的指令字面直接取自 `deploy.sh -n` 的輸出,所以指引寫的就是實際會跑的指令;kiro 走不走本地複製退路也依實際偵測結果標注 |
|
||
| `tools/list-models.sh` | 讀各 CLI 設定檔列出模型(TSV:cli / model / in-use);設定檔缺失就不輸出該 CLI 的列,一律 exit 0。設定檔位置只寫在這支腳本裡 |
|
||
| `tools/model-config.sh` | 解析 SDLC 各階段的偏好模型鏈(`get {stage}`、`list`、`resolve {stage}` 印出目前 CLI 可用的第一個模型);專案 `.jsc/models` 優先於 `$JSC_HOME/models.conf`,格式見 `references/model-tags.md`。鏈只影響建議與偏好順序,不影響閘門放行 |
|
||
| `tools/config-spec.tsv` | 設定規格表:每個環境變數與設定檔一列,標明必要或選擇、預設值、驗證方式、修法。體檢與設定共用這一份,新增設定時要同步補一列 |
|
||
| `tools/scan-config.sh` | 依規格表盤點設定現況(`scan {global\|project\|all}` 印 TSV 與 summary、`spec` 印規格表、`orphans` 找出漏登錄的變數);`-o` 為離線模式,需要連 Gitea 的檢查一律標 skipped。唯讀,不寫任何設定;帶 TOKEN 的項目只印 set 或 unset |
|
||
| `tools/apply-config.sh` | 把設定寫進 shell rc 檔(`set {KEY} {VALUE}`、`unset {KEY}`)或建立目錄(`mkdir {PATH}`);`show` 印出目前設定,`rcfiles` 印出會寫入的檔案。內容一律收在 `# jsc-config` 標記段落之間,整段重寫不疊加,段落外不動。動檔案前先備份到 `$JSC_HOME/backup/config/{yyyyMMdd_HHmmss}/`,備份失敗就不寫;寫完重讀驗證。fish 自動改用 `set -gx` 語法 |
|
||
| `tools/model-tags.sh` | 解析 `references/model-tags.md` 的能力標籤與 SDLC 階段必要標籤(`dump`、`sync`、`stage {階段}`、`model {模型 id}`、`gate {階段} {模型 id}`);`sync` 寫出 `$JSC_HOME/model-tags.tsv` 供 `jsc-hooks` 的 sdlc-gate 讀取。`gate` 的結束碼 0 為 PASS、1 為缺標籤、2 為模型或階段不在表上,`/jsc-cli:delegate` 依這三碼決定收下或退回 |
|
||
| `tools/build-todo.sh` | 把三支檢查腳本的輸出合併成一張「待修項目」表(`--config` 收 `scan-config.sh scan`、`--wiring {cli}=` 收 `wire-cli.sh status`、`--version` 收 `version-guard.sh report`);類別固定排成 missing、invalid、unwired、落後,一項都沒有時仍印一列「無」。`/jsc-cli:doctor` 與 `/jsc-cli:setup` 共用這一份合併規則,兩邊各寫一次就會各自漂移 |
|
||
|
||
## Skills 目錄
|
||
|
||
呼叫方式:Claude / Antigravity `/jsc-cli:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。
|
||
|
||
<!-- JSC-SKILLS:START -->
|
||
|
||
### `models`
|
||
|
||
**三支腳本併行取得資料**:`tools/detect-clis.sh` 取已安裝 CLI、`tools/list-models.sh` 讀出各 CLI 可使用的模型、`tools/model-config.sh list` 取各階段的偏好模型鏈。接著依 `references/model-tags.md` 加上能力標籤,用 `tools/model-tags.sh sync` 把標籤表寫進 `$JSC_HOME/model-tags.tsv` 供 sdlc-gate 讀取,並列出 SDLC 各階段的必要標籤(plan、analyze 需 `reasoning-max`;implement 需 `coding`;maintain 任意)。`jsc-sdlc` 閘門一律以能力標籤判定,偏好鏈只用來建議切換目標。
|
||
|
||
### `delegate`
|
||
|
||
把單一明確任務交給另一個已安裝的 AI agent CLI 當作 subagent 執行。CLI 清單一律取自 `tools/detect-clis.sh`,模型夠不夠格一律由 `tools/model-tags.sh gate` 的結束碼判定,不讓模型自評標籤。可指定目標 CLI,也可依任務需求用能力標籤篩選模型,或強制指定模型。每個目標分開派工,預設只讀,寫入範圍以路徑清單明列;回傳採固定的 TSV 契約(`result`、`summary`、`criterion`、`wrote`、`error`),供主 agent 逐項驗證。此技能不負責模型盤點或 plugin 部署。
|
||
|
||
### `deploy`
|
||
|
||
技能庫批次安裝、更新、解除安裝:**偵測 CLI、版本建議、marketplace domain 清單三者併行取得** → 秀出 `jsc-hooks/hooks/version-guard.sh report` 的版本證據表,再依 `recommend` 的一行結論(`recommend<TAB>{update|none|unverifiable}`,只印這一行,不混印表格)標出推薦選項;輸出裡找不到 `recommend` 行就退回 `report` 自行推導並在回報寫明是降級路徑 → 決策樹選模式(呼叫方已確認過模式就沿用,不重問)→ 每個 CLI 一個 sub agent,**全部同時啟動**呼叫 `tools/deploy.sh` 執行原生 plugin 指令(統一 marketplace `jsc`,token `jsc-{domain}@jsc`)→ update 前逐一檢查 `jsc.requires`,版本不符只印 `warn` 並照樣更新該 domain、回報還缺哪一版,阻擋交給 `version-guard.sh` 在技能被叫用時執行;檢查腳本自己出錯才印 `skip` 跳過該 domain → Codex 更新 `jsc-cli` 與 `jsc-hooks` 時保留舊快取相容連結 → install、update 後把偵測到的 CLI 清單交給 `jsc-hooks:hooks-install`,取它的彙總結果,只自行加判一條「smoke 出現 `No such file` 一律視為更新失敗」。domain 名單動態取自 `plugins/meta` 的 marketplace.json,不硬編碼。
|
||
|
||
### `doctor`
|
||
|
||
一次體檢執行環境,只讀不改。四項檢查**同時啟動,各一個 sub agent**:技能版本(`jsc-hooks/hooks/version-guard.sh report`)、Hook 接線(`jsc-hooks/tools/wire-cli.sh status`,唯讀子命令)、設定現況(`tools/scan-config.sh scan all` 比對 `tools/config-spec.tsv`,一次掃完全域與專案兩個範圍)、漏登錄變數(`scan-config.sh orphans`)。唯讀契約下放程式層:呼叫 `wire-cli.sh` 一律帶 `JSC_READONLY=1`,打錯子命令也不會改到機器。每項各出一張表,專案那張一定寫出掃的是哪個目錄;待修項目由 `tools/build-todo.sh` 合併三份輸出並排序。整份結果寫進 wiki `CHECK_{HASH}`,`HASH` 取 `{短主機名}/{登入帳號}`,兩個值都由程式取,主機名一律切掉網域,只保留最新一次。目錄頁 `CHECK_CONTENTS` 住另一個庫(`JSC_WIKI_REPO_CONTENTS`),版面是 H1 加 `>` 引言,再一台機器一個 H2 區塊,頁上沒有 markdown 表格;改由 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 CHECK_{HASH}` 只寫本機那一個區塊,欄位是 `- {欄位名}:{值}` 的條列。鍵是 H2 標題,也就是體檢頁頁名 `CHECK_{HASH}`,不是任何含網址的值:網址會隨站台、存取庫與頁名編碼改變,頁名只由主機加帳號決定,拿網址當鍵就比不中,同一台機器每體檢一次就多附一個區塊。第二個參數 `1` 是 `<key-col>`,只在頁面還是舊表格、需要自動轉檔時用得到,指舊表格裡持有 `[CHECK_{HASH}](網址)` 的第 1 欄。「體檢頁」那一條的連結給人點,填絕對網址。修復交給 `/jsc-cli:setup`,體檢本身不動任何設定。
|
||
|
||
### `setup`
|
||
|
||
修復 `/jsc-cli:doctor` 找出的問題,一次一項,逐項確認才動手。待修清單優先讀 wiki `CHECK_{HASH}`,沒有頁面就當場重掃:**三支檢查腳本併行跑**,再用 `tools/build-todo.sh` 合併成同一張表。依修法分流:`auto` 用 `tools/apply-config.sh` 直接寫、`ask` 先用決策樹問到值再寫、`manual` 印出步驟交給操作者。複合修復交回原主:版本落後找 `/jsc-cli:deploy`(連同已確認的模式與版本報告一起傳過去,不讓它重問重查)、hook 未接線找 `/jsc-hooks:hooks-install`、缺 `model-tags.tsv` 找 `/jsc-cli:models`。逐項確認維持循序,**寫完的重驗併行**;環境變數類只驗「rc 段落裡確實有那一行」,環境層面交給下一次 `/jsc-cli:doctor`。最後覆寫體檢頁 `CHECK_{HASH}`,並用 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 CHECK_{HASH}` 以 H2 標題(也就是體檢頁頁名)當鍵,更新目錄頁 `CHECK_CONTENTS` 的本機那一個區塊,兩頁分屬不同存取庫。
|
||
|
||
<!-- JSC-SKILLS:END -->
|
||
|
||
## 環境變數
|
||
|
||
| 變數 | 用途 | 未設定時 |
|
||
| --- | --- | --- |
|
||
| `GITEA_HOST` | Gitea 站台(可省略 scheme,預設 https) | 用正本站台 `https://gitea.jsc.idv.tw` |
|
||
| `JSC_GITEA_OWNER` | 技能組存取庫的 owner | 用 `plugins` |
|
||
| `JSC_LOCAL_PLUGINS` | antigravity 與 kiro 退路用的本地 clone 目錄 | 用 `$JSC_HOME/plugins`(即 `~/.jsc/plugins`) |
|
||
| `JSC_KIRO_SKILLS` | kiro 退路複製 skills 的目標目錄 | 用 `~/.kiro/skills` |
|
||
| `JSC_DEPLOY_DRYRUN` | 設為 `1` 等同 `deploy.sh -n`,只印指令不執行 | 照常執行 |
|
||
| `JSC_WIKI_REPO_CHECK` | 體檢內容頁 `CHECK_{HASH}` 所在的 `{owner}/{repo}`;只管內容頁,管不到目錄頁 | 退回 `JSC_WIKI_REPO`;兩個都沒有就略過寫入,並把這一項列進待修 |
|
||
| `JSC_WIKI_REPO_CONTENTS` | 目錄頁 `CHECK_CONTENTS` 所在的 `{owner}/{repo}`。目錄頁與內容頁分屬不同存取庫:所有 `{TYPE}_CONTENTS` 一律住這一個庫,內容頁才看各自的型別變數 | 退回 `JSC_WIKI_REPO`;兩個都沒有就略過目錄頁寫入,並把這一項列進待修。不退回 `JSC_WIKI_REPO_CHECK` |
|
||
| `JSC_CONFIG_SPEC` | 改讀別份設定規格表(測試 `scan-config.sh` 時用) | 用 `tools/config-spec.tsv` |
|
||
| `JSC_HOME` | hook 資料目錄,兩份指引與重啟狀態檔都寫在這裡 | 用 `~/.jsc` |
|
||
| `JSC_RESTART_GATE` | 設成 `off` 可略過部署後的重啟提示閘門(判讀在 `jsc-hooks`,`jsc-cli` 只負責寫狀態檔) | 照常提示重啟 |
|
||
|
||
`JSC_LOCAL_PLUGINS` 的預設值刻意避開 `~/plugins`:那是維護者放技能組開發 checkout 的地方,`git pull` 下去會蓋掉未提交的工作。這個變數指到的目錄若是開發中的樹(有未提交變更,或有未推送的 commit),`deploy.sh` 只印一行 `skip` 並直接用現地內容安裝,不執行 `git pull`。
|
||
|
||
## 部署留在機器上的檔案
|
||
|
||
| 檔案 | 何時產生 | 用途 |
|
||
| --- | --- | --- |
|
||
| `$JSC_HOME/update-guide.md` | install、update 收尾 | 下次更新的依據:偵測到的 CLI、每支的安裝方式與實際指令、marketplace token、domain 清單 |
|
||
| `$JSC_HOME/remove-guide.md` | install、update 收尾 | 整組移除的依據:各 CLI 的移除指令,加上 `$JSC_HOME`、本地 clone、kiro 技能目錄、rc 檔 `# jsc-config` 段落這些殘留物 |
|
||
| `$JSC_HOME/restart-required.d/{cli}` | install、update 全數成功時 | 一支 CLI 一份,內容是四行 key=value(`at`、`mode`、`domains`、`cli`)。`jsc-hooks` 只讀當前 CLI 那一份來提示重啟,別支的不影響這一支;逃生門 `JSC_RESTART_GATE=off` 也在那邊判讀。路徑與格式的唯一來源是 `jsc-hooks/hooks/restart-gate.sh`,`deploy.sh` 只轉呼叫它的 `require` |
|
||
|
||
兩份指引一律整份覆寫,內容依產生當下的偵測結果生成,不寫死。`uninstall` 不產生指引、也不寫重啟狀態檔。
|
||
|
||
## 相關 domain
|
||
|
||
- [`jsc-gitea`](https://gitea.jsc.idv.tw/plugins/gitea):取得 domain 名單(`tools/gitea.sh repos plugins`)
|
||
- [`jsc-hooks`](https://gitea.jsc.idv.tw/plugins/hooks):deploy 完成後重新接線 hooks(`jsc-hooks:hooks-install`)
|