308 lines
21 KiB
JSON
308 lines
21 KiB
JSON
{
|
||
"generatedAt": "2026/07/20 18:31:07",
|
||
"commitSha": "0c29daa01434d6b6fa1b20ce81c513c75b1b1c6a",
|
||
"prNumber": null,
|
||
"tool": {
|
||
"name": "code-review-resolve",
|
||
"version": "0.0.9",
|
||
"model": "(工具預設)"
|
||
},
|
||
"findings": [
|
||
{
|
||
"id": "F001",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "警告",
|
||
"file": "src/index.js",
|
||
"startLine": 119,
|
||
"endLine": 139,
|
||
"problem": "流程步驟編號被當成跨模組識別值,散落在 `index.js`、各 library 的日誌與 JSDoc、README 流程圖及功能表。這次僅因插入並延後一步,就必須同步修改大量 `步驟2`~`步驟8` 字串,而且實際執行順序已變成 1、3~8、2、9~10;未來再調整流程時非常容易讓文件、日誌與程式碼脫節。",
|
||
"suggestion": "以穩定的語意階段名稱取代硬編碼序號,例如 `TOOL_DETECTION`、`COLLECT_DIFF`、`RESOLVE_OLD_COMMENTS`,由單一常數表集中決定顯示名稱;README 的流程順序則從同一份定義產生,或至少不要在各函式文件重複紀錄易變的數字。",
|
||
"suggestedCode": "```\nconst PHASE = Object.freeze({\n FAST_RESULT: '快速回報',\n DETECT_TOOL: '偵測 AI 工具',\n COLLECT_DIFF: '整理變更',\n RESOLVE_OLD: '處理舊留言',\n PUBLISH_FINDINGS: '發布審查結果',\n});\n\nlog(PHASE.DETECT_TOOL, 'INF', `選用工具:${tool.name}。`);\n```",
|
||
"sourceIssue": 11
|
||
},
|
||
{
|
||
"id": "F002",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "警告",
|
||
"file": "readme.md",
|
||
"startLine": 59,
|
||
"endLine": 130,
|
||
"problem": "功能索引把分支名稱與原始碼行號硬編碼在數十個連結中;本次僅因程式碼增行,就必須人工把大量 `#L...` 全面更新,已直接顯示這份文件存在高同步成本。之後任一檔案前段增刪程式碼,都會讓這些連結再次漂移,而且指向會持續變動的 `develop` 分支,使舊版 README 與實際連結內容無法穩定對應。",
|
||
"suggestion": "不要手動維護原始碼行號。若只需導覽,連到檔案並由右欄既有章節錨點提供函式級定位;若必須精確指向定義,應由 AST/文件產生工具在 CI 自動建立索引,並連到固定 commit SHA 或版本 tag。至少增加連結檢查,避免半年後整張功能表悄悄失準。",
|
||
"suggestedCode": "```\n| 功能名稱 | 功能描述 |\n| --- | --- |\n| [gitrepo.resolveMergeBase](src/lib/gitrepo.js) | [解析 base 分支與 HEAD 的 merge-base](#gitreporesolvemergebase) |\n```",
|
||
"sourceIssue": 12
|
||
},
|
||
{
|
||
"id": "F006",
|
||
"reviewer": "Rogue",
|
||
"focus": "",
|
||
"badge": "⚡",
|
||
"severity": "警告",
|
||
"file": "src/lib/gitrepo.js",
|
||
"startLine": 134,
|
||
"endLine": 134,
|
||
"problem": "在檢查是否為淺層 repository (shallow repository) 時,呼叫了外部子行程執行 `git rev-parse --is-shallow-repository`。建立與啟動 OS 子行程是非常昂貴的操作,會白白浪費數十毫秒的 CPU 週期與系統資源。",
|
||
"suggestion": "Git 在淺層 clone 時會在 `.git` 目錄下建立一個 `shallow` 檔案。我們可以使用 Node.js 內建的 `fs.existsSync` 進行本地檔案檢查,不需啟動額外的 Git 子行程,執行速度可快上百倍。",
|
||
"suggestedCode": "```\nconst fs = require('fs');\n// ...\nif (fs.existsSync(path.join(cwd, '.git', 'shallow'))) {\n strategies.push(['unshallow', 'fetch', '--no-tags', '--unshallow', 'origin']);\n}\n```",
|
||
"sourceIssue": 14
|
||
},
|
||
{
|
||
"id": "F007",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "警告",
|
||
"file": "src/lib/gitrepo.js",
|
||
"startLine": 293,
|
||
"endLine": 305,
|
||
"problem": "為了防止 Git 推送失敗時在例外訊息中回顯包含 Token 與遠端 URL 的命令列參數,pushWithCredential 的 catch 區塊直接拋出一個固定的 Error('推送審查結果 commit 失敗...')。但這樣一來,它完全吞掉了原始的錯誤(例如 non-fast-forward 非快轉、分支保護規則阻擋、或連線逾時),六個月後的維護者在 CI log 中看到此錯誤時,完全無從判斷失敗的原因。",
|
||
"suggestion": "建議在保留安全遮罩的前提下,保留原始 exception 的排錯線索。例如可以檢查並安全地過濾 err.message 或 err.stderr 中所有的敏感字串(如 Token/URL),然後將其作為新錯誤的 cause 屬性或附加訊息傳遞下去。",
|
||
"suggestedCode": "```\n} catch (err) {\n // 過濾敏感資訊後保留錯誤細節\n const safeMessage = err.message ? redactSecrets(err.message) : '未知錯誤';\n const error = new Error(`推送審查結果 commit 失敗(${safeMessage})。`);\n error.cause = err;\n throw error;\n }\n```",
|
||
"sourceIssue": 14
|
||
},
|
||
{
|
||
"id": "F009",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/index.js",
|
||
"startLine": 308,
|
||
"endLine": 368,
|
||
"problem": "新增或修改的步驟分隔註解(如步驟 8 分組、步驟 2 延後、步驟 9、步驟 10、及建問題模式收束)其尾隨的水平分隔線(─)長度不一或僅存單一字元,破壞了專案既有程式碼中整齊劃一的長分隔線視覺排版,視覺上顯得雜亂、走調。",
|
||
"suggestion": "補足尾隨的水平線 ─,使其與鄰近步驟分隔註解的長度(約 70~80 字元寬度)與視覺風格保持一致,維持排版的美觀。",
|
||
"suggestedCode": "```\n// ── 步驟 8(分組):依嚴重等級分組(嚴重/警告+建議),組內已依檔案與行數排序 ────────────────\n```",
|
||
"sourceIssue": 14
|
||
},
|
||
{
|
||
"id": "F010",
|
||
"reviewer": "Rogue",
|
||
"focus": "",
|
||
"badge": "⚡",
|
||
"severity": "建議",
|
||
"file": "src/index.js",
|
||
"startLine": 366,
|
||
"endLine": 382,
|
||
"problem": "在建問題模式收束時,先 `await gitea.createIssueComment` 再 `await gitea.addIssueDependency`,這兩個 Gitea API 呼叫是獨立且無資料相依性的,卻以序列(Sequential)方式執行,白白浪費了一次網路往返(RTT)的等待時間。",
|
||
"suggestion": "使用 `Promise.all` 同時發起這兩個請求,並行處理以減少整體 execution 的等待時間。",
|
||
"suggestedCode": "```\nconst commentPromise = gitea.createIssueComment(\n ctx,\n templates.issueLinkComment({\n issueNumber: issue.number,\n issueUrl: issue.html_url,\n severeCount: severe.length,\n otherCount: others.length,\n })\n );\n const dependencyPromise = gitea.addIssueDependency(ctx, ctx.prNumber, issue.number)\n .then(() => log('建問題', 'INF', `已將 PR #${ctx.prNumber} 設為相依於 issue #${issue.number}。`))\n .catch((err) => log('建問題', 'WRN', `設定 PR 相依失敗:${err.message}。`));\n \n await Promise.all([commentPromise, dependencyPromise]);\n```",
|
||
"sourceIssue": 14
|
||
},
|
||
{
|
||
"id": "F012",
|
||
"reviewer": "Rogue",
|
||
"focus": "",
|
||
"badge": "⚡",
|
||
"severity": "建議",
|
||
"file": "src/lib/review.js",
|
||
"startLine": 48,
|
||
"endLine": 53,
|
||
"problem": "在 `agentFailureDetail` 之中,進行 stderr 與 stdout 的遮罩處理時,是先截斷至 2,000 字元,然後執行多次複雜的 `redactSecrets` 正規表示式替換,最後再截斷至 500 字元輸出。這會造成 1,500 字元的複雜 regex 運算結果在下一步被直接丟棄,白白浪費了 CPU 進行字串比對與取代的週期。",
|
||
"suggestion": "應在呼叫 `redactSecrets` 之前,就先將字串截斷至目標長度(500 字元),再進行遮罩,可大幅減少 regex 運算負擔。",
|
||
"suggestedCode": "```\nconst stderr = redactSecrets(String((res && res.stderr) || '').slice(0, 500));\n if (stderr) parts.push(`stderr:${stderr}`);\n const stdout = redactSecrets(String((res && res.output) || '').slice(0, 500));\n if (stdout) parts.push(`stdout:${stdout}`);\n```",
|
||
"sourceIssue": 14
|
||
},
|
||
{
|
||
"id": "F014",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "建議",
|
||
"file": "readme.md",
|
||
"startLine": 56,
|
||
"endLine": 132,
|
||
"problem": "README 的功能表大量手動維護 `src/branch/develop/...#Lxx` 深連結,這次光是分支與行號就改了整排。這類文件會隨任何插入註解、重排函式、換預設分支而失準,未來維護者必須在改程式時同步更新一大段文件,維護成本偏高。",
|
||
"suggestion": "改成不依賴行號的相對連結,或用文件產生腳本從原始碼 JSDoc 自動產出這張表。若仍要指向 Gitea,建議至少移除 `#Lxx`,或集中定義分支名稱,避免每次改分支都要全表搜尋替換。",
|
||
"suggestedCode": "",
|
||
"sourceIssue": 15
|
||
},
|
||
{
|
||
"id": "F015",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/lib/gitea.js",
|
||
"startLine": 190,
|
||
"endLine": 197,
|
||
"problem": "`dependency` 作為參數名太薄,與 `issueNumber` 並列時看不出它也是 issue 編號。讀到 `addIssueDependency(ctx, ctx.prNumber, issue.number)` 時,語意要靠上下文補拍子。",
|
||
"suggestion": "改用更完整的名稱,例如 `dependencyIssueNumber` 或 `blockingIssueNumber`,讓「誰被誰阻擋」在簽名裡就清楚成形。",
|
||
"suggestedCode": "```\nfunction addIssueDependency(ctx, issueNumber, dependencyIssueNumber) {\n return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${issueNumber}/dependencies`, {\n index: dependencyIssueNumber,\n owner: ctx.owner,\n repo: ctx.repo,\n });\n}\n```",
|
||
"sourceIssue": 15
|
||
},
|
||
{
|
||
"id": "F022",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "警告",
|
||
"file": "readme.md",
|
||
"startLine": 76,
|
||
"endLine": 132,
|
||
"problem": "README 內大量函式清單同時硬編分支名稱與行號錨點,這次 diff 已經整批從 `master` 改成 `develop` 並同步調整行號。這類文件和原始碼結構高度重複,後續只要插入幾行程式,文件連結就會失準,維護者必須靠人工記得同步整張表。",
|
||
"suggestion": "改成不含行號的穩定檔案連結,或把這份 API/功能表改由 JSDoc/腳本產生。若一定要保留行號,建議把產生流程寫入 npm script,避免每次程式碼位移都人工批次修改 README。",
|
||
"suggestedCode": "",
|
||
"sourceIssue": 18
|
||
},
|
||
{
|
||
"id": "F023",
|
||
"reviewer": "Mage",
|
||
"focus": "",
|
||
"badge": "🔮",
|
||
"severity": "警告",
|
||
"file": "src/index.js",
|
||
"startLine": 374,
|
||
"endLine": 387,
|
||
"problem": "建問題模式下只要有任何保留 finding 就會建立 issue,且後續一律把 PR 設為相依於該 issue;但收尾結果仍是 `severe.length === 0 ? 'success' : 'failure'`。最小情境:攻擊方只產生 1 條「建議」,`severe.length` 為 0,action commit `[success]`,但 PR 被 issue dependency 擋住無法合併。這讓「success=可通過」與「非嚴重問題也阻擋合併」兩個語義互相矛盾。",
|
||
"suggestion": "明確對齊語義:若只有嚴重問題才應阻擋合併,則只在 `severe.length > 0` 時建立 dependency;若所有保留問題都要阻擋合併,則 result/exit code 不應只看嚴重問題。",
|
||
"suggestedCode": "```\nif (severe.length > 0) {\n try {\n await gitea.addIssueDependency(ctx, ctx.prNumber, issue.number);\n log('建問題', 'INF', `已將 PR #${ctx.prNumber} 設為相依於 issue #${issue.number}。`);\n } catch (err) {\n log('建問題', 'WRN', `設定 PR 相依失敗(可能未啟用「問題相依」功能):${err.message}。`);\n }\n}\n```",
|
||
"sourceIssue": 18
|
||
},
|
||
{
|
||
"id": "F024",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/index.js",
|
||
"startLine": 179,
|
||
"endLine": 181,
|
||
"problem": "`issueBuffer` 的旋律太含糊:讀者會以為裡面放的是 issue,實際上暫存的是尚未送出的留言 body。命名沒有把資料形狀唱清楚。",
|
||
"suggestion": "改成能描述內容與用途的名稱,例如 `pendingIssueCommentBodies`,並同步調整註解與迴圈變數。",
|
||
"suggestedCode": "```\nconst pendingIssueCommentBodies = [];\nlet issue = null;\n```",
|
||
"sourceIssue": 18
|
||
},
|
||
{
|
||
"id": "F025",
|
||
"reviewer": "Assassin",
|
||
"focus": "",
|
||
"badge": "🗡️",
|
||
"severity": "警告",
|
||
"file": "action.yml",
|
||
"startLine": 21,
|
||
"endLine": 24,
|
||
"problem": "這裡建議呼叫端傳入「能觸發 CI 的 PAT」作為 action token。攻擊者最愛這種長效、可推送、可觸發 workflow 的憑證:只要此 action 在不受信任 PR 上執行,或 PR 能影響 action/workflow 執行內容,惡意變更就可能讀取 `INPUT_TOKEN`、推送結果 commit、再藉由可觸發 CI 的身分製造後續執行鏈。自動 token 原本不觸發 CI 是一道防線,這個建議等於要求使用者把防線拆掉。",
|
||
"suggestion": "不要泛稱建議使用可觸發 CI 的 PAT。文件與介面應明確要求最小權限、repo 限定、短效或可輪替 token,並禁止在 fork/不受信任 PR context 暴露 PAT。更穩的設計是分離 API 留言 token 與 push token,且只有在明確受信任事件或受保護分支才允許 push token 存在;否則拒絕 commit/push,只做留言或 artifact。",
|
||
"suggestedCode": "",
|
||
"sourceIssue": 19
|
||
},
|
||
{
|
||
"id": "F026",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "建議",
|
||
"file": "readme.md",
|
||
"startLine": 59,
|
||
"endLine": 132,
|
||
"problem": "README 的功能列表手動維護了大量 `src/branch/develop/...#Lxx` 深連結與行號。這次 PR 已經一次改動數十個 branch/line anchor,代表文件和原始碼行號高度耦合;下一次只要插入幾行程式,文件就會悄悄過期,維護者很難知道哪些連結還準。",
|
||
"suggestion": "避免在手寫 README 綁定行號,改連到函式所在檔案或穩定章節錨點;若必須保留行號,請把這段改成產生式文件,讓 CI 或腳本從原始碼/JSDoc 重新生成,減少人工同步成本。",
|
||
"suggestedCode": "",
|
||
"sourceIssue": 19
|
||
},
|
||
{
|
||
"id": "F027",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/index.js",
|
||
"startLine": 181,
|
||
"endLine": 185,
|
||
"problem": "`issue` 這個變數名太素,像樂譜上只寫「音符」卻不說是哪一聲部。此處承載的是建問題模式建立出的追蹤 issue,後面還會與 PR issue 編號、Gitea issue API 參數交錯出現,名稱過泛會讓閱讀節奏變濁。",
|
||
"suggestion": "改成能表明角色的名稱,例如 `trackingIssue`。對應的 `ensureIssueCreated` 也可改為 `ensureTrackingIssueCreated`,讓閉包狀態與用途一眼對上。",
|
||
"suggestedCode": "```\nconst issueBuffer = [];\nlet trackingIssue = null;\n\nconst postComment = async (body) => {\n if (ctx.createIssue) {\n if (trackingIssue) return gitea.createCommentOnIssue(ctx, trackingIssue.number, body);\n issueBuffer.push(body);\n return null;\n }\n const created = await gitea.createIssueComment(ctx, body);\n currentRunCommentIds.add(created.id);\n return created;\n};\n```",
|
||
"sourceIssue": 19
|
||
},
|
||
{
|
||
"id": "F029",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/lib/templates.js",
|
||
"startLine": 388,
|
||
"endLine": 405,
|
||
"problem": "`issueLinkComment` 產生的是 PR 上唯一的建問題模式回貼留言,但名稱少了 PR 的聲部;同檔已有 `issueBody`、`issueFindingComment`,乍看會以為這也是 issue 內留言模板,命名層次不夠分明。",
|
||
"suggestion": "改名為 `prIssueLinkComment` 或 `trackingIssueLinkComment`,讓模板的投遞位置與用途直接寫在名稱裡,避免與 issue 內文、issue finding 留言混成一團。",
|
||
"suggestedCode": "```\nfunction prIssueLinkComment({ issueNumber, issueUrl, severeCount, otherCount }) {\n return `${MARK}\n## 🔍 AI Code Review|已建立追蹤問題\n\n本次審查結果已彙整到 issue [#${issueNumber}](${issueUrl})(🔴 嚴重 ${severeCount} 條、🟠🔵 警告+建議 ${otherCount} 條),請至該問題追蹤與討論。`;\n}\n```",
|
||
"sourceIssue": 19
|
||
},
|
||
{
|
||
"id": "F031",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "建議",
|
||
"file": "readme.md",
|
||
"startLine": 56,
|
||
"endLine": 132,
|
||
"problem": "README 的功能表大量硬編遠端分支名稱與行號,這次只是從 `master` 改成 `develop` 並同步行號,但這種文件很容易在下一次函式移動、預設分支更名或重排時再次整批失準。未來維護者會被迫反覆做低價值的連結校正,文件也可能在沒人注意時指到錯誤位置。",
|
||
"suggestion": "若 README 是 repo 內文件,優先改成相對路徑連結,並避免固定行號;若必須保留行號,建議用產生腳本統一輸出這張表,讓分支名與行號只從單一來源計算。",
|
||
"suggestedCode": "",
|
||
"sourceIssue": 21
|
||
},
|
||
{
|
||
"id": "F032",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/index.js",
|
||
"startLine": 183,
|
||
"endLine": 224,
|
||
"problem": "`issue` 與 `issueBuffer` 的命名過於泛泛;在 Gitea 裡 PR 也是 issue,追蹤問題也是 issue,單靠 `issue` 這個名字無法唱出它究竟是哪一個聲部。",
|
||
"suggestion": "建議改成更具語義的名稱,例如 `trackingIssue`、`trackingIssueCommentBuffer`,讓讀者不用回讀 create-issue 模式的整段脈絡。",
|
||
"suggestedCode": "```\nlet trackingIssue = null;\nconst trackingIssueCommentBuffer = [];\n```",
|
||
"sourceIssue": 21
|
||
},
|
||
{
|
||
"id": "F033",
|
||
"reviewer": "Bard",
|
||
"focus": "",
|
||
"badge": "🎼",
|
||
"severity": "建議",
|
||
"file": "src/lib/gitea.js",
|
||
"startLine": 190,
|
||
"endLine": 194,
|
||
"problem": "`addIssueDependency(ctx, issueNumber, dependency)` 的兩個參數名稱太相似,且 `dependency` 少了 issue 語義。這支 API 的方向性本來就容易讀錯,命名再模糊就像兩個音符共用同一個名字。",
|
||
"suggestion": "建議把參數改成能表達方向的名稱,例如 `blockedIssueNumber` 與 `blockingIssueNumber`,呼叫端也會更清楚是誰被誰擋住。",
|
||
"suggestedCode": "```\nfunction addIssueDependency(ctx, blockedIssueNumber, blockingIssueNumber) {\n return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${blockedIssueNumber}/dependencies`, {\n index: blockingIssueNumber,\n owner: ctx.owner,\n repo: ctx.repo,\n });\n}\n```",
|
||
"sourceIssue": 21
|
||
},
|
||
{
|
||
"id": "F040",
|
||
"reviewer": "Assassin",
|
||
"focus": "",
|
||
"badge": "🗡️",
|
||
"severity": "警告",
|
||
"file": "action.yml",
|
||
"startLine": 19,
|
||
"endLine": 22,
|
||
"problem": "這段新增說明鼓勵呼叫端傳入「能觸發 CI 的 PAT」。攻擊者最喜歡這種長效、高權限、可觸發 workflow 的憑證:若 action 跑在不可信 PR、AI CLI 被 prompt injection 誘導讀環境變數,或同 repo PR 可改動本 action 程式碼,就可能把 PAT 外送或濫用成寫入 repo/觸發 CI 的跳板。",
|
||
"suggestion": "不要把長效 PAT 當建議預設。改用最小權限、短效的 GitHub App/Gitea App token,並明確禁止在不可信 fork PR 傳入可寫 token。若目標只是回報檢查結果,優先用 status/check API 寫結果,不要靠 PAT push 再觸發下一輪 CI。",
|
||
"suggestedCode": "```\ndescription: 'Gitea API token(PR/issue 留言與 push findings 用;請使用最小權限、短效 token,勿在不可信 PR 傳入長效 PAT)'\n```",
|
||
"sourceIssue": 23
|
||
},
|
||
{
|
||
"id": "F041",
|
||
"reviewer": "Leo",
|
||
"focus": "",
|
||
"badge": "🧰",
|
||
"severity": "建議",
|
||
"file": "readme.md",
|
||
"startLine": 58,
|
||
"endLine": 131,
|
||
"problem": "README 的功能表把分支名稱與行號大量硬編在外部 URL 裡,這次已經需要整批 `master` 改 `develop` 並同步多個 `#Lxx`。這類文件會隨任何程式碼插行、函式移動或預設分支變更而失準,維護成本會線性累積,最後讀者點到的文件比沒有文件更誤導。",
|
||
"suggestion": "改用 repo 相對連結、不固定行號,或把這段功能表改由腳本從 JSDoc 自動產生。若需要連到特定實作,優先連到檔案或錨點,避免每次重排程式碼都要同步更新幾十個行號。",
|
||
"suggestedCode": "```\n| log.taipeiNow | [src/lib/log.js](src/lib/log.js) | 取得台北時區 yyyy/MM/dd HH:mm:ss 時間字串 |\n| review.runAttackers | [src/lib/review.js](src/lib/review.js) | 攻擊方 sub agent 並行找問題並合併列表 |\n```",
|
||
"sourceIssue": 23
|
||
}
|
||
],
|
||
"excluded": []
|
||
}
|