Files
code/skills/review-resolve/SKILL.md
T
jiantw83andClaude Sonnet 5 766142c3ba feat(spec-version-guard): 新增版本檢查 hook 並在 9 個 skill 檔頭引用規範
新建 hooks/hooks.json,PreToolUse 沿用跨 plugin 腳本定位手法呼叫 shared 的
version-guard.mjs(判定仍固定讀自己的 plugin.json,只是腳本檔借用 shared 的);
action-composite/action-docker/action-node/image/issues/nuget/review-resolve/
sync/target 九個 skill 檔頭補上 spec-version-guard。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 14:54:07 +08:00

418 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: review-resolve
description: 解決新版 AI Code Review 的 findings——一律讀取專案內 `.gitea/ai-review/findings/` 目錄下的 wrapper 物件 JSON(含 `findings`/`excluded` 陣列、中文 severity)與 `findings.json`,並可額外用 `--issue`(無/單選/多選/全部)從 Gitea 議題(建問題模式)解析嚴重問題留言為 findings,兩來源合併去重後逐條解決;先同步 git(fetch → 檢查目前分支的遠端分支是否存在;存在則留在目前分支 pull 更新,不存在則切換到 develop/master 後 pull 更新;只有來源分支與 PR 目標分支相同時才建立新的工作分支;必要時嘗試解衝突),再依嚴重度(嚴重/警告/建議)逐條修復;可判斷為誤報者寫入 `.gitea/ai-review/exclusions.json`,已解決或已登記為誤報的 finding 逐檔就地自來源 wrapper 移除;處理過的議題於留言進度、把只來自議題的待人工處理問題寫回 findings 目錄後關閉;接著可將工作區變更依 conventional commit 類型分類提交、push 當前分支,並透過 Gitea API 對指定目標分支開 PR。當使用者說解決 findings、處理 AI review 問題、依議題編號修 code review 問題、修掉 `.gitea/ai-review` 問題、依嚴重度修復後分類提交,或要分類 commit 並 push/開 PR 時觸發。不適用於:產生 findings、單純 code review 不修改、只保存裁決紀錄,或只要不分類的單一 commit。
argument-hint: "[--issue <編號|編號清單|all>] [--findings <findings 檔或目錄路徑>] [--target <目標分支>] [--pr-desc <full|simple|自訂文字>] [--no-commit] [--no-pr] [--yes]"
---
# review-resolve — 解決 AI review findings、分類提交、push 並開 PR
五階段 skill:先執行 **git 同步**(`git fetch` → 確認目前分支的遠端分支是否存在;存在則留在目前分支 `git pull` 更新,不存在則切換到 `develop`,再退而 `master` 後 `git pull` 更新;只有來源分支與 PR 目標分支相同時才建立新的工作分支;必要時告知並嘗試解衝突),再**蒐集問題來源**——**一律讀** `.gitea/ai-review/findings/` 目錄下的 wrapper 物件 JSON(取其 `findings` 陣列)與 `findings.json`,並依 `--issue`(無/單選/多選/全部)**額外**從 Gitea 議題留言解析嚴重問題,兩來源**合併去重**後逐條處理;成立問題修復後自來源 wrapper 逐檔就地移除,可判斷為誤報者寫入 `.gitea/ai-review/exclusions.json` 後也可移除(處理過的議題於留言進度、把只來自議題的待人工處理問題寫回 findings 目錄後關閉),接著**必須盤點並處理工作區的所有變更**,再依 conventional commit 類型分門別類 commit,然後 **push 當前分支**,最後**透過 Gitea API 發 PR**。
| 階段 | 動作 |
| --- | --- |
| A. Git 同步 | `git fetch` → 當前分支不在遠端則切換 develop(再退而 master)→ `git pull`/必要時解衝突 → 只有來源分支與 PR 目標分支相同時才建立新工作分支 |
| B. 解決問題 | 一律讀 `.gitea/ai-review/findings/*.json`+`findings.json`(wrapper,取 `findings`),依 `--issue`(無/單選/多選/全部)加讀議題留言 → 合併去重 → 依等級 🔴嚴重→🟠警告→🔵建議 逐條修復或判斷誤報 → 已解決者逐檔就地自來源 wrapper 移除,誤報寫入 `exclusions.json`;處理過的議題回寫(留言+只來自議題的待人工寫回 findings 後關閉);**無待處理問題則跳到階段 D** |
| C. 分類提交 | 必須分析並涵蓋工作區所有變更(已修改/新增/刪除/改名,含已暫存與未暫存、未追蹤檔、findings/exclusions 更新)→ 依 feat/fix/docs/style/refactor/perf/test/chore/revert 分組 → 各組一個 commit |
| D. Push 當前分支 | 認證管理器 → 失敗改 token → 再失敗詢問使用者;**無 commit 可 push 則跳到階段 E** |
| E. 發出 PR | 確定目標分支(不明必問)→ 選 PR 描述(完整/簡單/自訂)→ token 呼叫 Gitea API 建 PR → 通知並清除內文 |
---
## 共用規範(必要前置)
先載入 `/jsc-shared:spec-preflight` 並依其流程處理;載入不到即代表 shared plugin 未安裝,
依該 spec 詢問使用者是否安裝 `https://gitea.jsc.idv.tw/plugins/shared.git`,不安裝則中斷本 skill。
本 skill 需要的規範:`spec-version-guard`、`spec-output`、`spec-execution`、`spec-gitea`、`spec-git-safety`、`spec-issue-read`、`spec-conventional-commit`、`spec-git-push`、`spec-pull-request`、`spec-skill-invocation`
本 skill 特有補充:
- **必要決策**(會中斷詢問):目標分支缺失、pull 策略不明、無法安全解衝突、需人工判斷的設計取捨、push 憑證皆失敗。
- 涵蓋範圍:更新後的 `findings/*.json`(wrapper)/ `exclusions.json`、議題留言、commit 訊息、PR 標題/描述、終端訊息,與等級 emoji 🔴🟠🔵。
- 階段 E 完成後依規範清除對話內文(見 E6)。
---
## 參數
格式:`[--issue <編號|編號清單|all>] [--findings <路徑>] [--target <目標分支>] [--pr-desc <full|simple|自訂文字>] [--no-commit] [--no-pr] [--yes]`
- `--issue <無 / 編號 / 編號清單 / all>`:**額外**從 Gitea 議題(對應 code-review 建問題模式)撈問題,支援四種——**無**(省略此參數,不讀議題)、**單選**(`--issue 12`)、**多選**(`--issue 12,15,20` 逗號分隔)、**全部**(`--issue all`,讀 repo 內所有 open 議題)。帶此參數時走 B1b 解析議題嚴重問題留言為 findings;修復後於 B6 對每個處理過的議題留言進度、把「只來自議題」的待人工處理問題寫回 `findings/` 目錄後關閉該議題。需要 `GITEA_TOKEN` 與可解析的 `origin` repo 座標。**注意:不論有無此參數,findings 檔一律照讀(見 `--findings`),議題只是額外來源。**
- `--findings <路徑>`:findings 檔來源路徑。**不論是否帶 `--issue`,都一律讀取**;省略時預設讀目錄 `.gitea/ai-review/findings/` 下所有 `*.json` **與**(若存在)單一檔 `.gitea/ai-review/findings.json`(每個檔為新版 wrapper 物件,取其 `findings` 陣列)(相對於工作目錄根)。指定路徑時:目錄則讀該目錄所有 `*.json`;單一檔則讀該檔(同為 wrapper 物件)。findings 檔與 `--issue` 議題的問題會在 B1c 合併去重後一起處理。
- `--target <目標分支>`:PR 的目標分支。**省略時必須詢問使用者,不可猜測**;若需要避免把同名來源分支誤推到目標分支,可能會在階段 D 先詢問並於階段 E 沿用。
- `--pr-desc <full|simple|自訂文字>`:PR 描述形式。`full`=完整版(重新分析 diff 總結)、`simple`=簡單版(逐條列 commit 訊息)、或直接給自訂文字;省略時預設 `full`,不要為描述形式中斷詢問。
- `--no-commit`:只做階段 A/B(git 同步 + 修復 + 更新 findings/exclusions),**不**執行階段 C/D/E(不提交、不推送、不開 PR)。
- `--no-pr`:執行到階段 D(push)為止,**不**開 PR(階段 E 略過)。
- `--yes`:明確要求全自動執行(修復、更新 findings/exclusions、commit、push、開 PR 一氣呵成);即使未帶此參數,也依「自動執行原則」盡量不中斷,只有必要決策才詢問。
---
## 階段 A:Git 同步
### A1. 檢查同步前狀態
執行同步前先盤點目前分支與工作區狀態:
```bash
git rev-parse --abbrev-ref HEAD
git status --porcelain
```
- **若工作區已有未提交變更**:先告知使用者同步可能需要 merge / rebase 並可能與本地變更衝突;除非使用者要求先確認或目前狀態明顯容易覆蓋未提交工作,否則繼續同步。
- **若目前不在一般分支上**(例如 detached HEAD):回報狀態並停止,請使用者切到要處理的分支後再執行。
### A2. Fetch 遠端更新
```bash
git fetch --all --prune
```
- fetch 失敗時回報錯誤並停止,不進入 findings 修復。
- 若錯誤訊息可能含 credential / token,輸出前必須遮蔽。
### A3. 確認當前分支存在於遠端,必要時切換(develop → master)
fetch 後,確認當前分支在遠端是否有對應分支:
```bash
current_branch="$(git rev-parse --abbrev-ref HEAD)"
git rev-parse --verify --quiet "origin/${current_branch}"
```
- **`origin/<current_branch>` 存在** → 維持當前分支,直接進入 A4 更新到最新;不要只因目前分支有遠端同名分支就建立新工作分支,後續只有來源分支與 PR 目標分支相同時才需要開新分支。
- **`origin/<current_branch>` 不存在**(當前分支為本地獨有,遠端無對應)→ 切換前先確認工作區可安全切換(承接 A1 結果,若有未提交變更導致切換失敗,停止並回報,請使用者先處理未提交變更;不可強制丟棄),再依 `/jsc-shared:spec-git-safety`「develop → master 後備分支」切換到後備分支;develop/master 在遠端都不存在時,依該規範回報並停止,不臆測其他分支。
- 切換完成後,A4 先更新該後備分支到最新;是否建立新的工作分支交由 A5 依來源分支與 PR 目標分支是否相同判斷。切換到後備分支屬不可忽略的狀態變更,需在輸出中明確告知使用者已從原分支切換到哪一個分支。
### A4. Pull(更新到最新)
對當前分支(可能已於 A3 切換為 develop/master)拉取最新:
```bash
git pull
```
- pull 成功後進入 A5,必要時建立新的工作分支,再進入階段 B。
- 若顯示需要指定 merge / rebase 策略,先回報原因;未帶 `--yes` 時詢問使用者要採用哪種策略,不可自行猜測。
- 若 pull 產生衝突,立即告知使用者發生衝突,接著依階段 A6 嘗試解衝突。
### A5. 只有來源分支與 PR 目標分支相同時,建立新的工作分支
A4 更新完成後,取得目前來源分支與 PR 目標分支:
```bash
source_branch="$(git rev-parse --abbrev-ref HEAD)"
# target 來自 --target;若未提供且後續會建立 PR(未帶 --no-pr),需先依 E1 詢問目標分支。
```
- **若後續會建立 PR(未帶 `--no-pr`)且尚未知道目標分支** → 先依階段 E1 的規則詢問目標分支,避免後續修復 commit 落在與 PR 目標同名的來源分支上才發現 source/base 相同。若帶 `--no-pr`,此階段不因缺少目標分支而詢問。
- 是否需要建立新的工作分支、判斷方式(只有同名才開新分支)與命名避免覆蓋既有分支,依 `/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}"
```
- 若因未提交變更導致建立/切換新分支失敗,停止並回報,請使用者先處理未提交變更;不可強制丟棄。
- 建立新分支屬不可忽略的狀態變更,需在輸出中明確告知使用者「因來源分支與目標分支同名,已從 `<source_branch>` 建立並切換到 `<work_branch>`」。
### A6. 必要時嘗試解衝突
當 `git pull` 後出現衝突,依 `/jsc-shared:spec-git-safety` 的保守解衝突流程處理(定位衝突檔 → 最小合理整合 → 可安全解決者 `git add` 標記、無法安全判斷者停止並列出決策點);全部衝突解完後,依 git 當前狀態完成 merge / rebase 的必要步驟,確認 `git status --porcelain` 沒有未解衝突,再回到 A5 判斷是否需要建立新工作分支。
---
## 階段 B:蒐集問題(findings 檔 + Gitea 議題)合併去重、依嚴重度逐條解決或登記誤報
新版 AI Code Review 的產出格式(對齊 `https://gitea.jsc.idv.tw/actions/code-review`,`src/index.js` / `src/lib/review.js`):
- **findings**:每次 review 在目錄 `.gitea/ai-review/findings/` 產生一個**新檔**(檔名為台北時間戳,如 `2026-07-20-14:30:15.json`),內容是**頂層物件(wrapper)**,不是陣列:
```json
{
"generatedAt": "<台北時間>",
"commitSha": "<head sha>",
"prNumber": 9,
"tool": { "name": "...", "version": "...", "model": "..." },
"findings": [ { "<finding>": "..." } ],
"excluded": [ { "<finding>": "..." } ]
}
```
每個 `<finding>` 欄位:
| 欄位 | 語義 |
| --- | --- |
| `id` | 流水號(`F001`…) |
| `reviewer` | 審查角色名稱 |
| `focus` | 審查面向代碼(如 `logic`) |
| `badge` | 角色徽章(emoji,可空) |
| `severity` | 嚴重等級(**中文**:`嚴重` / `警告` / `建議`) |
| `file` | 問題所在檔案(repo 相對路徑) |
| `startLine` / `endLine` | 問題起訖行號 |
| `problem` | 問題描述 |
| `suggestion` | 修改建議 |
| `suggestedCode` | 建議寫法(程式碼,可空) |
- **exclusions**:固定 `.gitea/ai-review/exclusions.json`,**頂層陣列**,每筆:
```json
{
"addedAt": "<台北時間>",
"prNumber": 9,
"reviewer": "<審查角色>",
"severity": "嚴重|警告|建議",
"file": "<檔案路徑>",
"startLine": 10,
"endLine": 12,
"problem": "<問題描述>",
"reason": "<判定誤報/重複的理由>"
}
```
- **建問題模式**:某些 review 不寫 findings 檔,改開 Gitea 議題、把嚴重問題**逐則發成議題留言**(見 B1)。
> 本 skill 只支援上述**新版格式**;讀到非 wrapper 物件(例如舊版頂層陣列 findings)時,回報格式不符並停止,不臆測欄位。
### B1. 蒐集問題來源並合併去重
**不論是否處理議題,都必須先讀取專案內的 findings 檔**;若有指定議題,再加上議題來源,兩者合併去重後才進入 B3。
**B1a. 讀 findings 檔(一律執行)**
- 讀目錄 `.gitea/ai-review/findings/` 下所有 `*.json`(含子目錄、依檔名排序)**與**(若存在)單一檔 `.gitea/ai-review/findings.json`;每個檔皆為上述 **wrapper 物件**,取其 `findings` 陣列。
- 指定 `--findings <路徑>` 時改讀該路徑(目錄則讀其所有 `*.json`;單一檔則讀該檔),仍須為 wrapper 物件。
- 逐筆記住各 finding 的**來源檔路徑與所屬 wrapper**,供 B5 逐檔就地寫回;此步不得更動任何來源檔。
- JSON 解析失敗或非 wrapper 物件 → 回報該檔路徑並停止,不臆測、不亂改檔。
**B1b. 讀議題(依 `--issue` 選擇)**
`--issue` 支援四種形式:
| 形式 | 寫法 | 行為 |
| --- | --- | --- |
| 無 | 省略 `--issue` | 不讀議題,只用 findings 檔 |
| 單選 | `--issue 12` | 讀議題 #12 |
| 多選 | `--issue 12,15,20` | 讀多個議題(逗號分隔) |
| 全部 | `--issue all` | 讀 repo 內**所有 open 議題** |
1. 解析 repo 座標(同 E3),token 由 `GITEA_TOKEN` 提供。
2. `all` 時先取所有 open 議題(分頁全取):`GET /repos/<owner>/<repo>/issues?state=open&type=issues`。
3. 對每個選定議題讀取本文、全部留言與全部附件,依 `/jsc-shared:spec-issue-read` 執行(分頁完整讀取、不得只取部分留言;輸出遮蔽 token);議題標題/描述與附件內容作為整體背景脈絡納入判斷,本階段只從留言中解析下列嚴重問題留言格式。
4. 解析每則符合「嚴重問題留言」格式(action `severeCommentBody` 產出)的留言為一條 finding:
```
### <emoji> <severity>|<badge> <reviewer>
**位置**:`<file>` 第 <startLine>–<endLine> 行
**問題**
<problem>
**修改建議**
<suggestion>
**建議寫法**(可選)
<suggestedCode>
```
映射為 `{ reviewer, severity, file, startLine, endLine, problem, suggestion, suggestedCode }`,並記住**來源議題編號與留言 id**(供 B6 回寫)。
5. 某議題解析不到任何嚴重問題留言 → 記錄並略過該議題(`all`/多選時不因單一議題無問題而中斷整批),不臆測。
**B1c. 合併去重**
- 把 B1a(findings 檔)與 B1b(議題)的所有 finding 合併成單一待處理清單。
- **去重鍵**:`file` + `startLine` + `endLine` + `reviewer` + `problem`(正規化空白後比對;行號缺失時以 `file` + `reviewer` + `problem` 為準)。
- 重複的 finding **合併其來源清單**(同一問題可能同時來自某 findings 檔與某議題),只列入一次;後續解決後要**同時回寫所有來源**(B5 移除該 finding 檔項目、B6 計入相關議題的關閉與留言)。
- 合併去重後即為待處理 finding 清單,進入 B3 排序。
**無任何 finding**(findings 檔皆空/不存在,且未選議題或選到的議題都無問題)→ 視為「無問題待解決」,輸出告知。`--no-commit` 時就此結束;否則**跳過階段 C 直接進入階段 D**。
### B2. 讀取 exclusions
`.gitea/ai-review/exclusions.json` 不存在時視為空陣列(頂層陣列);解析失敗或非陣列時回報,並在本次不寫入誤報(避免破壞既有內容、需人工確認)。
### B3. 依嚴重等級排序
把所有 finding 依等級由高到低排序:**🔴 嚴重 → 🟠 警告 → 🔵 建議**。
`severity` 為中文 `嚴重` / `警告` / `建議`;容錯對應:`嚴重`/`critical`/`high`/`blocker`→🔴、`警告`/`warning`/`major`→🟠、`建議`/`info`/`low`/`minor`→🔵;無法辨識者**排在最後**並標「等級未知」。排序後先處理高等級。
### B4. 逐條處理(高等級先)
輸出一張「處理計畫」表,除非使用者要求確認或遇到必要決策,否則**依排序由上而下逐條**處理:
| # | 等級 | 審查員 | 問題 | 檔案 | 行數 | 處理方式 |
| --- | --- | --- | --- | --- | --- | --- |
對每一條 finding:
1. 讀取對應檔案與其脈絡(依 `file` 與 `startLine`–`endLine`)。
2. 先判斷是否為誤報或不適用:例如程式碼脈絡證明指控不成立、已有等價防護、命中 `exclusions.json` 已知排除、CI/CD 必要權限、或 finding 對非本次變更做不合理要求。
3. **可判斷為誤報** → 不改程式碼;將排除條目 append 到 `.gitea/ai-review/exclusions.json`(新版欄位,見 B5),保留原始問題文字與語意;若已有等價 exclusion(同 `file`+`reviewer`+`problem` 高度相似)則不重複新增。
4. **確認為真問題且可安全修復** → 依 `suggestion`/`suggestedCode`/`problem` 在程式碼中實作最小合理修正;建議含糊或與現況不符時,依原始碼脈絡做最小且合理的修正。
5. **無法安全自動修復或無法確認真偽**(例如需求不明、牽涉設計取捨、檔案不存在)→ **不硬改**,標記為「待人工處理」並記錄原因,繼續下一條。
6. 逐條完成後,記錄該條結果(✅ 已解決 / 🚫 誤報已寫入 exclusions / ⏭️ 待人工處理 + 原因),並保留該 finding 依 B1c 記錄的**所有來源**(findings 檔路徑 / 議題編號+留言 id)供 B5/B6 回寫。
> 一次只處理一條、修完再處理下一條,避免互相干擾;同檔多條問題可合併讀取但仍逐條套用修正。
### B5. 更新 findings 檔與 exclusions.json
**所有條目處理完畢後**,依每條 finding 於 B1c 記錄的來源回寫。**只來自議題、無任何 findings 檔來源的 finding 不在本節處理**(交由 B6 回寫議題與新建 findings 檔);有 findings 檔來源者(含同時來自議題與檔案的)依下列更新其來源檔:
- **只有確認已解決,或已確認為誤報並成功寫入 `exclusions.json` 的 finding,才可從來源 findings 檔移除**;待人工處理、無法確認真偽、修復未驗證成功者一律保留於來源檔。
- **逐檔就地更新(wrapper 物件)**:依 B1a 記錄的來源檔,把該檔 `findings` 陣列中要保留的 finding 留下、其餘移除,**只改該檔的 `findings` 陣列**;wrapper 其他欄位(`generatedAt`、`commitSha`、`prNumber`、`tool`、`excluded`)原樣保留,**不得**把多個來源檔合併成單一檔。某檔 `findings` 全數已解決/誤報時,把該檔的 `findings` 寫成 `[]`(保留檔案與 wrapper、維持目錄結構,不刪檔)。
- 保留的 finding 維持其原 wrapper 內欄位(`id`、`reviewer`、`severity`、`file`、`startLine`、`endLine`、`problem`、`suggestion`、`suggestedCode`…)與排序。
- **誤報一律 append 到 `.gitea/ai-review/exclusions.json`(頂層陣列)**,欄位對齊新版:`addedAt`(當下台北時間,`date` 取得)、`prNumber`(已知則帶、未知可省略)、`reviewer`、`severity`、`file`、`startLine`、`endLine`、`problem`、`reason`(誤報理由);append 後去重。
- 誤報移出 findings 前,必須先確認 `exclusions.json` 已成功寫入該條或已有等價條目;寫入失敗則該 finding 保留於來源檔。
- 所有寫檔以 UTF-8(不含 BOM)寫入,結尾保留一個換行。
### B6. 議題收尾:回寫並關閉每個處理過的議題(B1b 有讀到議題時)
**對每個處理過的議題**(單選/多選/全部各自處理),依該議題涵蓋的 finding 結果收尾:
1. **在該議題留言回報進度**(Gitea API `POST /repos/<owner>/<repo>/issues/<編號>/comments`,body 以 UTF-8 JSON 檔帶入、token 不 echo):以表格逐條列出該議題各問題結果(✅ 已解決 / 🚫 誤報已寫入 exclusions / ⏭️ 待人工處理 + 原因);同時來自 findings 檔的問題,一併註明已於 B5 更新來源檔。
2. **把「待人工處理」且只來自議題的問題寫回 findings**:收集所有 ⏭️ 待人工處理 且**沒有任何 findings 檔來源**的問題(已在 findings 檔的靠 B5 保留,不重複寫),寫成一個**新的 wrapper 物件檔**到目錄 `.gitea/ai-review/findings/`(檔名用台北時間戳,如 `2026-07-20-14:30:15.json`),格式同本階段開頭的 findings wrapper:
```json
{
"generatedAt": "<台北時間>",
"commitSha": "<目前 HEAD sha>",
"prNumber": null,
"tool": { "name": "review-resolve", "version": "<版本或工具預設>", "model": "<模型或工具預設>" },
"findings": [ { "id": "F001", "reviewer": "...", "severity": "嚴重|警告|建議", "file": "...", "startLine": 1, "endLine": 1, "problem": "...", "suggestion": "...", "suggestedCode": "" } ],
"excluded": []
}
```
- `findings` 帶入這些待人工處理問題(欄位沿用 B1b 從議題留言解析出的欄位,並補流水號 `id`);`excluded` 固定為 `[]`。多個議題可合寫成同一個檔。
- 沒有「只來自議題」的待人工處理問題時**不建立**此檔。
- 此新檔屬工作區變更,會在階段 C 一併分類提交(歸 `chore`)。UTF-8(不含 BOM)、結尾保留一個換行。
3. **關閉該議題(一律關閉)**:該議題的問題已全部分流為 ✅ 已修復 / 🚫 誤報寫入 exclusions / ⏭️ 待人工處理已寫回 findings(B5 保留或 B6 新建)追蹤,因此收尾時關閉:
```bash
curl -sS -X PATCH -H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://<host>/api/v1/repos/<owner>/<repo>/issues/<編號>" \
--data '{"state":"closed"}'
```
關閉前先在步驟 1 的留言中標明各待人工處理項寫回哪個 findings 檔與原因,確保關閉議題不會遺失待辦。
4. 誤報仍寫入 `exclusions.json`(同 B5),讓後續 review 沿用。
---
## 階段 C:分析變更並分類提交(`--no-commit` 時略過)
依 `/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(範圍): 一句總結"`。
本 skill 特有補充:
- **階段 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 完成後推送**當前分支**,憑證選擇的三段式流程(認證管理器 → token push → 詢問使用者,前者失敗才退到下一個;token 全程遮蔽、用完即棄不落地)依 `/jsc-shared:spec-git-push` 執行。
推送前先記錄目前來源分支與其遠端基準,並確認是否有 commit 需要 push:
```bash
source_branch="$(git rev-parse --abbrev-ref HEAD)"
git rev-parse --verify "origin/${source_branch}"
git log --oneline "origin/${source_branch}..${source_branch}" # 領先遠端的 commit
```
- **無 commit 可 push**(來源分支已存在於遠端,且相對 `origin/<source_branch>` 沒有領先 commit)→ **跳過本階段 push,直接進入階段 E**(仍可對既有遠端分支開 PR);若帶 `--no-pr`,則就此結束。
- 若來源分支沒有對應的 `origin/<source_branch>`,視為需要 push 的新分支,先記錄「無遠端基準」並繼續;這種情況不需要切回 develop/master,因為 A3 已在同步階段處理「當前分支不存在於遠端」的後備切換。
- 若後續會建立 PR(未帶 `--no-pr`)且尚未知道目標分支,先依階段 E1 的規則詢問目標分支,這屬於不可忽略的必要決策。
- 若來源分支名稱與目標分支相同,**不要 push 原來源分支**;記下來源分支與遠端基準,直接進入階段 E,由 E2 建立新的 PR 來源分支、帶入 commit 後再 push 新分支。
push 成功後記下遠端分支名,進入階段 E。若因來源分支與目標分支相同而延後 push,記下原因並進入階段 E2 建立新分支。
---
## 階段 E:透過 Gitea API 發出 PR(`--no-pr`/`--no-commit` 時略過)
### E1. 確定目標分支(不可猜想)
- 帶 `--target <分支>` → 直接採用。
- 若階段 D 已為了避免 source/base 相同而詢問過目標分支,沿用該目標分支。
- **否則**依 `/jsc-shared:spec-pull-request`「目標分支不得臆測」詢問使用者(可用 `git branch -r` 列出輔助選擇),嚴禁臆測或預設。
### E2. 若來源分支與目標分支相同,改建 PR 來源分支(cherry-pick 帶入已提交的異動)
先取得目前 PR 來源分支:
```bash
git rev-parse --abbrev-ref HEAD
```
若來源分支名稱與 `--target` 指定(或使用者選定)的目標分支相同,依 `/jsc-shared:spec-git-safety`「工作分支選擇」,**不可直接建立 head=base 的 PR,也不可先把原來源分支 push 到目標分支**。此處的情境是修復與 commit 已經發生在這個(事後才發現同名的)來源分支上,因此改用下列流程建立新的 PR 來源分支,把原來源分支的 commit cherry-pick 帶入後再開 PR:
1. 先記錄原來源分支名稱與要帶入的 commit 清單:
```bash
source_branch="$(git rev-parse --abbrev-ref HEAD)"
git log --oneline "origin/${target}..${source_branch}"
```
- 若 `origin/<target>` 不存在,先回報並停止,不猜測替代 base。
- 若沒有任何 commit 可帶入,回報「來源分支沒有領先目標分支的 commit」,停止開 PR。
- 若階段 D 已記錄來源分支的遠端基準,使用該基準判斷要帶入的 commit,避免把原來源分支直接推進目標分支。
2. 從目標分支的遠端基準建立新分支,命名依 `/jsc-shared:spec-git-safety`「建立分支不覆蓋」避免覆蓋既有分支,例如:
```bash
git switch -c "ai-review-resolve/<短時間戳>" "origin/${target}"
```
3. 將原來源分支領先目標分支的 commit 依序帶入新分支:
```bash
git cherry-pick "origin/${target}..${source_branch}"
```
衝突時依 `/jsc-shared:spec-git-safety`「保守解衝突」處理(先告知使用者、依專案脈絡嘗試最小合理整合;可安全解決者移除衝突標記、`git add -- <檔案...>` 後 `git cherry-pick --continue`;無法安全判斷者停止並列出決策點,不硬選任一邊)。
4. 將新分支 push 到遠端,憑證策略依 `/jsc-shared:spec-git-push`(同階段 D),並把後續 PR 的 `head` 改為這個新分支。
若來源分支與目標分支不同,直接以目前分支作為 PR 的 `head`。
### E3. 解析 repo 座標
依 `/jsc-shared:spec-pull-request`「解析 origin 座標」從 `git remote get-url origin` 解析出 host/owner/repo。
### E4. 決定 PR 描述形式(完整版/簡單版/使用者輸入)
依 `--pr-desc` 三選一,各形式的內容與產生方式依 `/jsc-shared:spec-pull-request`「PR 描述模式」執行;未提供時預設 `full`,不要為描述形式中斷詢問。PR **標題**預設取一句總結(可用首個 feat/fix commit 或分支用途);使用者另有指定則從之。
### E5. 呼叫 Gitea API 建立 PR(使用 token)
依 `/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)供排查。
### E6. 完成通知+清除對話內文(重要:可能含 token)
依 `/jsc-shared:spec-pull-request`「完成後提醒清除對話內文」通知使用者 push 結果、PR 連結/編號與描述形式;因 push/API 過程可能使對話內文殘留 gitea token,完成後依 `/jsc-shared:spec-gitea`「Token 機密保護」提醒清除對話內文/上下文,清除前再次確認輸出與 log 中沒有任何明文 token。
---
## 總結
各階段執行後輸出:
- **階段 A**:git fetch / pull 是否成功、當前分支是否存在於遠端(不存在時切到 develop/master 的結果)、若來源分支與目標分支同名是否已建立新工作分支、是否發生衝突、衝突是否已解決或仍需人工處理。
- **階段 B**:問題來源(findings 檔筆數+議題來源:無/`#N`/多個/全部)、合併去重後總數、已解決 N 條(依等級分佈)、誤報寫入 exclusions M 條、待人工處理 K 條(列出原因)、來源 findings 檔保留/移除筆數與 `exclusions.json` 新增筆數;有處理議題時另報各議題已留言、只來自議題的待人工問題寫回哪個 findings 檔、以及各議題已關閉;若無待處理問題,說明已跳過階段 C 直接進入階段 D。
- **階段 C**:建立了哪幾個 commit(type+訊息+檔數),或為何略過(`--no-commit` / 無變更)。
- **階段 D**:push 成功與否、用了哪種方式(認證管理器/token/使用者指定),遠端分支名;若無 commit 可 push,說明已跳過 push 直接進入階段 E。
- **階段 E**:PR 連結/編號、目標分支、描述形式;並提醒已(或請使用者)清除對話內文以防 token 外洩。
---
## 呼叫方式
各助理呼叫一個 skill 的方式依 `/jsc-shared:spec-skill-invocation`;本 skill 參數格式與範例:
格式:`[--issue <編號|編號清單|all>] [--findings <路徑>] [--target <目標分支>] [--pr-desc <full|simple|自訂文字>] [--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(完整版描述)」)自動觸發