From 6f2bbab0417bda561fc96b73e8bf273892d760ab Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 18 Jun 2026 08:07:20 +0000 Subject: [PATCH] =?UTF-8?q?fix(ai-review=20=E5=90=8C=E6=AD=A5):=20?= =?UTF-8?q?=E9=99=90=E5=88=B6=E8=87=AA=E5=8B=95=E6=8F=90=E4=BA=A4=E5=8F=AA?= =?UTF-8?q?=E5=8C=85=E5=90=AB=E5=95=8F=E9=A1=8C=E6=AA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/triage-findings/SKILL.md | 46 ---- .amazonq/skills/triage-findings/SKILL.md | 41 --- .antigravity/skills/triage-findings/SKILL.md | 46 ---- .claude/skills/triage-findings/SKILL.md | 46 ---- .codex/skills/triage-findings/SKILL.md | 46 ---- .../skills/triage-findings/agents/openai.yaml | 4 - .gemini/skills/triage-findings/SKILL.md | 46 ---- .github/copilot-instructions.md | 14 - .github/skills/triage-findings/SKILL.md | 41 --- Dockerfile | 10 - app/git.js | 255 +----------------- app/gitea.js | 10 - 12 files changed, 9 insertions(+), 596 deletions(-) delete mode 100644 .agents/skills/triage-findings/SKILL.md delete mode 100644 .amazonq/skills/triage-findings/SKILL.md delete mode 100644 .antigravity/skills/triage-findings/SKILL.md delete mode 100644 .claude/skills/triage-findings/SKILL.md delete mode 100644 .codex/skills/triage-findings/SKILL.md delete mode 100644 .codex/skills/triage-findings/agents/openai.yaml delete mode 100644 .gemini/skills/triage-findings/SKILL.md delete mode 100644 .github/copilot-instructions.md delete mode 100644 .github/skills/triage-findings/SKILL.md diff --git a/.agents/skills/triage-findings/SKILL.md b/.agents/skills/triage-findings/SKILL.md deleted file mode 100644 index 2422e52..0000000 --- a/.agents/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: triage-findings -description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions. ---- - -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/.amazonq/skills/triage-findings/SKILL.md b/.amazonq/skills/triage-findings/SKILL.md deleted file mode 100644 index d747008..0000000 --- a/.amazonq/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/.antigravity/skills/triage-findings/SKILL.md b/.antigravity/skills/triage-findings/SKILL.md deleted file mode 100644 index 2422e52..0000000 --- a/.antigravity/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: triage-findings -description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions. ---- - -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/.claude/skills/triage-findings/SKILL.md b/.claude/skills/triage-findings/SKILL.md deleted file mode 100644 index 2422e52..0000000 --- a/.claude/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: triage-findings -description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions. ---- - -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/.codex/skills/triage-findings/SKILL.md b/.codex/skills/triage-findings/SKILL.md deleted file mode 100644 index 2422e52..0000000 --- a/.codex/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: triage-findings -description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions. ---- - -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/.codex/skills/triage-findings/agents/openai.yaml b/.codex/skills/triage-findings/agents/openai.yaml deleted file mode 100644 index 9b2c3ea..0000000 --- a/.codex/skills/triage-findings/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Triage Findings" - short_description: "Triage, sort, fix, and exclude review findings" - default_prompt: "Use $triage-findings to merge review findings, sort and renumber them by severity, resolve real issues one by one, and add false positives to `.gitea/ai-review/exclusions.json` as a top-level JSON array." diff --git a/.gemini/skills/triage-findings/SKILL.md b/.gemini/skills/triage-findings/SKILL.md deleted file mode 100644 index 2422e52..0000000 --- a/.gemini/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: triage-findings -description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions. ---- - -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index ca2d3a9..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,14 +0,0 @@ -# Triage Findings - -Use the triage-finding workflow for review issue lists: - -1. Merge findings into one list. -2. Remove duplicates. -3. Sort by severity: `critical` -> `warning` -> `info`. -4. Renumber from 1. -5. Fix real issues with the smallest safe change. -6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible. -7. Add or update tests when behavior changes. -8. Re-check after each fix. - -The full reusable skill lives in `.github/skills/triage-findings/SKILL.md`. diff --git a/.github/skills/triage-findings/SKILL.md b/.github/skills/triage-findings/SKILL.md deleted file mode 100644 index d747008..0000000 --- a/.github/skills/triage-findings/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ -# Triage Findings - -## When To Use - -Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list. -It is also used when some findings are false positives and should be moved into the exclusions list. - -## Workflow - -1. Collect all findings into one list. -2. Merge duplicates into a single finding when they describe the same issue. -3. Sort the final list by severity: - - critical - - warning - - info -4. Renumber the sorted list from 1 upward. -5. Rewrite each finding concisely so the final list reads cleanly and consistently. -6. If a finding is a false positive, do not keep it in the final list. -7. Add false positives to the exclusions list as a top-level JSON array in `.gitea/ai-review/exclusions.json`, and preserve the original finding wording as much as possible, including language and semantics. Do not wrap the array in `exclusions` or `excluded_findings`. - -## Resolution Flow - -After the list is merged and ordered, resolve the remaining findings one by one. - -1. Start from the highest severity item. -2. Identify the root cause in the relevant file or context. -3. Apply the smallest safe change that fixes the issue. -4. Add or update tests when behavior changes. -5. Re-check the issue after the change. -6. If the item is confirmed false positive, move it to exclusions instead of changing code. -7. Continue until the list is either fixed or explicitly excluded. - -## Output Rules - -- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable. -- Keep numbering contiguous after filtering and merging. -- Preserve useful details like file path, location, and suggested fix. -- Keep exclusions entries minimal and consistent with the project schema. -- When writing exclusions, always output a top-level JSON array. -- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema. -- If the source already provides a severity or title, keep it unless it conflicts with the final ordering. diff --git a/Dockerfile b/Dockerfile index b3c58fe..ee322d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,16 +10,6 @@ WORKDIR /action COPY app/package.json /action/app/ RUN cd /action/app && npm install -COPY .amazonq/ /action/.amazonq/ -COPY .codex/ /action/.codex/ -COPY .agents/ /action/.agents/ -COPY .claude/ /action/.claude/ -COPY .gemini/ /action/.gemini/ -COPY .github/ /action/.github/ -COPY AGENTS.md /action/ -COPY CLAUDE.md /action/ -COPY GEMINI.md /action/ - COPY app/ /action/app/ COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh diff --git a/app/git.js b/app/git.js index 39468fe..9be810c 100644 --- a/app/git.js +++ b/app/git.js @@ -1,51 +1,12 @@ import { spawnSync } from 'child_process'; import fs from 'fs'; import path from 'path'; -import { fileURLToPath } from 'url'; -import { GITEA_SERVER_URL, GITEA_REPOSITORY, GITEA_TOKEN, PR_HEAD_BRANCH, FINDINGS_PATH, getLLMConfig } from './config.js'; -import { line, ok, warn, error } from './log.js'; +import { GITEA_SERVER_URL, GITEA_REPOSITORY, GITEA_TOKEN, PR_HEAD_BRANCH, FINDINGS_PATH } from './config.js'; +import { line, ok, warn } from './log.js'; -const ACTION_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -const GENERATED_SYNC_PATHS = [FINDINGS_PATH, '.gitea/ai-review/exclusions.json']; +const REVIEW_FILE_PATHS = [FINDINGS_PATH, '.gitea/ai-review/exclusions.json']; const remoteUrl = `${GITEA_SERVER_URL.replace(/\/$/, '')}/${GITEA_REPOSITORY}.git`; export const BOT_COMMIT_MARKER = '[ai-review-bot]'; -export const SYNC_PATHS = [ - '.amazonq/rules/triage-findings.md', - '.agents/skills/triage-findings/SKILL.md', - '.antigravity/skills/triage-findings/SKILL.md', - '.codex/skills/triage-findings/SKILL.md', - '.codex/skills/triage-findings/agents/openai.yaml', - '.claude/skills/triage-findings/SKILL.md', - '.gemini/skills/triage-findings/SKILL.md', - '.github/copilot-instructions.md', - '.github/skills/triage-findings/SKILL.md', - 'AGENTS.md', - 'ANTIGRAVITY.md', - 'CLAUDE.md', - 'GEMINI.md', -]; -const FORCE_SYNC_FILE_PATHS = [ - '.github/copilot-instructions.md', - 'AGENTS.md', - 'ANTIGRAVITY.md', - 'CLAUDE.md', - 'GEMINI.md', -]; -const MERGE_SYNC_FILE_PATHS = new Set([ - 'AGENTS.md', - 'ANTIGRAVITY.md', - 'CLAUDE.md', - 'GEMINI.md', -]); -let instructionMergeAssistantPromise = null; -const SYNC_TREE_PATHS = [ - '.agents/skills/triage-findings', - '.antigravity/skills/triage-findings', - '.codex/skills/triage-findings', - '.claude/skills/triage-findings', - '.gemini/skills/triage-findings', - '.github/skills/triage-findings', -]; function makeRunner(spawn) { return function run(args, cwd, env) { @@ -88,173 +49,6 @@ function readGitOutput(run, args, cwd, env) { } } -function normalizeText(text) { - return text.replace(/\r\n/g, '\n'); -} - -function splitTextBlocks(text) { - const normalized = normalizeText(text).replace(/\n+$/, ''); - if (!normalized) return []; - return normalized.split(/\n{2,}/).map(block => block.trimEnd()).filter(Boolean); -} - -function mergeText(existingText, sourceText) { - const existing = normalizeText(existingText); - const source = normalizeText(sourceText); - if (existing === source) return existing; - - const mergedBlocks = splitTextBlocks(existing); - const seenBlocks = new Set(mergedBlocks.map(block => block.trim())); - let changed = false; - - for (const block of splitTextBlocks(source)) { - const key = block.trim(); - if (seenBlocks.has(key)) continue; - seenBlocks.add(key); - mergedBlocks.push(block); - changed = true; - } - - if (!changed) return existing; - return `${mergedBlocks.join('\n\n')}\n`; -} - -function uniqueBlocksFromTexts(...texts) { - const seen = new Set(); - const blocks = []; - for (const text of texts) { - for (const block of splitTextBlocks(text)) { - const key = block.trim(); - if (!key || seen.has(key)) continue; - seen.add(key); - blocks.push(block); - } - } - return blocks; -} - -function validateMergedInstructionText(mergedText, requiredBlocks) { - const candidate = normalizeText(mergedText); - return requiredBlocks.every(block => candidate.includes(normalizeText(block).trim())); -} - -class InstructionMergeError extends Error { - constructor(message, options) { - super(message, options); - this.name = 'InstructionMergeError'; - } -} - -function abortInstructionMerge(message) { - error(message); - process.exit(1); - throw new InstructionMergeError(message); -} - -function syncFileOverwrite(sourceRoot, repoDir, relPath) { - const src = path.join(sourceRoot, relPath); - if (!fs.existsSync(src)) return null; - - const dest = path.join(repoDir, relPath); - fs.mkdirSync(path.dirname(dest), { recursive: true }); - fs.copyFileSync(src, dest); - return relPath; -} - -async function getInstructionMergeAssistant() { - const { provider } = getLLMConfig(); - if (!provider) return null; - if (instructionMergeAssistantPromise) return instructionMergeAssistantPromise; - - instructionMergeAssistantPromise = (async () => { - try { - const { chatJSON } = await import('./llm.js'); - return async ({ relPath, existingText, sourceText, deterministicText }) => { - const systemPrompt = [ - 'You merge repository instruction files without losing any skill, command, or rule.', - 'Never delete unique content from either input.', - 'You may only remove exact duplicates or improve ordering/formatting.', - 'Return JSON with a single field: merged_text.', - ].join(' '); - const userContent = JSON.stringify({ - path: relPath, - existing_text: existingText, - source_text: sourceText, - deterministic_candidate: deterministicText, - }); - const result = await chatJSON(systemPrompt, userContent); - if (typeof result === 'string') return result; - if (result && typeof result.merged_text === 'string') return result.merged_text; - return null; - }; - } catch (e) { - warn(`[merge] AI instruction merge unavailable: ${e.message}`); - return null; - } - })(); - - return instructionMergeAssistantPromise; -} - -export async function mergeInstructionText(existingText, sourceText, relPath, aiMergeAssistant = null) { - const deterministic = mergeText(existingText, sourceText); - const requiredBlocks = uniqueBlocksFromTexts(existingText, sourceText); - if (!aiMergeAssistant || requiredBlocks.length === 0) return deterministic; - - try { - const aiMerged = await aiMergeAssistant({ relPath, existingText, sourceText, deterministicText: deterministic, requiredBlocks }); - if (aiMerged == null) { - warn(`[merge] ${relPath} AI result unavailable; using deterministic merge`); - return deterministic; - } - if (typeof aiMerged === 'string' && validateMergedInstructionText(aiMerged, requiredBlocks)) { - return normalizeText(aiMerged) === normalizeText(existingText) ? existingText : aiMerged; - } - abortInstructionMerge(`[merge] ${relPath} AI result rejected; refusing fallback`); - } catch (e) { - if (e instanceof InstructionMergeError) throw e; - abortInstructionMerge(`[merge] ${relPath} AI merge failed: ${e.message}`); - } -} - -async function syncInstructionFile(sourceRoot, repoDir, relPath, aiMergeAssistant = null) { - const src = path.join(sourceRoot, relPath); - if (!fs.existsSync(src)) return null; - - const dest = path.join(repoDir, relPath); - fs.mkdirSync(path.dirname(dest), { recursive: true }); - - if (!fs.existsSync(dest)) { - fs.copyFileSync(src, dest); - return relPath; - } - - const existingText = fs.readFileSync(dest, 'utf8'); - const sourceText = fs.readFileSync(src, 'utf8'); - const merged = await mergeInstructionText(existingText, sourceText, relPath, aiMergeAssistant); - if (merged !== existingText) { - fs.writeFileSync(dest, merged, 'utf8'); - } - return relPath; -} - -function syncTree(sourceRoot, repoDir, relDir) { - const srcDir = path.join(sourceRoot, relDir); - if (!fs.existsSync(srcDir)) return []; - - const copied = []; - for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) { - const relPath = path.join(relDir, entry.name); - if (entry.isDirectory()) { - copied.push(...syncTree(sourceRoot, repoDir, relPath)); - continue; - } - const synced = syncFileOverwrite(sourceRoot, repoDir, relPath); - if (synced) copied.push(synced); - } - return copied; -} - export function getRepoState(repoDir, _spawnSync = spawnSync) { const run = makeRunner(_spawnSync); const headSha = readGitOutput(run, ['rev-parse', 'HEAD'], repoDir); @@ -311,7 +105,7 @@ export function cloneRepo(workspace, _spawnSync = spawnSync) { }); } -export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync, sourceRoot = ACTION_ROOT, reviewOutcome = 'success') { +export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync, _sourceRoot = null, reviewOutcome = 'success') { const run = makeRunner(_spawnSync); try { @@ -323,51 +117,20 @@ export async function commitAndPush(workspace, repoDir, _spawnSync = spawnSync, run(['reset', '--hard', `origin/${PR_HEAD_BRANCH}`], repoDir); } - const existingSyncPaths = new Set(); - const aiMergeAssistant = await getInstructionMergeAssistant(); - - // Copy action skill trees into the target repo. Existing files are merged with - // the action source; missing source files are ignored so we do not delete - // target repo content. - for (const relDir of SYNC_TREE_PATHS) { - for (const relPath of syncTree(sourceRoot, repoDir, relDir)) { - existingSyncPaths.add(relPath); - } - } - - // Merge only the direct instruction files that must preserve repository-specific - // skills, commands, and rules. Everything else keeps the source copy. - for (const relPath of FORCE_SYNC_FILE_PATHS) { - const copied = MERGE_SYNC_FILE_PATHS.has(relPath) - ? await syncInstructionFile(sourceRoot, repoDir, relPath, aiMergeAssistant) - : syncFileOverwrite(sourceRoot, repoDir, relPath); - if (copied) existingSyncPaths.add(copied); - } - - // Merge standalone action files into the target repo. - for (const relPath of SYNC_PATHS) { - if (FORCE_SYNC_FILE_PATHS.includes(relPath)) continue; - const copied = syncFileOverwrite(sourceRoot, repoDir, relPath); - if (copied) existingSyncPaths.add(copied); - } - - if (existingSyncPaths.size > 0) { - run(['add', ...existingSyncPaths], repoDir); - } - const generatedSyncPaths = GENERATED_SYNC_PATHS.filter(relPath => fs.existsSync(path.join(workspace, relPath))); - if (generatedSyncPaths.length > 0) { - for (const relPath of generatedSyncPaths) { + const reviewFilePaths = REVIEW_FILE_PATHS.filter(relPath => fs.existsSync(path.join(workspace, relPath))); + if (reviewFilePaths.length > 0) { + for (const relPath of reviewFilePaths) { const src = path.join(workspace, relPath); const dest = path.join(repoDir, relPath); fs.mkdirSync(path.dirname(dest), { recursive: true }); fs.copyFileSync(src, dest); } - run(['add', ...generatedSyncPaths], repoDir); + run(['add', ...reviewFilePaths], repoDir); } const status = run(['status', '--porcelain'], repoDir); if (!status) { - line('sync files 無變更,跳過 commit'); + line('review files 無變更,跳過 commit'); return; } diff --git a/app/gitea.js b/app/gitea.js index 2442ff7..7aeffed 100644 --- a/app/gitea.js +++ b/app/gitea.js @@ -25,18 +25,8 @@ export function getBotReviewOutcome(message) { export async function getPRDiff() { const resp = await axios.get(api(`/repos/${GITEA_REPOSITORY}/pulls/${PR_NUMBER}.diff`), { headers: headers(), timeout: 60000, httpsAgent }); return filterDiff(resp.data, [ - '.amazonq/', - '.agents/', - '.antigravity/', - '.claude/', - '.codex/', - '.gemini/', '.gitea/', '.github/', - 'AGENTS.md', - 'ANTIGRAVITY.md', - 'CLAUDE.md', - 'GEMINI.md', 'README.md', 'TODO.md', ]);