docs(ai-code-review): 補齊各模組 JSDoc、指令檔逐行註解並重建 README
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
303104bb20
commit
1378f03595
+88
-14
@@ -7,8 +7,20 @@ import { warn } from './log.js';
|
||||
const ROLES_DIR = path.join(fileURLToPath(import.meta.url), '..', 'prompts', 'roles');
|
||||
|
||||
/**
|
||||
* 解析單一角色 .md 檔:前置 YAML frontmatter(徽章、代表色、面向、個性等)+ 本文(審查重點)。
|
||||
* 回傳合併後的角色物件:{ name, side, focus, badge, color, personality, body }。
|
||||
* 解析單一角色 Markdown 檔內容,拆出前置 YAML frontmatter 與本文。
|
||||
*
|
||||
* 會先將 CRLF 正規化為 LF,再以 `---` 分隔線切出 frontmatter(徽章、代表色、
|
||||
* 面向、個性等欄位)與其後的本文(審查重點 / 裁決準則)。frontmatter 欄位會
|
||||
* 被攤平到回傳物件,本文則放入 `body`(已去除頭尾空白)。
|
||||
*
|
||||
* @param {string} content - 角色 `.md` 檔的完整文字內容。
|
||||
* @returns {{ name?: string, side?: string, focus?: string, badge?: string,
|
||||
* color?: string, personality?: string, body: string,
|
||||
* [key: string]: unknown }} 合併 frontmatter 與本文後的角色物件。
|
||||
* @throws {Error} 當內容缺少合法 `---` frontmatter 區塊時拋出「角色檔缺少 frontmatter」。
|
||||
* @throws {import('js-yaml').YAMLException} 當 frontmatter 不是合法 YAML 時(由 `yaml.load` 拋出,未攔截)。
|
||||
*
|
||||
* @remarks 純字串處理,無任何檔案 IO;frontmatter 中若自帶 `body` 欄位會被本文覆蓋。
|
||||
*/
|
||||
export function parseRoleFile(content) {
|
||||
const normalized = content.replace(/\r\n/g, '\n');
|
||||
@@ -21,8 +33,16 @@ export function parseRoleFile(content) {
|
||||
let cachedRoles = null;
|
||||
|
||||
/**
|
||||
* 讀取並解析所有角色 .md,結果快取於模組層級(單次程序生命週期內檔案不變)。
|
||||
* 單一檔案解析失敗(壞 YAML、缺 frontmatter 等)時記錄警告並略過,不讓整個流程崩潰。
|
||||
* 讀取並解析 `ROLES_DIR` 下所有角色 `.md` 檔,依檔名排序後回傳角色陣列。
|
||||
*
|
||||
* 結果快取於模組層級(`cachedRoles`),同一程序生命週期內只讀檔一次;之後即使
|
||||
* 角色檔有變動也不會重新載入,需重啟程序才會生效。單一檔案解析失敗(壞 YAML、
|
||||
* 缺 frontmatter 等)只記錄警告並略過,不會中斷其他角色的載入。
|
||||
*
|
||||
* @returns {Array<ReturnType<typeof parseRoleFile>>} 已解析的角色物件陣列(依檔名排序)。
|
||||
*
|
||||
* @remarks 模組私有函式;使用同步檔案 IO。目錄不存在或無權限時,`fs.readdirSync`
|
||||
* 會在容錯範圍外拋出錯誤。
|
||||
*/
|
||||
function readRoleFiles() {
|
||||
if (cachedRoles) return cachedRoles;
|
||||
@@ -39,22 +59,47 @@ function readRoleFiles() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 載入攻擊方角色(Step3 產生 findings 用),依檔名排序。
|
||||
* 防守方(如 Paladin)不在此列,裁決邏輯由去重/誤報過濾流程承擔。
|
||||
* 載入所有「攻擊方」角色(frontmatter `side === 'attack'`),依檔名排序。
|
||||
*
|
||||
* 供 Step3 產生 findings 階段使用。防守方角色(如 Paladin)不在回傳之列,
|
||||
* 其裁決邏輯由去重 / 誤報過濾流程處理。
|
||||
*
|
||||
* @returns {Array<ReturnType<typeof parseRoleFile>>} 攻擊方角色物件陣列。
|
||||
*
|
||||
* @remarks 透過 `readRoleFiles` 取得快取後的全部角色再過濾,首次呼叫會觸發檔案讀取。
|
||||
*/
|
||||
export function loadRoles() {
|
||||
return readRoleFiles().filter(r => r.side === 'attack');
|
||||
}
|
||||
|
||||
/** 依 frontmatter name 取得單一角色(不分大小寫),找不到回傳 null。 */
|
||||
/**
|
||||
* 依 frontmatter `name` 取得單一角色(比對不分大小寫),找不到回傳 `null`。
|
||||
*
|
||||
* 不分攻擊方 / 防守方,所有已成功載入的角色皆可查得。
|
||||
*
|
||||
* @param {string} name - 角色名稱(大小寫不拘)。
|
||||
* @returns {ReturnType<typeof parseRoleFile> | null} 對應角色物件,無對應時為 `null`。
|
||||
*
|
||||
* @remarks 透過 `readRoleFiles` 取得快取角色清單,首次呼叫會觸發檔案讀取。
|
||||
*/
|
||||
export function loadRole(name) {
|
||||
const target = String(name).toLowerCase();
|
||||
return readRoleFiles().find(r => String(r.name).toLowerCase() === target) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 由角色定義組出攻擊方的 system prompt:
|
||||
* 套用其個性與審查重點本文,並要求以固定 JSON 陣列格式回傳 findings。
|
||||
* 由攻擊方角色定義組出其分析用 system prompt。
|
||||
*
|
||||
* 套用角色的徽章、名稱、面向(focus,缺省為「綜合」)、個性(personality,可選)
|
||||
* 與審查重點本文(body),並附上固定指示:分析 Git Diff 僅針對新增/修改處找問題,
|
||||
* 並以固定 JSON 陣列格式(level / role / location / problem / suggestion)回傳 findings,
|
||||
* 強制每條問題帶 `檔案路徑:行號`。
|
||||
*
|
||||
* @param {ReturnType<typeof parseRoleFile>} role - 攻擊方角色物件(需含 `name`、`body`;`badge`/`focus`/`personality` 可選)。
|
||||
* @returns {string} 組裝完成的多行 system prompt 文字。
|
||||
* @throws {TypeError} 當 `role` 為 `null`/`undefined` 時(未做防呆,存取屬性即拋出)。
|
||||
*
|
||||
* @remarks 純字串組裝,無副作用;空白分段行在串接前會被過濾移除。
|
||||
*/
|
||||
export function buildAnalysisPrompt(role) {
|
||||
return [
|
||||
@@ -90,8 +135,16 @@ export function buildAnalysisPrompt(role) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 由角色定義組出「補行號」的 system prompt:
|
||||
* 當該角色先前提出的問題只有檔名、缺行號時,請它對照 Git Diff 找出實際行號。
|
||||
* 組出「補行號」用的 system prompt。
|
||||
*
|
||||
* 用於某角色先前提出的 finding 其 `location` 只有檔名、缺行號的情境:請 LLM 對照
|
||||
* 該檔 Git Diff 找出問題對應的實際行號,並只回 `{"line": 數字}`(找不到回 `{"line": 0}`)。
|
||||
*
|
||||
* @param {ReturnType<typeof parseRoleFile> | null | undefined} [role] - 角色物件;可省略或為 null,
|
||||
* 此時名稱退回 `'AI Review'` 且不帶徽章與面向子句。
|
||||
* @returns {string} 組裝完成的多行 system prompt 文字。
|
||||
*
|
||||
* @remarks 使用選擇性串接(`?.`),對 `role` 為空值具防呆,不會拋出例外。
|
||||
*/
|
||||
export function buildLocateLinePrompt(role) {
|
||||
const name = role?.name || 'AI Review';
|
||||
@@ -104,9 +157,18 @@ export function buildLocateLinePrompt(role) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 由防守方角色定義組出「單條 finding 誤報裁決」的 system prompt:
|
||||
* 套用其個性與裁決準則本文,要求對一條 finding 判定成立或誤報,回固定 JSON 物件。
|
||||
* role 為 null 時退回不帶角色的通用裁判 prompt。
|
||||
* 由防守方角色定義組出「單條 finding 誤報裁決」用的 system prompt。
|
||||
*
|
||||
* `role` 存在時套用其徽章、名稱、面向(focus,缺省「裁決」)、個性與裁決準則本文(body);
|
||||
* `role` 為空值時退回固定的通用裁判 persona(🛡️ Paladin 聖騎士)。prompt 要求對一條
|
||||
* 攻擊方 finding 判定「成立 / 誤報」,並只回 `{"verdict", "reason"}`;無法確定時一律回
|
||||
* `"confirmed"`(寧可保留、不冤枉)。
|
||||
*
|
||||
* @param {ReturnType<typeof parseRoleFile> | null | undefined} role - 防守方角色物件;為空值時改用通用裁判 persona。
|
||||
* @param {string} [exclusionHint=''] - 額外的排除 / 已知誤報提示文字;為空字串時該行會被略過。
|
||||
* @returns {string} 組裝完成的多行 system prompt 文字。
|
||||
*
|
||||
* @remarks 純字串組裝,無副作用;空白分段行在串接前會被過濾移除。
|
||||
*/
|
||||
export function buildVerdictPrompt(role, exclusionHint = '') {
|
||||
const persona = role
|
||||
@@ -129,6 +191,18 @@ export function buildVerdictPrompt(role, exclusionHint = '') {
|
||||
].filter(l => l !== '').join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 由角色陣列產生「AI Code Review 團隊」介紹用的 Markdown 表格。
|
||||
*
|
||||
* 表格含三欄:角色(粗體,含徽章)、面向(focus)、個性(personality);缺省欄位以空字串呈現。
|
||||
* 通常用於 PR 留言 / 審查報告開頭呈現參與審查的角色陣容。
|
||||
*
|
||||
* @param {Array<ReturnType<typeof parseRoleFile>>} roles - 角色物件陣列(每個可含 `badge`/`name`/`focus`/`personality`)。
|
||||
* @returns {string} Markdown 格式的多行表格字串。
|
||||
* @throws {TypeError} 當 `roles` 非可迭代值(如 `null`/`undefined`)時,`for...of` 會拋出。
|
||||
*
|
||||
* @remarks 純字串組裝,無副作用;傳入空陣列會得到只有標題與表頭的表格。
|
||||
*/
|
||||
export function getRoleIntro(roles) {
|
||||
const lines = [
|
||||
'## 🤖 AI Code Review 團隊', '',
|
||||
|
||||
Reference in New Issue
Block a user