426 lines
21 KiB
JavaScript
426 lines
21 KiB
JavaScript
'use strict';
|
||
|
||
// 固定留言模板:本 action 發到 PR 的留言一律由此產生(繁體中文、UTF-8、表格優先)。
|
||
|
||
// 隱藏標記:辨識哪些留言是本 action 發的(步驟 2 標註過時時使用)。
|
||
const MARK = '<!-- ai-code-review -->';
|
||
|
||
// 舊留言標註前綴(步驟 2 的降級做法:無 resolve API 時編輯加註)。
|
||
const OUTDATED_PREFIX = '> 〔已過時〕本留言屬於較舊的審查回合。\n\n';
|
||
|
||
// 嚴重等級對應的 emoji 與排序權重。
|
||
const SEVERITY_EMOJI = { 嚴重: '🔴', 警告: '🟠', 建議: '🔵' };
|
||
const SEVERITY_ORDER = { 嚴重: 0, 警告: 1, 建議: 2 };
|
||
|
||
// 面向代碼對應的中文標籤。
|
||
const FOCUS_LABEL = {
|
||
logic: '邏輯',
|
||
security: '安全性',
|
||
efficiency: '效率',
|
||
style: '風格',
|
||
testing: '測試',
|
||
maintainability: '可維護性',
|
||
verdict: '裁決',
|
||
};
|
||
|
||
/**
|
||
* 把任意文字整理成可安全放進 Markdown 表格儲存格的單行內容。
|
||
*
|
||
* 處理順序:nullish 轉空字串 → 逸出管線符號(`|` → `\|`)→ 換行轉 `<br>`
|
||
* → 去除頭尾空白 → 空字串以 `—` 佔位。純函式、無副作用,任何輸入
|
||
* (含 null/undefined/數字)都不會拋出例外。
|
||
*
|
||
* @param {*} text - 任意待處理內容;非字串會先以 `String()` 轉型,null/undefined 視為空字串。
|
||
* @returns {string} 已逸出、單行化的儲存格內容;若結果為空則回傳 `'—'`。
|
||
* @remarks
|
||
* 使用情境:審查流程中所有表格型留言的共用防呆——例如步驟 4 的
|
||
* `diffComment()` 產生變更摘要表格時,檔名與用途欄位都經本函式處理,
|
||
* 避免檔名或 AI 產生的描述含 `|` 或換行而撐破 Markdown 表格。
|
||
* 本函式未匯出,僅供模組內部使用。
|
||
*/
|
||
function cell(text) {
|
||
return String(text ?? '')
|
||
.replace(/\|/g, '\\|')
|
||
.replace(/\r?\n/g, '<br>')
|
||
.trim() || '—';
|
||
}
|
||
|
||
/**
|
||
* 把審查面向代碼轉成「中文(原文)」的顯示字串。
|
||
*
|
||
* 以模組常數 `FOCUS_LABEL` 查表,支援 logic/security/efficiency/
|
||
* style/testing/maintainability/verdict 七種代碼;查表命中回傳
|
||
* 「中文(代碼)」格式,未命中則原樣回傳代碼,falsy 輸入回傳 `—`。
|
||
*
|
||
* @param {string} focus - 審查面向代碼(例如 `'logic'`、`'security'`);可為 undefined。
|
||
* @returns {string} 顯示字串:命中時如 `'邏輯(logic)'`;未命中時原樣回傳 `focus`;falsy 時回傳 `'—'`。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 5/7 的角色登場留言——`rolesComment()`
|
||
* 產生「角色|面向|個性」表格時,以本函式把每位審查員
|
||
* (攻擊方/防守方)的 focus 代碼轉成中英並列的面向欄位內容。
|
||
* 本函式未匯出,僅供模組內部使用。
|
||
*/
|
||
function focusLabel(focus) {
|
||
const label = FOCUS_LABEL[focus];
|
||
return label ? `${label}(${focus})` : focus || '—';
|
||
}
|
||
|
||
/**
|
||
* 產生審查流程步驟 3 的「審查工具」PR 留言內容。
|
||
*
|
||
* 留言以隱藏標記 `MARK` 開頭,包含工具資訊表格(工具/版本/模型/
|
||
* 審查 commit/Run Job 連結)與一張 mermaid 流程圖,說明整條審查管線
|
||
* (整理 git diff → 攻擊方找問題 → 防守方裁決 → 保存 findings → 留言到 PR)。
|
||
*
|
||
* @param {Object} params - 工具資訊(解構參數)。
|
||
* @param {string} params.toolName - 審查工具名稱,直接以行內程式碼呈現(不經 cell 逸出)。
|
||
* @param {string} params.version - 工具版本;經 cell() 防呆。
|
||
* @param {string} [params.model] - 使用的 AI 模型;falsy 時顯示「(工具預設)」。
|
||
* @param {string} params.sha - 本回合審查的 commit SHA;經 cell() 防呆。
|
||
* @param {string|number} params.runNumber - CI run 編號,作為連結文字;經 cell() 防呆。
|
||
* @param {string} params.runLink - CI run 的網址,直接內插為 Markdown 連結目標。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記,結尾帶換行)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 3——每回合審查開始時,先把工具身分與
|
||
* 管線流程圖留言到 PR,讓開發者知道這回合由哪個版本/模型執行;
|
||
* 留言開頭的 MARK 讓步驟 2 能辨識並將舊回合留言標註為過時。
|
||
*/
|
||
function toolComment({ toolName, version, model, sha, runNumber, runLink }) {
|
||
return `${MARK}
|
||
## 🤖 AI Code Review|審查工具
|
||
|
||
| 項目 | 內容 |
|
||
| --- | --- |
|
||
| 工具 | \`${toolName}\` |
|
||
| 版本 | \`${cell(version)}\` |
|
||
| 模型 | ${model ? `\`${cell(model)}\`` : '(工具預設)'} |
|
||
| 審查 commit | \`${cell(sha)}\` |
|
||
| Run Job | [#${cell(runNumber)}](${runLink}) |
|
||
|
||
\`\`\`mermaid
|
||
flowchart LR
|
||
A[整理 git diff] --> B[⚔️ 攻擊方找問題]
|
||
B --> C[🛡️ 防守方裁決]
|
||
C --> D[保存 findings]
|
||
D --> E[留言到 PR]
|
||
\`\`\`
|
||
`;
|
||
}
|
||
|
||
/**
|
||
* 產生審查流程步驟 4 的「變更摘要(送審 git diff)」PR 留言內容。
|
||
*
|
||
* 以四欄表格(檔案/用途/git diff 長度/最後更新時間)列出本回合
|
||
* 送審的每個檔案;diff 過長被截斷送審的檔案會加註「(過長截斷送審)」,
|
||
* 無任何檔案時補上「(無)」佔位列。結尾以引言統計納入審查的檔案數,
|
||
* 並在有排除檔案時註明 `.reviewignore` 排除數量。
|
||
*
|
||
* @param {Array<Object>} rows - 送審檔案清單,每筆一列。
|
||
* @param {string} rows[].file - 檔案路徑;經 cell() 防呆後以行內程式碼呈現。
|
||
* @param {string} rows[].purpose - 檔案用途說明;經 cell() 防呆。
|
||
* @param {number} rows[].lines - 該檔 git diff 行數。
|
||
* @param {number} rows[].chars - 該檔 git diff 字元數。
|
||
* @param {boolean} [rows[].truncated] - 是否因 diff 過長而截斷送審。
|
||
* @param {string} rows[].lastUpdated - 檔案最後更新時間;經 cell() 防呆。
|
||
* @param {number} ignoredCount - 依 `.reviewignore` 排除的檔案數;大於 0 才顯示排除註記。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 4——整理完 git diff 後,把「哪些檔案、多長、
|
||
* 是否截斷、哪些被 .reviewignore 排除」留言到 PR,讓開發者確認送審範圍
|
||
* 與 AI 實際看到的內容一致。
|
||
*/
|
||
function diffComment(rows, ignoredCount) {
|
||
const lines = [
|
||
MARK,
|
||
'## 📋 變更摘要(送審 git diff)',
|
||
'',
|
||
'| 檔案 | 用途 | git diff 長度 | 最後更新時間 |',
|
||
'| --- | --- | --- | --- |',
|
||
];
|
||
for (const row of rows) {
|
||
const length = `${row.lines} 行/${row.chars} 字元${row.truncated ? '(過長截斷送審)' : ''}`;
|
||
lines.push(`| \`${cell(row.file)}\` | ${cell(row.purpose)} | ${length} | ${cell(row.lastUpdated)} |`);
|
||
}
|
||
if (rows.length === 0) {
|
||
lines.push('| (無) | — | — | — |');
|
||
}
|
||
lines.push('');
|
||
lines.push(`> 共 ${rows.length} 個檔案納入審查${ignoredCount > 0 ? `;另有 ${ignoredCount} 個檔案依 \`.reviewignore\` 排除` : ''}。`);
|
||
return lines.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 產生審查流程步驟 5/7 共用的「角色登場」PR 留言內容。
|
||
*
|
||
* 以三欄表格(角色/面向/個性)列出本回合登場的審查員;
|
||
* 攻擊方(步驟 5)與防守方(步驟 7)共用本模板,僅標題不同。
|
||
* 面向欄位經 focusLabel() 轉成「中文(原文)」並列格式。
|
||
*
|
||
* @param {Object} params - 留言內容(解構參數)。
|
||
* @param {string} params.title - 留言標題(接在 `## ` 之後),由呼叫端決定攻擊方或防守方文案;不經逸出。
|
||
* @param {Array<Object>} params.roles - 登場角色清單,每筆一列。
|
||
* @param {Object} params.roles[].meta - 角色的中繼資料。
|
||
* @param {string} [params.roles[].meta.badge] - 角色徽章(通常為 emoji);缺省時以空字串呈現。
|
||
* @param {string} params.roles[].meta.name - 角色名稱,粗體呈現;經 cell() 防呆。
|
||
* @param {string} params.roles[].meta.focus - 審查面向代碼(如 `'logic'`);經 focusLabel() 轉為「中文(原文)」。
|
||
* @param {string} params.roles[].meta.personality - 角色個性描述;經 cell() 防呆。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 5(攻擊方登場)與步驟 7(防守方登場)——
|
||
* 在各階段開始審查前,把該回合參與的審查員角色、負責面向與個性
|
||
* 留言到 PR,讓開發者理解後續 findings 是由哪些視角產出的。
|
||
*/
|
||
function rolesComment({ title, roles }) {
|
||
const lines = [
|
||
MARK,
|
||
`## ${title}`,
|
||
'',
|
||
'| 角色 | 面向 | 個性 |',
|
||
'| --- | --- | --- |',
|
||
];
|
||
for (const role of roles) {
|
||
lines.push(
|
||
`| ${role.meta.badge || ''} **${cell(role.meta.name)}** | ${cell(focusLabel(role.meta.focus))} | ${cell(role.meta.personality)} |`,
|
||
);
|
||
}
|
||
return lines.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 產生審查流程步驟 9 的「單條嚴重問題」留言內容(掛在程式碼行上)。
|
||
*
|
||
* 留言含嚴重度 emoji 標題(審查員具名)、可選的位置與程式碼片段、
|
||
* 「問題」「修改建議」段落、可選的「建議寫法」程式碼區塊,並以
|
||
* 「開發者可直接回覆本留言討論或說明取捨」收尾。
|
||
*
|
||
* @param {Object} finding - 單條審查發現。
|
||
* @param {string} finding.severity - 嚴重等級(嚴重/警告/建議);決定標題 emoji,未知等級 fallback 為 🔴。
|
||
* @param {string} [finding.badge] - 審查員徽章(通常為 emoji);缺省時省略。
|
||
* @param {string} finding.reviewer - 審查員名稱。
|
||
* @param {string} finding.file - 問題所在檔案路徑;僅 withLocation 為 true 時輸出。
|
||
* @param {number} finding.startLine - 問題起始行號;僅 withLocation 為 true 時輸出。
|
||
* @param {number} finding.endLine - 問題結束行號;僅 withLocation 為 true 時輸出。
|
||
* @param {string} [finding.problem] - 問題描述;缺省以 `—` 佔位。
|
||
* @param {string} [finding.suggestion] - 修改建議;缺省以 `—` 佔位。
|
||
* @param {string} [finding.suggestedCode] - 建議寫法程式碼;有值才輸出「建議寫法」區塊。
|
||
* @param {string} [snippet] - 問題所在的原始程式碼片段;有值才輸出程式碼圍欄。
|
||
* @param {Object} [options] - 選項(解構參數,預設空物件)。
|
||
* @param {boolean} [options.withLocation=false] - 是否在內文標明「位置」(檔案與起訖行);掛行留言本身已定位時可省略。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 9——防守方裁決後保留的每條「嚴重」finding,
|
||
* 逐條以 inline review comment 掛在 PR 對應程式碼行上;若平台不支援
|
||
* 掛行而降級為一般留言時,改以 `withLocation: true` 在內文標明位置。
|
||
*/
|
||
function severeCommentBody(finding, snippet, { withLocation = false } = {}) {
|
||
const emoji = SEVERITY_EMOJI[finding.severity] || '🔴';
|
||
const parts = [MARK, `### ${emoji} ${finding.severity}|${finding.badge || ''} ${finding.reviewer}`, ''];
|
||
if (withLocation) {
|
||
parts.push(`**位置**:\`${finding.file}\` 第 ${finding.startLine}–${finding.endLine} 行`, '');
|
||
}
|
||
if (snippet) {
|
||
parts.push('```', snippet, '```', '');
|
||
}
|
||
parts.push('**問題**', '', finding.problem || '—', '');
|
||
parts.push('**修改建議**', '', finding.suggestion || '—', '');
|
||
if (finding.suggestedCode) {
|
||
parts.push('**建議寫法**', '', '```', finding.suggestedCode, '```', '');
|
||
}
|
||
parts.push('> 開發者可直接回覆本留言討論或說明取捨。');
|
||
return parts.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 產生審查流程步驟 9 的「嚴重問題 review 總覽」內容。
|
||
*
|
||
* 作為 PR review 的整體 body:標題標明嚴重問題總數,並說明各條問題
|
||
* 已逐條掛在對應程式碼行上(由 severeCommentBody() 產生的 inline
|
||
* comment),請開發者逐一處理或回覆說明。
|
||
*
|
||
* @param {number} count - 本回合嚴重 findings 的總條數,直接內插進標題。
|
||
* @returns {string} review 總覽 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 9——把所有嚴重 findings 以單一 PR review
|
||
* 送出時,本函式產生 review 的 body 總覽,搭配每條 finding 各自的
|
||
* inline comment(severeCommentBody),讓開發者先看到總數再逐條處理。
|
||
*/
|
||
function severeReviewBody(count) {
|
||
return `${MARK}
|
||
## 🔴 嚴重問題(共 ${count} 條)
|
||
|
||
以下嚴重問題已逐條掛在對應程式碼行上,請逐一處理或回覆說明。`;
|
||
}
|
||
|
||
/**
|
||
* 產生審查流程步驟 10 的「其他問題(警告+建議)彙整」PR 留言內容。
|
||
*
|
||
* 非嚴重的 findings 不逐條掛在程式碼行上,改以六欄表格
|
||
* (等級/審查員/檔案名稱/問題起訖行數/問題描述/修改建議)
|
||
* 集中呈現;等級欄依 SEVERITY_EMOJI 顯示 emoji(警告 🟠、建議 🔵,
|
||
* 未知等級 fallback 為 🔵),描述與建議經 cell() 防呆避免撐破表格。
|
||
*
|
||
* @param {Array<Object>} findings - 警告+建議等級的審查發現清單,每筆一列。
|
||
* @param {string} findings[].severity - 嚴重等級(警告/建議);決定等級欄 emoji。
|
||
* @param {string} [findings[].badge] - 審查員徽章(通常為 emoji);缺省時省略。
|
||
* @param {string} findings[].reviewer - 審查員名稱;經 cell() 防呆。
|
||
* @param {string} findings[].file - 問題所在檔案路徑;經 cell() 防呆後以行內程式碼呈現。
|
||
* @param {number} findings[].startLine - 問題起始行號。
|
||
* @param {number} findings[].endLine - 問題結束行號。
|
||
* @param {string} findings[].problem - 問題描述;經 cell() 防呆。
|
||
* @param {string} findings[].suggestion - 修改建議;經 cell() 防呆。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 10——防守方裁決後留下的「警告」與「建議」
|
||
* 等級 findings,不像嚴重問題逐條掛行(步驟 9),而是彙整成單一
|
||
* 表格留言發到 PR,讓開發者一覽非阻擋性的改善事項。
|
||
*/
|
||
function othersComment(findings) {
|
||
const lines = [
|
||
MARK,
|
||
`## 🟠 其他問題(警告+建議,共 ${findings.length} 條)`,
|
||
'',
|
||
'| 等級 | 審查員 | 檔案名稱 | 問題起訖行數 | 問題描述 | 修改建議 |',
|
||
'| --- | --- | --- | --- | --- | --- |',
|
||
];
|
||
for (const finding of findings) {
|
||
const emoji = SEVERITY_EMOJI[finding.severity] || '🔵';
|
||
lines.push(
|
||
`| ${emoji} ${finding.severity} | ${finding.badge || ''} ${cell(finding.reviewer)} | \`${cell(finding.file)}\` | ${finding.startLine}–${finding.endLine} | ${cell(finding.problem)} | ${cell(finding.suggestion)} |`,
|
||
);
|
||
}
|
||
return lines.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 產生建問題模式(input: create-issue)新 issue 的本文:
|
||
* 以 PR 描述為主體(缺省時以「(PR 無描述)」佔位),
|
||
* 尾端附水平線與追溯引言,標明本 issue 由 AI Code Review 依哪個 PR 自動建立、
|
||
* 問題明細見 issue 下方留言。
|
||
*
|
||
* @param {Object} params - 解構參數。
|
||
* @param {number} params.prNumber - 來源 PR 編號;內插到追溯引言(`PR #N`)。
|
||
* @param {string} [params.prBody] - PR 描述原文;nullish 或 trim 後為空時輸出佔位文字。
|
||
* @returns {string} 完整 issue 本文 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:建問題模式下 `main()`(src/index.js)的 `createIssueAndFlushBufferedComments` 建立 issue 時,
|
||
* 以「標題=PR 標題、本文=本函式輸出」呼叫 `gitea.createIssue`,
|
||
* 讓 issue 讀者能從本文回溯到觸發審查的 PR,再從下方留言逐條查看問題明細。
|
||
*/
|
||
function issueBody({ prNumber, prBody }) {
|
||
const body = (prBody || '').trim();
|
||
return `${MARK}
|
||
${body || '(PR 無描述)'}
|
||
|
||
---
|
||
> 本問題由 AI Code Review 依 PR #${prNumber} 的審查結果自動建立,問題明細見下方留言。`;
|
||
}
|
||
|
||
/**
|
||
* 產生建問題模式(input: create-issue)下單條 finding 的 issue 留言內容。
|
||
*
|
||
* 固定模板:嚴重等級 emoji 標題(審查員具名)→ 位置(檔案與起訖行數,一律輸出)
|
||
* → 問題描述 → 修改建議 → 可選的「建議寫法」程式碼區塊。
|
||
* issue 留言無法掛在程式碼行上,故位置固定以內文標明。
|
||
*
|
||
* @param {Object} finding - 單條審查發現。
|
||
* @param {string} finding.severity - 嚴重等級(嚴重/警告/建議);決定標題 emoji,未知等級 fallback 為 🔵。
|
||
* @param {string} [finding.badge] - 審查員徽章(通常為 emoji);缺省時省略。
|
||
* @param {string} finding.reviewer - 審查員名稱。
|
||
* @param {string} finding.file - 問題所在檔案路徑(repo 相對路徑)。
|
||
* @param {number} finding.startLine - 問題起始行號(新版檔案行號)。
|
||
* @param {number} finding.endLine - 問題結束行號(新版檔案行號)。
|
||
* @param {string} [finding.problem] - 問題描述;缺省以 `—` 佔位。
|
||
* @param {string} [finding.suggestion] - 修改建議;缺省以 `—` 佔位。
|
||
* @param {string} [finding.suggestedCode] - 建議寫法程式碼;有值才輸出「建議寫法」區塊。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:建問題模式下 `review.postSevereToIssue` 與 `review.postOthersToIssue`
|
||
* 把每條 finding 以本函式產生留言內容、經 `gitea.createCommentOnIssue`
|
||
* 發布到追蹤 issue 上,作為問題明細的追蹤紀錄。
|
||
*/
|
||
function issueFindingComment(finding) {
|
||
const emoji = SEVERITY_EMOJI[finding.severity] || '🔵';
|
||
const parts = [
|
||
MARK,
|
||
`### ${emoji} ${finding.severity}|${finding.badge || ''} ${finding.reviewer}`,
|
||
'',
|
||
`**位置**:\`${finding.file}\` 第 ${finding.startLine}–${finding.endLine} 行`,
|
||
'',
|
||
'**問題描述**',
|
||
'',
|
||
finding.problem || '—',
|
||
'',
|
||
'**修改建議**',
|
||
'',
|
||
finding.suggestion || '—',
|
||
];
|
||
if (finding.suggestedCode) {
|
||
parts.push('', '**建議寫法**', '', '```', finding.suggestedCode, '```');
|
||
}
|
||
return parts.join('\n');
|
||
}
|
||
|
||
/**
|
||
* 產生「無可審查變更」時的 PR 留言內容。
|
||
*
|
||
* 當 git diff 套用 `.reviewignore` 過濾後沒有任何檔案需要送審時,
|
||
* 以本留言取代正常的變更摘要(沿用相同標題「📋 變更摘要」),
|
||
* 說明本次 PR 沒有可審查的變更並宣告視為審查通過;
|
||
* 有檔案被排除時加註排除數量。
|
||
*
|
||
* @param {number} ignoredCount - 依 `.reviewignore` 排除的檔案數;大於 0 才顯示「(N 個檔案被排除)」註記。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:審查流程步驟 4 的替代路徑——整理 git diff 時發現
|
||
* 過濾後送審清單為空(例如整包變更都被 .reviewignore 排除),
|
||
* 直接以本留言告知開發者本回合視為審查通過,不再進入
|
||
* 攻擊方/防守方審查階段。
|
||
*/
|
||
function nothingToReviewComment(ignoredCount) {
|
||
return `${MARK}
|
||
## 📋 變更摘要(送審 git diff)
|
||
|
||
本次 PR 套用 \`.reviewignore\` 後**沒有可審查的變更**${ignoredCount > 0 ? `(${ignoredCount} 個檔案被排除)` : ''},視為審查通過。`;
|
||
}
|
||
|
||
/**
|
||
* 產生建問題模式(input: create-issue)下,回貼到「PR」的追蹤問題連結留言。
|
||
*
|
||
* 建問題模式把審查內容全部發到 issue、不留在 PR;本留言是 PR 上唯一的一則審查留言,
|
||
* 提供 issue 連結與問題數量統計,讓 PR 讀者一眼看到「本次審查結果在哪個 issue」。
|
||
*
|
||
* @param {Object} params - 解構參數。
|
||
* @param {number} params.issueNumber - 追蹤問題的 issue 編號;內插為 Markdown 連結文字。
|
||
* @param {string} params.issueUrl - 追蹤問題的 issue 網址;作為 Markdown 連結目標。
|
||
* @param {number} params.severeCount - 嚴重問題條數,顯示在統計。
|
||
* @param {number} params.otherCount - 警告+建議問題條數,顯示在統計。
|
||
* @returns {string} 完整留言 Markdown 字串(含 MARK 隱藏標記)。
|
||
* @remarks
|
||
* 使用情境:建問題模式下 `main()`(src/index.js)在 issue 建立並寫入全部審查內容後,
|
||
* 以本函式對 PR 留一則連結留言,達成「問題關聯回 PR」;issue 內文另以
|
||
* {@link issueBody} 反向引用 `PR #N`,形成雙向交叉連結。
|
||
*/
|
||
function prIssueLinkComment({ issueNumber, issueUrl, severeCount, otherCount }) {
|
||
return `${MARK}
|
||
## 🔍 AI Code Review|已建立追蹤問題
|
||
|
||
本次審查結果已彙整到 issue [#${issueNumber}](${issueUrl})(🔴 嚴重 ${severeCount} 條、🟠🔵 警告+建議 ${otherCount} 條),請至該問題追蹤與討論。`;
|
||
}
|
||
|
||
module.exports = {
|
||
MARK,
|
||
OUTDATED_PREFIX,
|
||
SEVERITY_EMOJI,
|
||
SEVERITY_ORDER,
|
||
toolComment,
|
||
diffComment,
|
||
rolesComment,
|
||
severeCommentBody,
|
||
severeReviewBody,
|
||
othersComment,
|
||
issueBody,
|
||
issueFindingComment,
|
||
nothingToReviewComment,
|
||
prIssueLinkComment,
|
||
};
|