Merge pull request 'develop-to-master' (#17) from develop into master
Reviewed-on: #17 Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #17.
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
"source": "url",
|
||||
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
||||
},
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄"
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
|
||||
},
|
||||
{
|
||||
"name": "jsc-log",
|
||||
@@ -75,7 +75,7 @@
|
||||
"source": "url",
|
||||
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
||||
},
|
||||
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組"
|
||||
"description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組"
|
||||
},
|
||||
{
|
||||
"name": "jsc-sdlc",
|
||||
@@ -83,7 +83,7 @@
|
||||
"source": "url",
|
||||
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
||||
},
|
||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)"
|
||||
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"source": "url",
|
||||
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
||||
},
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄"
|
||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
|
||||
},
|
||||
{
|
||||
"name": "jsc-log",
|
||||
@@ -75,7 +75,7 @@
|
||||
"source": "url",
|
||||
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
||||
},
|
||||
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組"
|
||||
"description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組"
|
||||
},
|
||||
{
|
||||
"name": "jsc-sdlc",
|
||||
@@ -83,7 +83,7 @@
|
||||
"source": "url",
|
||||
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
||||
},
|
||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)"
|
||||
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-meta",
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.10",
|
||||
"description": "技能組自我管理:新建、更新、刪除技能與技能準則",
|
||||
"skills": "./skills",
|
||||
"author": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-meta",
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.10",
|
||||
"description": "技能組自我管理:新建、更新、刪除技能與技能準則",
|
||||
"skills": "./skills"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
jsc 技能組的 meta domain:新建、更新、刪除技能的流程,以及**全技能組的準則單一來源** `references/guidelines.md`(命名、description 規則、hook / 工具 / sub agent 下放、語言、環境變數、wiki 頁命名總表、審核檢查清單)。
|
||||
|
||||
## 安裝 / 更新 / 移除
|
||||
## 安裝、更新、移除
|
||||
|
||||
Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安裝 token 為 `jsc-meta@jsc`。每個指令一行:
|
||||
|
||||
@@ -34,7 +34,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
|
||||
### `skill-delete`
|
||||
|
||||
刪除技能:先查 Gitea 正本 marketplace 取得 domain 清單並補 clone 缺少的存取庫,列出全部技能 → 使用者選擇 → sub agent 盤點關聯檔案 → 逐檔判斷是否需修正以維持功能(需要就決策樹問修正細節並依準則檢查)→ 刪除 → sub agent 實地檢查各 CLI 的技能與 hook 保存位置,確認沒有殘留 → PR。
|
||||
刪除技能:`sync-domains.sh` 依 Gitea 正本 marketplace 同步所有存取庫,`list-skills.sh` 列出全部技能 → 使用者選擇 → `find-skill-refs.sh` 盤點關聯檔案 → sub agent 逐檔判斷是否需修正以維持功能(需要就決策樹問修正細節並依準則檢查)→ 刪除 → `verify-skill-removed.sh` 實地檢查各 CLI 的技能快取與 hook 設定,確認沒有殘留 → PR。
|
||||
|
||||
### `skillset-update`
|
||||
|
||||
@@ -46,7 +46,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
|
||||
### `ste100-sync`
|
||||
|
||||
同步上游 speak-human-tw 的語言規則:比對 `references/ste100.md` 釘住的上游版本,有新版就萃取適用的變更、更新 lint 樣式、全庫重掃,最後開 PR。適合列為本 repo 的維護方式。
|
||||
同步上游 speak-human-tw 的語言規則:比對 `references/ste100.md` 釘住的上游版本,有新版就萃取適用的變更、逐項決策樹確認、更新 lint 樣式、全庫重掃、bump manifest,最後開 PR。適合列為本 repo 的維護方式。
|
||||
|
||||
<!-- JSC-SKILLS:END -->
|
||||
|
||||
@@ -56,7 +56,13 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
| --- | --- |
|
||||
| `references/guidelines.md` | 技能準則唯一來源,所有 domain 的 AGENTS.md 都指向這裡 |
|
||||
| `references/ste100.md` | STE100 擬人台灣感語言規則唯一來源(改寫自 speak-human-tw,MIT) |
|
||||
| `tools/ste100-lint.sh` | 語言規則的機檢工具:中國用語、中文句內半形標點、AI 套話;命中 exit 1 |
|
||||
| `tools/ste100-lint.sh` | 語言規則的機檢工具:中國用語、中文句內半形標點、AI 套話、簡體字、中文並列斜線;命中 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/list-skills.sh` | 列出正本 marketplace 上各 domain 存取庫的技能,印出 `domain<TAB>name<TAB>description`;不在正本清單上的存取庫不列 |
|
||||
| `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/find-skill-refs.sh` | 盤點一個技能在正本 marketplace 各 domain 存取庫裡的引用檔案(不掃非技能組存取庫與點開頭目錄);技能名稱為純子字串比對,命中要逐檔確認;零命中 exit 1,掃描失敗 exit 3 |
|
||||
| `tools/verify-skill-removed.sh` | 刪除技能後實地檢查各 CLI 的技能快取與 hook 設定有無殘留;有殘留 exit 1,沒偵測到 CLI 或沒有可查位置 exit 3(**不等於乾淨**) |
|
||||
|
||||
## 相關 domain
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jsc-meta",
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.10",
|
||||
"description": "技能組自我管理:新建、更新、刪除技能與技能準則",
|
||||
"skills": "./skills/"
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
3. Skill 名稱:小寫、數字、連字號(`-`),最長 64 字元。名稱即指令(`/jsc-{domain}:{name}`)。
|
||||
4. 每個 domain 是一個獨立存取庫 `https://gitea.jsc.idv.tw/plugins/{domain}.git`。新 domain 必須依 `https://gitea.jsc.idv.tw/plugins/template` 的結構建立新存取庫(三份 plugin manifest、`skills/`、`README.md`、`AGENTS.md`),並把 plugin 條目(URL 來源指向新存取庫)加入 `plugins/meta` 正本的兩份 marketplace 檔,再把更新後的檔案**同步到所有 domain 存取庫**(含新存取庫自己)。
|
||||
5. Git 分支名**只允許 ASCII**(`a-z0-9` 與 `/`、`-`);中文需求或標題先翻譯成英文短語再 slug 化。
|
||||
6. Manifest 版本號從 `0.0.1` 開始,三份 manifest 同步 bump。
|
||||
6. Manifest 版本號從 `0.0.1` 開始,三份 manifest 同步 bump;`minor` 與 `patch` 不得超過 `9`,滿 `9` 就往左進位,`major` 可以超過 `9`。
|
||||
|
||||
## Description 規則
|
||||
|
||||
1. frontmatter 的 `description` 為一行英文,不超過 5 句或 5 個步驟。
|
||||
1. frontmatter 的 `description` 為一行英文,不超過 5 句或 5 個步驟——**兩個上限滿足任一個就算通過**,句數與步驟數都超過才要精簡。
|
||||
2. 使用專有名詞、概念或指引詞(例:WBS、TDD、decision tree、STE100)取代解釋。
|
||||
3. 必須寫清楚觸發時機(何時用、何時不用),這是各 CLI 自動載入的唯一依據。
|
||||
4. 複雜流程透過**組合其他技能**實現,不在單一 description 裡塞流程。
|
||||
@@ -77,13 +77,17 @@
|
||||
|
||||
技能組的每一支技能在被呼叫前都要確認本機版本沒有落後遠端發佈版本。判定在程式層,由 `jsc-hooks` 的 `version-guard.sh`(PreToolUse,matcher `Skill`)執行,**不靠技能內文自我約束**——寫在內文的規則,模型可以無視。
|
||||
|
||||
這道檢查只擋「確定落後」一種情況。查不到任何一項基礎資訊就安靜放行(exit 0),不要求先修好環境:五支 CLI 只有 claude 讀得到本機載入版本,fail-closed 會把另外四支整批鎖死。
|
||||
|
||||
| 項目 | 規則 |
|
||||
| --- | --- |
|
||||
| 比對對象 | 遠端發佈版本(`master` 的 `plugin.json`)對本機**實際載入**版本 |
|
||||
| 實際載入版本 | 讀 `installed_plugins.json` 的 `installPath` 底下那份 `plugin.json`,**不可只看註冊欄位**——兩者可能不同,只看註冊值會放過真正被載入的舊版 |
|
||||
| 落後 | 擋下該次技能呼叫(exit 2),並印出更新指令 |
|
||||
| 相等或超前 | 放行。開發技能組時本機本來就會超前 `master`,擋下去維護者自己動不了 |
|
||||
| 查不到遠端版本 | **擋**(fail-closed)。查詢失敗會重試一次,仍失敗才擋 |
|
||||
| 比對對象 | 遠端發佈版本(存取庫**預設分支**的 `plugin.json`,經 `jsc-gitea/tools/gitea.sh` 讀取,不寫死分支名)對本機**實際載入**版本 |
|
||||
| 實際載入版本 | 只認 `installed_plugins.json` 的 `installPath` 底下那份 `plugin.json`。註冊在 `installed_plugins.json` 的 `version` 欄位**不當備援**——註冊值可能比實際載入的版本新,拿它來比對會放過真正被載入的舊版 |
|
||||
| 落後 | 擋下該次技能呼叫(exit 2),並印出更新指令。**只有這一種情況會擋** |
|
||||
| 相等或超前 | 放行。開發技能組時本機本來就會超前預設分支,擋下去維護者自己動不了 |
|
||||
| 查不到本機載入版本 | **放行**(exit 0,安靜降級)。讀不到 `installed_plugins.json`、裡面沒有該 plugin 的條目、取不到 `installPath`、`installPath` 底下那份 `plugin.json` 讀不到,四種都算這一列,不退回註冊欄位 |
|
||||
| 解不出 Gitea 站台 | **放行**(exit 0,安靜降級) |
|
||||
| 查不到遠端版本 | **放行**(exit 0,安靜降級)。缺基礎設施不等於落後,擋下去會把四支非 Claude CLI 整批鎖死 |
|
||||
| 逃生門 | `JSC_VERSION_GUARD=off`(離線工作用),快取秒數 `JSC_VERSION_TTL`(預設 600) |
|
||||
|
||||
**豁免清單**(永遠放行,改動前想清楚後果):
|
||||
@@ -122,7 +126,7 @@
|
||||
新增或更新技能後逐項檢查,任一不符就修正:
|
||||
|
||||
- [ ] 名稱符合命名規則,且與既有技能目標不重複
|
||||
- [ ] description 為英文、≤ 5 句或 5 步驟、含觸發時機
|
||||
- [ ] description 為英文、≤ 5 句或 ≤ 5 步驟(滿足任一即通過)、含觸發時機(何時用、何時不用)
|
||||
- [ ] 可 hook 的規則已下放 jsc-hooks;可工具化的流程已下放 tools/;SKILL.md 沒有保留可由標準輸入輸出執行的細節流程
|
||||
- [ ] 細節流程已標示 MUST run as a sub agent
|
||||
- [ ] gitea 操作透過 gitea.sh 或 tea
|
||||
|
||||
@@ -9,7 +9,7 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
|
||||
|
||||
## Flow
|
||||
|
||||
1. Query the Gitea canonical marketplace for the authoritative domain list: run `jsc-gitea/tools/gitea.sh api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json`. Clone any domain repo missing from the working directory (`gitea.sh clone-url plugins/{domain}`) and pull the rest. Completion condition: every domain repo exists locally and is current.
|
||||
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.
|
||||
2. 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. Completion condition: every domain has an audit result.
|
||||
3. Present each failed item via the `jsc-ask:ask` decision tree (apply the proposed fix / skip / custom fix). Every option states its impact scope (example: skipping leaves the skill non-compliant until the next audit). Completion condition: every finding has a recorded decision.
|
||||
4. Apply the confirmed fixes — the fix-application part MUST run as a sub agent, one sub agent per affected domain repo: modify the files per the confirmed fix. 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 fixes and the manifest bump.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: skill-delete
|
||||
description: Remove a skill from the jsc skill set safely. List skills from the Gitea canonical marketplace (cloning any missing domain repo) and let the user pick, inventory every file referencing the skill via a sub agent, fix each affected file through decision-tree questions until guideline checks pass, then delete the skill, verify the removal is clean, and open a PR via jsc-git pr. 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 until guideline checks pass, delete the skill and verify no on-disk leftover in any CLI, then open a PR via jsc-git pr. Use only for removal; not for renaming (use skill-update).
|
||||
---
|
||||
|
||||
# skill-delete — delete a skill
|
||||
@@ -9,14 +9,22 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
|
||||
|
||||
## Flow
|
||||
|
||||
1. Query Gitea for the canonical skill set first: run `jsc-gitea/tools/gitea.sh api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json` to get the authoritative domain list, then scan local `jsc-*/skills/*/SKILL.md` and present a "domain / name / description" list covering every domain in the marketplace. Completion condition: the list covers all marketplace domains.
|
||||
2. For any marketplace domain whose repo is missing from the working directory, clone it first (`gitea.sh clone-url plugins/{domain}`), then rescan. Completion condition: every domain repo exists locally.
|
||||
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.
|
||||
4. Inventory every file related to the skill: run `tools/find-skill-refs.sh {domain} {name}` to list every file across all jsc-* repositories referencing the skill name or its `/jsc-{domain}:{name}` command form (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). Completion condition: the tool's file list is captured for step 5.
|
||||
5. For each affected file:
|
||||
1. Decide whether the file needs a fix to keep its current behavior after the deletion. If no fix is needed, **skip the rest of this loop**.
|
||||
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.
|
||||
3. After fixing, check the guidelines.md audit checklist. On failure, return to step 5.2.
|
||||
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.
|
||||
7. Deep-delete verification — this step MUST run as a sub agent: after deleting via each CLI's native plugin commands, physically inspect every installed CLI's on-disk skill and hook storage. Detect CLIs via `jsc-cli/tools/detect-clis.sh`; check Claude's `~/.claude/plugins/cache/` and hook entries in settings, plus the equivalent locations for codex / copilot / antigravity / kiro. Confirm no file or hook wiring for the deleted skill remains. Completion condition: every location checked and clean; remove any leftover by hand and recheck.
|
||||
8. Call `jsc-git:pr` to open a Push Request.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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:
|
||||
- Exit 1 — leftovers printed as `{file}:{line}:{content}`. Remove every one by hand, then rerun.
|
||||
- Exit 2 — usage error. Fix the two arguments and rerun.
|
||||
- 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.
|
||||
- Exit 0 — no leftover in the locations listed on stderr.
|
||||
|
||||
Completion condition: the script exits 0, or exit 3 is reported to the user and recorded in the PR.
|
||||
8. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back.
|
||||
|
||||
+19
-10
@@ -10,18 +10,27 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
|
||||
## Flow
|
||||
|
||||
1. Ask for skill details via the `jsc-ask:ask` decision tree until no doubt remains:
|
||||
- Goal (single and not duplicating an existing skill; first scan `jsc-*/skills/*/SKILL.md` and list similar skills for comparison — options must state the impact scope of "reuse existing" versus "create new")
|
||||
- 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")
|
||||
- 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)
|
||||
- Owning domain (list the existing `jsc-*` domains to choose from)
|
||||
2. If the domain does not exist (if the repository exists on Gitea but not in the working directory, clone it and skip to step 3):
|
||||
1. Propose one short English word for the new domain (a single word preferred) and confirm it with the user.
|
||||
2. Ask the user to create the repository `plugins/{domain}`. Clone it, then build the content following the structure of `https://gitea.jsc.idv.tw/plugins/template`: three plugin manifests (plugin name `jsc-{domain}`, version starting at `0.0.1`), `skills/`, README.md, AGENTS.md.
|
||||
3. Add the plugin entry (URL source pointing at the new repository) to `.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json` in `plugins/meta` (the canonical copy), then sync the two updated files to every domain repository, including the new one. Every repo carries the same marketplace files, so any repo works as the registration entry point. The sync MUST run as a sub agent.
|
||||
- Owning domain (run `tools/sync-domains.sh` and offer its domain list — the domains registered in the canonical marketplace)
|
||||
|
||||
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):
|
||||
1. Propose one short English word for the new domain (a single word preferred) and confirm it with the user. Completion condition: the user confirms the domain word.
|
||||
2. Check before creating: run `jsc-gitea/tools/gitea.sh clone-url plugins/{domain}`. A URL comes back when the repository already exists — clone it, skip creation, and go on to step 2.3 to fill in whatever content is missing. Only when no URL comes back create the repository through the tool, never by hand: `gitea.sh api POST /orgs/plugins/repos` when `plugins` is an organization, `POST /user/repos` when `plugins` is the token's own account (`tea repo create` does the same job). Only when the call is refused (403 — the token has write but not admin rights on the owner) ask the user to create `plugins/{domain}` by hand, then continue. Completion condition: `gitea.sh clone-url plugins/{domain}` prints a URL and cloning it succeeds.
|
||||
3. Build the content following the structure of `https://gitea.jsc.idv.tw/plugins/template`: three plugin manifests (plugin name `jsc-{domain}`, version starting at `0.0.1`), `skills/`, README.md, AGENTS.md. Completion condition: the three manifests, `skills/`, README.md and AGENTS.md all exist in the new repo.
|
||||
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 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 0 — every copy holds identical bytes; the script verifies that itself.
|
||||
|
||||
Completion condition: the script exits 0 and prints the touched paths.
|
||||
3. Generate the skill per guidelines.md — this step MUST run as a sub agent:
|
||||
- `skills/{name}/SKILL.md`: entirely in English (description ≤ 5 sentences with trigger conditions; 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/`
|
||||
|
||||
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.
|
||||
4. Self-check every item of the guidelines.md audit checklist; fix anything that fails.
|
||||
5. Call `jsc-git:pr` to open a Push Request.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: skill-update
|
||||
description: Update an 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, then re-check against the guidelines checklist until it passes and open a PR via jsc-git pr. Use for modifying a skill; not for creating (skill-new) or removing (skill-delete).
|
||||
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, then re-check against the guidelines checklist until it passes and open a PR via jsc-git pr. 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
|
||||
@@ -9,10 +9,10 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
|
||||
|
||||
## Flow
|
||||
|
||||
1. Query Gitea for the canonical skill set first: run `jsc-gitea/tools/gitea.sh api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json` to get the authoritative domain list, then scan local `jsc-*/skills/*/SKILL.md` and present a "domain / name / description" list covering every domain in the marketplace. Completion condition: the list covers all marketplace domains.
|
||||
2. For any marketplace domain whose repo is missing from the working directory, clone it first (`gitea.sh clone-url plugins/{domain}`), then rescan. Completion condition: every domain repo exists locally.
|
||||
3. Let the user pick the skill to update.
|
||||
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).
|
||||
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.
|
||||
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.
|
||||
7. Call `jsc-git:pr` to open a Push Request.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -10,7 +10,7 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
|
||||
## 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.
|
||||
2. Query the Gitea canonical marketplace for the authoritative domain list: run `jsc-gitea/tools/gitea.sh api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json`. Clone any domain repo missing from the working directory (`gitea.sh clone-url plugins/{domain}`) and pull the rest. Completion condition: every domain repo exists locally and is current.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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, 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 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.
|
||||
---
|
||||
|
||||
# ste100-sync
|
||||
@@ -9,17 +9,22 @@ Keep `references/ste100.md` in sync with its upstream source, [speak-human-tw](h
|
||||
|
||||
## Steps
|
||||
|
||||
1. Read the pinned version from the「上游版本」line in `references/ste100.md`.
|
||||
2. Clone the upstream repo (`--depth 1`). Read the `version` and `changelog` fields in its `SKILL.md` frontmatter.
|
||||
3. Same version: report「上游沒有新版」and stop.
|
||||
4. Newer version — distill the changes. **MUST run as a sub agent**:
|
||||
1. Read the pinned version from the「上游版本」line in `references/ste100.md`. Completion condition: the pinned version string is in hand.
|
||||
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.
|
||||
3. Same version: report「上游沒有新版」and stop. Completion condition: either the run stops here, or the upstream version is newer than the pinned one.
|
||||
4. Newer version — distill the changes into a change list. **MUST run as a sub agent**:
|
||||
- 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.
|
||||
- Drop marketing-copy scenes, eval material, and workflow-mode changes.
|
||||
- Apply the distilled changes to `references/ste100.md`. Keep its trimmed structure. Update the「上游版本」line.
|
||||
5. If the replacement table or cliché list changed, update the `TERMS` and `CLICHES` patterns in `tools/ste100-lint.sh`.
|
||||
6. Run `tools/ste100-lint.sh` over every jsc repo. Fix hits in files this repo owns; report hits elsewhere.
|
||||
7. Open a PR via `jsc-git:pr`.
|
||||
- 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
10. Open a PR via `jsc-git:pr`. Completion condition: a PR URL comes back.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -4,15 +4,28 @@
|
||||
# 用法: find-skill-refs.sh <domain> <skill-name>
|
||||
#
|
||||
# 掃描樣式:
|
||||
# 1. 技能名稱字面 {skill-name}
|
||||
# 1. 技能名稱字面 {skill-name}——**純子字串比對**,不看邊界。
|
||||
# 所以短名稱也會命中「名稱+後綴」的其他技能名,以及純敘述文字裡的同一個詞。
|
||||
# 這是故意的:盤點寧可多列,漏一個引用會讓刪除技能少改檔案。
|
||||
# 呼叫端必須逐檔看過命中內容再判斷,不可把清單當成「一定要改的檔案」。
|
||||
# 2. 指令形式 /jsc-{domain}:{skill-name}
|
||||
# 這兩個樣式已涵蓋其他 SKILL.md 的引用、domain README 的「Skills 目錄」小節、
|
||||
# plugins/meta 兩份 marketplace.json 與其在各 domain 存取庫的同步副本、tools/ 腳本、
|
||||
# jsc-hooks 接線等位置——只要檔案裡出現技能名稱或指令形式就會被列出。
|
||||
#
|
||||
# 掃描範圍: /root/plugins 底下所有 domain 存取庫,排除 .git 目錄。
|
||||
# 輸出: 命中檔案清單(去重、排序),一行一個路徑;exit 0 一律成功,即使沒有命中。
|
||||
set -eu
|
||||
# 掃描範圍: **只掃正本 marketplace 上的 domain 存取庫**。
|
||||
# 工作區可能還放著不屬於技能組的存取庫(例 shared、persona、code、doc)與
|
||||
# 點開頭的目錄(例 .jsc-monorepo-archive、.kiro),掃進去會讓刪除技能改到
|
||||
# 技能組以外的檔案。domain 清單取自 marketplace.json,不寫死。
|
||||
# 每個 domain 先找 {root}/{domain},再找 {root}/jsc-{domain};兩者都沒有就略過。
|
||||
# 每個存取庫內排除 .git 目錄。
|
||||
# 根目錄預設取本腳本位置的上兩層(meta/tools -> meta -> 根),換機器不必改腳本;
|
||||
# 用 JSC_PLUGINS_ROOT 覆寫,指向別處的技能組工作目錄。
|
||||
# 輸出: 命中檔案清單(去重、排序),一行一個路徑(stdout);掃描摘要走 stderr。
|
||||
# 結束碼: 0=有命中 1=掃完但零命中 2=用法錯誤
|
||||
# 3=找不到根目錄、讀不到 domain 清單、本機一個 domain 存取庫都沒有,或掃描失敗
|
||||
# 零命中與掃描失敗必須分開:拿掃描失敗當「沒有引用」會讓刪除技能少改檔案。
|
||||
set -u
|
||||
|
||||
usage() {
|
||||
echo 'usage: find-skill-refs.sh <domain> <skill-name>' >&2
|
||||
@@ -22,13 +35,51 @@ usage() {
|
||||
[ "$#" -eq 2 ] || usage
|
||||
DOMAIN=$1
|
||||
SKILL=$2
|
||||
[ -n "$DOMAIN" ] && [ -n "$SKILL" ] || usage
|
||||
|
||||
ROOT=/root/plugins
|
||||
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄: $ROOT" >&2; exit 1; }
|
||||
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}"
|
||||
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 3; }
|
||||
|
||||
# 正本 domain 清單:優先讀本機任一份 marketplace 副本(每個 repo 都帶同一份)。
|
||||
mkt=""
|
||||
for cand in "$HERE/../.claude-plugin/marketplace.json" "$ROOT"/*/.claude-plugin/marketplace.json; do
|
||||
[ -f "$cand" ] && { mkt="$cand"; break; }
|
||||
done
|
||||
[ -n "$mkt" ] || { echo "找不到 marketplace.json,無法判定 domain 清單" >&2; exit 3; }
|
||||
DOMAINS=$(sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"jsc-\([a-z0-9-]*\)".*/\1/p' "$mkt" | sort -u)
|
||||
[ -n "$DOMAINS" ] || { echo "marketplace.json 裡沒有 jsc-{domain} 條目:$mkt" >&2; exit 3; }
|
||||
|
||||
# 只收本機存在、且不是點開頭目錄的 domain 存取庫路徑。
|
||||
TARGETS=""
|
||||
for d in $DOMAINS; do
|
||||
for cand in "$ROOT/$d" "$ROOT/jsc-$d"; do
|
||||
[ -d "$cand" ] || continue
|
||||
TARGETS="$TARGETS $cand"
|
||||
break
|
||||
done
|
||||
done
|
||||
[ -n "$TARGETS" ] || { echo "本機找不到任何 domain 存取庫:$ROOT(先跑 sync-domains.sh)" >&2; exit 3; }
|
||||
|
||||
PATTERN1="$SKILL"
|
||||
PATTERN2="/jsc-${DOMAIN}:${SKILL}"
|
||||
|
||||
grep -rlE --exclude-dir=.git -e "$PATTERN1" -e "$PATTERN2" "$ROOT" 2>/dev/null | sort -u
|
||||
TMP=$(mktemp) || { echo "無法建立暫存檔" >&2; exit 3; }
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
# grep 的結束碼要單獨看:接管線的話 sort 永遠回 0,掃描失敗就被吃掉了。
|
||||
# shellcheck disable=SC2086
|
||||
grep -rlI --exclude-dir=.git -e "$PATTERN1" -e "$PATTERN2" $TARGETS > "$TMP" 2>/dev/null
|
||||
status=$?
|
||||
[ "$status" -le 1 ] || { echo "掃描失敗:$ROOT(grep 結束碼 $status)" >&2; exit 3; }
|
||||
OUT=$(sort -u "$TMP")
|
||||
|
||||
count=$(printf '%s' "$TARGETS" | wc -w | tr -d ' ')
|
||||
if [ -z "$OUT" ]; then
|
||||
echo "掃描完成,零命中:$count 個 domain 存取庫" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$OUT"
|
||||
printf '掃描完成,命中 %s 個檔案(掃了 %s 個 domain 存取庫;技能名稱為純子字串比對,請逐檔確認)\n' \
|
||||
"$(printf '%s\n' "$OUT" | wc -l | tr -d ' ')" "$count" >&2
|
||||
exit 0
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env sh
|
||||
# list-skills.sh — 列出本機所有 domain 存取庫的技能,供選技能與稽核用。
|
||||
#
|
||||
# 用法: list-skills.sh(不吃參數)
|
||||
#
|
||||
# 掃描: {root}/*/skills/*/SKILL.md,讀 frontmatter 的 name 與 description。
|
||||
# domain 取存取庫目錄名,開頭的 jsc- 會去掉。
|
||||
# **只列正本 marketplace 上的 domain**:工作區可能還放著不屬於技能組的
|
||||
# 存取庫(例 shared、persona、code、doc),列出來會讓選技能的流程
|
||||
# 誤把它們當成技能組的一部分。domain 清單取自 marketplace.json,不寫死。
|
||||
# 清單只反映本機檔案;要確保檔案齊全請先跑 sync-domains.sh。
|
||||
#
|
||||
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。
|
||||
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。
|
||||
#
|
||||
# 輸出: 一行一個技能,格式 {domain}<TAB>{name}<TAB>{description},依 domain、name 排序。
|
||||
# 結束碼: 0=至少列出一個技能 1=找不到根目錄、讀不到 domain 清單,或一個技能都沒有
|
||||
set -u
|
||||
|
||||
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}"
|
||||
|
||||
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 1; }
|
||||
|
||||
# 正本 domain 清單:優先讀本機任一份 marketplace 副本(每個 repo 都帶同一份)。
|
||||
mkt=""
|
||||
for cand in "$HERE/../.claude-plugin/marketplace.json" "$ROOT"/*/.claude-plugin/marketplace.json; do
|
||||
[ -f "$cand" ] && { mkt="$cand"; break; }
|
||||
done
|
||||
[ -n "$mkt" ] || { echo "找不到 marketplace.json,無法判定 domain 清單" >&2; exit 1; }
|
||||
DOMAINS=$(sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"jsc-\([a-z0-9-]*\)".*/\1/p' "$mkt" | sort -u)
|
||||
[ -n "$DOMAINS" ] || { echo "marketplace.json 裡沒有 jsc-{domain} 條目:$mkt" >&2; exit 1; }
|
||||
|
||||
OUT=$(
|
||||
for f in "$ROOT"/*/skills/*/SKILL.md; do
|
||||
[ -f "$f" ] || continue
|
||||
repo=${f#"$ROOT"/}
|
||||
repo=${repo%%/*}
|
||||
domain=${repo#jsc-}
|
||||
# 不在正本清單上的存取庫直接跳過。
|
||||
printf '%s\n' "$DOMAINS" | grep -qx "$domain" || continue
|
||||
name=$(awk 'BEGIN{f=0} /^---$/{f++; next} f==1 && /^name:/{sub(/^name: */,""); print; exit}' "$f")
|
||||
desc=$(awk 'BEGIN{f=0} /^---$/{f++; next} f==1 && /^description:/{sub(/^description: */,""); print; exit}' "$f")
|
||||
[ -n "$name" ] || continue
|
||||
printf '%s\t%s\t%s\n' "$domain" "$name" "$desc"
|
||||
done | sort -t"$(printf '\t')" -k1,1 -k2,2
|
||||
)
|
||||
|
||||
[ -n "$OUT" ] || { echo "掃不到任何 SKILL.md:$ROOT/*/skills/*/SKILL.md(先跑 sync-domains.sh)" >&2; exit 1; }
|
||||
|
||||
printf '%s\n' "$OUT"
|
||||
count=$(printf '%s\n' "$OUT" | wc -l | tr -d ' ')
|
||||
echo "共 $count 個技能" >&2
|
||||
exit 0
|
||||
+25
-2
@@ -5,15 +5,36 @@
|
||||
# 1. 中國用語(references/ste100.md 的替換表左欄)
|
||||
# 2. 中文句內的半形標點(, ; ! ? 緊鄰中日韓字元)
|
||||
# 3. 常見 AI 套話(總的來說、綜上所述、希望這對你有幫助 等)
|
||||
# 4. 簡體字(只收沒有繁體正當用法的字,避免誤報)
|
||||
# 5. 中文並列項用斜線(半形 / 或全形 / 夾在中日韓字元之間),應改頓號「、」
|
||||
# 輸出: {檔案}:{行號}:{類別}:{命中內容};全部通過 exit 0,有命中 exit 1。
|
||||
# 限制: 只掃 .md 檔;程式碼圍欄內的內容可能誤報,人工複核。
|
||||
# 限制: 掃 .md 與 .json 檔;程式碼圍欄內的內容可能誤報,人工複核。
|
||||
# references/ste100.md 本身跳過——規則文件裡的詞是被討論,不是被使用。
|
||||
# 並列斜線會誤報兩類:含中文的路徑或分支名範例(例 feat/報表/P2),
|
||||
# 以及英文項目並列(準則允許)。命中後先判斷是不是真的並列項。
|
||||
set -u
|
||||
TERMS='默認|支持某|兼容|信息|數據庫|服務器|軟件|硬件|網絡|質量|卸載|反饋|視頻|屏幕|鼠標|打印|立馬|靠譜|賦能|閉環|抓手|復盤'
|
||||
CLICHES='總的來說|綜上所述|希望這對你有幫助|好問題|在當今|瞬息萬變|標誌著|奠定了基礎|體現了|不僅僅是|讓我們一起'
|
||||
# 刻意排除繁體也在用的字(后、台、干、只、里、面、制、志),只留簡化後才出現的字形。
|
||||
SIMPLIFIED='应|为|这|说|发|国|过|对|开|关|问|题|东|车|马|鸟|龙|飞|见|无|产|业|务|书|写|学|习|报|纸|认|识|证|际|网|络|计|划|实|现|给|条|约|级|组|织|变|换|电|脑|两|双|单|构|价|钱|众|议|论|传|统|么|儿|们|从|来|时|间|长|门|闻|声|员|图|团|转|输|达|运|进|远|连|边|还|经|该|营|规|则|范|围|参|数|类|结|态|设|备|辑|译|码|库|档'
|
||||
hit=0
|
||||
# 不帶參數會空跑並回 0,看起來像「全部通過」。這種假通過比不檢查更危險,所以擋掉。
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo '用法: ste100-lint.sh <file|dir> [...]' >&2
|
||||
echo '沒有給檢查對象。空跑會回 0,看起來像通過,所以這裡直接視為錯誤。' >&2
|
||||
exit 2
|
||||
fi
|
||||
files() {
|
||||
for p in "$@"; do
|
||||
if [ -d "$p" ]; then find "$p" -name '*.md' -not -path '*/.git/*'; else echo "$p"; fi
|
||||
if [ -d "$p" ]; then
|
||||
find "$p" \( -name '*.md' -o -name '*.json' \) \
|
||||
-not -path '*/.git/*' -not -path '*/references/ste100.md'
|
||||
else
|
||||
case "$p" in
|
||||
*/references/ste100.md|references/ste100.md) ;;
|
||||
*) echo "$p" ;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
}
|
||||
check() { # $1=檔案 $2=類別 $3=grep 模式 $4=grep 旗標
|
||||
@@ -24,5 +45,7 @@ for f in $(files "$@"); do
|
||||
check "$f" "中國用語" "$TERMS" E
|
||||
check "$f" "半形標點" '[\x{4e00}-\x{9fff}][,;!?]|[,;][\x{4e00}-\x{9fff}]' P
|
||||
check "$f" "AI 套話" "$CLICHES" E
|
||||
check "$f" "簡體字" "$SIMPLIFIED" E
|
||||
check "$f" "並列斜線" '[\x{4e00}-\x{9fff}][ ]?[//][ ]?[\x{4e00}-\x{9fff}]' P
|
||||
done
|
||||
exit $hit
|
||||
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env sh
|
||||
# sync-domains.sh — 依 Gitea 正本 marketplace 把所有 domain 存取庫同步到本機。
|
||||
#
|
||||
# 用法: sync-domains.sh(不吃參數)
|
||||
#
|
||||
# 流程:
|
||||
# 1. 透過 jsc-gitea/tools/gitea.sh 讀正本 marketplace
|
||||
# (plugins/meta 的 .claude-plugin/marketplace.json),取得權威 domain 清單。
|
||||
# 2. 本機缺少的 domain:用 gitea.sh clone-url plugins/{domain} 取網址後 clone。
|
||||
# 3. 本機已有的 domain:git pull。工作區有未提交變更就跳過 pull,只在 stderr 提醒——
|
||||
# 正在改的 repo 不該被自動 pull 覆蓋。
|
||||
#
|
||||
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。
|
||||
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。
|
||||
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain};都沒有才 clone 成 {root}/{domain}。
|
||||
#
|
||||
# 輸出: 一行一個 domain,格式 {domain}<TAB>{path}(stdout);警告與失敗說明走 stderr。
|
||||
# 結束碼: 0=正本讀到、每個 domain 都在本機,而且每個既有存取庫都更新到最新
|
||||
# 1=讀不到正本 marketplace 或找不到 gitea.sh(不輸出任何 domain)
|
||||
# 2=正本讀到,但有 domain 沒能取得或 clone 失敗(已輸出其餘 domain)
|
||||
# 3=每個 domain 都在本機,但有存取庫沒更新到最新:工作區脏而跳過 pull,
|
||||
# 或 pull 失敗。stderr 會逐一列出這些路徑。**exit 0 才代表「全部最新」**;
|
||||
# 拿 3 當乾淨會讓後續步驟改在落後的檔案上。
|
||||
# 同時發生時回較嚴重的那個:2 蓋過 3。
|
||||
# 環境變數: JSC_PLUGINS_ROOT(根目錄)
|
||||
# JSC_SYNC_DRY_RUN=1(只印出會做什麼,不動檔案;不執行 pull/clone,
|
||||
# 所以也不因為「沒更新」回 3)
|
||||
set -u
|
||||
|
||||
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}"
|
||||
DRY="${JSC_SYNC_DRY_RUN:-}"
|
||||
|
||||
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 1; }
|
||||
|
||||
GITEA=""
|
||||
for g in "$ROOT/gitea/tools/gitea.sh" "$ROOT/jsc-gitea/tools/gitea.sh"; do
|
||||
[ -f "$g" ] && { GITEA="$g"; break; }
|
||||
done
|
||||
[ -n "$GITEA" ] || { echo "找不到 gitea.sh:請先取得 jsc-gitea 存取庫" >&2; exit 1; }
|
||||
|
||||
mkt=$(sh "$GITEA" api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json 2>/dev/null) || mkt=""
|
||||
[ -n "$mkt" ] || { echo "讀不到正本 marketplace:檢查 GITEA_HOST、GITEA_TOKEN 或 tea 登入" >&2; exit 1; }
|
||||
|
||||
DOMAINS=$(printf '%s\n' "$mkt" | sed -n 's/.*"name": *"jsc-\([a-z0-9-]*\)".*/\1/p' | sort -u)
|
||||
[ -n "$DOMAINS" ] || { echo "正本 marketplace 裡沒有 jsc-{domain} 條目" >&2; exit 1; }
|
||||
|
||||
repo_path() { # $1=domain;印出既有路徑(return 0)或預設路徑(return 1)
|
||||
for d in "$ROOT/$1" "$ROOT/jsc-$1"; do
|
||||
[ -d "$d" ] && { printf '%s' "$d"; return 0; }
|
||||
done
|
||||
printf '%s' "$ROOT/$1"
|
||||
return 1
|
||||
}
|
||||
|
||||
rc=0
|
||||
stale=0
|
||||
for domain in $DOMAINS; do
|
||||
if path=$(repo_path "$domain"); then
|
||||
if [ -n "$(git -C "$path" status --porcelain 2>/dev/null)" ]; then
|
||||
echo "工作區有未提交變更,跳過 pull,未更新到最新:$path" >&2
|
||||
stale=1
|
||||
elif [ "$DRY" = 1 ]; then
|
||||
echo "[dry-run] git -C $path pull --ff-only" >&2
|
||||
elif ! git -C "$path" pull --quiet --ff-only >/dev/null 2>&1; then
|
||||
echo "pull 失敗,保留本機版本,未更新到最新:$path" >&2
|
||||
stale=1
|
||||
fi
|
||||
else
|
||||
url=$(sh "$GITEA" clone-url "plugins/$domain" 2>/dev/null) || url=""
|
||||
if [ -z "$url" ]; then
|
||||
echo "取不到 clone 網址,略過:plugins/$domain" >&2
|
||||
rc=2
|
||||
continue
|
||||
fi
|
||||
if [ "$DRY" = 1 ]; then
|
||||
echo "[dry-run] git clone $url $path" >&2
|
||||
elif ! git clone --quiet "$url" "$path" >/dev/null 2>&1; then
|
||||
echo "clone 失敗:$url" >&2
|
||||
rc=2
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
printf '%s\t%s\n' "$domain" "$path"
|
||||
done
|
||||
|
||||
# 2(取不到或 clone 失敗)比 3(沒更新到最新)嚴重,優先回報。
|
||||
[ "$rc" -eq 0 ] && [ "$stale" -eq 1 ] && rc=3
|
||||
exit $rc
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env sh
|
||||
# sync-marketplace.sh — 把一個 plugin 條目寫進正本 marketplace,再同步到所有 domain 存取庫。
|
||||
#
|
||||
# 用法: sync-marketplace.sh <domain> <repo-url> <description>
|
||||
# 例: sync-marketplace.sh doc https://gitea.jsc.idv.tw/plugins/doc.git "文件產出與同步"
|
||||
#
|
||||
# 行為:
|
||||
# 1. 在 plugins/meta 的兩份正本(.claude-plugin/marketplace.json 與
|
||||
# .agents/plugins/marketplace.json)插入或更新 jsc-{domain} 條目;已存在就覆寫網址與描述。
|
||||
# 條目依 name 排序,格式維持兩格縮排、不轉義非 ASCII、結尾一個換行。
|
||||
# 2. 兩份正本與所有副本都由同一份算好的內容複製過去,保證位元組完全一致。
|
||||
# 3. 逐一複製到每個 domain 存取庫的相同兩個路徑(含新 domain 自己)。
|
||||
# 4. 全部寫完後逐檔比對,有任何一份不一致就報錯。
|
||||
#
|
||||
# 依賴: **python3**(只用標準函式庫的 json 模組)。
|
||||
# 本 repo 的腳本一律 shell 優先,這裡是唯一例外:條目的插入、排序與重新序列化要動 JSON,
|
||||
# 純 shell 拼 JSON 會壞掉(縮排、逸出、非 ASCII 描述)。所以保留 python3。
|
||||
# python3 不在 PATH 時:腳本不寫任何檔案,印出缺少 python3 的訊息,回 1。
|
||||
# 呼叫端要先裝 python3 再重跑,不可改用 sed 手動改 marketplace。
|
||||
#
|
||||
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。
|
||||
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。
|
||||
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain}。
|
||||
#
|
||||
# 輸出: 一行一個實際寫入的檔案路徑(stdout);略過與失敗說明走 stderr。
|
||||
# 結束碼: 0=全部寫入且逐檔一致 2=用法錯誤
|
||||
# 1=缺 python3、正本讀寫失敗,或有檔案比對不一致
|
||||
# 3=寫入成功,但有 domain 存取庫不在本機(先跑 sync-domains.sh 再重跑)
|
||||
set -u
|
||||
|
||||
usage() {
|
||||
echo 'usage: sync-marketplace.sh <domain> <repo-url> <description>' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ "$#" -eq 3 ] || usage
|
||||
DOMAIN=$1
|
||||
URL=$2
|
||||
DESC=$3
|
||||
[ -n "$DOMAIN" ] && [ -n "$URL" ] && [ -n "$DESC" ] || usage
|
||||
|
||||
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
META=$(CDPATH= cd -- "$HERE/.." && pwd)
|
||||
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}"
|
||||
|
||||
REL_CLAUDE='.claude-plugin/marketplace.json'
|
||||
REL_AGENTS='.agents/plugins/marketplace.json'
|
||||
CANON="$META/$REL_CLAUDE"
|
||||
|
||||
[ -f "$CANON" ] || { echo "找不到正本 marketplace:$CANON" >&2; exit 1; }
|
||||
|
||||
# 先擋掉缺 python3 的環境,才不會寫到一半才失敗。
|
||||
command -v python3 >/dev/null 2>&1 || {
|
||||
echo '找不到 python3:本腳本用 python3 的 json 模組改 marketplace 條目。請先安裝 python3 再重跑。' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
WORKDIR=$(mktemp -d) || exit 1
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
NEW="$WORKDIR/marketplace.json"
|
||||
|
||||
# 條目的插入與更新交給 json 解析器;用 sed 拼 JSON 會壞掉。
|
||||
JSC_MK_DOMAIN="$DOMAIN" JSC_MK_URL="$URL" JSC_MK_DESC="$DESC" \
|
||||
JSC_MK_IN="$CANON" JSC_MK_OUT="$NEW" python3 - <<'PY' || { echo "更新正本內容失敗:$CANON" >&2; exit 1; }
|
||||
import json, os
|
||||
|
||||
name = "jsc-" + os.environ["JSC_MK_DOMAIN"]
|
||||
entry = {
|
||||
"name": name,
|
||||
"source": {"source": "url", "url": os.environ["JSC_MK_URL"]},
|
||||
"description": os.environ["JSC_MK_DESC"],
|
||||
}
|
||||
with open(os.environ["JSC_MK_IN"], encoding="utf-8") as fh:
|
||||
data = json.load(fh)
|
||||
plugins = [p for p in data.get("plugins", []) if p.get("name") != name]
|
||||
plugins.append(entry)
|
||||
data["plugins"] = sorted(plugins, key=lambda p: p.get("name", ""))
|
||||
with open(os.environ["JSC_MK_OUT"], "w", encoding="utf-8") as fh:
|
||||
fh.write(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
|
||||
PY
|
||||
|
||||
rc=0
|
||||
|
||||
write_copy() { # $1=目標檔案
|
||||
target=$1
|
||||
dir=$(dirname "$target")
|
||||
mkdir -p "$dir" 2>/dev/null || { echo "無法建立目錄:$dir" >&2; rc=1; return 1; }
|
||||
cp "$NEW" "$target" 2>/dev/null || { echo "無法寫入:$target" >&2; rc=1; return 1; }
|
||||
printf '%s\n' "$target"
|
||||
}
|
||||
|
||||
# ---- 1. 兩份正本 ----
|
||||
write_copy "$META/$REL_CLAUDE"
|
||||
write_copy "$META/$REL_AGENTS"
|
||||
|
||||
# ---- 2. 每個 domain 存取庫的副本 ----
|
||||
DOMAINS=$(sed -n 's/.*"name": *"jsc-\([a-z0-9-]*\)".*/\1/p' "$NEW" | sort -u)
|
||||
for d in $DOMAINS; do
|
||||
path=""
|
||||
for cand in "$ROOT/$d" "$ROOT/jsc-$d"; do
|
||||
[ -d "$cand" ] && { path="$cand"; break; }
|
||||
done
|
||||
if [ -z "$path" ]; then
|
||||
echo "存取庫不在本機,略過:$d(先跑 sync-domains.sh)" >&2
|
||||
[ "$rc" -eq 0 ] && rc=3
|
||||
continue
|
||||
fi
|
||||
[ "$path" = "$META" ] && continue
|
||||
write_copy "$path/$REL_CLAUDE"
|
||||
write_copy "$path/$REL_AGENTS"
|
||||
done
|
||||
|
||||
# ---- 3. 逐檔比對,確認位元組一致 ----
|
||||
for d in "$META" $(for x in $DOMAINS; do
|
||||
for cand in "$ROOT/$x" "$ROOT/jsc-$x"; do [ -d "$cand" ] && { echo "$cand"; break; }; done
|
||||
done); do
|
||||
for rel in "$REL_CLAUDE" "$REL_AGENTS"; do
|
||||
[ -f "$d/$rel" ] || continue
|
||||
cmp -s "$NEW" "$d/$rel" || { echo "內容不一致:$d/$rel" >&2; rc=1; }
|
||||
done
|
||||
done
|
||||
|
||||
exit $rc
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
# sync-skill-manifest.sh — 同步 domain 的 README「Skills 目錄」區塊,並把該 domain 所有
|
||||
# plugin manifest 的 version 各 bump 一個 patch。
|
||||
# plugin manifest 的 version 各 bump 一次,且 major 可超過 9;minor 與 patch 仍是單位數。
|
||||
#
|
||||
# 用法: sync-skill-manifest.sh <domain-path>
|
||||
#
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# manifest: 依序找出 domain 下存在的 plugin.json、.claude-plugin/plugin.json、
|
||||
# .codex-plugin/plugin.json,version 一律 bump 成同一個新值(以第一份找到的 manifest 版本為準,
|
||||
# patch 位加一;major.minor 不變)。
|
||||
# 右側數字加一,滿 9 就往左進位;major 不設上限,minor 與 patch 都不超過 9)。
|
||||
#
|
||||
# 輸出: 變更摘要——README 新增/移除的技能小節、各 manifest 的舊版本 -> 新版本。
|
||||
set -eu
|
||||
@@ -151,7 +151,20 @@ old=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' "$first" | head -1)
|
||||
major=$(printf '%s' "$old" | cut -d. -f1)
|
||||
minor=$(printf '%s' "$old" | cut -d. -f2)
|
||||
patch=$(printf '%s' "$old" | cut -d. -f3)
|
||||
new="$major.$minor.$((patch + 1))"
|
||||
|
||||
major=$((major + 0))
|
||||
minor=$((minor + 0))
|
||||
patch=$((patch + 1))
|
||||
while [ "$patch" -gt 9 ]; do
|
||||
patch=$((patch - 10))
|
||||
minor=$((minor + 1))
|
||||
done
|
||||
while [ "$minor" -gt 9 ]; do
|
||||
minor=$((minor - 10))
|
||||
major=$((major + 1))
|
||||
done
|
||||
|
||||
new="$major.$minor.$patch"
|
||||
|
||||
echo "== manifest version =="
|
||||
for m in $MANIFESTS; do
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env sh
|
||||
# verify-skill-removed.sh — 刪除技能後,實地檢查各 CLI 的磁碟上有沒有殘留。
|
||||
#
|
||||
# 用法: verify-skill-removed.sh <domain> <skill-name>
|
||||
#
|
||||
# 行為: 先用 jsc-cli/tools/detect-clis.sh 找出已安裝的 CLI,再逐一 grep 該 CLI 的技能快取
|
||||
# 與 hook 設定,找技能名稱字面與指令形式 /jsc-{domain}:{name}。
|
||||
# 未安裝的 CLI 不檢查;設定位置不存在就跳過(安靜降級,不算殘留)。
|
||||
#
|
||||
# 檢查位置(每個 CLI 的技能快取與 hook 設定):
|
||||
# claude — ~/.claude/plugins/、~/.claude/settings.json、~/.claude/settings.local.json
|
||||
# codex — ${CODEX_HOME:-~/.codex}/(config.toml 的 notify、AGENTS.md、plugins/)
|
||||
# copilot — ~/.config/copilot/
|
||||
# antigravity — ~/.antigravity/、~/.config/antigravity/
|
||||
# kiro — ~/.kiro/、工作目錄的 .kiro/hooks/
|
||||
#
|
||||
# 輸出: 檢查過的位置一行一個(stderr),殘留一行一個 {file}:{line}:{內容}(stdout)。
|
||||
# 沒有殘留會印「無殘留」到 stderr,區別於「什麼都沒檢查」。
|
||||
# 結束碼: 0=沒有殘留 1=有殘留 2=用法錯誤 3=沒偵測到任何 CLI 或找不到可檢查的位置
|
||||
set -u
|
||||
|
||||
usage() {
|
||||
echo 'usage: verify-skill-removed.sh <domain> <skill-name>' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ "$#" -eq 2 ] || usage
|
||||
DOMAIN=$1
|
||||
SKILL=$2
|
||||
[ -n "$DOMAIN" ] && [ -n "$SKILL" ] || usage
|
||||
|
||||
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}"
|
||||
|
||||
DETECT=""
|
||||
for d in "$ROOT/cli/tools/detect-clis.sh" "$ROOT/jsc-cli/tools/detect-clis.sh"; do
|
||||
[ -f "$d" ] && { DETECT="$d"; break; }
|
||||
done
|
||||
[ -n "$DETECT" ] || { echo "找不到 detect-clis.sh:請先取得 jsc-cli 存取庫" >&2; exit 3; }
|
||||
|
||||
CLIS=$(sh "$DETECT" 2>/dev/null | cut -f1)
|
||||
[ -n "$CLIS" ] || { echo "沒偵測到任何已安裝的 CLI,無處可查" >&2; exit 3; }
|
||||
|
||||
cli_paths() { # $1=CLI 代號;印出該 CLI 的技能快取與 hook 設定位置
|
||||
case "$1" in
|
||||
claude)
|
||||
printf '%s\n' "$HOME/.claude/plugins" "$HOME/.claude/settings.json" \
|
||||
"$HOME/.claude/settings.local.json" ;;
|
||||
codex)
|
||||
printf '%s\n' "${CODEX_HOME:-$HOME/.codex}" ;;
|
||||
copilot)
|
||||
printf '%s\n' "$HOME/.config/copilot" ;;
|
||||
antigravity)
|
||||
printf '%s\n' "$HOME/.antigravity" "$HOME/.config/antigravity" ;;
|
||||
kiro)
|
||||
printf '%s\n' "$HOME/.kiro" "./.kiro" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
checked=0
|
||||
hit=0
|
||||
for cli in $CLIS; do
|
||||
for p in $(cli_paths "$cli"); do
|
||||
[ -e "$p" ] || continue
|
||||
checked=$((checked + 1))
|
||||
echo "檢查 $cli:$p" >&2
|
||||
out=$(grep -rnI --exclude-dir=.git -e "$SKILL" -e "/jsc-${DOMAIN}:${SKILL}" "$p" 2>/dev/null)
|
||||
if [ -n "$out" ]; then
|
||||
printf '%s\n' "$out"
|
||||
hit=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
[ "$checked" -gt 0 ] || { echo "偵測到 CLI,但沒有任何設定位置存在,無處可查" >&2; exit 3; }
|
||||
[ "$hit" -eq 0 ] && echo "無殘留:已檢查 $checked 個位置" >&2
|
||||
exit $hit
|
||||
Reference in New Issue
Block a user