feat(ai-review 對話收斂): 讀 PR review 留言判斷解決狀態並收斂 findings

This commit is contained in:
Jeffery
2026-06-23 11:09:50 +08:00
parent 5e2ee59cca
commit a99163468b
3 changed files with 307 additions and 1 deletions
+73
View File
@@ -153,3 +153,76 @@ export async function postPullReview({ body, comments = [] }) {
);
return resp.data;
}
/**
* 取得 PR 上所有的 review(每個 review 可含多個行內 comment)。
*/
export async function listPullReviews() {
const resp = await axios.get(
api(`/repos/${GITEA_REPOSITORY}/pulls/${PR_NUMBER}/reviews`),
{ headers: headers(), timeout: 30000, httpsAgent },
);
return Array.isArray(resp.data) ? resp.data : [];
}
/**
* 取得單一 review 底下的所有行內 comment。
*/
export async function getPullReviewComments(reviewId) {
const resp = await axios.get(
api(`/repos/${GITEA_REPOSITORY}/pulls/${PR_NUMBER}/reviews/${reviewId}/comments`),
{ headers: headers(), timeout: 30000, httpsAgent },
);
return Array.isArray(resp.data) ? resp.data : [];
}
/**
* 取得 PR 上所有 review 的行內 comment,展平成單一陣列。
* 單一 review 取 comment 失敗時記錄警告並略過,不中斷整體流程。
*/
export async function listAllReviewComments() {
const reviews = await listPullReviews();
const all = [];
for (const review of reviews) {
if (!review?.id) continue;
try {
all.push(...await getPullReviewComments(review.id));
} catch (e) {
warn(`取得 review #${review.id} 的 comments 失敗(略過): ${e.message}`);
}
}
line(`取得 PR review comments: reviews=${reviews.length} comments=${all.length}`);
return all;
}
/**
* 解決(resolve)一個 review comment 所屬的對話。
* 對應 Gitea 官方 APIPOST /repos/{repo}/pulls/comments/{id}/resolve。
*/
export async function resolvePullReviewComment(commentId) {
const resp = await axios.post(
api(`/repos/${GITEA_REPOSITORY}/pulls/comments/${commentId}/resolve`),
{},
{ headers: headers(GITEA_COMMENT_TOKEN || GITEA_TOKEN), timeout: 30000, httpsAgent },
);
return resp.data;
}
/**
* 取得指定 ref(預設 PR head)下某檔案的最新文字內容;
* Gitea contents API 回傳 base64,這裡解碼成字串。檔案不存在或非文字時回傳空字串。
*/
export async function getFileContentAtRef(filePath, ref = PR_HEAD_SHA || PR_HEAD_BRANCH) {
try {
const resp = await axios.get(
api(`/repos/${GITEA_REPOSITORY}/contents/${encodeURIComponent(filePath).replace(/%2F/g, '/')}`),
{ headers: headers(), params: ref ? { ref } : undefined, timeout: 30000, httpsAgent },
);
const { content, encoding } = resp.data || {};
if (typeof content !== 'string') return '';
return encoding === 'base64' ? Buffer.from(content, 'base64').toString('utf8') : content;
} catch (e) {
warn(`取得檔案內容失敗(視為空): ${filePath}@${ref || 'head'} error=${e.message}`);
return '';
}
}