@aaswe/codebase-ai
Version:
AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs
601 lines • 24.7 kB
JavaScript
;
/**
* Business Context Preserver
*
* System to preserve developer enhancements and business context during re-analysis.
* Handles conflict resolution, context merging, and enhancement tracking.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BusinessContextPreserver = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const events_1 = require("events");
const logger_1 = __importDefault(require("../../utils/logger"));
/**
* Business Context Preserver
*
* Preserves developer enhancements and business context during automatic re-analysis
* with intelligent conflict resolution and enhancement tracking.
*/
class BusinessContextPreserver extends events_1.EventEmitter {
config;
enhancementCache = new Map();
conflictQueue = [];
backupFilesCreated = 0;
isInitialized = false;
constructor(config = {}) {
super();
this.config = {
preservationEnabled: true,
backupEnabled: true,
backupDirectory: './backups/business-context',
conflictResolution: 'merge',
enhancementMarkers: [
'# Business Context:',
'# Developer Note:',
'# Custom Enhancement:',
'aide:businessContext',
'aide:developerNote',
'aide:customProperty'
],
businessContextPatterns: [
/aide:businessContext\s+"([^"]+)"/g,
/aide:businessDomain\s+"([^"]+)"/g,
/aide:developerNote\s+"([^"]+)"/g,
/aide:customProperty\s+"([^"]+)"/g,
/#\s*Business Context:\s*(.+)/g,
/#\s*Developer Note:\s*(.+)/g
],
maxBackupVersions: 10,
...config
};
}
/**
* Initialize the business context preserver
*/
async initialize() {
if (this.isInitialized) {
return;
}
try {
logger_1.default.info('Initializing Business Context Preserver', {
preservationEnabled: this.config.preservationEnabled,
backupEnabled: this.config.backupEnabled,
conflictResolution: this.config.conflictResolution
});
// Create backup directory if enabled
if (this.config.backupEnabled) {
await fs.mkdir(this.config.backupDirectory, { recursive: true });
}
this.isInitialized = true;
logger_1.default.info('Business Context Preserver initialized successfully');
}
catch (error) {
logger_1.default.error('Failed to initialize Business Context Preserver', { error });
throw error;
}
}
/**
* Preserve business context before re-analysis
*/
async preserveContext(filePaths) {
if (!this.isInitialized) {
await this.initialize();
}
const startTime = Date.now();
const result = {
success: false,
preservedEnhancements: 0,
conflictsDetected: 0,
conflictsResolved: 0,
backupsCreated: 0,
errors: [],
warnings: [],
processingTime: 0,
statistics: this.initializeStats()
};
try {
logger_1.default.info('Starting business context preservation', {
totalFiles: filePaths.length,
preservationEnabled: this.config.preservationEnabled
});
if (!this.config.preservationEnabled) {
result.success = true;
result.processingTime = Date.now() - startTime;
return result;
}
// Process each file
for (const filePath of filePaths) {
try {
const fileResult = await this.preserveFileContext(filePath);
result.statistics.totalFilesProcessed++;
result.statistics.enhancementsExtracted += fileResult.enhancementsExtracted;
result.preservedEnhancements += fileResult.enhancementsExtracted;
if (fileResult.backupCreated) {
result.backupsCreated++;
result.statistics.backupFilesCreated++;
}
this.emit('file_processed', {
filePath,
enhancementsExtracted: fileResult.enhancementsExtracted,
backupCreated: fileResult.backupCreated
});
}
catch (error) {
logger_1.default.error('Failed to preserve context for file', { filePath, error });
result.errors.push({
type: 'EXTRACTION_ERROR',
message: `Failed to preserve context: ${error.message}`,
filePath,
details: error
});
}
}
result.statistics.enhancementsPreserved = result.preservedEnhancements;
result.processingTime = Date.now() - startTime;
result.success = result.errors.length === 0 || result.preservedEnhancements > 0;
logger_1.default.info('Business context preservation completed', {
success: result.success,
preservedEnhancements: result.preservedEnhancements,
backupsCreated: result.backupsCreated,
processingTime: result.processingTime
});
this.emit('preservation_completed', result);
return result;
}
catch (error) {
logger_1.default.error('Business context preservation failed', { error });
result.errors.push({
type: 'EXTRACTION_ERROR',
message: `Preservation failed: ${error.message}`,
details: error
});
result.processingTime = Date.now() - startTime;
this.emit('preservation_failed', { error, result });
return result;
}
}
/**
* Restore business context after re-analysis
*/
async restoreContext(filePaths) {
if (!this.isInitialized) {
await this.initialize();
}
const startTime = Date.now();
const result = {
success: false,
preservedEnhancements: 0,
conflictsDetected: 0,
conflictsResolved: 0,
backupsCreated: 0,
errors: [],
warnings: [],
processingTime: 0,
statistics: this.initializeStats()
};
try {
logger_1.default.info('Starting business context restoration', {
totalFiles: filePaths.length,
cachedEnhancements: this.enhancementCache.size
});
// Process each file
for (const filePath of filePaths) {
try {
const fileResult = await this.restoreFileContext(filePath);
result.statistics.totalFilesProcessed++;
result.statistics.enhancementsPreserved += fileResult.enhancementsRestored;
result.statistics.conflictsAutoResolved += fileResult.conflictsResolved;
result.conflictsDetected += fileResult.conflictsDetected;
result.conflictsResolved += fileResult.conflictsResolved;
this.emit('file_restored', {
filePath,
enhancementsRestored: fileResult.enhancementsRestored,
conflictsResolved: fileResult.conflictsResolved
});
}
catch (error) {
logger_1.default.error('Failed to restore context for file', { filePath, error });
result.errors.push({
type: 'MERGE_ERROR',
message: `Failed to restore context: ${error.message}`,
filePath,
details: error
});
}
}
result.processingTime = Date.now() - startTime;
result.success = result.errors.length === 0 || result.statistics.enhancementsPreserved > 0;
logger_1.default.info('Business context restoration completed', {
success: result.success,
enhancementsRestored: result.statistics.enhancementsPreserved,
conflictsResolved: result.conflictsResolved,
processingTime: result.processingTime
});
this.emit('restoration_completed', result);
return result;
}
catch (error) {
logger_1.default.error('Business context restoration failed', { error });
result.errors.push({
type: 'MERGE_ERROR',
message: `Restoration failed: ${error.message}`,
details: error
});
result.processingTime = Date.now() - startTime;
return result;
}
}
/**
* Get pending conflicts requiring manual resolution
*/
getPendingConflicts() {
return this.conflictQueue.filter(conflict => conflict.resolution === 'manual');
}
/**
* Resolve a conflict manually
*/
async resolveConflict(conflictId, resolution, resolvedContent) {
try {
const conflict = this.conflictQueue.find(c => c.conflictId === conflictId);
if (!conflict) {
throw new Error(`Conflict not found: ${conflictId}`);
}
conflict.resolution = resolution;
if (resolvedContent !== undefined) {
conflict.resolvedContent = resolvedContent;
}
// Apply the resolution
if (resolvedContent) {
await fs.writeFile(conflict.filePath, resolvedContent, 'utf-8');
}
// Remove from queue
this.conflictQueue = this.conflictQueue.filter(c => c.conflictId !== conflictId);
logger_1.default.info('Conflict resolved manually', {
conflictId,
filePath: conflict.filePath,
resolution
});
this.emit('conflict_resolved', { conflictId, resolution });
return true;
}
catch (error) {
logger_1.default.error('Failed to resolve conflict', { conflictId, error });
return false;
}
}
/**
* Get preservation statistics
*/
getStatistics() {
const totalEnhancements = Array.from(this.enhancementCache.values())
.reduce((total, enhancements) => total + enhancements.length, 0);
return {
totalFilesProcessed: this.enhancementCache.size,
enhancementsExtracted: totalEnhancements,
enhancementsPreserved: totalEnhancements,
enhancementsLost: 0,
conflictsAutoResolved: 0,
conflictsRequiringManualResolution: this.conflictQueue.length,
backupFilesCreated: this.backupFilesCreated
};
}
/**
* Preserve business context (alias for preserveContext)
*/
async preserveBusinessContext(filePaths) {
return this.preserveContext(filePaths);
}
// Private helper methods
async preserveFileContext(filePath) {
try {
// Check if file exists
const fileExists = await fs.access(filePath).then(() => true).catch(() => false);
if (!fileExists) {
return { enhancementsExtracted: 0, backupCreated: false };
}
// Read file content
const content = await fs.readFile(filePath, 'utf-8');
// Extract enhancements
const enhancements = await this.extractEnhancements(filePath, content);
// Cache enhancements
if (enhancements.length > 0) {
this.enhancementCache.set(filePath, enhancements);
}
// Create backup if enabled
let backupCreated = false;
if (this.config.backupEnabled && enhancements.length > 0) {
await this.createBackup(filePath, content);
backupCreated = true;
this.backupFilesCreated++;
}
return {
enhancementsExtracted: enhancements.length,
backupCreated
};
}
catch (error) {
logger_1.default.error('Failed to preserve file context', { filePath, error });
throw error;
}
}
async restoreFileContext(filePath) {
try {
const cachedEnhancements = this.enhancementCache.get(filePath);
if (!cachedEnhancements || cachedEnhancements.length === 0) {
return { enhancementsRestored: 0, conflictsDetected: 0, conflictsResolved: 0 };
}
// Read current file content
const currentContent = await fs.readFile(filePath, 'utf-8');
// Merge enhancements
const mergeResult = await this.mergeEnhancements(filePath, currentContent, cachedEnhancements);
// Write merged content
if (mergeResult.mergedContent !== currentContent) {
await fs.writeFile(filePath, mergeResult.mergedContent, 'utf-8');
}
return {
enhancementsRestored: mergeResult.enhancementsRestored,
conflictsDetected: mergeResult.conflictsDetected,
conflictsResolved: mergeResult.conflictsResolved
};
}
catch (error) {
logger_1.default.error('Failed to restore file context', { filePath, error });
throw error;
}
}
async extractEnhancements(filePath, content) {
const enhancements = [];
try {
// Extract business context patterns
for (const pattern of this.config.businessContextPatterns) {
const matches = content.matchAll(pattern);
for (const match of matches) {
const enhancement = {
id: this.generateEnhancementId(filePath, match[0]),
filePath,
type: this.determineEnhancementType(match[0]),
content: match[0],
timestamp: new Date(),
version: '1.0.0',
hash: this.generateHash(match[0]),
preservationPriority: this.determinePriority(match[0])
};
enhancements.push(enhancement);
}
}
// Extract comment-based enhancements
const commentEnhancements = this.extractCommentEnhancements(filePath, content);
enhancements.push(...commentEnhancements);
logger_1.default.debug('Extracted enhancements from file', {
filePath,
enhancementCount: enhancements.length
});
return enhancements;
}
catch (error) {
logger_1.default.error('Failed to extract enhancements', { filePath, error });
return [];
}
}
async mergeEnhancements(filePath, currentContent, enhancements) {
let mergedContent = currentContent;
let enhancementsRestored = 0;
let conflictsDetected = 0;
let conflictsResolved = 0;
try {
for (const enhancement of enhancements) {
const mergeResult = await this.mergeEnhancement(mergedContent, enhancement);
if (mergeResult.conflict) {
conflictsDetected++;
if (mergeResult.autoResolved) {
conflictsResolved++;
mergedContent = mergeResult.content;
enhancementsRestored++;
}
else {
// Add to conflict queue for manual resolution
this.conflictQueue.push({
conflictId: this.generateConflictId(),
filePath,
conflictType: 'content_mismatch',
originalContent: currentContent,
newContent: mergedContent,
enhancedContent: enhancement.content,
resolution: 'manual',
timestamp: new Date()
});
}
}
else {
mergedContent = mergeResult.content;
enhancementsRestored++;
}
}
return {
mergedContent,
enhancementsRestored,
conflictsDetected,
conflictsResolved
};
}
catch (error) {
logger_1.default.error('Failed to merge enhancements', { filePath, error });
throw error;
}
}
async mergeEnhancement(content, enhancement) {
// Simple merge strategy - append enhancement if not already present
if (content.includes(enhancement.content)) {
return { content, conflict: false, autoResolved: false };
}
// Try to find appropriate insertion point
const insertionPoint = this.findInsertionPoint(content, enhancement);
if (insertionPoint !== -1) {
const beforeContent = content.substring(0, insertionPoint);
const afterContent = content.substring(insertionPoint);
const mergedContent = beforeContent + '\n' + enhancement.content + afterContent;
return { content: mergedContent, conflict: false, autoResolved: false };
}
// If no good insertion point, append at end
const mergedContent = content + '\n\n' + enhancement.content;
return { content: mergedContent, conflict: false, autoResolved: false };
}
extractCommentEnhancements(filePath, content) {
const enhancements = [];
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
for (const marker of this.config.enhancementMarkers) {
if (line.startsWith(marker)) {
const enhancement = {
id: this.generateEnhancementId(filePath, line),
filePath,
type: 'documentation',
content: line,
timestamp: new Date(),
version: '1.0.0',
hash: this.generateHash(line),
preservationPriority: 'medium'
};
enhancements.push(enhancement);
}
}
}
return enhancements;
}
async createBackup(filePath, content) {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = path.basename(filePath);
const backupFileName = `${fileName}.backup.${timestamp}`;
const backupPath = path.join(this.config.backupDirectory, backupFileName);
await fs.writeFile(backupPath, content, 'utf-8');
// Clean up old backups
await this.cleanupOldBackups(fileName);
logger_1.default.debug('Created backup file', { originalPath: filePath, backupPath });
}
catch (error) {
logger_1.default.error('Failed to create backup', { filePath, error });
throw error;
}
}
async cleanupOldBackups(fileName) {
try {
const files = await fs.readdir(this.config.backupDirectory);
const backupFiles = files
.filter(file => file.startsWith(fileName + '.backup.'))
.sort()
.reverse();
if (backupFiles.length > this.config.maxBackupVersions) {
const filesToDelete = backupFiles.slice(this.config.maxBackupVersions);
for (const file of filesToDelete) {
await fs.unlink(path.join(this.config.backupDirectory, file));
}
}
}
catch (error) {
logger_1.default.warn('Failed to cleanup old backups', { fileName, error });
}
}
findInsertionPoint(content, enhancement) {
// Try to find a logical insertion point based on enhancement type
const lines = content.split('\n');
if (enhancement.type === 'business_context') {
// Insert after module declaration or at the beginning
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes('aide:Module') || lines[i].includes('@prefix')) {
return content.indexOf(lines[i]) + lines[i].length;
}
}
}
return -1; // No specific insertion point found
}
determineEnhancementType(content) {
if (content.includes('businessContext') || content.includes('Business Context')) {
return 'business_context';
}
if (content.includes('developerNote') || content.includes('Developer Note')) {
return 'documentation';
}
if (content.includes('customProperty')) {
return 'custom_property';
}
return 'documentation';
}
determinePriority(content) {
if (content.includes('businessContext') || content.includes('businessDomain')) {
return 'high';
}
if (content.includes('customProperty') || content.includes('relationship')) {
return 'medium';
}
return 'low';
}
generateEnhancementId(filePath, content) {
const hash = this.generateHash(filePath + content);
return `enhancement_${hash.substring(0, 8)}`;
}
generateConflictId() {
return `conflict_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
}
generateHash(content) {
// Simple hash function for demonstration
let hash = 0;
for (let i = 0; i < content.length; i++) {
const char = content.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return Math.abs(hash).toString(16);
}
initializeStats() {
return {
totalFilesProcessed: 0,
enhancementsExtracted: 0,
enhancementsPreserved: 0,
enhancementsLost: 0,
conflictsAutoResolved: 0,
conflictsRequiringManualResolution: 0,
backupFilesCreated: 0
};
}
}
exports.BusinessContextPreserver = BusinessContextPreserver;
//# sourceMappingURL=BusinessContextPreserver.js.map