feat(建問題模式): PR 相依追蹤 issue、標籤建立時帶入、議題問題逐條可回覆

整併議題 #7 的建問題模式流程調整與多項 findings 修復(index.js、gitea.js 交錯同檔,整檔歸此)。

- addIssueDependency:建立追蹤 issue 後讓 PR 相依於該 issue,issue 關閉前無法合併(需 repo 啟用問題相依)。
- 🔵 #10(Rogue/efficiency):標籤改於建立 issue 前先 selectLabels、createIssue 時一次帶入,移除事後 addLabelsToIssue 補掛的多餘 API 往返(addLabelsToIssue 保留為通用工具)。
- 使用者補充:建問題模式警告+建議改逐條發到 issue(postOthersToIssue),每條問題可個別回覆。
- 🔴 #1(Mage/logic)+使用者補充:resolveOldComments 延後到審查成功產生結果、發布問題留言前才執行,並以 currentRunCommentIds 排除本回合新留言,避免前置步驟失敗時舊結果被清卻無新結果。
- 🔵 #9(Bard/style):修正「無保留問題」段落註解與實作(靜默通過、不留言)一致。
- 同步更新相關 JSDoc(含 gitea.js listLabels/createIssue/addLabelsToIssue 使用情境)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeffery
2026-07-20 15:00:15 +08:00
co-authored by Claude Opus 4.8
parent 35887f1a68
commit e44a8ed0cb
2 changed files with 104 additions and 43 deletions
+35 -7
View File
@@ -134,9 +134,9 @@ function createIssueComment(ctx, body) {
* @returns {Promise<object[]>} 標籤物件陣列(每筆含 `id`、`name`、`color` 等欄位,
* 依 Gitea API 回應而定);存取庫無標籤時為空陣列。
* @throws {Error} 任一頁請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 收束時
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 於確定有保留問題後
* 先以本函式取得可用標籤,再交給 `review.selectLabels` 讓 AI 挑出適合的標籤子集合,
* 最後以 `addLabelsToIssue` 補掛到追蹤 issue 上
* 最後於建立追蹤 issue 時(`createIssue`)一次帶入這些標籤
*/
function listLabels(ctx) {
return listAll(ctx, `/repos/${ctx.owner}/${ctx.repo}/labels`);
@@ -157,8 +157,8 @@ function listLabels(ctx) {
* `html_url` 等欄位,依 Gitea API 回應而定)。
* @throws {Error} 請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 的 `ensureIssueCreated`
* 以 PR 標題/描述為 issue 標題與本文(先不掛標籤)呼叫本函式建立追蹤問題的 issue,
* 之後再把審查內容留言到該 issue、並以 `addLabelsToIssue` 補掛標籤
* 以 PR 標題/描述為 issue 標題與本文,並帶入 `review.selectLabels` 事先挑好的標籤 id
* 呼叫本函式一次建立追蹤問題的 issue(連同標籤),之後再把審查內容逐條留言到該 issue。
*/
function createIssue(ctx, { title, body, labels }) {
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues`, {
@@ -180,15 +180,42 @@ function createIssue(ctx, { title, body, labels }) {
* @returns {Promise<object[]|null>} 追加後該 issue 的標籤陣列(依 Gitea API 回應而定);
* `labels` 為空時回傳 `null`(未發出請求)。
* @throws {Error} 請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
* @remarks 使用情境:建問題模式(input: create-issue)下先以空標籤建立 issue、
* 待防守方裁決得到保留問題後,再以 `selectLabels` 挑出的標籤 id 呼叫本函式補掛
* 讓標籤挑選能參考最終的問題清單
* @remarks 使用情境:為既有 issue 動態追加標籤的通用工具(不影響既有標籤)。
* 註:建問題模式主流程已改為「建立 issue 時一次帶入標籤」(見 `createIssue`
* 不再於事後補掛;本函式保留為 Gitea 客戶端的通用能力,供需要事後追加標籤的情境使用
*/
function addLabelsToIssue(ctx, issueNumber, labels) {
if (!labels || labels.length === 0) return Promise.resolve(null);
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${issueNumber}/labels`, { labels });
}
/**
* 建立「問題相依」關係:讓 URL 上的 issue/PR 相依於(被阻擋於)表單指定的 issue。
* 對應 endpoint`POST /repos/{owner}/{repo}/issues/{issueNumber}/dependencies`
* body 為 IssueMeta`{index, owner, repo}`)。
*
* 語義:URL 的 issue`issueNumber`)相依於 body 的 issue`dependency`)——
* 在 `dependency` 關閉前,`issueNumber` 無法合併/關閉。本 endpoint 需 repo 啟用
* 「問題相依(issue dependencies)」功能,屬版本/設定相依;未啟用或不支援時 API 會回非 2xx。
*
* @param {object} ctx - 執行環境 context。必要欄位:`apiBase`、`token`、
* `owner`repo 擁有者)、`repo`repo 名稱)。
* @param {number|string} issueNumber - 要被阻擋的 issuePR 編號(相依方)。
* @param {number} dependency - 作為阻擋來源的 issue 編號(同一 repo)。
* @returns {Promise<object>} 建立成功的相依關係物件(依 Gitea API 回應而定)。
* @throws {Error} 請求失敗(非 2xx,例如未啟用問題相依功能)由底層 `api` 丟出,錯誤附 `status`、`data`。
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 建立追蹤 issue 後,
* 以本函式把「PR`ctx.prNumber`)相依於追蹤 issue」,讓 issue 完成/關閉前 PR 無法合併;
* 呼叫端以 try/catch 降級(功能未啟用時記 WRN、不阻斷流程)。
*/
function addIssueDependency(ctx, issueNumber, dependency) {
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${issueNumber}/dependencies`, {
index: dependency,
owner: ctx.owner,
repo: ctx.repo,
});
}
/**
* 列出 PR 上的全部一般留言(自動分頁撈取,每頁 50 筆直到取完)。
* 對應 endpoint`GET /repos/{owner}/{repo}/issues/{prNumber}/comments`。
@@ -324,6 +351,7 @@ module.exports = {
listLabels,
createIssue,
addLabelsToIssue,
addIssueDependency,
listIssueComments,
editIssueComment,
createReview,