UNPKG

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
"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