chore(release): 放行技能組稽核修正到預設分支 #46

Merged
admin merged 5 commits from develop into master 2026-08-31 03:56:30 +00:00
26 changed files with 785 additions and 177 deletions
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-meta", "name": "jsc-meta",
"version": "0.2.2", "version": "0.2.3",
"description": "技能組自我管理:新建、更新、刪除技能與技能準則", "description": "技能組自我管理:新建、更新、刪除技能與技能準則",
"skills": "./skills", "skills": "./skills",
"author": { "author": {
@@ -19,7 +19,8 @@
"jsc-ask": ">=0.0.6", "jsc-ask": ">=0.0.6",
"jsc-cli": ">=0.2.1", "jsc-cli": ">=0.2.1",
"jsc-git": ">=0.0.9", "jsc-git": ">=0.0.9",
"jsc-gitea": ">=0.1.7" "jsc-gitea": ">=0.1.8",
"jsc-hooks": ">=0.3.1"
} }
} }
} }
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-meta", "name": "jsc-meta",
"version": "0.2.2", "version": "0.2.3",
"description": "技能組自我管理:新建、更新、刪除技能與技能準則", "description": "技能組自我管理:新建、更新、刪除技能與技能準則",
"skills": "./skills", "skills": "./skills",
"jsc": { "jsc": {
@@ -8,7 +8,8 @@
"jsc-ask": ">=0.0.6", "jsc-ask": ">=0.0.6",
"jsc-cli": ">=0.2.1", "jsc-cli": ">=0.2.1",
"jsc-git": ">=0.0.9", "jsc-git": ">=0.0.9",
"jsc-gitea": ">=0.1.7" "jsc-gitea": ">=0.1.8",
"jsc-hooks": ">=0.3.1"
} }
} }
} }
+18 -9
View File
@@ -26,31 +26,31 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
### `skill-new` ### `skill-new`
新建技能:決策樹問細節(目標、觸發、輸入輸出、domain)→ 缺 domain 時依 template 結構建立新存取庫 → sub agent 依準則產生技能 → 審核清單自檢 → PR,並依 `references/pr-report.md` 回報。 新建技能:先併行預跑 `list-skills.sh` 與 `sync-domains.sh`,再用決策樹問細節(目標、觸發、輸入輸出、domain)→ 缺 domain 時依 template 結構建立新存取庫 → sub agent 依準則產生技能 → 審核清單自檢 → PR,並依 `references/pr-report.md` 回報 → 依 `references/deploy-verify.md` 部署與驗證。
### `skill-update` ### `skill-update`
更新技能:先查 Gitea 正本 marketplace 取得 domain 清單並補 clone 缺少的存取庫,列出全部技能 → 使用者選擇 → 決策樹問更新細節 → 更新後依審核檢查清單逐項檢查,不符就回到詢問 → PR,並依 `references/pr-report.md` 回報。 更新技能:先查 Gitea 正本 marketplace 取得 domain 清單並補 clone 缺少的存取庫,列出全部技能 → 使用者選擇 → 決策樹問更新細節 → 更新後依審核檢查清單逐項檢查,不符就回到詢問 → PR,並依 `references/pr-report.md` 回報 → 依 `references/deploy-verify.md` 部署與驗證。
### `skill-delete` ### `skill-delete`
刪除技能:`sync-domains.sh` 依 Gitea 正本 marketplace 同步所有存取庫,`list-skills.sh` 列出全部技能 → 使用者選擇 → `find-skill-refs.sh` 盤點關聯檔案 → sub agent 逐檔判斷是否需修正以維持功能(需要就決策樹問修正細節並依準則檢查)→ 刪除 → `verify-skill-removed.sh` 實地檢查各 CLI 的技能快取與 hook 設定,確認沒有殘留 → PR。 刪除技能:`sync-domains.sh` 依 Gitea 正本 marketplace 同步所有存取庫,`list-skills.sh` 列出全部技能 → 使用者選擇 → `find-skill-refs.sh` 盤點關聯檔案 → 併行的 sub agent 逐檔判斷是否需修正以維持功能(需要就決策樹問修正細節並依準則檢查)→ 刪除 → PR → 依 `references/deploy-verify.md` 部署,再用 `verify-skill-removed.sh` 實地檢查各 CLI 的技能快取與 hook 設定。深層刪除只在部署後查一次:部署前的刪除還沒生效,查了一定乾淨,證明不了任何事。
### `skillset-update` ### `skillset-update`
批次更新——把一份變更需求套用到整個技能組的多個技能/domain,先檢查工具化、sub agent 與環境變數優先規則,再逐 repo 開 PR;PR 回報格式見 `references/pr-report.md`;單一技能改用 skill-update。 批次更新——把一份變更需求套用到整個技能組的多個技能、domain。同步存取庫與決策樹併行起跑,先檢查工具化、sub agent 與環境變數優先規則,再由併行的 sub agent 逐 repo 改檔與開 PR;PR 回報格式見 `references/pr-report.md`,部署與驗證見 `references/deploy-verify.md`;單一技能改用 skill-update。
### `skill-check` ### `skill-check`
例行稽核——沒有變更需求時,先跑腳本語法檢查與 hook smoke,再把整個技能組逐一對照準則的審核檢查清單,並分開跑流程優化審查。優化面向包含可平行化、可下放工具、重複來回、冗餘步驟、過早或過晚的閘門;不符項目與優化建議分開回報,逐項決策樹確認後才套用,最後逐 repo 開 PR。有變更需求改用 skillset-update。 例行稽核——沒有變更需求時,同步存取庫之後併行跑三組:`lint-scripts.sh` 加 hook smoke、準則審核檢查清單、流程與成本優化審查。優化面向包含可平行化、可下放工具、重複來回、冗餘步驟、過早或過晚的閘門與可省的成本;不符項目與優化建議分開回報,逐項決策樹確認後才套用,最後逐 repo 開 PR。有變更需求改用 skillset-update。
### `ste100-sync` ### `ste100-sync`
同步上游 speak-human-tw 的語言規則:比對 `references/ste100.md` 釘住的上游版本,有新版就萃取適用的變更、逐項決策樹確認、更新 lint 樣式、全庫重掃、bump manifest,最後開 PR。適合列為本 repo 的維護方式。 同步上游 speak-human-tw 的語言規則:先只讀上游 `SKILL.md` frontmatter 的版本來比對,判定要更新才 clone;有新版就萃取適用的變更、逐項決策樹確認、更新 lint 樣式與 `jsc-hooks` 的簡體字表、全庫併行重掃、bump manifest,最後開 PR。適合列為本 repo 的維護方式。
### `tooling-guide` ### `tooling-guide`
盤點目前支援的 plugins、skills、hooks 管理與使用路徑,產出技能組基礎指引。適合建立技能組地圖、支援清單、hook 管理總覽與新人交接資料;安裝、更新、刪除、稽核與修復改用對應技能。 盤點目前支援的 plugins、skills、hooks 管理與使用路徑,產出技能組基礎指引。基礎盤點一律沿用 `inventory-tooling.sh` 的輸出,不再重跑它內部已經跑過的三支腳本。適合建立技能組地圖、支援清單、hook 管理總覽與新人交接資料;安裝、更新、刪除、稽核與修復改用對應技能。
<!-- JSC-SKILLS:END --> <!-- JSC-SKILLS:END -->
@@ -60,15 +60,24 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
| --- | --- | | --- | --- |
| `references/guidelines.md` | 技能準則唯一來源,所有 domain 的 AGENTS.md 都指向這裡 | | `references/guidelines.md` | 技能準則唯一來源,所有 domain 的 AGENTS.md 都指向這裡 |
| `references/ste100.md` | STE100 擬人台灣感語言規則唯一來源(改寫自 speak-human-tw,MIT) | | `references/ste100.md` | STE100 擬人台灣感語言規則唯一來源(改寫自 speak-human-tw,MIT) |
| `tools/ste100-lint.sh` | 語言規則的機檢工具:中國用語、中文句內半形標點、AI 套話、簡體字、中文並列斜線;命中 exit 1 | | `references/pr-report.md` | PR 收尾回報格式唯一來源,所有會開 PR 的技能都指向這裡 |
| `references/deploy-verify.md` | 四支異動技能共用的部署與驗證流程:判路線、部署或工作樹、**在新的 CLI 行程裡驗證**、失敗分流 |
| `templates/tooling-contents.md` | `TOOLING_CONTENTS` 目錄頁樣板。一列代表一組「機器、CLI、帳號」;只更新自己那一列,別人的列原樣保留,**禁止整頁覆蓋** |
| `templates/tooling-page.md` | `TOOLING_{HASH}` 內容頁樣板。分節對應 `inventory-tooling.sh` 的輸出;**每次盤點覆寫整頁**,只留現況,不留歷史 |
| `tools/plugins-root.sh` | 推導技能組工作目錄的根,六支腳本共用。以 plugin 形式安裝時「腳本上兩層」會落在快取目錄,所以推導規則抽出來;推不出來 exit 1 並指名要設 `JSC_PLUGINS_ROOT` |
| `tools/ste100-lint.sh` | 語言規則的機檢工具:中國用語、中文句內半形標點、AI 套話、簡體字、中文並列斜線;命中 exit 1,沒給檢查對象 exit 2 |
| `tools/lint-scripts.sh` | 一個 domain 的腳本檢查三合一:`sh -n` 語法、執行權限、檔頭結束碼宣告;有不合格 exit 1,沒有腳本可掃 exit 3(**不等於通過**) |
| `tools/deploy-route.sh` | 判定改動有沒有進存取庫的預設分支,決定走部署路線(exit 0)或工作樹路線(exit 3);判不出來 exit 1,**不等於工作樹路線** |
| `tools/sync-domains.sh` | 依 Gitea 正本 marketplace 把所有 domain 存取庫 clone 或 pull 到本機,印出 `domain<TAB>path`;**只有 exit 0 代表全部到位且最新**,exit 3 代表有存取庫跳過或 pull 失敗(stderr 列路徑),exit 2 代表有 domain clone 失敗 | | `tools/sync-domains.sh` | 依 Gitea 正本 marketplace 把所有 domain 存取庫 clone 或 pull 到本機,印出 `domain<TAB>path`;**只有 exit 0 代表全部到位且最新**,exit 3 代表有存取庫跳過或 pull 失敗(stderr 列路徑),exit 2 代表有 domain clone 失敗 |
| `tools/list-skills.sh` | 列出正本 marketplace 上各 domain 存取庫的技能,印出 `domain<TAB>name<TAB>description`;不在正本清單上的存取庫不列 | | `tools/list-skills.sh` | 列出正本 marketplace 上各 domain 存取庫的技能,印出 `domain<TAB>name<TAB>description`;不在正本清單上的存取庫不列 |
| `tools/inventory-tooling.sh` | 盤點目前支援的 plugins、skills、tools、hooks,輸出技能組基礎指引 Markdown | | `tools/inventory-tooling.sh` | 盤點目前支援的 plugins、skills、tools、hooks,輸出技能組基礎指引 Markdown |
| `tools/sync-marketplace.sh` | 寫入或更新兩份正本 marketplace 的 plugin 條目,再複製到每個 domain 存取庫(保證位元組一致)。**需要 python3**(json 模組改 JSON),缺 python3 不寫檔並 exit 1;存取庫不在本機 exit 3 | | `tools/sync-marketplace.sh` | 寫入或更新兩份正本 marketplace 的 plugin 條目,再複製到每個 domain 存取庫(保證位元組一致)。**需要 python3**(json 模組改 JSON),缺 python3 不寫檔並 exit 1;存取庫不在本機 exit 3 |
| `tools/sync-skill-manifest.sh` | 同步 domain README 的「Skills 目錄」,並 bump 三份 manifest 的 version;`minor` 與 `patch` 不得超過 `9`,`major` 可以超過 `9` | | `tools/sync-skill-manifest.sh` | 同步 domain README 的「Skills 目錄」,並 bump 三份 manifest 的 version;`minor` 與 `patch` 不得超過 `9`,`major` 可以超過 `9`。缺目錄、缺標記或缺 version 欄位 exit 1,用法錯誤 exit 2 |
| `tools/find-skill-refs.sh` | 盤點一個技能在正本 marketplace 各 domain 存取庫裡的引用檔案(不掃非技能組存取庫與點開頭目錄);技能名稱為純子字串比對,命中要逐檔確認;零命中 exit 1,掃描失敗 exit 3 | | `tools/find-skill-refs.sh` | 盤點一個技能在正本 marketplace 各 domain 存取庫裡的引用檔案(不掃非技能組存取庫與點開頭目錄);技能名稱為純子字串比對,命中要逐檔確認;零命中 exit 1,掃描失敗 exit 3 |
| `tools/verify-skill-removed.sh` | 刪除技能後實地檢查各 CLI 的技能快取與 hook 設定有無殘留;有殘留 exit 1,沒偵測到 CLI 或沒有可查位置 exit 3(**不等於乾淨**) | | `tools/verify-skill-removed.sh` | 刪除技能後實地檢查各 CLI 的技能快取與 hook 設定有無殘留;有殘留 exit 1,沒偵測到 CLI 或沒有可查位置 exit 3(**不等於乾淨**) |
兩份 `TOOLING` 樣板的寫入語意剛好相反,套用前先分清楚。目錄頁是共用的,整頁覆蓋會刪掉別台機器的紀錄,所以只准動自己那一列。內容頁只屬於一組「機器、CLI、帳號」,記的是當下現況,舊的安裝內容早就不成立,所以整頁覆寫。頁名與雜湊規則見 `references/guidelines.md` 的「Wiki 頁命名總表」。
## 相關 domain ## 相關 domain
- [`jsc-ask`](https://gitea.jsc.idv.tw/plugins/ask):決策樹問詢 - [`jsc-ask`](https://gitea.jsc.idv.tw/plugins/ask):決策樹問詢
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-meta", "name": "jsc-meta",
"version": "0.2.2", "version": "0.2.3",
"description": "技能組自我管理:新建、更新、刪除技能與技能準則", "description": "技能組自我管理:新建、更新、刪除技能與技能準則",
"skills": "./skills/", "skills": "./skills/",
"jsc": { "jsc": {
@@ -8,7 +8,8 @@
"jsc-ask": ">=0.0.6", "jsc-ask": ">=0.0.6",
"jsc-cli": ">=0.2.1", "jsc-cli": ">=0.2.1",
"jsc-git": ">=0.0.9", "jsc-git": ">=0.0.9",
"jsc-gitea": ">=0.1.7" "jsc-gitea": ">=0.1.8",
"jsc-hooks": ">=0.3.1"
} }
} }
} }
+65
View File
@@ -0,0 +1,65 @@
# 部署與驗證
`skill-new`、`skill-update`、`skill-delete`、`skillset-update` 四支異動技能的收尾共用這份流程。四支只在 SKILL.md 留一行指標指過來,不各自抄一份——抄四份會各自漂移,改一次要記得改四個地方。
## 1. 判路線
改動有沒有進存取庫的**預設分支**,決定走哪條路線。marketplace 與 `version-guard.sh` 都讀預設分支,停在 `develop` 的改動 `jsc-cli:deploy` 看不到。
判定交給 `jsc-meta/tools/deploy-route.sh {domain-path}`,不要自己用 `git log` 目測。多個 domain 就每個各跑一次,可以並行。
| 結束碼 | 意思 | 怎麼辦 |
| --- | --- | --- |
| 0 | 改動已在預設分支上 | 走第 2 節的部署路線 |
| 3 | 改動還沒併進預設分支 | 走第 3 節的工作樹路線 |
| 2 | 用法錯誤 | 修參數重跑 |
| 1 | 判不出來:不是 git 存取庫、沒有 origin、fetch 失敗,或取不到預設分支 | 停下回報 stderr 的原因。**1 不等於工作樹路線**,判不出來就問使用者,不要自己挑一條走 |
完成條件:每個受影響的 domain 存取庫都有一個路線判定,且判定來自腳本輸出的 `route` 欄位。
## 2. 部署路線(結束碼 0)
1. 叫用 `jsc-cli:deploy` 的更新模式,讓每支已安裝的 CLI 都載入新版。deploy 收尾會寫 `$JSC_HOME/restart-required.d/{cli}`,一支 CLI 一份;重啟該支 CLI 之後由 `jsc-hooks` 清掉自己那一份。閘門規則見 [`guidelines.md`](guidelines.md) 的「部署後重啟閘門」。
2. deploy 更新不了某些 CLI 時,記下哪幾支確實載入了新版,改用其中一支繼續。一支都沒載入就停下,把這次異動回報為未驗證。
完成條件:`claude plugin list`(或別支已安裝 CLI 的等效指令)印出的 `jsc-{domain}` 版本,等於三份 manifest 現在的版本。
## 3. 工作樹路線(結束碼 3)
改動還沒進預設分支,部署路線的完成條件永遠達不到,所以改對**工作樹**驗證:
1. 第 4 節的驗證對象改成 `{root}/{domain}` 工作樹,不是已安裝的副本。
2. 回報標注「工作樹驗證、尚未部署」。
3. 點名還沒合併的釋出 PR——改動要等它合進預設分支才會到任何 CLI。跨多個存取庫的異動要等最後一支合併,所以全部列出來。刪除技能還要補一句:PR 合併前技能仍然裝著,指令仍然叫得動。
完成條件:第 4 節的驗證在每個工作樹上都通過,且回報裡列出每一支待合的釋出 PR。
## 4. 功能驗證:一定要換一個工作階段
**部署會把閘門立在自己腳下。** `jsc-cli:deploy` 收尾寫下重啟閘門的狀態檔,緊接著在**同一個工作階段**叫用剛做好的技能,那支技能不在豁免清單上就必被擋;連修復路徑 `jsc-cli:doctor` 與 `jsc-cli:setup` 也一起被擋。
解法不是把它們加進豁免清單。豁免只擋得住閘門,擋不住「目前行程還載著舊版」這個事實——豁免過關的驗證,驗到的是舊版行為,等於假通過。
**正解:驗證一律在新的 CLI 行程裡跑。** 用 `jsc-cli/tools/detect-clis.sh` 取得已安裝 CLI 的執行檔路徑,對每支支援非互動 Prompt 的 CLI,另外開一個行程送出最小 Prompt。新行程是新的工作階段,`jsc-hooks` 開場就清掉那支 CLI 自己的狀態檔,載入的也是磁碟上的新版。
四支技能都不得在部署的那個工作階段內叫用剛異動的技能。
驗證項目:
1. 跑 `jsc-meta/tools/list-skills.sh`,確認該技能的列符合這次異動:新增看得到 `{domain}<TAB>{name}` 那一列;更新看得到新的 `description`;刪除看不到那一列。
2. 這次異動碰過的每一支工具,都用真實參數跑一次,把實際結束碼對照工具檔頭宣告的意思。
3. 每支可測的 CLI 各開一個新行程,送出一個最小 Prompt 叫用 `/jsc-{domain}:{name}`,記錄 CLI 結束碼與 stderr。新增與更新要確認載入的是異動後的 SKILL.md 內文,不是「未知指令」;刪除要確認指令已消失,或替代路徑仍可用。各支 CLI 可以並行送。
完成條件:上列三項各有結果;每支可測 CLI 的 Prompt 都沒有非預期 stderr;不可測的 CLI 逐支寫明原因。
## 5. 驗證失敗怎麼辦
Prompt 因為 CLI 工具、plugin 安裝、hook 接線、模型標籤表或設定而失敗時:
1. 先跑 `jsc-cli:doctor`,再用 `jsc-cli:setup` 修待修項目,然後在新行程重跑同一個 Prompt。
2. hook 冒煙測試失敗,交給 `jsc-hooks:hooks-install`,由它把 hook 接線或執行期錯誤轉給 `jsc-hooks:repair`。
3. 根因若在技能組本身的規格、工具或 hook 實作,就修對應 domain,跑 `jsc-meta/tools/sync-skill-manifest.sh {domain-path}` 同步與驗證,再用 `jsc-git:pr` 對 `develop` 開 PR;PR 送出後回到第 1 節重判路線。
任一項對不上——列不見、結束碼不在工具文件裡、指令載不到、Prompt 失敗、非預期 stderr——就修掉成因,回到第 1 節重跑整段。
完成條件:每一項不符都有對應的修正動作與重跑結果,沒有留下「已知失敗但照樣收尾」的項目。
+28 -7
View File
@@ -89,7 +89,7 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| `JSC_WIKI_REPO_PLAN` | `PLAN_CONTENTS`、`PLAN_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_PLAN` | `PLAN_CONTENTS`、`PLAN_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_ANALYZE` | `ANALYZE_CONTENTS`、`ANALYZE_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_ANALYZE` | `ANALYZE_CONTENTS`、`ANALYZE_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_DELIVER` | `DELIVER_CONTENTS`、`DELIVER_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_DELIVER` | `DELIVER_CONTENTS`、`DELIVER_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_MAINTAIN` | `MAINTAIN_CONTENTS`、`MAINTAIN_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_MAINTAIN` | `MAINTAIN_CONTENTS` 所在的 `{owner}/{repo}`(本類型只有目錄頁) | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_REPO` | `REPO_CONTENTS`、`REPO_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_REPO` | `REPO_CONTENTS`、`REPO_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_LOG` | `LOG_CONTENTS`、`LOG_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_LOG` | `LOG_CONTENTS`、`LOG_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_LEARN` | `LEARN_CONTENTS`、`LEARN_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_LEARN` | `LEARN_CONTENTS`、`LEARN_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
@@ -97,6 +97,7 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| `JSC_WIKI_REPO_CHECK` | `CHECK_CONTENTS`、`CHECK_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_CHECK` | `CHECK_CONTENTS`、`CHECK_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_REPORT` | `REPORT_CONTENTS`、`REPORT_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_REPORT` | `REPORT_CONTENTS`、`REPORT_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_SKILLSET` | `SKILLSET_CONTENTS`、`SKILLSET_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` | | `JSC_WIKI_REPO_SKILLSET` | `SKILLSET_CONTENTS`、`SKILLSET_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO_TOOLING` | `TOOLING_CONTENTS`、`TOOLING_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO` |
| `JSC_WIKI_REPO` | 未逐類設定時的共用 wiki `{owner}/{repo}` | 詢問使用者 | | `JSC_WIKI_REPO` | 未逐類設定時的共用 wiki `{owner}/{repo}` | 詢問使用者 |
| `JSC_HOME` | Hook 資料目錄 | 預設 `~/.jsc` | | `JSC_HOME` | Hook 資料目錄 | 預設 `~/.jsc` |
| `JSC_PR_WATCH_INTERVAL` | `jsc-gitea/tools/pr-watch.sh` 輪詢 PR 狀態的間隔秒數 | 預設 60 | | `JSC_PR_WATCH_INTERVAL` | `jsc-gitea/tools/pr-watch.sh` 輪詢 PR 狀態的間隔秒數 | 預設 60 |
@@ -127,8 +128,13 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| --- | --- | | --- | --- |
| `jsc-cli:deploy` | 更新整組技能的入口。擋了就沒有任何方法更新,形成死鎖 | | `jsc-cli:deploy` | 更新整組技能的入口。擋了就沒有任何方法更新,形成死鎖 |
| `jsc-hooks:hooks-install` | 更新後要重新接線,擋了會讓更新做一半卡住 | | `jsc-hooks:hooks-install` | 更新後要重新接線,擋了會讓更新做一半卡住 |
| `jsc-hooks:repair` | hook 壞掉時的唯一修復路徑。擋了就修不好 hook |
| `jsc-cli:models` | SDLC 階段閘門依賴它產生 `model-tags.tsv` | | `jsc-cli:models` | SDLC 階段閘門依賴它產生 `model-tags.tsv` |
| `jsc-meta:*` | 開發技能組本身的工具,擋了就修不了技能組 | | `jsc-meta:*` | 開發技能組本身的工具,擋了就修不了技能組 |
| `jsc-ask:ask` | 上面幾支都要問使用者 |
| `jsc-gitea:wiki` | 上面幾支的收尾要寫 wiki |
共 7 項。這張表的唯一真實來源是 `jsc-hooks/hooks/version-guard.sh` 的檔頭與豁免清單,兩邊要逐項對齊。
沒有 pre-tool hook 的 CLI 接不上這道檢查,`hooks-install` 要據實回報,不得暗示每個 CLI 都有保護。 沒有 pre-tool hook 的 CLI 接不上這道檢查,`hooks-install` 要據實回報,不得暗示每個 CLI 都有保護。
@@ -151,6 +157,7 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| --- | --- | | --- | --- |
| `jsc-cli:deploy` | 部署本身的入口。擋了就沒有方法重跑部署,形成死鎖 | | `jsc-cli:deploy` | 部署本身的入口。擋了就沒有方法重跑部署,形成死鎖 |
| `jsc-hooks:hooks-install` | 部署後要重新接線,擋了會讓部署做一半卡住 | | `jsc-hooks:hooks-install` | 部署後要重新接線,擋了會讓部署做一半卡住 |
| `jsc-hooks:repair` | hook 壞掉時的唯一修復路徑。擋了就修不好 hook |
| `jsc-gitea:wiki` | 寫 `SKILLSET_{HASH}` 異動報告與工作日誌的唯一路徑 | | `jsc-gitea:wiki` | 寫 `SKILLSET_{HASH}` 異動報告與工作日誌的唯一路徑 |
| `jsc-log:worklog` | 部署後還要結清工作日誌 | | `jsc-log:worklog` | 部署後還要結清工作日誌 |
| `jsc-log:learn` | 部署後還要記這次的教訓 | | `jsc-log:learn` | 部署後還要記這次的教訓 |
@@ -159,15 +166,17 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| `jsc-git:pr` | 報告與異動的收尾要開 PR,擋了收尾做不完 | | `jsc-git:pr` | 報告與異動的收尾要開 PR,擋了收尾做不完 |
| `jsc-git:commit` | 同上,`pr` 的第一步就是它 | | `jsc-git:commit` | 同上,`pr` 的第一步就是它 |
豁免這幾支的理由是同一件事:`jsc-meta` 四支異動技能的收尾要求把驗證結果寫進 `SKILLSET_{HASH}`,還要結清工作日誌,而這條路徑必經 `jsc-gitea:wiki` 與 `jsc-log`。全擋的話,部署一跑完就沒有路徑寫完報告,重啟閘門與報告要求互相打死。閘門不自鎖的通則見「審核檢查清單」的流程檢查第 4 項。 豁免這幾支的理由是同一件事:`jsc-meta` 四支異動技能的收尾要求把驗證結果寫進 `SKILLSET_{HASH}`,還要結清工作日誌,而這條路徑必經 `jsc-gitea:wiki` 與 `jsc-log`。全擋的話,部署一跑完就沒有路徑寫完報告,重啟閘門與報告要求互相打死。閘門不自鎖的通則見「審核檢查清單」的流程檢查第 4 項。共 10 項;這張表的唯一真實來源是 `jsc-hooks/hooks/restart-gate.sh` 的檔頭與豁免清單,兩邊要逐項對齊。
**狀態檔為什麼一支 CLI 一份。** 這道閘門管的是「這支 CLI 的行程還在跑舊版」,那是每支 CLI 各自的事實。初版用全機器單一檔案,2026-08-27 部署時實測出兩個後果:並行部署互相覆蓋,`cli=` 與 `domains=` 只留最後一支;更嚴重的是清除也是全域的,**任一支 CLI 重啟就解除全部五支的閘門**,其餘四支沒重啟卻不再被擋,這道閘門在多 CLI 環境等於半失效。改成 per-CLI 之後,`require` 寫自己那份、判定只看自己那份、`clear` 只刪自己那份,`report` 才列得出「哪幾支還沒重啟」。**跨 CLI 或跨工作階段的狀態檔,設計時先問清楚那個事實屬於誰**——同一類錯誤在工作包歸屬狀態檔上也踩過(見「PR 分支階梯」相關的兩層閘門分工)。 **豁免清單不是自鎖的通解。** 部署剛跑完就要在同一個工作階段叫用剛做好的技能,那支技能本來就不該靠豁免過關——豁免只擋得住這一道閘門,擋不住「行程還載著舊版」這個事實,驗證結果會是舊版的行為。正解是換一個工作階段:由 `jsc-cli/tools/detect-clis.sh` 開出的新 CLI 行程去叫用,新行程自己會清掉那份狀態檔,載到的也是新版。做法見 [`deploy-verify.md`](deploy-verify.md)。
**清單認的是技能名,不是呼叫鏈。** 豁免技能轉呼叫的下一層若不在清單上,那一層照樣會被擋。後三支(`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)自己不是收尾規則的主體,是為了讓前六支走得完才補進來的。`version-guard.sh` 的豁免清單當年也是為同一個原因收進 `jsc-ask:ask`。新增豁免技能時要一併想它會呼叫誰。 **狀態檔為什麼一支 CLI 一份。** 這道閘門管的是「這支 CLI 的行程還在跑舊版」,那是每支 CLI 各自的事實。初版用全機器單一檔案,2026-08-27 部署時實測出兩個後果:並行部署互相覆蓋,`cli=` 與 `domains=` 只留最後一支;更嚴重的是清除也是全域的,**任一支 CLI 重啟就解除全部五支的閘門**,其餘四支沒重啟卻不再被擋,這道閘門在多 CLI 環境等於半失效。改成 per-CLI 之後,`require` 寫自己那份、判定只看自己那份、`clear` 只刪自己那份,`report` 才列得出「哪幾支還沒重啟」。**跨 CLI 或跨工作階段的狀態檔,設計時先問清楚那個事實屬於誰**——同一類錯誤在工作包歸屬狀態檔上也踩過,解法見 `jsc-sdlc/tools/wp-gate.sh` 的 `claim` 與 `owns`:`claim` 在領包當下把歸屬登錄成「這個存取庫目前是哪一包」,`owns` 查驗每支 PR 是不是自己那一包的,兩層各管一件事實,狀態檔也刻意不綁工作階段。
**清單認的是技能名,不是呼叫鏈。** 豁免技能轉呼叫的下一層若不在清單上,那一層照樣會被擋。後三支(`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)自己不是收尾規則的主體,是為了讓前七支走得完才補進來的。`version-guard.sh` 的豁免清單當年也是為同一個原因收進 `jsc-ask:ask`。新增豁免技能時要一併想它會呼叫誰。
## Wiki 頁命名總表 ## Wiki 頁命名總表
所有 wiki 頁面一律採雙層命名: 所有 wiki 頁面一律採雙層命名。`MAINTAIN` 是唯一只有目錄頁的類型,理由見表下:
| 類型 | 目錄頁 | 內容頁 | 用途 | 擁有者 | | 類型 | 目錄頁 | 內容頁 | 用途 | 擁有者 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
@@ -175,7 +184,7 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| `PLAN` | `PLAN_CONTENTS` | `PLAN_{HASH}` | 計畫目錄、計畫頁 | jsc-sdlc | | `PLAN` | `PLAN_CONTENTS` | `PLAN_{HASH}` | 計畫目錄、計畫頁 | jsc-sdlc |
| `ANALYZE` | `ANALYZE_CONTENTS` | `ANALYZE_{HASH}` | 分析目錄、分析頁 | jsc-sdlc | | `ANALYZE` | `ANALYZE_CONTENTS` | `ANALYZE_{HASH}` | 分析目錄、分析頁 | jsc-sdlc |
| `DELIVER` | `DELIVER_CONTENTS` | `DELIVER_{HASH}` | 交付目錄、工作包交付文件 | jsc-sdlc | | `DELIVER` | `DELIVER_CONTENTS` | `DELIVER_{HASH}` | 交付目錄、工作包交付文件 | jsc-sdlc |
| `MAINTAIN` | `MAINTAIN_CONTENTS` | `MAINTAIN_{HASH}` | 維護目錄、維護頁 | jsc-sdlc | | `MAINTAIN` | `MAINTAIN_CONTENTS` | 無 | 維護登記目錄:登記進維護期的專案、維護方式、起訖日期與前次維護時間 | jsc-sdlc |
| `REPO` | `REPO_CONTENTS` | `REPO_{HASH}` | 盤點目錄、存取庫盤點頁 | jsc-sdlc | | `REPO` | `REPO_CONTENTS` | `REPO_{HASH}` | 盤點目錄、存取庫盤點頁 | jsc-sdlc |
| `LOG` | `LOG_CONTENTS` | `LOG_{HASH}` | 日誌目錄、工作日誌頁 | jsc-log | | `LOG` | `LOG_CONTENTS` | `LOG_{HASH}` | 日誌目錄、工作日誌頁 | jsc-log |
| `LEARN` | `LEARN_CONTENTS` | `LEARN_{HASH}` | 教訓目錄、技能教訓頁 | jsc-log | | `LEARN` | `LEARN_CONTENTS` | `LEARN_{HASH}` | 教訓目錄、技能教訓頁 | jsc-log |
@@ -183,6 +192,9 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
| `CHECK` | `CHECK_CONTENTS` | `CHECK_{HASH}` | 體檢目錄、執行環境體檢頁 | jsc-cli | | `CHECK` | `CHECK_CONTENTS` | `CHECK_{HASH}` | 體檢目錄、執行環境體檢頁 | jsc-cli |
| `REPORT` | `REPORT_CONTENTS` | `REPORT_{HASH}` | 報表目錄、工作報表頁(年、月、週、日各一頁) | jsc-log | | `REPORT` | `REPORT_CONTENTS` | `REPORT_{HASH}` | 報表目錄、工作報表頁(年、月、週、日各一頁) | jsc-log |
| `SKILLSET` | `SKILLSET_CONTENTS` | `SKILLSET_{HASH}` | 技能組異動目錄、技能組異動報告頁(新增、更新、刪除、批次更新之後的驗證結果與改動清單) | jsc-meta | | `SKILLSET` | `SKILLSET_CONTENTS` | `SKILLSET_{HASH}` | 技能組異動目錄、技能組異動報告頁(新增、更新、刪除、批次更新之後的驗證結果與改動清單) | jsc-meta |
| `TOOLING` | `TOOLING_CONTENTS` | `TOOLING_{HASH}` | 技能盤點目錄、單機單 CLI 的技能盤點頁:一台機器上某一支 CLI 的已安裝 plugin 與版本、可用技能、hook 接線狀態 | jsc-meta |
`MAINTAIN` 沒有內容頁。維護登記全部寫在 `MAINTAIN_CONTENTS` 的表格裡:`jsc-sdlc:implement` 只往那一頁附加登記,`jsc-sdlc:maintain` 只讀那一頁再回寫「前次維護時間」,兩支都沒有產生 `MAINTAIN_{HASH}` 的步驟,`jsc-sdlc/templates/` 也沒有對應範本。總表以前列著這個內容頁,照著找只會找到一個不存在的頁。要補內容頁就先補技能步驟與範本,不能只在總表上寫著。
`{HASH}` 一律為 `{owner}/{repo}`(必要時加上主題字串)的 SHA-1 前 8 碼,大寫。 `{HASH}` 一律為 `{owner}/{repo}`(必要時加上主題字串)的 SHA-1 前 8 碼,大寫。
若第一碼是 `0-9`、`A`、`B`、`C`,就改成 `H` 加上原 SHA-1 前 7 碼,總長仍維持 8 碼。 若第一碼是 `0-9`、`A`、`B`、`C`,就改成 `H` 加上原 SHA-1 前 7 碼,總長仍維持 8 碼。
@@ -198,6 +210,14 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
8 碼與 `H` 前綴的算法完全相同,由同一支 `jsc-gitea/tools/hash-id` 產生。 8 碼與 `H` 前綴的算法完全相同,由同一支 `jsc-gitea/tools/hash-id` 產生。
在沒有存取庫的目錄也跑得出體檢,是這個例外存在的原因。 在沒有存取庫的目錄也跑得出體檢,是這個例外存在的原因。
`TOOLING` 記的也是機器層事實,雜湊來源再多一段:`{主機名}/{工具名稱}/{登入帳號}`。
`{工具名稱}` 是 CLI 代號,取自 `jsc-cli/tools/detect-clis.sh` 輸出的第一欄,值為 `claude`、`codex`、`copilot`、`antigravity`、`kiro` 其中之一。
一台機器、一支 CLI、一個帳號各一頁;算法同上,由同一支 `jsc-gitea/tools/hash-id` 產生。
**為什麼要帶工具名稱。** 每支 CLI 各有自己的已安裝 plugin 集合,也各有自己的 hook 接線狀態,那是五組互相獨立的事實。
少了中間那一段,同一台機器上五支 CLI 會算出同一個雜湊,五份盤點互相覆蓋,最後只剩最後寫入的那一支,讀的人卻看不出被蓋掉。
帶上工具名稱,一支 CLI 就有一頁,換一支 CLI 重跑也不會動到別支的頁。
## 審核檢查清單 ## 審核檢查清單
新增或更新技能後逐項檢查,任一不符就修正: 新增或更新技能後逐項檢查,任一不符就修正:
@@ -210,7 +230,8 @@ PR 開立、更新、留言修正的收尾回報格式只看 [`references/pr-rep
- [ ] wiki repo 與 Gitea 認證先讀目前 shell 繼承的環境變數;只有缺值或無法解析時才詢問;頁面類型不得跨用其他 `JSC_WIKI_REPO_{TYPE}` - [ ] wiki repo 與 Gitea 認證先讀目前 shell 繼承的環境變數;只有缺值或無法解析時才詢問;頁面類型不得跨用其他 `JSC_WIKI_REPO_{TYPE}`
- [ ] 問詢透過 jsc-ask 決策樹規則 - [ ] 問詢透過 jsc-ask 決策樹規則
- [ ] `tools/` 與 `hooks/` 內的 shell 腳本都通過 `sh -n`;技能直接呼叫的腳本都存在、可執行,且退出碼有分流 - [ ] `tools/` 與 `hooks/` 內的 shell 腳本都通過 `sh -n`;技能直接呼叫的腳本都存在、可執行,且退出碼有分流
- [ ] hook 相關變更已用 `jsc-hooks/tools/wire-cli.sh smoke {cli}` 實測;沒有偵測到 CLI 時,至少跑 `smoke codex` 並標明是預設 hook smoke - [ ] hook 相關變更已用 `jsc-hooks/tools/wire-cli.sh smoke {cli}` 實測;沒有偵測到 CLI 時,至少跑 `smoke codex` 並標明是預設 hook smoke。行數讀腳本自己印的 `lines` 那一行,**技能與 README 都不得寫死數字**——腳本會自我斷言,抄一份數字進文件,加減判定路徑時就漂移,稽核反而被舊數字誤導
- [ ] 唯讀的稽核與體檢流程呼叫 `wire-cli.sh` 時帶 `JSC_READONLY=1`:打錯子命令就由程式擋下(exit 6),不靠呼叫端自我約束;`status` 與 `smoke` 不受影響
- [ ] SKILL.md 整份為英文(要原樣輸出的繁中字面除外);README、AGENTS、templates、references 為 STE100 繁中;UTF-8 無亂碼 - [ ] SKILL.md 整份為英文(要原樣輸出的繁中字面除外);README、AGENTS、templates、references 為 STE100 繁中;UTF-8 無亂碼
- [ ] 所有非程式碼輸出(程式碼註解、commit 訊息、PR 描述、wiki 頁、回報、文件)為繁體中文、UTF-8、無亂碼、無簡體字,且 `tools/ste100-lint.sh` 對該 domain 全綠 - [ ] 所有非程式碼輸出(程式碼註解、commit 訊息、PR 描述、wiki 頁、回報、文件)為繁體中文、UTF-8、無亂碼、無簡體字,且 `tools/ste100-lint.sh` 對該 domain 全綠
- [ ] 已同步更新該 domain 的 README「Skills 目錄」與三份 manifest 的 version - [ ] 已同步更新該 domain 的 README「Skills 目錄」與三份 manifest 的 version
+28 -22
View File
@@ -1,6 +1,6 @@
--- ---
name: skill-check name: skill-check
description: Routine compliance, script, hook, flow-efficiency, and cost-efficiency audit of the whole jsc skill set with no change request in hand. Sync every domain repo from the Gitea canonical marketplace, validate scripts and hook smoke, audit every skill against guidelines.md, then review parallelism, tool extraction, repeated interaction, redundant checks, misplaced gates, and avoidable token, sub-agent, API, scan, or interaction cost. Confirm compliance fixes and optimization suggestions before applying them, re-check until accepted fixes pass, then open a PR per affected repo via jsc-git pr. Use for periodic or on-demand skill-set checks; not for applying a change request (use skillset-update) or editing one skill (use skill-update). description: Routine compliance, script, hook, flow-efficiency, and cost-efficiency audit of the whole jsc skill set with no change request in hand. Sync every domain repo from the Gitea canonical marketplace, then run three parallel groups - lint-scripts.sh plus hook smoke, the guidelines.md checklist audit, and a review of parallelism, tool extraction, repeated interaction, redundant checks, misplaced gates, and avoidable token, sub-agent, API, scan, or interaction cost. Confirm compliance fixes and optimization suggestions before applying them, re-check until accepted fixes pass, then open a PR per affected repo via jsc-git pr. Use for periodic or on-demand skill-set checks; not for applying a change request (use skillset-update) or editing one skill (use skill-update).
--- ---
# skill-check — audit compliance, flow efficiency, and cost efficiency # skill-check — audit compliance, flow efficiency, and cost efficiency
@@ -9,22 +9,26 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
## Flow ## Flow
1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned and exit 1 means the canonical marketplace was unreadable — resolve either before continuing. 1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned. Exit 1 means the root could not be derived, `gitea.sh` was not found, or the canonical marketplace was unreadable; when stderr says the root could not be derived, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos and rerun, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there instead of the domain workspace. Resolve 2 and 1 before continuing.
2. Validate scripts and hooks before reading skill text:
1. For every synced domain repo, run `find {domain-path}/tools {domain-path}/hooks -type f -name '*.sh' -exec sh -n {} \;` for directories that exist. Report each script that fails with path and parser output. Missing `tools/` or `hooks/` directories are not failures. The three review groups of step 2 all read this synced tree, so the sync finishes first.
2. For every shell script directly named by a touched or audited SKILL.md, confirm the script exists, is executable when it is meant to be called directly, and documents or implements every exit code the skill routes. Report evidence as `skill file:line -> script path`. 2. Run the three review groups over the synced repos. They are independent — every one only reads, none writes a file — so **launch all three in parallel** and merge their results in step 3.
3. When the `jsc-hooks` domain is present, run `jsc-hooks/tools/wire-cli.sh smoke {cli}` for every CLI reported by `jsc-cli/tools/detect-clis.sh`. When no CLI is detected, run `jsc-hooks/tools/wire-cli.sh smoke codex` as the minimum hook behavior check and label it 「預設 hook smoke」 in the report. Use `smoke`, not `purge` or rewiring actions.
**Group 1 — validate scripts and hooks.**
1. For every synced domain repo, run `tools/lint-scripts.sh {domain-path}`. One run per domain, and the runs go **in parallel** — no domain's verdict depends on another's. The tool covers three checks in one pass: `sh -n` syntax, executable bit, and an exit-code declaration in the file header. Route each exit code: 0 — the domain's scripts pass all three; 1 — the failing items are printed as `{file}:{check}:{detail}`, so report each one; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the path is missing or the domain has neither `tools/` nor `hooks/`. Record exit 3 as 「無腳本可掃」; a domain with no script directory is not a failure, but exit 3 is **never** a pass.
2. For every shell script directly named by a SKILL.md, confirm the skill routes every exit code the script's header declares. `lint-scripts.sh` proves the script exists and declares its codes; this check is the other half — that the caller branches on each of them. Report evidence as `skill file:line -> script path`.
3. When the `jsc-hooks` domain is present, run `jsc-hooks/tools/wire-cli.sh smoke {cli}` for every CLI reported by `jsc-cli/tools/detect-clis.sh`; the per-CLI smokes run **in parallel**. When no CLI is detected, run `jsc-hooks/tools/wire-cli.sh smoke codex` as the minimum hook behavior check and label it 「預設 hook smoke」 in the report. Use `smoke`, not `purge` or rewiring actions, and set `JSC_READONLY=1` for the whole audit so a mistyped sub-command is refused in code (exit 6) instead of rewiring the machine; `status` and `smoke` are unaffected by that variable. Route each `smoke` exit code: 0 — the run passed its own assertions; 2 — usage error, so fix the CLI code and rerun; 4 — the smoke failed, which includes the script's own result-line count not matching what it expected. **Read the count from the script's `lines<TAB>{數量}` output line; never write the number into this skill.** The script counts its own result lines and asserts them, so a hardcoded number here goes stale the moment a hook or a decision path is added — an out-of-date count in a SKILL.md is exactly what misled the previous audit.
4. When a hook or script smoke fails, route it as a compliance failure with script name, exit code, output summary, and proposed fix. Do not continue to report the affected hook as compliant. 4. When a hook or script smoke fails, route it as a compliance failure with script name, exit code, output summary, and proposed fix. Do not continue to report the affected hook as compliant.
Completion condition: every domain has a script syntax verdict, every named script has an existence and exit-code-routing verdict, and the `jsc-hooks` domain has a hook smoke verdict. **Group 2 — audit every skill of every domain against the guidelines.md audit checklist.** This group MUST run as a sub agent, one sub agent per domain repo, and those sub agents run **in parallel**. Each sub agent reports its findings: skill, failed checklist item, evidence (file:line), proposed fix. Cover the checklist's four flow checks by name, not only the naming and language items:
3. Audit every skill of every domain against the guidelines.md audit checklist — this step MUST run as a sub agent, one sub agent per domain repo. Each sub agent reports its findings: skill, failed checklist item, evidence (file:line), proposed fix. Cover the checklist's four flow checks by name, not only the naming and language items: - Every step number, file path and section title the skill references — inside itself and in other files — really exists (the pointer points at something).
1. Every step number, file path and section title the skill references — inside itself and in other files — really exists (the pointer points at something). - Every step ends in a checkable completion condition, with no vague wording.
2. Every step ends in a checkable completion condition, with no vague wording. - Every external call (script, API, other skill) states what to do on failure and routes every exit code.
3. Every external call (script, API, other skill) states what to do on failure and routes every exit code. - No gate the skill installs blocks the only path that lifts that gate.
4. No gate the skill installs blocks the only path that lifts that gate.
Completion condition: every domain has an audit result that names a verdict for all checklist items, the four flow checks included. Three checklist items are **already decided by group 1** and must not be re-run here: `sh -n` on every `tools/` and `hooks/` script, script existence with the executable bit, and the hook smoke. Tell each sub agent to skip those three and leave them blank; the main agent fills them in from the group 1 verdicts when merging in step 3. Re-scanning the same files in every domain sub agent buys nothing — group 1 already scanned them all, with the same tool, on the same synced tree.
4. Run a separate flow and cost optimization review after the compliance audit. Each aspect **MUST run as a sub agent**, and the six aspects may run in parallel:
**Group 3 — a flow and cost optimization review**, kept separate from the compliance audit. Each aspect **MUST run as a sub agent**, and the six aspects run in parallel with each other and with groups 1 and 2:
| Aspect | Scope | | Aspect | Scope |
| --- | --- | | --- | --- |
@@ -35,19 +39,21 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
| 5 Gate timing | Gates that run too early or too late, causing wasted work before a block or blocking the only path that clears the gate | | 5 Gate timing | Gates that run too early or too late, causing wasted work before a block or blocking the only path that clears the gate |
| 6 Cost efficiency | Avoidable token, sub-agent, API, file-scan, full-repo audit, or user-interaction cost that can be reduced without weakening correctness | | 6 Cost efficiency | Avoidable token, sub-agent, API, file-scan, full-repo audit, or user-interaction cost that can be reduced without weakening correctness |
Each optimization finding reports skill, aspect, evidence (file:line), current flow step count, proposed flow step count, what time or interaction it saves, what cost it saves, current cost driver, proposed cost driver, whether correctness decreases, and which protection would be weakened if any. Cost savings may be token volume, sub-agent count, API calls, file scans, full-repo audits, or user prompts. Keep optimization findings separate from compliance failures. Completion condition: every aspect has returned a verdict for every domain; aspects with no findings return 「無發現」. Each optimization finding reports skill, aspect, evidence (file:line), current flow step count, proposed flow step count, what time or interaction it saves, what cost it saves, current cost driver, proposed cost driver, whether correctness decreases, and which protection would be weakened if any. Cost savings may be token volume, sub-agent count, API calls, file scans, full-repo audits, or user prompts. Keep optimization findings separate from compliance failures.
5. Present compliance failures and optimization findings separately via the `jsc-ask:ask` decision tree:
Completion condition for all three groups: every domain has a `lint-scripts.sh` verdict, every script named by a SKILL.md has an exit-code-routing verdict, and every smoked CLI has a `smoke` exit code plus the `lines` value the script printed for it; every domain has a group 2 audit result that names a verdict for all checklist items — the four flow checks included, and the three group 1 items left blank for the step 3 merge rather than re-scanned; and every one of the six aspects has returned a verdict for every domain, 「無發現」 where an aspect found nothing.
3. Merge the three groups, then present compliance failures and optimization findings separately via the `jsc-ask:ask` decision tree. Merging means one thing in code: fill the three skipped checklist items of every group 2 sub agent report from the matching group 1 verdicts, so each domain ends with one complete checklist and no item counted twice.
- Compliance failure options: apply the proposed fix / skip / custom fix. Every option states its impact scope, for example skipping leaves the skill non-compliant until the next audit. - Compliance failure options: apply the proposed fix / skip / custom fix. Every option states its impact scope, for example skipping leaves the skill non-compliant until the next audit.
- Optimization options: apply / defer / custom. Any suggestion that weakens a protection must name the protection it removes and must not be applied unless the user explicitly accepts that tradeoff. Cost optimization may move, merge, cache, or narrow checks; it must not delete a compliance check only because it is expensive. - Optimization options: apply / defer / custom. Any suggestion that weakens a protection must name the protection it removes and must not be applied unless the user explicitly accepts that tradeoff. Cost optimization may move, merge, cache, or narrow checks; it must not delete a compliance check only because it is expensive.
Completion condition: every compliance failure and every optimization finding has a recorded decision. Completion condition: every domain's checklist is complete after the merge, and every compliance failure and every optimization finding has a recorded decision.
6. Apply the confirmed fixes and accepted optimizations — the file-change part MUST run as a sub agent, one sub agent per affected domain repo: modify the files per the recorded decision. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to refresh that domain README's 「Skills 目錄」 section and bump the version in all three manifests. Completion condition: every affected repo carries the changes and the manifest bump. 4. Apply the confirmed fixes and accepted optimizations — the file-change part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents run **in parallel**: each repo's files are independent. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to refresh that domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected repo carries the changes and the manifest bump.
7. Sync the canonical marketplace — a **required** step, never optional. The canonical pair lives in `plugins/meta` and every domain repo carries a byte-identical copy, so a fix that leaves the copies apart makes some repos register a stale plugin set. Run `tools/sync-marketplace.sh {domain} {repo-url} {description}` once with an existing entry's own current values (rewriting the same entry is idempotent); the script rewrites both canonical files and copies them into every domain repo. Route each exit code: 5. Sync the canonical marketplace — a **required** step, never optional. The canonical pair lives in `plugins/meta` and every domain repo carries a byte-identical copy, so a fix that leaves the copies apart makes some repos register a stale plugin set. Run `tools/sync-marketplace.sh {domain} {repo-url} {description}` once with an existing entry's own current values (rewriting the same entry is idempotent); the script rewrites both canonical files and copies them into every domain repo. Route each exit code:
- Exit 3 — written, but some domain repo is not present locally. Run `tools/sync-domains.sh`, then rerun this step. - Exit 3 — written, but some domain repo is not present locally. Run `tools/sync-domains.sh`, then rerun this step.
- Exit 2 — usage error: the script takes exactly three arguments. Fix them and rerun. - Exit 2 — usage error: the script takes exactly three arguments. Fix them and rerun.
- Exit 1 — missing python3, an unreadable canonical file, or a byte mismatch between copies. Read stderr, fix the named cause (install python3 for the first), then rerun. - Exit 1 — the root could not be derived, python3 is missing, a canonical file was unreadable, or copies differ byte for byte. Read stderr and fix the named cause: install python3 for the second; for the root case set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there. Then rerun.
- Exit 0 — every copy holds identical bytes; the script verifies that itself. - Exit 0 — every copy holds identical bytes; the script verifies that itself.
Completion condition: the script exits 0 and prints the touched paths. Completion condition: the script exits 0 and prints the touched paths.
8. Re-run the script and hook validation from step 2, re-check the guidelines.md audit checklist for every touched skill, then re-run the optimization aspect that produced each accepted optimization. On any compliance failure, **return to step 5**: confirm and fix again, until all accepted compliance fixes pass. On an accepted optimization that does not produce the promised step reduction or cost reduction, or still weakens correctness beyond the recorded decision, return to step 5 for a new decision. Completion condition: all script and hook checks pass, all checklist items pass, and every accepted optimization has a matching verification result. 6. Re-run the group 1 script and hook validation, re-check the guidelines.md audit checklist for every touched skill, then re-run the optimization aspect that produced each accepted optimization. These three re-runs are as independent as the first pass, so run them **in parallel** and merge them the same way step 3 did. On any compliance failure, **return to step 3**: confirm and fix again, until all accepted compliance fixes pass. On an accepted optimization that does not produce the promised step reduction or cost reduction, or still weakens correctness beyond the recorded decision, return to step 3 for a new decision. Completion condition: `tools/lint-scripts.sh` exits 0 or 3 for every domain, every hook smoke exits 0 with the `lines` count the script itself asserted, all checklist items pass, and every accepted optimization has a matching verification result.
9. Call `jsc-git:pr` once per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in `references/pr-report.md`. 7. Call `jsc-git:pr` once per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in [`../../references/pr-report.md`](../../references/pr-report.md).
+16 -19
View File
@@ -1,6 +1,6 @@
--- ---
name: skill-delete name: skill-delete
description: Remove a skill from the jsc skill set safely. Pick the skill from the Gitea canonical marketplace skill list, inventory every file referencing it, fix each affected file through decision-tree questions until guideline checks pass, delete the skill and verify no on-disk leftover in any CLI, open a PR via jsc-git pr, then deploy the deletion into the current session and append the change report to wiki SKILLSET_{HASH}. Use only for removal; not for renaming (use skill-update). description: Remove a skill from the jsc skill set safely. Pick the skill from the Gitea canonical marketplace skill list, inventory every file referencing it, fix each affected file through decision-tree questions in parallel sub agents until guideline checks pass, delete the skill, open a PR via jsc-git pr, then deploy per references/deploy-verify.md and verify from a fresh CLI process that no on-disk leftover remains, and append the change report to wiki SKILLSET_{HASH}. Use only for removal; not for renaming (use skill-update).
--- ---
# skill-delete — delete a skill # skill-delete — delete a skill
@@ -9,28 +9,25 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
## Flow ## Flow
1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned and exit 1 means the canonical marketplace was unreadable — resolve either before continuing. 1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned. Exit 1 means the root could not be derived, `gitea.sh` was not found, or the canonical marketplace was unreadable; when stderr says the root could not be derived, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos and rerun, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there instead of the domain workspace. Resolve 2 and 1 before continuing.
2. Run `tools/list-skills.sh` and present its `domain / name / description` rows to the user. The tool prints skills, not domains, so read the domain column to prove coverage. Completion condition: every domain printed by step 1 appears in at least one row; a domain with no row means its repo is missing or holds no skill — return to step 1 for that domain. 2. Run `tools/list-skills.sh` and present its `domain / name / description` rows to the user. The tool prints skills, not domains, so read the domain column to prove coverage. Exit 1 means the root could not be derived, the domain list was unreadable, or no skill was found — read stderr, fix the named cause (`JSC_PLUGINS_ROOT` for the root case, as in step 1) and rerun; never read it as an empty skill set. Completion condition: the script exits 0 and every domain printed by step 1 appears in at least one row; a domain with no row means its repo is missing or holds no skill — return to step 1 for that domain.
3. Let the user pick the skill to delete. Options state the impact scope: which skills reference it, and that its command stops working after deletion. Completion condition: one `{domain}/{name}` pair is confirmed for deletion. 3. Let the user pick the skill to delete. Options state the impact scope: which skills reference it, and that its command stops working after deletion. Completion condition: one `{domain}/{name}` pair is confirmed for deletion.
4. Inventory every file related to the skill: run `tools/find-skill-refs.sh {domain} {name}` to list every file that references the skill name or its `/jsc-{domain}:{name}` command form, across every marketplace domain repo on this machine (covers other SKILL.md files, the domain README's 「Skills 目錄」 section, the two marketplace.json files in `plugins/meta` plus their synced copies in every domain repo, `tools/`, and the `jsc-hooks` wiring). The skill-name pattern is a bare substring match, so the list also carries other skills whose name starts with the same word plus plain prose — treat it as candidates to read, not as files that must change. Exit 1 means a clean zero-hit scan; exit 3 means the scan failed — fix the root path or the missing domain repos and rerun, never treat it as zero hits. Completion condition: the tool's file list is captured as the step 5 inventory. 4. Inventory every file related to the skill: run `tools/find-skill-refs.sh {domain} {name}` to list every file that references the skill name or its `/jsc-{domain}:{name}` command form, across every marketplace domain repo on this machine (covers other SKILL.md files, the domain README's 「Skills 目錄」 section, the two marketplace.json files in `plugins/meta` plus their synced copies in every domain repo, `tools/`, and the `jsc-hooks` wiring). The skill-name pattern is a bare substring match, so the list also carries other skills whose name starts with the same word plus plain prose — treat it as candidates to read, not as files that must change. Exit 0 means hits were printed; exit 1 means a clean zero-hit scan; exit 2 means a usage error, so fix the two arguments and rerun; exit 3 means the scan never ran — the root could not be derived, the domain list was unreadable, no domain repo is on this machine, or grep failed. Read stderr and fix the named cause; when it names the root, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there. **Never read exit 3 as zero hits** — a failed scan taken as "no references" makes this skill skip files it must fix. Completion condition: the tool's file list is captured as the step 5 inventory.
5. Fix every file in the step 4 inventory. For each file: 5. Fix every file in the step 4 inventory. For each file:
1. Read the file and decide whether it needs a fix to keep its current behavior after the deletion. If no fix is needed, record it as no-fix-needed with the reason and **skip the rest of this loop**. Completion condition: the file carries a recorded verdict — needs-fix or no-fix-needed with a reason. 1. Read the file and decide whether it needs a fix to keep its current behavior after the deletion. If no fix is needed, record it as no-fix-needed with the reason and **skip the rest of this loop**. Completion condition: the file carries a recorded verdict — needs-fix or no-fix-needed with a reason.
2. Ask for fix details via the `jsc-ask:ask` decision tree (call a replacement skill? move a deterministic input/output flow to `tools/`? run the detailed flow as a sub agent? drop the feature too?). If the fix touches wiki or Gitea access, confirm it reads inherited environment variables before asking the user. Every option states its impact scope. Completion condition: every question has a recorded answer. 2. Ask for fix details via the `jsc-ask:ask` decision tree (call a replacement skill? move a deterministic input/output flow to `tools/`? run the detailed flow as a sub agent? drop the feature too?). If the fix touches wiki or Gitea access, confirm it reads inherited environment variables before asking the user. Every option states its impact scope. Completion condition: every question has a recorded answer.
3. Apply the confirmed fix, then check the guidelines.md audit checklist for the file — the per-file fix work MUST run as a sub agent, one sub agent per affected domain repo, each reporting one line per file: the path and either the applied fix or「無需修正」with the reason. On any checklist failure, return to step 5.2. Completion condition: the fix is in the file and every checklist item passes for it. 3. Apply the confirmed fix, then check the guidelines.md audit checklist for the file — the per-file fix work MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents **run in parallel**: each repo's files are independent, so serialising them only adds waiting. Each sub agent reports one line per file: the path and either the applied fix or「無需修正」with the reason. On any checklist failure, return to step 5.2. Completion condition: the fix is in the file and every checklist item passes for it.
Completion condition: every file in the step 4 inventory is marked either fixed-with-a-clean-checklist or explicitly no-fix-needed with a reason — no file is left without a verdict. Completion condition: every file in the step 4 inventory is marked either fixed-with-a-clean-checklist or explicitly no-fix-needed with a reason — no file is left without a verdict.
6. Delete the skill directory `skills/{name}/`, then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README and bump the version in all three manifests. Completion condition: the directory is gone, the README's 「Skills 目錄」 no longer lists the skill, and all three manifests show the same new version. 6. Delete the skill directory `skills/{name}/`, then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a remaining `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the directory is gone, the README's 「Skills 目錄」 no longer lists the skill, and all three manifests show the same new version.
7. Deep-delete verification: after removing the plugin through each CLI's native plugin commands, run `tools/verify-skill-removed.sh {domain} {name}`. It detects the installed CLIs and greps each one's skill cache and hook config for the skill. Route each exit code: 7. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md).
- Exit 1 — leftovers printed as `{file}:{line}:{content}`. Remove every one by hand, then rerun. 8. Deploy the deletion, verify it took, then report:
- Exit 2 — usage error. Fix the two arguments and rerun. 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill set, so verifying inside it either gets blocked or passes on stale behavior. On the worktree route, add that the skill stays installed and stays callable until the outstanding release PR merges. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo.
- Exit 3 — nothing was checkable: no CLI detected, or no config location exists. The script prints no leftover because it looked nowhere, so this is **never** clean. Report「無處可查」with the reason from stderr and stop the deep-delete verification here; state in the PR that on-disk verification did not run. 2. Verify the deletion concretely, on top of the `deploy-verify.md` items:
- Exit 0 — no leftover in the locations listed on stderr. - `tools/list-skills.sh` prints no row carrying the deleted `{domain}/{name}`.
- **Deep-delete verification.** Run `tools/verify-skill-removed.sh {domain} {name}`; it detects the installed CLIs and greps each one's skill cache and hook config. Route each exit code: exit 0 — no leftover in the locations listed on stderr; exit 1 — leftovers printed as `{file}:{line}:{content}`, so remove every one by hand and rerun; exit 2 — usage error, fix the two arguments and rerun; exit 3 — nothing was checkable, because the root could not be derived, `detect-clis.sh` was not found, no CLI was detected, or no config location exists. The script printed no leftover because it looked nowhere, so exit 3 is **never** clean: report「無處可查」with the reason from stderr, and carry that sentence into step 8.3's wiki section and the final report, so nobody later reads the deletion as verified on disk. This is the only place the deep-delete check runs — running it before the PR only scanned a deletion that had not been deployed yet, so it always came back clean and proved nothing.
- Every tool and skill that step 5 fixed still finishes with its documented exit code — a fix that broke a caller shows up here, not earlier.
- One minimal prompt per checkable CLI, each in its own fresh process and all in parallel, confirming `/jsc-{domain}:{name}` is gone or that the replacement path still works.
Completion condition: the script exits 0, or exit 3 is reported to the user and recorded in the PR. On any mismatch — the deleted skill still listed, a leftover from exit 1, a fixed caller that now fails, a prompt failure, or unexpected stderr — fix the cause and rerun this step from 8.1. Completion condition: the skill is absent from the list, the verification script exits 0 or its exit 3 is reported as「無處可查」and carried into step 8.3, every fixed caller ran, every checkable CLI completed the prompt with the expected result, and every untestable CLI has a stated reason.
8. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in `references/pr-report.md`. 3. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent. Call `jsc-gitea:wiki`; `{HASH}` comes from the `{owner}/{repo}` of the domain repo that lost the skill, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「刪除」, skill name, changed files (the step 5 inventory verdicts included), PR URL, the step 8.1 route verdict and the step 8.2 verification result per item, the deep-delete verdict「無處可查」included when it applies — and keep every earlier section. Add the page to `SKILLSET_CONTENTS` when it is new. When the write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: the page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links it.
9. Apply the deletion to the current working session, verify it took, then report:
1. Force the change into the session — which of the two routes applies depends on where the change has reached, because the marketplace and `version-guard.sh` both read the repository's **default branch** (`master`), so `jsc-cli:deploy` cannot see anything that stopped at `develop`:
- The PR is merged all the way to `master`: call `jsc-cli:deploy` in update mode so every installed CLI loads the version without the skill, and restart the CLI when it asks (the deploy writes `$JSC_HOME/restart-required`; see guidelines.md「部署後重啟閘門」). When the deploy cannot update a CLI, record which CLIs did load the new version and carry on with one of those; when none did, stop and report the deletion as unverified. Completion condition: `claude plugin list` (or the equivalent command of another installed CLI) prints `jsc-{domain}` at the version the three manifests now carry.
- The PR is still short of `master` (waiting on review, or merged only into `develop`): deploying is pointless and its completion condition is unreachable, so verify against the **worktree** instead — run the next sub-step against `/root/plugins/{domain}` rather than the installed copy, mark the report as 「工作樹驗證、尚未部署」, and say plainly which release PR still has to merge before the deletion reaches any CLI. Until it merges the skill is still installed and still callable, so say that too. Completion condition: the verification sub-step passed against the worktree and the outstanding release PR is named in the report.
2. Verify the function concretely: run `tools/list-skills.sh` and confirm no row carries the deleted `{domain}/{name}`, rerun `tools/verify-skill-removed.sh {domain} {name}` for exit 0, then run every tool and skill that step 5 fixed and confirm each still finishes with its documented exit code — a fix that broke a caller shows up here, not earlier. 用 `jsc-cli/tools/detect-clis.sh` 偵測已安裝的測試環境 CLI;每個支援非互動 Prompt 的 CLI 都要實際送出一個最小 Prompt,確認 `/jsc-{domain}:{name}` 已消失,或確認替代路徑仍可用,並記錄 CLI 結束碼與 stderr。Prompt 若因 CLI 工具、plugin 安裝、hook 接線、模型標籤表或設定而失敗,先跑 `jsc-cli:doctor`,再用 `jsc-cli:setup` 修復待修項目,然後重跑同一個 Prompt。hook 冒煙測試若失敗,交給 `jsc-hooks:hooks-install`,由它把 hook 接線或執行期錯誤轉給 `jsc-hooks:repair`。若根因是技能組本身的規格、工具或 hook 實作,修正對應 domain,跑 manifest 同步與驗證,然後用 `jsc-git:pr develop` 開 PR;PR 送出後回到本步驟重跑同一個 Prompt。只有每個可測 CLI Prompt 都得到預期結果且沒有非預期 stderr,或該 CLI 以明確原因列為不可測,才能停止。On any mismatch — the deleted skill still listed, a leftover from exit 1, a fixed caller that now fails, a prompt failure, or unexpected stderr — fix the cause and rerun this step from 9.1. Completion condition: the skill is absent from the list, the verification script exits 0 (or its exit 3 stays reported as「無處可查」), every fixed caller ran, every checkable test-environment CLI completed the prompt with the expected result, and every untestable CLI has a stated reason.
3. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent. Call `jsc-gitea:wiki`; `{HASH}` comes from the `{owner}/{repo}` of the domain repo that lost the skill, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「刪除」, skill name, changed files (the step 5 inventory verdicts included), PR URL, the step 9.2 verification result per item — and keep every earlier section. Add the page to `SKILLSET_CONTENTS` when it is new. When the write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: the page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links it.
+16 -13
View File
@@ -1,6 +1,6 @@
--- ---
name: skill-new name: skill-new
description: Create a new skill in the jsc skill set. Ask skill details via decision tree, generate the skill under the right jsc-{domain} per guidelines.md (create the domain from the template repo if missing), open a PR via jsc-git pr, then deploy the skill into the current session, verify it runs, and append the change report to wiki SKILLSET_{HASH}. Use when the user wants to add a skill; not for editing an existing one (use skill-update). description: Create a new skill in the jsc skill set. Prefetch the skill list and the domain list in parallel, ask skill details via decision tree, generate the skill under the right jsc-{domain} per guidelines.md (create the domain from the template repo if missing), open a PR via jsc-git pr, then deploy and verify per references/deploy-verify.md from a fresh CLI process, and append the change report to wiki SKILLSET_{HASH}. Use when the user wants to add a skill; not for editing an existing one (use skill-update).
--- ---
# skill-new — create a skill # skill-new — create a skill
@@ -9,11 +9,17 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
## Flow ## Flow
1. Ask for skill details via the `jsc-ask:ask` decision tree until no doubt remains: 1. Collect the decision tree's inputs first, then ask:
- Goal (single and not duplicating an existing skill; run `tools/list-skills.sh` first and show the similar skills for comparison — options must state the impact scope of "reuse existing" versus "create new") 1. Prefetch both inputs — run `tools/list-skills.sh` and `tools/sync-domains.sh` **in parallel**. They share no data, and both answers are needed before the first question, so running them after the questions only makes the user wait. Route each exit code:
- `list-skills.sh` exit 0 — keep the `domain<TAB>name<TAB>description` rows. Exit 1 — the root could not be derived, the domain list was unreadable, or no skill was found; read stderr and fix the named cause. When stderr says the root could not be derived, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos and rerun: under a plugin install the script sits in the CLI's plugin cache, so its built-in guess lands in that cache instead of the domain workspace.
- `sync-domains.sh` exit 0 — the only code that means every repo is present and current; keep the `domain<TAB>path` rows. Exit 3 — some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version. Exit 2 — a domain could not be cloned. Exit 1 — the root could not be derived, `gitea.sh` was not found, or the canonical marketplace was unreadable; for the root case set `JSC_PLUGINS_ROOT` as above and rerun. Resolve 2 and 1 before continuing.
Completion condition: the skill rows and the `domain<TAB>path` rows are both in hand.
2. Ask for skill details via the `jsc-ask:ask` decision tree until no doubt remains:
- Goal (single and not duplicating an existing skill; show the similar skills from the step 1.1 rows for comparison — options must state the impact scope of "reuse existing" versus "create new")
- Trigger (when to use, when not to, trigger keywords) - Trigger (when to use, when not to, trigger keywords)
- Input and output (can a standard input/output flow move down to `tools/`; does it need Gitea operations — if so, make the skill use `jsc-gitea/tools/gitea.sh` + token) - Input and output (can a standard input/output flow move down to `tools/`; does it need Gitea operations — if so, make the skill use `jsc-gitea/tools/gitea.sh` + token)
- Owning domain (run `tools/sync-domains.sh` and offer its domain list — the domains registered in the canonical marketplace) - Owning domain (offer the domain list from the step 1.1 `domain<TAB>path` rows — the domains registered in the canonical marketplace)
Completion condition: goal, trigger, input/output and owning domain each have a recorded answer. Completion condition: goal, trigger, input/output and owning domain each have a recorded answer.
2. If the domain does not exist (`tools/sync-domains.sh` clones every domain **registered in the marketplace**, so a missing directory means the domain is unregistered — the repository itself may already exist on Gitea): 2. If the domain does not exist (`tools/sync-domains.sh` clones every domain **registered in the marketplace**, so a missing directory means the domain is unregistered — the repository itself may already exist on Gitea):
@@ -23,7 +29,7 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
4. Register the plugin: run `tools/sync-marketplace.sh {domain} {repo-url} {description}`. It needs `python3` on PATH — it edits the marketplace JSON with the json module. It writes the entry into both canonical marketplace files in `plugins/meta` and copies both into every domain repo, so any repo works as the registration entry point. Route each exit code: 4. Register the plugin: run `tools/sync-marketplace.sh {domain} {repo-url} {description}`. It needs `python3` on PATH — it edits the marketplace JSON with the json module. It writes the entry into both canonical marketplace files in `plugins/meta` and copies both into every domain repo, so any repo works as the registration entry point. Route each exit code:
- Exit 3 — written, but some domain repo is not present locally. Run `tools/sync-domains.sh`, then rerun this step. - Exit 3 — written, but some domain repo is not present locally. Run `tools/sync-domains.sh`, then rerun this step.
- Exit 2 — usage error. Fix the three arguments and rerun. - Exit 2 — usage error. Fix the three arguments and rerun.
- Exit 1 — missing python3, an unreadable canonical file, or a byte mismatch between copies. Read stderr, fix the named cause (install python3 for the first), then rerun. - Exit 1 — the root could not be derived, python3 is missing, a canonical file was unreadable, or copies differ byte for byte. Read stderr and fix the named cause: install python3 for the second; for the root case set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there. Then rerun.
- Exit 0 — every copy holds identical bytes; the script verifies that itself. - Exit 0 — every copy holds identical bytes; the script verifies that itself.
Completion condition: the script exits 0 and prints the touched paths. Completion condition: the script exits 0 and prints the touched paths.
@@ -31,12 +37,9 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
- `skills/{name}/SKILL.md`: entirely in English (description within either cap — ≤ 5 sentences or ≤ 5 steps — and stating when to use and when not to; body in STE100-style English) - `skills/{name}/SKILL.md`: entirely in English (description within either cap — ≤ 5 sentences or ≤ 5 steps — and stating when to use and when not to; body in STE100-style English)
- Rules enforceable by hooks go to `jsc-hooks` (never scattered in this domain); standard input/output flows go to `tools/` - Rules enforceable by hooks go to `jsc-hooks` (never scattered in this domain); standard input/output flows go to `tools/`
Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Completion condition: `skills/{name}/SKILL.md` exists, the README lists the skill, and all three manifests show the same new version. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: `skills/{name}/SKILL.md` exists, the README lists the skill, and all three manifests show the same new version.
4. Self-check every item of the guidelines.md audit checklist; fix anything that fails. Completion condition: every checklist item passes. 4. Self-check every item of the guidelines.md audit checklist; fix anything that fails. Completion condition: every checklist item passes.
5. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in `references/pr-report.md`. 5. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md).
6. Apply the new skill to the current working session, verify it works, then report: 6. Deploy the new skill, verify it runs, then report:
1. Force the change into the session — which of the two routes applies depends on where the change has reached, because the marketplace and `version-guard.sh` both read the repository's **default branch** (`master`), so `jsc-cli:deploy` cannot see anything that stopped at `develop`: 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill body, so verifying inside it either gets blocked or passes on stale behavior. Verify the added skill's row in `tools/list-skills.sh`, every tool the skill added, and one minimal prompt per checkable CLI — the per-CLI prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo.
- The PR is merged all the way to `master`: call `jsc-cli:deploy` in update mode so every installed CLI loads the new version, and restart the CLI when it asks (the deploy writes `$JSC_HOME/restart-required`; see guidelines.md「部署後重啟閘門」). When the deploy cannot update a CLI, record which CLIs did load the new version and carry on with one of those; when none did, stop and report the change as unverified. Completion condition: `claude plugin list` (or the equivalent command of another installed CLI) prints `jsc-{domain}` at the version the three manifests now carry. 2. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent. Call `jsc-gitea:wiki`; `{HASH}` comes from the `{owner}/{repo}` of the domain repo that gained the skill, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「新增」, skill name, changed files, PR URL, the step 6.1 route verdict and verification result per item — and keep every earlier section. Add the page to `SKILLSET_CONTENTS` when it is new. When the write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: the page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links it.
- The PR is still short of `master` (waiting on review, or merged only into `develop`): deploying is pointless and its completion condition is unreachable, so verify against the **worktree** instead — run step 6.2 against `/root/plugins/{domain}` rather than the installed copy, mark the report in 6.3 as 「工作樹驗證、尚未部署」, and say plainly which release PR still has to merge before the change reaches any CLI. Completion condition: step 6.2 passed against the worktree and the outstanding release PR is named in the report.
2. Verify the function concretely: run `tools/list-skills.sh` and see the `{domain}<TAB>{name}` row for the new skill, then run every tool the skill added with real arguments and compare each exit code against its documented meaning. Invoke `/jsc-{domain}:{name}` once and confirm the CLI loads the SKILL.md body instead of reporting an unknown command. 用 `jsc-cli/tools/detect-clis.sh` 偵測已安裝的測試環境 CLI;每個支援非互動 Prompt 的 CLI 都要實際送出一個最小 Prompt,呼叫 `/jsc-{domain}:{name}`,並記錄 CLI 結束碼與 stderr。Prompt 若因 CLI 工具、plugin 安裝、hook 接線、模型標籤表或設定而失敗,先跑 `jsc-cli:doctor`,再用 `jsc-cli:setup` 修復待修項目,然後重跑同一個 Prompt。hook 冒煙測試若失敗,交給 `jsc-hooks:hooks-install`,由它把 hook 接線或執行期錯誤轉給 `jsc-hooks:repair`。若根因是技能組本身的規格、工具或 hook 實作,修正對應 domain,跑 manifest 同步與驗證,然後用 `jsc-git:pr develop` 開 PR;PR 送出後回到本步驟重跑同一個 Prompt。只有每個可測 CLI Prompt 都沒有非預期 stderr,或該 CLI 以明確原因列為不可測,才能停止。On any mismatch — a missing row, an exit code the tool's own documentation does not describe, an unknown command, a prompt failure, or unexpected stderr — fix the cause and rerun this step from 6.1. Completion condition: the row is printed, every added tool ran with an expected exit code, the command loaded, every checkable test-environment CLI completed the prompt without unexpected stderr, and every untestable CLI has a stated reason.
3. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent. Call `jsc-gitea:wiki`; `{HASH}` comes from the `{owner}/{repo}` of the domain repo that gained the skill, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「新增」, skill name, changed files, PR URL, the step 6.2 verification result per item — and keep every earlier section. Add the page to `SKILLSET_CONTENTS` when it is new. When the write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: the page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links it.
+8 -11
View File
@@ -1,6 +1,6 @@
--- ---
name: skill-update name: skill-update
description: Update one existing skill in the jsc skill set. List all skills from the Gitea canonical marketplace (cloning any missing domain repo) and let the user pick one, ask update details via decision tree, apply the change, re-check against the guidelines checklist until it passes, open a PR via jsc-git pr, then deploy the change into the current session, verify it runs, and append the change report to wiki SKILLSET_{HASH}. Use for modifying a single skill; not for creating (skill-new), not for removing (skill-delete), and not for a change spanning several skills or domains (skillset-update). description: Update one existing skill in the jsc skill set. List all skills from the Gitea canonical marketplace (cloning any missing domain repo) and let the user pick one, ask update details via decision tree, apply the change, re-check against the guidelines checklist until it passes, open a PR via jsc-git pr, then deploy and verify per references/deploy-verify.md from a fresh CLI process, and append the change report to wiki SKILLSET_{HASH}. Use for modifying a single skill; not for creating (skill-new), not for removing (skill-delete), and not for a change spanning several skills or domains (skillset-update).
--- ---
# skill-update — update a skill # skill-update — update a skill
@@ -9,16 +9,13 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
## Flow ## Flow
1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned and exit 1 means the canonical marketplace was unreadable — resolve either before continuing. 1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned. Exit 1 means the root could not be derived, `gitea.sh` was not found, or the canonical marketplace was unreadable; when stderr says the root could not be derived, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos and rerun, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there instead of the domain workspace. Resolve 2 and 1 before continuing.
2. Run `tools/list-skills.sh` and present its `domain / name / description` rows to the user. The tool prints skills, not domains, so read the domain column to prove coverage. Completion condition: every domain printed by step 1 appears in at least one row; a domain with no row means its repo is missing or holds no skill — return to step 1 for that domain. 2. Run `tools/list-skills.sh` and present its `domain / name / description` rows to the user. The tool prints skills, not domains, so read the domain column to prove coverage. Exit 1 means the root could not be derived, the domain list was unreadable, or no skill was found — read stderr, fix the named cause (`JSC_PLUGINS_ROOT` for the root case, as in step 1) and rerun; never read it as an empty skill set. Completion condition: the script exits 0 and every domain printed by step 1 appears in at least one row; a domain with no row means its repo is missing or holds no skill — return to step 1 for that domain.
3. Let the user pick the skill to update. Completion condition: one `{domain}/{name}` pair is confirmed. 3. Let the user pick the skill to update. Completion condition: one `{domain}/{name}` pair is confirmed.
4. Ask for update details via the `jsc-ask:ask` decision tree (change the goal? the trigger? the flow? move rules down to a hook or a tool?). Every option states its impact scope (example: renaming breaks the existing invocation command). Completion condition: every question has a recorded answer. 4. Ask for update details via the `jsc-ask:ask` decision tree (change the goal? the trigger? the flow? move rules down to a hook or a tool?). Every option states its impact scope (example: renaming breaks the existing invocation command). Completion condition: every question has a recorded answer.
5. Update the skill — the modification part MUST run as a sub agent: modify SKILL.md and related files. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Completion condition: the skill files carry the change and all three manifests show the same new version. 5. Update the skill — the modification part MUST run as a sub agent: modify SKILL.md and related files. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the skill files carry the change and all three manifests show the same new version.
6. Check every item of the guidelines.md audit checklist. On any failure, **return to step 4**: ask again and fix, until all items pass. Completion condition: every checklist item passes. 6. Check every item of the guidelines.md audit checklist. On any failure, **return to step 4**: ask again and fix, until all items pass. Completion condition: every checklist item passes.
7. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in `references/pr-report.md`. 7. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md).
8. Apply the update to the current working session, verify it works, then report: 8. Deploy the update, verify it runs, then report:
1. Force the change into the session — which of the two routes applies depends on where the change has reached, because the marketplace and `version-guard.sh` both read the repository's **default branch** (`master`), so `jsc-cli:deploy` cannot see anything that stopped at `develop`: 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill body, so verifying inside it either gets blocked or passes on stale behavior. Verify the updated `description` in the skill's `tools/list-skills.sh` row, every tool this change touched, and one minimal prompt per checkable CLI — the per-CLI prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo.
- The PR is merged all the way to `master`: call `jsc-cli:deploy` in update mode so every installed CLI loads the new version, and restart the CLI when it asks (the deploy writes `$JSC_HOME/restart-required`; see guidelines.md「部署後重啟閘門」). When the deploy cannot update a CLI, record which CLIs did load the new version and carry on with one of those; when none did, stop and report the change as unverified. Completion condition: `claude plugin list` (or the equivalent command of another installed CLI) prints `jsc-{domain}` at the version the three manifests now carry. 2. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent. Call `jsc-gitea:wiki`; `{HASH}` comes from the `{owner}/{repo}` of the changed domain repo, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「更新」, skill name, changed files, PR URL, the step 8.1 route verdict and verification result per item — and keep every earlier section. Add the page to `SKILLSET_CONTENTS` when it is new. When the write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: the page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links it.
- The PR is still short of `master` (waiting on review, or merged only into `develop`): deploying is pointless and its completion condition is unreachable, so verify against the **worktree** instead — run the next sub-step against `/root/plugins/{domain}` rather than the installed copy, mark the report as 「工作樹驗證、尚未部署」, and say plainly which release PR still has to merge before the change reaches any CLI. Completion condition: the verification sub-step passed against the worktree and the outstanding release PR is named in the report.
2. Verify the function concretely: run `tools/list-skills.sh` and see the updated `description` in the skill's row, then run every tool this change touched with real arguments and compare each exit code against its documented meaning. Invoke `/jsc-{domain}:{name}` once and confirm the CLI loads the changed SKILL.md body. 用 `jsc-cli/tools/detect-clis.sh` 偵測已安裝的測試環境 CLI;每個支援非互動 Prompt 的 CLI 都要實際送出一個最小 Prompt,呼叫 `/jsc-{domain}:{name}`,並記錄 CLI 結束碼與 stderr。Prompt 若因 CLI 工具、plugin 安裝、hook 接線、模型標籤表或設定而失敗,先跑 `jsc-cli:doctor`,再用 `jsc-cli:setup` 修復待修項目,然後重跑同一個 Prompt。hook 冒煙測試若失敗,交給 `jsc-hooks:hooks-install`,由它把 hook 接線或執行期錯誤轉給 `jsc-hooks:repair`。若根因是技能組本身的規格、工具或 hook 實作,修正對應 domain,跑 manifest 同步與驗證,然後用 `jsc-git:pr develop` 開 PR;PR 送出後回到本步驟重跑同一個 Prompt。只有每個可測 CLI Prompt 都沒有非預期 stderr,或該 CLI 以明確原因列為不可測,才能停止。On any mismatch — a stale row, an exit code the tool's own documentation does not describe, an unchanged body, a prompt failure, or unexpected stderr — fix the cause and rerun this step from 8.1. Completion condition: the row shows the new description, every touched tool ran with an expected exit code, the command loaded, every checkable test-environment CLI completed the prompt without unexpected stderr, and every untestable CLI has a stated reason.
3. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent. Call `jsc-gitea:wiki`; `{HASH}` comes from the `{owner}/{repo}` of the changed domain repo, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「更新」, skill name, changed files, PR URL, the step 8.2 verification result per item — and keep every earlier section. Add the page to `SKILLSET_CONTENTS` when it is new. When the write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: the page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links it.
+12 -12
View File
@@ -1,6 +1,6 @@
--- ---
name: skillset-update name: skillset-update
description: Apply one change request across the whole jsc skill set — multiple skills in multiple domains in one pass. Ask the change details via decision tree, sync every domain repo from the Gitea canonical marketplace, apply the change per affected domain via sub agents, re-check against the guidelines checklist until it passes, open a PR per affected repo via jsc-git pr, then deploy the change into the current session, verify it runs, and append the change report to wiki SKILLSET_{HASH}. Use when a change spans multiple skills or domains; not for a single skill (use skill-update). description: Apply one change request across the whole jsc skill set — multiple skills in multiple domains in one pass. Sync every domain repo from the Gitea canonical marketplace while the decision tree asks the change details, apply the change per affected domain via parallel sub agents, re-check against the guidelines checklist until it passes, open a PR per affected repo via jsc-git pr, then deploy and verify per references/deploy-verify.md from a fresh CLI process, and append the change report to wiki SKILLSET_{HASH}. Use when a change spans multiple skills or domains; not for a single skill (use skill-update).
--- ---
# skillset-update — apply one change across the skill set # skillset-update — apply one change across the skill set
@@ -9,14 +9,14 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
## Flow ## Flow
1. Ask for the change details via the `jsc-ask:ask` decision tree: what rule or behavior changes, which skills and which domains are affected. Include three required checks before the affected-skill list is final: whether any deterministic input/output flow must move to `tools/`, whether any detailed flow must run as a sub agent, and whether any wiki or Gitea flow must read inherited environment variables before asking the user. Every option states its impact scope (example: changing a shared flow step touches every skill that calls it). Completion condition: the affected-skill list and the three checks are agreed with the user. 1. Start `tools/sync-domains.sh` and the change-details decision tree **in parallel** — the sync touches no answer the tree needs, and the tree's answers change nothing the sync does, so waiting for one before the other only adds idle time.
2. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints one `domain<TAB>path` line per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned and exit 1 means the canonical marketplace was unreadable — resolve either before continuing. 1. Run `tools/sync-domains.sh` to sync every domain repo of the Gitea canonical marketplace. Exit 0 is the only code that means every repo is present and current; keep the `domain<TAB>path` rows. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned. Exit 1 means the root could not be derived, `gitea.sh` was not found, or the canonical marketplace was unreadable; when stderr says the root could not be derived, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos and rerun, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there instead of the domain workspace. Resolve 2 and 1 before continuing.
3. Apply the change to every affected skill — the modification part MUST run as a sub agent, one sub agent per affected domain repo: modify SKILL.md and related files and tools. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to sync that domain README's 「Skills 目錄」 section and bump the version in all three manifests. Completion condition: every affected domain repo carries the change, the README sync, and the manifest bump. 2. Ask for the change details via the `jsc-ask:ask` decision tree: what rule or behavior changes, which skills and which domains are affected. Include three required checks before the affected-skill list is final: whether any deterministic input/output flow must move to `tools/`, whether any detailed flow must run as a sub agent, and whether any wiki or Gitea flow must read inherited environment variables before asking the user. Every option states its impact scope (example: changing a shared flow step touches every skill that calls it). These three are a shaping guardrail asked before any file is touched; keep asking them even when a later step would catch the same problem.
4. Check every item of the guidelines.md audit checklist for each touched skill. On any failure, **return to step 1**: ask again and fix, until all items pass. Completion condition: every checklist item passes for every touched skill.
5. Call `jsc-git:pr` once per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in `references/pr-report.md`. Completion condition: the `domain<TAB>path` rows are in hand, and the affected-skill list plus the three checks are agreed with the user.
6. Apply the batch change to the current working session, verify it works, then report: 2. Apply the change to every affected skill — the modification part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents **run in parallel**: each repo's files are independent. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) for each affected domain repo to sync that domain README's 「Skills 目錄」 section and bump the version in all three manifests; these runs are independent per repo and may also go in parallel. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected domain repo carries the change, the README sync, and the manifest bump.
1. Force the change into the session — which of the two routes applies depends on where the change has reached, because the marketplace and `version-guard.sh` both read each repository's **default branch** (`master`), so `jsc-cli:deploy` cannot see anything that stopped at `develop`: 3. Check every item of the guidelines.md audit checklist for each touched skill — one sub agent per affected domain repo, run in parallel. On any failure, **return to step 1.2**: ask again and fix, until all items pass. Completion condition: every checklist item passes for every touched skill.
- Every affected repo's PR is merged all the way to `master`: call `jsc-cli:deploy` in update mode so every installed CLI loads the new version of **every** affected plugin, and restart the CLI when it asks (the deploy writes `$JSC_HOME/restart-required`; see guidelines.md「部署後重啟閘門」). When the deploy cannot update a CLI, record which CLIs did load the new versions and carry on with one of those; when none did, stop and report the change as unverified. Completion condition: `claude plugin list` (or the equivalent command of another installed CLI) prints every affected `jsc-{domain}` at the version its three manifests now carry. 4. Call `jsc-git:pr` once per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in [`../../references/pr-report.md`](../../references/pr-report.md).
- Any affected repo is still short of `master` (waiting on review, or merged only into `develop`): deploying is pointless and its completion condition is unreachable, so verify against the **worktree** instead — run the next sub-step against each `/root/plugins/{domain}` rather than the installed copies, mark the report as 「工作樹驗證、尚未部署」, and list every release PR that still has to merge before the change reaches any CLI. A change spanning several repos reaches the CLIs only when the last of them merges, so name them all. Completion condition: the verification sub-step passed against every affected worktree and every outstanding release PR is named in the report. 5. Deploy the batch change, verify it runs, then report:
2. Verify the function concretely: run `tools/list-skills.sh` and check the row of every touched skill against the change, then run every tool this change touched with real arguments and compare each exit code against its documented meaning. Invoke one touched skill per affected domain as `/jsc-{domain}:{name}` and confirm the CLI loads the changed SKILL.md body. 用 `jsc-cli/tools/detect-clis.sh` 偵測已安裝的測試環境 CLI;每個支援非互動 Prompt 的 CLI,都要對每個受影響 domain 實際送出一個最小 Prompt,呼叫一個被異動的技能,並記錄 CLI 結束碼與 stderr。Prompt 若因 CLI 工具、plugin 安裝、hook 接線、模型標籤表或設定而失敗,先跑 `jsc-cli:doctor`,再用 `jsc-cli:setup` 修復待修項目,然後重跑同一個 Prompt。hook 冒煙測試若失敗,交給 `jsc-hooks:hooks-install`,由它把 hook 接線或執行期錯誤轉給 `jsc-hooks:repair`。若根因是技能組本身的規格、工具或 hook 實作,修正對應 domain,跑 manifest 同步與驗證,然後用 `jsc-git:pr develop` 開 PR;PR 送出後回到本步驟重跑同一個 Prompt。只有每個可測 CLI Prompt 都沒有非預期 stderr,或該 CLI 以明確原因列為不可測,才能停止。On any mismatch — a stale row, an exit code the tool's own documentation does not describe, an unchanged body, a prompt failure, or unexpected stderr — fix the cause and rerun this step from 6.1. Completion condition: every touched skill's row matches the change, every touched tool ran with an expected exit code, one command per affected domain loaded, every checkable test-environment CLI completed the prompt without unexpected stderr, and every untestable CLI has a stated reason. 1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5, once per affected domain repo — the route judgements run in parallel. The batch takes the deploy route only when **every** affected repo's `tools/deploy-route.sh` exits 0; a single exit 3 puts the whole batch on the worktree route, because the change reaches the CLIs only when the last repo merges, so name every outstanding release PR. The verification then runs in a **fresh CLI process**, never in the session that ran the deploy: that session raised the restart gate itself and still holds the old skill bodies. Verify every touched skill's row in `tools/list-skills.sh`, every tool this change touched, and one minimal prompt per affected domain per checkable CLI — the per-CLI and per-domain prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for every affected domain repo.
3. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent, one sub agent per affected domain repo. Call `jsc-gitea:wiki`; `{HASH}` comes from that repo's `{owner}/{repo}`, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「批次更新」, the change request in one line, touched skills, changed files, PR URL, the step 6.2 verification result per item — and keep every earlier section. Add each page to `SKILLSET_CONTENTS` when it is new. When a write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: every affected repo's page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links them all. 2. Write the change report to wiki page `SKILLSET_{HASH}` — this part MUST run as a sub agent, one sub agent per affected domain repo, run in parallel. Call `jsc-gitea:wiki`; `{HASH}` comes from that repo's `{owner}/{repo}`, and the wiki repo resolves through `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. **Append** a section for this change — date, 「批次更新」, the change request in one line, touched skills, changed files, PR URL, the step 5.1 route verdict and verification result per item — and keep every earlier section. Add each page to `SKILLSET_CONTENTS` when it is new. When a write fails — no `{owner}/{repo}` resolves, or `jsc-gitea:wiki` reports an API error — hand the page name and the unwritten entry back to the user and leave this step open; never close the flow on an unwritten report. Completion condition: every affected repo's page holds the new section plus all earlier sections, and `SKILLSET_CONTENTS` links them all.
+13 -11
View File
@@ -1,6 +1,6 @@
--- ---
name: ste100-sync name: ste100-sync
description: Sync the STE100 language rules with upstream speak-human-tw. Compare the pinned upstream version in references/ste100.md against the latest release, distill applicable changes and confirm each one via decision tree, refresh ste100-lint.sh patterns, re-lint all jsc repos, then open a PR via jsc-git pr. Use on periodic maintenance or when upstream releases a new version; not for editing local-only rules. description: Sync the STE100 language rules with upstream speak-human-tw. Compare the pinned upstream version in references/ste100.md against the raw upstream frontmatter version before cloning anything, distill applicable changes and confirm each one via decision tree, refresh ste100-lint.sh patterns and jsc-hooks simplified.txt, re-lint all jsc repos in parallel, then open a PR via jsc-git pr. Use on periodic maintenance or when upstream releases a new version; not for editing local-only rules.
--- ---
# ste100-sync # ste100-sync
@@ -9,22 +9,24 @@ Keep `references/ste100.md` in sync with its upstream source, [speak-human-tw](h
## Steps ## Steps
1. Read the pinned version from the「上游版本」line in `references/ste100.md`. Completion condition: the pinned version string is in hand. 1. Compare versions before fetching anything large — the common case is that upstream has no new release, and a clone done first is then wasted every time:
2. Clone the upstream repo (`--depth 1`). Read the `version` and `changelog` fields in its `SKILL.md` frontmatter. Completion condition: the upstream version string and its changelog entries are in hand. 1. Read the pinned version from the「上游版本」line in `references/ste100.md`. Completion condition: the pinned version string is in hand.
3. Same version: report「上游沒有新版」and stop. Completion condition: either the run stops here, or the upstream version is newer than the pinned one. 2. Read the upstream `version` from the raw `SKILL.md` frontmatter over HTTPS, without cloning. When the raw read fails — network error, a moved path, or no `version` line in the frontmatter — fall back to the `--depth 1` clone and read the same field from the working copy. Completion condition: the upstream version string is in hand, and the report names which route produced it, raw or clone.
4. Newer version — distill the changes into a change list. **MUST run as a sub agent**: 3. Same version: report「上游沒有新版」and stop, without cloning. Completion condition: either the run stops here, or the upstream version is newer than the pinned one.
2. Newer version — get the changelog. Clone the upstream repo (`--depth 1`) when step 1.2 did not already clone it, and read the `changelog` field in its `SKILL.md` frontmatter. Completion condition: the changelog entries newer than the pinned version are in hand.
3. Distill the changes into a change list. **MUST run as a sub agent**:
- Walk the changelog entries newer than the pinned version. - Walk the changelog entries newer than the pinned version.
- Keep only changes that apply to technical documents and conversation: Taiwan term replacements, punctuation rules, de-AI patterns, humanize targets. - Keep only changes that apply to technical documents and conversation: Taiwan term replacements, punctuation rules, de-AI patterns, humanize targets.
- Drop marketing-copy scenes, eval material, and workflow-mode changes. - Drop marketing-copy scenes, eval material, and workflow-mode changes.
- Decide nothing and edit no file. Report one line per candidate change: the rule, the upstream wording, and what it would change in `references/ste100.md` or in the lint patterns. - Decide nothing and edit no file. Report one line per candidate change: the rule, the upstream wording, and what it would change in `references/ste100.md` or in the lint patterns.
Completion condition: every kept changelog entry appears as one line in the distilled list. Completion condition: every kept changelog entry appears as one line in the distilled list.
5. Present the distilled list via the `jsc-ask:ask` decision tree, one question per change (adopt / drop / adapt). Every option states its impact scope (example: adopting a term replacement changes the `TERMS` pattern, so every repo re-linted in step 8 can gain new hits). `references/ste100.md` is the single source of truth for the whole skill set, so no change lands without a recorded decision. Completion condition: every distilled change has a recorded decision. 4. Present the distilled list via the `jsc-ask:ask` decision tree, one question per change (adopt / drop / adapt). Every option states its impact scope (example: adopting a term replacement changes the `TERMS` pattern, so every repo re-linted in step 7 can gain new hits). `references/ste100.md` is the single source of truth for the whole skill set, so no change lands without a recorded decision. Completion condition: every distilled change has a recorded decision.
6. Apply the adopted and adapted changes to `references/ste100.md`. Keep its trimmed structure. Update the「上游版本」line. Completion condition: every adopted change is visible in the file and the「上游版本」line shows the new upstream version. 5. Apply the adopted and adapted changes to `references/ste100.md`. Keep its trimmed structure. Update the「上游版本」line. Completion condition: every adopted change is visible in the file and the「上游版本」line shows the new upstream version.
7. If the replacement table or the cliché list changed, update the `TERMS`, `CLICHES` and `SIMPLIFIED` patterns in `tools/ste100-lint.sh`. Completion condition: `sh -n tools/ste100-lint.sh` passes and each newly adopted term hits on a test string. 6. If the replacement table or the cliché list changed, update the `TERMS`, `CLICHES` and `SIMPLIFIED_FALLBACK` patterns in `tools/ste100-lint.sh`. `SIMPLIFIED` is the runtime variable the lint builds from the shared character table, not an editable pattern: the real source is `jsc-hooks/hooks/simplified.txt`, which `ste100-guard.sh` reads too, and `SIMPLIFIED_FALLBACK` is only the built-in backup for machines without `jsc-hooks`. A simplified-character change therefore lands in `simplified.txt` first and in `SIMPLIFIED_FALLBACK` second — editing the lint alone leaves the hook enforcing the old table. Completion condition: `sh -n tools/ste100-lint.sh` passes, each newly adopted term hits on a test string, and any simplified-character change is in `jsc-hooks/hooks/simplified.txt` as well.
8. Run `tools/ste100-lint.sh` over every jsc repo (`tools/sync-domains.sh` prints the repo paths). Fix hits in files this repo owns. Completion condition: the lint exits 0 for this repo, and hits in other repos are reported with `file:line` for their owners. 7. Run `tools/ste100-lint.sh` over every jsc repo (`tools/sync-domains.sh` prints the repo paths). The repos are independent, so lint them **in parallel**, one run per repo. Route each exit code: 0 — that repo is clean; 1 — hits printed as `{檔案}:{行號}:{類別}:{命中內容}`; 2 — no target was given, so fix the arguments and rerun, never read it as clean. Fix hits in files this repo owns. Completion condition: the lint exits 0 for this repo, and hits in other repos are reported with `file:line` for their owners.
9. Run `tools/sync-skill-manifest.sh .` to sync the README's 「Skills 目錄」 section and bump the manifests. Completion condition: all three manifests show the same new version. 8. Run `tools/sync-skill-manifest.sh .` to sync the README's 「Skills 目錄」 section and bump the manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: all three manifests show the same new version.
10. Open a PR via `jsc-git:pr`. Completion condition: a PR URL comes back and is reported with the table format in `references/pr-report.md`. 9. Open a PR via `jsc-git:pr`. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md).
## Notes ## Notes
+50 -37
View File
@@ -1,6 +1,6 @@
--- ---
name: tooling-guide name: tooling-guide
description: Inventory the current jsc plugins, skills, hook management, and usage paths as the baseline tooling guide. Use when the user asks for a skill-set guide, tooling map, supported plugin list, supported skill list, hook management overview, or onboarding reference. Do not use for installing, updating, deleting, auditing, or repairing the skill set; use jsc-cli:deploy, jsc-meta:skill-check, jsc-meta:skill-update, jsc-meta:skill-delete, or jsc-hooks:hooks-install instead. description: Inventory the current jsc plugins, skills, hook management, and usage paths as the baseline tooling guide, taking the skill, CLI, and hook-wiring facts from one inventory-tooling.sh run rather than re-running the scripts it already called. On request, publish that inventory through jsc-gitea:wiki to TOOLING_{HASH}, hashed from {hostname}/{tool}/{account}, and register it in TOOLING_CONTENTS. Use when the user asks for a skill-set guide, tooling map, supported plugin list, supported skill list, hook management overview, or onboarding reference. Do not use for installing, updating, deleting, auditing, or repairing the skill set; use jsc-cli:deploy, jsc-meta:skill-check, jsc-meta:skill-update, jsc-meta:skill-delete, or jsc-hooks:hooks-install instead.
--- ---
# tooling-guide - build the baseline tooling guide # tooling-guide - build the baseline tooling guide
@@ -11,65 +11,55 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
## Rules ## Rules
- Keep the guide factual and current. - Every factual claim in the guide carries the source path or the tool output line it came from.
- Prefer script output over copied lists. - Prefer script output over copied lists.
- Use `tools/inventory-tooling.sh` for the baseline inventory. - Use `tools/inventory-tooling.sh` for the baseline inventory.
- Keep command syntax in the guide only when it comes from README files or tool output. - Keep command syntax in the guide only when it comes from README files or tool output.
- Do not modify README files, manifests, marketplace files, hooks, tools, or other skills. - Do not modify README files, manifests, marketplace files, hooks, tools, or other skills.
- Put generated guide text in the response or in the user-requested target only. - Put generated guide text in the response or in the user-requested target only.
- Run detail synthesis as a sub agent when the guide needs explanations, grouping, or onboarding prose. - Run detail synthesis as a sub agent when the guide needs explanations, grouping, or onboarding prose.
- Route every wiki read and write through `jsc-gitea:wiki`, and every `{HASH}` through `jsc-gitea/tools/hash-id`.
Done when these rules are all checked before the final report. Done when each rule above has a recorded pass, or a recorded exception naming the claim and the reason, checked before the final report.
## Inputs ## Inputs
- Optional user scope: plugin inventory, skill inventory, hook management, CLI usage, or all areas. - Optional user scope: plugin inventory, skill inventory, hook management, CLI usage, or all areas.
- Optional output target: chat response, wiki draft text, or a named file that the user explicitly requests. - Optional output target: chat response, wiki draft text, a named file that the user explicitly requests, or the wiki pages `TOOLING_{HASH}` and `TOOLING_CONTENTS`.
Done when the scope and output target are known. If the user gives no scope, use all areas. If the user gives no target, return the guide in chat. Done when the scope and the output target are each written down as one of the values listed above. With no scope given, write down `all areas`. With no target given, write down `chat response`. Only the `wiki page` target runs step 7.
## Flow ## Flow
1. Confirm the working roots. Use `/root/plugins/meta` as the meta root. Use sibling repos under `/root/plugins` for current domain checkouts. Completion condition: the meta root exists and contains `references/guidelines.md`. 1. Confirm the working roots. Run `tools/plugins-root.sh` — it prints the workspace root that holds the domain checkouts, and it is the same derivation every other tool here uses. Exit 1 means the root could not be derived: read stderr, set `JSC_PLUGINS_ROOT` to the directory that holds the domain repos, and rerun. Under a plugin install the tools sit in the CLI's plugin cache, so the built-in guess lands in that cache instead of the workspace. The meta root is `{root}/meta`, or `{root}/jsc-meta` when that is the checkout name; the sibling directories under `{root}` are the domain checkouts. Completion condition: the script exits 0, and the meta root it names exists and contains `references/guidelines.md`.
2. Sync the domain inventory with `tools/sync-domains.sh` from the meta root. This script owns marketplace discovery and local repo synchronization. 2. Sync the domain inventory with `tools/sync-domains.sh`. This script owns marketplace discovery and local repo synchronization.
- Exit 0: continue with the printed `domain<TAB>path` rows. - Exit 0: continue with the printed `domain<TAB>path` rows.
- Exit 3: keep the printed rows, report every skipped or dirty repo from stderr as stale input, and continue only after the user accepts a guide with stale rows. - Exit 3: keep the printed rows, report every skipped or dirty repo from stderr as stale input, and continue only after the user accepts a guide with stale rows.
- Exit 2: report the clone failure and stop. - Exit 2: report the clone failure and stop.
- Exit 1: report the unreadable canonical marketplace and stop. - Exit 1: report which cause stderr names — the root could not be derived, `gitea.sh` was not found, or the canonical marketplace was unreadable — then stop. For the root case, fix it the same way as step 1 and rerun.
Completion condition: each plugin row used by the guide has a domain and a local path, or the stale-input decision is recorded. Completion condition: each plugin row used by the guide has a domain and a local path, or the stale-input decision is recorded.
3. Build the baseline guide with `tools/inventory-tooling.sh` from the meta root. Use its Markdown output as the base document. 3. Collect the baseline inventory and the management-flow facts. The two halves are independent — one reads tool output, the other reads files — so run them **at the same time**.
**Baseline inventory.** Build it with `tools/inventory-tooling.sh` and use its Markdown output as the base document.
- Exit 0: continue with the generated guide. - Exit 0: continue with the generated guide.
- Exit 1: report which cause stderr names — the root could not be derived, the root does not exist, or the marketplace or `list-skills.sh` is missing — then stop.
- Any other exit: report the command, exit code, and stderr, then stop. - Any other exit: report the command, exit code, and stderr, then stop.
Completion condition: the generated guide contains `Source freshness`, `Supported plugins`, `Supported skills`, `Supported CLIs`, `Hook management`, `Plugin and skill management`, `Operational checks`, and `Use this when`. **This one run already covers the skill catalog, the CLI detection and the hook wiring status.** Internally it runs `meta/tools/list-skills.sh`, `cli/tools/detect-clis.sh` and `hooks/tools/wire-cli.sh status {cli}` and writes each result into its own section, so read those sections instead of calling the three scripts again:
- `Supported skills` — one row per skill, from `list-skills.sh`. An empty table means no skill was scanned; return to step 2.
- `Supported CLIs` — one row per detected CLI with its path and version, from `detect-clis.sh`. The single placeholder row means no CLI is installed here, or `detect-clis.sh` is missing; mark CLI-specific checks as not available on this machine.
- `Hook wiring status` — one row per detected CLI with the `wire-cli.sh status` exit code and verdict, `CLI 代號不符合 wire-cli.sh 用法` for exit 2 and `未知狀態,結束碼 {rc}` for anything outside 0, 1, 3 and 5. The single placeholder row means no CLI was detected or `wire-cli.sh` is missing; state the hook verdict as unknown and say why.
4. Build the skill catalog with `tools/list-skills.sh` from the meta root. Use its TSV rows as the skill list when the baseline guide needs verification or a smaller scope. Re-running those three scripts on top of this buys nothing and can disagree with the base document — the second run sees a different machine state, and the guide then carries two answers for one fact. What that costs is written down under `Notes`.
- Exit 0: continue with the printed `domain<TAB>name<TAB>description` rows.
- Any other exit: report the command, exit code, and stderr, then stop.
Completion condition: every skill row used by the guide comes from the script output. **Management-flow facts.** Collect them from the current docs and skills. Read only README files, `SKILL.md` files, and tool help or headers from `tools/` under the domain repo paths that step 2 printed — never a hardcoded absolute path, because the workspace root differs per machine and per install form. Do not infer support from missing or stale files.
5. Detect supported CLIs with `/root/plugins/cli/tools/detect-clis.sh`. Use its TSV rows as the installed CLI list when the baseline guide needs verification or a smaller scope. Completion condition: the generated guide contains `Source freshness`, `Supported plugins`, `Supported skills`, `Supported CLIs`, `Hook wiring status`, `Hook management`, `Plugin and skill management`, `Operational checks` and `Use this when`; the skill, CLI and hook facts used by the guide are quoted from those sections; and each management flow in the guide points to one source file or one tool output.
- Exit 0 with rows: continue with the printed `name<TAB>path<TAB>version` rows.
- Exit 0 with no rows: continue and mark CLI-specific checks as not available on this machine.
- Any other exit: report the command, exit code, and stderr, then stop.
Completion condition: the guide states the detected CLI set, or states that no installed CLI was detected. 4. Synthesize the guide. This step MUST run as a sub agent when the output needs explanations, grouping, onboarding prose, or cross-domain comparison. Give the sub agent only the collected inventories, the relevant README and SKILL paths, and this required section list:
6. Collect hook wiring facts for each detected CLI with `/root/plugins/hooks/tools/wire-cli.sh status {cli}`. Use `status` only when the baseline guide needs verification or a smaller scope.
- Exit 0, 1, or 5: keep the status and all `item` lines.
- Exit 3: mark that CLI as skipped.
- Exit 2: fix the CLI code from the detect output and rerun.
- Any other exit: report the command, exit code, and stderr, then mark the hook status as unknown.
Completion condition: every detected CLI has one hook-wiring verdict, or the guide states why the verdict is unknown.
7. Collect management-flow facts from the current docs and skills. Read only README files, `SKILL.md` files, and tool help or headers from `tools/` under the synced domain repos. Do not infer support from missing or stale files. Completion condition: each management flow in the guide points to one source file or one tool output.
8. Synthesize the guide. This step MUST run as a sub agent when the output needs explanations, grouping, onboarding prose, or cross-domain comparison. Give the sub agent only the collected inventories, the relevant README and SKILL paths, and this required section list:
- Supported plugins - Supported plugins
- Supported skills - Supported skills
- Supported CLIs and usage forms - Supported CLIs and usage forms
@@ -80,16 +70,37 @@ Done when the scope and output target are known. If the user gives no scope, use
Completion condition: the sub agent returns a guide draft with every required section and with source paths for each factual claim. Completion condition: the sub agent returns a guide draft with every required section and with source paths for each factual claim.
9. Verify the draft in the main agent. 5. Verify the draft in the main agent.
- Check that each plugin comes from `sync-domains.sh`. - Check that each plugin comes from the step 2 `sync-domains.sh` rows.
- Check that each skill comes from `list-skills.sh`. - Check that each skill comes from the step 3 `Supported skills` section.
- Check that each hook-wiring fact comes from `wire-cli.sh status`. - Check that each CLI fact comes from the step 3 `Supported CLIs` section, and each hook-wiring fact from the step 3 `Hook wiring status` section.
- Check that install, update, delete, audit, repair, and health-check actions point to the owning skill or tool. - Check that install, update, delete, audit, repair, and health-check actions point to the owning skill or tool.
- Check that the draft does not copy long implementation details from README files or scripts. - Check that the draft does not copy long implementation details from README files or scripts.
Completion condition: every factual claim has a source path or tool output, and no required section is empty. Completion condition: every factual claim has a source path or tool output, and no required section is empty.
10. Deliver the guide in the requested target. If the target is chat, keep it concise and include the source paths used. If the target is a file, write only that user-requested file and do not update manifests or README files. Completion condition: the guide is delivered and the final report names the target, source freshness, stale inputs if any, and any unknown hook verdicts. 6. Deliver the guide in the requested target. If the target is chat, keep it concise and include the source paths used. If the target is a file, write only that user-requested file and do not update manifests or README files. If the target is the wiki page, step 7 performs the delivery — keep the chat summary short here and let step 7 report the page names and URLs. Completion condition: the guide is delivered and the final report names the target, source freshness, stale inputs if any, and any unknown hook verdicts.
7. Publish the inventory to the wiki. Run this step only when the recorded output target is the wiki page; for any other target, record `wiki publish skipped — target is {target}` and go to the final report. **This whole step MUST run as a sub agent.** Every wiki read and write goes through `jsc-gitea:wiki`; never assemble a Gitea API call here.
7.1 **Build one page name per detected CLI.** Take the CLI code names from the step 3 `Supported CLIs` section — that section already carries the first column of `jsc-cli/tools/detect-clis.sh`, one of `claude`, `codex`, `copilot`, `antigravity`, `kiro`. Pair each code name with this machine's host name and the current login account, then hand `{hostname}/{tool}/{account}` to `jsc-gitea/tools/hash-id`. The hash rules live in `../../references/guidelines.md` and are not restated here; compute nothing by hand. One page per host, CLI, and account: every CLI carries its own installed plugin set and its own hook wiring, and the tool segment is what keeps five CLIs off one page. A missing host name, tool name, or account stops the step — name the missing segment and substitute no default value. `hash-id` exit 1 means this machine has neither `sha1sum` nor `shasum`: stop and report that one of them has to be installed. Completion condition: every detected CLI has one `TOOLING_{HASH}` name built from three non-empty segments, all of them produced by `hash-id`.
7.2 **Resolve the wiki repo** for type `TOOLING` through `jsc-gitea:wiki`, which reads `JSC_WIKI_REPO_TOOLING` first and `JSC_WIKI_REPO` second. Exit 3 — neither variable is set: ask for that type's `{owner}/{repo}` per the `jsc-ask:ask` rules. Exit 2 — the installed `jsc-gitea` does not accept the `TOOLING` type yet: stop and report that the type has to be registered there first. Completion condition: exactly one `{owner}/{repo}` is recorded, and every write in this step targets it.
7.3 **Write the content pages first.** Render `templates/tooling-page.md` for each `TOOLING_{HASH}` from the step 3 inventory, keeping only that page's own CLI row in the `Supported CLIs` and `Hook wiring status` tables. Each run overwrites the whole page: it records what this machine looks like right now, so keeping earlier runs buys nothing. Content pages go before the contents page for the same reason as every other jsc skill — a contents row must never point at a page whose write failed. Completion condition: every `TOOLING_{HASH}` write returned exit 0, or its failure went to step 7.5.
7.4 **Register the pages in `TOOLING_CONTENTS` second.** Read that page first, then route the read exit code:
- 0 — the page is there. Find the row whose host, tool, and account all match this run, refresh that one row per `templates/tooling-contents.md`, leave every other row exactly as it was, and write the whole page back.
- 4 — the page does not exist yet. **This is the only code that allows creating it.** Build it from the template with this run's rows.
- 7 or 8 — the key was rejected, or the API failed, so the old content is unknown. Stop. Create nothing and overwrite nothing: a page built on top of unknown content deletes rows that nobody can get back. Report the exit code and the page name.
Completion condition: `TOOLING_CONTENTS` holds one row per page written in step 7.3, every row belonging to another machine or CLI is unchanged, or the step stopped with the read exit code and the page name reported.
7.5 **Route a failed write.** Retry the failed `jsc-gitea:wiki` write once. When it fails again, stop the publish and report the page name together with the content that never reached the wiki, so the user can place it by hand. Report a page as written only after its write returned exit 0. Completion condition: every page named in this step is either confirmed written with its page name, or listed as unwritten with its exit code and its full content.
## Notes
- **Removed protection, on purpose.** The flow used to carry three more steps that re-ran `list-skills.sh`, `detect-clis.sh` and `wire-cli.sh status {cli}` after `inventory-tooling.sh` had already called all three. That second pass doubled as an independent cross-check: it read the same three facts straight from the source scripts, so a wrong skill row, a missing CLI, or a stale hook verdict produced by `inventory-tooling.sh` surfaced as a disagreement between the two sets. That cross-check is gone. The guide now takes the skill catalog, the CLI list and the hook wiring status from one `inventory-tooling.sh` run, with no second raw output to compare against, so a bug in that script's own scanning, parsing, or section writing reaches the guide unnoticed and reads as fact. Two things bound the risk: step 5 still rejects any claim with no source section behind it, and `Hook wiring status` carries the per-CLI exit code, so a nonsense verdict stays visible. When a decision rests on the guide's skill, CLI, or hook facts, get the second opinion elsewhere — run the three scripts by hand and compare, or run `jsc-cli:doctor` for an independent wiring verdict.
## Output Contract ## Output Contract
@@ -99,9 +110,11 @@ The guide must include these fields in this order:
2. `Supported plugins`: one row per domain. 2. `Supported plugins`: one row per domain.
3. `Supported skills`: one row per skill. 3. `Supported skills`: one row per skill.
4. `Supported CLIs`: one row per detected CLI, or one sentence for none detected. 4. `Supported CLIs`: one row per detected CLI, or one sentence for none detected.
5. `Hook management`: wiring, smoke, error scan, repair owner, and coverage limits. 5. `Hook management`: wiring, smoke, error scan, repair owner, and coverage limits, with the per-CLI wiring verdicts from `Hook wiring status`.
6. `Plugin and skill management`: install, update, delete, audit, and creation owners. 6. `Plugin and skill management`: install, update, delete, audit, and creation owners.
7. `Operational checks`: doctor, setup, version guard, restart gate, language guard, and comment-scope guard. 7. `Operational checks`: doctor, setup, version guard, restart gate, language guard, and comment-scope guard.
8. `Use this when`: short usage guidance for maintainers. 8. `Use this when`: short usage guidance for maintainers.
Done when the output has all fields in order and each non-empty table has at least one source reference. Done when the output has all fields in order and each non-empty table has at least one source reference.
For the wiki-page target, the same fields go to `TOOLING_{HASH}` in the section order of `templates/tooling-page.md`, and the row registered in `TOOLING_CONTENTS` follows `templates/tooling-contents.md`. Both templates own their own field lists; do not restate them here.
+31
View File
@@ -0,0 +1,31 @@
# 技能盤點目錄
> 由 `jsc-meta:tooling-guide` 維護。這是目錄頁 `TOOLING_CONTENTS`。
> 一列代表一組「機器、CLI、帳號」。同一台機器裝了幾支 CLI,就有幾列。
> `TOOLING_{HASH}` 的 `{HASH}` 交給 `jsc-gitea/tools/hash-id` 產生,雜湊來源見 `jsc-meta/references/guidelines.md` 的「Wiki 頁命名總表」。
| 盤點頁 | 主機 | 工具 | 帳號 | plugin 數 | 技能數 | hook 接線 | 最後盤點 |
| --- | --- | --- | --- | ---: | ---: | --- | --- |
| [[TOOLING_{HASH}]] | {主機名} | {claude、codex、copilot、antigravity、kiro 五選一} | {登入帳號} | {n} | {n} | {wired、degraded、unwired、unknown 四選一} | {yyyy-MM-dd HH:mm} |
## 欄位說明
| 欄位 | 內容 |
| --- | --- |
| 盤點頁 | 指向 `TOOLING_{HASH}` 的同 wiki 連結 |
| 主機 | 這次盤點的機器名,與雜湊第一段相同 |
| 工具 | CLI 代號,與雜湊第二段相同 |
| 帳號 | 執行盤點的登入帳號,與雜湊第三段相同 |
| plugin 數 | 該頁「已安裝 plugin」表的列數 |
| 技能數 | 該頁「可用技能」表的列數 |
| hook 接線 | 該頁「hook 接線狀態」對這支 CLI 的判定 |
| 最後盤點 | 該頁盤點時間,與內容頁標頭一致 |
## 寫入規則
- 先整頁讀回來,再比對主機、工具、帳號三欄。
- 三欄都相同就更新那一列,其餘欄位覆寫成本次結果。
- 三欄找不到相同的一列,才新增一列。
- 只動自己那一列,別人的列原樣保留。
- 禁止整頁覆蓋。這一頁是共用目錄,覆蓋等於刪掉別台機器的紀錄。
- 讀不到舊內容就中止,不新增列,也不寫入。
+129
View File
@@ -0,0 +1,129 @@
# 技能盤點 — {主機名}/{工具名稱}/{登入帳號}
> 由 `jsc-meta:tooling-guide` 維護。這是盤點頁 `TOOLING_{HASH}`。
> 這頁記的是「現在這台機器上這支 CLI 長什麼樣」。每次盤點覆寫整頁,不保留歷史。
> 覆寫是刻意的:舊的安裝內容與接線狀態早就不成立,留著只會讓人照著過期的事實下判斷。
> 目錄頁 `TOOLING_CONTENTS` 的規則相反,那頁只更新自己那一列,兩者不要混用。
> 要看技能組歷次異動請翻 `SKILLSET_{HASH}`,累積紀錄在那一頁。
## 本次盤點
| 項目 | 內容 |
| --- | --- |
| 盤點時間 | {yyyy-MM-dd HH:mm} |
| 主機 | {主機名} |
| 工具 | {claude、codex、copilot、antigravity、kiro 五選一} |
| 帳號 | {登入帳號} |
| 資料來源 | `meta/tools/inventory-tooling.sh` 單次執行的輸出 |
| plugins 根目錄 | {絕對路徑} |
| marketplace | {絕對路徑} |
> 全頁事實出自上面那一次執行。同一趟不重跑 `list-skills.sh`、`detect-clis.sh` 與 `wire-cli.sh status`,避免同一件事出現兩個答案。
## 資料新鮮度
對應輸出的 `Source freshness` 一節。
| 項目 | 狀態 |
| --- | --- |
| 本機 domain 清單 | {synced、stale accepted、blocked 三選一} |
| marketplace | {絕對路徑} |
## 現況摘要
對應輸出的 `現況摘要` 一節。
| 項目 | 數量 |
| --- | ---: |
| 已註冊 plugin domain | {n} |
| 已掃到技能 | {n} |
| 已偵測 CLI | {n} |
## 已安裝 plugin
對應輸出的 `Supported plugins` 一節。
| domain | 版本 | 本機路徑 |
| --- | --- | --- |
| {domain} | {版本或「缺本機存取庫」} | {絕對路徑} |
## 可用技能
對應輸出的 `Supported skills` 一節。
| domain | skill | 用途 |
| --- | --- | --- |
| {domain} | {技能名} | {一句用途} |
## 已偵測 CLI
對應輸出的 `Supported CLIs` 一節。只留這一頁對應的那支 CLI,其餘 CLI 各自有自己的盤點頁。
| CLI | 執行檔 | 版本 |
| --- | --- | --- |
| {工具名稱} | {絕對路徑} | {版本字串} |
## 可用工具
對應輸出的 `Supported tools` 一節。
| domain | tool | 用途 |
| --- | --- | --- |
| {domain} | {腳本檔名} | {檔頭第二行的說明} |
## hook 管理
對應輸出的 `Hook management` 一節。
| hook | 用途 |
| --- | --- |
| {腳本檔名} | {檔頭第二行的說明} |
## hook 接線狀態
對應輸出的 `Hook wiring status` 一節。只留這一頁對應的那支 CLI。
| CLI | 結束碼 | 狀態 |
| --- | ---: | --- |
| {工具名稱} | {n} | {狀態字串;查不到就寫「未知狀態,結束碼 {n}」} |
## plugin 與技能管理
對應輸出的 `Plugin and skill management` 一節。
| 需求 | 入口 |
| --- | --- |
| 新增技能 | `jsc-meta:skill-new` |
| 更新單一技能 | `jsc-meta:skill-update` |
| 批次更新技能組 | `jsc-meta:skillset-update` |
| 刪除技能 | `jsc-meta:skill-delete` |
| 安裝、更新、移除整組 plugin | `jsc-cli:deploy` |
| 例行稽核 | `jsc-meta:skill-check` |
| 重新接線 hook | `jsc-hooks:hooks-install` |
## 例行檢查
對應輸出的 `Operational checks` 一節。
| 檢查 | 負責的技能或 hook |
| --- | --- |
| 體檢目前環境 | `jsc-cli:doctor` |
| 修復體檢項目 | `jsc-cli:setup` |
| 版本前置檢查 | `jsc-hooks/hooks/version-guard.sh` |
| 部署後重啟閘門 | `jsc-hooks/hooks/restart-gate.sh` |
| 語言提示與掃描 | `jsc-hooks/hooks/ste100-guard.sh`、`jsc-hooks/hooks/lang-guard.sh` |
| 註解範圍檢查 | `jsc-hooks/hooks/comment-scope.sh` |
## 使用時機
對應輸出的 `Use this when` 一節。
- 這頁只回答「這台機器這支 CLI 現在裝了什麼」。
- 要動手安裝、更新或修復,照上面兩張表找對應的入口。
- 盤點時間離現在太久就重跑一次 `jsc-meta:tooling-guide`,不要拿舊頁當現況。
## 已知限制
| 限制 | 說明 |
| --- | --- |
| {限制項目} | {為什麼這一項在這台機器上查不到或不適用} |
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env sh
# deploy-route.sh — 判定一個 domain 存取庫的改動是否已進預設分支,決定走部署路線或工作樹路線。
#
# 用法: deploy-route.sh <domain-path> [branch]
# branch 不給就用目前分支。
#
# 為什麼要有這支: marketplace 與 version-guard.sh 都讀存取庫的**預設分支**,
# 停在 develop 的改動 jsc-cli:deploy 看不到,部署路線的完成條件永遠達不到。
# skill-new、skill-update、skill-delete、skillset-update 四支各抄一段同樣的判定散文,
# 四份會各自漂移,所以下放成同一支腳本。
#
# 判定:
# 1. git fetch --prune origin,讓比對基準是遠端而不是本機殘影。
# 2. 預設分支依序取 origin/HEAD 的指向、遠端有沒有 master、遠端有沒有 main。
# 3. 待判分支的 HEAD 是不是 origin/{預設分支} 的祖先——是就代表已經合進去。
#
# 輸出(stdout,一行一組 {鍵}<TAB>{值}):
# route<TAB>deploy|worktree 要走哪條路線
# default-branch<TAB>{name} 比對用的預設分支
# branch<TAB>{name} 被判定的分支
# head<TAB>{sha} 被判定分支的 commit
# pending<TAB>{name} 只有 worktree 路線才有:還沒併進預設分支的分支名
# 說明與失敗原因走 stderr。
#
# 結束碼: 0=deploy 路線,改動已在預設分支上,可以叫 jsc-cli:deploy
# 3=worktree 路線,改動還沒進預設分支,要改對工作樹驗證並在回報裡點名待合的 PR
# 2=用法錯誤(一或兩個參數)
# 1=判不出來:路徑不是 git 存取庫、沒有 origin、fetch 失敗,或取不到預設分支。
# **1 不等於 worktree**:判不出來就停下問人,別自己挑一條路線走。
set -u
usage() {
echo 'usage: deploy-route.sh <domain-path> [branch]' >&2
exit 2
}
[ "$#" -ge 1 ] && [ "$#" -le 2 ] || usage
DOMAIN=${1%/}
[ -n "$DOMAIN" ] || usage
BRANCH=${2:-}
[ -d "$DOMAIN/.git" ] || git -C "$DOMAIN" rev-parse --git-dir >/dev/null 2>&1 || {
echo "不是 git 存取庫:$DOMAIN" >&2; exit 1; }
git -C "$DOMAIN" remote get-url origin >/dev/null 2>&1 || {
echo "存取庫沒有 origin 遠端,無從比對預設分支:$DOMAIN" >&2; exit 1; }
git -C "$DOMAIN" fetch --prune --quiet origin >/dev/null 2>&1 || {
echo "git fetch origin 失敗,比對基準會是本機殘影,先修連線再重跑:$DOMAIN" >&2; exit 1; }
if [ -z "$BRANCH" ]; then
BRANCH=$(git -C "$DOMAIN" rev-parse --abbrev-ref HEAD 2>/dev/null) || BRANCH=''
fi
[ -n "$BRANCH" ] && [ "$BRANCH" != HEAD ] || {
echo "取不到目前分支名(可能在 detached HEAD),請用第二個參數指定分支:$DOMAIN" >&2; exit 1; }
# 預設分支:先問 origin/HEAD,再退回遠端實際存在的 master、main。
DEFAULT=$(git -C "$DOMAIN" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null) || DEFAULT=''
DEFAULT=${DEFAULT#origin/}
if [ -z "$DEFAULT" ]; then
for cand in master main; do
if git -C "$DOMAIN" rev-parse --verify --quiet "refs/remotes/origin/$cand" >/dev/null 2>&1; then
DEFAULT=$cand
break
fi
done
fi
[ -n "$DEFAULT" ] || { echo "取不到預設分支(origin/HEAD、origin/master、origin/main 都沒有):$DOMAIN" >&2; exit 1; }
git -C "$DOMAIN" rev-parse --verify --quiet "refs/remotes/origin/$DEFAULT" >/dev/null 2>&1 || {
echo "遠端沒有 origin/$DEFAULT:$DOMAIN" >&2; exit 1; }
HEADSHA=$(git -C "$DOMAIN" rev-parse --verify --quiet "$BRANCH" 2>/dev/null) || HEADSHA=''
[ -n "$HEADSHA" ] || { echo "取不到分支的 commit:$BRANCH" >&2; exit 1; }
TAB=$(printf '\t')
if git -C "$DOMAIN" merge-base --is-ancestor "$HEADSHA" "origin/$DEFAULT" 2>/dev/null; then
ROUTE=deploy
else
ROUTE=worktree
fi
printf 'route%s%s\n' "$TAB" "$ROUTE"
printf 'default-branch%s%s\n' "$TAB" "$DEFAULT"
printf 'branch%s%s\n' "$TAB" "$BRANCH"
printf 'head%s%s\n' "$TAB" "$HEADSHA"
if [ "$ROUTE" = deploy ]; then
echo "改動已在 origin/$DEFAULT 上,走部署路線:$DOMAIN" >&2
exit 0
fi
printf 'pending%s%s\n' "$TAB" "$BRANCH"
echo "改動還沒併進 origin/$DEFAULT,走工作樹路線;回報時要點名待合的 PR:$DOMAIN" >&2
exit 3
+8 -5
View File
@@ -19,12 +19,15 @@
# 技能組以外的檔案。domain 清單取自 marketplace.json,不寫死。 # 技能組以外的檔案。domain 清單取自 marketplace.json,不寫死。
# 每個 domain 先找 {root}/{domain},再找 {root}/jsc-{domain};兩者都沒有就略過。 # 每個 domain 先找 {root}/{domain},再找 {root}/jsc-{domain};兩者都沒有就略過。
# 每個存取庫內排除 .git 目錄。 # 每個存取庫內排除 .git 目錄。
# 根目錄預設取本腳本位置的上兩層(meta/tools -> meta -> 根),換機器不必改腳本; # 根目錄交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
# 用 JSC_PLUGINS_ROOT 覆寫,指向別處的技能組工作目錄。 # 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
# 輸出: 命中檔案清單(去重、排序),一行一個路徑(stdout);掃描摘要走 stderr。 # 輸出: 命中檔案清單(去重、排序),一行一個路徑(stdout);掃描摘要走 stderr。
# 結束碼: 0=有命中 1=掃完但零命中 2=用法錯誤 # 結束碼: 0=有命中 1=掃完但零命中 2=用法錯誤
# 3=找不到根目錄、讀不到 domain 清單、本機一個 domain 存取庫都沒有,或掃描失敗 # 3=推導不出根目錄、讀不到 domain 清單、本機一個 domain 存取庫都沒有、
# 建不了暫存檔,或掃描失敗
# 零命中與掃描失敗必須分開:拿掃描失敗當「沒有引用」會讓刪除技能少改檔案。 # 零命中與掃描失敗必須分開:拿掃描失敗當「沒有引用」會讓刪除技能少改檔案。
# 環境變數: JSC_PLUGINS_ROOT(根目錄,見 plugins-root.sh)
set -u set -u
usage() { usage() {
@@ -38,8 +41,8 @@ SKILL=$2
[ -n "$DOMAIN" ] && [ -n "$SKILL" ] || usage [ -n "$DOMAIN" ] && [ -n "$SKILL" ] || usage
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}" . "$HERE/plugins-root.sh"
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 3; } ROOT=$(jsc_plugins_root) || exit 3
# 正本 domain 清單:優先讀本機任一份 marketplace 副本(每個 repo 都帶同一份)。 # 正本 domain 清單:優先讀本機任一份 marketplace 副本(每個 repo 都帶同一份)。
mkt="" mkt=""
+14 -4
View File
@@ -3,15 +3,25 @@
# #
# 用法: inventory-tooling.sh [root] # 用法: inventory-tooling.sh [root]
# #
# root: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。 # root: 給了參數就用參數。沒給就交給 tools/plugins-root.sh 推導
# 也可用參數或 JSC_PLUGINS_ROOT 覆寫。 # (JSC_PLUGINS_ROOT -> 從 $PWD 往上找 -> 腳本位置上兩層 -> $HOME/plugins)。
# 以 plugin 形式安裝時,「腳本位置上兩層」會落在 plugin 快取目錄,單靠它一定推錯,
# 所以推導規則抽成共用腳本。
# #
# 輸出: Markdown。內容包含 domain、manifest、技能、工具、hooks 與管理入口。 # 輸出: Markdown。內容包含 domain、manifest、技能、工具、hooks 與管理入口。
# 結束碼: 0=成功 1=根目錄、marketplace 或必要工具缺失 # 同一趟已經跑過 list-skills.sh、detect-clis.sh 與 wire-cli.sh status,結果都寫進輸出,
# 呼叫端沿用即可,不必再各跑一次。
# 結束碼: 0=成功 1=推導不出根目錄、根目錄不存在、marketplace 或必要工具缺失
# 環境變數: JSC_PLUGINS_ROOT(根目錄,見 plugins-root.sh)
set -eu set -eu
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT=${1:-${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}} . "$HERE/plugins-root.sh"
if [ "$#" -ge 1 ] && [ -n "$1" ]; then
ROOT=$1
else
ROOT=$(jsc_plugins_root) || exit 1
fi
ROOT=${ROOT%/} ROOT=${ROOT%/}
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT" >&2; exit 1; } [ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT" >&2; exit 1; }
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env sh
# lint-scripts.sh — 檢查一個 domain 存取庫的 shell 腳本:語法、可執行、結束碼有沒有記載。
#
# 用法: lint-scripts.sh <domain-path>
#
# 檢查三項(掃 {domain-path}/tools 與 {domain-path}/hooks 底下的 *.sh):
# 1. 語法 — sh -n 通過。
# 2. 可執行 — 檔案有執行權限。技能直接呼叫的腳本沒有 +x,會在部署後才炸。
# 3. 結束碼 — 檔頭前 60 行要有結束碼宣告列(「結束碼:」或「exit code」)。
# 沒有宣告的腳本,呼叫端沒辦法逐碼分流,只能猜;猜錯就把失敗當成功。
#
# 為什麼合成一支: skill-check 原本在 SKILL.md 裡寫 find ... -exec sh -n,另外兩項靠散文
# 要求人工比對。三項的輸入輸出都固定,散文版每個 domain 各做一次,還會跟腳本實況漂移。
#
# 例外: 只被 source 的共用函式庫(判準見 is_library)第 2、3 項都不查——它不是可執行入口,
# 沒有 +x 的必要,也沒有結束碼語意可宣告。第 1 項語法檢查照查,函式庫一樣會被 sh -n 讀。
# 豁免不是靜默的: 每豁免一支就在 stderr 記一行,摘要也帶函式庫支數,才看得出誰被跳過。
#
# 輸出: 一行一個不合格項目,格式 {檔案}:{檢查項}:{說明}(stdout);
# 函式庫豁免通知與統計摘要走 stderr。
# 結束碼: 0=掃到腳本且三項全過
# 1=有不合格項目(清單在 stdout)
# 2=用法錯誤(本腳本只吃一個參數)
# 3=domain 路徑不存在,或 tools/ 與 hooks/ 都沒有 *.sh——**什麼都沒掃**,
# 不等於通過。缺這兩個目錄本身不是失敗,呼叫端照實記「無腳本可掃」即可。
set -u
usage() {
echo 'usage: lint-scripts.sh <domain-path>' >&2
exit 2
}
[ "$#" -eq 1 ] || usage
DOMAIN=${1%/}
[ -n "$DOMAIN" ] || usage
[ -d "$DOMAIN" ] || { echo "找不到 domain 路徑:$DOMAIN" >&2; exit 3; }
SCAN=''
for d in "$DOMAIN/tools" "$DOMAIN/hooks"; do
[ -d "$d" ] && SCAN="$SCAN $d"
done
[ -n "$SCAN" ] || { echo "沒有 tools/ 也沒有 hooks/,無腳本可掃:$DOMAIN" >&2; exit 3; }
TMP=$(mktemp) || { echo "無法建立暫存檔" >&2; exit 3; }
trap 'rm -f "$TMP"' EXIT
# shellcheck disable=SC2086
find $SCAN -type f -name '*.sh' 2>/dev/null | sort > "$TMP"
[ -s "$TMP" ] || { echo "tools/ 與 hooks/ 底下沒有 *.sh,無腳本可掃:$DOMAIN" >&2; exit 3; }
has_exit_stmt() { # $1=檔案;去掉整行註解後還找得到 exit 敘述就回 0
grep -v '^[[:space:]]*#' "$1" 2>/dev/null \
| grep -qE '(^|[;&|(){}[:space:]])exit([[:space:]]|$)'
}
is_library() { # $1=檔案;只被 source 的共用函式庫,可執行與結束碼兩項都豁免
# 判準兩條,缺一不可:
# 1. 自我宣告是函式庫——檔名 lib.sh,或檔頭前 30 行寫了「以 source 載入」。
# 2. 程式碼裡一個 exit 敘述都沒有。沒有 exit 就沒有結束碼語意,也就沒有東西可宣告;
# 這一條同時是「不是可執行入口」的證據,兩項豁免共用同一個事實。
# 為什麼不能只看第 1 條: 那是字樣比對,會命中「提到這件事」的腳本。本腳本自己的
# 例外段就寫了「以 source 載入」,plugins-root.sh 的用法段也寫了,但兩支都是有
# 結束碼語意的可執行入口。只拿第 1 條去豁免結束碼,等於把工具自己漏掉。
case "${1##*/}" in
lib.sh) ;;
*) head -30 "$1" 2>/dev/null | grep -q '以 source 載入' || return 1 ;;
esac
! has_exit_stmt "$1"
}
hit=0
total=0
libs=0
while IFS= read -r f; do
[ -n "$f" ] || continue
total=$((total + 1))
err=$(sh -n "$f" 2>&1) || {
printf '%s:語法:%s\n' "$f" "$(printf '%s' "$err" | tr '\n' ' ')"
hit=1
}
if is_library "$f"; then
libs=$((libs + 1))
printf '%s:函式庫:判定為只被 source 的共用函式庫(無 exit 敘述),略過「可執行」與「結束碼」\n' "$f" >&2
continue
fi
if [ ! -x "$f" ]; then
printf '%s:可執行:缺執行權限,請 chmod +x\n' "$f"
hit=1
fi
if ! head -60 "$f" 2>/dev/null | grep -qE '結束碼|exit code'; then
printf '%s:結束碼:檔頭沒有結束碼宣告列,呼叫端無法逐碼分流\n' "$f"
hit=1
fi
done < "$TMP"
if [ "$hit" -eq 0 ]; then
echo "腳本檢查通過:$total 支(語法、可執行、結束碼宣告),其中 $libs 支判定為函式庫" >&2
else
echo "腳本檢查有不合格項目:共掃 $total 支(其中 $libs 支判定為函式庫),清單見 stdout" >&2
fi
exit $hit
+7 -5
View File
@@ -10,17 +10,19 @@
# 誤把它們當成技能組的一部分。domain 清單取自 marketplace.json,不寫死。 # 誤把它們當成技能組的一部分。domain 清單取自 marketplace.json,不寫死。
# 清單只反映本機檔案;要確保檔案齊全請先跑 sync-domains.sh。 # 清單只反映本機檔案;要確保檔案齊全請先跑 sync-domains.sh。
# #
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。 # 根目錄: 交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。 # 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
# #
# 輸出: 一行一個技能,格式 {domain}<TAB>{name}<TAB>{description},依 domain、name 排序。 # 輸出: 一行一個技能,格式 {domain}<TAB>{name}<TAB>{description},依 domain、name 排序。
# 結束碼: 0=至少列出一個技能 1=找不到根目錄、讀不到 domain 清單,或一個技能都沒有 # 結束碼: 0=至少列出一個技能
# 1=推導不出根目錄、讀不到 domain 清單,或一個技能都沒有
set -u set -u
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}" . "$HERE/plugins-root.sh"
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 1; } ROOT=$(jsc_plugins_root) || exit 1
# 正本 domain 清單:優先讀本機任一份 marketplace 副本(每個 repo 都帶同一份)。 # 正本 domain 清單:優先讀本機任一份 marketplace 副本(每個 repo 都帶同一份)。
mkt="" mkt=""
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env sh
# plugins-root.sh — 推導 jsc 技能組工作目錄的根,meta/tools 六支腳本共用同一套規則。
#
# 用法:
# . "$(dirname -- "$0")/plugins-root.sh" # 以 source 載入,取得 jsc_plugins_root()
# ROOT=$(jsc_plugins_root) || exit {該腳本的碼}
# sh plugins-root.sh # 直接執行,把推導結果印到 stdout
#
# 為什麼要抽出來:
# 舊寫法一律取「本腳本位置的上兩層」。技能組以 plugin 形式安裝時,腳本落在
# ~/.claude/plugins/cache/jsc/jsc-meta/{版本}/tools/,上兩層是 .../cache/jsc/jsc-meta,
# 那是 plugin 快取,不是放各 domain 存取庫的工作目錄,**一定推錯**。
# 2026-08 例行稽核第一步就實際踩到:不手動設 JSC_PLUGINS_ROOT 就跑不動,
# 而呼叫這些腳本的 SKILL.md 都沒提要設。
#
# 推導順序(取第一個帶得出 gitea.sh 的候選):
# 1. JSC_PLUGINS_ROOT
# 2. 從 $PWD 逐層往上,找含 meta/.claude-plugin/marketplace.json
# (或 jsc-meta/.claude-plugin/marketplace.json)的目錄
# 3. 本腳本位置的上兩層({root}/meta/tools -> {root})
# 4. $HOME/plugins
#
# 判準: 候選目錄下要有 gitea/tools/gitea.sh 或 jsc-gitea/tools/gitea.sh。
# marketplace.json 每個 domain 存取庫都帶一份,單看它會把某個 domain 存取庫自己
# 誤判成根;gitea.sh 只存在於 jsc-gitea 存取庫裡,拿它當標記才分得出根與 domain。
#
# 逃生門: 候選都不帶 gitea.sh,但 JSC_PLUGINS_ROOT 有設且是目錄時,照設定值使用,
# 並在 stderr 提醒。只 clone plugins/meta 的環境(CI、單存取庫維護)本來就沒有
# jsc-gitea;這時硬擋會讓「請設 JSC_PLUGINS_ROOT」變成解不開的死路。
#
# 結束碼(直接執行時): 0=推導成功,根目錄印在 stdout
# 1=推導不出來,訊息會指名要設 JSC_PLUGINS_ROOT 並列出試過的候選
# 環境變數: JSC_PLUGINS_ROOT(根目錄,最優先)
set -u
_jsc_walk_up() { # 從 $PWD 逐層往上,印出第一個含 meta 存取庫的目錄
_jpr_d=$(pwd -P 2>/dev/null) || return 1
while [ -n "$_jpr_d" ]; do
if [ -f "$_jpr_d/meta/.claude-plugin/marketplace.json" ] ||
[ -f "$_jpr_d/jsc-meta/.claude-plugin/marketplace.json" ]; then
printf '%s\n' "$_jpr_d"
return 0
fi
[ "$_jpr_d" = / ] && break
_jpr_d=$(dirname -- "$_jpr_d")
done
return 1
}
_jsc_has_gitea() { # $1=候選根目錄;jsc-gitea 存取庫是「這是根」的唯一可靠標記
[ -f "$1/gitea/tools/gitea.sh" ] || [ -f "$1/jsc-gitea/tools/gitea.sh" ]
}
jsc_plugins_root() { # 印出根目錄(stdout);推不出來回 1,說明走 stderr
_jpr_tried=''
for _jpr_src in env pwd here home; do
case "$_jpr_src" in
env) _jpr_c="${JSC_PLUGINS_ROOT:-}" ;;
pwd) _jpr_c=$(_jsc_walk_up 2>/dev/null) || _jpr_c='' ;;
here) _jpr_c=$(CDPATH= cd -- "$(dirname -- "$0")/../.." 2>/dev/null && pwd) || _jpr_c='' ;;
home) if [ -n "${HOME:-}" ]; then _jpr_c="$HOME/plugins"; else _jpr_c=''; fi ;;
*) _jpr_c='' ;;
esac
[ -n "$_jpr_c" ] || continue
_jpr_tried="$_jpr_tried $_jpr_src: $_jpr_c
"
[ -d "$_jpr_c" ] || continue
_jsc_has_gitea "$_jpr_c" || continue
_jpr_c=$(CDPATH= cd -- "$_jpr_c" && pwd) || continue
printf '%s\n' "${_jpr_c%/}"
return 0
done
if [ -n "${JSC_PLUGINS_ROOT:-}" ] && [ -d "$JSC_PLUGINS_ROOT" ]; then
echo "JSC_PLUGINS_ROOT 底下找不到 gitea/tools/gitea.sh,仍照設定值使用:$JSC_PLUGINS_ROOT" >&2
_jpr_c=$(CDPATH= cd -- "$JSC_PLUGINS_ROOT" && pwd) || return 1
printf '%s\n' "${_jpr_c%/}"
return 0
fi
{
echo '推導不出 jsc 技能組的根目錄。'
echo '以 plugin 形式安裝時,腳本位在 ~/.claude/plugins/cache/jsc/jsc-meta/{版本}/tools/,'
echo '上兩層落在 plugin 快取目錄,不是放各 domain 存取庫的工作目錄,所以推不出來。'
echo '請設 JSC_PLUGINS_ROOT 指向放各 domain 存取庫的工作目錄(底下要有 gitea 或 jsc-gitea)。'
echo '已試過的候選:'
printf '%s' "$_jpr_tried"
} >&2
return 1
}
# 直接執行時印出結果;被 source 時只提供函式。
case "${0##*/}" in
plugins-root.sh) jsc_plugins_root || exit 1 ;;
esac
+3 -1
View File
@@ -8,7 +8,9 @@
# 4. 簡體字(只收沒有繁體正當用法的字,避免誤報) # 4. 簡體字(只收沒有繁體正當用法的字,避免誤報)
# 5. 中文並列項用斜線(半形 / 或全形 / 夾在中日韓字元之間),應改頓號「、」 # 5. 中文並列項用斜線(半形 / 或全形 / 夾在中日韓字元之間),應改頓號「、」
# 6. 亂碼(U+FFFD 替代字元、Latin-1 雙重編碼殘留) # 6. 亂碼(U+FFFD 替代字元、Latin-1 雙重編碼殘留)
# 輸出: {檔案}:{行號}:{類別}:{命中內容};全部通過 exit 0,有命中 exit 1。 # 輸出: {檔案}:{行號}:{類別}:{命中內容}(stdout);用法錯誤走 stderr。
# 結束碼: 0=掃過的檔案全部通過 1=有命中 2=沒給檢查對象(空跑會回 0,看起來像通過,所以擋掉)
# 環境變數: JSC_SIMPLIFIED_FILE(簡體字表路徑,優先於自動搜尋)
# 掃描範圍與分流: # 掃描範圍與分流:
# 文件檔(.md、.json)跑全部六項。 # 文件檔(.md、.json)跑全部六項。
# 程式碼檔(.sh .js .ts .py .cs .java .go .rb .php .sql .yml .yaml .toml)只跑簡體字與亂碼。 # 程式碼檔(.sh .js .ts .py .cs .java .go .rb .php .sql .yml .yaml .toml)只跑簡體字與亂碼。
+6 -5
View File
@@ -10,13 +10,14 @@
# 3. 本機已有的 domain:git pull。工作區有未提交變更就跳過 pull,只在 stderr 提醒—— # 3. 本機已有的 domain:git pull。工作區有未提交變更就跳過 pull,只在 stderr 提醒——
# 正在改的 repo 不該被自動 pull 覆蓋。 # 正在改的 repo 不該被自動 pull 覆蓋。
# #
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。 # 根目錄: 交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。 # 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain};都沒有才 clone 成 {root}/{domain}。 # 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain};都沒有才 clone 成 {root}/{domain}。
# #
# 輸出: 一行一個 domain,格式 {domain}<TAB>{path}(stdout);警告與失敗說明走 stderr。 # 輸出: 一行一個 domain,格式 {domain}<TAB>{path}(stdout);警告與失敗說明走 stderr。
# 結束碼: 0=正本讀到、每個 domain 都在本機,而且每個既有存取庫都更新到最新 # 結束碼: 0=正本讀到、每個 domain 都在本機,而且每個既有存取庫都更新到最新
# 1=讀不到正本 marketplace 或找不到 gitea.sh(不輸出任何 domain) # 1=推導不出根目錄、讀不到正本 marketplace,或找不到 gitea.sh(不輸出任何 domain)
# 2=正本讀到,但有 domain 沒能取得或 clone 失敗(已輸出其餘 domain) # 2=正本讀到,但有 domain 沒能取得或 clone 失敗(已輸出其餘 domain)
# 3=每個 domain 都在本機,但有存取庫沒更新到最新:工作區髒而跳過 pull, # 3=每個 domain 都在本機,但有存取庫沒更新到最新:工作區髒而跳過 pull,
# 或 pull 失敗。stderr 會逐一列出這些路徑。**exit 0 才代表「全部最新」**; # 或 pull 失敗。stderr 會逐一列出這些路徑。**exit 0 才代表「全部最新」**;
@@ -28,10 +29,10 @@
set -u set -u
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}" . "$HERE/plugins-root.sh"
DRY="${JSC_SYNC_DRY_RUN:-}" DRY="${JSC_SYNC_DRY_RUN:-}"
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 1; } ROOT=$(jsc_plugins_root) || exit 1
GITEA="" GITEA=""
for g in "$ROOT/gitea/tools/gitea.sh" "$ROOT/jsc-gitea/tools/gitea.sh"; do for g in "$ROOT/gitea/tools/gitea.sh" "$ROOT/jsc-gitea/tools/gitea.sh"; do
+8 -4
View File
@@ -18,14 +18,17 @@
# python3 不在 PATH 時:腳本不寫任何檔案,印出缺少 python3 的訊息,回 1。 # python3 不在 PATH 時:腳本不寫任何檔案,印出缺少 python3 的訊息,回 1。
# 呼叫端要先裝 python3 再重跑,不可改用 sed 手動改 marketplace。 # 呼叫端要先裝 python3 再重跑,不可改用 sed 手動改 marketplace。
# #
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。 # 根目錄: 交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。 # 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain}。 # 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain}。
# #
# 輸出: 一行一個實際寫入的檔案路徑(stdout);略過與失敗說明走 stderr。 # 輸出: 一行一個實際寫入的檔案路徑(stdout);略過與失敗說明走 stderr。
# 結束碼: 0=全部寫入且逐檔一致 2=用法錯誤 # 結束碼: 0=全部寫入且逐檔一致 2=用法錯誤
# 1=缺 python3、正本讀寫失敗,或有檔案比對不一致 # 1=推導不出根目錄、缺 python3、正本讀寫失敗、建不了暫存目錄,
# 或有檔案比對不一致
# 3=寫入成功,但有 domain 存取庫不在本機(先跑 sync-domains.sh 再重跑) # 3=寫入成功,但有 domain 存取庫不在本機(先跑 sync-domains.sh 再重跑)
# 環境變數: JSC_PLUGINS_ROOT(根目錄,見 plugins-root.sh)
set -u set -u
usage() { usage() {
@@ -41,7 +44,8 @@ DESC=$3
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
META=$(CDPATH= cd -- "$HERE/.." && pwd) META=$(CDPATH= cd -- "$HERE/.." && pwd)
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}" . "$HERE/plugins-root.sh"
ROOT=$(jsc_plugins_root) || exit 1
REL_CLAUDE='.claude-plugin/marketplace.json' REL_CLAUDE='.claude-plugin/marketplace.json'
REL_AGENTS='.agents/plugins/marketplace.json' REL_AGENTS='.agents/plugins/marketplace.json'
+8 -1
View File
@@ -13,7 +13,14 @@
# .codex-plugin/plugin.json,version 一律 bump 成同一個新值(以第一份找到的 manifest 版本為準, # .codex-plugin/plugin.json,version 一律 bump 成同一個新值(以第一份找到的 manifest 版本為準,
# 右側數字加一,滿 9 就往左進位;major 不設上限,minor 與 patch 都不超過 9)。 # 右側數字加一,滿 9 就往左進位;major 不設上限,minor 與 patch 都不超過 9)。
# #
# 輸出: 變更摘要——README 新增/移除的技能小節、各 manifest 的舊版本 -> 新版本。 # 輸出: 變更摘要——README 新增或移除的技能小節、各 manifest 的舊版本 -> 新版本(stdout);
# 失敗說明走 stderr。
# 結束碼: 0=README 區塊與三份 manifest 都已同步
# 1=domain 路徑不存在、缺 skills/、缺 README.md、README 缺 JSC-SKILLS 標記、
# 掃不到任何 SKILL.md、找不到 plugin manifest,或 manifest 讀不到 version 欄位
# 2=用法錯誤(本腳本只吃一個參數)
# 本腳本是 set -e:上列以外的指令失敗會直接中止,結束碼由該指令決定,
# 呼叫端把「不是 0、1、2」一律當執行環境故障處理,不得視為同步成功。
set -eu set -eu
usage() { usage() {
+10 -2
View File
@@ -14,9 +14,16 @@
# antigravity — ~/.antigravity/、~/.config/antigravity/ # antigravity — ~/.antigravity/、~/.config/antigravity/
# kiro — ~/.kiro/、工作目錄的 .kiro/hooks/ # kiro — ~/.kiro/、工作目錄的 .kiro/hooks/
# #
# 根目錄: 交給 tools/plugins-root.sh 推導(JSC_PLUGINS_ROOT -> 從 $PWD 往上找 ->
# 腳本位置上兩層 -> $HOME/plugins)。以 plugin 形式安裝時,「腳本位置上兩層」會落在
# plugin 快取目錄,單靠它一定推錯,所以推導規則抽成共用腳本。
#
# 輸出: 檢查過的位置一行一個(stderr),殘留一行一個 {file}:{line}:{內容}(stdout)。 # 輸出: 檢查過的位置一行一個(stderr),殘留一行一個 {file}:{line}:{內容}(stdout)。
# 沒有殘留會印「無殘留」到 stderr,區別於「什麼都沒檢查」。 # 沒有殘留會印「無殘留」到 stderr,區別於「什麼都沒檢查」。
# 結束碼: 0=沒有殘留 1=有殘留 2=用法錯誤 3=沒偵測到任何 CLI 或找不到可檢查的位置 # 結束碼: 0=沒有殘留 1=有殘留 2=用法錯誤
# 3=推導不出根目錄、找不到 detect-clis.sh、沒偵測到任何 CLI,
# 或找不到可檢查的位置(**不等於乾淨**)
# 環境變數: JSC_PLUGINS_ROOT(根目錄,見 plugins-root.sh)
set -u set -u
usage() { usage() {
@@ -30,7 +37,8 @@ SKILL=$2
[ -n "$DOMAIN" ] && [ -n "$SKILL" ] || usage [ -n "$DOMAIN" ] && [ -n "$SKILL" ] || usage
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}" . "$HERE/plugins-root.sh"
ROOT=$(jsc_plugins_root) || exit 3
DETECT="" DETECT=""
for d in "$ROOT/cli/tools/detect-clis.sh" "$ROOT/jsc-cli/tools/detect-clis.sh"; do for d in "$ROOT/cli/tools/detect-clis.sh" "$ROOT/jsc-cli/tools/detect-clis.sh"; do