feat(shared): 新增14個共用spec、models/todo工具與樣板產生器,收斂跨repo重複規範

依 todo.md 執行的規範治理專案:新增 spec-preflight 等 14 個共用規範(含
conventional-commit/pull-request/git-push/issue-read/todo-list/ask-user/
subagent/no-scratch-files/skill-invocation/script-path/action-scaffold/
node-src-layout/plugin-cli/model),擴充 spec-git-safety 與 spec-gitea(token
優先序、機密遮蔽、Wiki 頁名轉義規則);新增可執行 skill `models`(模型能力
查詢與標籤)與 `todo`(依指定模型產生/附加 todo.md);新增 plugin.meta.json
單一事實來源與 gen-plugin-files.mjs 樣板產生器,統一四個 repo 的 manifest/
README/AGENTS.md 並移除寫死的本機使用者路徑;新增 shared/scripts/lib 的
log/機密遮蔽三語言參考實作。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 06:02:30 +00:00
co-authored by Claude Sonnet 5
parent d49ae1085d
commit 1030f9d403
38 changed files with 2329 additions and 139 deletions
+197
View File
@@ -0,0 +1,197 @@
---
name: models
description: 查詢目前可用哪些模型並依 spec-model 的固定標籤體系標註(能力等級/成本/延遲/上下文/用途/可用性),維護 `~/.claude/jsc/models.json` 快取,並依任務類型推薦模型或檢查當前模型是否符合指定模型。提供 `--refresh`(重跑探測與 smoke test 並重寫快取)、`--task <analysis|implement|review|summary|persona>`(依對映表推薦模型+一行理由+次選)、`--check <model>`(比對指定模型與當前模型,不符則依強制切換規則輸出錯誤並回非零狀態)、`--json`(結構化輸出)四個參數,預設模式讀快取(過期或不存在則自動探測)輸出模型 × 標籤表格。當使用者問現在有哪些模型可用、要幫某個任務選模型、要檢查目前模型對不對、要重新驗證模型可用性、或提到 `models` skill、模型標籤、模型快取、`~/.claude/jsc/models.json` 時觸發。不適用於:切換模型本身(使用者自行執行 `/model`)、產生指定模型的 `todo.md`(用 `/jsc-shared:todo`,它會呼叫本 skill 取推薦)、與模型選型無關的一般查詢。
argument-hint: "[--refresh] [--task <analysis|implement|review|summary|persona>] [--check <model>] [--json]"
---
# models — 取得可用模型並加上標籤
依 `/jsc-shared:spec-model` 的標籤體系與來源優先序,查詢目前帳號/CLI 可用的模型、標註標籤、維護快取,並提供任務推薦與模型檢查兩種決策輔助。**本 skill 是 spec-model 的唯一可執行入口**——其他 skill(如 `/jsc-shared:todo`、`worklog --tune`)需要模型清單或推薦時,一律呼叫本 skill,不得自行重寫探測或推薦邏輯。
| 模式 | 用途 | 是否重跑探測 |
| --- | --- | --- |
| 預設(無參數) | 輸出模型 × 標籤表格 | 快取有效才不跑;過期或不存在才自動探測 |
| `--refresh` | 強制重跑探測+smoke test,重寫快取 | 一律重跑 |
| `--task <type>` | 依任務對映表推薦模型 | 沿用當前有效快取(無效才先探測) |
| `--check <model>` | 檢查指定模型與當前模型是否一致 | 沿用當前有效快取(無效才先探測) |
`--json` 可疊加在任一模式上,改變輸出格式,不改變邏輯。
---
## 共用規範(必要前置)
先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝,
依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。
本 skill 需要的規範:`spec-model`、`spec-output`、`spec-execution`、`spec-time-log`、`spec-skill-invocation`
`spec-model` 是本 skill 的行為本體(標籤體系、任務對映表、來源優先序、快取設計、強制切換規則五節),下文只描述**本 skill 如何呼叫這五節**,不重抄內容;標籤字彙、對映表、來源優先序、快取欄位、錯誤訊息格式如與本檔敘述有出入,一律以 `spec-model` 當次實際載入到的內容為準。
---
## 參數
| 參數 | 說明 | 預設 |
| --- | --- | --- |
| `--refresh` | 忽略快取新鮮度,強制依 spec-model 第三節重跑探測與 smoke test,重寫快取 | 不帶則只在快取過期/不存在時才探測 |
| `--task <analysis\|implement\|review\|summary\|persona>` | 依 spec-model 第二節對映表推薦模型 | 不帶則不做推薦,走預設表格輸出 |
| `--check <model>` | 指定模型 id 或別名,比對當前模型 | 不帶則不做檢查 |
| `--json` | 輸出結構化 JSON 取代 Markdown 表格/文字 | 不帶則輸出 Markdown |
**參數互斥與優先序**:`--refresh` 是修飾詞,可與任何模式並存(一律先重跑探測,再往下做該模式的動作)。`--check` 與 `--task` 同時出現時,兩者語意不同不建議並用;若使用者仍同時帶入,以 `--check` 為主要動作、`--task` 的推薦結果附帶輸出,並在回報開頭註明「同時指定 `--check` 與 `--task`,本次以 `--check` 的判定結果為準」。都未帶時走預設模式(表格輸出)。
---
## 快取
路徑與欄位固定依 `spec-model` 第四節:`~/.claude/jsc/models.json`,每筆 `{ id, aliases[], tags[], context, pricing, verified_at, verdict, reason }`,`verified_at` 超過 30 天視為過期。
- **讀取**:優先用助理的檔案讀取工具(Claude Code 為 Read)直接讀取並解析 JSON;shell 環境改用 `python3 -c "import json,sys; ..."` 解析,避免依賴 `jq`(是否安裝因環境而異)。
- **寫入**:優先用助理的檔案寫入工具(Write/Edit)整份覆寫,確保 UTF-8 無 BOM(依 `spec-output`);shell 環境改用 `python3` 組字典後 `json.dump(..., ensure_ascii=False, indent=2)` 寫檔,不用字串拼接手刻 JSON(容易漏跳脫字元)。
- **目錄不存在**:`~/.claude/jsc/` 不存在時,寫入前先建立(`mkdir -p ~/.claude/jsc`)。
- **`reason` 欄位一併記錄來源**:因快取欄位固定、不得新增欄位,「這筆是怎麼判定出來的」寫進 `reason`,格式建議:`<來源層次摘要(CLI 自陳 --help/使用痕跡 stats-cache.json/claude-api skill 文件)> + <smoke test 結果摘要或「未能於此 CLI 驗證」>`。輸出表格的「來源」欄直接取這裡的內容,不另外judge。
- **內容邊界**:只准存模型中繼資料七個欄位,**絕不寫入任何工作內容、對話內容、專案路徑、議題內容或個資**——寫入前逐筆檢查是否越界。
---
## 預設模式(無參數)
1. 檢查快取檔是否存在;不存在 → 視同過期,走步驟 3。
2. 存在則讀出每筆 `verified_at`,與目前時間(`TZ='Asia/Taipei' date +'%Y/%m/%d %H:%M:%S'`)比對,**任一筆超過 30 天** → 整份快取視為過期,走步驟 3;全部未過期 → 直接跳到步驟 4 用現有內容輸出。
3. **自動探測**(等同 `--refresh` 的探測部分,見下節「探測與 smoke test 流程」),完成後覆寫快取,再進入步驟 4。
4. **輸出模型 × 標籤表格**(Markdown,依 `/jsc-shared:spec-output`):
| 模型 id | 別名 | 標籤 | 上下文 | 定價 | verified_at | 判定 | 來源/理由 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `claude-sonnet-5` | `sonnet` | `#均衡實作` `#實作` `#中成本` `#標準上下文` `#本機可用` | 200k | (查證當下定價) | 2026/08/11 10:00:00 | 本機可用 | CLI 自陳 + smoke test 通過 |
(表格內容為格式示意,實際筆數與標籤依當次快取內容輸出,不得照抄範例值。)
5. `--json` 時改輸出:
```json
{
"cache_path": "~/.claude/jsc/models.json",
"generated_at": "2026/08/11 10:00:00",
"models": [
{ "id": "...", "aliases": ["..."], "tags": ["..."], "context": 200000,
"pricing": "...", "verified_at": "2026/08/11 10:00:00", "verdict": "本機可用", "reason": "..." }
]
}
```
---
## `--refresh`:探測與 smoke test 流程
不論快取是否有效都執行本節,完成後整份覆寫快取。依 `spec-model` 第三節,**前一項可取得就不往下**,但仍要把後續層次能補充的候選都納入再統一做 smoke test:
1. **`claude-api` skill**:以 Skill 工具載入,取得當下模型 id/定價/上下文長度清單,**不憑記憶、不援引本檔或過去對話中的舊數字**。這是候選清單與 `pricing`/`context` 欄位的權威來源。
2. **CLI 自陳**:Claude Code 執行 `claude --help`,解析 `--model` 說明列出的別名(如 `sonnet`/`opus`/`fable` 等,實際內容以當次輸出為準);非 Claude Code 助理依自身列出指令(`codex`、`opencode`、`agy`、`copilot` 各自對應指令),取不到就不強行湊數。
3. **使用痕跡(僅 Claude Code)**:讀 `~/.claude/stats-cache.json` 的 `dailyModelTokens`/`modelUsage` 等欄位,把出現過的模型 id 併入候選清單(只補充候選,不代表現在仍可用)。
4. **Smoke test**(所有候選逐一跑,Claude Code 專屬做法;其他助理見下節「非 Claude Code 助理」):
```bash
CLAUDE_CODE_CHILD_SESSION=1 timeout 45 claude -p 'OK' --model <id>
```
stderr/stdout 出現 `is not a model this version of Claude Code recognizes` 或 `There's an issue with the selected model` → 該筆 `verdict = 不可用`;正常回應 → `verdict = 本機可用`;因不在此 CLI 驗證範圍(例如純文件記載、非本帳號方案)而無法判定 → `verdict = 未驗證`。
⚠️ 每個候選都要帶 `CLAUDE_CODE_CHILD_SESSION=1`,避免觸發巢狀 session 與 SessionStart hook。
5. **貼標籤**:依 spec-model 第一節標籤字彙表,對每個候選同時判斷能力等級、成本(依步驟 1 查到的定價分三段,不憑記憶)、延遲、上下文(`context ≥ 1,000,000` 記 `#長上下文`,否則 `#標準上下文`)、用途(可多個)、可用性(步驟 4 的判定結果)。
6. **寫回快取**:`verified_at` 取本次完成時間,`reason` 依上節「`reason` 欄位一併記錄來源」的格式寫入,整份以 `models.json` 覆寫(不是逐筆 append,避免殘留已下架模型的舊紀錄;若某模型本次探測不到但快取內既有記錄,先詢問是否移除或保留標記為 `#不可用`,不擅自沉默刪除)。
7. 完成後照預設模式步驟 4/5 輸出表格。
---
## `--task <analysis|implement|review|summary|persona>`
1. 確保快取有效(無效先走上節探測流程)。
2. 依 `spec-model` 第二節對映表,把參數值對應到任務類型與必要/加分標籤:
| 參數值 | 對映任務類型 | 必要標籤 | 加分標籤 |
| --- | --- | --- | --- |
| `analysis` | 需求分析/拆 TODO/架構決策 | `#深度推理` `#分析` `#本機可用` | `#長上下文` |
| `implement` | 依清單實作/規格落地 | `#均衡實作` `#實作` `#本機可用` | `#中成本` |
| `review` | code review/findings 判讀 | `#深度推理` `#審查` `#本機可用` | — |
| `summary` | 逐輪摘要(worklog)/分類 | `#輕量快速` `#摘要` `#延遲敏感可用` `#低成本` | — |
| `persona` | 人格對話 | `#對話人格` `#本機可用` | `#延遲敏感可用` |
參數值不在上表 → 回報「不支援的任務類型 `<值>`,可用值:analysis/implement/review/summary/persona」並停止,不猜測近似值。
3. 篩出快取中**同時具備全部必要標籤**的模型;都不具備 → 回報「目前快取內沒有符合『<必要標籤>』的模型,建議先執行 `--refresh` 重新探測」並停止。
4. 候選排序:加分標籤命中數較多者優先;仍並列則 `verified_at` 較新者優先。
5. 輸出**推薦 id + 一行理由 + 次選**:
```
推薦:<id>(<alias>)—— <一行理由,說明命中哪些必要標籤與加分標籤>
次選:<id2>(<alias2>)—— <一行理由>(若只有一個候選則省略此行並註明「無其他候選」)
```
6. `--json` 時改輸出:
```json
{
"task": "implement",
"required_tags": ["#均衡實作", "#實作", "#本機可用"],
"bonus_tags": ["#中成本"],
"recommended": { "id": "...", "alias": "...", "reason": "..." },
"alternatives": [ { "id": "...", "alias": "...", "reason": "..." } ]
}
```
---
## `--check <model>`
用於判斷「當前執行者的模型」是否等於指定模型,供其他 skill(例如開啟帶 `model:` frontmatter 清單檔時)呼叫,行為依 `spec-model` 第五節:
1. 確保快取有效(無效先走探測流程),在快取的 `id`/`aliases[]` 中解析 `<model>`(可傳 id 或別名);快取查不到 → 仍照字面值往下比對,並在輸出中註明「快取未收錄此模型,僅做字面比對」。
2. 取得「當前模型」:**Claude Code 沒有環境變數可讀當前模型 id**,以 agent 對自身系統提示所述的 exact model id 自我回報為準;自我回報不確定時,請使用者執行 `/status` 確認後再比對,**不得用猜的**。
3. 比對指定模型(含別名)與當前模型:
- **相符** → 輸出 `[時間][模型檢查][INF]: 當前模型 <current-id> 符合指定的 <model>(<alias>)。`(時間格式依 `/jsc-shared:spec-time-log`),視為成功結束。
- **不符** → 依 spec-model 第五節格式輸出並停止,**不得自行降級或升級頂替,不得先做一部分**:
```
[yyyy/MM/dd HH:mm:ss][模型檢查][ERR]: 本清單/本 skill 指定 <model>(<alias>),當前模型為 <current-model-id>。
請執行 /model <alias> 切換後重新載入,本次不進行任何修改。
```
**回非零狀態**:本 skill 以 agent 對話形式執行時沒有 process exit code 可回傳,以上述 `[ERR]` 行本身作為失敗訊號——呼叫方(其他 skill 或以 headless 模式呼叫本 skill 的腳本)應以輸出是否含 `[模型檢查][ERR]` 判定失敗;若未來新增腳本化實作(例如 `shared/scripts/lib` 的參考實作),該腳本須以 `exit 1` 對應此狀態。
4. `--json` 時改輸出:
```json
{
"check_model": "sonnet",
"resolved_id": "claude-sonnet-5",
"current_model": "claude-opus-5",
"match": false,
"message": "[2026/08/11 10:00:00][模型檢查][ERR]: 本清單/本 skill 指定 sonnet(claude-sonnet-5),當前模型為 claude-opus-5。請執行 /model sonnet 切換後重新載入,本次不進行任何修改。",
"exit_status": 1
}
```
相符時 `match: true`、`exit_status: 0`,`message` 改為 INF 那行。
---
## 非 Claude Code 助理
`claude-api` skill(來源優先序第 1 層)與 `~/.claude/stats-cache.json` 使用痕跡(第 3 層)都是 Claude Code 專屬,其他 CLI(Codex/OpenCode/Antigravity/GitHub Copilot)不可取得,**只能做到**:
- **CLI 自陳**(第 2 層):各自的模型列出指令(例如 `codex` 的設定/說明輸出、`opencode models`、`agy` 對應指令、`copilot` 對應指令),取得到就以此作為候選與別名來源。
- **smoke test**(第 4 層):以該 CLI 自己的無互動單輪呼叫方式驗證候選模型可用性(例如 `codex exec`、`opencode run`、`agy -p`、`copilot -p` 等一次性呼叫,回應正常記 `#本機可用`、明確報錯記 `#不可用`),需在該助理環境下能否比照 Claude Code smoke test 的無害單輪呼叫方式自行判斷;判斷不出對應方式就不勉強跑。
無法取得 `pricing`/`context`(需要 `claude-api` skill 才查得到的欄位)時,**不得憑記憶填入**,該筆欄位留空或標註「未知(無法在此 CLI 取得)」;無法完成第 2、4 層探測的模型,**整批標 `#未驗證`**,`reason` 寫「僅文件記載,未能於此 CLI 驗證」。
**不得中斷**:不論第 1、3 層缺失、或第 2、4 層部分候選探測失敗,都要照常完成本次模式並輸出結果,缺失的部分如實標註,不視為錯誤而中止整個 skill。
---
## 呼叫方式
依 `/jsc-shared:spec-skill-invocation` 的統一呼叫方式,本 skill 的實際參數格式與範例:
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc-shared:models`、`/jsc-shared:models --refresh`、`/jsc-shared:models --task implement`、`/jsc-shared:models --check sonnet`、`/jsc-shared:models --task analysis --json` |
| Codex | `$models --task review`,或用 `/skills` 選單 |
| OpenCode | 依本 skill 的 description 自動觸發,或以自然語言描述「幫我查現在有哪些模型可用」等同預設模式 |
+3 -6
View File
@@ -17,13 +17,10 @@ argument-hint: "[--assistant <助理清單,逗號分隔,或 all>] [--plugins
---
## 共用規範(shared plugin,必要前置)
## 共用規範(必要前置)
執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守:
- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼、表格呈現。
- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認。
- `/jsc-shared:spec-git-safety`:Antigravity/其他會動到本機 clone 的工具路徑有未提交變更時不得強制更新。
先載入 `/jsc-shared:spec-preflight` 並依其流程處理。
本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-git-safety`
本 skill 特有補充:
+3 -6
View File
@@ -17,13 +17,10 @@ argument-hint: "[--assistant <助理清單,逗號分隔,或 all>] [--plugins
---
## 共用規範(shared plugin,必要前置)
## 共用規範(必要前置)
執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守:
- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼、表格呈現。
- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認。
- `/jsc-shared:spec-git-safety`:不破壞既有工作——本機 clone 只在使用者明確同意時刪除,且**有未提交變更就一律保留**。
先載入 `/jsc-shared:spec-preflight` 並依其流程處理。
本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-git-safety`
本 skill 特有補充:
+43
View File
@@ -0,0 +1,43 @@
---
name: spec-action-scaffold
description: JSC plugins 共用「action 從零建立骨架」:目錄裡沒有既有 action manifest(action.yml/action.yaml)時如何判斷 action 根目錄並觸發問答式從零建立,以及問答式從零建立時必須依序詢問使用者三個固定骨架問題(action 名稱/用途、輸入輸出、執行目標);各 skill 可依自身 action 類型調整第 2、3 題的具體措辭,但三題的骨架與順序一致。當其他 skill 內文引用 spec-action-scaffold 或 /jsc-shared:spec-action-scaffold、或需要處理「目錄無 action manifest 時的從零建立問答」時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-action-scaffold — 共用 action 從零建立骨架
所有處理 Gitea/GitHub action 的 JSC skills(composite/docker/node 等 action 類型),在目錄裡**找不到既有 action manifest** 時,一律遵守以下規範判斷 action 根目錄,並以固定三題骨架問答式從零建立。
## 適用範圍
本規範適用於「目標本身就是 action manifest(`action.yml`/`action.yaml`)」的 skill(例如 `action-composite`/`action-docker`/`action-node`)。**不適用**於目標是既有 `Dockerfile` 而非 action manifest 的 skill(例如 `image`)——那類 skill 的職責是整理既有檔案,找不到目標檔案時應回報並詢問正確路徑或轉導到對應的 action 化 skill,**不可**套用本規範的問答式從零建立(不臆造 `Dockerfile`)。
## 判斷 action 根目錄
1. 帶 `--action-dir <action 根目錄>` → 採用(展開 `~`)。
2. 省略 → 用目前工作目錄。
3. 根目錄須存在 action manifest:帶 `--manifest` 則採用指定路徑;否則於根目錄找 `action.yml`,再退而 `action.yaml`。
4. **找不到** action manifest → **不臆測**、不逕自動工;以 `AskUserQuestion` 詢問使用者:
- 「從零建立」新的該類型 action → 進入「問答式從零建立」。
- 「提供正確的 action 路徑」→ 依新路徑重新判斷根目錄(回到第 1 步)。
## 問答式從零建立三題骨架
選擇「從零建立」後,依序以問答收集需求(順序固定、不可跳題或調換),再產生 manifest:
1. **action 名稱/用途**:action 的 `name`,以及此 action 要達成什麼(整理濃縮成一句話,作為 `action.yml` 的 `description`;盡量繁體中文、無亂碼)。
2. **輸入輸出**:`inputs`/`outputs` 長什麼樣——逐一收集名稱、`description`(盡量繁體中文、無亂碼)、`required`/`default`;沒有可留空。
3. **執行目標**:這個 action 實際要跑什麼指令或程式,據此決定 `runs` 的具體實作(composite 的 steps、docker 的主程式與 image、node 的主程式與打包方式等)。
收集完成後於 action 根目錄產生對應的 action manifest 與(若該 action 類型需要)主程式骨架;開發過程中若需要新的參數值,依 `/jsc-shared:spec-action-params` 的優先序處理,不得繞過本骨架直接詢問或編造。
## 各 skill 差異對照(骨架不變、措辭可調整)
三題的**骨架與順序一律一致**;下表列出目前三個 action 類型 skill 對第 2、3 題的具體措辭差異,僅供對照,新增其他 action 類型的 skill 時可依自身特性調整措辭,但不可更動骨架本身。
| 來源 skill | 第 1 題(固定:名稱/用途) | 第 2 題(輸入輸出,措辭可調) | 第 3 題(執行目標,措辭可調) |
| --- | --- | --- | --- |
| `action-composite` | action 名稱,用於 `action.yml` 的 `name` | 逐一收集 `inputs`/`outputs` 的名稱與 `description`、`required`/`default` | 詢問此 action 要達成什麼,濃縮成一句話作為 `description`;據此以 composite steps 實作 |
| `action-docker` | 同上 | 同上 | 同上;據此以 Node 主程式(`src/index.js`)實作,輸入以 `process.env.INPUT_<NAME>` 讀取 |
| `action-node` | 同上 | 逐一收集 `inputs`(名稱、`description`、`required`、`default`)與 `outputs`(名稱、`description`;**不收集 `value`**) | 同上;據此以 Node 主程式實作,預設走零相依路線 |
各 skill 完成問答並產生 manifest 與主程式骨架後,接續各自原本「已存在 manifest」時的後續流程(判斷/對齊 action 類型、注入橫幅或處理相依等),視同該 manifest 原本就存在,不再重複問答。
+50
View File
@@ -0,0 +1,50 @@
---
name: spec-ask-user
description: JSC plugins 共用「詢問使用者規範」:何時該用 AskUserQuestion(單選/多選判斷時機)、選項數量上限 4(工具限制,超過改列文字選項並支援使用者輸入 all/全部代表全選)、每組問題必須含「其他」自訂輸入、破壞性決策不得被 --yes 等自動確認旗標略過而必須真的詢問、已從其他管道得知答案時跳過詢問、絕不要求使用者把 token 或密碼貼進對話框。當其他 skill 內文引用 spec-ask-user 或 /jsc-shared:spec-ask-user、或需要決定是否/如何向使用者提問時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-ask-user — 共用詢問使用者規範
所有 JSC skills 需要向使用者提問(確認決策、多選範圍、選擇執行方式)時,一律遵守以下規範。
## 何時該用 `AskUserQuestion`(單選 vs 多選)
| 情境 | 用哪種 | 說明 |
| --- | --- | --- |
| 使用者已在參數或對話中明確給答案 | **不問**(跳過) | 見「已知答案時跳過詢問」 |
| 答案只能是其中一個(例如選一種工具、選一個要處理的目標) | **單選** | 選項間互斥 |
| 答案可以同時成立多個(例如挑選要同步的擁有者、要同步的標籤) | **多選**(`multiSelect`) | 選項間不互斥 |
| 屬於破壞性、對外且不易復原的動作(關閉議題、改寫程式語言、對齊高風險結構) | **必問**,不可省略 | 見「破壞性決策」 |
不確定答案是否互斥時,先判斷「使用者是否可能同時要好幾個」——可能就用多選,不確定就傾向多選(漏選比誤判單選更容易補救)。
## 選項數量上限 4(工具本身限制)
`AskUserQuestion` 每組問題最多只能放 **4 個選項**,這是工具本身的限制,不是建議值。
- 候選項目 ≤ 4:直接用 `AskUserQuestion` 呈現。
- 候選項目 > 4:**改用文字列出全部選項**(表格或條列,附編號),請使用者以編號或名稱回覆(多選時支援逗號或空白分隔多個),並**支援使用者輸入 `all`/`全部` 代表全選**。
- 文字列出時仍要解析使用者回覆為明確集合;無法對應的輸入回報並請使用者重選,**不臆測**。
## 每組問題必須包含「其他」
不論選項數在 4 以內用 `AskUserQuestion`、還是超過 4 改文字列出,都必須提供「其他」讓使用者自訂輸入,用於選項未涵蓋使用者實際需求的情況。不可只列預設選項就視為選項齊全。
## 破壞性決策不得被自動確認旗標略過
凡屬破壞性、對外、不易復原的決策(例如:關閉議題/專案、改寫程式語言、對齊高風險結構、覆寫既有檔案內容、刪除或搬移資源),**必須真的詢問使用者**並取得明確回覆才可執行:
- `--yes`、`--force`、`--auto` 之類的自動確認旗標**不得**用來略過這類詢問;這些旗標最多只能省略「是否繼續」這種非破壞性的確認,不能代替破壞性決策的確認。
- 未獲得使用者針對該次破壞性決策的明確回覆前,不得執行對應的寫入或不可逆動作。
- 選項至少要能區分「執行」「不執行/取消」,並視情境提供更細的選項(例如逐項確認),再加上「其他」。
## 已從其他管道得知答案時要跳過詢問
使用者已在本次對話、參數、或上游流程結果中明確給過答案時(例如已指定 `--owner`、已在對話中選定工具、已提供目標議題編號),**跳過對應詢問**,不要明知道答案還多問一次;只需驗證該答案是否有效(例如清單中確實存在),無效才回頭詢問。
## 絕不要求使用者把 token 或密碼貼進對話框
任何情況下都不得請使用者把 token、密碼、或其他機密資料貼到 `AskUserQuestion` 或一般對話輸入框中:
- 機密一律透過環境變數、設定檔或既有登入態取得並驗證可用性(見 `/jsc-shared:spec-gitea` 的 token 解析優先序)。
- 需要的機密不可用時,回報缺少什麼(例如「缺少 `GITEA_TOKEN`」)並停止,改請使用者在環境變數或設定檔中設置,而不是詢問使用者輸入機密內容。
+59
View File
@@ -0,0 +1,59 @@
---
name: spec-conventional-commit
description: JSC plugins 共用「Conventional Commit 分類提交規範」:以 git status --porcelain=v1 -uall 完整盤點工作區變更、9 種 commit 類型對照(feat/fix/docs/style/refactor/perf/test/chore/revert)、commit 訊息格式為 type(範圍):一句總結、範圍不得重述 type 本身、依邏輯分組後逐組以精準檔案路徑分別 git add(不用 git add -A 或 git add .)分類提交。當其他 skill 內文引用 spec-conventional-commit 或 /jsc-shared:spec-conventional-commit、或需要把工作區變更依 conventional commit 分類提交時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-conventional-commit — 共用 Conventional Commit 分類提交規範
工作區有變更需要 commit 時,一律遵守以下規範:先完整盤點、依實際異動內容歸類,再依邏輯分組並逐組精準提交。
## 盤點工作區變更
- 一律以 `git status --porcelain=v1 -uall` 為主,不可只看 `git diff` —— 那會漏掉未追蹤檔。`git diff`(已追蹤檔未暫存變更)、`git diff --staged`(已暫存變更)、`git ls-files --others --exclude-standard`(未追蹤檔)只作輔助核對。
- 盤點範圍必須包含**所有**變更:已修改檔、新增檔(`??` 未追蹤檔)、刪除檔、改名檔,以及已暫存與未暫存的變更。
- **無任何變更** → 回報「工作區無變更可提交」,跳過提交。
## commit 類型對照表
逐一檢視每個變更檔的**實際異動內容**(不只看路徑),歸入下列其一;所有 `??` 未追蹤檔都必須納入分類,不能因為不在 `git diff` 裡就漏掉。
| type | 適用情境 |
| --- | --- |
| `feat` | 新增功能/新行為/新 API/新 skill |
| `fix` | 修正錯誤、修掉 bug |
| `docs` | 只改文件(README、註解、`*.md`、說明) |
| `style` | 不影響邏輯的格式調整(排版、空白、分號、命名一致化) |
| `refactor` | 重構:不改外部行為的內部結構調整 |
| `perf` | 效能優化 |
| `test` | 新增或修改測試 |
| `chore` | 雜項:建置、設定、相依套件、版本號 bump、忽略檔等 |
| `revert` | 還原先前的提交 |
- **同一檔案橫跨多型** → 以該檔主要異動性質歸類;難以拆分時就近歸入影響最大的一類,並在總結註記。
- **未追蹤新檔**:必須照實際內容歸入對應 type,必要時在提交前明確 `git add -- <path>`,不可因為是新檔就略過。
## commit 訊息格式
`type(範圍): 一句總結`
- `type`:上表其中一個英文類型。
- **範圍**(括號內):必須是這組異動實際牽涉的功能/模組/元件名稱,**不得重述 type 本身**。取名規則:優先沿用程式碼/專案中既有的識別名(檔名、模組名、skill 名、功能名,可中可英、保持與原碼一致),讓人一眼看出「改到哪個東西」。
- ✅ 對:`feat(使用者登入)`、`fix(結帳流程)`、`perf(物件查詢)`、`docs(README)`、`refactor(訂單服務)`、`chore(plugin 版本)`。
- ❌ 錯(只是重述 type,禁止):`feat(新增功能)`、`fix(修正錯誤)`、`perf(優化效能)`、`docs(文件)`、`chore(雜項)`。
- 一組異動橫跨多個功能而無單一主體時,才退而取最貼近的上層範圍(例如多個 manifest → `plugin 設定`)。
- **一句總結**:把這個 commit 內所有異動總結成一句繁體中文,簡短、聚焦做了什麼。
- 範例:`feat(使用者登入): 新增帳密登入與 token 簽發`、`fix(結帳流程): 修正空購物車導致的結帳例外`、`perf(物件查詢): 改用批次查詢降低 DB 往返`、`docs(README): 補上安裝與呼叫方式說明`、`chore(ai-review 狀態): 更新 findings 與 exclusions.json`。
## 分組與提交順序
1. 依 commit 類型把變更檔分組,**每個 type 一個 commit**;提交計畫必須完整對應盤點出的所有變更項目,不能遺漏任何 `??` 未追蹤檔。
2. 逐組執行,每次僅暫存該組檔案:
```bash
git add -- <該組檔案...> # 僅暫存該組檔案,逐組精準 add
git commit -m "type(範圍): 一句總結"
```
3. **不用 `git add -A` 或 `git add .`**,避免把不同類型的異動混進同一個 commit。
4. 改名/刪除檔一併納入對應組的 `git add`(`git add -A -- <路徑>` 或明確列出該路徑)。
5. **提交順序建議**:`fix`/`feat` 等核心異動在前,`docs`/`style`/`chore` 在後(純屬建議,可依相依性調整)。
+74
View File
@@ -0,0 +1,74 @@
---
name: spec-git-push
description: JSC plugins 共用「git push 憑證選擇規範」:push 分支前依序嘗試認證管理器(既有 git credential helper)→ token(環境變數組帶 token 遠端 URL,全程遮蔽、用完即棄不落地)→ 詢問使用者三段式,前者失敗才退到下一個;與 `/jsc-shared:spec-gitea`「token 解析優先序」(API 呼叫用、多來源逐一驗證取優)不同,本規範專講 `git push` 本身的憑證選擇順序。當其他 skill 內文引用 spec-git-push 或 /jsc-shared:spec-git-push、或執行任何會 `git push` 的 JSC skill 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-git-push — 共用 git push 憑證選擇規範
機密保護的總則(不 echo、遮蔽、不落地)沿用 `/jsc-shared:spec-gitea`「Token 機密保護」章節;本規範只補 `git push` 情境特有的三段式憑證選擇順序,不重複該章節已涵蓋的內容。
## 與 spec-gitea「token 解析優先序」的差異
兩者都在講「憑證怎麼決定」,但用途與判定方式不同,**不可互相取代**:
| | `/jsc-shared:spec-gitea`「token 解析優先序」 | 本規範(`spec-git-push`) |
| --- | --- | --- |
| 用途 | Gitea REST API 呼叫(GET/POST 等) | `git push` 本身取得推送權限 |
| 選擇單位 | 多個「token 來源」 | 多種「推送方式」(不只 token) |
| 判定方式 | 每個候選以輕量 API 請求**實際驗證**(HTTP 200 才採用),失敗就換下一個候選 | 直接嘗試該推送方式,**push 本身失敗**才換下一個 |
| 候選順序 | 專用變數 → `$GITEA_TOKEN` → tea 設定檔 → `~/.git-credentials` | 認證管理器 → token → 詢問使用者 |
| 最終手段 | 全部候選驗證失敗 → 回報錯誤並停止 | 前兩段都失敗 → **詢問使用者**如何 push,不猜測其他憑證 |
同一個 skill 可以先用 spec-gitea 的順序決定 API 呼叫要用哪個 token,再於 push 時套用本規範的三段式;兩套順序彼此獨立,不共用判定結果。
## 三段式流程
push 前先確認目前分支是否有領先遠端的 commit;**無 commit 可推就跳過整段,不需要嘗試任何憑證**:
```bash
source_branch="$(git rev-parse --abbrev-ref HEAD)"
git rev-parse --verify "origin/${source_branch}" 2>/dev/null # 遠端無此分支視為全部要推
git log --oneline "origin/${source_branch}..${source_branch}" 2>/dev/null # 領先遠端的 commit
```
確認有 commit 需要 push 後,依序嘗試以下三段,**前者失敗才退到下一個**,任一段成功即停止:
| 順序 | 方式 | 適用條件 |
| --- | --- | --- |
| 1 | 認證管理器(優先) | git 既有的 credential helper 可用(如 Windows 的 `manager-core`) |
| 2 | token push | 環境變數已提供 token(如 `GITEA_TOKEN`) |
| 3 | 詢問使用者 | 前兩段都失敗 |
### 1. 認證管理器
不預先判斷有沒有 credential helper,直接嘗試,讓 git 自己走既有認證流程:
```bash
git push -u origin "$(git rev-parse --abbrev-ref HEAD)"
```
### 2. token push
失敗才進入此段。從環境變數讀 token,組帶 token 的遠端 URL 推送:
```bash
# GITEA_TOKEN 來自環境變數;host/owner/repo 解析自 origin
git push "https://oauth2:${GITEA_TOKEN}@<host>/<owner>/<repo>.git" \
"$(git rev-parse --abbrev-ref HEAD)"
```
- token 一律用變數帶入組出遠端 URL,**指令與輸出全程不可印出含 token 的字串**。
- token 用完即棄:**不寫進 `git remote set-url`/`.git/config`、不落地**——這個帶 token 的 URL 只用於這一次 push,用完即丟,不留存、不設回 remote。
- 若 push 之外的流程(例如 clone)曾把帶 token 的 URL 設進 remote,還原成乾淨 URL 屬於 clone/remote 憑證管理範疇,做法見 `/jsc-shared:spec-gitea`「Token 機密保護」章節,不在本規範重複。
### 3. 詢問使用者
兩段都失敗才進入此步:列出兩段各自的失敗原因(**先遮蔽 token**),請使用者指示要如何推送,**不可自行猜測**其他憑證或來源;排程情境(無人可即時回應)改為寫入 log 並結束,等使用者事後處理。
## 全程遮蔽 token
三段流程中任何會印出指令、URL、錯誤訊息,或寫進 log 的地方,一律套用 `/jsc-shared:spec-gitea`「機密遮蔽實作」章節的規則過濾後才輸出,不得只遮蔽字面 token 值。
## 適用情境
供任何會執行 `git push` 的 JSC skill 引用,例如 `/jsc-code:review-resolve` push 當前分支的階段、`/jsc-code:target` push `develop` 的階段。**clone 時組裝帶 token 的 URL 與 `remote set-url origin` 還原乾淨 URL**(例如 `/jsc-code:sync` 建立資料夾並 clone 的階段)屬於 clone/remote 憑證管理,已由 `/jsc-shared:spec-gitea`「Token 機密保護」章節涵蓋,不屬本規範範疇。
+18 -1
View File
@@ -1,6 +1,6 @@
---
name: spec-git-safety
description: JSC plugins 共用「Git 安全操作規範」:不破壞既有工作(未提交變更先提醒、絕不 reset --hard/checkout -f/clean)、git mv 保留歷史、develop → master 後備分支選擇、pull --ff-only、保守解衝突。當其他 skill 內文引用 spec-git-safety 或 /jsc-shared:spec-git-safety、或執行任何會操作 git 工作區/分支的 JSC skill 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
description: JSC plugins 共用「Git 安全操作規範」:不破壞既有工作(未提交變更先提醒、絕不 reset --hard/checkout -f/clean)、git mv 保留歷史、develop → master 後備分支選擇、pull --ff-only、工作分支選擇(用 git remote show origin/origin/HEAD 判定遠端預設分支、develop 不存在時從遠端預設分支建立、只有來源與目標分支同名才開新分支)、保守解衝突、建立分支不覆蓋(時間戳或短 hash)。當其他 skill 內文引用 spec-git-safety 或 /jsc-shared:spec-git-safety、或執行任何會操作 git 工作區/分支的 JSC skill 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-git-safety — 共用 Git 安全操作規範
@@ -37,6 +37,23 @@ git switch develop 2>/dev/null || git switch -c develop --track origin/develop
- 切換後備分支屬不可忽略的狀態變更,需明確告知使用者已從原分支切換到哪個分支。
- 批次更新既有 repo 時用 `git pull --ff-only`;無法快進(本地與遠端分歧)→ 回報需人工處理,**不**自動 merge/rebase/reset。
## 工作分支選擇
判定遠端預設分支,以及是否需要為當前操作另開一條工作分支:
- **判定遠端預設分支**:優先用 `git symbolic-ref --quiet refs/remotes/origin/HEAD`(結果形如 `refs/remotes/origin/<預設分支>`),取不到時退而用 `git remote show origin`(找輸出中的 `HEAD branch:` 那行)。兩者都取不到 → 回報並停止,**不臆測** `master`/`main`。
- **`develop` 在遠端不存在時**,改由上一步判定出的遠端預設分支(通常是 `master`,但仍須以實測結果為準,不可不判定就固定假設)建立:
```bash
git switch -c develop "origin/<上一步判定出的遠端預設分支>"
```
- **只有來源分支與目標分支同名時才開新的工作分支**:
- 同名 → 不可在該分支上直接操作/commit,也不可直接把它 push 成目標分支。改從已更新到最新的目標分支建立新的工作分支,後續操作(修復、commit、push)都以新分支為準。
- 不同名 → 直接在目前分支操作,不另開分支;即使目前分支已存在對應的遠端分支,也照常在目前分支處理。
- 新工作分支的命名(避免覆蓋既有分支、加時間戳或短 hash)依下方「建立分支不覆蓋」規範辦理。
- 切換或建立分支皆屬不可忽略的狀態變更,需在輸出中明確告知使用者原因(例如「來源分支與目標分支同名」或「遠端沒有 develop,已從 `<預設分支>` 建立」)與結果的分支名稱。
## 保守解衝突
pull/merge/cherry-pick 發生衝突時:
+67
View File
@@ -40,6 +40,43 @@ description: JSC plugins 共用「Gitea 工具規範」:tea 或 Gitea REST API
- 流程若可能使對話內文殘留 token(push/API 呼叫),完成後提醒使用者清除對話(Claude Code:`/clear`),並先確認輸出與 log 無明文 token。
### token 解析優先序
完整優先序(依序取第一個驗證成功者):
1. 呼叫端工具自訂的專用環境變數(若有,例如 persona 的 `PERSONA_GITEA_TOKEN`)。
2. `$GITEA_TOKEN`。
3. tea 設定檔(`~/.config/tea/config.yml` 或 `~/.tea/config.yml`)中對應 host 的 token。
4. `~/.git-credentials`(`credential.helper=store`)中對應 host 的密碼。
每個候選都必須用 `GET /repos/<owner>/<repo>` 之類的輕量請求實際驗證可用(HTTP 200 才採用),驗證失敗就換下一個候選;全部候選都失敗,才回報錯誤並停止。
各工具可以只實作其中適用的子集(例如純粹用 git `http.extraHeader` 認證的工具可以不需要 tea 設定檔/git-credentials 這兩層 fallback),但已實作的部分不可打亂這個順序,且專用變數只能插在最前面,不能插在中間。
參考實作:`doc/scripts/worklog/wiki_api.py` 的 `resolve_token()` 依「`GITEA_TOKEN` → tea 設定檔 → git-credentials」順序逐一以 API 驗證;`persona/scripts/persona-gitea.mjs` 的 `giteaEnv()` 則示範了「專用變數插在最前面」——優先取 `PERSONA_GITEA_TOKEN`,其次才是 `GITEA_TOKEN`。
### 機密遮蔽實作
任何會把 Gitea API 回應內容、git stderr/stdout、或組出的錯誤訊息顯示給使用者/寫進 log 的地方,一律先套用下列遮蔽規則。
| 規則 | 用途 |
| --- | --- |
| `[A-Za-z0-9_-]*:[A-Za-z0-9_-]{16,}@` → 取代為 `***@` | URL 內嵌憑證 user:token@ |
| `\b[0-9a-f]{40}\b` → `***` | Gitea 40 字元 token |
| `\bgh[pousr]_[A-Za-z0-9_]{16,}\b` → `***` | GitHub token |
| `\bsk-[A-Za-z0-9\-_]{16,}\b` → `***` | API key |
| `(?i)\b(token\|password\|passwd\|pwd\|secret\|api[_-]?key)\b\s*[:=]\s*\S+` → `\1=***` | key=value 形式機密 |
| `(?i)Authorization:\s*(token\|bearer)\s+\S+` → `Authorization: \1 ***` | Authorization 標頭 |
| `[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}` → `***` | Email |
| `\b09\d{2}[-\s]?\d{3}[-\s]?\d{3}\b` → `***` | 台灣手機 |
| `\b[A-Z][12]\d{8}\b` → `***` | 台灣身分證字號 |
(來源:`doc/scripts/worklog/transcript.py` 的 `REDACT_PATTERNS`,逐條照抄。)
這 9 條規則的**唯一權威資料來源**為 `shared/scripts/lib/redact-patterns.json`(Python/JavaScript/Bash 三語言共用),上表僅供閱讀對照,異動一律先改該 JSON 檔再回頭同步本表,避免文件與 JSON 各自漂移。
任何存取 Gitea 的 JSC 腳本/skill,其錯誤輸出路徑都必須套用這 9 條規則(或功能對等實作),而不只是遮蔽 token 字面值。
## 不依賴 `jq`(環境未必安裝)
- 解析 JSON 用 `tea` 的結構化輸出(`--output csv`/`--fields`),或把原始 JSON 直接交給助理/subagent 解析,**不要 pipe 到 `jq`**。
@@ -53,8 +90,38 @@ description: JSC plugins 共用「Gitea 工具規範」:tea 或 Gitea REST API
- API 失敗(401/403/網路錯誤)→ 回報錯誤(**遮蔽 token**)並停止;401/403 多半是 token 失效或權限不足。
- 版本相依端點(project/column/dependency 等)先以 GET 探測(404/501 視為不支援),**不得對未確認存在的端點做寫入**。
### Wiki 頁名轉義規則
Gitea wiki 的「title」與實際存放用的「sub_url/檔名」不是同一個字串,Gitea 會對 title
做內部轉義;這個轉義演算法未完全公開,**不可自行用字串取代規則反推**。已知規律與收斂
後的共通作法如下:
1. 已觀測到的規律:title 中的**空白**在 sub_url/檔名中會對應成 `-`;但字面上的 `-`
本身另有轉義形式(例如 title `Worklog-2026-07-W4`(本身含 `-`、不含空白)對應到的
sub_url 觀測值為 `Worklog-2026-07-W4.-`)。也就是「title→sub_url」只有空白這一條
規則可靠,`-`/`.-` 等其他符號的精確對應**不可靠、不要照抄硬編碼**。
2. **查表優先於猜測轉義規則**:任何需要「用 title 找到實際頁面路徑」的操作(讀取、
更新、刪除既有頁面),一律先呼叫 `GET /repos/<owner>/<repo>/wiki/pages`(分頁完整
讀取,見「API 呼叫慣例」)列出全部頁面,比對 `title` 欄位取得真正的 `sub_url`,
再用該 `sub_url` 組出 `/wiki/page/<sub_url>`。查不到才退回把 title 本身當作
sub_url 使用(新頁尚未建立時的合理退路)。
3. **建立新頁不必自行轉義**:`POST /wiki/new` 直接帶完整、人類可讀的 title 字串即可
(含空白與符號皆可),轉義是 Gitea 伺服器端完成的,呼叫端不用預先處理。
4. 若寫入策略是透過 **git clone/push** 直接操作 wiki repo 產生 `.md` 檔(而非呼叫
REST API),則不必還原 Gitea 的內部轉義:改由呼叫端自建一份「工作路徑 → 儲存
檔名」manifest(例如 `_paths.json`),寫入時查 manifest 決定檔名、讀回時查 manifest
還原原始路徑,全程不依賴、也不猜測 Gitea 從檔名反推 title 的規則。
5. 兩種寫入策略(REST API 查表 vs git clone/push + 自建 manifest)各有各的理由,
不合併;但上述查表優先、不猜測轉義的原則對兩者都適用。
參考實作:`doc/scripts/worklog/wiki_api.py` 的 `resolve_sub_url()` 走 REST API 查表
(規則 2);`persona/scripts/persona-gitea.mjs` 的 `wikiName()` 走 git clone/push +
`_paths.json` manifest(規則 4)。
## gitea 主機決定順序
本節為唯一權威順序,其他 skill 若需描述 host 決定流程,只能引用本節,不可另行複述或改動順序。
依序決定(取第一個成功者):
1. 參數 `--host <主機>`。
+47
View File
@@ -0,0 +1,47 @@
---
name: spec-issue-read
description: JSC plugins 共用「Gitea 議題讀取與需求彙整規範」:讀取議題必須含描述、所有留言與所有附件內容(不得只讀描述)、留言與附件分頁完整讀取(分頁規則見 spec-gitea,不重複細節)、彙整議題需求時不得臆測缺漏的部分,須詢問使用者或如實標記「未提及」。當其他 skill 內文引用 spec-issue-read 或 /jsc-shared:spec-issue-read、或讀取 Gitea 議題/彙整議題需求時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-issue-read — 共用 Gitea 議題讀取與需求彙整規範
所有 JSC skills 讀取 Gitea 議題並據以彙整需求時,一律遵守以下規範。
## 議題必須完整讀取,不能只讀描述
讀取任一議題(含專案看板底下展開的議題)時,至少必須取得:
| 欄位 | 說明 |
| --- | --- |
| `title`/`body`/`state` | 議題標題、描述、狀態 |
| `labels`/`milestone`/`assignees` | 既有分類與指派資訊 |
| **所有留言(comments)** | 不得只取第一頁或前幾則,見下節分頁規則 |
| **所有附件(attachments/assets)** | 議題本身與**每一則留言**各自的附件都要讀,見下節附件讀取 |
- `tea`:`tea issues <index> --repo <owner>/<repo> --comments` 取得本文與留言;`tea` 目前沒有附件指令,附件一律改走 API。
- `api`:`GET {base}/issues/{index}` 取得本文,`GET {base}/issues/{index}/comments` 取得留言。
- 留言中若有需求補充、變更或取消,必須納入需求彙整,並以**最新留言**為準;只讀描述、略過留言即視為讀取不完整,不得據此彙整需求或判定 TODO 完成度。
## 附件讀取
- 附件清單一律走 API(`tea` 無此功能):
- 議題附件:`GET {base}/repos/{owner}/{repo}/issues/{index}/assets`
- 留言附件:`GET {base}/repos/{owner}/{repo}/issues/comments/{id}/assets`
- 取得每個附件的檔名、類型與下載 URL。
- 依附件類型決定讀取方式:
| 附件類型 | 讀取方式 |
| --- | --- |
| 文字類(Markdown、純文字、CSV、JSON 等) | 以 `curl` 直接取得內容到對話中分析,不落地 |
| 圖片或其他二進位 | 依絕對準則的例外,唯讀暫存下載到系統暫存目錄讀取(例如圖片以視覺方式讀取內容),讀取完畢後立即刪除暫存檔 |
| 無法讀取的格式,或僅有 `tea` 而無 token 可下載附件 | 列出附件檔名與 URL 並標註「附件無法讀取,需人工確認」,不得忽略附件的存在,也不得臆測其內容 |
## 分頁必須完整讀取
留言與附件清單都是分頁 API,完整分頁規則(持續累加 `page` 直到回傳筆數小於 `limit` 或回空陣列為止,不可只取第一頁)一律依 `/jsc-shared:spec-gitea` 的「API 呼叫慣例」執行,本規範不重複細節。
## 彙整需求時不得臆測
- 把議題描述與所有留言(含附件內容)整理成需求彙整(目標、驗收條件、限制條件)時,**只做歸納,不編造來源未提及的需求**。
- 來源之間說法不一致、範圍不明、驗收條件缺漏、附件無法讀取造成的資訊缺口等,一律先詢問使用者澄清;來不及或無法立即詢問時,如實在彙整內容中標記「未提及」或「需人工確認」,不得自行補完、猜測或以常見做法代填。
- 議題描述既有的 Markdown checklist(`- [ ]`/`- [x]`)視為既有 TODO 的一部分納入盤點;已勾選項目視為已完成,不重做,也不得因臆測而改判其完成狀態。
+103
View File
@@ -0,0 +1,103 @@
---
name: spec-model
description: JSC plugins 共用「取得可用模型並加上標籤」規範:固定標籤體系(能力等級/成本/延遲/上下文/用途/可用性)、任務→必要標籤對映表、取得模型清單的權威來源優先序(`claude-api` skill/CLI 自陳/使用痕跡/smoke test)、`~/.claude/jsc/models.json` 快取設計(30 天過期)、強制切換規則,以及讀到帶 `model:` frontmatter 清單檔時的模型檢查義務。當其他 skill 內文引用 spec-model 或 /jsc-shared:spec-model、或需要挑選模型/驗證模型可用性/開啟帶 `model:` frontmatter 的清單檔時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-model — 共用「取得可用模型並加上標籤」規範
所有 JSC skills 需要查詢目前可用哪些模型、為模型加上可供選型決策的標籤、依任務挑選推薦模型,或檢查執行者目前使用的模型是否符合某份清單/某個 skill 的要求時,一律遵守以下規範。
## 一、標籤體系(固定字彙表,不可自由發揮)
下表為**唯一**標籤字彙表;新增或改名標籤前一律先改本表,不得在其他 skill 內文自創同義詞。
| 面向 | 標籤 | 判準 |
| --- | --- | --- |
| 能力等級 | `#深度推理` | 多步推理、架構設計、跨檔案分析、需求拆解 |
| | `#均衡實作` | 一般編碼、文件重整、規格落地 |
| | `#輕量快速` | 摘要、分類、格式轉換、固定欄位抽取 |
| 成本 | `#高成本` `#中成本` `#低成本` | 依當下官方定價分三段,**不憑記憶**,每次都要查 |
| 延遲 | `#延遲敏感可用` | 在使用者等待路徑上仍可接受 |
| | `#可長時間跑` | 背景/批次工作 |
| 上下文 | `#長上下文` | ≥ 1M(如 `[1m]` 變體) |
| | `#標準上下文` | 200k 級 |
| 用途 | `#分析` `#實作` `#審查` `#摘要` `#對話人格` | 一個模型可帶多個標籤 |
| 可用性 | `#本機可用` | 已通過 smoke test |
| | `#不可用` | smoke test 失敗(無權限或不存在) |
| | `#未驗證` | 無法在此 CLI 驗證,只有文件記載 |
## 二、任務 → 必要標籤對映表
挑選模型時,先依任務類型找出下表的「必要標籤」——候選模型必須**同時具備**必要標籤才可推薦;「加分標籤」不具備也可推薦,只是排序上較後。
| 任務類型 | 必要標籤 | 加分標籤 |
| --- | --- | --- |
| 需求分析/拆 TODO/架構決策 | `#深度推理` `#分析` `#本機可用` | `#長上下文` |
| 依清單實作/規格落地 | `#均衡實作` `#實作` `#本機可用` | `#中成本` |
| code review/findings 判讀 | `#深度推理` `#審查` `#本機可用` | — |
| 逐輪摘要(worklog)/分類 | `#輕量快速` `#摘要` `#延遲敏感可用` `#低成本` | — |
| 人格對話 | `#對話人格` `#本機可用` | `#延遲敏感可用` |
同一任務有多個候選都滿足必要標籤時,優先挑加分標籤命中較多者;仍並列則挑 `verified_at`(見〔四、快取設計〕)較新者。
## 三、取得清單的權威來源優先序
依序取得可用模型清單與其中繼資料(id/定價/上下文長度),**前一項可取得就不往下**:
1. **Claude Code 內建 `claude-api` skill**——模型 id/定價/上下文長度的權威來源,**不憑記憶**,每次查都要實際載入這個 skill 取得當下資料,不可用過去對話中記得的數字代替。
2. **CLI 自陳**:
- Claude Code:`claude --help` 的 `--model` 說明列出的 alias(例如 `fable`/`opus`/`sonnet` 這類最新模型別名,實際列出內容以當次 `--help` 輸出為準,不可硬編碼)。
- 其他 CLI(`codex`/`opencode`/`agy`/`copilot`)用各自的列出指令取得;取不到就把該模型標為 `#未驗證`,不得省略、也不得用其他 CLI 的結果替代。
3. **使用痕跡**:`~/.claude/stats-cache.json` 內 `dailyModelTokens`/`modelUsage` 等欄位出現過的模型 id(例如曾實際用過的 `claude-opus-5`、`claude-sonnet-5`、`claude-haiku-4-5-20251001`)。這一層**只當補充候選**,用來發現前兩層沒列出但實際上帳號可用的模型,**不當權威**——出現在這裡不代表現在仍可用,仍須走第 4 步驗證。
4. **Smoke test 驗證可用性**(標準做法,所有候選模型都要跑):
```bash
CLAUDE_CODE_CHILD_SESSION=1 timeout 45 claude -p 'OK' --model <id>
```
不可用時 stderr/stdout 會出現「is not a model this version of Claude Code recognizes」或「There's an issue with the selected model」;出現任一即標 `#不可用`;正常回應則標 `#本機可用`。
⚠️ **必須帶 `CLAUDE_CODE_CHILD_SESSION=1`**,否則會觸發巢狀 session 與 SessionStart hook,汙染 smoke test 結果也可能造成非預期副作用。
## 四、快取設計
- 路徑:`~/.claude/jsc/models.json`。
- 每筆模型記錄的欄位:`{ id, aliases[], tags[], context, pricing, verified_at, verdict, reason }`。
- `id`:模型完整 id(如 `claude-sonnet-5`)。
- `aliases[]`:CLI 自陳取得的別名(如 `sonnet`)。
- `tags[]`:依〔一、標籤體系〕貼上的標籤陣列。
- `context`:上下文長度(依〔一〕分類為 `#長上下文`/`#標準上下文` 的依據數值)。
- `pricing`:查證當下的定價摘要(來源為 `claude-api` skill 或對應 CLI 文件)。
- `verified_at`:本筆最後驗證(含 smoke test)的時間,格式依 `/jsc-shared:spec-time-log`。
- `verdict`:`本機可用`/`不可用`/`未驗證` 三者之一,對應〔一〕的可用性標籤。
- `reason`:判定 `verdict` 的簡短依據(例如 smoke test 的錯誤訊息摘要、或「僅文件記載未能於此 CLI 驗證」)。
- **過期規則**:`verified_at` 距今**超過 30 天視為過期**(沿用 worklog 快取的年限慣例),過期記錄需重新走〔三、取得清單的權威來源優先序〕更新,不得直接沿用。
- **內容邊界(重要)**:快取檔**只准存模型中繼資料**(上述欄位),**不得寫入任何工作內容或使用者資料**(例如對話內容、專案路徑、議題內容、任何個資)。任何要寫進這份快取的內容,寫入前都要檢查是否落在這個邊界內。
## 五、強制切換規則(供 `/jsc-shared:todo` 與其他 skill 使用)
任何 skill 宣告了必要標籤(依〔二〕的對映表)或直接指定模型時,執行者當前模型不符就**停止並要求使用者切換**:
- **不得自行降級或升級**到別的模型頂替。
- **不得先做一部分**再提醒使用者切換——必須在動手前就完成檢查並停下。
- 錯誤訊息格式依 `/jsc-shared:spec-time-log` 的 `[時間][階段][等級]: 訊息`:
```
[yyyy/MM/dd HH:mm:ss][模型檢查][ERR]: 本清單/本 skill 指定 <model>(<alias>),當前模型為 <current-model-id>。
請執行 /model <alias> 切換後重新載入,本次不進行任何修改。
```
- **限制**:Claude Code **沒有環境變數可讀取當前模型 id**,只能以 agent 對自己當下模型的自我回報為準;自我回報不確定時,請使用者執行 `/status` 確認目前模型後再繼續判斷,不得用猜測代替確認。
## 六、讀到帶 `model:` frontmatter 的清單檔時的檢查義務
任何 agent 開啟像 `todo.md` 這種帶 `model:` frontmatter 的清單檔時,**開始執行清單內容之前**都要先依〔五、強制切換規則〕做一次模型檢查:
1. 讀出清單檔 frontmatter 的 `model`(與可能並列的 `model_alias`)。
2. 依〔五〕的方式確認當前模型(agent 自我回報,不確定就請使用者 `/status` 確認)。
3. 兩者不符 → 依〔五〕的錯誤訊息格式停止,不得先執行清單中的任何一項再提醒。
4. 相符才繼續往下執行清單內容。
**具體操作步驟(可直接照做,不需另外解讀)**:開啟一份 `.md` 檔案、發現其 YAML frontmatter 含 `model` 欄位時,**在做任何修改前**先自我回報目前模型 id,並與 frontmatter 的 `model` 欄位比對;不符就依〔五、強制切換規則〕的錯誤訊息格式中斷,本次不進行任何修改;相符才繼續往下執行檔案內容。
本節會被 `/jsc-shared:todo`(產生指定模型 `todo.md` 的 skill)與其他消費這類清單檔的 skill(例如處理 `TARGET.md`、處理議題 TODO 的 skill)引用,各消費端不必重抄本節內容,直接引用本節即可。
+38
View File
@@ -0,0 +1,38 @@
---
name: spec-no-scratch-files
description: JSC plugins 共用「不落地絕對準則」:全程不建立任何草稿檔/暫存檔(不寫 `.docs/`、不用本機檔案傳遞中間結果),中間成果一律留在對話內容、議題描述、留言,或 subagent 的回傳內容裡;唯一例外是處理 Gitea 議題附件(圖片、文件等)時可唯讀暫存下載到系統暫存目錄以讀取內容,讀取完畢立即刪除,不得累積成工作目錄垂圾、也不得用來傳遞其他中間成果。當其他 skill 內文引用 spec-no-scratch-files 或 /jsc-shared:spec-no-scratch-files、或執行任何 JSC skill 需要確認「是否可以落地檔案」時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-no-scratch-files — 共用不落地絕對準則
所有 JSC skills 的絕對準則是**全程不落地任何草稿檔/暫存檔**。中間成果(需求彙整、TODO 排序、同步計畫、勾稽結果、進度摘要等)一律保存在**對話內容**、**議題描述**、**留言**,或**subagent 的回傳內容**裡,不得用本機檔案傳遞。
## 禁止事項
- **不建立任何草稿檔**:不寫 `.docs/`、不寫暫存檔、不用本機檔案傳遞中間結果。
- **不建立工作目錄垂圾**:不因為流程方便就在 repo 內或家目錄下新增暫存資料夾長期留存。
- **不依賴檔案交接 subagent**:分派給 subagent 的輸入與其回傳結果都走訊息內容,不透過檔案。
## 中間成果的合法保存位置
| 成果類型 | 保存位置 |
| --- | --- |
| 需求彙整、TODO 列表、排序結果 | 議題描述(`body`)或議題留言(comment) |
| 同步計畫、勾稽結果、待確認清單 | 對話內容,或 subagent 的回傳值 |
| 進度回報、驗證結果 | 議題留言 |
| 一次性分析過程 | 對話內容,不需保存 |
## 唯一例外:Gitea 議題附件
處理 Gitea 議題(含留言)的附件時,圖片、文件等二進位或需要另行讀取的內容,可能必須先落地才能讀取:
- **唯讀且用完即棄**:只為了讀取內容而暫存下載到系統暫存目錄(非工作目錄、非任何 repo 內),讀取完畢後**立即刪除**。
- **不得擴大用途**:不得把這個暫存目錄拿來傳遞其他中間成果,也不得留著給後續步驟重複使用。
- **文字類附件優先不落地**:Markdown、純文字、CSV、JSON 等文字類附件,直接以 `curl` 取得內容到對話中分析即可,不需要落地。
- **無法讀取就列出待確認**:格式無法讀取、或沒有下載權限時,在對話或議題內容中列出附件檔名與 URL 並標註「附件無法讀取,需人工確認」,不得臆測其內容,也不得為此保留暫存檔。
## 自我檢查
1. 流程中若出現 `Write`/`mkdir` 之類會在磁碟建立內容的動作,先確認是否為附件暫存的合法例外;不是則改用對話或議題留言保存。
2. 附件暫存檔在讀取完成後,同一輪流程內即刪除,不留到流程結束才清理。
3. subagent 之間交接的內容,全部走 SendMessage/回傳值,不出現「先寫檔案給下一個 subagent 讀」的設計。
+42
View File
@@ -0,0 +1,42 @@
---
name: spec-node-src-layout
description: JSC plugins 共用「Node 主程式 src/ 收攏規範」:主程式改寫為 Node 之後,主程式入口及其 require/import 依賴鏈的 .js/.mjs/.cjs 檔集中收進 src/;明文排除工具設定檔(如 *.config.js)與 test/、tests/、scripts/ 目錄;搬移後必須更新所有引用該路徑的地方(require/import 相對路徑、package.json 的 main/bin/scripts/exports、action.yml/Dockerfile/entrypoint.sh 等指令檔內的路徑)。當其他 skill 內文引用 spec-node-src-layout 或 /jsc-shared:spec-node-src-layout、或需要把 Node 主程式的 .js 收進 src/ 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-node-src-layout — 共用 Node 主程式 src/ 收攏規範
主程式改寫(或確認)為 Node 之後,一律依以下規範把主程式及其依賴鏈收進 `src/`,不得另行約定不同的收攏範圍或排除規則。
## 收攏範圍
- 在專案根目錄建立 `src/`(若不存在)。
- 將**主程式入口及其 `require`/`import` 依賴鏈**的 `.js`/`.mjs`/`.cjs` 檔**移入 `src/`**,優先 `git mv` 保留歷史。
- 依賴鏈的判定只沿著專案自有原始碼的 `require`/`import` 遞迴展開,**排除** `node_modules`/`.git`/`.docs`/`dist`/`bin`/`obj` 與其他第三方依賴目錄。
- 主程式入口統一為 `src/index.js`(或 `src/<main>.js`)。
## 明文排除、不搬
以下檔案/目錄即使被主程式引用,仍**留在原位、不移入 `src/`**,因為外部工具依慣例路徑尋找它們,搬走會弄壞 lint/test/build 流程:
| 類型 | 範例 |
| --- | --- |
| 工具設定檔 | `*.config.js`(`eslint.config.js`/`jest.config.js`/`webpack.config.js` 等)、`.*rc.js`(husky/commitlint 等工具設定) |
| 測試目錄 | `test/`、`tests/` |
| 腳本目錄 | `scripts/` |
## 搬移後必須更新的引用
搬移完成後,逐一確認並更新所有引用該檔案舊路徑的地方,確保不破壞既有行為:
- 模組間的 `require`/`import` 相對路徑(包含被排除、留在原位但引用了搬入 `src/` 檔案的檔案,例如 `test/`/`scripts/` 內的引用路徑也要同步更新)。
- `package.json` 的 `main`/`bin`/`scripts`/`exports`(改指向 `src/...`)。
- 指令檔內對主程式路徑的引用:`action.yml` 的 `runs.main`/`runs.entrypoint`、`Dockerfile`、`entrypoint.sh` 等。
若 `src/` 下需要的相依尚未宣告,於對應的 `package.json` 補上;不擅自新增與功能無關的相依。
## 自我檢查
1. `src/` 內的主程式與其依賴鏈完整,沒有漏搬的中間模組(執行時不會因路徑找不到而失敗)。
2. 明文排除清單內的檔案/目錄確實留在原位。
3. 全專案搜尋舊路徑(檔名/相對路徑字串),確認沒有殘留引用。
4. 若環境可執行,實際跑一次主程式或既有測試/lint/build 指令驗證搬移未破壞行為;無法執行時說明原因並標註風險。
+153
View File
@@ -0,0 +1,153 @@
---
name: spec-plugin-cli
description: JSC plugins 共用「五種助理 plugin 安裝/更新/移除指令」規範:Claude Code、Codex、Antigravity(agy)、OpenCode、GitHub Copilot CLI 各自的 marketplace add/plugin install/plugin update/plugin uninstall 完整指令語法,以 <host>/<name>/<plugin>/<marketplace>/<token>/<url> 佔位符套用到任一 JSC plugin repo(code/doc/persona/shared),本機開發(免 push)的本地路徑一律用 %USERPROFILE%\.../$HOME/... 佔位符示範,不得寫死真實使用者帳號路徑。當其他 skill 或 README 內文引用 spec-plugin-cli 或 /jsc-shared:spec-plugin-cli、或需要說明/產生某個 plugin 的安裝、更新、移除指令時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-plugin-cli — 共用 Plugin 安裝/更新/移除指令規範
四個 JSC plugin repo(`code`/`doc`/`persona`/`shared`)各自的 README 都有一節「安裝 / 更新 / 移除(各助理)」,內容除了 plugin 名稱之外幾乎完全重複;`shared/skills/plugins-install/SKILL.md`、`shared/skills/plugins-uninstall/SKILL.md` 又各自重述一次同一組指令。本規範是這組指令的**唯一權威版本**:其他 skill 內文或 README 需要說明某個助理的 plugin 安裝/更新/移除指令時,一律引用本節並只代換下方佔位符,不得另行複述完整指令區塊。
## 佔位符(唯一權威定義)
| 佔位符 | 意義 | 以 `jsc-shared` 為例 |
| --- | --- | --- |
| `<host>` | gitea 主機;決定順序見 `/jsc-shared:spec-gitea` 的「gitea 主機決定順序」一節,本規範不重複定義 | `gitea.jsc.idv.tw` |
| `<name>` | plugin repo 短名 | `shared`(其餘為 `doc`/`code`/`persona`) |
| `<url>` | repo 網址 = `https://<host>/plugins/<name>.git` | `https://gitea.jsc.idv.tw/plugins/shared.git` |
| `<plugin>` | plugin 名(各助理 manifest 的 `name` 欄位,例如 `.claude-plugin/plugin.json`) | `jsc-shared` |
| `<marketplace>` | marketplace 名(Claude/Codex/Copilot marketplace 登錄名) | `shared` |
| `<token>` | 安裝/更新/移除用的 install token,固定等於 `<plugin>@<marketplace>` | `jsc-shared@shared` |
> **`persona` 沒有例外**:現行 marketplace 名就是 repo 名 `persona`(即 `<token>` = `jsc-persona@persona`),跟其他三個 repo 規則一致。`jsc-plugins` 是**已停用的舊名**,只會出現在早期安裝過的本機殘留鍵裡;遇到使用者本機還留著 `jsc-persona@jsc-plugins`,要提醒先移除舊鍵再用 `jsc-persona@persona` 重裝,不要把舊名當成現行規則的例外套用。
## 通用前提
- **Claude / Codex 從 git URL 安裝會 clone 遠端**,安裝前請先把該 repo `push` 到 gitea。
- **Antigravity 的 `agy plugin install <url>` 目前只支援 github.com**;gitea 等自架 git 不支援 URL 安裝,一律走「clone 到本機固定目錄 + 本地路徑安裝」,且**該 clone 目錄持久保留、更新用 `git pull` 而非每次重新 clone**(與 `plugins-install`/`plugins-uninstall` 的階段 C 實作一致)。
- **OpenCode 沒有原生 plugin 匯入指令,但可使用 skill**:改用「clone 到本機固定目錄 → 複製 `skills/` 到工具的 skills 目錄」的目錄安裝法,同樣採持久 clone + `git pull`。
- **GitHub Copilot CLI** 具備與 Claude Code 相同形態的 `marketplace` / `plugin` 原生指令。
- 下方每個助理小節的指令都以「安裝 → 更新 → 移除」固定順序給出。
---
## Claude Code
```bash
# 安裝
claude plugin marketplace add <url>
claude plugin install <token>
# 更新
claude plugin marketplace update <marketplace>
claude plugin update <token>
# 移除
claude plugin uninstall <token>
claude plugin marketplace remove <marketplace>
```
- 工作階段內 slash 版(等價):把 `claude plugin` 換成 `/plugin`。
- **本機開發(免 push)**:改用本地路徑安裝,例如 Windows `claude plugin marketplace add %USERPROFILE%\<你的工作區路徑>\<name>`、Linux/macOS/WSL `claude plugin marketplace add $HOME/<你的工作區路徑>/<name>`,安裝後同樣接 `claude plugin install <token>`。**路徑僅示範結構,實際位置依開發者本機安排;引用端產生文件時不得把真實使用者帳號名稱寫死進路徑**,一律用 `%USERPROFILE%\...`/`$HOME/...` 這類佔位符。正式安裝仍以 Gitea 遠端+上方指令為準。
- 呼叫:Claude Code / Antigravity 用 `/jsc-<name>:<skill>`(例 `/jsc-shared:spec-output`)。
## Codex
```bash
# 安裝
codex plugin marketplace add <url>
codex plugin add <token>
# 更新(重新抓取 marketplace 的 git 快照)
codex plugin marketplace upgrade <marketplace>
# 移除
codex plugin remove <token>
codex plugin marketplace remove <marketplace>
```
- 安裝 token `<token>` = plugin 名(`.codex-plugin/plugin.json` 的 `name`)@ marketplace 名(`.agents/plugins/marketplace.json` 的 `name`)。
- 該 repo 的 Codex marketplace 以 `url` 來源指向自己,故 Codex **一律從 gitea 安裝**(需先 push);安裝後重啟 Codex。
- 呼叫:`$<skill>`(例 `$spec-output`),或用 `/skills` 選單。
## Antigravity(`agy`)
> `agy plugin install <url>` 目前**只支援 github.com**;gitea 等自架 git 不支援 URL 安裝,一律走「clone 到本機固定目錄,再用本地路徑安裝」;`agy` 沒有 `update` 子指令,更新=`git pull` 後重裝。
```bash
# 安裝(本機尚無 clone)
git clone <url> <clone-dir>/<name>
agy plugin install <clone-dir>/<name>
# 更新(已有 clone → git pull 後重裝,不重新 clone)
git -C <clone-dir>/<name> pull
agy plugin uninstall <plugin>
agy plugin install <clone-dir>/<name>
# 移除
agy plugin uninstall <plugin>
```
- `<clone-dir>` 沒有跨助理強制的唯一值:Windows 佔位符範例 `%USERPROFILE%\plugins`,Linux/macOS/WSL 佔位符範例 `$HOME/plugins`;引用端(README、`plugins-install` 等)可自訂自己的預設 clone 根目錄,但**一律用佔位符表示,不得寫死真實使用者帳號路徑**。
- 若把 skills 放到 GitHub,可直接 `agy plugin install https://github.com/<owner>/<repo>`,不需 clone。
- 其他常用子指令:`agy plugin list`、`agy plugin enable <plugin>` / `disable <plugin>`、`agy plugin validate <path>`。安裝或更新後需重啟工作階段。
- 呼叫:`/jsc-<name>:<skill>` 或依描述自動觸發。
## OpenCode
> OpenCode 沒有原生 plugin 匯入/移除指令,但可使用 skill:改用「clone 到本機固定目錄 → 複製 `skills/` 到工具 skills 目錄」的目錄安裝法。OpenCode 會讀 `~/.config/opencode/skills/`(也會讀 `~/.claude/skills/`、`~/.agents/skills/`)。
```bash
# 安裝
git clone <url> <clone-dir>/<name>
mkdir -p ~/.config/opencode/skills
cp -r <clone-dir>/<name>/skills/* ~/.config/opencode/skills/
# 更新(git pull 後重新複製,不重新 clone)
git -C <clone-dir>/<name> pull
cp -r <clone-dir>/<name>/skills/* ~/.config/opencode/skills/
# 移除:從 clone 目錄的 skills/ 即時推導清單逐一刪除,不要手抄寫死的 skill 名單(plugin 新增 skill 後手抄清單會漏)
for s in <clone-dir>/<name>/skills/*/; do rm -rf "$HOME/.config/opencode/skills/$(basename "$s")"; done
```
- `cp -r` 是合併不是覆蓋:上游已刪除的 skill 目錄會在本機殘留,要乾淨更新請先用上面的移除迴圈清掉舊目錄再複製一次。
- **Windows PowerShell**:`cp -r A B` → `Copy-Item A B -Recurse -Force`、`rm -rf X` → `Remove-Item X -Recurse -Force`、`~` → `$HOME`、`<clone-dir>` 佔位符範例 `%USERPROFILE%\plugins`。
- 呼叫:直接描述需求,模型會依 skill 描述自動透過 skill 工具呼叫。
## GitHub Copilot CLI
```bash
# 安裝
copilot plugin marketplace add <url>
copilot plugin install <token>
# 更新
copilot plugin marketplace update <marketplace>
copilot plugin update <token>
# 移除
copilot plugin uninstall <token>
copilot plugin marketplace remove <marketplace>
```
- 安裝 token `<token>` = plugin 名(plugin manifest 的 `name`)@ marketplace 名。
- `copilot plugin marketplace add` 支援 GitHub `owner/repo`、git URL 與本地路徑;Gitea repo 用上方 HTTPS URL。
- 呼叫:自然語言或 plugin skills,例如 `copilot -i "請使用 <skill> 說明其內容"`。
---
## 各助理速查
| 助理 | 安裝 | 更新 | 移除 |
| --- | --- | --- | --- |
| Claude Code | `marketplace add` + `install` | `marketplace update` + `update` | `uninstall` + `marketplace remove` |
| Codex | `marketplace add` + `add` | `marketplace upgrade` | `remove` + `marketplace remove` |
| Antigravity | `git clone` + `install` | `git pull` + `uninstall`→`install` | `uninstall` |
| OpenCode | `git clone` + `cp -r skills/*` | `git pull` + `cp -r skills/*` | 依 clone 的 `skills/` 逐一 `rm -rf` |
| GitHub Copilot CLI | `marketplace add` + `install` | `marketplace update` + `update` | `uninstall` + `marketplace remove` |
## 給樣板產生器的備註
- 四份 README 的「安裝 / 更新 / 移除(各助理)」章節,之後應改為引用本規範並只代換 `<host>`/`<name>`/`<plugin>`/`<marketplace>`/`<token>`/`<url>`,不再各自重複完整指令區塊;`shared/skills/plugins-install/SKILL.md`、`plugins-uninstall/SKILL.md` 亦同。
- `plugins-install`/`plugins-uninstall` 的階段 C 已採用與本規範一致的「持久 clone + `git pull`」寫法,可視為本規範的既有實作範例;`shared/README.md` 目前的 Antigravity/OpenCode 段落改用「每次重新 clone 到暫存目錄」的寫法,與本規範及 `doc`/`code` README 不一致,樣板產生時請一併改為本規範版本。
- 本機開發本地路徑一律使用 `%USERPROFILE%\...`/`$HOME/...` 佔位符,不得出現真實使用者帳號名稱路徑。
+1 -1
View File
@@ -43,5 +43,5 @@ description: JSC plugins 共用「plugin 版號規則」:三個 manifest(plu
## commit 與發佈
- 版本 bump 的 commit 訊息:`chore(plugin 版本): 三家 manifest 升版 X.Y.Z`(僅含 3 個 manifest 的版本變更;與其他設定異動混提時說明清楚)。
- 版本 bump 的 commit 訊息格式依 `/jsc-shared:spec-conventional-commit`,type 固定用 `chore`、範圍固定為 `plugin 版本`(例:`chore(plugin 版本): 三家 manifest 升版 X.Y.Z`;僅含 3 個 manifest 的版本變更,與其他設定異動混提時說明清楚)。
- 合併發佈後,各助理的更新方式見該 plugin README(`claude plugin update`/`codex plugin marketplace upgrade`/Antigravity 重新安裝/OpenCode 重新複製 `skills/`)。
+68
View File
@@ -0,0 +1,68 @@
---
name: spec-preflight
description: JSC plugins 共用「規範前置載入流程」:每個 skill 執行前先載入本規範自身、再依序載入自己需要的其他 /jsc-shared:spec-xxx;任一載入不到即代表 shared plugin 未安裝,用 AskUserQuestion 詢問是否安裝,使用者拒絕就中斷該 skill,絕不憑名稱或記憶臆測規範內容繼續執行。當其他 skill 內文引用 spec-preflight 或 /jsc-shared:spec-preflight、或執行任何需要先載入共用規範的 JSC skill 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-preflight — 共用規範前置載入流程
「shared plugin(`jsc-shared`)本身是所有共用規範的存放處,但『要先去載入 shared 的規範』這件事沒有地方可以事先講」——這是**雞生蛋問題**:其他 16+ 個 skill 都得依賴 shared 的 spec 才能正確執行,卻不能把「怎麼判斷 shared 有沒有裝」這段邏輯也放進 shared 裡,否則 shared 未安裝時連這段判斷邏輯都載入不到。本規範就是這個問題的**唯一解法本體**:其他 skill 只需在檔頭保留一段**最小**的本地文字(見〔被引用時的最小前置區塊〕),把完整流程都委派給本規範。
## 載入順序
每個 skill 執行前,依下列順序以 Skill 工具載入,**順序不可顛倒**:
1. **先載入本規範自身**:`/jsc-shared:spec-preflight`。這一步本身就是探測——載入成功代表 shared plugin 已安裝,可以繼續往下載入其他 spec;載入失敗直接進入〔載入失敗的處理〕。
2. **再依該 skill 自己列出的規範清單,逐一載入其他 `/jsc-shared:spec-xxx`**。清單與載入順序由各 skill 自己在檔頭決定(通常照該 skill 內文實際用到的先後順序排列),本規範不代為規定其他 spec 之間的順序。
## 載入失敗的處理
只要**任一** spec(包含本規範自身)載入不到,一律判定為**shared plugin(`jsc-shared`)未安裝**,不視為暫時性錯誤、不重試、不略過繼續:
1. 用 **AskUserQuestion** 詢問使用者是否要安裝 shared plugin,安裝目標固定是:
```
https://gitea.jsc.idv.tw/plugins/shared.git
```
安裝步驟可參考 `/jsc-shared:plugins-install` 的安裝方式(若該 skill 當下也載入不到,代表連它都不存在,此時直接依一般 plugin 安裝方式引導使用者:把上述 repo 加入對應助理的 plugin marketplace 並安裝 `jsc-shared`)。
2. 使用者同意安裝 → 完成安裝後,**從〔載入順序〕第 1 步重新開始**逐一載入,全部成功才繼續執行原 skill 剩餘步驟。
3. 使用者拒絕安裝 → 依〔使用者拒絕時的中斷規則〕處理。
## 使用者拒絕時的中斷規則
**使用者不安裝則直接中斷本 skill**:
- 立刻停止呼叫本規範的那個 skill,不得繼續執行任何後續步驟(包含它原本排在前面、看似與缺失的 spec 無關的步驟)。
- 不得因為「這個 skill 大部分邏輯不依賴那份 spec」而自行判斷可以跳過繼續做。
- 只需回報「因缺少共用規範 `spec-xxx` 且使用者未安裝 shared plugin,本次 `<skill 名稱>` 已中斷」,不需要也不應該杜撰替代做法。
## 禁令(不可違反)
**絕不允許在 spec 載入失敗的情況下,僅憑該 spec 名稱字面意思或以往記憶臆測其內容繼續執行;規範內容以實際載入到的 spec 檔案為準。**
- 即使助理「記得」某個 `spec-xxx` 通常講什麼(例如過去對話中讀過),只要**這次呼叫**沒有成功載入,就必須視為「內容未知」,不可用記憶內容替代。
- skill 描述(`description`)裡對某個 spec 的一行摘要只是索引用途,**不是規範本文**,載入失敗時不得只憑那一行摘要繼續執行。
- 這條禁令沒有例外,即使使用者在對話中催促「先照你知道的做」也不成立——中斷並如實說明原因,交由使用者裁示。
## 被引用時的最小前置區塊(給其他 skill 抄的範本)
其他 skill 檔案在檔頭只需保留下列**最小**文字,不可展開重抄本規範全文,也不可省略「載入不到即中斷」這句:
```markdown
## 共用規範(必要前置)
先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝,
依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。
本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-gitea`、…(依各 skill 實際需要的規範清單列出)
```
- 最後一行的規範清單**只列名稱**,不附一行摘要(摘要是規範內容的重抄,會與本文漂移不一致;需要摘要時直接載入該 spec 看本文)。
- 清單順序建議照該 skill 內文實際用到的先後排列,方便對照。
## 適用範圍
| 情境 | 是否適用本規範 |
| --- | --- |
| 任何會在檔頭載入一個或多個 `/jsc-shared:spec-xxx` 的 skill | 適用,且必須放在所有其他規範載入之前 |
| skill 本身就是某個 `spec-xxx`(例如 `spec-gitea`、`spec-dockerfile`) | 不適用;規範檔本身不需要也不應該引用本規範,避免循環依賴 |
| 使用者直接呼叫 `/jsc-shared:spec-preflight` 詢問內容 | 不適用「執行前載入」的情境,直接依本檔內容回答即可 |
+82
View File
@@ -0,0 +1,82 @@
---
name: spec-pull-request
description: JSC plugins 共用「PR 建立流程」:目標分支不得臆測(明確得知或詢問使用者)、解析 origin 座標決定 owner/repo(host 依 spec-gitea 主機決定順序)、full 與 simple 兩種 PR 描述模式、`POST /pulls` 的 body 以 UTF-8 檔案帶入(不可字面 `\n`)、已有相同 head→base 的 open PR 時沿用不重開、API 失敗遮蔽 token、完成後提醒清除對話內文。當其他 skill 內文引用 spec-pull-request 或 /jsc-shared:spec-pull-request、或需要透過 Gitea API 建立 PR 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-pull-request — 共用 PR 建立流程
所有 JSC skills 需要透過 Gitea API 開 Pull Request(例如 `review-resolve` 依議題/findings 修復後發 PR、`target` 每日待辦完成後對預設分支發 PR)時,一律遵守以下規範。本 spec 只講 PR 流程本身;token 機密保護與遮蔽規則一律引用 `/jsc-shared:spec-gitea`,不在此重複。
## 目標分支不得臆測
- 目標分支必須是**明確得知**(呼叫端參數帶入、或流程中已確認的遠端預設分支)或**詢問使用者**取得,**嚴禁猜測或預設**(不可自行假設 `develop`/`main`/`master`)。
- 若呼叫端有「遠端預設分支」這類已判定好的來源(如 `git symbolic-ref refs/remotes/origin/HEAD`),可直接採用視為「明確得知」,不需再問;沒有把握時一律停下詢問,可用 `git branch -r` 列出分支輔助使用者選擇。
- 目標分支一旦確定,後續建立 PR、查詢既有 PR 都以同一個值為準,不可中途換掉。
## 解析 origin 座標(host/owner/repo)
PR 相關的 API 呼叫都建立在 `<owner>/<repo>` 這個座標上,一律從 `git remote get-url origin` 解析:
```bash
git remote get-url origin
# 例:https://gitea.jsc.idv.tw/plugins/code-review.git
# → host=gitea.jsc.idv.tw、owner=plugins、repo=code-review
```
- host 的決定順序(`--host` 參數/`$GITEA_HOST`/origin 所在主機/詢問使用者)是唯一權威流程,一律引用 `/jsc-shared:spec-gitea` 的「gitea 主機決定順序」,不在此另行複述或改動順序。
- 解析失敗(origin 不是 Gitea 網址、或無法拆出 owner/repo)→ 回報並停止,不猜測替代座標。
## 已有相同 head→base 的 open PR 時沿用不重開
**每次開 PR 前必須先查**,不可直接送出建立請求造成重複 PR:
```bash
curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
"https://<host>/api/v1/repos/<owner>/<repo>/pulls?state=open&base=<目標分支>"
```
- API 只能用 `base` 篩選,**head 需要在回應中自行比對**:分頁完整讀取(依 `/jsc-shared:spec-gitea` 的分頁慣例),逐筆檢查 `head.ref`(或對等欄位)是否等於本次 PR 的來源分支。
- 找到 `head=<來源分支>` → `base=<目標分支>` 的 open PR → **沿用它**,不重複建立:
- 回報既有 PR 的連結/編號給使用者。
- 本次新推的 commit 會自動出現在該 PR 上;視情境需要可在該 PR 補一則本次進度留言,但**不再呼叫 `POST /pulls`**。
- 沒找到才進入下一節建立新 PR。
## PR 描述模式(`full` / `simple`)
呼叫端依需求二選一,未指定時預設 `full`,**不要為描述形式中斷詢問**:
| 模式 | 內容 | 產生方式 |
| --- | --- | --- |
| `full`(完整版) | 結構化繁體中文說明——變更摘要、影響範圍、重點檔案/模組、風險或注意事項 | **重新分析並總結** `git diff <目標分支>...<PR 來源分支>`(比對來源分支自分岔點以來的變更),不是貼原始 diff,而是「人讀得懂的總結」 |
| `simple`(簡單版) | 逐條列出本分支領先目標分支的 commit 訊息 | `git log --oneline "<目標分支>..<PR 來源分支>"`,以條列呈現每行 commit 訊息 |
- 呼叫端也可直接提供自訂描述文字取代以上兩種模式;此時原樣採用,不再套用 `full`/`simple` 的產生方式。
- **標題**預設取一句總結(可用首個 `feat`/`fix` commit 訊息或分支用途);呼叫端若有更貼合情境的固定命名規則(例如含日期的前綴),可自行覆寫此預設。
## 建立 PR:body 一律用 UTF-8 檔案帶入
`POST /pulls` 的 body **必須**以 UTF-8 檔案帶入,換行用**實際換行**,不可送出字面 `\n`(例如讓 PR 顯示成 `## Commit\n\n- ...` 這種未展開的跳脫字串):
```bash
# body.json 先以 UTF-8 檔案(heredoc/printf/程式寫檔)準備好,換行是實際換行
curl -sS -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://<host>/api/v1/repos/<owner>/<repo>/pulls" \
--data @body.json
```
- 不可用 shell 字面 `"標題\n內容"` 這種寫法組 body;若用 `jq` 組 JSON,改用 `jq --rawfile` 或等效方式帶入多行內容。
- **成功**:取回應中的 PR 連結/編號回報使用者。
- **失敗**:顯示 API 回應的錯誤訊息供排查,**先依 `/jsc-shared:spec-gitea` 的機密遮蔽規則遮蔽 token** 才能輸出。常見錯誤:
- 目標分支不存在。
- 已有相同 head→base 的 open PR(回到上一節查詢並沿用,不當作失敗處理)。
- token 權限不足(401/403)。
## 完成後提醒清除對話內文
push/API 呼叫過程可能讓 token 殘留在對話內文,PR 建立完成後:
1. 通知使用者:PR 連結/編號、目標分支、採用的描述形式(`full`/`simple`/自訂)。
2. 提醒清除對話內文以防 token 外洩(互動情境用 Claude Code `/clear` 或當前助理對等指令;排程情境只寫 log、不輸出 token)。
3. 清除前再次確認輸出與 log 中沒有明文 token——細節與遮蔽規則一律依 `/jsc-shared:spec-gitea`,本 spec 不重複。
+61
View File
@@ -0,0 +1,61 @@
---
name: spec-script-path
description: JSC plugins 共用「plugin 內腳本路徑解析」規範:Claude Code 用 `${CLAUDE_PLUGIN_ROOT}`、其他助理用 skill 載入時提示的 base directory 往上兩層推導出 plugin 根目錄,絕不可用相對路徑呼叫腳本,解析不到就回報並停止。當其他 skill 內文引用 spec-script-path 或 /jsc-shared:spec-script-path、或需要呼叫 plugin 內 `scripts/` 下的腳本時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-script-path — 共用 plugin 內腳本路徑解析
skill 執行時的工作目錄是**使用者的專案目錄**,不是 plugin 根目錄,因此**絕不可用相對路徑呼叫 plugin 內的腳本**。所有 JSC skill 呼叫 `scripts/` 下的腳本前,一律先依本節解析出 plugin 根目錄,再組成絕對路徑。
## 核心規則:plugin 根目錄解析
| 環境 | plugin 根目錄 |
| --- | --- |
| Claude Code | `${CLAUDE_PLUGIN_ROOT}` |
| 其他助理 | 本 skill 載入時提示的 base directory(形如 `.../skills/<skill-name>`)往上兩層 |
- Claude Code 有現成環境變數可直接取用;其他助理沒有這個變數,只能從「skill 被載入時系統提示的 base directory」往上推導——`skills/<skill-name>` 往上兩層即為 plugin 根目錄(`<base>/../..`)。
- 兩種環境推導出的路徑**指向同一個 plugin 根目錄**,只是取得方式不同,接到 `scripts/...` 之後即為腳本絕對路徑。
## 鐵則:絕不用相對路徑重試
- 一律使用上面解析出的絕對路徑呼叫腳本;**不可**因為解析失敗就退回用相對路徑(例如 `./scripts/xxx` 或 `../scripts/xxx`)試著呼叫——工作目錄是使用者專案目錄,相對路徑幾乎必定指向錯誤位置,靜默失敗或誤動作都比明確報錯更危險。
- 解析出的路徑要實際指向存在的目錄/檔案才算解析成功,不可只組出字串就當作可用。
## 解析不到時的標準錯誤處理
解析不到 plugin 根目錄,或組出的路徑下不存在對應的 `scripts/` 內容時:
1. 回報「plugin 目錄未包含 scripts/xxx,本 skill 在此環境不可用」(`xxx` 替換為實際缺少的腳本或目錄名)。
2. 停止該 skill 的後續流程,不要改用相對路徑重試,也不要臆測其他路徑。
## 兩種合規寫法
以下兩種表達方式都符合本規範,各 skill 可依自身複雜度(是否只呼叫一個固定 CLI、或需要組多個腳本路徑)自由選用。
### 寫法一:多行版(適合需要說明推導細節、或腳本路徑本身會被多處引用的 skill)
```bash
# Claude Code
WORKLOG_DIR="${CLAUDE_PLUGIN_ROOT}/scripts/worklog"
# 其他助理:以 skill base directory 推導(<base>/../.. 即 plugin 根)
WORKLOG_DIR="<skill base directory>/../../scripts/worklog"
```
之後全文以 `${WORKLOG_DIR}` 表示該目錄,不重複解析。若解析不到或該目錄不存在,回報「plugin 目錄未包含 scripts/worklog,本 skill 在此環境不可用」並停止。
(範例出處:`doc/skills/worklog/SKILL.md` 的「腳本路徑解析(重要)」一節。)
### 寫法二:單行 CLI 宣告版(適合整個 skill 只圍繞單一 CLI 腳本的情況)
```
**CLI**:`node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs"`
(其他助理請改成本 plugin 目錄下的 `scripts/persona.mjs`;以下簡稱 `persona.mjs`)
```
之後全文的指令範例一律以 `node "${CLAUDE_PLUGIN_ROOT}/scripts/persona.mjs" <子指令>` 的形式呈現;其他助理讀到這行時,依核心規則表格自行把 `${CLAUDE_PLUGIN_ROOT}` 換成 base directory 往上兩層推導出的路徑。
(範例出處:`persona/skills/persona-chat/SKILL.md`、`persona/skills/persona-create/SKILL.md` 開頭的 `**CLI**:` 宣告行。)
兩種寫法本質是同一套「Claude Code 用環境變數、其他助理用 base directory 推導」規則的不同表達密度——寫法一把推導過程完整展開並存進一個變數方便全文引用,寫法二把推導過程收進一行宣告、後續直接照抄同一行指令模板。兩者都必須保留「絕不用相對路徑」與「解析不到就回報並停止」這兩條鐵則,不可省略。
+20
View File
@@ -0,0 +1,20 @@
---
name: spec-skill-invocation
description: JSC plugins 共用「skill 呼叫方式」:Claude Code/Antigravity 用 `/jsc-<plugin>:<name>` 斜線指令、Codex 用 `$name`、OpenCode 依 description 自動觸發,各助理呼叫一個 skill 的統一表格與說明。當其他 skill 內文引用 spec-skill-invocation 或 /jsc-shared:spec-skill-invocation、或需要在 skill 檔案末尾放「呼叫方式」章節時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-skill-invocation — 共用 skill 呼叫方式
所有 JSC skill 檔案末尾的「呼叫方式」章節,內容都是同一張表格(各助理如何呼叫一個 skill),不需要每個 skill 各自重複解釋——只要在該 skill 的「呼叫方式」章節引用本規範,或直接沿用下表即可。
## 呼叫方式表格
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc-<plugin>:<name>` 斜線指令 |
| Codex | `$name` |
| OpenCode | 依該 skill 的 description 自動觸發(不需使用者手動輸入指令) |
- `<plugin>` 為該 skill 所屬的 plugin 名稱(`code`/`doc`/`persona`/`shared`),`<name>` 為該 skill 的 `name`(frontmatter 內定義)。
- 各 skill 若帶參數,於自身「呼叫方式」章節列出實際參數格式與範例(可參考 `/jsc-code:image`、`/jsc-code:nuget`、`/jsc-doc:worklog` 等既有寫法),本規範只統一「各助理怎麼呼叫」這一段。
- OpenCode 沒有斜線指令或 `$name` 語法,使用者只需以自然語言描述需求,系統依 skill 的 `description` 自動比對觸發,不需額外指令。
+55
View File
@@ -0,0 +1,55 @@
---
name: spec-subagent
description: JSC plugins 共用「Subagent 派工規範」:一個明確目標派一個 subagent、subagent 只讀不寫(除非該 skill 明確授權寫入並聲明例外)、回傳結構化結果供主 agent 判讀而非直接面向使用者、派工 prompt 需帶入 /jsc-shared:spec-output 規範、不得改動原始碼與不得直接寫外部系統。當其他 skill 內文引用 spec-subagent 或 /jsc-shared:spec-subagent、或需要派 subagent/Agent 工具執行任務時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-subagent — 共用 Subagent 派工規範
所有 JSC skills 需要用 Agent/Task 工具派出 subagent 執行任務時,一律遵守以下規範。
## 一個明確目標派一個 subagent
- 派工前先把任務拆成一組**明確且彼此獨立**的目標(例如:一個 function、一個指令檔、一個議題、一個實作階段),再對**每一個目標各派一個 subagent**。
- **不要**把多個不相關目標塞進同一個 subagent 的 prompt(例如同時要求它「分析議題 A 又順便處理議題 B」「產生文件草稿又順便改標籤」);目標之間有明確依賴或必須共用同一份上下文時才可合併,且合併前要先確認這仍是同一個目標的不同面向,而不是把兩件事湊在一起圖方便。
- 目標數量不固定時(例如依議題清單、依 function 清單動態展開),一律逐一展開後才派工,不得為了省事而幾個目標共用一個 subagent。
## subagent 只讀不寫
- subagent 預設**只讀**:只讀取程式碼、設定檔、議題、留言等既有內容,**不落地任何檔案、不修改任何工作目錄的原始碼**。
- 只有在**該 skill 明確授權**的情況下,subagent 才可以寫入,且必須符合下列兩種之一,不得無宣告地擴大寫入範圍:
1. **寫草稿檔**:僅限寫入該 skill 指定的草稿目錄(例如 `.docs/` 底下),不可覆蓋或修改原始碼、原始指令檔。
2. **回傳結構化結果**:不落地任何檔案,把結果整理成結構化內容回傳給主 agent(見下一節)。
- 派工的 skill 本身必須在文件中**明確聲明**subagent 被授權寫入的範圍(寫哪個目錄、哪些檔案),沒有聲明就一律視為只讀。
## 回傳結構化結果,不是直接面向使用者的訊息
- subagent 的回傳內容是給**主 agent 判讀**的結構化資料(例如:判斷結果、建議異動、草稿內容、需人工確認項目清單),**不是**直接顯示給使用者的最終訊息。
- 面向使用者的輸出(回報、確認詢問、劇場模式對白等)一律由**主 agent 統一組織**:主 agent 收齊所有 subagent 的回傳後,才彙整、檢查、排序,最後產出面向使用者的內容。
- subagent 不得自行決定要不要通知使用者、也不得自行對使用者發言(除非該 skill 明確定義 subagent 本身就是對話的一方,例如人格扮演情境下的發言)。
## 派工 prompt 需帶入輸出規範
- 派工的 prompt 內必須帶入 `/jsc-shared:spec-output` 的規範內容(或明確引用它),讓 subagent 知道回傳內容的語言/編碼/呈現慣例(繁體中文為主英文為輔、UTF-8 不含 BOM、優先用表格與 Mermaid 呈現、個資(PII)去識別化)。
- 不得省略這一步:subagent 若沒被告知輸出規範,容易產生語言混雜、編碼錯誤或洩漏個資的回傳內容,主 agent 事後才發現就必須整批重派。
## subagent 不得改動原始碼
- subagent **不得修改任何工作目錄內的原始碼、設定檔或既有文件**,即使它判斷該怎麼改也只能在回傳內容中描述建議,交由主 agent(或使用者確認後由主 agent)實際落地。
- 唯一例外是該 skill **明確授權**subagent 直接改動原始碼(例如某個 skill 的設計就是「派 subagent 逐項實作」),此時該 skill 文件必須清楚聲明這項例外與授權範圍,不得含糊帶過。
## subagent 不得直接寫外部系統
- subagent **不得**直接呼叫外部系統的寫入操作,包括但不限於:Gitea(議題正文/留言/標籤/PR/看板欄位)、Wiki、以及其他任何會對外產生不易復原變更的 API。
- 這類寫入一律收斂到**主 agent 統一執行**,且必須先經**使用者確認**(例如透過 AskUserQuestion)才能執行;subagent 只負責讀取與分析,把「建議要寫入什麼」整理進結構化回傳內容即可。
- subagent 需要讀取外部系統(例如讀議題、讀 Gitea 標籤清單)供分析用時不受此限,此節只限制**寫入**。
## 快速檢查表
| 檢查項目 | 通過條件 |
| --- | --- |
| 目標拆分 | 每個 subagent 對應一個明確、獨立的目標 |
| 讀寫範圍 | 預設只讀;若可寫,該 skill 已明確聲明授權範圍 |
| 回傳格式 | 結構化內容,供主 agent 判讀,非面向使用者的最終訊息 |
| 輸出規範 | prompt 已帶入或引用 `/jsc-shared:spec-output` |
| 原始碼 | 未改動,或該 skill 已明確授權例外 |
| 外部系統 | 未直接寫入 Gitea/Wiki 等,寫入交由主 agent 且已經使用者確認 |
+4
View File
@@ -5,6 +5,10 @@ description: JSC plugins 共用「時間戳與輸出訊息格式規範」:更
# spec-time-log — 共用時間戳與訊息格式規範
## 參考實作
- `shared/scripts/lib/{log.sh,log.py,log.mjs}` 是本規範在 bash/Python/Node(mjs)三種語言下的**唯一參考實作來源**(純參考、不會被其他 repo 跨 repo 呼叫)。其他 repo 的腳本要嘛直接複製其邏輯,要嘛在檔頭註明自己與這份參考實作的對應關係,禁止各自發明第三種時間戳或 log 格式。
## 更新時間
- 一律使用**台灣時區(Asia/Taipei)**並固定為 `yyyy/MM/dd HH:mm:ss`,例如 `2026/06/30 18:30:05`。取得方式:
+55
View File
@@ -0,0 +1,55 @@
---
name: spec-todo-list
description: JSC plugins 共用「TODO list 規範」:一律用 Markdown checklist(`- [ ]`)格式、每項要具體到可執行可驗收、不得憑空編造需求外的項目、依影響範圍由小到大排序、每項要能舉證對應到 `path:line` 或議題描述的哪一句、完成一項就勾選並留言回報進度。當其他 skill 內文引用 spec-todo-list 或 /jsc-shared:spec-todo-list、或需要產生/追蹤議題(或文件)TODO list 時載入此 skill。單獨被使用者呼叫時,直接說明本規範內容。
---
# spec-todo-list — 共用 TODO list 規範
所有 JSC skills 在議題描述、保存議題、`TARGET.md` 等場合產生或追蹤 TODO 清單時,一律遵守以下規範。
## 格式:Markdown checklist
- 一律使用 Markdown 任務清單語法:未完成 `- [ ]`,已完成 `- [x]`;不得改用其他符號(`*`、純文字條列、表情符號打勾等)。
- 沒有既有 `## TODO` 區塊時,於描述或文件最後新增 `## TODO` 標題再接清單;已有該區塊時在其中追加,不新開第二個 `## TODO` 區塊。
- 子項目以縮排表示上層項目的子步驟,隨上層一起追蹤;子項目全部完成才可把上層一併勾選。
- 標題、說明文字等非清單內容原樣保留,只當作項目的背景脈絡,不得因為新增/調整 TODO 而改寫。
## 項目內容:可執行、可驗收
- 每一項 TODO 都必須具體到**看到這一句就知道要做什麼、做完後能明確判斷是否達成**;不得使用「優化一下」「檢查看看」「處理相關問題」這類模糊、無驗收標準的措辭。
- 動詞+對象+(必要時)驗收條件三者盡量齊備,例如「把 `UserService.Login` 的密碼驗證改為使用雪湯 hash 比對,單元測試涵蓋密碼錯誤與帳號鎖定兩種情境」,而非「改善登入安全性」。
- 一項 TODO 只對應一件可獨立完成、可獨立驗收的工作;範圍過大時拆成多項,不得把整個議題塞成一項。
## 禁止憑空編造
- TODO 只能從需求來源(議題描述、留言、來源文件、使用者明確補充)推導;不得加入來源未提及、也無法從來源合理推得的項目。
- 推導有疑慮或來源本身模糊時,標註「需人工確認」,不得用臆測或「合理推測」補上內容並當作既定需求。
- 盤點既有 TODO 時,已勾選(`- [x]`)項目視為已完成,不得重新編造或重做;只在確有缺漏時才補上新項目,並標明「新增」以便使用者辨識。
## 排序:依影響範圍由小到大
清單依每項 TODO 的**影響範圍**(預計修改的檔案/模組數與波及面)由小到大排序;範圍相同時,前置依賴項排在前面。可參考下列分級(節錄自 `code/skills/issues/SKILL.md`):
| 影響範圍 | 定義(參考) |
| --- | --- |
| XS | 單一檔案內的局部修改(文案、設定值、小修正) |
| S | 單一檔案或單一函式的邏輯調整 |
| M | 同一模組內跨多檔案的修改 |
| L | 跨模組修改或介面/契約變更 |
| XL | 跨專案、資料結構或流程性的大改動 |
排序目的是讓小範圍、低風險的項目先完成,逐步逼近影響面較大的項目;不得因為「比較想先做」而打亂由小到大的順序。
## 舉證:對應 `path:line` 或議題描述語句
- 每一項 TODO 都必須能舉證它從何而來,二擇一(或並列):
- 對應到需求來源(議題描述、留言、來源文件)中的**哪一句**——引用或指出該句內容;
- 對應到程式碼中的**哪個位置**——以 `path:line` 標明(例如 `src/services/UserService.cs:42`)。
- 判斷 TODO 是否已完成時,同樣要以 `path:line` 指出對應的實作位置作為依據;無法從檔案或來源可靠判斷者,維持未完成並標註「需人工確認」,不得憑印象判定完成。
- 舉證資訊留在 TODO 項目本身、留言或回傳內容中,方便日後追溯每一項 TODO 的來源與完成依據。
## 完成回報:勾選並留言
- 完成一項 TODO,就地把該行改成 `- [x]`(子項目全部完成才勾選上層),不得留待多項一起補勾。
- 每完成一項,都要把進度回報成留言(或依所在流程指定的回報位置),內容至少包含:完成了哪一項、對應的 `path:line` 或需求語句依據;多項同時完成時可整理成一則留言,但每一項都要能個別對應到依據。
- 無法安全完成的項目保持未勾選,並標註原因(例如「需人工確認」)供後續處理,不得略而不報。
+171
View File
@@ -0,0 +1,171 @@
---
name: todo
description: 把「需求 → 分析 → 產生 todo.md → 交給指定模型實作」整條路徑固定成可重複執行的流程:先依 `/jsc-shared:spec-model` 的「需求分析」任務挑出分析模型,當前模型不符就停止並要求切換、不得先分析再說;接著讀取來源(需求描述、本機檔案,或走 `/jsc-shared:spec-issue-read` 讀取的 Gitea 議題)並釐清需求,任何不清楚之處一律依 `/jsc-shared:spec-ask-user` 詢問使用者、絕不臆測或編造;再依「依清單實作」任務挑出實作模型(使用者可用 `--impl-model` 指定,會在 `model_reason` 註明是使用者指定);最後產生(或視既有檔案 frontmatter 的 `model` 是否相同,決定附加或詢問是否覆蓋)一份帶 `model`/`model_alias`/`model_reason`/`analyzed_by`/`analyzed_at`/`scope` frontmatter 與「給執行本清單 Agent 的強制規則」區塊的 `todo.md`,供使用者另開一個以該模型執行的 session 落地實作。當使用者說要把需求整理成待辦清單交給指定模型做、產生或更新 `todo.md`、幫需求選一個實作模型並寫成清單、要一份鎖定模型的執行清單,或提到 todo skill、需求轉 todo.md、指定模型 todo 時觸發。不適用於:把需求拆分成多個 Gitea 議題(用 `/jsc-doc:issues-analyze`)、實作既有 Gitea 議題的 TODO(用 `/jsc-code:issues`)。
argument-hint: "[--source <需求描述|檔案路徑|議題編號>] [--file <path>] [--impl-model <id|alias>] [--append|--overwrite] [--yes]"
---
# todo — 需求分析並產生指定模型執行的 todo.md
把「需求 → 分析 → 產生 `todo.md` → 交給指定模型實作」固定成六個階段,全程強制模型一致性:分析本身要用夠強的模型做,產出的清單要鎖定一個實作模型,執行清單的 agent 開工前必須自我核對模型,不符就停。
| 階段 | 做什麼 | 產出 |
| --- | --- | --- |
| 1. 選分析模型 | 依 `spec-model`「需求分析」任務取得推薦模型,比對當前模型 | 相符才繼續,不符則停止 |
| 2. 分析需求 | 讀取來源、釐清不清楚之處 | 需求彙整(目標/驗收條件/限制/`scope`) |
| 3. 選實作模型 | 依 `spec-model`「依清單實作」任務取得推薦模型,或採用使用者指定 | 實作模型 id/alias/理由 |
| 4. 產生 `todo.md` | 依固定格式寫出 frontmatter+強制規則區塊+checklist | 草稿內容(尚未寫檔) |
| 5. 檔案已存在的處理 | 依既有檔案 frontmatter 決定建立/附加/詢問覆蓋 | 實際寫入的檔案 |
| 6. 交付 | 輸出摘要表格,提醒以哪個模型開新 session 執行 | 面向使用者的總結 |
---
## 共用規範(必要前置)
先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝,
依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。
本 skill 需要的規範:`spec-model`、`spec-output`、`spec-execution`、`spec-issue-read`、`spec-todo-list`、`spec-ask-user`、`spec-time-log`、`spec-gitea`、`spec-skill-invocation`
本 skill 特有補充:
- 本 skill 產生的 `todo.md` 就是 `spec-model` 第六節所述「帶 `model:` frontmatter 的清單檔」的**源頭**;本 skill 自己只負責產生這份清單,不執行清單內容。
- 全程不落地草稿檔以外的臨時檔案;`--source` 若指向 Gitea 議題附件需要暫存讀取時,比照 `spec-issue-read` 的唯讀暫存例外,讀完立即刪除。
- 階段 1 的模型檢查針對「執行本 skill 分析工作的 agent 自己」;階段 3 選出的實作模型是**寫進檔案給未來另一個 session 用的**,兩者是不同的模型、不同的檢查時機,不要混淆。
---
## 參數
`[--source <需求描述|檔案路徑|議題編號>] [--file <path>] [--impl-model <id|alias>] [--append|--overwrite] [--yes]`
| 參數 | 說明 |
| --- | --- |
| `--source` | 需求來源。未帶時視為「目標不明」的必要決策(依 `spec-execution`),詢問使用者要用描述/檔案路徑/議題編號哪一種,不得臆測。 |
| `--file` | 產出路徑,**預設 `./todo.md`**(相對於執行本 skill 當下的工作目錄)。 |
| `--impl-model` | 直接指定實作模型(id 或 alias),跳過階段 3 的推薦流程;仍會在 `model_reason` 註明「使用者指定」。 |
| `--append` / `--overwrite` | 針對階段 5「檔案存在且 `model` 不同」情境**提前作答**,用於跳過該次 `AskUserQuestion`;不影響「`model` 相同」時固定附加的規則(見階段 5)。二擇一,同時提供視為衝突,仍需詢問使用者。 |
| `--yes` | 依 `spec-execution` 略過一般性確認(例如「要不要現在就寫檔」);**不得**用來略過階段 5 對「模型不同是否覆蓋」的詢問——那是破壞性決策,依 `spec-ask-user` 一律必須真的問到答案,除非已用 `--append`/`--overwrite` 明確作答。 |
---
## 階段 1:選分析模型
1. 依 `/jsc-shared:spec-model` 第三節取得可用模型清單與標籤(`claude-api` skill → CLI 自陳 → 使用痕跡 → smoke test,前一項取得就不必往下)。
2. 依第二節「需求分析/拆 TODO/架構決策」任務列比對必要標籤 `#深度推理` `#分析` `#本機可用`,選出推薦模型(加分標籤 `#長上下文`)。
3. 確認**當前執行本 skill 的模型 id**:agent 依自身系統提示所述的 exact model id 自我回報;無法確定時請使用者以 `/status` 確認,不要用猜的。
4. 當前模型 ≠ 推薦模型 → 依 `spec-model` 第五節格式**停止並要求使用者切換**,不得先做階段 2 的任何分析:
```
[yyyy/MM/dd HH:mm:ss][模型檢查][ERR]: 本次需求分析建議使用 <推薦 model>(<alias>),當前模型為 <current-model-id>。
請執行 /model <alias> 切換後重新呼叫本 skill,本次不進行任何分析。
```
5. 相符 → 記錄此模型 id(含變體標記,如 `[1m]`,依自我回報原樣記錄)供階段 4 的 `analyzed_by` 使用,繼續階段 2。
## 階段 2:分析需求
1. **判斷來源型態**(`--source` 未帶則先依〔參數〕表詢問使用者):
- 對應到本機可讀取的檔案路徑 → 視為**檔案**,讀取全文。
- 純數字、`#123`、或 Gitea 議題 URL → 視為**議題編號**,走 `/jsc-shared:spec-issue-read`(含描述、所有留言、所有附件;分頁規則依 `spec-gitea`);議題所在 repo 無法從目前工作目錄的 git remote 推斷時,詢問使用者 `owner/repo`,不得臆測。
- 都不是 → 視為**需求描述**本文,直接採用 `--source` 的文字內容。
2. **釐清需求**:依 `/jsc-shared:spec-execution`「不臆測/需人工確認」——目標、驗收條件、限制條件、影響範圍任一模糊或缺漏,一律依 `/jsc-shared:spec-ask-user` 詢問使用者(單選/多選判斷時機、選項上限 4、必含「其他」),**不得自行補完或用常見做法代填**。
3. 產出**需求彙整**:目標、驗收條件、限制條件,以及本次 `todo.md` 的 `scope`(受影響的目錄/repo/模組範圍,供階段 4 frontmatter 使用)。
4. 若來源本身有既有 Markdown checklist(例如議題描述或既有文件),依 `/jsc-shared:spec-todo-list`「盤點既有 TODO」處理:已勾選視為完成不重做,缺漏才補新項目並標「新增」。
## 階段 3:選實作模型
1. **使用者指定**(`--impl-model` 有帶):以使用者指定為準。依 `spec-model` 第三節盡可能驗證其存在性與可用性(CLI 自陳比對,可行時 smoke test);無法驗證也不得拒絕使用者的選擇,改在 `model_reason` 註明「使用者以 `--impl-model` 指定,未能於本機驗證可用性」。`model_reason` 必須明確寫出**這是使用者指定,非本 skill 推薦結果**。
2. **未指定**:依 `/jsc-shared:spec-model` 第二節「依清單實作/規格落地」任務列比對必要標籤 `#均衡實作` `#實作` `#本機可用`(加分標籤 `#中成本`),選出推薦模型;`model_reason` 寫成一段自然語言理由,**結合階段 2 的需求彙整內容**判斷(例如任務量體、是否需要跨檔案架構判斷、粒度是否已拆解到可直接動手),而不是照抄標籤名稱。
3. 記下最終的實作模型 `id`、`alias`(若有)、`model_reason`,供階段 4 使用。
## 階段 4:產生 todo.md
格式**完全比照** `/home/coder/plugins/todo.md` 現有樣式,逐項對齊:
### frontmatter
```yaml
---
model: <實作模型 id>
model_alias: <實作模型 alias,若無 alias 則省略此欄>
model_reason: <階段 3 產出的理由,含「使用者指定」註記時比照>
analyzed_by: <階段 1 確認的分析模型 id,原樣含變體標記>
analyzed_at: <yyyy/MM/dd HH:mm:ss,依 spec-time-log,Asia/Taipei>
scope: <階段 2 產出的 scope>
---
```
### 「給執行本清單 Agent 的強制規則」區塊
固定內容,只代換 `<model>`/`<alias>` 與時間戳,四條規則與錯誤訊息格式**逐字比照**、不可簡化或改寫措辭:
````markdown
## 0. 給執行本清單 Agent 的強制規則(先讀完再動手)
| 規則 | 內容 |
| --- | --- |
| **模型鎖定** | 本檔 frontmatter 的 `model` 是**強制**的,不是建議。開工前先自我確認當前模型 id。 |
| **不符就停** | 當前模型 ≠ `<model>` 時,**立刻停止、不做任何檔案修改**,輸出下方錯誤訊息並要求使用者切換。 |
| **不得自行升降級** | 不可以「先用手上的模型做一點」、不可以自行判定「我這顆更強所以沒關係」。降級與升級同樣禁止。 |
| **附加不覆蓋** | 若之後要往本檔追加新需求:`model` 相同 → 附加到檔尾;`model` 不同 → 先問使用者是否覆蓋,未得同意不得寫入。 |
```
[<yyyy/MM/dd HH:mm:ss>][模型檢查][ERR]: 本清單指定 <model>(<alias>),當前模型為 <current-model-id>。
請執行 /model <alias> 切換後重新載入本清單,本次不進行任何修改。
```
> 當前模型 id 的取得方式:Claude Code 沒有提供模型 id 的環境變數,agent 依自身系統提示所述的 exact model ID 自我回報即可;無法確定時請使用者以 `/status` 確認,**不要用猜的**。
````
### checklist(依 `/jsc-shared:spec-todo-list` 排序與具體化)
- 逐項 `- [ ] **編號 短標題**:具體內容`,內容需動詞+對象+驗收條件齊備,能舉證對應 `path:line` 或需求彙整中的哪一句。
- 依影響範圍由小到大排序(XS/S/M/L/XL,見 `spec-todo-list`),範圍相同時前置依賴排前面。
- **編號規則**:清單有自然的分組(例如多個獨立工作方向)時,用「群組代號+序號」(如 `A0-1`、`B1-2`,仿本檔範例);沒有分組時用整數流水號(`1`、`2`、`3`…)。日後附加新項目時,編號延續既有規則、不得重複或跳號造成混淆。
- 清單分成多個彼此有先後依賴的群組時,仿照本檔範例在 checklist 前加一段「### 執行順序」註明群組間的強制先後與理由;若清單本身沒有這種分階段結構,省略此小節即可,不必為了套版而硬分組。
- 不得把任何需求彙整未提及、也無法合理推得的項目塞進清單;有疑慮的項目標「需人工確認」。
## 階段 5:檔案已存在的處理(需求核心,不可簡化)
寫檔前先讀 `--file`(預設 `./todo.md`)目前是否存在、若存在再解析其 frontmatter:
| 狀況 | 動作 |
| --- | --- |
| 檔案不存在 | 直接建立,寫入階段 4 產出的完整內容。 |
| 存在 且 frontmatter `model` **相同** | **附加到檔案最後**:加一條 `---` 分隔與 `## 追加(<yyyy/MM/dd HH:mm:ss>)` 標題,接新的 checklist 項目(依既有編號規則延續)。frontmatter 只更新 `analyzed_at`,**不動 `model`/`model_alias`/`model_reason`**。 |
| 存在 且 frontmatter `model` **不同** | **停下來用 `AskUserQuestion` 詢問使用者是否覆蓋**,見下方選項。**未得同意不得寫入任何內容。** |
| 存在 但**沒有** frontmatter | 視為「不同模型」處理,走上一列同樣的詢問流程。 |
- **詢問選項**(依 `/jsc-shared:spec-ask-user`,4 個以內、含「其他」):`覆蓋(改用新模型)`/`保留舊檔改用舊模型附加`/`取消`/`其他`。
- 選「覆蓋」→ 以階段 3~4 產出的新內容整份覆寫,`model`/`model_alias`/`model_reason` 全部換新。
- 選「保留舊檔改用舊模型附加」→ **放棄階段 3 選出的新實作模型**,改沿用舊檔 frontmatter 的 `model`;階段 4 產生的 checklist 內容改為附加在舊檔最後(同「model 相同」列的附加格式)。
- 選「取消」→ 不寫入任何內容,回報「已取消,`<path>` 未變更」。
- 選「其他」→ 依使用者實際輸入處理,仍不得在未取得明確同意前寫入。
- `--append`/`--overwrite` 已明確回答這個分支時(見〔參數〕表),依 `spec-ask-user`「已從其他管道得知答案時跳過詢問」直接照該旗標執行,不再彈出 `AskUserQuestion`;`--yes` 單獨出現**不算**已回答,仍要問。
## 階段 6:交付
輸出摘要表格:
| 項目 | 內容 |
| --- | --- |
| 分析模型 | 階段 1 確認的模型(`analyzed_by`) |
| 實作模型 | 階段 3 選出的模型(`model` / `model_alias`),並註明是推薦還是使用者指定 |
| 項目數 | 本次新增的 checklist 項目數 |
| 檔案路徑 | 實際寫入的絕對路徑 |
| 本次動作 | 新建/附加/覆蓋 三者之一 |
並在最後明確提醒使用者:
> 請以 `<alias>`(找不到 alias 時用完整 id)模型開新 session 執行本清單:`<path>`。
---
## 呼叫方式
依 `/jsc-shared:spec-skill-invocation` 的統一呼叫方式,本 skill 的實際參數格式與範例:
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc-shared:todo --source "把 X 模組改成非同步"`、`/jsc-shared:todo --source ./RFC.md --file ./plans/x.todo.md`、`/jsc-shared:todo --source 123 --impl-model sonnet`、`/jsc-shared:todo --source ./RFC.md --append` |
| Codex | `$todo --source "..."`,或用 `/skills` 選單 |
| OpenCode | 描述需求(如「幫我把這段需求分析清楚,寫成一份鎖定模型的 todo.md」)自動觸發 |