diff --git a/x.js b/x.js index f779bca..8d5362a 100644 --- a/x.js +++ b/x.js @@ -8,6 +8,9 @@ const { exec, spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); +// Use built-in fetch (Node.js 18+) or fallback to node-fetch +const fetch = globalThis.fetch || require('node-fetch'); + // --- Helpers: Git log + LLM summarization for Executive Summary (DE) --- // Promise-based exec wrapper with timeout @@ -106,8 +109,46 @@ async function callLLMOpenAICompatible({ baseUrl, apiKey, model, system, user, t } } -// Summarize the most recent commit in German -async function summarizeMostRecentCommitDE() { +// Get commit diff from Gitea API +async function getCommitDiffFromGitea(giteaBaseUrl, repoOwner, repoName, commitHash) { + const giteaToken = process.env.GITEA_TOKEN; + if (!giteaToken) { + logMessage('GITEA_TOKEN not configured; cannot fetch diff from API', 'warn'); + return null; + } + + try { + const apiUrl = `${giteaBaseUrl}/api/v1/repos/${repoOwner}/${repoName}/git/commits/${commitHash}.diff`; + logMessage(`Fetching commit diff from: ${apiUrl}`); + + const response = await fetch(apiUrl, { + headers: { + 'Authorization': `token ${giteaToken}`, + 'Accept': 'text/plain' + }, + timeout: 10000 + }); + + if (!response.ok) { + logMessage(`Gitea API request failed: ${response.status} ${response.statusText}`, 'warn'); + return null; + } + + const diffText = await response.text(); + + if (diffText && diffText.trim()) { + return diffText; + } + + return null; + } catch (e) { + logMessage(`Failed to fetch commit diff from Gitea API: ${e.message}`, 'warn'); + return null; + } +} + +// Summarize the most recent commit in German using webhook payload data +async function summarizeMostRecentCommitDE(webhookPayload = null) { // Determine provider const provider = (process.env.LLM_PROVIDER || 'openrouter').toLowerCase(); const model = process.env.LLM_MODEL || (provider === 'openrouter' ? 'openrouter/anthropic/claude-3.5-sonnet' : 'gpt-4o-mini'); @@ -128,15 +169,41 @@ async function summarizeMostRecentCommitDE() { return null; } - // Pull commits from the current working directory (assumed repo root or subdir) - const commits = await getLastCommits({ count: 10, repoDir: process.cwd() }); + let commits = []; + let diff = ''; + + if (webhookPayload && webhookPayload.commits && webhookPayload.commits.length > 0) { + // Use webhook payload data + commits = webhookPayload.commits.map(commit => ({ + hash: commit.id, + author: commit.author?.name || 'Unknown', + date: commit.timestamp || new Date().toISOString(), + subject: commit.message?.split('\n')[0] || 'No subject', + body: commit.message?.split('\n').slice(1).join('\n') || '' + })); + + // Extract Gitea base URL from webhook payload + const giteaBaseUrl = webhookPayload.repository?.html_url?.match(/^https?:\/\/[^\/]+/)?.[0]; + const repoOwner = webhookPayload.repository?.owner?.login; + const repoName = webhookPayload.repository?.name; + const latestCommitHash = commits[0]?.hash; + + if (giteaBaseUrl && repoOwner && repoName && latestCommitHash) { + diff = await getCommitDiffFromGitea(giteaBaseUrl, repoOwner, repoName, latestCommitHash); + } + } else { + // Fallback to local git (original behavior) + commits = await getLastCommits({ count: 10, repoDir: process.cwd() }); + if (!commits.length) return null; + diff = await getLastCommitDiff({ repoDir: process.cwd(), contextLines: 3 }); + } + if (!commits.length) return null; const prompt = buildMostRecentCommitPromptGerman(commits); if (!prompt) return null; - // Also include the unified diff of the latest commit in the system prompt - const diff = await getLastCommitDiff({ repoDir: process.cwd(), contextLines: 3 }); + // Include the diff in the system prompt const systemWithDiff = `${prompt.system}\n\n` + `Kontext (unified diff der neuesten Änderung):\n` + @@ -322,7 +389,7 @@ function formatCommitMessage(payload) { return (async () => { let summaryBlock = ''; try { - const summary = await summarizeMostRecentCommitDE(); + const summary = await summarizeMostRecentCommitDE(payload); if (summary) { const escapedSummary = escapeMdV2(summary); summaryBlock = `\n\n———————————————\n\n📋 *Executive Summary*\n\n${escapedSummary}\n\n\n`;