cortexweaver
Version:
CortexWeaver is a command-line interface (CLI) tool that orchestrates a swarm of specialized AI agents, powered by Claude Code and Gemini CLI, to assist in software development. It transforms a high-level project plan (plan.md) into a series of coordinate
464 lines • 18.5 kB
JavaScript
"use strict";
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PersonaLoader = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const yaml = __importStar(require("js-yaml"));
/**
* PersonaLoader class for loading and managing agent personas
* Extracted from the main persona.ts file to handle file loading and caching
*/
class PersonaLoader {
constructor(config = {}) {
this.cache = new Map();
this.watchers = new Map();
this.config = {
promptsDirectory: config.promptsDirectory || path.join(process.cwd(), 'prompts'),
enableHotReload: config.enableHotReload ?? false,
cacheTtl: config.cacheTtl ?? 300000, // 5 minutes
validateFormat: config.validateFormat ?? true,
fallbackToRaw: config.fallbackToRaw ?? true,
...config
};
// Ensure prompts directory exists
if (!fs.existsSync(this.config.promptsDirectory)) {
throw new Error(`Prompts directory not found: ${this.config.promptsDirectory}`);
}
}
/**
* Load a persona for the specified agent
*/
async loadPersona(agentName) {
try {
const personaPath = this.getPersonaPath(agentName);
// Check cache first
const cached = this.getCachedPersona(agentName, personaPath);
if (cached) {
return {
success: true,
persona: cached,
usedFallback: false,
warnings: []
};
}
// Load from file
const result = await this.loadPersonaFromFile(personaPath);
// Cache the result if successful
if (result.success && result.persona) {
this.cachePersona(agentName, result.persona, personaPath);
// Setup hot reload if enabled
if (this.config.enableHotReload) {
this.setupHotReload(agentName, personaPath);
}
}
return result;
}
catch (error) {
return {
success: false,
error: `Failed to load persona for ${agentName}: ${error.message}`,
usedFallback: false,
warnings: []
};
}
}
/**
* Load persona from file and parse it
*/
async loadPersonaFromFile(filePath) {
try {
if (!fs.existsSync(filePath)) {
return {
success: false,
error: `Persona file not found: ${filePath}`,
usedFallback: false,
warnings: []
};
}
const stats = fs.statSync(filePath);
const content = fs.readFileSync(filePath, 'utf-8');
// Try to parse the persona
const parseResult = this.parsePersona(content, filePath, stats.mtime);
if (parseResult.success && parseResult.persona) {
return parseResult;
}
// If parsing failed and fallback is enabled, return raw content
if (this.config.fallbackToRaw) {
const fallbackPersona = {
role: this.extractRole(content) || 'Unknown Role',
coreIdentity: this.extractCoreIdentity(content) || content.substring(0, 200) + '...',
primaryResponsibilities: [],
behavioralGuidelines: [],
interactionPatterns: {},
successMetrics: [],
adaptationTriggers: [],
version: {
initialRelease: 'Unknown',
lastUpdated: stats.mtime.toISOString(),
improvementTrigger: 'Fallback mode'
},
metadata: {
id: path.basename(filePath, '.md'),
name: path.basename(filePath, '.md'),
category: 'fallback',
priority: 'medium',
tags: ['fallback'],
dependencies: [],
capabilities: [],
custom: {}
},
rawContent: content,
filePath,
lastModified: stats.mtime
};
return {
success: true,
persona: fallbackPersona,
usedFallback: true,
warnings: [`Failed to parse persona, using fallback: ${parseResult.error}`]
};
}
return parseResult;
}
catch (error) {
return {
success: false,
error: `Error reading persona file: ${error.message}`,
usedFallback: false,
warnings: []
};
}
}
/**
* Parse persona markdown content into structured format
*/
parsePersona(content, filePath, lastModified) {
try {
const warnings = [];
// Parse front-matter and content
const { frontMatter, markdownContent } = this.parseFrontMatter(content);
// Extract metadata from front-matter
const metadata = this.extractMetadata(frontMatter, filePath);
// Extract role
const role = this.extractRole(markdownContent);
// Extract core identity
const coreIdentity = this.extractCoreIdentity(markdownContent);
// Extract sections
const primaryResponsibilities = this.extractListSection(markdownContent, 'Primary Responsibilities');
const behavioralGuidelines = this.extractListSection(markdownContent, 'Behavioral Guidelines');
const interactionPatterns = this.extractInteractionPatterns(markdownContent);
const successMetrics = this.extractListSection(markdownContent, 'Success Metrics');
const adaptationTriggers = this.extractListSection(markdownContent, 'Adaptation Triggers');
const technicalExpertise = this.extractListSection(markdownContent, 'Technical Expertise');
const toolsAndTechniques = this.extractListSection(markdownContent, 'Tools & Techniques');
// Extract version information
const version = this.extractVersion(markdownContent);
const persona = {
role: role || 'Unknown Role',
coreIdentity: coreIdentity || 'No core identity defined',
primaryResponsibilities,
behavioralGuidelines,
interactionPatterns,
successMetrics,
adaptationTriggers,
version,
metadata,
technicalExpertise: technicalExpertise.length > 0 ? technicalExpertise : undefined,
toolsAndTechniques: toolsAndTechniques.length > 0 ? toolsAndTechniques : undefined,
rawContent: content,
filePath,
lastModified
};
// Validate if enabled
if (this.config.validateFormat) {
const validationWarnings = this.validatePersona(persona);
warnings.push(...validationWarnings);
}
return {
success: true,
persona,
usedFallback: false,
warnings
};
}
catch (error) {
return {
success: false,
error: `Failed to parse persona: ${error.message}`,
usedFallback: false,
warnings: []
};
}
}
parseFrontMatter(content) {
const frontMatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
const match = content.match(frontMatterRegex);
if (match) {
try {
const frontMatter = yaml.load(match[1]) || {};
return { frontMatter, markdownContent: match[2] };
}
catch (error) {
return { frontMatter: {}, markdownContent: content };
}
}
return { frontMatter: {}, markdownContent: content };
}
extractMetadata(frontMatter, filePath) {
const defaultMetadata = {
id: path.basename(filePath, '.md'),
name: frontMatter.name || path.basename(filePath, '.md'),
category: frontMatter.category || 'general',
priority: frontMatter.priority || 'medium',
tags: frontMatter.tags || [],
dependencies: frontMatter.dependencies || [],
capabilities: frontMatter.capabilities || [],
modelPreferences: frontMatter.modelPreferences,
performance: frontMatter.performance,
custom: frontMatter.custom || {}
};
return { ...defaultMetadata, ...frontMatter };
}
extractRole(content) {
const roleMatch = content.match(/##\s*Role\s*\n\*\*(.*?)\*\*/);
return roleMatch ? roleMatch[1].trim() : null;
}
extractCoreIdentity(content) {
const match = content.match(/##\s*Core Identity\s*\n([\s\S]*?)(?=\n##|\n$)/);
return match ? match[1].trim() : null;
}
extractListSection(content, sectionName) {
const headerPattern = new RegExp(`(##|###)\\s*${sectionName}\\s*\\n`, 'i');
const headerMatch = content.match(headerPattern);
if (!headerMatch)
return [];
const headerIndex = headerMatch.index + headerMatch[0].length;
const afterHeader = content.substring(headerIndex);
const nextHeaderMatch = afterHeader.match(/\n(##|###)\s/);
const sectionContent = nextHeaderMatch
? afterHeader.substring(0, nextHeaderMatch.index)
: afterHeader;
return sectionContent.split('\n')
.map(line => line.trim())
.filter(line => line.startsWith('-'))
.map(line => line.replace(/^-\s*/, '').trim())
.filter(item => item.length > 0);
}
extractInteractionPatterns(content) {
const patterns = {};
const sectionMatch = content.match(/##\s*Interaction Patterns\s*\n([\s\S]*?)(?=\n##|\n$)/);
if (!sectionMatch)
return patterns;
const sectionContent = sectionMatch[1];
const subsectionMatches = sectionContent.matchAll(/###\s*(.+?)\n([\s\S]*?)(?=\n###|\n$|$)/g);
for (const match of subsectionMatches) {
const title = match[1].trim();
const subsectionText = match[2];
if (title) {
const items = subsectionText.split('\n')
.map(line => line.trim())
.filter(line => line.startsWith('-'))
.map(line => line.replace(/^-\s*/, '').trim())
.filter(item => item.length > 0);
if (items.length > 0) {
patterns[title] = items;
}
}
}
return patterns;
}
extractVersion(content) {
const versionMatch = content.match(/##\s*Version\s*\n([\s\S]*?)(?=\n##|\n$)/);
const defaultVersion = {
initialRelease: 'Unknown',
lastUpdated: new Date().toISOString(),
improvementTrigger: 'Unknown'
};
if (!versionMatch)
return defaultVersion;
const versionContent = versionMatch[1];
const initialMatch = versionContent.match(/-\s*Initial Release:\s*(.*)/);
const updatedMatch = versionContent.match(/-\s*Last Updated:\s*(.*)/);
const triggerMatch = versionContent.match(/-\s*Improvement Trigger:\s*(.*)/);
return {
initialRelease: initialMatch ? initialMatch[1].trim() : defaultVersion.initialRelease,
lastUpdated: updatedMatch ? updatedMatch[1].trim() : defaultVersion.lastUpdated,
improvementTrigger: triggerMatch ? triggerMatch[1].trim() : defaultVersion.improvementTrigger
};
}
validatePersona(persona) {
const warnings = [];
if (!persona.role || persona.role === 'Unknown Role') {
warnings.push('Missing or invalid role definition');
}
if (!persona.coreIdentity || persona.coreIdentity.length < 50) {
warnings.push('Core identity is missing or too short');
}
if (persona.primaryResponsibilities.length === 0) {
warnings.push('No primary responsibilities defined');
}
if (persona.behavioralGuidelines.length === 0) {
warnings.push('No behavioral guidelines defined');
}
if (Object.keys(persona.interactionPatterns).length === 0) {
warnings.push('No interaction patterns defined');
}
if (persona.successMetrics.length === 0) {
warnings.push('No success metrics defined');
}
return warnings;
}
getPersonaPath(agentName) {
return path.join(this.config.promptsDirectory, `${agentName}.md`);
}
getCachedPersona(agentName, filePath) {
const cached = this.cache.get(agentName);
if (!cached)
return null;
// Check if cache is still valid
const now = new Date();
const cacheAge = now.getTime() - cached.loadedAt.getTime();
if (cacheAge > this.config.cacheTtl) {
this.cache.delete(agentName);
return null;
}
// Check if file has been modified
try {
const currentStats = fs.statSync(filePath);
if (currentStats.mtime.getTime() > cached.fileStats.mtime.getTime()) {
this.cache.delete(agentName);
return null;
}
}
catch {
// File might have been deleted
this.cache.delete(agentName);
return null;
}
return cached.persona;
}
cachePersona(agentName, persona, filePath) {
try {
const stats = fs.statSync(filePath);
this.cache.set(agentName, {
persona,
loadedAt: new Date(),
fileStats: stats
});
}
catch {
// Ignore caching errors
}
}
setupHotReload(agentName, filePath) {
const existingWatcher = this.watchers.get(agentName);
if (existingWatcher) {
existingWatcher.close();
}
try {
const watcher = fs.watch(filePath, (eventType) => {
if (eventType === 'change') {
this.cache.delete(agentName);
}
});
this.watchers.set(agentName, watcher);
}
catch {
// Ignore watcher setup errors
}
}
getAvailablePersonas() {
try {
const files = fs.readdirSync(this.config.promptsDirectory);
return files
.filter(file => file.endsWith('.md'))
.map(file => path.basename(file, '.md'));
}
catch {
return [];
}
}
getPersonaMetrics(agentName) {
const cached = this.cache.get(agentName);
if (!cached)
return null;
return {
loadTime: Date.now() - cached.loadedAt.getTime(),
cacheHits: 1, // Simplified - would need more sophisticated tracking
lastLoaded: cached.loadedAt,
fileSize: cached.persona.rawContent.length,
complexity: this.calculateComplexity(cached.persona)
};
}
calculateComplexity(persona) {
const complexity = Math.min(persona.rawContent.length / 1000, 10) +
(persona.primaryResponsibilities.length + persona.behavioralGuidelines.length +
persona.successMetrics.length + persona.adaptationTriggers.length) * 0.1 +
Object.keys(persona.interactionPatterns).length * 0.2;
return Math.round(complexity * 10) / 10;
}
async savePersonaVersion(persona, changes, reason) {
const historyEntry = {
version: `v${Date.now()}`,
timestamp: new Date().toISOString(),
changes,
reason,
metrics: {}
};
if (!persona.version.history) {
persona.version.history = [];
}
persona.version.history.push(historyEntry);
persona.version.lastUpdated = historyEntry.timestamp;
if (persona.version.history.length > 10) {
persona.version.history = persona.version.history.slice(-10);
}
}
dispose() {
this.cache.clear();
this.watchers.forEach(watcher => {
try {
watcher.close();
}
catch {
// Ignore cleanup errors
}
});
this.watchers.clear();
}
}
exports.PersonaLoader = PersonaLoader;
//# sourceMappingURL=loader.js.map