aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
245 lines (213 loc) ⢠7.53 kB
JavaScript
/**
* Manifest Preview Utility
* Shows diff view for manifest updates
* @module manifest-preview
*/
const fs = require('fs-extra');
const path = require('path');
const yaml = require('js-yaml');
const ComponentPreview = require('./component-preview');
const chalk = require('chalk');
class ManifestPreview {
constructor(rootPath) {
this.rootPath = rootPath || process.cwd();
this.manifestPath = path.join(this.rootPath, 'aios-core', 'team-manifest.yaml');
this.componentPreview = new ComponentPreview();
}
/**
* Preview manifest update for a new component
* @param {string} componentType - Type of component
* @param {Object} componentInfo - Component information
* @returns {Promise<Object>} Preview result with diff
*/
async previewManifestUpdate(componentType, componentInfo) {
try {
// Read current manifest
const currentContent = await fs.readFile(this.manifestPath, 'utf8');
const manifest = yaml.load(currentContent);
// Create updated manifest
const updatedManifest = this.addComponentToManifest(manifest, componentType, componentInfo);
const updatedContent = yaml.dump(updatedManifest, {
styles: {
'!!null': 'canonical'
},
sortKeys: false
});
// Generate diff preview
const diff = this.componentPreview.generateManifestDiff(currentContent, updatedContent);
return {
success: true,
diff,
currentContent,
updatedContent,
changes: this.summarizeChanges(manifest, updatedManifest)
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
/**
* Add component to manifest structure
* @private
*/
addComponentToManifest(manifest, componentType, componentInfo) {
const updated = JSON.parse(JSON.stringify(manifest)); // Deep clone
switch (componentType) {
case 'agent':
if (!updated.team) updated.team = {};
if (!updated.team.agents) updated.team.agents = [];
// Check if agent already exists
const existingAgentIndex = updated.team.agents.findIndex(
a => a.id === componentInfo.id
);
if (existingAgentIndex >= 0) {
// Update existing
updated.team.agents[existingAgentIndex] = {
id: componentInfo.id,
path: `agents/${componentInfo.id}.md`,
name: componentInfo.name || componentInfo.id,
...(componentInfo.description && { description: componentInfo.description })
};
} else {
// Add new
updated.team.agents.push({
id: componentInfo.id,
path: `agents/${componentInfo.id}.md`,
name: componentInfo.name || componentInfo.id,
...(componentInfo.description && { description: componentInfo.description })
});
}
break;
case 'workflow':
if (!updated.workflows) updated.workflows = [];
const existingWorkflowIndex = updated.workflows.findIndex(
w => w.id === componentInfo.id
);
if (existingWorkflowIndex >= 0) {
updated.workflows[existingWorkflowIndex] = {
id: componentInfo.id,
path: `workflows/${componentInfo.id}.yaml`,
name: componentInfo.name || componentInfo.id,
type: componentInfo.type || 'standard',
...(componentInfo.description && { description: componentInfo.description })
};
} else {
updated.workflows.push({
id: componentInfo.id,
path: `workflows/${componentInfo.id}.yaml`,
name: componentInfo.name || componentInfo.id,
type: componentInfo.type || 'standard',
...(componentInfo.description && { description: componentInfo.description })
});
}
break;
}
// Update metadata
if (!updated.metadata) updated.metadata = {};
updated.metadata.lastUpdated = new Date().toISOString();
updated.metadata.version = this.incrementVersion(updated.metadata.version || '1.0.0');
return updated;
}
/**
* Increment semantic version
* @private
*/
incrementVersion(version) {
const parts = version.split('.');
parts[2] = String(parseInt(parts[2] || '0') + 1);
return parts.join('.');
}
/**
* Summarize changes made to manifest
* @private
*/
summarizeChanges(oldManifest, newManifest) {
const changes = [];
// Check agents
const oldAgents = oldManifest.team?.agents || [];
const newAgents = newManifest.team?.agents || [];
if (newAgents.length > oldAgents.length) {
const added = newAgents.slice(oldAgents.length);
added.forEach(agent => {
changes.push({
type: 'added',
category: 'agent',
item: agent.id
});
});
}
// Check workflows
const oldWorkflows = oldManifest.workflows || [];
const newWorkflows = newManifest.workflows || [];
if (newWorkflows.length > oldWorkflows.length) {
const added = newWorkflows.slice(oldWorkflows.length);
added.forEach(workflow => {
changes.push({
type: 'added',
category: 'workflow',
item: workflow.id
});
});
}
// Version change
if (oldManifest.metadata?.version !== newManifest.metadata?.version) {
changes.push({
type: 'updated',
category: 'version',
from: oldManifest.metadata?.version || '1.0.0',
to: newManifest.metadata?.version
});
}
return changes;
}
/**
* Apply manifest update after preview
* @param {string} updatedContent - New manifest content
*/
async applyManifestUpdate(updatedContent) {
await fs.writeFile(this.manifestPath, updatedContent, 'utf8');
}
/**
* Show interactive manifest update prompt
* @param {string} componentType - Type of component
* @param {Object} componentInfo - Component information
* @returns {Promise<boolean>} Whether update was applied
*/
async interactiveManifestUpdate(componentType, componentInfo) {
const preview = await this.previewManifestUpdate(componentType, componentInfo);
if (!preview.success) {
console.log(chalk.red(`\nâ Failed to preview manifest update: ${preview.error}`));
return false;
}
// Show diff
console.log(preview.diff);
// Show change summary
console.log(chalk.cyan('\nđ Summary of changes:'));
preview.changes.forEach(change => {
if (change.type === 'added') {
console.log(chalk.green(` + Added ${change.category}: ${change.item}`));
} else if (change.type === 'updated') {
console.log(chalk.yellow(` ~ Updated ${change.category}: ${change.from} â ${change.to}`));
}
});
// Confirm update
const inquirer = require('inquirer');
const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: 'Apply these changes to team-manifest.yaml?',
default: true
}]);
if (confirm) {
await this.applyManifestUpdate(preview.updatedContent);
console.log(chalk.green('\nâ
Manifest updated successfully!'));
return true;
}
console.log(chalk.yellow('\nâ ď¸ Manifest update cancelled'));
return false;
}
}
module.exports = ManifestPreview;