import { execSync } from "node:child_process" interface GitFileStat { path: string added: number removed: number status: "modified" | "added" | "deleted" } export function getGitDiffStats(directory: string): GitFileStat[] { try { const statusOutput = execSync("git status --porcelain", { cwd: directory, encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"], }).trim() if (!statusOutput) return [] const statusMap = new Map() const untrackedFiles: string[] = [] for (const line of statusOutput.split("\n")) { if (!line) continue const status = line.substring(0, 2).trim() const filePath = line.substring(3) if (status === "??") { statusMap.set(filePath, "added") untrackedFiles.push(filePath) } else if (status === "A") { statusMap.set(filePath, "added") } else if (status === "D") { statusMap.set(filePath, "deleted") } else { statusMap.set(filePath, "modified") } } const output = execSync("git diff --numstat HEAD", { cwd: directory, encoding: "utf-8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"], }).trim() const stats: GitFileStat[] = [] const trackedPaths = new Set() if (output) { for (const line of output.split("\n")) { const parts = line.split("\t") if (parts.length < 3) continue const [addedStr, removedStr, path] = parts const added = addedStr === "-" ? 0 : parseInt(addedStr, 10) const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10) trackedPaths.add(path) stats.push({ path, added, removed, status: statusMap.get(path) ?? "modified", }) } } for (const filePath of untrackedFiles) { if (trackedPaths.has(filePath)) continue try { const content = execSync(`wc -l < "${filePath}"`, { cwd: directory, encoding: "utf-8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"], }).trim() const lineCount = parseInt(content, 10) || 0 stats.push({ path: filePath, added: lineCount, removed: 0, status: "added" }) } catch { stats.push({ path: filePath, added: 0, removed: 0, status: "added" }) } } return stats } catch { return [] } } export function formatFileChanges(stats: GitFileStat[], notepadPath?: string): string { if (stats.length === 0) return "[FILE CHANGES SUMMARY]\nNo file changes detected.\n" const modified = stats.filter((s) => s.status === "modified") const added = stats.filter((s) => s.status === "added") const deleted = stats.filter((s) => s.status === "deleted") const lines: string[] = ["[FILE CHANGES SUMMARY]"] if (modified.length > 0) { lines.push("Modified files:") for (const f of modified) { lines.push(` ${f.path} (+${f.added}, -${f.removed})`) } lines.push("") } if (added.length > 0) { lines.push("Created files:") for (const f of added) { lines.push(` ${f.path} (+${f.added})`) } lines.push("") } if (deleted.length > 0) { lines.push("Deleted files:") for (const f of deleted) { lines.push(` ${f.path} (-${f.removed})`) } lines.push("") } if (notepadPath) { const notepadStat = stats.find((s) => s.path.includes("notepad") || s.path.includes(".sisyphus")) if (notepadStat) { lines.push("[NOTEPAD UPDATED]") lines.push(` ${notepadStat.path} (+${notepadStat.added})`) lines.push("") } } return lines.join("\n") }