From 46dd8320d143524309d8bdc284463bd5739cc991 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 11 May 2026 07:52:21 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9A=8E=E6=AE=B5=E4=BA=8C=20-=20Findi?= =?UTF-8?q?ngs=20=E7=94=A2=E7=94=9F=E8=88=87=E5=90=88=E4=BD=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app/findings.js: 各角色分析 diff、讀取舊 findings、合併去重、等級排序 - app/main.js: 實作 Step2/Step3,log findings 統計 --- app/findings.js | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ app/main.js | 25 ++++++++++++++++---- 2 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 app/findings.js diff --git a/app/findings.js b/app/findings.js new file mode 100644 index 0000000..3975a80 --- /dev/null +++ b/app/findings.js @@ -0,0 +1,63 @@ +import fs from 'fs'; +import path from 'path'; +import { chatJSON } from './llm.js'; +import { FINDINGS_PATH } from './config.js'; + +const LEVELS = ['critical', 'warning', 'info']; + +/** + * 用單一角色分析 diff,回傳 findings 陣列 + */ +export async function analyzeWithRole(role, diff) { + console.log(` [${role.name}] 開始分析...`); + const findings = await chatJSON(role.system_prompt, `以下是 Git Diff 內容:\n\n${diff}`); + // 確保每筆都有必要欄位,並標記為新問題 + const valid = findings.filter(f => f.level && f.role && f.location && f.suggestion) + .map(f => ({ ...f, is_new: true })); + console.log(` [${role.name}] 找到 ${valid.length} 個問題`); + return valid; +} + +/** + * 讀取舊 findings(從 workspace 的 FINDINGS_PATH) + */ +export function loadOldFindings(workspace) { + const fullPath = path.join(workspace, FINDINGS_PATH); + if (!fs.existsSync(fullPath)) { + console.log(' 舊 findings 檔案不存在,視為空'); + return []; + } + try { + const data = JSON.parse(fs.readFileSync(fullPath, 'utf8')); + const old = (Array.isArray(data) ? data : []).map(f => ({ ...f, is_new: false })); + console.log(` 讀取舊 findings: ${old.length} 筆`); + return old; + } catch (e) { + console.log(` ⚠️ 讀取舊 findings 失敗: ${e.message},視為空`); + return []; + } +} + +/** + * 合併新舊 findings,以 (role + location + suggestion前50字) 為 key 去除重複 + * 舊問題保留,新問題若與舊問題重複則捨棄 + */ +export function mergeFindings(oldFindings, newFindings) { + const key = f => `${f.role}|${f.location}|${String(f.suggestion).slice(0, 50)}`; + const seen = new Set(oldFindings.map(key)); + const deduped = newFindings.filter(f => { + if (seen.has(key(f))) return false; + seen.add(key(f)); + return true; + }); + const merged = [...oldFindings, ...deduped]; + console.log(` 合併結果: 舊=${oldFindings.length} 新(去重後)=${deduped.length} 總計=${merged.length}`); + return merged; +} + +/** + * 依等級排序(critical > warning > info) + */ +export function sortByLevel(findings) { + return [...findings].sort((a, b) => LEVELS.indexOf(a.level) - LEVELS.indexOf(b.level)); +} diff --git a/app/main.js b/app/main.js index 16981ba..e317d61 100644 --- a/app/main.js +++ b/app/main.js @@ -1,6 +1,9 @@ import { GITEA_REPOSITORY, PR_NUMBER, PR_HEAD_BRANCH, PR_BASE_BRANCH, getLLMConfig } from './config.js'; import { loadRoles, getRoleIntro } from './roles.js'; import { getPRDiff, postComment } from './gitea.js'; +import { analyzeWithRole, loadOldFindings, mergeFindings, sortByLevel } from './findings.js'; + +const WORKSPACE = process.env.GITHUB_WORKSPACE || '/workspace'; async function main() { console.log('='.repeat(60)); @@ -46,11 +49,25 @@ async function main() { console.log(` ⚠️ comment 發布失敗(繼續執行): ${e.message}`); } - console.log('\n📊 Step2: Findings 產生(待實作)'); - console.log(' [stub] 各角色分析 diff...'); + // Step2: 各角色分析 diff 產生新 findings + console.log('\n📊 Step2: Findings 產生'); + const newFindings = []; + for (const role of roles) { + try { + const found = await analyzeWithRole(role, diff); + newFindings.push(...found); + } catch (e) { + console.log(` ⚠️ [${role.name}] 分析失敗(跳過): ${e.message}`); + } + } + console.log(` Step2 完成: 新 findings 總計 ${newFindings.length} 筆`); - console.log('\n🔀 Step3: Findings 合併與去重(待實作)'); - console.log(' [stub] 合併新舊 findings...'); + // Step3: 讀取舊 findings,合併去重 + console.log('\n🔀 Step3: Findings 合併'); + const oldFindings = loadOldFindings(WORKSPACE); + const mergedFindings = mergeFindings(oldFindings, newFindings); + const sorted = sortByLevel(mergedFindings); + console.log(` Step3 merged findings total=${sorted.length} (critical=${sorted.filter(f=>f.level==='critical').length} warning=${sorted.filter(f=>f.level==='warning').length} info=${sorted.filter(f=>f.level==='info').length})`); console.log('\n📝 Step4: Findings 寫入與 Comment 發布(待實作)'); console.log(' [stub] 寫入 findings.json,發布 comment...');