This repository has been archived on 2026-07-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

463 lines
32 KiB
Markdown
Raw Permalink 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: code-review-resolve
description: 解決 `.gitea/ai-review/findings.json` 的 AI review findings,先同步 gitfetch → 檢查目前分支的遠端分支是否存在;存在則留在目前分支 pull 更新,不存在則切換到 developmaster 後 pull 更新;只有來源分支與 PR 目標分支相同時才建立新的工作分支;必要時嘗試解衝突),再依嚴重度逐條修復;可判斷為誤報者寫入 `.gitea/ai-review/exclusions.json`,已解決或已登記為誤報的 finding 可自 findings 移除;接著可將工作區變更依 conventional commit 類型分類提交、push 當前分支,並透過 Gitea API 對指定目標分支開 PR。當使用者說解決 findings、處理 AI review 問題、修掉 `.gitea/ai-review` 問題、依嚴重度修復後分類提交,或要分類 commit 並 push/開 PR 時觸發。不適用於:產生 findings、單純 code review 不修改、只保存裁決紀錄,或只要不分類的單一 commit。
argument-hint: "[--findings <findings.json 路徑>] [--target <目標分支>] [--pr-desc <full|simple|自訂文字>] [--no-commit] [--no-pr] [--yes]"
---
# code-review-resolve — 解決 AI review findings、分類提交、push 並開 PR
五階段 skill:先執行 **git 同步**`git fetch` → 確認目前分支的遠端分支是否存在;存在則留在目前分支 `git pull` 更新,不存在則切換到 `develop`,再退而 `master``git pull` 更新;只有來源分支與 PR 目標分支相同時才建立新的工作分支;必要時告知並嘗試解衝突),再**逐條處理** `.gitea/ai-review/findings.json` 裡的問題;成立問題修復後自 findings 移除,可判斷為誤報者寫入 `.gitea/ai-review/exclusions.json` 後也可自 findings 移除,接著**必須盤點並處理工作區的所有變更**,再依 conventional commit 類型分門別類 commit,然後 **push 當前分支**,最後**透過 Gitea API 發 PR**。
| 階段 | 動作 |
| --- | --- |
| A. Git 同步 | `git fetch` → 當前分支不在遠端則切換 develop(再退而 master)→ `git pull`/必要時解衝突 → 只有來源分支與 PR 目標分支相同時才建立新工作分支 |
| B. 解決問題 | 讀 `findings.json` / `exclusions.json` → 依等級 🔴→🟠→🟡→🔵 逐條修復或判斷誤報 → 已解決者自 `findings.json` 移除,誤報寫入 `exclusions.json` 後也可移除;**無待處理問題則跳到階段 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 → 通知並清除內文 |
---
## 輸出規範(務必遵守)
- **語言**:所有面向使用者的輸出(修復清單、提交計畫、總結、反問)與 **commit 訊息** 一律使用**繁體中文(台灣用語)**;
僅程式碼識別字、檔名、git 指令、conventional commit 的 `type``feat`/`fix`…)等技術標識保留原文,**不可**使用簡體字。
- **編碼無亂碼(含繁中、全形標點、emoji)**:凡輸出或寫入只要含繁體中文,一律 **UTF-8(不含 BOM**,不得出現問號方框()或錯碼。涵蓋:更新後的 `findings.json` / `exclusions.json`、**commit 訊息**、**PR 標題/描述**、終端訊息,與等級 emoji 🔴🟠🟡🔵。實作要點:
- **寫檔**:優先用助理的檔案寫入工具(預設 UTF-8 無 BOM)。若改用 shell 寫檔,**避免 PowerShell 的 `>``Out-File`**(預設可能寫成 UTF-16 或加 BOM);需要時用 `Set-Content -Encoding utf8NoBOM`,或在 bash 用 `printf`heredoc。
- **commit 訊息**:用 `git commit -m` 直接帶字串,或寫進 UTF-8 無 BOM 的檔案再 `git commit -F <file>`;確保 `git config i18n.commitEncoding utf-8`
- **PR body**:以 UTF-8 JSON 經 API 送出(如 `--data @body.json`,該檔為 UTF-8 無 BOM)。
- **送出前自我檢查**:產生含繁中的檔案/訊息後,回頭確認沒有亂碼或 BOM 再提交/送出。
- **自動執行原則**:除非使用者明確要求先確認,或遇到不可忽略的必要決策(例如目標分支缺失、pull 策略不明、無法安全解衝突、需人工判斷的設計取捨、push 憑證皆失敗),否則各階段只需輸出簡短計畫/進度後直接執行到完成;不要為一般修復、寫檔、分類 commit、push 或開 PR 反覆詢問。
- **Token 機密保護(極重要)**gitea token 一律**從環境變數讀取**(如 `$GITEA_TOKEN`),**絕不**寫死在 skill、commit、PR 內文或任何輸出;**不可** echo 含 token 的指令或 URL、不可寫進 log。所有顯示給使用者的指令/錯誤訊息都要**遮蔽 token**(如以 `***` 取代)。階段 E 完成後依規範清除對話內文(見 E6)。
---
## 參數
格式:`[--findings <路徑>] [--target <目標分支>] [--pr-desc <full|simple|自訂文字>] [--no-commit] [--no-pr] [--yes]`
- `--findings <路徑>`findings 檔路徑。**省略時預設 `.gitea/ai-review/findings.json`**(相對於工作目錄根)。
- `--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 一氣呵成);即使未帶此參數,也依「自動執行原則」盡量不中斷,只有必要決策才詢問。
---
## 階段 AGit 同步
### 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>` 不存在**(當前分支為本地獨有,遠端無對應)→ 依序嘗試切換到後備分支:
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 後備分支」並停止,不臆測其他分支。
- 切換完成後,A4 先更新該後備分支到最新;是否建立新的工作分支交由 A5 依來源分支與 PR 目標分支是否相同判斷。切換到後備分支屬不可忽略的狀態變更,需在輸出中明確告知使用者已從原分支切換到哪一個分支。
### A4. Pull(更新到最新)
對當前分支(可能已於 A3 切換為 developmaster)拉取最新:
```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`,此階段不因缺少目標分支而詢問。
- **若來源分支名稱與目標分支相同** → 不在該分支上直接修復/commit,也不可後續直接建立 head=base 的 PR。從目前已更新到最新的目標分支建立新的工作分支,後續階段(修復、commit、push、PR 的 `head`)都以新分支為準。
- **若來源分支名稱與目標分支不同** → 維持目前分支,不另開分支;即使目前分支存在 `origin/<source_branch>`,也照常在目前分支處理。
- 新分支名稱需可讀且避免覆蓋既有分支;預設格式:
```bash
work_branch="ai-review-resolve/${source_branch}-$(date +%Y%m%d-%H%M%S)"
git switch -c "${work_branch}"
```
- 建立前若本地或遠端已存在同名分支,換一個時間戳或短 hash,不可覆蓋既有分支。
- 若因未提交變更導致建立/切換新分支失敗,停止並回報,請使用者先處理未提交變更;不可強制丟棄。
- 建立新分支屬不可忽略的狀態變更,需在輸出中明確告知使用者「因來源分支與目標分支同名,已從 `<source_branch>` 建立並切換到 `<work_branch>`」。
### A6. 必要時嘗試解衝突
當 `git pull` 後出現衝突:
1. 用 `git status --porcelain` 與衝突標記定位衝突檔。
2. 讀取衝突檔脈絡,依專案現有行為與遠端變更做最小合理整合。
3. 可安全解決的衝突:編輯檔案移除衝突標記,執行 `git add -- <檔案...>` 標記已解決。
4. 無法安全判斷的衝突:停止處理,列出檔案、衝突原因與需要使用者決策的點;不要硬選任一邊。
5. 全部衝突解完後,依 git 當前狀態完成 merge / rebase 的必要步驟,確認 `git status --porcelain` 沒有未解衝突,再回到 A5 判斷是否需要建立新工作分支。
---
## 階段 B:依嚴重等級逐條解決問題或登記誤報
### B1. 讀取 findings 與 exclusions
讀 `--findings` 指定(或預設 `.gitea/ai-review/findings.json`)的檔案,內容為 **top-level JSON 陣列**,每筆是一個問題物件。常見欄位(不同產生器命名略有差異,需容錯對應):
| 語義 | 可能欄位名 |
| --- | --- |
| 嚴重等級 | `severity` / `level` / `等級` |
| 檔案位置 | `file` / `path` / `檔案位置` |
| 所在行數 | `line` / `lines` / `所在行數` |
| 問題標題 | `title` / `message` / `問題` |
| 問題描述 | `description` / `detail` / `描述` |
| 修正建議 | `suggestion` / `fix` / `建議` |
`findings.json` 與 `.gitea/ai-review/exclusions.json` 的 canonical 格式須對齊 `https://gitea.jsc.idv.tw/actions/code-review`
- 兩者都使用 **top-level JSON array**,不得包在 `{ "findings": [...] }`、`{ "exclusions": [...] }` 或 `{ "excluded_findings": [...] }`。
- `findings.json` 每筆至少保留 action 使用的必要欄位:
```json
{
"level": "critical|warning|info",
"role": "審查角色名稱",
"location": "檔案路徑:行號 或 檔案路徑",
"suggestion": "繁體中文(台灣用語)的具體修改建議"
}
```
- `exclusions.json` 每筆以最小且穩定的排除條目為主,優先保留原始問題文字、語言與語意;建議欄位:
```json
{
"location": "檔案路徑:行號 或 檔案路徑",
"role": "審查角色名稱",
"original_finding": "原 finding 的 suggestion 或完整問題文字",
"reason": "判斷為誤報或不適用的原因"
}
```
若既有 exclusions 使用 `suggestion`、`title`、`note` 等欄位,讀取時可容錯;寫回時仍要維持 top-level array,並盡量採用上述 canonical 欄位。
- **檔案不存在、內容為 `[]` 或空白** → 視為「無問題待解決」,輸出告知。`--no-commit` 時就此結束;否則**跳過階段 C 直接進入階段 D**(無待處理問題代表本 skill 未產生需要分類提交的修復變更)。
- **JSON 解析失敗** → 回報錯誤與檔案路徑並停止,不臆測內容、不亂改檔。
- `exclusions.json` 不存在時視為空陣列,必要時建立 `.gitea/ai-review/exclusions.json` 並寫入 `[]` 或新增後的排除項目。
- 若 `exclusions.json` 是舊 wrapper 格式(例如 `{ "exclusions": [...] }` 或 `{ "excluded_findings": [...] }`),先正規化為 top-level array 再寫回。
### B2. 依嚴重等級排序
把所有 finding 依等級由高到低排序:**🔴 嚴重 → 🟠 高 → 🟡 中 → 🔵 低**。
canonical 等級為 `critical` / `warning` / `info`;其他寫法容錯對應:`critical`/`blocker`→🔴、`high`/`major`/`warning`→🟠、`medium`/`moderate`→🟡、`low`/`minor`/`info`→🔵;無法辨識的等級**排在最後**並標註「等級未知」。寫回 `findings.json` 時盡量保留原欄位值,除非需要修正成 canonical 格式。
### B3. 逐條處理(高等級先)
輸出一張「處理計畫」表,除非使用者要求確認或遇到必要決策,否則**依排序由上而下逐條**處理:
| # | 等級 | 問題 | 檔案位置 | 行數 | 處理方式 |
| --- | --- | --- | --- | --- | --- |
對每一條 finding
1. 讀取對應檔案與其脈絡(依 `file` / `line`)。
2. 先判斷是否為誤報或不適用:例如程式碼脈絡證明指控不成立、已有等價防護、問題來自 action 已知排除規則、CI/CD 必要權限、或 finding 對非本次變更做不合理要求。
3. **可判斷為誤報** → 不改程式碼;將排除條目 append 到 `.gitea/ai-review/exclusions.json`,保留原始 finding 的語言與語意,避免過度改寫;若已有等價 exclusion(同檔案路徑、角色、原始文字或 suggestion 高度相似)則不要重複新增。
4. **確認為真問題且可安全修復** → 依 `suggestion``description` 在程式碼中實作最小合理修正;建議含糊或與現況不符時,依原始碼脈絡做最小且合理的修正。
5. **無法安全自動修復或無法確認真偽**(例如需求不明、牽涉設計取捨、檔案不存在)→ **不硬改**,標記為「待人工處理」並記錄原因,繼續下一條。
6. 逐條完成後,記錄該條結果(✅ 已解決 / 🚫 誤報已寫入 exclusions / ⏭️ 待人工處理 + 原因)。
> 一次只處理一條、修完再處理下一條,避免互相干擾;同檔多條問題可合併讀取但仍逐條套用修正。
### B4. 更新 findings.json 與 exclusions.json
**所有條目處理完畢後**,依結果更新 `findings.json` 與 `exclusions.json`
- **只有確認已解決,或已確認為誤報並成功寫入 `exclusions.json` 的 finding,才可從 `findings.json` 移除**。
- 誤報移出 `findings.json` 前,必須先確認 `exclusions.json` 已存在、格式合法、且該排除條目已寫入或已有等價條目;若 exclusions 寫入失敗,該 finding 必須保留。
- 待人工處理、無法確認真偽、或修復未驗證成功的 finding 必須保留在 `findings.json`。
- 寫回 `findings.json` 時只保留尚未解決且尚未登記為誤報的 finding;已解決或已登記為誤報的 finding 逐項移除,不需要等待其他問題也處理完。
- 如果移除後沒有任何 finding 需要保留,將 `findings.json` 寫成空陣列:
```json
[]
```
- 寫回 `findings.json` 時保留未解決 finding 的 canonical 欄位(`level`、`role`、`location`、`suggestion`)與有用原欄位;依嚴重度排序。
- 寫回 `exclusions.json` 時使用 top-level JSON array;新增項目 append 後依既有順序保留並去重。
- 兩個檔案都以 UTF-8(不含 BOM)寫入,結尾保留一個換行。
---
## 階段 C:分析變更並分類提交(`--no-commit` 時略過)
### C1. 盤點工作區變更
```bash
git status --porcelain=v1 -uall
git diff # 已追蹤檔的未暫存變更
git diff --staged # 已暫存變更
git ls-files --others --exclude-standard # 未追蹤檔
```
盤點時必須以 `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`…)。`findings.json` / `exclusions.json` 的問題狀態更新歸 `chore`。
- **未追蹤新檔**:必須照實際內容歸入對應 type,必要時在提交前明確 `git add -- <path>`,不可因為是新檔就略過。
### C3. 產出提交計畫
把變更檔依 type 分組,**每個 type 一個 commit**,輸出提交計畫;**所有變更項目都必須被追蹤並納入計畫,不能遺漏任何 `??` 未追蹤檔**。除非使用者要求確認或分組有不可忽略的取捨,否則直接提交:
| 順序 | type(範圍) | commit 訊息 | 納入檔案 |
| --- | --- | --- | --- |
- **commit 訊息格式**`type(範圍): 一句總結` —
- `type`:上表英文類型。
- `範圍`(括號內):**必須是這組異動實際牽涉的功能/模組/元件名稱**,而**不是**重述 type 的類別詞。
取名規則:優先沿用程式碼/專案中既有的識別名(檔名、模組名、skill 名、功能名,可中可英、保持與原碼一致),讓人一眼看出「改到哪個東西」。
- ✅ 對:`feat(使用者登入)`、`fix(結帳流程)`、`perf(物件查詢)`、`docs(README)`、`refactor(訂單服務)`、`chore(plugin 版本)`、`feat(code-review-resolve)`。
- ❌ 錯(只是重述 type,禁止):`feat(新增功能)`、`fix(修正錯誤)`、`perf(優化效能)`、`docs(文件)`、`chore(雜項)`。
- 一組異動橫跨多個功能而無單一主體時,才退而取最貼近的上層範圍(例如多個 manifest → `plugin 設定`)。
- `一句總結`:把這個 commit 內所有異動**總結成一句**繁體中文(簡短、聚焦做了什麼)。
- 範例:`feat(使用者登入): 新增帳密登入與 token 簽發`、`fix(結帳流程): 修正空購物車導致的結帳例外`、`perf(物件查詢): 改用批次查詢降低 DB 往返`、`docs(README): 補上安裝與呼叫方式說明`、`chore(ai-review 狀態): 更新 findings.json 與 exclusions.json`。
- **提交順序建議**`fix`/`feat` 等核心異動在前,`docs`/`style`/`chore` 在後(純屬建議,可依相依性調整)。
- **盤點要求**:提交計畫必須完整對應 C1 盤點出的所有變更項目,若有新檔或未追蹤檔,必須明確列入對應 commit,不能只根據 `git diff` 下結論。
### C4. 執行分類提交
對每組依序:
```bash
git add -- <該組檔案...> # 僅暫存該組檔案,逐組精準 add
git commit -m "type(範圍): 一句總結" # 範圍=實際異動的功能/模組名
```
- **逐組 addcommit**,確保每個 commit 只含該類異動;不要一次 `git add -A` 再混在一起。
- 改名/刪除檔一併納入對應組的 `git add``git add -A -- <路徑>` 或明確列出)。
- **所有 `??` 未追蹤檔都必須納入提交**;若是新檔,必要時要明確執行 `git add -- <path>`,不可漏掉。
- **不可只根據 `git diff` 判斷變更**;C1 盤點出的所有異動都必須反映到分類與提交,否則視為提交計畫不完整。
- commit 完成後進入階段 Dpush)。`--no-commit` 時不進入後續階段;若工作區無變更而沒有產生任何新 commit,仍進入階段 D,由 D 判斷無 commit 可 push 後跳到階段 E。
---
## 階段 DPush 當前分支(`--no-commit` 時略過;無 commit 可 push 時跳到階段 E
commit 完成後推送**當前分支**,依序嘗試三種方式,前者失敗才退到下一個:
推送前先記錄目前來源分支與其遠端基準,並確認是否有 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 新分支。
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}@<host>/<owner>/<repo>.git" \
"$(git rev-parse --abbrev-ref HEAD)"
```
(token 用完即棄,不寫進 git remote 設定、不落地。)
3. **再失敗 → 詢問使用者要如何 push**:列出失敗原因(遮蔽 token),請使用者指示推送方式,**不可自行猜測**其他憑證或來源。
push 成功後記下遠端分支名,進入階段 E。若因來源分支與目標分支相同而延後 push,記下原因並進入階段 E2 建立新分支。
---
## 階段 E:透過 Gitea API 發出 PR`--no-pr``--no-commit` 時略過)
### E1. 確定目標分支(不可猜想)
- 帶 `--target <分支>` → 直接採用。
- 若階段 D 已為了避免 source/base 相同而詢問過目標分支,沿用該目標分支。
- **否則必須詢問使用者目標分支**(可用 `git branch -r` 列出輔助選擇),**嚴禁臆測或預設**(不可自行假設 developmainmaster)。
### E2. 若來源分支與目標分支相同,改建 PR 來源分支
先取得目前 PR 來源分支:
```bash
git rev-parse --abbrev-ref HEAD
```
若來源分支名稱與 `--target` 指定(或使用者選定)的目標分支相同,**不可直接建立 head=base 的 PR,也不可先把原來源分支 push 到目標分支**。改用下列流程建立新的 PR 來源分支,並把原來源分支的 commit 帶入後再開 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. 從目標分支的遠端基準建立新分支。新分支名稱需可讀且避免覆蓋既有分支,例如:
```bash
git switch -c "ai-review-resolve/<短時間戳>" "origin/${target}"
```
3. 將原來源分支領先目標分支的 commit 依序帶入新分支:
```bash
git cherry-pick "origin/${target}..${source_branch}"
```
- cherry-pick 發生衝突時,先告知使用者,再依專案脈絡嘗試最小合理解衝突。
- 可安全解決的衝突:移除衝突標記、`git add -- <檔案...>`,再繼續 `git cherry-pick --continue`。
- 無法安全判斷的衝突:停止處理,列出衝突檔案與需要使用者決策的點;不要硬選任一邊。
4. 將新分支 push 到遠端,使用階段 D 的同一套 push 憑證策略,並把後續 PR 的 `head` 改為這個新分支。
若來源分支與目標分支不同,直接以目前分支作為 PR 的 `head`。
### E3. 解析 repo 座標
從 `git remote get-url origin` 解析出 **hostownerrepo**(例:`https://gitea.jsc.idv.tw/plugins/code-review.git` → host=`gitea.jsc.idv.tw`、owner=`plugins`、repo=`code-review`)。
### E4. 決定 PR 描述形式(完整版/簡單版/使用者輸入)
依 `--pr-desc` 三選一;若未提供則預設使用 `full`,不要為描述形式中斷詢問:
- **完整版(`full`****重新分析並總結** `git diff <target>...<PR 來源分支>`(比對 source 自分岔點以來的變更),整理成結構化繁體中文說明 —— 變更摘要、影響範圍、重點檔案/模組、風險或注意事項。不是貼原始 diff,而是「人讀得懂的總結」。
- **簡單版(`simple`)**:直接把本分支領先 target 的 commit 訊息**逐條列出**
```bash
git log --oneline "<target>..<PR 來源分支>"
```
以條列呈現每行 commit 訊息。
- **使用者輸入**:採用使用者提供的描述文字。
PR **標題**預設取一句總結(可用首個 feat/fix commit 或分支用途);使用者另有指定則從之。
產生 PR body 時必須使用實際換行與 Markdown 內容,不可把跳脫字串當作正文送出。若用 shell / `jq` 組 JSON,應以 UTF-8 暫存檔、heredoc、`printf` 或 `jq --rawfile` 帶入內容;避免讓 PR 顯示成 `## Commit\n\n- ...` 這類字面 `\n`。
### E5. 呼叫 Gitea API 建立 PR(使用 token
token 從環境變數讀取,呼叫 Gitea 建立 PR:
```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://<host>/api/v1/repos/<owner>/<repo>/pulls" \
--data @body.json
```
- **成功**:取回應中的 PR 連結/編號回報使用者。
- **失敗**:顯示 API 回應的錯誤訊息供排查(**先遮蔽 token**)。常見錯誤:目標分支不存在、已有相同 head→base 的 PR、token 權限不足。
### E6. 完成通知 + 清除對話內文(重要:可能含 token)
1. **通知使用者**:push 結果、PR 連結/編號、PR 描述採用哪種形式。
2. **清除 AI 助理對話內文**:因為 push/API 過程可能使對話內文殘留 gitea token,**完成後務必清除對話內文/上下文**以免外洩:
- Claude Code:提示使用者執行 `/clear`(或依當前助理對等指令清空對話)。
- 其他助理:執行各自清除對話/上下文的方式。
- 在清除前,請再次確認輸出與 log 中沒有任何明文 token。
---
## 總結
各階段執行後輸出:
- **階段 A**git fetch / pull 是否成功、當前分支是否存在於遠端(不存在時切到 develop/master 的結果)、若來源分支與目標分支同名是否已建立新工作分支、是否發生衝突、衝突是否已解決或仍需人工處理。
- **階段 B**:已解決 N 條(依等級分佈)、誤報寫入 exclusions M 條、待人工處理 K 條(列出原因)、`findings.json` 保留/移除筆數與 `exclusions.json` 新增筆數;若無待處理問題,說明已跳過階段 C 直接進入階段 D。
- **階段 C**:建立了哪幾個 commit(type+訊息+檔數),或為何略過(`--no-commit` / 無變更)。
- **階段 D**:push 成功與否、用了哪種方式(認證管理器/token/使用者指定),遠端分支名;若無 commit 可 push,說明已跳過 push 直接進入階段 E。
- **階段 E**:PR 連結/編號、目標分支、描述形式;並提醒已(或請使用者)清除對話內文以防 token 外洩。
---
## 呼叫方式
格式:`[--findings <路徑>] [--target <目標分支>] [--pr-desc <full|simple|自訂文字>] [--no-commit] [--no-pr] [--yes]` —
全部可省略(findings 預設 `.gitea/ai-review/findings.json`;目標分支省略時必問、不猜測)。token 一律由環境變數(如 `GITEA_TOKEN`)提供。
| 助理 | 呼叫 |
| --- | --- |
| Claude Code / Antigravity | `/jsc:code-review-resolve`,或 `/jsc:code-review-resolve --target develop --pr-desc full --yes`、`/jsc:code-review-resolve --no-pr`、`/jsc:code-review-resolve --no-commit` |
| Codex | `$code-review-resolve`,或 `$code-review-resolve --target develop --pr-desc full --yes`,或用 `/skills` 選單 |
| OpenCode | 描述需求(如「讀 .gitea/ai-review/findings.json 依嚴重度逐條處理,更新 findings/exclusions,把工作區變更依 conventional commit 分類提交,push 後對 develop 發 PR(完整版描述)」)自動觸發 |