UNPKG

automagik-genie

Version:

Self-evolving AI agent orchestration framework with Model Context Protocol support

128 lines (127 loc) 4.56 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.applyUpgrade = applyUpgrade; const path_1 = __importDefault(require("path")); const fs_1 = require("fs"); const child_process_1 = require("child_process"); const fs_utils_1 = require("../fs-utils"); /** * Apply upgrade diff to workspace */ async function applyUpgrade(diff, context) { const { workspacePath, oldVersion, newVersion } = context; // Backup .genie directory const backupId = await backupGenieDirectory(workspacePath); try { // Test if patch applies cleanly (0, child_process_1.execSync)(`git apply --check ${diff.patchFile}`, { cwd: workspacePath, stdio: 'pipe' }); // Clean apply - no conflicts (0, child_process_1.execSync)(`git apply ${diff.patchFile}`, { cwd: workspacePath, stdio: 'pipe' }); // Update .framework-version await updateFrameworkVersion(workspacePath, oldVersion, newVersion, context); // Clean up temp patch file await fs_1.promises.unlink(diff.patchFile).catch(() => { }); return { success: true, filesUpdated: diff.affectedFiles.length, filesPreserved: 0, // TODO: Count user files conflicts: [], backupId }; } catch (error) { // Conflicts detected const conflicts = parseConflicts(error, diff.affectedFiles); return { success: false, filesUpdated: 0, filesPreserved: 0, conflicts, backupId }; } } /** * Backup .genie directory before upgrade * Returns backup ID for tracking */ async function backupGenieDirectory(workspacePath) { const genieDir = path_1.default.join(workspacePath, '.genie'); const backupId = (0, fs_utils_1.toIsoId)(); const backupDir = path_1.default.join(workspacePath, `.genie.backup-${backupId}`); try { // Use cp -r for recursive copy (0, child_process_1.execSync)(`cp -r "${genieDir}" "${backupDir}"`, { stdio: 'pipe' }); return backupId; } catch (error) { throw new Error(`Failed to backup .genie directory: ${error instanceof Error ? error.message : String(error)}`); } } /** * Update .framework-version file */ async function updateFrameworkVersion(workspacePath, oldVersion, newVersion, context) { const versionPath = path_1.default.join(workspacePath, '.genie', '.framework-version'); const now = new Date().toISOString(); // Read existing version data (if exists) let existingData = {}; try { const existingContent = await fs_1.promises.readFile(versionPath, 'utf8'); existingData = JSON.parse(existingContent); } catch { // File doesn't exist or is invalid, start fresh } const versionData = { installed_version: newVersion, installed_commit: context.newCommit, installed_date: existingData.installed_date || now, package_name: 'automagik-genie', customized_files: existingData.customized_files || [], deleted_files: existingData.deleted_files || [], last_upgrade_date: now, previous_version: oldVersion, upgrade_history: [ ...(existingData.upgrade_history || []), { from: oldVersion, to: newVersion, date: now, success: true } ] }; await fs_1.promises.writeFile(versionPath, JSON.stringify(versionData, null, 2), 'utf8'); } /** * Parse conflicts from git apply error output */ function parseConflicts(error, affectedFiles) { const errorMessage = error instanceof Error ? error.message : String(error); // Git apply error format: "error: patch failed: .genie/spells/learn.md:15" const conflictPattern = /error: patch failed: (.+?):(\d+)/g; const matches = [...errorMessage.matchAll(conflictPattern)]; if (matches.length === 0) { // Generic error, assume all files conflicted return affectedFiles.map(file => ({ file, hunks: [], reason: 'Failed to apply patch' })); } return matches.map(match => ({ file: match[1], hunks: [`Line ${match[2]}`], reason: 'User modification conflicts with upstream changes' })); }