From 85a4128f5ec3749d8d7e0d8553001842dc3fa435 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 11 Aug 2026 06:04:18 +0000 Subject: [PATCH] =?UTF-8?q?refactor(code):=20=E6=8E=A5=E4=B8=8A=20shared?= =?UTF-8?q?=20=E5=85=B1=E7=94=A8=E8=A6=8F=E7=AF=84=EF=BC=8C=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E9=87=8D=E6=8A=84=E6=AE=B5=E8=90=BD=E4=B8=A6=E8=A3=9C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=AA=A2=E6=9F=A5=E4=B8=B2=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 依 todo.md 執行的規範治理專案:action-composite/action-docker/action-node/ image/issues/nuget/review-resolve/sync/target 九個 skill 改為引用 shared 新增的共用 spec(conventional-commit、pull-request、git-push、 git-safety 分支選擇、issue-read、todo-list、ask-user、action-scaffold、 node-src-layout、skill-invocation),移除大量重抄內容(review-resolve/ target 光是 commit/PR/push 三塊就精簡約 175 行);target/issues 補上讀到 帶 model: frontmatter 清單檔時依 /jsc-shared:spec-model 做模型檢查的規則。 Co-Authored-By: Claude Sonnet 5 --- .claude-plugin/plugin.json | 14 ++- .codex-plugin/plugin.json | 2 +- README.md | 120 ++----------------- plugin.json | 4 +- plugin.meta.json | 28 +++++ skills/action-composite/SKILL.md | 39 ++---- skills/action-docker/SKILL.md | 124 ++++--------------- skills/action-node/SKILL.md | 52 +++----- skills/image/SKILL.md | 69 ++--------- skills/issues/SKILL.md | 65 ++++------ skills/nuget/SKILL.md | 17 +-- skills/review-resolve/SKILL.md | 196 +++++-------------------------- skills/sync/SKILL.md | 47 +++----- skills/target/SKILL.md | 109 +++++------------ 14 files changed, 215 insertions(+), 671 deletions(-) create mode 100644 plugin.meta.json diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 3573b74..d32ff25 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,12 +1,20 @@ { "name": "jsc-code", - "version": "0.0.3", - "description": "JSC 程式碼工作流 plugin(Claude Code / Codex / Antigravity / OpenCode),提供 Gitea AI review findings 修復/PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步,以及 action / Dockerfile 標準化流程;於 Claude Code 以 /jsc-code: 前綴呼叫。", + "version": "0.1.0", + "description": "JSC 程式碼工作流 plugin(Claude Code / Codex / Antigravity / OpenCode / GitHub Copilot CLI),提供 Gitea AI review findings 修復/PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步,以及 action / Dockerfile 標準化流程;於 Claude Code 以 /jsc-code: 前綴呼叫。", "skills": "./skills", "author": { "name": "JSC" }, "homepage": "https://gitea.jsc.idv.tw/plugins/code", "repository": "https://gitea.jsc.idv.tw/plugins/code.git", - "keywords": ["code-review", "git-diff", "gitea", "nuget", "docker", "skills", "jsc"] + "keywords": [ + "code-review", + "git-diff", + "gitea", + "nuget", + "docker", + "skills", + "jsc" + ] } diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index f765aba..66b894f 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-code", - "version": "0.0.3", + "version": "0.1.0", "description": "JSC 程式碼工作流 plugin,提供 Gitea AI review findings 修復/PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步,以及 action / Dockerfile 標準化流程。", "skills": "./skills" } diff --git a/README.md b/README.md index b1aeb0b..3540fd7 100644 --- a/README.md +++ b/README.md @@ -54,117 +54,16 @@ code/ ## 安裝 / 更新 / 移除(各助理) -> 指令中的 repo 網址換成你的:`https://gitea.jsc.idv.tw/plugins/code.git` +> 完整的安裝/更新/移除指令(Claude Code、Codex、Antigravity、OpenCode、GitHub Copilot CLI 五種助理),一律以 [`/jsc-shared:spec-plugin-cli`](https://gitea.jsc.idv.tw/plugins/shared/src/branch/master/skills/spec-plugin-cli/SKILL.md) 為唯一權威版本,套用時代入下列佔位符: > -> **Claude / Codex 從 git URL 安裝(會 clone 遠端),請先把本 repo `push` 到 gitea。** -> **Antigravity 的 `agy plugin install ` 目前只支援 github.com**;gitea 請改用「clone + 本地路徑」(見 Antigravity 節)。 -> 本機/離線:Claude 可用本地路徑加 marketplace;Antigravity 用本地路徑安裝。 - -### Claude Code - -```bash -# 安裝 -claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/code.git -claude plugin install jsc-code@code - -# 更新 -claude plugin marketplace update code -claude plugin update jsc-code@code - -# 移除 -claude plugin uninstall jsc-code@code -claude plugin marketplace remove code -``` - -- 工作階段內 slash 版(等價):把 `claude plugin` 換成 `/plugin`。 -- 本機開發(免 push):`claude plugin marketplace add C:\Users\h3285\source\repos.plugins\code`(本地路徑)後再 install。 -- **呼叫**:`/jsc-code:`(例 `/jsc-code:issues`)。 - -### Codex - -```bash -# 安裝 -codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/code.git -codex plugin add jsc-code@code - -# 更新(重新抓取 marketplace 的 git 快照) -codex plugin marketplace upgrade code - -# 移除 -codex plugin remove jsc-code@code -codex plugin marketplace remove code -``` - -- 安裝 token `jsc-code@code` = plugin 名(`.codex-plugin/plugin.json` 的 `name`)@ marketplace 名(`.agents/plugins/marketplace.json` 的 `name`)。 -- 本 repo 的 Codex marketplace 以 `url` 來源指向自己,故 Codex **一律從 gitea 安裝**(需先 push);安裝後重啟 Codex。 -- **呼叫**:`$`(例 `$issues`),或用 `/skills` 選單。 - -### Antigravity(`agy`) - -> `agy plugin install ` 目前**只支援 github.com**;gitea 等自架 git 不支援 URL 安裝,請先 `git clone` 再用**本地路徑**安裝。 - -```bash -# 安裝:clone 後用本地路徑 -git clone https://gitea.jsc.idv.tw/plugins/code.git ~/plugins/code -agy plugin install ~/plugins/code - -# 更新(agy 無 update 子指令 → git pull 後重裝) -git -C ~/plugins/code pull -agy plugin uninstall jsc-code -agy plugin install ~/plugins/code - -# 移除 -agy plugin uninstall jsc-code -``` - -- 若把 skills 放到 GitHub,則可直接 `agy plugin install https://github.com//`。 -- 其他:`agy plugin list`、`agy plugin enable jsc-code` / `disable jsc-code`、`agy plugin validate `。安裝後重啟工作階段。 -- **呼叫**:`/jsc-code:`(例 `/jsc-code:issues`)或依描述自動觸發。 - -### OpenCode - -OpenCode 的「plugin」是 TypeScript/npm 套件,不適用於 skill 包;skills 改用**目錄安裝**。 -OpenCode 會讀 `~/.config/opencode/skills/`(也會讀 `~/.claude/skills/`、`~/.agents/skills/`)。 - -```bash -# 安裝 -git clone https://gitea.jsc.idv.tw/plugins/code.git ~/plugins/code -mkdir -p ~/.config/opencode/skills -cp -r ~/plugins/code/skills/* ~/.config/opencode/skills/ - -# 更新 -git -C ~/plugins/code pull -cp -r ~/plugins/code/skills/* ~/.config/opencode/skills/ - -# 移除 -rm -rf ~/.config/opencode/skills/{review-resolve,issues,nuget,sync,action-docker,action-composite,action-node,image,target} -``` - -> **Windows PowerShell**:`cp -r A B` → `Copy-Item A B -Recurse -Force`、`rm -rf X` → `Remove-Item X -Recurse -Force`、`~` → `$HOME`。 - -- **呼叫**:直接描述需求,模型會依 skill 描述自動透過 skill 工具呼叫。 - -### GitHub Copilot CLI - -Copilot CLI 支援與 Claude Code 類似的原生 plugin / marketplace 指令,可直接從 marketplace 安裝、更新與移除本 plugin。 - -```bash -# 安裝 -copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/code.git -copilot plugin install jsc-code@code - -# 更新 -copilot plugin marketplace update code -copilot plugin update jsc-code@code - -# 移除 -copilot plugin uninstall jsc-code@code -copilot plugin marketplace remove code -``` - -- 安裝 token `jsc-code@code` = plugin 名(plugin manifest 的 `name`)@ marketplace 名。 -- `copilot plugin marketplace add` 支援 GitHub `owner/repo`、git URL 與本地路徑;Gitea repo 可用上方 HTTPS URL。 -- **呼叫**:在 Copilot CLI 中用自然語言描述需求,例如 `copilot -i "請使用 issues 處理 plugins/code 的 12、15 號議題"`。 +> | 佔位符 | 值 | +> | --- | --- | +> | `` | `gitea.jsc.idv.tw` | +> | `` | `code` | +> | `` | `jsc-code` | +> | `` | `code` | +> | ``(= `@`) | `jsc-code@code` | +> | `` | `https://gitea.jsc.idv.tw/plugins/code.git` | --- @@ -303,3 +202,4 @@ copilot plugin marketplace remove code - Antigravity:`git -C ~/plugins/code pull && agy plugin uninstall jsc-code && agy plugin install ~/plugins/code` - OpenCode:`git pull` 後重新複製 `skills/` - Copilot:`copilot plugin marketplace update code && copilot plugin update jsc-code@code` + diff --git a/plugin.json b/plugin.json index e9756aa..c874d03 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-code", - "version": "0.0.3", + "version": "0.1.0", "description": "JSC 程式碼工作流 plugin,提供 Gitea AI review findings 修復/PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步,以及 action / Dockerfile 標準化流程;於 Antigravity 以 /jsc-code: 前綴呼叫。", - "skills": "./skills/" + "skills": "./skills" } diff --git a/plugin.meta.json b/plugin.meta.json new file mode 100644 index 0000000..b0a4623 --- /dev/null +++ b/plugin.meta.json @@ -0,0 +1,28 @@ +{ + "name": "jsc-code", + "shortName": "code", + "version": "0.1.0", + "descriptionCore": "JSC 程式碼工作流 plugin,提供 Gitea AI review findings 修復/PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步,以及 action / Dockerfile 標準化流程。", + "assistants": ["Claude Code", "Codex", "Antigravity", "OpenCode", "GitHub Copilot CLI"], + "cliPrefix": "/jsc-code:", + "callPrefixAssistants": { + "root": "Antigravity", + "claudePlugin": "Claude Code" + }, + "codexNote": "", + "skillsPath": "./skills", + "author": { "name": "JSC" }, + "homepage": "https://gitea.jsc.idv.tw/plugins/code", + "repository": "https://gitea.jsc.idv.tw/plugins/code.git", + "keywords": ["code-review", "git-diff", "gitea", "nuget", "docker", "skills", "jsc"], + "marketplace": { + "repoDescription": "JSC 程式碼工作流 skills 的 Claude Code marketplace,涵蓋 Gitea findings 修復/PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步與 action / Dockerfile 標準化。", + "pluginSummary": "JSC 程式碼工作流 skills:Gitea AI review findings 修復與 PR 流程、Gitea 議題 TODO 彙整與逐項實作、C# / .NET NuGet 套件更新、Gitea 專案批次同步、action / Dockerfile 標準化。" + }, + "_driftDecisions": [ + "決議1:skills 欄位統一為不帶尾斜線的 './skills'。現況:root plugin.json 為 './skills/'(帶尾斜線),.claude-plugin 與 .codex-plugin 已是 './skills';skillsPath 已定為統一值,root 需修正。", + "決議2:root plugin.json 已有「於 Antigravity 以 /jsc-code: 前綴呼叫」句尾,符合決議,不需調整。", + "決議3:.claude-plugin/plugin.json 助理括號清單現況為「(Claude Code / Codex / Antigravity / OpenCode)」——注意這不只是順序或命名問題,而是**完全遺漏「GitHub Copilot」這一項**(只有 4 項,doc 是 5 項且順序相同只差命名)。已依統一決議補齊為「(Claude Code / Codex / Antigravity / OpenCode / GitHub Copilot CLI)」五項。", + "決議4對應:.codex-plugin/plugin.json 無專屬補充資訊,codexNote 為空字串。" + ] +} diff --git a/skills/action-composite/SKILL.md b/skills/action-composite/SKILL.md index 0947c93..c319a14 100644 --- a/skills/action-composite/SKILL.md +++ b/skills/action-composite/SKILL.md @@ -17,16 +17,11 @@ argument-hint: "[--action-dir ] [--manifest ] [--manifest ] [--manifest ] [--manifest .js`)→ 轉為一個 `run` step,於 step 內以 `node .js` 執行(`shell: bash`)。composite step **不會自動注入 `INPUT_*` 環境變數、也不會自動轉接 outputs**,必須手動補齊兩段映射(見下方範例): @@ -142,7 +131,7 @@ runs: 在 `runs.steps` **最前面**插入(或更新)一個輸出橫幅的 step,讓 composite action 一啟動就**輸出 action 名稱、用途、更新時間**;並於 `action.yml` 開頭補用途/更新時間註解區塊。 -- **更新時間**依 `/jsc-shared:spec-time-log`(Asia/Taipei、固定 `yyyy/MM/dd HH:mm:ss`、寫成檔內固定字串)。本階段先寫入暫定時間戳,**階段 D 完成後會統一同步各處時間戳**(見階段 D)。 +- **更新時間**依 `/jsc-shared:spec-time-log` 處理,本階段先寫入暫定時間戳,**階段 D 完成後會統一同步各處時間戳**(見階段 D)。 - 名稱/用途取自階段 A2 的 `action.yml`(缺漏時以「(未提供)」標示)。 - 橫幅 step 須有可辨識的 `name`(如 `顯示 action 資訊`)與 `shell: bash`;其後緊接既有/對齊後的 steps,**不更動既有 steps**。 - 若已存在本 skill 先前插入的橫幅 step(依 `name` 辨識),則**更新**其內容與更新時間,不重複插入。 @@ -178,7 +167,7 @@ runs: ## 階段 D:完整執行 /jsc-doc:funcs 處理流程 -標準化完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**完整執行 `/jsc-doc:funcs` 流程(前置可用性檢查、完整流程、由使用者裁示實作方式、重建 README)。完成後依該 spec **統一時間戳**:回頭同步橫幅 step 內文、`action.yml` 開頭註解區塊與 README 的更新時間,確保各處一致。 +標準化完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**執行;完成後統一同步橫幅 step 內文、`action.yml` 開頭註解區塊與 README 的更新時間,確保各處一致。 --- @@ -196,10 +185,4 @@ runs: ## 呼叫方式 -格式:`[--action-dir ] [--manifest ]` — 全部可省略(根目錄預設目前工作目錄;manifest 自動尋找)。 - -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:action-composite`,或 `/jsc-code:action-composite --action-dir ~/work/my-action` | -| Codex | `$action-composite`,或 `$action-composite --action-dir ~/work/my-action`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「把這個 composite action 標準化,在 steps 最前面加一個會輸出 action 名稱/用途/更新時間的 step,最後跑 funcs 補文件並重建 README」)自動觸發 | +各助理的呼叫方式依 `/jsc-shared:spec-skill-invocation`;本 skill 參數格式:`[--action-dir ] [--manifest ]` — 全部可省略(根目錄預設目前工作目錄;manifest 自動尋找)。例:`/jsc-code:action-composite --action-dir ~/work/my-action`(Codex 對應 `$action-composite --action-dir ~/work/my-action`)。 diff --git a/skills/action-docker/SKILL.md b/skills/action-docker/SKILL.md index e89aaeb..da62b84 100644 --- a/skills/action-docker/SKILL.md +++ b/skills/action-docker/SKILL.md @@ -18,17 +18,11 @@ argument-hint: "[--action-dir ] [--node-version ] [- --- -## 共用規範(shared plugin,必要前置) +## 共用規範(必要前置) -執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到(shared plugin 未安裝)時,先詢問使用者是否安裝 shared plugin(`https://gitea.jsc.idv.tw/plugins/shared.git`),使用者不安裝則直接中斷本 skill**,不得只憑下方一行摘要繼續執行: - -- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼。 -- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認、不擴及無關檔案。 -- `/jsc-shared:spec-git-safety`:不破壞既有工作(絕不 `reset --hard`/`clean`)、`git mv` 保留歷史。 -- `/jsc-shared:spec-action-params`:action 參數來源優先序(環境變數 → 經同意新增 `inputs`)、`secrets`/`vars` 一律視為不可用。 -- `/jsc-shared:spec-time-log`:更新時間 Asia/Taipei `yyyy/MM/dd HH:mm:ss`、完成後統一同步時間戳。 -- `/jsc-shared:spec-dockerfile`:Dockerfile 六步流程、多階段建置、固定版號、自我檢查。 -- `/jsc-shared:spec-doc-funcs-handoff`:最終階段完整執行 `/jsc-doc:funcs` 的標準流程。 +先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝, +依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。 +本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-git-safety`、`spec-action-params`、`spec-action-scaffold`、`spec-ask-user`、`spec-node-src-layout`、`spec-time-log`、`spec-dockerfile`、`spec-doc-funcs-handoff`、`spec-skill-invocation` 本 skill 特有補充: @@ -40,7 +34,7 @@ argument-hint: "[--action-dir ] [--node-version ] [- ## 參數來源優先序(開發中需要新參數時) -從零建立(A1a)、主程式 Node 化(階段 B)、產生 `entrypoint.sh`(階段 C)或產生 `Dockerfile`(階段 D)的過程中,若需要新的參數值,一律依 `/jsc-shared:spec-action-params` 的優先序處理:Docker 容器 action 優先取 runner 注入的執行期環境變數(Node 主程式讀 `process.env.GITHUB_*`、`entrypoint.sh` 讀 `$GITHUB_*`;Gitea 亦提供 `GITEA_*` 同義變數,建議讀 `GITHUB_*` 以相容兩邊),取不到才以 `AskUserQuestion` 經使用者同意新增 `inputs`(容器內以 `INPUT_<大寫名稱>` 取用);`secrets`/`vars` 一律視為不可用,需要時宣告為 `input` 由呼叫端 workflow 傳入(範例見該 spec)。 +從零建立(A1a)、主程式 Node 化(階段 B)、產生 `entrypoint.sh`(階段 C)或產生 `Dockerfile`(階段 D)的過程中,若需要新的參數值,依 `/jsc-shared:spec-action-params` 執行(Docker 容器 action 讀取 runner 注入的執行期環境變數)。 --- @@ -58,19 +52,11 @@ argument-hint: "[--action-dir ] [--node-version ] [- ### A1. 決定 action 根目錄 -- 帶 `--action-dir` → 採用(展開 `~`)。 -- 省略 → 用目前工作目錄。 -- 根目錄須存在 `action.yml` 或 `action.yaml`(action manifest)。**找不到** action manifest → **不臆測**、不逕自動工;以 `AskUserQuestion` 詢問使用者要「**從零建立**新的 Docker 容器 action」還是「提供正確的 action 路徑」:選「從零建立」→ 進入 A1a;選「提供路徑」→ 依新路徑重跑 A1。 +依 `/jsc-shared:spec-action-scaffold` 判斷 action 根目錄;找不到 action manifest 時進入 A1a 問答式從零建立。 ### A1a. 從零建立 Docker 容器 action(問答式) -依序以問答收集需求,再產生 manifest 與主程式骨架: - -1. **action 名稱**:用於 `action.yml` 的 `name`。 -2. **輸入與輸出參數**:逐一收集 `inputs`/`outputs` 的名稱與 `description`(description 盡量繁體中文、無亂碼)、`required`/`default`;沒有可留空。 -3. **執行目標**:詢問此 action 要達成什麼,整理濃縮成一句話作為 `action.yml` 的 `description`(盡量繁體中文、無亂碼)。 - -收集完成後,於 action 根目錄產生:`action.yml`(`runs.using: docker`、`image: Dockerfile`,含收集到的 `name`/`description`/`inputs`/`outputs`)與 `src/index.js` 主程式(依執行目標以 Node 實作,輸入以 `process.env.INPUT_` 讀取,開發中需要新參數時套用「參數來源優先序」);完成後接續 A2 往後流程(A3 判定為 Node,階段 B 走「已是 Node」分支,階段 C/D 照常產生 `entrypoint.sh` 與 `Dockerfile`)。 +依 `/jsc-shared:spec-action-scaffold` 的三題問答骨架收集需求。收集完成後,於 action 根目錄產生:`action.yml`(`runs.using: docker`、`image: Dockerfile`,含收集到的 `name`/`description`/`inputs`/`outputs`)與 `src/index.js` 主程式(依執行目標以 Node 實作,輸入以 `process.env.INPUT_` 讀取,開發中需要新參數時套用「參數來源優先序」);完成後接續 A2 往後流程(A3 判定為 Node,階段 B 走「已是 Node」分支,階段 C/D 照常產生 `entrypoint.sh` 與 `Dockerfile`)。 ### A2. 讀取 action 名稱與用途 @@ -100,7 +86,7 @@ argument-hint: "[--action-dir ] [--node-version ] [- ### B1. 主程式 Node 化 - **已是 Node** → 確認入口檔,不改寫邏輯,直接進 B2。 -- **非 Node(shell/python/其他)** → 這是**破壞性高風險決策**:先以 `AskUserQuestion` 向使用者確認是否改寫為 Node,選項至少含「改寫為 Node」「維持原樣只做容器化(略過改寫)」「其他」。經確認後才改寫: +- **非 Node(shell/python/其他)** → 依 `/jsc-shared:spec-ask-user` 的破壞性決策規則,先以 `AskUserQuestion` 向使用者確認是否改寫為 Node,選項至少含「改寫為 Node」「維持原樣只做容器化(略過改寫)」「其他」。經確認後才改寫: - 逐段把原主程式邏輯**保守翻譯**為 Node(建議 `index.js`);保留對外行為、輸入(環境變數/`INPUT_*`/args)與輸出(stdout/exit code/檔案副作用)契約。 - 外部指令呼叫以 `child_process`(`execFileSync`/`spawnSync`)對應;檔案操作以 `fs`;環境變數以 `process.env`。 - 任何無法可靠等價翻譯處,**不臆測**:以 `// 需人工確認:...` 標註並回報。 @@ -108,14 +94,7 @@ argument-hint: "[--action-dir ] [--node-version ] [- ### B2. 主程式依賴鏈的 `.js` 集中到 `src/` -- 在 action 根目錄建立 `src/`(若不存在)。 -- 將**主程式入口及其 `require`/`import` 依賴鏈**的 `.js`/`.mjs`/`.cjs`(排除 `node_modules`/`.git`/`.docs`/`bin`/`obj`/第三方依賴)**移入 `src/`**,優先 `git mv` 保留歷史。主程式入口統一為 `src/index.js`(或 `src/
.js`)。 -- **明文排除、不搬**:外部工具依慣例路徑尋找的檔案——`*.config.js`(`eslint.config.js`/`jest.config.js`/`webpack.config.js` 等)、`.*rc.js`、husky/commitlint 等工具設定,以及 `test/`/`tests/`/`scripts/` 目錄;搬走會弄壞 lint/test/build 流程。 -- **移動後必須更新所有引用**,確保不破壞: - - 模組間的 `require`/`import` 相對路徑。 - - `package.json` 的 `main`/`bin`/`scripts`/`exports`(指向 `src/...`)。 - - `action.yml`/`Dockerfile`/`entrypoint.sh` 內對主程式路徑的引用。 -- 若 `src/` 下無 `package.json` 而專案需要相依,於 `src/` 建立或移入 `package.json`,`main` 指向入口檔;不擅自新增與功能無關的相依。 +依 `/jsc-shared:spec-node-src-layout` 把主程式入口及其依賴鏈的 `.js`/`.mjs`/`.cjs` 收進 `src/`,並同步更新所有引用。`action.yml`/`Dockerfile`/`entrypoint.sh` 內對主程式路徑的引用亦需一併更新;若 `src/` 下無 `package.json` 而專案需要相依,於 `src/` 建立或移入 `package.json`,`main` 指向入口檔。 ### B3. action.yml 對齊 docker @@ -139,7 +118,7 @@ runs: 在 action 根目錄產生(或覆寫)`entrypoint.sh`:**啟動 node 主程式前,先輸出 action 名稱、用途、更新時間**。 -- **更新時間**依 `/jsc-shared:spec-time-log`(Asia/Taipei、固定 `yyyy/MM/dd HH:mm:ss`、寫成檔內固定字串)。本階段先寫入暫定時間戳,**階段 E 完成後會統一同步各處時間戳**(見階段 E)。 +- **更新時間**依 `/jsc-shared:spec-time-log` 執行;本階段先寫入暫定時間戳,**階段 E 完成後會統一同步各處時間戳**(見階段 E)。 - 名稱/用途取自階段 A2 的 `action.yml`。 - 最後以 `exec node <主程式>`(如 `exec node /action/src/index.js "$@"`)啟動,讓 node 取代 shell 行程,正確傳遞訊號與 exit code;路徑須與階段 D 的 `Dockerfile` 落點一致。 - **若階段 B 裁示「維持非 Node」**:橫幅照常輸出,最後改以 `exec <原直譯器> <主程式>` 啟動(如 `exec python3 /action/src/main.py "$@"`、`exec sh /action/src/main.sh "$@"`)。 @@ -166,80 +145,21 @@ exec node /action/src/index.js "$@" --- -## 階段 D:產生 Dockerfile(使用最新 LTS node 版本,六步流程依主程式實作量身產生) +## 階段 D:產生 Dockerfile(使用最新 LTS node 版本) -在 action 根目錄產生(或覆寫)`Dockerfile`,**base image 使用最新 LTS node 版本(固定 major tag)**,採**多階段建置**以縮小最終映像檔。六步流程是**骨架**,但**每一步的實際內容必須依階段 B 的主程式(`src/`)實作決定**——不要套用與主程式無關的固定樣板,也不要硬塞主程式用不到的安裝或建置指令。 +先檢視 `src/` 主程式與 `package.json`(相依與 lockfile、是否需要 build/transpile、OS 層相依、執行期需求、入口檔),依盤點結果依 `/jsc-shared:spec-dockerfile` 的六步流程(參數處理 → 安裝套件 → 複製檔案 → 執行程序 → 縮小映像檔 → 設定入口)產生(或覆寫)action 根目錄的 `Dockerfile`;並產生(或補齊)`.dockerignore`,至少排除 `.git`、`.docs`、`node_modules`(宿主端,可能含平台不符的原生模組)。 -### D0. 先盤點主程式實作需求 +本 skill 特有差異: -產生 Dockerfile 前,先檢視 `src/` 主程式與 `package.json`,判斷各步該放什麼: - -- **相依套件**:`src/package.json` 的 `dependencies`/`devDependencies`、是否有 lockfile(`package-lock.json`/`npm-shrinkwrap.json`)。決定第 2 步用 `npm ci` 還是 `npm install`、是否需要 dev 相依來 build。 -- **是否需要 build/transpile**:有無 `scripts.build`、是否為 TypeScript/打包(產物落在 `dist/`/`build/` 等)。決定第 4 步是否要跑建置、第 5 步 runtime 該帶入哪個產物目錄。 -- **OS 層相依**:主程式是否以 `child_process` 呼叫外部執行檔(如 `git`、`bash`、`openssl`、`python3`、`ffmpeg`、`imagemagick`),或使用需編譯的原生模組(node-gyp/native addon)。決定第 2 步是否要 `apt-get install` 對應套件,以及第 5 步 runtime 基底是否需保留這些執行檔。 -- **執行期需求**:主程式讀取的環境變數、需存取的檔案路徑、是否寫入暫存目錄。決定 `WORKDIR`、需 `COPY` 的檔案範圍與必要的 `ENV`。 -- **入口檔**:階段 B 決定的主程式入口(如 `src/index.js`),決定第 6 步與 `entrypoint.sh` 內 `node` 路徑。 -- **產生 `.dockerignore`**:於 action 根目錄產生(或補齊)`.dockerignore`,至少排除 `.git`、`.docs`、`node_modules`(宿主端,可能含平台不符的原生模組)與其他非執行必需檔案,避免帶進 build context。 - -### D1~D6:依盤點結果填入六步 - -1. **參數處理**:以 `ARG` 注入 node 版本(預設最新)與主程式需要的 build-time 參數;可調參數集中在檔案開頭。 -2. **安裝套件**:依 D0 的相依結果安裝——有 lockfile 用 `npm ci`、否則 `npm install`,**二擇一寫死於 Dockerfile**(不得以 `npm ci || npm install` 之類 fallback 串接,避免靜默吞錯、破壞 lockfile 的可重現性);不需 build 時加 `--omit=dev`,需 build 時先裝完整相依(含 dev)供第 4 步使用。**只在主程式真的會用到時**才 `apt-get install` OS 套件,並於同一 `RUN` 清掉 apt 快取。先 `COPY` 相依描述(`package*.json`)再安裝,以利 layer 快取。 -3. **複製檔案**:`COPY` 主程式實際需要的檔案(`src/`、`entrypoint.sh`,及主程式會讀取的資源),**明列路徑、不整包 `COPY .`**,並配合 `.dockerignore` 排除非必要檔案。 -4. **執行程序**:賦予 `entrypoint.sh` 執行權限;**僅當 D0 判定需要 build/transpile 時**才執行(如 `npm run build`),否則此步只做權限設定,不要硬加建置指令。 -5. **縮小映像檔**:多階段建置,runtime 改用 slim 基底,只 `COPY --from=build` 帶入主程式執行**真正需要**的產物——有 build 產物時帶入 `dist/` 與 production `node_modules`(`npm prune --omit=dev` 或重裝 production 相依),純 JS 無 build 時帶入 `src/` 與 production `node_modules`;若 D0 判定 runtime 需要某些 OS 執行檔,於 runtime 階段一併安裝(slim 基底可能不含)。不要把 build 階段的 dev 相依與快取帶進最終映像;**`COPY --from=build` 必須逐項明列路徑,不得整包 `COPY --from=build /action /action`**(整包搬等於沒有縮小)。 -6. **設定入口**:`ENTRYPOINT` 指向 `entrypoint.sh`,其內 `node` 路徑為 D0 的入口檔。 - -base image 版本依 `/jsc-shared:spec-dockerfile`(固定 major tag、不用 `latest`):**預設使用產生當下的最新 LTS major 固定 tag**(如 build 基底 `node:22`、runtime 基底 `node:22-slim`),可查詢 Docker Hub(`https://hub.docker.com/_/node`)確認當前 LTS 版號;帶 `--node-version ` 時改用 `node:` 與 `node:-slim`。 - -下列為**最簡基準骨架**(純 JS、無 build、無 OS 相依的情況);實際內容須依 D0 盤點調整,第 4 步建置、第 2/5 步 OS 套件等視主程式需要增刪: - -```Dockerfile -# 1. 參數處理:node 版本以 ARG 注入(預設當前最新 LTS major,範例為 22;--node-version 可覆寫) -ARG NODE_VERSION=22 -ARG NODE_RUNTIME=22-slim - -# ---- build 階段:安裝相依、準備產物 ---- -FROM node:${NODE_VERSION} AS build -WORKDIR /action - -# 2. 安裝套件:先帶相依描述以利 layer 快取,再裝 src/ 相依 -# (依 D0 盤點二擇一寫死:有 lockfile 用 npm ci、否則 npm install,不用 || 串接; -# 需 build 時改裝完整相依;需 OS 套件時於此 apt-get install 並清快取) -COPY src/package*.json /action/src/ -RUN if [ -f /action/src/package.json ]; then \ - cd /action/src && npm ci --omit=dev; \ - fi - -# 3. 複製檔案:明列主程式實際需要的檔案(配合 .dockerignore,不整包 COPY .) -COPY src/ /action/src/ -COPY entrypoint.sh /action/entrypoint.sh - -# 4. 執行程序:賦予 entrypoint 執行權限(D0 判定需要 build 時於此 npm run build) -RUN chmod +x /action/entrypoint.sh - -# 5. 縮小映像檔:多階段建置,runtime 改用 slim 基底,逐項明列只帶執行真正需要的產物 -# (有 build 產物時改帶 dist/ 與 production node_modules;runtime 需要的 OS 執行檔於此安裝) -FROM node:${NODE_RUNTIME} AS runtime -WORKDIR /action -COPY --from=build /action/src /action/src -COPY --from=build /action/entrypoint.sh /action/entrypoint.sh - -# 6. 設定入口:以 entrypoint.sh 啟動(其內 node 指向 D0 的入口檔) -ENTRYPOINT ["/action/entrypoint.sh"] -``` - -- **依主程式實作**:上面只是基準骨架。若主程式是 TypeScript/需打包,第 4 步要加 `npm run build`、第 5 步只帶 `dist/` 與 production 相依;若主程式會 spawn 外部執行檔或用原生模組,第 2/5 步要補對應 OS 套件;用不到的步驟內容不要硬塞。 -- **若階段 B 裁示「維持非 Node」**:base image 改用對應語言官方映像(如 `python:3-slim`),第 2 步改安裝該生態相依(如 `pip install -r requirements.txt`),其餘六步結構不變;`ENTRYPOINT` 仍指向 `entrypoint.sh`(其內 `exec` 原直譯器,見階段 C)。 -- **版號對齊**:依 `/jsc-shared:spec-dockerfile`(runtime 與 build 版號一致、以獨立 runtime ARG 帶入不用 `${VERSION}-slim` 組裝、`-alpine`/`-slim` 變體沿用同一 tag、使用者要求 `latest` 時 runtime 用 `node:slim`)。 -- **自我檢查**:`entrypoint.sh` 的 `node <主程式>` 路徑(如 `/action/src/index.js`)需與 `Dockerfile` 的 `WORKDIR`/`COPY` 落點一致,避免容器啟動時找不到主程式;`action.yml` 的 `runs.entrypoint` 若存在,必須是**絕對路徑**且與 `Dockerfile` 落點一致(預設不設,讓 Dockerfile 的 `ENTRYPOINT` 生效)。 +- **base image 預設 Node LTS**:預設使用產生當下的最新 LTS major 固定 tag(如 build 基底 `node:22`、runtime 基底 `node:22-slim`,可查詢 `https://hub.docker.com/_/node` 確認版號),帶 `--node-version ` 時改用 `node:`/`node:-slim`。**若階段 B 裁示「維持非 Node」**:base image 改用對應語言官方映像(如 `python:3-slim`),第 2 步改安裝該生態相依(如 `pip install -r requirements.txt`),其餘六步結構不變;`ENTRYPOINT` 仍指向 `entrypoint.sh`(其內 `exec` 原直譯器,見階段 C)。 +- **入口一致性自我檢查**(補充於 spec-dockerfile 通用自我檢查之外):`entrypoint.sh` 內 `node <主程式>` 路徑(如 `/action/src/index.js`)須與 `Dockerfile` 的 `WORKDIR`/`COPY` 落點一致,避免容器啟動時找不到主程式;`action.yml` 的 `runs.entrypoint` 若存在,必須是**絕對路徑**且與 `Dockerfile` 落點一致(預設不設,讓 `Dockerfile` 的 `ENTRYPOINT` 生效)。 - 此檔的用途/更新日期註解與逐行註解,同樣於階段 E 由 funcs 指令檔流程統一補齊。 --- ## 階段 E:完整執行 /jsc-doc:funcs 處理流程 -容器化與 Node 化完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**完整執行 `/jsc-doc:funcs` 流程(前置可用性檢查、完整流程、由使用者裁示實作方式、重建 README;本次新增/變更的 `entrypoint.sh`、`Dockerfile` 與 `src/` 內 Node 主程式都會被涵蓋)。完成後依該 spec **統一時間戳**:回頭同步 `entrypoint.sh` 橫幅輸出與註解區塊、`Dockerfile` 註解區塊與 README 的更新時間,確保各處一致。 +容器化與 Node 化完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**執行 `/jsc-doc:funcs` 流程(本次新增/變更的 `entrypoint.sh`、`Dockerfile` 與 `src/` 內 Node 主程式都會被涵蓋);完成後依該 spec 統一時間戳,回頭同步 `entrypoint.sh` 橫幅輸出、`Dockerfile` 註解區塊與 README 的更新時間。 --- @@ -257,10 +177,10 @@ ENTRYPOINT ["/action/entrypoint.sh"] ## 呼叫方式 -格式:`[--action-dir ] [--node-version ] [--main <主程式檔>]` — 全部可省略(根目錄預設目前工作目錄;node 版本預設最新 LTS;主程式自動判斷)。 +依 `/jsc-shared:spec-skill-invocation`;本 skill 的 `` 為 `action-docker`,參數格式:`[--action-dir ] [--node-version ] [--main <主程式檔>]` — 全部可省略(根目錄預設目前工作目錄;node 版本預設最新 LTS;主程式自動判斷)。 -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:action-docker`,或 `/jsc-code:action-docker --action-dir ~/work/my-action --node-version 22` | -| Codex | `$action-docker`,或 `$action-docker --action-dir ~/work/my-action`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「把這個 action 主程式改成 node、主程式相關 js 收進 src/,產生會先輸出 action 名稱/用途/更新時間再啟動 node 的 entrypoint.sh、用最新 LTS node 版本的 Dockerfile,最後跑 funcs 補文件並重建 README」)自動觸發 | +範例: + +- Claude Code / Antigravity:`/jsc-code:action-docker`,或 `/jsc-code:action-docker --action-dir ~/work/my-action --node-version 22` +- Codex:`$action-docker`,或 `$action-docker --action-dir ~/work/my-action`,或用 `/skills` 選單 +- OpenCode:描述需求(如「把這個 action 主程式改成 node、主程式相關 js 收進 src/,產生會先輸出 action 名稱/用途/更新時間再啟動 node 的 entrypoint.sh、用最新 LTS node 版本的 Dockerfile,最後跑 funcs 補文件並重建 README」)自動觸發 diff --git a/skills/action-node/SKILL.md b/skills/action-node/SKILL.md index 05a5916..f491660 100644 --- a/skills/action-node/SKILL.md +++ b/skills/action-node/SKILL.md @@ -18,16 +18,11 @@ argument-hint: "[--action-dir ] [--node-version `(或 `core.getInput`)取用,取不到才以 `AskUserQuestion` 經使用者同意新增 `inputs`(由呼叫端 workflow 以 `with:` 傳入);`secrets`/`vars` 一律視為不可用,需要時宣告為 `input` 由呼叫端傳入(範例見該 spec)。 +從零建立(A1a)、主程式 Node 化(階段 B)、對齊 `action.yml`(階段 C)或打包(階段 D)的過程中,若需要新的參數值,依 `/jsc-shared:spec-action-params` 的優先序處理;node action 主程式讀值方式:優先取 `process.env.GITHUB_*`(Gitea 亦提供 `GITEA_*` 同義變數,建議讀 `GITHUB_*` 以相容兩邊),既有 `inputs` 以 `process.env.INPUT_<大寫名稱>`(或 `core.getInput`)取用。 --- @@ -77,17 +72,11 @@ node action 相對 docker/composite 的**關鍵差異**(全程據此處理 ### A1. 決定 action 根目錄 -- 帶 `--action-dir` → 採用(展開 `~`)。 -- 省略 → 用目前工作目錄。 -- 根目錄須存在 `action.yml` 或 `action.yaml`(action manifest)。**找不到** action manifest → **不臆測**、不逕自動工;以 `AskUserQuestion` 詢問使用者要「**從零建立**新的 Node action」還是「提供正確的 action 路徑」:選「從零建立」→ 進入 A1a;選「提供路徑」→ 依新路徑重跑 A1。 +依 `/jsc-shared:spec-action-scaffold` 判斷 action 根目錄;**找不到** action manifest 時,依該 spec 詢問使用者「從零建立」或「提供正確的 action 路徑」,選「從零建立」→ 進入 A1a。 ### A1a. 從零建立 Node action(問答式) -依序以問答收集需求,再產生 manifest 與主程式骨架: - -1. **action 名稱**:用於 `action.yml` 的 `name`。 -2. **輸入與輸出參數**:逐一收集 `inputs`(名稱、`description`、`required`、`default`)與 `outputs`(名稱、`description`;**不收集 `value`**);description 盡量繁體中文、無亂碼;沒有可留空。 -3. **執行目標**:詢問此 action 要達成什麼,整理濃縮成一句話作為 `action.yml` 的 `description`(盡量繁體中文、無亂碼)。 +依 `/jsc-shared:spec-action-scaffold` 的三題骨架問答收集需求(action 名稱/用途、輸入輸出、執行目標;第 2 題收集 `outputs` 時**不收集 `value`**)。 收集完成後,於 action 根目錄產生:`action.yml`(`runs.using: node24`、`runs.main: src/index.js`,含收集到的 `name`/`description`/`inputs`/`outputs`)與 `src/index.js` 主程式(依執行目標以 Node 實作,輸入以 `process.env.INPUT_` 讀取、輸出以附加寫入 `process.env.GITHUB_OUTPUT`,開發中需要新參數時套用「參數來源優先序」);預設走**零相依**路線。完成後接續 A2 往後流程(A3 判定為 Node,階段 B 走「已是 Node」分支,階段 C/D 照常對齊 manifest 與處理相依)。 @@ -121,7 +110,7 @@ node action 相對 docker/composite 的**關鍵差異**(全程據此處理 ### B1. 主程式 Node 化 - **已是 Node** → 確認入口檔,不改寫邏輯,直接進 B2。 -- **非 Node(shell/python/其他)** → 這是**破壞性高風險決策**:先以 `AskUserQuestion` 向使用者確認是否改寫為 Node,選項至少含「改寫為 Node」「改用 action-docker(維持原語言容器化)」「其他」。經確認後才改寫: +- **非 Node(shell/python/其他)** → 這是**破壞性高風險決策**,依 `/jsc-shared:spec-ask-user` 以 `AskUserQuestion` 向使用者確認是否改寫為 Node,選項至少含「改寫為 Node」「改用 action-docker(維持原語言容器化)」「其他」。經確認後才改寫: - 逐段把原主程式邏輯**保守翻譯**為 Node(建議 `src/index.js`);保留對外行為、輸入(環境變數/`INPUT_*`/args)與輸出(`$GITHUB_OUTPUT`/stdout/exit code/檔案副作用)契約。 - 外部指令呼叫以 `child_process`(`execFileSync`/`spawnSync`)對應;檔案操作以 `fs`;環境變數以 `process.env`。 - 任何無法可靠等價翻譯處,**不臆測**:以 `// 需人工確認:...` 標註並回報。 @@ -129,14 +118,7 @@ node action 相對 docker/composite 的**關鍵差異**(全程據此處理 ### B2. 主程式依賴鏈的 `.js` 集中到 `src/` -- 在 action 根目錄建立 `src/`(若不存在)。 -- 將**主程式入口及其 `require`/`import` 依賴鏈**的 `.js`/`.mjs`/`.cjs`(排除 `node_modules`/`.git`/`.docs`/`dist`/第三方依賴)**移入 `src/`**,優先 `git mv` 保留歷史。主程式入口統一為 `src/index.js`(或 `src/
.js`)。 -- **明文排除、不搬**:外部工具依慣例路徑尋找的檔案——`*.config.js`(`eslint.config.js`/`jest.config.js`/`ncc` 等工具設定)、`.*rc.js`、husky/commitlint 等工具設定,以及 `test/`/`tests/`/`scripts/` 目錄;搬走會弄壞 lint/test/build 流程。 -- **移動後必須更新所有引用**,確保不破壞: - - 模組間的 `require`/`import` 相對路徑。 - - `package.json` 的 `main`/`bin`/`scripts`/`exports`(指向 `src/...`;`scripts.build` 的 ncc 入口指向 `src/index.js`)。 - - `action.yml` 的 `runs.main`(先指向 `src/index.js`;若階段 D 判定需打包,再改指 `dist/index.js`)。 -- 若 `src/` 需要的相依尚未宣告,於 `package.json` 補上;不擅自新增與功能無關的相依(`@vercel/ncc` 屬階段 D 的 build 相依,經同意後才加)。 +依 `/jsc-shared:spec-node-src-layout` 把主程式入口及其依賴鏈的 `.js`/`.mjs`/`.cjs` 收進 `src/`,並同步更新所有引用。`package.json` 的 `scripts.build`(ncc 入口)與 `action.yml` 的 `runs.main` 先指向 `src/index.js`;若階段 D 判定需打包,再改指 `dist/index.js`。`@vercel/ncc` 屬階段 D 的 build 相依,經同意後才加,不在本階段新增。 --- @@ -162,7 +144,7 @@ runs: node action 沒有 `entrypoint.sh`,橫幅改由**主程式 JS 最前面輸出**。在 `src/index.js`(或階段 B 決定的入口)最上方,於任何其他邏輯**之前**插入(或更新)橫幅輸出: -- **更新時間**依 `/jsc-shared:spec-time-log`(Asia/Taipei、固定 `yyyy/MM/dd HH:mm:ss`、寫成檔內固定字串)。本階段先寫入暫定時間戳,**階段 E 完成後會統一同步各處時間戳**(見階段 E)。 +- **更新時間**依 `/jsc-shared:spec-time-log` 處理。本階段先寫入暫定時間戳,**階段 E 完成後會統一同步各處時間戳**(見階段 E)。 - 名稱/用途取自階段 A2 的 `action.yml`(缺漏時以「(未提供)」標示)。 - 若已存在本 skill 先前插入的橫幅(依可辨識註解標記),則**更新**其內容與時間戳,不重複插入。 @@ -223,7 +205,7 @@ node action 的 runner **不會自動 `npm install`**,因此相依必須隨 re ## 階段 E:完整執行 /jsc-doc:funcs 處理流程 -標準化與打包完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**完整執行 `/jsc-doc:funcs` 流程(前置可用性檢查、完整流程、由使用者裁示實作方式、重建 README;本次新增/變更的 `src/` 內 Node 主程式、`action.yml`、`package.json` 都會被涵蓋)。完成後依該 spec **統一時間戳**:回頭同步主程式橫幅輸出與註解區塊、`action.yml` 開頭註解區塊與 README 的更新時間,確保各處一致。 +標準化與打包完成後,以階段 A1 的 action 根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個 action 專案**完整執行 `/jsc-doc:funcs` 流程;本次新增/變更的 `src/` 內 Node 主程式、`action.yml`、`package.json` 都會被涵蓋,完成後依該 spec 統一同步主程式橫幅輸出、`action.yml` 開頭註解區塊與 README 的更新時間。 --- @@ -242,10 +224,10 @@ node action 的 runner **不會自動 `npm install`**,因此相依必須隨 re ## 呼叫方式 -格式:`[--action-dir ] [--node-version ] [--main <主程式檔>]` — 全部可省略(根目錄預設目前工作目錄;node 版本預設 `node24`;主程式自動判斷)。 +依 `/jsc-shared:spec-skill-invocation` 的呼叫方式;本 skill 為 `jsc-code:action-node`,格式:`[--action-dir ] [--node-version ] [--main <主程式檔>]` — 全部可省略(根目錄預設目前工作目錄;node 版本預設 `node24`;主程式自動判斷)。 -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:action-node`,或 `/jsc-code:action-node --action-dir ~/work/my-action --node-version node24` | -| Codex | `$action-node`,或 `$action-node --action-dir ~/work/my-action`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「把這個 action 主程式改成 node、主程式相關 js 收進 src/,對齊 action.yml 為 node action、在主程式最前面輸出 action 名稱/用途/更新時間,有相依就用 ncc 打包成 dist 並 commit,最後跑 funcs 補文件並重建 README」)自動觸發 | +範例: + +- Claude Code / Antigravity:`/jsc-code:action-node --action-dir ~/work/my-action --node-version node24` +- Codex:`$action-node --action-dir ~/work/my-action` +- OpenCode:描述需求(如「把這個 action 主程式改成 node、主程式相關 js 收進 src/,對齊 action.yml 為 node action、在主程式最前面輸出 action 名稱/用途/更新時間,有相依就用 ncc 打包成 dist 並 commit,最後跑 funcs 補文件並重建 README」)自動觸發 diff --git a/skills/image/SKILL.md b/skills/image/SKILL.md index 9dc672d..9a25ea0 100644 --- a/skills/image/SKILL.md +++ b/skills/image/SKILL.md @@ -16,15 +16,11 @@ argument-hint: "[--project-dir <專案根目錄>] [--dockerfile ] [--dockerfile ] [--dockerfile -ARG RUNTIME_IMAGE= - -# ---- build 階段:安裝相依、複製原始碼、建置產物 ---- -FROM ${BUILD_IMAGE} AS build -WORKDIR /app - -# 2. 安裝套件:先帶相依描述以利 layer 快取,再安裝(並清理快取縮小該層) -COPY ./ -RUN - -# 3. 複製檔案:複製其餘原始碼(搭配 .dockerignore) -COPY . . - -# 4. 執行程序:build / compile / transpile、必要權限設定 -RUN - -# ---- 5. 縮小映像檔:runtime 改用較小基底,只帶執行所需產物 ---- -FROM ${RUNTIME_IMAGE} AS runtime -WORKDIR /app -COPY --from=build /app/ ./ -# (搬移 runtime 需要的 ENV / EXPOSE / USER,語意與原檔一致) - -# 6. 設定入口:ENTRYPOINT / CMD 置於最後,語意同原檔 -ENTRYPOINT [] -``` - -- **自我檢查**:依 `/jsc-shared:spec-dockerfile` 的自我檢查清單(跨階段 `ARG` 重新宣告、runtime 帶齊產物、對外契約一致、路徑一致)。 -- 此檔的「用途/更新日期」開頭註解區塊與逐行註解,於階段 C 由 `/jsc-doc:funcs` 的指令檔流程統一補齊/覆寫為標準格式;本階段先確保**建置行為**正確、六步結構清楚即可。 -- **建置驗證**:若環境可執行 `docker build`,重整後做一次建置驗證(或至少 `docker build --check`/語法檢查)確認可建置;無法執行時明確說明原因並標註風險(階段 C funcs 第 11 步亦會做語法驗證)。 +此檔的「用途/更新日期」開頭註解區塊與逐行註解,於階段 C 由 `/jsc-doc:funcs` 的指令檔流程統一補齊/覆寫為標準格式;本階段先確保**建置行為**正確、六步結構清楚即可(階段 C funcs 第 11 步亦會做語法驗證)。 --- ## 階段 C:完整執行 /jsc-doc:funcs 處理流程 -Dockerfile 六步整理完成後,以階段 A1 的專案根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個專案**完整執行 `/jsc-doc:funcs` 流程(前置可用性檢查、完整流程、由使用者裁示實作方式、重建 README;本次整理的 `Dockerfile` 會被視為部署設定檔補齊標頭與逐行註解)。 +Dockerfile 六步整理完成後,以階段 A1 的專案根目錄為目標,依 `/jsc-shared:spec-doc-funcs-handoff` 對**整個專案**完整執行 `/jsc-doc:funcs`。 --- @@ -145,10 +104,4 @@ Dockerfile 六步整理完成後,以階段 A1 的專案根目錄為目標, ## 呼叫方式 -格式:`[--project-dir <專案根目錄>] [--dockerfile ] [--yes]` — 全部可省略(專案根目錄預設目前工作目錄;Dockerfile 自動定位,多個時詢問)。 - -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:image`,或 `/jsc-code:image --project-dir ~/work/my-app --dockerfile build/Dockerfile` | -| Codex | `$image`,或 `$image --dockerfile docker/Dockerfile`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「把這個專案的 Dockerfile 整理成參數處理→安裝套件→複製檔案→執行程序→縮小映像檔→設定入口六步、用多階段建置縮小映像,最後跑 funcs 補文件並重建 README」)自動觸發 | +依 `/jsc-shared:spec-skill-invocation` 執行,以 `image` 作為本 skill 名稱代入其呼叫方式表格;參數格式見上方〔參數〕章節。 diff --git a/skills/issues/SKILL.md b/skills/issues/SKILL.md index bed310f..c7f441b 100644 --- a/skills/issues/SKILL.md +++ b/skills/issues/SKILL.md @@ -18,21 +18,15 @@ argument-hint: "[--tool ] [--repo ] [--issues <編號,以 --- -## 絕對準則(不可違反) +## 共用規範(必要前置) -- **不建立任何草稿檔**:不寫 `.docs/`、不寫暫存檔、不用本機檔案傳遞中間結果。需求彙整、TODO 排序、實作進度、驗證結果等所有中間成果,一律留在**對話內容**,並透過 `tea` 或 Gitea API **保存到議題描述或議題留言**。唯一例外是階段 E 在議題範圍內對**目標 repo 原始碼**的正常程式修改。 -- **盡量使用表格或圖形**:依 `/jsc-shared:spec-output` — 面向使用者的輸出與寫入議題的內容(需求彙整、TODO 列表、進度回報),優先以 Markdown 表格與 Mermaid 圖呈現,忠實反映議題內容、不得杜撰。 - -## 共用規範(shared plugin,必要前置) - -執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到(shared plugin 未安裝)時,先詢問使用者是否安裝 shared plugin(`https://gitea.jsc.idv.tw/plugins/shared.git`),使用者不安裝則直接中斷本 skill**,不得只憑下方一行摘要繼續執行: - -- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼、API body 以 UTF-8 JSON 檔帶入且換行為實際換行。 -- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認、已知資訊跳過詢問。 -- `/jsc-shared:spec-gitea`:tea/API 工具選擇與檢查、`GITEA_TOKEN` 機密保護(不 echo、遮蔽)、不依賴 `jq`、API 分頁完整讀取。 +先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝, +依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。 +本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-gitea`、`spec-no-scratch-files`、`spec-issue-read`、`spec-todo-list`、`spec-skill-invocation`、`spec-model` 本 skill 特有補充: +- 階段 E 在議題範圍內對**目標 repo 原始碼**的正常程式修改,是 `spec-no-scratch-files` 不落地準則的**唯一例外**(不算草稿檔)。 - **必要決策**(會中斷詢問):工具皆不可用、專案不明、議題編號缺失、TODO 與需求衝突需人工裁示、實作失敗需使用者決策。 - 階段 A/B/C 的詢問在「可跳過條件」成立時**必須跳過**,不要重複確認已知資訊。 @@ -54,7 +48,7 @@ argument-hint: "[--tool ] [--repo ] [--issues <編號,以 **若使用者已透過 `--tool` 或對話明確選定工具,跳過詢問**,只做該工具的可用性驗證。 -依 `/jsc-shared:spec-gitea` 的工具選擇流程執行:檢查 `tea`(`command -v tea`、`tea login list`)與 `GITEA_TOKEN`(只輸出「已設定/未設定」)→ 以表格呈現檢查結果後詢問使用者要用 `tea` 或 `api` → 兩種方式都不可用則回報並停止(不要請使用者把 token 貼進對話)。 +依 `/jsc-shared:spec-gitea` 的工具選擇流程執行。 ## 階段 B:確認議題所在專案(已知則跳過) @@ -81,49 +75,34 @@ argument-hint: "[--tool ] [--repo ] [--issues <編號,以 對每一個選定議題執行: -### D1. 讀取議題完整內容 +### D1. 讀取議題完整內容並彙整需求 -- 讀取 `title`、`body`、`state`、`labels`、`milestone`、`assignees` 與**所有留言**: - - `tea`:`tea issues --repo / --comments` - - `api`:`GET {base}/issues/{index}` 與 `GET {base}/issues/{index}/comments` -- 留言中若有需求補充、變更或取消,必須納入需求彙整,並以最新留言為準。 +依 `/jsc-shared:spec-issue-read` 讀取議題完整內容(描述、所有留言、所有附件)並彙整需求;`title`/`state`/`labels`/`milestone`/`assignees` 一併取得供階段 B/C 與後續留言引用。 -### D2. 彙整需求並盤點既有 TODO +### D2. 盤點既有 TODO 並產生排序後的 TODO 列表 -- 把議題描述與留言整理成**需求彙整**:目標、驗收條件、限制條件;只做歸納,不編造議題未提及的需求,不確定處標註「需人工確認」。 -- 盤點議題描述中既有的 Markdown checklist(`- [ ]` / `- [x]`)作為既有 TODO;已勾選項目視為已完成,不重做。 +依 `/jsc-shared:spec-todo-list` 盤點議題描述既有 TODO、產生新 TODO(格式、可驗收性、禁止編造、依影響範圍小→大排序、舉證 `path:line` 或需求語句)。 -### D3. 產生排序後的 TODO 列表 - -- 每項 TODO 必須**可執行、可驗收**,並評估其**影響範圍**(預計修改的檔案/模組數與波及面): - - | 影響範圍 | 定義(參考) | - | --- | --- | - | XS | 單一檔案內的局部修改(文案、設定值、小修正) | - | S | 單一檔案或單一函式的邏輯調整 | - | M | 同一模組內跨多檔案的修改 | - | L | 跨模組修改或介面/契約變更 | - | XL | 跨專案、資料結構或流程性的大改動 | - -- **依影響範圍由小到大排序**(XS → XL);範圍相同時,前置依賴在前。 -- **勾稽需求覆蓋度**:逐條比對需求彙整與 TODO 列表;若既有 TODO **不足以達成議題描述與需求**,補上缺漏的 TODO(標註「新增」)。 +- **勾稽需求覆蓋度**:逐條比對 D1 需求彙整與 TODO 列表;若既有 TODO **不足以達成議題描述與需求**,補上缺漏的 TODO(標註「新增」)。 - 有助理解時,在留言或描述中加入 Mermaid 流程圖呈現 TODO 之間的依賴與執行順序。 -### D4. 缺漏 TODO 附加到議題描述 +### D3. 缺漏 TODO 附加到議題描述 -- 若 D3 有新增 TODO,用 `tea` 或 API **更新議題描述**:保留原描述內容,於既有 `## TODO` 區塊補上新項目;沒有該區塊時在描述最後加上 `## TODO`: +- 若 D2 有新增 TODO,依 `/jsc-shared:spec-todo-list` 的區塊格式規則,用 `tea` 或 API **更新議題描述**: - `tea`:`tea issues edit --repo / --description <更新後全文>`(tea 版本不支援時改用 API) - `api`:`PATCH {base}/issues/{index}`,body 以 UTF-8 JSON 檔帶入 `{"body": "..."}` - 更新前先向使用者以表格摘要「新增了哪些 TODO、為什麼需要」;帶 `--yes` 時直接執行並回報。 - 沒有新增 TODO 時不改動議題描述。 +若議題描述或其中的 TODO 清單帶有等價於 `model:` frontmatter 的模型宣告(或本 skill 未來擴充為讀取帶 `model:` frontmatter 的檔案),依 `/jsc-shared:spec-model` 的『讀到帶 `model:` frontmatter 的清單檔時的檢查義務』先做模型檢查,再進入階段 E。 + ## 階段 E:逐項實作 TODO,每完成一項留言到議題 -依 D3 排序(多議題時先完成一個議題的所有 TODO,再進入下一個議題;有跨議題依賴時先處理被依賴者)逐項執行: +依 D2 排序(多議題時先完成一個議題的所有 TODO,再進入下一個議題;有跨議題依賴時先處理被依賴者)逐項執行: 1. **實作**:在本機 repo 依 TODO 內容實作,只修改該 TODO 必要範圍;發現需要擴大範圍或牽動其他 TODO 時,先停止並詢問使用者。 2. **驗證**:執行適合專案的建置/測試/驗證;失敗時修正到通過,無法通過則記錄原因並在留言中標註「待人工處理」。 -3. **更新議題描述**:把該 TODO 的 checkbox 由 `- [ ]` 勾成 `- [x]`(同 D4 的更新方式)。 +3. **更新議題描述**:把該 TODO 的 checkbox 由 `- [ ]` 勾成 `- [x]`(同 D3 的更新方式);勾選與留言回報依 `/jsc-shared:spec-todo-list` 的完成回報規則。 4. **留言進度到議題**(每完成一項就留言,不可累積到最後一次補): - `tea`:`tea comment --repo / <內容>` - `api`:`POST {base}/issues/{index}/comments`,body 以 UTF-8 JSON 檔帶入 @@ -150,8 +129,8 @@ argument-hint: "[--tool ] [--repo ] [--issues <編號,以 格式:`[--tool ] [--repo ] [--issues <編號,以逗號分隔>] [--host ] [--yes]` — 全部可省略;省略時依階段 A/B/C 詢問(已知資訊一律跳過詢問)。token 一律由環境變數 `GITEA_TOKEN` 提供。 -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:issues`,或 `/jsc-code:issues --tool api --repo plugins/code --issues 12,15 --yes` | -| Codex | `$issues`,或 `$issues --tool tea --repo plugins/code --issues 12`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「用 GITEA_TOKEN 讀 plugins/code 的 12、15 號議題,整理需求成 TODO 依影響範圍小到大排序、缺的補進議題描述,然後逐項實作、每完成一項留言進度」)自動觸發 | +各助理呼叫方式依 `/jsc-shared:spec-skill-invocation`(本 skill 為 `/jsc-code:issues`),常用範例: + +- Claude Code / Antigravity:`/jsc-code:issues`,或 `/jsc-code:issues --tool api --repo plugins/code --issues 12,15 --yes` +- Codex:`$issues`,或 `$issues --tool tea --repo plugins/code --issues 12`,或用 `/skills` 選單 +- OpenCode:描述需求(如「用 GITEA_TOKEN 讀 plugins/code 的 12、15 號議題,整理需求成 TODO 依影響範圍小到大排序、缺的補進議題描述,然後逐項實作、每完成一項留言進度」)自動觸發 diff --git a/skills/nuget/SKILL.md b/skills/nuget/SKILL.md index ead8a41..93fc0ef 100644 --- a/skills/nuget/SKILL.md +++ b/skills/nuget/SKILL.md @@ -8,12 +8,11 @@ argument-hint: "[] [--include-prerelease] [--no-dedupe] [-- 把 C# / .NET repo 內的 NuGet 套件依專案盤點、清理可安全移除的重複參考,然後逐一更新到最新可用版本。每個套件異動後都要驗證專案可以正常編譯;若最新版本失敗,改從目前版本之後的最小可用版本逐版升級,直到第一個編譯失敗版本為止,保留最後一個可編譯版本並記錄失敗點。 -## 共用規範(shared plugin,必要前置) +## 共用規範(必要前置) -執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到(shared plugin 未安裝)時,先詢問使用者是否安裝 shared plugin(`https://gitea.jsc.idv.tw/plugins/shared.git`),使用者不安裝則直接中斷本 skill**,不得只憑下方一行摘要繼續執行: - -- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼(套件 id、檔名、指令、版本號保留原文)。 -- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認。 +先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝, +依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。 +本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-gitea` ## 輸出規範(本 skill 特有) @@ -133,7 +132,7 @@ argument-hint: "[] [--include-prerelease] [--no-dedupe] [-- ### 5. 特殊檔案與版本管理 - `packages.lock.json` 存在時,更新套件後允許 lock file 跟著變更;回復失敗更新時也要回復 lock file。 -- `nuget.config` 有私有 feed 時,不輸出 credential;錯誤訊息若含 token / password 必須遮蔽。 +- `nuget.config` 有私有 feed 時,不輸出 credential;token / password 的機密保護依 `/jsc-shared:spec-gitea` 執行。 - `packages.config` 專案不套用 `dotnet add package` 流程;先回報這是舊格式,使用 `nuget.exe update` 或請使用者確認遷移策略。 - 多 target framework 專案以完整目標 build 為準,不只 build 單一 framework。 @@ -155,8 +154,4 @@ argument-hint: "[] [--include-prerelease] [--no-dedupe] [-- ## 呼叫方式 -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:nuget`,或 `/jsc-code:nuget MySolution.sln --include-prerelease` | -| Codex | `$nuget`,或 `$nuget src/App/App.csproj --build "dotnet build App.sln -c Release"` | -| OpenCode | 描述需求(如「把這個 .NET solution 的 NuGet 都更新到最新,先清掉 ProjectReference 已提供的重複套件,每更新一包就 build」)自動觸發 | +依 `/jsc-shared:spec-skill-invocation` 執行,以 `nuget` 作為本 skill 名稱代入其呼叫方式表格。 diff --git a/skills/review-resolve/SKILL.md b/skills/review-resolve/SKILL.md index 5c9bf73..3fa294a 100644 --- a/skills/review-resolve/SKILL.md +++ b/skills/review-resolve/SKILL.md @@ -18,14 +18,11 @@ argument-hint: "[--issue <編號|編號清單|all>] [--findings ` 存在** → 維持當前分支,直接進入 A4 更新到最新;不要只因目前分支有遠端同名分支就建立新工作分支,後續只有來源分支與 PR 目標分支相同時才需要開新分支。 -- **`origin/` 不存在**(當前分支為本地獨有,遠端無對應)→ 依序嘗試切換到後備分支: - 1. 切換前先確認工作區可安全切換(承接 A1 結果)。若有未提交變更導致切換失敗,停止並回報,請使用者先處理未提交變更;不可強制丟棄。 - 2. 若 `origin/develop` 存在 → 切換到 `develop`: - - ```bash - git switch develop 2>/dev/null || git switch -c develop --track origin/develop - ``` - - 3. 否則若 `origin/master` 存在 → 切換到 `master`: - - ```bash - git switch master 2>/dev/null || git switch -c master --track origin/master - ``` - - 4. **`develop` 與 `master` 在遠端都不存在** → 回報「當前分支不在遠端,且找不到 develop/master 後備分支」並停止,不臆測其他分支。 +- **`origin/` 不存在**(當前分支為本地獨有,遠端無對應)→ 切換前先確認工作區可安全切換(承接 A1 結果,若有未提交變更導致切換失敗,停止並回報,請使用者先處理未提交變更;不可強制丟棄),再依 `/jsc-shared:spec-git-safety`「develop → master 後備分支」切換到後備分支;develop/master 在遠端都不存在時,依該規範回報並停止,不臆測其他分支。 - 切換完成後,A4 先更新該後備分支到最新;是否建立新的工作分支交由 A5 依來源分支與 PR 目標分支是否相同判斷。切換到後備分支屬不可忽略的狀態變更,需在輸出中明確告知使用者已從原分支切換到哪一個分支。 ### A4. Pull(更新到最新) @@ -121,16 +104,13 @@ source_branch="$(git rev-parse --abbrev-ref HEAD)" ``` - **若後續會建立 PR(未帶 `--no-pr`)且尚未知道目標分支** → 先依階段 E1 的規則詢問目標分支,避免後續修復 commit 落在與 PR 目標同名的來源分支上才發現 source/base 相同。若帶 `--no-pr`,此階段不因缺少目標分支而詢問。 -- **若來源分支名稱與目標分支相同** → 不在該分支上直接修復/commit,也不可後續直接建立 head=base 的 PR。從目前已更新到最新的目標分支建立新的工作分支,後續階段(修復、commit、push、PR 的 `head`)都以新分支為準。 -- **若來源分支名稱與目標分支不同** → 維持目前分支,不另開分支;即使目前分支存在 `origin/`,也照常在目前分支處理。 -- 新分支名稱需可讀且避免覆蓋既有分支;預設格式: +- 是否需要建立新的工作分支、判斷方式(只有同名才開新分支)與命名避免覆蓋既有分支,依 `/jsc-shared:spec-git-safety`「工作分支選擇」與「建立分支不覆蓋」執行;本 skill 新工作分支預設命名: ```bash work_branch="ai-review-resolve/${source_branch}-$(date +%Y%m%d-%H%M%S)" git switch -c "${work_branch}" ``` -- 建立前若本地或遠端已存在同名分支,換一個時間戳或短 hash,不可覆蓋既有分支。 - 若因未提交變更導致建立/切換新分支失敗,停止並回報,請使用者先處理未提交變更;不可強制丟棄。 - 建立新分支屬不可忽略的狀態變更,需在輸出中明確告知使用者「因來源分支與目標分支同名,已從 `` 建立並切換到 ``」。 @@ -216,15 +196,7 @@ source_branch="$(git rev-parse --abbrev-ref HEAD)" 1. 解析 repo 座標(同 E3),token 由 `GITEA_TOKEN` 提供。 2. `all` 時先取所有 open 議題(分頁全取):`GET /repos///issues?state=open&type=issues`。 -3. 對每個選定議題讀本文與全部留言(分頁全取;輸出遮蔽 token): - - ```bash - curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ - "https:///api/v1/repos///issues/<編號>" - curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ - "https:///api/v1/repos///issues/<編號>/comments" - ``` - +3. 對每個選定議題讀取本文、全部留言與全部附件,依 `/jsc-shared:spec-issue-read` 執行(分頁完整讀取、不得只取部分留言;輸出遮蔽 token);議題標題/描述與附件內容作為整體背景脈絡納入判斷,本階段只從留言中解析下列嚴重問題留言格式。 4. 解析每則符合「嚴重問題留言」格式(action `severeCommentBody` 產出)的留言為一條 finding: ``` @@ -238,7 +210,7 @@ source_branch="$(git rev-parse --abbrev-ref HEAD)" ``` - 映射為 `{ reviewer, severity, file, startLine, endLine, problem, suggestion, suggestedCode }`,並記住**來源議題編號與留言 id**(供 B6 回寫);議題標題/描述作為整體背景脈絡納入判斷。 + 映射為 `{ reviewer, severity, file, startLine, endLine, problem, suggestion, suggestedCode }`,並記住**來源議題編號與留言 id**(供 B6 回寫)。 5. 某議題解析不到任何嚴重問題留言 → 記錄並略過該議題(`all`/多選時不因單一議題無問題而中斷整批),不臆測。 **B1c. 合併去重** @@ -325,76 +297,19 @@ source_branch="$(git rev-parse --abbrev-ref HEAD)" ## 階段 C:分析變更並分類提交(`--no-commit` 時略過) -### C1. 盤點工作區變更 +依 `/jsc-shared:spec-conventional-commit` 完整盤點工作區變更(含所有已暫存/未暫存/未追蹤/改名/刪除檔案,一律以 `git status --porcelain=v1 -uall` 為主,`git diff` 只作輔助核對)、依實際異動內容歸類為 9 種 commit type、產出提交計畫,再逐組精準 `git add -- <路徑>`(不用 `git add -A`/`git add .`)後分別 `git commit -m "type(範圍): 一句總結"`。 -```bash -git status --porcelain=v1 -uall -git diff # 已追蹤檔的未暫存變更 -git diff --staged # 已暫存變更 -git ls-files --others --exclude-standard # 未追蹤檔 -``` +本 skill 特有補充: -盤點時必須以 `git status --porcelain=v1 -uall` 為主,不可只看 `git diff`,因為那會漏掉未追蹤檔。盤點範圍必須包含**所有**變更:已修改檔、新增檔(`??` 未追蹤檔)、刪除檔、改名檔,以及已暫存與未暫存變更。`git diff`、`git diff --staged`、`git ls-files --others --exclude-standard` 只作為輔助核對。**無任何變更** → 回報「工作區無變更可提交」,跳過提交直接進入階段 D(D 會因無 commit 可 push 而跳到階段 E)。 - -### C2. 依異動內容歸類 conventional commit 類型 - -逐一檢視每個變更檔的**實際異動內容**(不只看路徑),歸入下列其一;**所有 `??` 未追蹤檔都必須納入分類**,不能因為它們不在 `git diff` 裡就漏掉: - -| type | 適用情境 | -| --- | --- | -| `feat` | 新增功能/新行為/新 API/新 skill | -| `fix` | 修正錯誤、修掉 bug(**階段 B 的 bug 修復多半歸此**) | -| `docs` | 只改文件(README、註解、`*.md`、說明) | -| `style` | 不影響邏輯的格式調整(排版、空白、分號、命名一致化) | -| `refactor` | 重構:不改外部行為的內部結構調整 | -| `perf` | 效能優化 | -| `test` | 新增或修改測試 | -| `chore` | 雜項:建置、設定、相依套件、版本號 bump、忽略檔等 | -| `revert` | 還原先前的提交 | - -- **同一檔案橫跨多型** → 以該檔**主要異動性質**歸類;難以拆分時就近歸入影響最大的一類,並在總結註記。 -- **階段 B 修復產生的變更**:依其性質歸類(修 bug→`fix`、補功能→`feat`、改文件→`docs`…)。`.gitea/ai-review/findings/` 目錄下 `*.json`(wrapper)與 `exclusions.json` 的問題狀態更新歸 `chore`。 -- **未追蹤新檔**:必須照實際內容歸入對應 type,必要時在提交前明確 `git add -- `,不可因為是新檔就略過。 - -### C3. 產出提交計畫 - -把變更檔依 type 分組,**每個 type 一個 commit**,輸出提交計畫;**所有變更項目都必須被追蹤並納入計畫,不能遺漏任何 `??` 未追蹤檔**。除非使用者要求確認或分組有不可忽略的取捨,否則直接提交: - -| 順序 | type(範圍) | commit 訊息 | 納入檔案 | -| --- | --- | --- | --- | - -- **commit 訊息格式**:`type(範圍): 一句總結` — - - `type`:上表英文類型。 - - `範圍`(括號內):**必須是這組異動實際牽涉的功能/模組/元件名稱**,而**不是**重述 type 的類別詞。 - 取名規則:優先沿用程式碼/專案中既有的識別名(檔名、模組名、skill 名、功能名,可中可英、保持與原碼一致),讓人一眼看出「改到哪個東西」。 - - ✅ 對:`feat(使用者登入)`、`fix(結帳流程)`、`perf(物件查詢)`、`docs(README)`、`refactor(訂單服務)`、`chore(plugin 版本)`、`feat(review-resolve)`。 - - ❌ 錯(只是重述 type,禁止):`feat(新增功能)`、`fix(修正錯誤)`、`perf(優化效能)`、`docs(文件)`、`chore(雜項)`。 - - 一組異動橫跨多個功能而無單一主體時,才退而取最貼近的上層範圍(例如多個 manifest → `plugin 設定`)。 - - `一句總結`:把這個 commit 內所有異動**總結成一句**繁體中文(簡短、聚焦做了什麼)。 - - 範例:`feat(使用者登入): 新增帳密登入與 token 簽發`、`fix(結帳流程): 修正空購物車導致的結帳例外`、`perf(物件查詢): 改用批次查詢降低 DB 往返`、`docs(README): 補上安裝與呼叫方式說明`、`chore(ai-review 狀態): 更新 findings 與 exclusions.json`。 -- **提交順序建議**:`fix`/`feat` 等核心異動在前,`docs`/`style`/`chore` 在後(純屬建議,可依相依性調整)。 -- **盤點要求**:提交計畫必須完整對應 C1 盤點出的所有變更項目,若有新檔或未追蹤檔,必須明確列入對應 commit,不能只根據 `git diff` 下結論。 - -### C4. 執行分類提交 - -對每組依序: - -```bash -git add -- <該組檔案...> # 僅暫存該組檔案,逐組精準 add -git commit -m "type(範圍): 一句總結" # 範圍=實際異動的功能/模組名 -``` - -- **逐組 add/commit**,確保每個 commit 只含該類異動;不要一次 `git add -A` 再混在一起。 -- 改名/刪除檔一併納入對應組的 `git add`(`git add -A -- <路徑>` 或明確列出)。 -- **所有 `??` 未追蹤檔都必須納入提交**;若是新檔,必要時要明確執行 `git add -- `,不可漏掉。 -- **不可只根據 `git diff` 判斷變更**;C1 盤點出的所有異動都必須反映到分類與提交,否則視為提交計畫不完整。 +- **階段 B 產生的變更歸類**:修 bug→`fix`、補功能→`feat`、改文件→`docs`…;`.gitea/ai-review/findings/` 目錄下 `*.json`(wrapper)與 `exclusions.json` 的問題狀態更新一律歸 `chore`。 +- **無任何變更** → 回報「工作區無變更可提交」,跳過提交直接進入階段 D(D 會因無 commit 可 push 而跳到階段 E)。 - commit 完成後進入階段 D(push)。`--no-commit` 時不進入後續階段;若工作區無變更而沒有產生任何新 commit,仍進入階段 D,由 D 判斷無 commit 可 push 後跳到階段 E。 --- ## 階段 D:Push 當前分支(`--no-commit` 時略過;無 commit 可 push 時跳到階段 E) -commit 完成後推送**當前分支**,依序嘗試三種方式,前者失敗才退到下一個: +commit 完成後推送**當前分支**,憑證選擇的三段式流程(認證管理器 → token push → 詢問使用者,前者失敗才退到下一個;token 全程遮蔽、用完即棄不落地)依 `/jsc-shared:spec-git-push` 執行。 推送前先記錄目前來源分支與其遠端基準,並確認是否有 commit 需要 push: @@ -409,24 +324,6 @@ git log --oneline "origin/${source_branch}..${source_branch}" # 領先遠端 - 若後續會建立 PR(未帶 `--no-pr`)且尚未知道目標分支,先依階段 E1 的規則詢問目標分支,這屬於不可忽略的必要決策。 - 若來源分支名稱與目標分支相同,**不要 push 原來源分支**;記下來源分支與遠端基準,直接進入階段 E,由 E2 建立新的 PR 來源分支、帶入 commit 後再 push 新分支。 -1. **認證管理器(優先)**:直接用 git 既有的 credential helper(如 Windows 的 `manager-core`): - - ```bash - git push -u origin "$(git rev-parse --abbrev-ref HEAD)" - ``` - -2. **失敗 → 改用 token push**:從環境變數讀 token,組帶 token 的遠端 URL 推送。**整個過程不可把含 token 的指令/URL 印出來**(用變數帶入、輸出時遮蔽): - - ```bash - # GITEA_TOKEN 來自環境變數;解析 origin 的 host/owner/repo - git push "https://oauth2:${GITEA_TOKEN}@//.git" \ - "$(git rev-parse --abbrev-ref HEAD)" - ``` - - (token 用完即棄,不寫進 git remote 設定、不落地。) - -3. **再失敗 → 詢問使用者要如何 push**:列出失敗原因(遮蔽 token),請使用者指示推送方式,**不可自行猜測**其他憑證或來源。 - push 成功後記下遠端分支名,進入階段 E。若因來源分支與目標分支相同而延後 push,記下原因並進入階段 E2 建立新分支。 --- @@ -437,9 +334,9 @@ push 成功後記下遠端分支名,進入階段 E。若因來源分支與目 - 帶 `--target <分支>` → 直接採用。 - 若階段 D 已為了避免 source/base 相同而詢問過目標分支,沿用該目標分支。 -- **否則必須詢問使用者目標分支**(可用 `git branch -r` 列出輔助選擇),**嚴禁臆測或預設**(不可自行假設 develop/main/master)。 +- **否則**依 `/jsc-shared:spec-pull-request`「目標分支不得臆測」詢問使用者(可用 `git branch -r` 列出輔助選擇),嚴禁臆測或預設。 -### E2. 若來源分支與目標分支相同,改建 PR 來源分支 +### E2. 若來源分支與目標分支相同,改建 PR 來源分支(cherry-pick 帶入已提交的異動) 先取得目前 PR 來源分支: @@ -447,7 +344,7 @@ push 成功後記下遠端分支名,進入階段 E。若因來源分支與目 git rev-parse --abbrev-ref HEAD ``` -若來源分支名稱與 `--target` 指定(或使用者選定)的目標分支相同,**不可直接建立 head=base 的 PR,也不可先把原來源分支 push 到目標分支**。改用下列流程建立新的 PR 來源分支,並把原來源分支的 commit 帶入後再開 PR: +若來源分支名稱與 `--target` 指定(或使用者選定)的目標分支相同,依 `/jsc-shared:spec-git-safety`「工作分支選擇」,**不可直接建立 head=base 的 PR,也不可先把原來源分支 push 到目標分支**。此處的情境是修復與 commit 已經發生在這個(事後才發現同名的)來源分支上,因此改用下列流程建立新的 PR 來源分支,把原來源分支的 commit cherry-pick 帶入後再開 PR: 1. 先記錄原來源分支名稱與要帶入的 commit 清單: @@ -460,7 +357,7 @@ git rev-parse --abbrev-ref HEAD - 若沒有任何 commit 可帶入,回報「來源分支沒有領先目標分支的 commit」,停止開 PR。 - 若階段 D 已記錄來源分支的遠端基準,使用該基準判斷要帶入的 commit,避免把原來源分支直接推進目標分支。 -2. 從目標分支的遠端基準建立新分支。新分支名稱需可讀且避免覆蓋既有分支,例如: +2. 從目標分支的遠端基準建立新分支,命名依 `/jsc-shared:spec-git-safety`「建立分支不覆蓋」避免覆蓋既有分支,例如: ```bash git switch -c "ai-review-resolve/<短時間戳>" "origin/${target}" @@ -472,60 +369,27 @@ git rev-parse --abbrev-ref HEAD git cherry-pick "origin/${target}..${source_branch}" ``` - - cherry-pick 發生衝突時,先告知使用者,再依專案脈絡嘗試最小合理解衝突。 - - 可安全解決的衝突:移除衝突標記、`git add -- <檔案...>`,再繼續 `git cherry-pick --continue`。 - - 無法安全判斷的衝突:停止處理,列出衝突檔案與需要使用者決策的點;不要硬選任一邊。 + 衝突時依 `/jsc-shared:spec-git-safety`「保守解衝突」處理(先告知使用者、依專案脈絡嘗試最小合理整合;可安全解決者移除衝突標記、`git add -- <檔案...>` 後 `git cherry-pick --continue`;無法安全判斷者停止並列出決策點,不硬選任一邊)。 -4. 將新分支 push 到遠端,使用階段 D 的同一套 push 憑證策略,並把後續 PR 的 `head` 改為這個新分支。 +4. 將新分支 push 到遠端,憑證策略依 `/jsc-shared:spec-git-push`(同階段 D),並把後續 PR 的 `head` 改為這個新分支。 若來源分支與目標分支不同,直接以目前分支作為 PR 的 `head`。 ### E3. 解析 repo 座標 -從 `git remote get-url origin` 解析出 **host/owner/repo**(例:`https://gitea.jsc.idv.tw/plugins/code-review.git` → host=`gitea.jsc.idv.tw`、owner=`plugins`、repo=`code-review`)。 +依 `/jsc-shared:spec-pull-request`「解析 origin 座標」從 `git remote get-url origin` 解析出 host/owner/repo。 ### E4. 決定 PR 描述形式(完整版/簡單版/使用者輸入) -依 `--pr-desc` 三選一;若未提供則預設使用 `full`,不要為描述形式中斷詢問: - -- **完整版(`full`)**:**重新分析並總結** `git diff ...`(比對 source 自分岔點以來的變更),整理成結構化繁體中文說明 —— 變更摘要、影響範圍、重點檔案/模組、風險或注意事項。不是貼原始 diff,而是「人讀得懂的總結」。 -- **簡單版(`simple`)**:直接把本分支領先 target 的 commit 訊息**逐條列出**: - - ```bash - git log --oneline ".." - ``` - - 以條列呈現每行 commit 訊息。 -- **使用者輸入**:採用使用者提供的描述文字。 - -PR **標題**預設取一句總結(可用首個 feat/fix commit 或分支用途);使用者另有指定則從之。 - -產生 PR body 時必須使用實際換行與 Markdown 內容,不可把跳脫字串當作正文送出。若用 shell / `jq` 組 JSON,應以 UTF-8 暫存檔、heredoc、`printf` 或 `jq --rawfile` 帶入內容;避免讓 PR 顯示成 `## Commit\n\n- ...` 這類字面 `\n`。 +依 `--pr-desc` 三選一,各形式的內容與產生方式依 `/jsc-shared:spec-pull-request`「PR 描述模式」執行;未提供時預設 `full`,不要為描述形式中斷詢問。PR **標題**預設取一句總結(可用首個 feat/fix commit 或分支用途);使用者另有指定則從之。 ### E5. 呼叫 Gitea API 建立 PR(使用 token) -token 從環境變數讀取,呼叫 Gitea 建立 PR: +依 `/jsc-shared:spec-pull-request`「已有相同 head→base 的 open PR 時沿用不重開」與「建立 PR:body 一律用 UTF-8 檔案帶入」執行:先查詢目標分支是否已有相同來源的 open PR,有則沿用不重複建立;沒有才呼叫 `POST /pulls`,body 以 UTF-8 檔帶入、換行為實際換行;成功取回應中的 PR 連結/編號回報使用者,失敗時顯示錯誤訊息(先依 `/jsc-shared:spec-gitea` 遮蔽 token)供排查。 -```bash -# 不可 echo 含 token 的指令;body 以 UTF-8 JSON 檔帶入,輸出時遮蔽 token -# PR 描述內的換行必須是實際換行,不可使用字面 \n -curl -sS -X POST \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -H "Content-Type: application/json" \ - "https:///api/v1/repos///pulls" \ - --data @body.json -``` +### E6. 完成通知+清除對話內文(重要:可能含 token) -- **成功**:取回應中的 PR 連結/編號回報使用者。 -- **失敗**:顯示 API 回應的錯誤訊息供排查(**先遮蔽 token**)。常見錯誤:目標分支不存在、已有相同 head→base 的 PR、token 權限不足。 - -### E6. 完成通知 + 清除對話內文(重要:可能含 token) - -1. **通知使用者**:push 結果、PR 連結/編號、PR 描述採用哪種形式。 -2. **清除 AI 助理對話內文**:因為 push/API 過程可能使對話內文殘留 gitea token,**完成後務必清除對話內文/上下文**以免外洩: - - Claude Code:提示使用者執行 `/clear`(或依當前助理對等指令清空對話)。 - - 其他助理:執行各自清除對話/上下文的方式。 - - 在清除前,請再次確認輸出與 log 中沒有任何明文 token。 +依 `/jsc-shared:spec-pull-request`「完成後提醒清除對話內文」通知使用者 push 結果、PR 連結/編號與描述形式;因 push/API 過程可能使對話內文殘留 gitea token,完成後依 `/jsc-shared:spec-gitea`「Token 機密保護」提醒清除對話內文/上下文,清除前再次確認輸出與 log 中沒有任何明文 token。 --- @@ -543,11 +407,11 @@ curl -sS -X POST \ ## 呼叫方式 -格式:`[--findings <路徑>] [--target <目標分支>] [--pr-desc ] [--no-commit] [--no-pr] [--yes]` — +各助理呼叫一個 skill 的方式依 `/jsc-shared:spec-skill-invocation`;本 skill 參數格式與範例: + +格式:`[--issue <編號|編號清單|all>] [--findings <路徑>] [--target <目標分支>] [--pr-desc ] [--no-commit] [--no-pr] [--yes]` — 除 `--target` 外皆可省略(findings 一律讀目錄 `.gitea/ai-review/findings/` 下所有 wrapper `*.json` 與 `findings.json`;`--issue` 無/單選/多選/全部 額外從 Gitea 議題取問題,與 findings 合併去重;目標分支省略時必問、不猜測)。token 一律由環境變數(如 `GITEA_TOKEN`)提供。 -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:review-resolve`,或 `/jsc-code:review-resolve --target develop --pr-desc full --yes`、`/jsc-code:review-resolve --issue 12,15 --target master`、`/jsc-code:review-resolve --issue all --target master`、`/jsc-code:review-resolve --no-pr`、`/jsc-code:review-resolve --no-commit` | -| Codex | `$review-resolve`,或 `$review-resolve --target develop --pr-desc full --yes`、`$review-resolve --issue all --target master`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「讀 .gitea/ai-review/findings/ 目錄與 findings.json 的 wrapper findings,再併入議題 #12、#15 的 code review 問題去重後依嚴重度逐條處理,更新 findings/exclusions,把工作區變更依 conventional commit 分類提交,push 後對 develop 發 PR(完整版描述)」)自動觸發 | +- Claude Code / Antigravity:`/jsc-code:review-resolve`,或 `/jsc-code:review-resolve --target develop --pr-desc full --yes`、`/jsc-code:review-resolve --issue 12,15 --target master`、`/jsc-code:review-resolve --issue all --target master`、`/jsc-code:review-resolve --no-pr`、`/jsc-code:review-resolve --no-commit` +- Codex:`$review-resolve`,或 `$review-resolve --target develop --pr-desc full --yes`、`$review-resolve --issue all --target master`,或用 `/skills` 選單 +- OpenCode:描述需求(如「讀 .gitea/ai-review/findings/ 目錄與 findings.json 的 wrapper findings,再併入議題 #12、#15 的 code review 問題去重後依嚴重度逐條處理,更新 findings/exclusions,把工作區變更依 conventional commit 分類提交,push 後對 develop 發 PR(完整版描述)」)自動觸發 diff --git a/skills/sync/SKILL.md b/skills/sync/SKILL.md index 65a9610..0838a3b 100644 --- a/skills/sync/SKILL.md +++ b/skills/sync/SKILL.md @@ -18,14 +18,11 @@ argument-hint: "[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] --- -## 共用規範(shared plugin,必要前置) +## 共用規範(必要前置) -執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到(shared plugin 未安裝)時,先詢問使用者是否安裝 shared plugin(`https://gitea.jsc.idv.tw/plugins/shared.git`),使用者不安裝則直接中斷本 skill**,不得只憑下方一行摘要繼續執行: - -- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼。 -- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認。 -- `/jsc-shared:spec-gitea`:`GITEA_TOKEN` 機密保護(不 echo、遮蔽、clone 後還原乾淨 origin)、API 分頁完整讀取、host 決定順序。 -- `/jsc-shared:spec-git-safety`:不破壞既有工作(未提交變更不強切、絕不 `reset --hard`/`clean`)、develop → master 後備、`pull --ff-only`。 +先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝, +依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。 +本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-gitea`、`spec-git-safety`、`spec-ask-user`、`spec-skill-invocation` 本 skill 特有補充: @@ -71,18 +68,7 @@ argument-hint: "[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] ### B1. 分頁取回可讀專案 -以 token 呼叫 `GET /api/v1/user/repos`(回傳 token 對應使用者**有權限存取**的 repo:自有、組織成員、協作者),**逐頁抓到取回筆數不足一頁為止**(每頁上限以 API 為準,常見 `limit=50`): - -```bash -# 不可印出含 token 的指令;token 以 Header 帶入,輸出時遮蔽 -page=1 -curl -sS \ - -H "Authorization: token ${GITEA_TOKEN}" \ - "https:///api/v1/user/repos?page=${page}&limit=50" -``` - -- 持續累加 `page` 直到某頁回傳筆數 `< limit`(或回空陣列)為止,確保**取得全部**而非只第一頁。 -- API 失敗(401/403/網路錯誤)→ 回報錯誤(**遮蔽 token**)並停止;401/403 多半是 token 失效或權限不足。 +依 `/jsc-shared:spec-gitea`「API 呼叫慣例」的分頁完整讀取與 401/403 錯誤處理方式,以 token 呼叫 `GET /api/v1/user/repos`(回傳 token 對應使用者**有權限存取**的 repo:自有、組織成員、協作者),持續累加 `page` 直到某頁回傳筆數不足一頁(或回空陣列)為止,確保**取得全部**而非只第一頁。 ### B2. 篩選與正規化 @@ -107,11 +93,8 @@ curl -sS \ 選擇方式: -- **帶 `--owner` 參數** → 直接採用其中的擁有者,**跳過詢問**;清單中沒有的擁有者回報並略過。 -- **未帶 `--owner`** → 這是**不可忽略的必要決策**,必須讓使用者**多選**: - - 擁有者數量 ≤ 4 時,可用助理的多選提問元件(如 Claude Code 的多選問題)。 - - 擁有者較多時,請使用者直接以**編號或擁有者名稱**回覆(支援多個,以逗號或空白分隔),並支援回覆 `all`/`全部` 代表全選。 - - 解析使用者回覆為擁有者集合;無法對應的輸入請回報並請使用者重選,**不臆測**。 +- **帶 `--owner` 參數** → 依 `/jsc-shared:spec-ask-user`「已從其他管道得知答案時要跳過詢問」,直接採用其中的擁有者,**跳過詢問**;清單中沒有的擁有者回報並略過。 +- **未帶 `--owner`** → 依 `/jsc-shared:spec-ask-user` 決定呈現方式(擁有者數量 ≤ 4 用 `AskUserQuestion` 多選,超過 4 改文字列出並支援編號/名稱回覆、`all`/`全部` 代表全選),這是**不可忽略的必要決策**;解析使用者回覆為擁有者集合,無法對應的輸入回報並請使用者重選,**不臆測**。 選定後輸出「將同步的擁有者:…」再進入階段 D。 @@ -143,13 +126,11 @@ mkdir -p "<根目錄>/<擁有者>" ### E2. 專案不存在 → clone -- **HTTPS(預設)**:以 token 組 clone URL,**用變數帶入、不可印出**;clone 完成後**還原乾淨 origin**: +- **HTTPS(預設)**:依 `/jsc-shared:spec-gitea`「Token 機密保護」以 token 組 clone URL(用變數帶入、不可印出),clone 完成後還原成不含 token 的乾淨 origin: ```bash dest="<根目錄>/<擁有者>/<專案名>" - # GITEA_TOKEN 來自環境變數;clone_url 形如 https:////.git git clone "https://oauth2:${GITEA_TOKEN}@//.git" "${dest}" - # 還原成不含 token 的乾淨 URL,避免 token 落地在 .git/config git -C "${dest}" remote set-url origin "https:////.git" ``` @@ -209,11 +190,11 @@ git -C "${dest}" fetch --all --prune ## 呼叫方式 -格式:`[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host ] [--include-forks] [--ssh] [--yes]` — +依 `/jsc-shared:spec-skill-invocation` 的呼叫方式表格;格式:`[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host ] [--include-forks] [--ssh] [--yes]` — 全部可省略(根目錄預設家目錄;未帶 `--owner` 時於階段 C 詢問多選)。token 一律由環境變數(如 `GITEA_TOKEN`)提供,不寫死、不印出。 -| 助理 | 呼叫 | -| --- | --- | -| Claude Code / Antigravity | `/jsc-code:sync`,或 `/jsc-code:sync --owner plugins --target-dir ~/work`、`/jsc-code:sync --include-forks --ssh` | -| Codex | `$sync`,或 `$sync --owner plugins --target-dir ~/work`,或用 `/skills` 選單 | -| OpenCode | 描述需求(如「用 GITEA_TOKEN 取得我有讀取權限的所有 Gitea 專案,依擁有者分組讓我多選,把選定擁有者的專案 clone 到家目錄;已存在的切到 develop/master 更新到最新」)自動觸發 | +範例: + +- Claude Code / Antigravity:`/jsc-code:sync`,或 `/jsc-code:sync --owner plugins --target-dir ~/work`、`/jsc-code:sync --include-forks --ssh` +- Codex:`$sync`,或 `$sync --owner plugins --target-dir ~/work`,或用 `/skills` 選單 +- OpenCode:描述需求(如「用 GITEA_TOKEN 取得我有讀取權限的所有 Gitea 專案,依擁有者分組讓我多選,把選定擁有者的專案 clone 到家目錄;已存在的切到 develop/master 更新到最新」)自動觸發 diff --git a/skills/target/SKILL.md b/skills/target/SKILL.md index a4539f6..afc9543 100644 --- a/skills/target/SKILL.md +++ b/skills/target/SKILL.md @@ -19,15 +19,11 @@ argument-hint: "[--schedule on|off] [--run] [--project-dir <專案根目錄>] [- --- -## 共用規範(shared plugin,必要前置) +## 共用規範(必要前置) -執行本 skill 前,先以 Skill 工具載入下列共用規範並全程遵守;**任一載入不到(shared plugin 未安裝)時,先詢問使用者是否安裝 shared plugin(`https://gitea.jsc.idv.tw/plugins/shared.git`),使用者不安裝則直接中斷本 skill**,不得只憑下方一行摘要繼續執行: - -- `/jsc-shared:spec-output`:繁體中文(台灣用語)、UTF-8(不含 BOM)無亂碼(含 `TARGET.md` 回寫、commit 訊息、PR 標題/描述)、表格呈現。 -- `/jsc-shared:spec-execution`:自動執行原則(必要決策才中斷)、不臆測/需人工確認。 -- `/jsc-shared:spec-git-safety`:不破壞既有工作(絕不 `reset --hard`/`clean`)、`pull --ff-only`、保守解衝突、新分支不覆蓋既有分支。 -- `/jsc-shared:spec-gitea`:`GITEA_TOKEN` 機密保護(不 echo、遮蔽 `***`)、API body 以 UTF-8 JSON 檔帶入。 -- `/jsc-shared:spec-time-log`:Asia/Taipei `yyyy/MM/dd HH:mm:ss` 時間格式、`[時間][階段][等級]: 訊息` log 格式。 +先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝, +依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。 +本 skill 需要的規範:`spec-output`、`spec-execution`、`spec-git-safety`、`spec-gitea`、`spec-time-log`、`spec-todo-list`、`spec-conventional-commit`、`spec-git-push`、`spec-pull-request`、`spec-skill-invocation`、`spec-model` 本 skill 特有補充: @@ -45,7 +41,7 @@ argument-hint: "[--schedule on|off] [--run] [--project-dir <專案根目錄>] [- - `--run`:立即執行一次 `TARGET.md`(排程條目帶的就是這個)。 - `--project-dir <專案根目錄>`:目標專案根目錄,**省略時取目前工作目錄**(會先 `git rev-parse --show-toplevel` 正規化成 repo 根)。`TARGET.md` 固定讀**專案根目錄**的那一份。 - `--cli `:排程條目要用哪個助理的 headless 指令。省略時用**目前正在執行本 skill 的助理**;判斷不出來且未帶此參數 → 詢問,不猜測。 -- `--base `:PR 的目標分支。**省略時自動取遠端預設分支**(見 F1),取不到才詢問。 +- `--base `:PR 的目標分支。**省略時自動取遠端預設分支**(見階段 F),取不到才詢問。 - `--no-pr`:執行到 push 為止,不開 PR。 - `--yes`:全自動,不做確認式詢問(必要決策仍會中斷)。 @@ -66,15 +62,16 @@ argument-hint: "[--schedule on|off] [--run] [--project-dir <專案根目錄>] [- - [x] 移除已停用的舊設定檔(2026/08/02 23:41:07 完成) ``` +checklist 語法、已完成略過不重做、子項目連動、標題/說明文字原樣保留、禁止憑空編造等格式規則依 `/jsc-shared:spec-todo-list` 執行。 + +本 skill 特有: + | 規則 | 說明 | | --- | --- | -| 待辦 | `- [ ]` 未勾選的項目,**由上而下**依序處理 | -| 已完成 | `- [x]` 直接略過,不重做 | -| 子項目 | 縮排的 `- [ ]` 視為上層項目的子步驟,隨上層一起處理;子項目全部完成才勾選上層 | -| 標題/說明文字 | 原樣保留,只當作項目的背景脈絡,不改寫 | +| 待辦 | `- [ ]` 未勾選的項目,**由上而下**依檔案既有順序處理(與 `spec-todo-list` 建立議題 TODO 時「依影響範圍排序」的情境不同,此處沿用 `TARGET.md` 本身的行順序) | | 檔案不存在 | `--run` 視為「無待辦」正常結束(不建檔、不報錯);`--schedule on` 則提醒尚無 `TARGET.md`,非 `--yes` 時詢問是否仍要建立排程 | -完成一項後就地改成 `- [x]`,並在該行**行尾**補上 `( 完成)`(Asia/Taipei)。無法安全完成的項目**保持未勾選**,並在該行下方以縮排補一行 `> ⏭️ 待人工處理:<原因>`(已有同樣註記則更新而非重複新增)。 +完成一項後就地改成 `- [x]`,並在該行**行尾**補上 `( 完成)`(Asia/Taipei,時間格式依 `/jsc-shared:spec-time-log`)。無法安全完成的項目**保持未勾選**,並在該行下方以縮排補一行 `> ⏭️ 待人工處理:<原因>`(已有同樣註記則更新而非重複新增)。 --- @@ -189,32 +186,22 @@ git status --porcelain git rev-parse --abbrev-ref HEAD ``` -- **工作區有未提交變更** → **停止**並回報(排程情境寫入 log)。理由:本 skill 結尾會分類提交所有變更,混入使用者未完成的工作會把它一起提交上去。依 `/jsc-shared:spec-git-safety`,**不得** stash、`reset --hard` 或 `clean` 來清場。 +- **工作區有未提交變更** → **停止**並回報(排程情境寫入 log)。理由:本 skill 結尾會分類提交所有變更,混入使用者未完成的工作會把它一起提交上去。依 `/jsc-shared:spec-git-safety`「不破壞既有工作」,不得 stash、`reset --hard` 或 `clean` 來清場。 - **detached HEAD** → 停止並回報。 ### A2. Fetch 與判定遠端預設分支 ```bash git fetch --all --prune -git symbolic-ref --quiet refs/remotes/origin/HEAD # → refs/remotes/origin/<預設分支> ``` -取不到時退而用 `git remote show origin`(找 `HEAD branch:` 那行);`--base` 有帶則直接採用。兩者都不成立 → 停止並詢問,不臆測 `master`/`main`。 +依 `/jsc-shared:spec-git-safety`「工作分支選擇」判定遠端預設分支(`git symbolic-ref refs/remotes/origin/HEAD` → 取不到退而 `git remote show origin` → 兩者皆無則停止詢問,不臆測 `master`/`main`)。**本 skill 特有**:帶 `--base` 時直接採用 `--base` 的值,不再另行判定。 ### A3. 切到 `develop`(不存在就從預設分支建立) -```bash -if git rev-parse --verify --quiet origin/develop >/dev/null; then - git switch develop 2>/dev/null || git switch -c develop --track origin/develop - git pull --ff-only -else - git switch -c develop "origin/<預設分支>" # 遠端沒有 develop → 從預設分支開一條新的 -fi -``` +依 `/jsc-shared:spec-git-safety`「develop → master 後備分支」與「工作分支選擇」處理:`origin/develop` 存在則切換並 `git pull --ff-only`(分岔失敗 → 停止並回報,不做 merge/rebase 猜測);不存在則從 A2 判定出的遠端預設分支建立 `develop`。 -- `pull --ff-only` 失敗(分岔)→ 停止並回報,交由使用者處理,不做 merge/rebase 猜測。 -- 本地已有 `develop` 但遠端沒有 → 沿用本地那條,**不覆蓋、不重建**;並在回報中說明它還沒推上遠端。 -- 新建 develop 屬不可忽略的狀態變更,必須在回報與 log 中明講「遠端沒有 develop,已從 `<預設分支>` 建立」。 +本 skill 特有:**本地已有 `develop` 但遠端沒有** → 沿用本地那條,**不覆蓋、不重建**,並在回報中說明它還沒推上遠端(`git switch develop 2>/dev/null || git switch -c develop --track origin/develop` 的寫法即可自然落在此情形)。新建 `develop` 屬不可忽略的狀態變更,必須在回報與 log 中明講「遠端沒有 develop,已從 `<預設分支>` 建立」。 --- @@ -228,6 +215,8 @@ fi | # | 項目 | 子項目數 | 備註 | | --- | --- | --- | --- | +若 `TARGET.md` 帶有 `model:` frontmatter,依 `/jsc-shared:spec-model` 的『讀到帶 `model:` frontmatter 的清單檔時的檢查義務』先做模型檢查。 + --- ## 階段 C:逐項實作並回寫 `TARGET.md` @@ -246,77 +235,37 @@ fi ## 階段 D:分類提交 -1. 盤點**所有**變更(含未追蹤檔): +依 `/jsc-shared:spec-conventional-commit` 執行:以 `git status --porcelain=v1 -uall` 完整盤點所有變更(含未追蹤檔)、依實際異動內容歸入 9 種 commit 類型(每個 type 各一個 commit,訊息格式 `type(範圍): 一句總結`,範圍不得重述 type 本身)、逐組精準 `git add -- <該組檔案...>` 後分別 commit(不用 `git add -A`)。 - ```bash - git status --porcelain=v1 -uall - ``` +本 skill 特有: -2. 依實際異動內容歸入 conventional commit 類型(`feat`/`fix`/`docs`/`style`/`refactor`/`perf`/`test`/`chore`/`revert`),**每個 type 各一個 commit**。 -3. commit 訊息格式 `type(範圍): 一句總結`,**括號內是實際異動的功能/模組名**,不是重述 type: - - ✅ `feat(使用者服務): 補上帳號建立與停用的單元測試`、`docs(README): 更新安裝章節的 CLI 指令` - - ❌ `feat(新增功能)`、`docs(文件)` - - `TARGET.md` 本身的勾選更新歸 `chore`,訊息用 `chore(TARGET): 更新每日待辦完成狀態`。 -4. 逐組精準 `git add -- <該組檔案...>` 後 commit,不用 `git add -A` 一次混提。 -5. 無任何變更(例如所有項目都待人工處理,只有 `TARGET.md` 的註記異動)→ 仍照常提交 `TARGET.md` 的異動;連 `TARGET.md` 都沒變則跳過 D、E、F,直接進入總結。 - -> 分類與訊息規則與 `/jsc-code:review-resolve` 階段 C 一致,細節可參照該 skill。 +- `TARGET.md` 本身的勾選更新歸 `chore`,訊息固定用 `chore(TARGET): 更新每日待辦完成狀態`。 +- 無任何變更(例如所有項目都待人工處理,只有 `TARGET.md` 的註記異動)→ 仍照常提交 `TARGET.md` 的異動;連 `TARGET.md` 都沒變則跳過 D、E、F,直接進入總結。 --- ## 階段 E:Push `develop` +依 `/jsc-shared:spec-git-push` 的三段式流程(認證管理器 → token push → 詢問使用者,前者失敗才換下一個,token 全程遮蔽且不寫進 remote 設定)推送 `develop`。推送前先確認是否有領先遠端的 commit: + ```bash git log --oneline "origin/develop..develop" 2>/dev/null # 領先遠端的 commit;遠端無 develop 時視為全部要推 ``` -無 commit 可推 → 跳過 push,直接進入階段 F(既有遠端分支仍可開 PR)。否則依序嘗試,前者失敗才換下一個: - -1. **認證管理器**:`git push -u origin develop` -2. **token**:從環境變數帶入,指令與輸出**全程遮蔽 token、不寫進 remote 設定**: - - ```bash - git push "https://oauth2:${GITEA_TOKEN}@//.git" develop - ``` - -3. **都失敗** → 停止並回報失敗原因(先遮蔽 token)。排程情境寫入 log,等使用者處理,不猜測其他憑證。 +本 skill 特有:無 commit 可推 → 跳過 push,直接進入階段 F(既有遠端分支仍可開 PR);三段皆失敗 → 停止並回報失敗原因(先遮蔽 token),排程情境寫入 log,等使用者處理,不猜測其他憑證。 --- ## 階段 F:對預設分支發 PR(`--no-pr` 時略過) -### F1. 目標分支 +依 `/jsc-shared:spec-pull-request` 執行:目標分支不臆測 → 解析 origin 座標(host/owner/repo)→ 先查有沒有相同 head=`develop` → base=`<目標分支>` 的 open PR,有則沿用不重開(今天新推的 commit 會自動出現在該 PR,可視情況補一則今日進度留言)→ 沒有才以 UTF-8 JSON 檔帶入 body 建立新 PR(換行用實際換行,不可送出字面 `\n`)→ 失敗回報 API 錯誤訊息(先依 `/jsc-shared:spec-gitea` 遮蔽 token)→ 完成後提醒清除對話內文。 -`--base` 有帶則用它,否則用 A2 判定出的**遠端預設分支**。若預設分支就是 `develop`(等於 head=base)→ 不開 PR,回報原因即可。 - -### F2. 先查有沒有現成的 open PR(每天跑,不可重複開) - -```bash -curl -sS -H "Authorization: token ${GITEA_TOKEN}" \ - "https:///api/v1/repos///pulls?state=open&base=<預設分支>" -``` - -已存在 `head=develop` → `base=<預設分支>` 的 open PR → **沿用它**(今天新推的 commit 會自動出現在該 PR),回報既有 PR 連結,並視情況在該 PR 補一則今日進度留言;**不重複建立**。 - -### F3. 建立 PR - -repo 座標由 `git remote get-url origin` 解析(host/owner/repo)。body 以 UTF-8 JSON 檔帶入,換行用實際換行、不可送出字面 `\n`: - -```bash -curl -sS -X POST \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -H "Content-Type: application/json" \ - "https:///api/v1/repos///pulls" \ - --data @body.json -``` +本 skill 特有: +- **目標分支**:`--base` 有帶則用它,否則用 A2 判定出的遠端預設分支;若該目標分支就是 `develop`(等於 head=base)→ 不開 PR,回報原因即可。 - **標題**:`chore(TARGET): 每日待辦`(有明確主軸時可改成該主軸的一句總結)。 - **描述**:本次完成的項目清單(✅/⏭️ 各自列出+待人工處理原因)、變更摘要與影響範圍、本次的 commit 一覽。 -- 失敗 → 回報 API 錯誤訊息(先遮蔽 token)。常見原因:目標分支不存在、已有相同 head→base 的 PR(回到 F2 沿用)、token 權限不足。 - -### F4. 收尾 - -push/API 過程可能讓 token 殘留在對話內文;**互動情境**完成後提醒使用者清除對話(Claude Code 用 `/clear`)。排程情境只寫 log,不輸出 token。 +- **收尾**:push/API 過程可能讓 token 殘留在對話內文;**互動情境**完成後提醒使用者清除對話(Claude Code 用 `/clear`)。**排程情境**沒有互動對話,只寫 log,不輸出 token。 --- @@ -335,6 +284,8 @@ push/API 過程可能讓 token 殘留在對話內文;**互動情境**完成 ## 呼叫方式 +依 `/jsc-shared:spec-skill-invocation` 的統一呼叫方式,本 skill 的實際參數格式與範例: + | 助理 | 呼叫 | | --- | --- | | Claude Code / Antigravity | `/jsc-code:target --schedule on`、`/jsc-code:target --schedule off`、`/jsc-code:target --run --yes`、`/jsc-code:target --schedule on --run --project-dir ~/work/app` | -- 2.53.0