From 40e283294c502e84de693e15dbe60107fe0d9b71 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Fri, 26 Jun 2026 09:44:28 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(code-review-sync):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E4=BE=9D=E6=93=81=E6=9C=89=E8=80=85=E6=89=B9=E6=AC=A1?= =?UTF-8?q?=20clone=EF=BC=8F=E6=9B=B4=E6=96=B0=20Gitea=20=E5=B0=88?= =?UTF-8?q?=E6=A1=88=E7=9A=84=20skill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/code-review-sync/SKILL.md | 228 +++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 skills/code-review-sync/SKILL.md diff --git a/skills/code-review-sync/SKILL.md b/skills/code-review-sync/SKILL.md new file mode 100644 index 0000000..52ad693 --- /dev/null +++ b/skills/code-review-sync/SKILL.md @@ -0,0 +1,228 @@ +--- +name: code-review-sync +description: 透過 `GITEA_TOKEN` 取得使用者有讀取權限的所有 Gitea 專案,依擁有者(owner)分組後列出讓使用者多選,再把選定擁有者底下有讀取權限的專案,於目標目錄(預設家目錄)依擁有者建立資料夾並逐一 clone;若專案已存在,則切換到 develop(再退而 master)並更新到最新。當使用者說同步 Gitea 專案、批次 clone、把某些擁有者的 repo 全抓下來、依擁有者分類 clone、批次更新本機專案到最新時觸發。不適用於:建立/刪除遠端 repo、單一專案的 clone(直接用 git 即可)、非 Gitea 的來源,或需要 push/改動遠端的操作。 +argument-hint: "[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host ] [--include-forks] [--ssh] [--yes]" +--- + +# code-review-sync — 依擁有者批次 clone/更新 Gitea 專案 + +五階段 skill:先做**前置設定**(解析 token、gitea 主機、目標根目錄),再**取得使用者有讀取權限的所有專案並依擁有者分組**,接著**列出所有擁有者讓使用者多選**,然後**列出選定擁有者底下有讀取權限的專案**,最後在目標根目錄**依擁有者建立資料夾並逐一 clone**;專案已存在時改為**切換到 develop(再退而 master)並更新到最新**。 + +| 階段 | 動作 | +| --- | --- | +| A. 前置設定 | 確認 `GITEA_TOKEN` → 決定 gitea 主機(`--host`/`$GITEA_HOST`/目前 repo 的 origin/詢問)→ 決定目標根目錄(`--target-dir`,預設家目錄) | +| B. 取得可讀專案 | 以 token 呼叫 Gitea API 分頁取回使用者有讀取(`pull`)權限的所有專案 → 依 `owner.login` 分組 | +| C. 選擇擁有者 | 列出所有擁有者(含各自專案數)→ 讓使用者**多選**(`--owner` 已指定則沿用,不再詢問) | +| D. 列出選定專案 | 列出選定擁有者底下所有有讀取權限的專案(含是否已存在於本機) | +| E. Clone/更新 | 依擁有者建立資料夾 → 不存在則 clone;已存在則 `fetch` → 切到 develop(再退而 master)→ `pull` 更新到最新 | + +--- + +## 輸出規範(務必遵守) + +- **語言**:所有面向使用者的輸出(擁有者清單、專案清單、進度、總結、反問)一律使用**繁體中文(台灣用語)**;僅識別字、檔名、git/curl 指令、API 路徑等技術標識保留原文,**不可**使用簡體字。 +- **編碼無亂碼**:凡輸出含繁體中文、全形標點、emoji,一律 **UTF-8(不含 BOM)**,不得出現問號方框或錯碼。 +- **Token 機密保護(極重要)**:gitea token 一律**從環境變數讀取**(如 `$GITEA_TOKEN`),**絕不**寫死在 skill、log 或任何輸出;**不可** echo 含 token 的指令或 URL。所有顯示給使用者的指令/錯誤訊息都要**遮蔽 token**(如以 `***` 取代)。clone 時帶 token 的 URL 用變數帶入、**不可印出**,且 clone 完成後要把 origin 還原成不含 token 的乾淨 URL(見 E3),避免 token 落地在 `.git/config`。 +- **自動執行原則**:除非使用者明確要求先確認,或遇到不可忽略的必要決策(例如缺 token、無法決定 gitea 主機、目標目錄不可寫、單一專案更新時工作區有未提交變更需使用者裁示),否則各階段只需輸出簡短計畫/進度後直接執行到完成。**唯一一定會中斷的點是階段 C 的擁有者多選**(除非已帶 `--owner`)。 +- **不破壞既有工作**:更新既有專案時若工作區有未提交變更而導致切換/pull 失敗,**停止該專案的更新並回報**,請使用者自行處理;**絕不**強制丟棄(不可 `reset --hard`/`checkout -f`/`clean`)。 +- **唯讀本意**:本 skill 只做 clone 與本機分支更新,**不 push、不改遠端、不刪本機未追蹤檔**。 + +--- + +## 參數 + +格式:`[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host ] [--include-forks] [--ssh] [--yes]` + +- `--target-dir <根目錄>`:clone 的根目錄。**省略時預設家目錄**(`$HOME`,Windows 為 `%USERPROFILE%`)。實際每個專案會 clone 到 `<根目錄>/<擁有者>/<專案名>`。 +- `--owner <擁有者,以逗號分隔>`:預先指定要同步的擁有者(可多個,以逗號分隔,例如 `plugins,jeffery`)。**帶此參數時跳過階段 C 的多選詢問**,直接同步這些擁有者;若指定的擁有者不在可讀清單內,回報並略過該擁有者。 +- `--host `:gitea 主機(如 `gitea.jsc.idv.tw`)。省略時依 A2 規則決定。 +- `--include-forks`:一併納入 fork 來的專案;**省略時預設排除 fork**(`fork=true` 的 repo 不列入)。 +- `--ssh`:改用 SSH(`ssh_url`)clone,不帶 token;**省略時預設用 HTTPS+token**。 +- `--yes`:全自動。即使未帶此參數,也依「自動執行原則」盡量不中斷;但階段 C 的擁有者多選仍會詢問(除非已帶 `--owner`)。 + +--- + +## 階段 A:前置設定 + +### A1. 確認 token + +讀取環境變數 `GITEA_TOKEN`(或助理慣用的等價變數名)。 + +```bash +[ -n "${GITEA_TOKEN}" ] && echo "GITEA_TOKEN 已設定" || echo "GITEA_TOKEN 未設定" +``` + +- **未設定** → 回報「缺少 `GITEA_TOKEN` 環境變數,無法存取 Gitea API」並停止;提示使用者於環境變數提供 token(不要請使用者把 token 貼進對話)。 +- **不可** echo token 本身,只確認是否存在。 + +### A2. 決定 gitea 主機 + +依序決定主機(取第一個成功者): + +1. 帶 `--host <主機>` → 直接採用。 +2. 否則讀環境變數 `$GITEA_HOST`(若有)。 +3. 否則若**目前工作目錄是 git repo** 且 `git remote get-url origin` 指向某 gitea 主機 → 取該 host。 +4. 以上皆無 → **詢問使用者** gitea 主機,**不臆測**。 + +主機僅取 host 部分(如 `gitea.jsc.idv.tw`),組 API base 為 `https:///api/v1`。 + +### A3. 決定目標根目錄 + +- 帶 `--target-dir <根目錄>` → 採用(可含 `~`,需展開為實際家目錄)。 +- 省略 → 預設家目錄(`$HOME`)。 +- 確認根目錄存在且可寫;不存在時先建立(`mkdir -p`)。若無法建立或不可寫,回報並停止。 + +輸出本次設定摘要(主機、根目錄、是否含 fork、HTTPS/SSH),再進入階段 B。 + +--- + +## 階段 B:取得使用者有讀取權限的所有專案並依擁有者分組 + +### 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 失效或權限不足。 + +### B2. 篩選與正規化 + +每筆 repo 取下列欄位:`owner.login`、`name`、`full_name`、`clone_url`、`ssh_url`、`permissions.pull`、`fork`、`default_branch`。 + +- **只保留 `permissions.pull == true`** 的專案(確有讀取權限)。 +- **預設排除 `fork == true`**;帶 `--include-forks` 才納入。 +- 去除重複(以 `full_name` 為鍵)。 + +### B3. 依擁有者分組 + +以 `owner.login` 分組,統計每個擁有者底下的可讀專案數,並依擁有者名稱排序(專案數多者可優先列出)。**無任何可讀專案** → 回報並結束。 + +--- + +## 階段 C:列出所有擁有者讓使用者多選 + +輸出一張「擁有者清單」表,含編號、擁有者、可讀專案數: + +| # | 擁有者 | 可讀專案數 | +| --- | --- | --- | + +選擇方式: + +- **帶 `--owner` 參數** → 直接採用其中的擁有者,**跳過詢問**;清單中沒有的擁有者回報並略過。 +- **未帶 `--owner`** → 這是**不可忽略的必要決策**,必須讓使用者**多選**: + - 擁有者數量 ≤ 4 時,可用助理的多選提問元件(如 Claude Code 的多選問題)。 + - 擁有者較多時,請使用者直接以**編號或擁有者名稱**回覆(支援多個,以逗號或空白分隔),並支援回覆 `all`/`全部` 代表全選。 + - 解析使用者回覆為擁有者集合;無法對應的輸入請回報並請使用者重選,**不臆測**。 + +選定後輸出「將同步的擁有者:…」再進入階段 D。 + +--- + +## 階段 D:列出選定擁有者底下有讀取權限的專案 + +對每個選定擁有者,列出其底下所有可讀專案,並標示本機是否已存在(`<根目錄>/<擁有者>/<專案名>/.git` 是否存在): + +| 擁有者 | 專案 | 狀態 | +| --- | --- | --- | +| | | 🆕 將 clone/♻️ 已存在將更新 | + +輸出總計(將 clone N 個、將更新 M 個),除非使用者要求先確認,否則直接進入階段 E 執行。 + +--- + +## 階段 E:依擁有者建立資料夾並逐一 clone/更新 + +對選定擁有者底下的每個專案,依序處理(一次一個,互不干擾): + +### E1. 建立擁有者資料夾 + +```bash +mkdir -p "<根目錄>/<擁有者>" +``` + +專案目標路徑為 `<根目錄>/<擁有者>/<專案名>`。 + +### E2. 專案不存在 → clone + +- **HTTPS(預設)**:以 token 組 clone URL,**用變數帶入、不可印出**;clone 完成後**還原乾淨 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" + ``` + +- **SSH(`--ssh`)**:直接用 `ssh_url`,不帶 token: + + ```bash + git clone "" "<根目錄>/<擁有者>/<專案名>" + ``` + +- clone 後若遠端有 `develop`,將工作分支切到 `develop`(沒有則維持 `default_branch`/`master`);clone 失敗回報原因(**遮蔽 token**)並繼續下一個專案,不中斷整批。 + +### E3. 專案已存在 → 切到 develop(再退而 master)並更新到最新 + +先盤點該專案狀態,**有未提交變更則不強切**: + +```bash +dest="<根目錄>/<擁有者>/<專案名>" +git -C "${dest}" rev-parse --is-inside-work-tree # 確認是 git repo +git -C "${dest}" status --porcelain # 是否乾淨 +git -C "${dest}" fetch --all --prune +``` + +- **目錄存在但不是 git repo**(無 `.git`)→ 回報「目標已存在且非 git repo,略過」並繼續下一個;**不刪除、不覆蓋**。 +- **工作區有未提交變更**(`status --porcelain` 非空)→ 回報「有未提交變更,略過更新以免覆蓋」並繼續下一個;**絕不**強制丟棄。 +- **工作區乾淨** → 依序選擇要更新的分支並切換、拉取: + + ```bash + if git -C "${dest}" rev-parse --verify --quiet origin/develop; then + git -C "${dest}" switch develop 2>/dev/null || git -C "${dest}" switch -c develop --track origin/develop + elif git -C "${dest}" rev-parse --verify --quiet origin/master; then + git -C "${dest}" switch master 2>/dev/null || git -C "${dest}" switch -c master --track origin/master + fi + git -C "${dest}" pull --ff-only + ``` + + - `origin/develop` 存在 → 切到 `develop`;否則 `origin/master` 存在 → 切到 `master`;兩者都不存在 → 回報「找不到 develop/master」並維持原分支、略過該專案。 + - `pull --ff-only` 無法快進(本地與遠端分歧)→ 回報該專案需人工處理(**不**自動 merge/rebase/reset),繼續下一個。 + - 已在目標分支時直接 `pull --ff-only` 更新即可。 + +### E4. 逐一處理、彙整結果 + +- 每個專案處理完記錄結果:🆕 已 clone/♻️ 已更新到最新/⏭️ 已略過(原因)/❌ 失敗(原因,遮蔽 token)。 +- 單一專案失敗或略過**不應中斷整批**;全部處理完再輸出總結。 + +--- + +## 總結 + +各階段執行後輸出: + +- **階段 A**:使用的 gitea 主機、目標根目錄、是否含 fork、HTTPS/SSH。 +- **階段 B/C**:可讀專案總數、擁有者總數、使用者選定的擁有者。 +- **階段 D/E**:本批處理結果統計 —— 新 clone N 個、更新 M 個、略過 K 個(列原因)、失敗 J 個(列原因,遮蔽 token),並列出各專案的本機路徑。 +- 若過程曾在指令中帶入 token,提醒確認輸出與 log 無明文 token。 + +--- + +## 呼叫方式 + +格式:`[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host ] [--include-forks] [--ssh] [--yes]` — +全部可省略(根目錄預設家目錄;未帶 `--owner` 時於階段 C 詢問多選)。token 一律由環境變數(如 `GITEA_TOKEN`)提供,不寫死、不印出。 + +| 助理 | 呼叫 | +| --- | --- | +| Claude Code / Antigravity | `/jsc:code-review-sync`,或 `/jsc:code-review-sync --owner plugins --target-dir ~/work`、`/jsc:code-review-sync --include-forks --ssh` | +| Codex | `$code-review-sync`,或 `$code-review-sync --owner plugins --target-dir ~/work`,或用 `/skills` 選單 | +| OpenCode | 描述需求(如「用 GITEA_TOKEN 取得我有讀取權限的所有 Gitea 專案,依擁有者分組讓我多選,把選定擁有者的專案 clone 到家目錄;已存在的切到 develop/master 更新到最新」)自動觸發 | From 7f44ba61744f1368aa087091a290d435212d59b6 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Fri, 26 Jun 2026 09:44:28 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs(README):=20=E8=A3=9C=E4=B8=8A=20code-r?= =?UTF-8?q?eview-sync=20=E7=9A=84=E7=9B=AE=E9=8C=84=E7=B5=90=E6=A7=8B?= =?UTF-8?q?=E8=88=87=20skill=20=E8=AA=AA=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 85f2b2b..90d4dd5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ code-review/ │ │ └── roles/ # 一角色一檔(bard/mage/rogue/assassin/paladin) │ ├── code-review-archive/ # 保存裁決:成立→已知問題、誤判→排除事項 │ ├── code-review-resolve/ # 解決 findings.json + 分類提交 + push + 發 PR -│ └── code-review-nuget/ # C# / .NET NuGet 套件更新 + build 驗證 +│ ├── code-review-nuget/ # C# / .NET NuGet 套件更新 + build 驗證 +│ └── code-review-sync/ # 依擁有者批次 clone/更新 Gitea 專案 ├── AGENTS.md # 跨助理共用指引 └── README.md ``` @@ -207,6 +208,16 @@ rm -rf ~/.config/opencode/skills/code-review - **Codex**:`$code-review-nuget`,或 `$code-review-nuget src/App/App.csproj --build "dotnet build App.sln -c Release"`,或用 `/skills` 選單 - **OpenCode**:描述需求(如「把這個 .NET solution 的 NuGet 都更新到最新,先清掉 ProjectReference 已提供的重複套件,每更新一包就 build」)自動觸發 +### `code-review-sync` + +透過 `GITEA_TOKEN` 把使用者有讀取權限的 Gitea 專案批次 clone 到本機並依擁有者分類。流程會先確認 token、決定 gitea 主機與目標根目錄(預設家目錄),再以 token 分頁呼叫 `GET /api/v1/user/repos` 取回**所有有讀取權限**的專案、依 `owner` 分組(預設排除 fork);接著列出所有擁有者讓使用者**多選**(帶 `--owner` 則跳過詢問),列出選定擁有者底下的專案後,於 `<根目錄>/<擁有者>/<專案名>` 逐一處理:**不存在則 clone**(HTTPS+token,clone 後還原乾淨 origin;或 `--ssh`),**已存在則 `fetch` → 切到 develop(再退而 master)→ `pull --ff-only` 更新到最新**。全程唯讀(不 push、不改遠端),工作區有未提交變更的專案會略過而非強制覆蓋;token 一律由環境變數提供、不寫死也不印出。 + +參數:`[--target-dir <根目錄>] [--owner <擁有者,以逗號分隔>] [--host ] [--include-forks] [--ssh] [--yes]`(根目錄預設家目錄;`--owner` 預先指定擁有者並跳過多選;`--host` 指定 gitea 主機;`--include-forks` 納入 fork;`--ssh` 改用 SSH clone;`--yes` 略過一般確認但不略過擁有者多選)。 + +- **Claude Code / Antigravity**:`/jsc:code-review-sync`,或 `/jsc:code-review-sync --owner plugins --target-dir ~/work`、`/jsc:code-review-sync --include-forks --ssh` +- **Codex**:`$code-review-sync`,或 `$code-review-sync --owner plugins --target-dir ~/work`,或用 `/skills` 選單 +- **OpenCode**:描述需求(如「用 GITEA_TOKEN 取得我有讀取權限的所有 Gitea 專案,依擁有者分組讓我多選,把選定擁有者的專案 clone 到家目錄;已存在的切到 develop/master 更新到最新」)自動觸發 + --- From a684091dd0c56a2fe640fa6322e415a190d4568e Mon Sep 17 00:00:00 2001 From: Jeffery Date: Fri, 26 Jun 2026 09:44:28 +0800 Subject: [PATCH 3/3] =?UTF-8?q?chore(plugin=20=E7=89=88=E6=9C=AC):=20bump?= =?UTF-8?q?=20=E8=87=B3=200.1.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 69f390a..7397c76 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc", - "version": "0.1.6", + "version": "0.1.7", "description": "JSC 程式碼審查 plugin(Claude Code / Codex / Antigravity / OpenCode),提供 RPG 攻防式 git diff review、裁決結果歸檔、Gitea AI review findings 修復/PR 流程,以及 C# / .NET NuGet 套件更新流程;於 Claude Code 以 /jsc: 前綴呼叫。", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 1cab669..82e8613 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc", - "version": "0.1.6", + "version": "0.1.7", "description": "JSC 程式碼審查 plugin,提供 RPG 攻防式 git diff review、裁決結果歸檔、Gitea AI review findings 修復/PR 流程,以及 C# / .NET NuGet 套件更新流程。", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index 3b049b8..369747a 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc", - "version": "0.1.6", + "version": "0.1.7", "description": "JSC 程式碼審查 plugin,提供 RPG 攻防式 git diff review、裁決結果歸檔、Gitea AI review findings 修復/PR 流程,以及 C# / .NET NuGet 套件更新流程;於 Antigravity 以 /jsc: 前綴呼叫。", "skills": "./skills/" }