1
0

auto-git:

[change] main.js
This commit is contained in:
2025-05-24 15:04:04 +02:00
parent cc96dd3d14
commit 24b4a8e6f0

236
main.js
View File

@@ -1,3 +1,239 @@
const { app, BrowserWindow, ipcMain, dialog, Menu, shell, clipboard } = require('electron');
app.name = 'Auto-Git';
const { exec } = require('child_process');
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const Store = require('electron-store');
const simpleGit = require('simple-git');
//const fetch = require('node-fetch');
const chokidar = require('chokidar');
const store = new Store({
defaults: {
folders: [],
selected: null,
skymode: true,
skipGitPrompt: true,
intelligentCommitThreshold: 100
}
});
// Map zum Speichern der Watcher pro Ordner
const repoWatchers = new Map();
// Debug Helper
function debug(msg) {
console.log(`[DEBUG ${new Date().toISOString()}] ${msg}`);
}
/**
* Erstellt das BrowserWindow und lädt index.html.
* Gibt das Window-Objekt zurück.
*/
function createWindow() {
const win = new BrowserWindow({
width: 900,
height: 600,
minWidth: 600,
minHeight: 400,
title: 'Auto-Git',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
}
});
win.loadFile('index.html');
return win;
}
// Settings-Fenster
let settingsWin;
function openSettings(win) {
if (settingsWin) {
settingsWin.focus();
return;
}
settingsWin = new BrowserWindow({
parent: win,
modal: true,
width: 400,
height: 300,
resizable: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
}
});
settingsWin.removeMenu();
settingsWin.loadFile('settings.html');
settingsWin.on('closed', () => settingsWin = null);
}
/**
* Startet einen File-Watcher auf .git/refs/heads/master,
* sendet bei Änderungen 'repo-updated' an den Renderer.
*/
function watchRepo(folder, win) {
const gitHead = path.join(folder, '.git', 'refs', 'heads', 'master');
const watcher = chokidar.watch(gitHead, { ignoreInitial: true });
watcher.on('change', () => {
win.webContents.send('repo-updated', folder);
});
repoWatchers.set(folder, watcher);
}
/**
* Initiiert ein Git-Repo in `folder`, falls noch nicht vorhanden,
* und erzeugt einen Initial-Commit mit Timestamp.
*/
async function initGitRepo(folder) {
const git = simpleGit(folder);
const gitDir = path.join(folder, '.git');
if (!fs.existsSync(gitDir)) {
await git.init();
const message = `Initial commit (generated by auto-git)`;
const readmePath = path.join(folder, 'README.md');
fs.writeFileSync(readmePath, `# Projekt in ${path.basename(folder)}\n`);
await git.add('./*');
await git.commit(message);
}
}
// Map für Monitoring-Watcher (nicht repoWatchers!)
const monitoringWatchers = new Map();
// Helper: Baut eine Commit-Message aus git.status()
function buildCommitMessageFromStatus(status, prefix = '[auto]') {
const changes = [];
status.not_added.forEach(f => changes.push(`[add] ${f}`));
status.created.forEach(f => changes.push(`[add] ${f}`));
status.modified.forEach(f => changes.push(`[change] ${f}`));
status.deleted.forEach(f => changes.push(`[unlink] ${f}`));
status.renamed.forEach(r => changes.push(`[rename] ${r.from}${r.to}`));
return prefix + '\n' + changes.map(l => ` ${l}`).join('\n');
}
function startMonitoringWatcher(folderPath, win) {
if (monitoringWatchers.has(folderPath)) return;
const watcher = chokidar.watch(folderPath, {
ignored: /(^|[\/\\])\..|node_modules|\.git/,
ignoreInitial: true,
persistent: true,
depth: 99,
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 }
});
// Initialer Commit
(async () => {
debug(`[MONITOR] Starte initialen Commit-Check für ${folderPath}`);
const git = simpleGit(folderPath);
const status = await git.status();
if (
status.not_added.length > 0 ||
status.created.length > 0 ||
status.modified.length > 0 ||
status.deleted.length > 0 ||
status.renamed.length > 0
) {
const msg = buildCommitMessageFromStatus(status, 'auto-git: ');
const did = await autoCommit(folderPath, msg);
if (did) {
win.webContents.send('repo-updated', folderPath);
debug(`[MONITOR] Initialer Auto-Commit für ${folderPath} durchgeführt:\n${msg}`);
}
}
})();
// Bei jedem Event → status neu holen, Message wie beim initialen Check bauen
watcher.on('all', async () => {
const git = simpleGit(folderPath);
const status = await git.status();
if (
status.not_added.length > 0 ||
status.created.length > 0 ||
status.modified.length > 0 ||
status.deleted.length > 0 ||
status.renamed.length > 0
) {
const msg = buildCommitMessageFromStatus(status, 'auto-git: ');
await autoCommit(folderPath, msg);
win.webContents.send('repo-updated', folderPath);
}
});
monitoringWatchers.set(folderPath, watcher);
debug(`[MONITOR] Watcher aktiv für ${folderPath}`);
}
function stopMonitoringWatcher(folderPath) {
const watcher = monitoringWatchers.get(folderPath);
if (watcher) {
watcher.close();
monitoringWatchers.delete(folderPath);
debug(`[MONITOR] Watcher gestoppt für ${folderPath}`);
}
}
// ---- Rewrite Git Messages with LLM generated messages ----
// 1. Commits & Diffs für LLM sammeln
async function getCommitsForLLM(folderPath, hashes) {
const git = simpleGit(folderPath);
const commits = [];
for (const hash of hashes) {
const diff = await git.diff([`${hash}^!`]);
const msg = (await git.show(['-s', '--format=%B', hash])).trim();
commits.push({
hash,
message: msg,
diff
});
}
return commits;
}
async function getPrompt(folderPath, hashes) {
const commits = await getCommitsForLLM(folderPath, hashes);
if (commits.length === 1) {
// Only one commit: Prompt LLM for a message just for this diff.
const diff = commits[0].diff;
return `You're a professional programmer who writes git commit messages all day.
Generate a concise git commit message for these changes:
${diff}
--------------------------------------
Answer VERY BRIEFLY. Don't give any feedback on the code, just analyze what changed and write the git commit message. Keep it short! A commit message MUST BE STRAIGHT TO THE POINT!
Also reply to my message, just give me the commit message.`;
} else if (commits.length > 1) {
// Multiple commits: Squash them, give all diffs as a big change.
const combinedDiffs = commits.map(c => c.diff).join('\n\n');
return `You're a professional programmer who writes git commit messages all day.
Analyze the following code changes (from multiple commits). To squash them into a single commit I need a concise git commit message from you describing the changes in a single sentence.
Here are the combined diffs:
${combinedDiffs}
--------------------------------------
Even if this might seem like a lot of code, I need you to answer VERY BRIEFLY. A git commit message to be precise is what I need from you, to protocol these changes.
Don't give any feedback on the code! Just analyze what changed and write the git commit message. Keep it short! A commit message MUST BE STRAIGHT TO THE POINT!
Don't reply to my message, just give me the commit message.`;
} else {
throw new Error('No commits found for LLM prompt.');
}
}
// 3. LLM Streaming Call