import path from 'path'; import fs from 'fs-extra'; import yaml from 'yaml'; import chalk from 'chalk'; export interface ResourceDependency { type: 'persona' | 'task' | 'template' | 'checklist' | 'data' | 'util'; name: string; version?: string; } export interface PersonaResource { name: string; description: string; capabilities: string[]; dependencies: ResourceDependency[]; templates: string[]; tasks: string[]; checklists: string[]; } export interface TaskResource { name: string; description: string; steps: string[]; dependencies: ResourceDependency[]; outputs: string[]; } export interface TemplateResource { name: string; description: string; type: 'document' | 'configuration' | 'schema'; template: string; variables: Record; } export class ResourceManager { private resourcesDir: string; private loadedPersonas: Map = new Map(); private loadedTasks: Map = new Map(); private loadedTemplates: Map = new Map(); constructor() { // Resources are in the CLI root/resources directory this.resourcesDir = path.join(__dirname, '..', '..', 'resources'); } async validateResources(): Promise { const validationResults: string[] = [ chalk.blue('📋 Resource Validation Report'), '' ]; try { // Check if resources directory exists const resourcesExist = await fs.pathExists(this.resourcesDir); if (!resourcesExist) { validationResults.push(chalk.yellow('⚠️ Resources directory not found')); validationResults.push(chalk.gray(` Expected: ${this.resourcesDir}`)); validationResults.push(chalk.gray(' Status: Will be created during Phase 1 development')); validationResults.push(''); validationResults.push(chalk.bold('📍 Current Task: Phase 1, Task 2.1.1 - Create knowledge base structure')); return validationResults.join('\n'); } // Validate each resource type const resourceTypes = ['personas', 'tasks', 'templates', 'checklists', 'data', 'utils']; for (const type of resourceTypes) { const typePath = path.join(this.resourcesDir, type); const typeExists = await fs.pathExists(typePath); if (typeExists) { const files = await fs.readdir(typePath); validationResults.push(chalk.green(`✅ ${type}: ${files.length} files`)); } else { validationResults.push(chalk.yellow(`⚠️ ${type}: Directory not found`)); } } validationResults.push(''); validationResults.push(chalk.bold('Resource System Status: In Development')); validationResults.push(chalk.gray('Phase 1 will implement the complete knowledge base')); } catch (error) { validationResults.push(chalk.red('❌ Validation failed:'), String(error)); } return validationResults.join('\n'); } async loadPersona(personaName: string): Promise { if (this.loadedPersonas.has(personaName)) { return this.loadedPersonas.get(personaName)!; } const personaPath = path.join(this.resourcesDir, 'personas', `${personaName}.yaml`); if (!await fs.pathExists(personaPath)) { throw new Error(`Persona not found: ${personaName}`); } const personaData = await fs.readFile(personaPath, 'utf-8'); const persona = yaml.parse(personaData) as PersonaResource; this.loadedPersonas.set(personaName, persona); return persona; } async loadTask(taskName: string): Promise { if (this.loadedTasks.has(taskName)) { return this.loadedTasks.get(taskName)!; } const taskPath = path.join(this.resourcesDir, 'tasks', `${taskName}.yaml`); if (!await fs.pathExists(taskPath)) { throw new Error(`Task not found: ${taskName}`); } const taskData = await fs.readFile(taskPath, 'utf-8'); const task = yaml.parse(taskData) as TaskResource; this.loadedTasks.set(taskName, task); return task; } async loadTemplate(templateName: string): Promise { if (this.loadedTemplates.has(templateName)) { return this.loadedTemplates.get(templateName)!; } const templatePath = path.join(this.resourcesDir, 'templates', `${templateName}.yaml`); if (!await fs.pathExists(templatePath)) { throw new Error(`Template not found: ${templateName}`); } const templateData = await fs.readFile(templatePath, 'utf-8'); const template = yaml.parse(templateData) as TemplateResource; this.loadedTemplates.set(templateName, template); return template; } async resolvePersonaDependencies(persona: PersonaResource): Promise<{ tasks: TaskResource[]; templates: TemplateResource[]; }> { const tasks: TaskResource[] = []; const templates: TemplateResource[] = []; // Load persona's tasks for (const taskName of persona.tasks) { try { const task = await this.loadTask(taskName); tasks.push(task); } catch (error) { console.warn(chalk.yellow(`⚠️ Task not found: ${taskName}`)); } } // Load persona's templates for (const templateName of persona.templates) { try { const template = await this.loadTemplate(templateName); templates.push(template); } catch (error) { console.warn(chalk.yellow(`⚠️ Template not found: ${templateName}`)); } } return { tasks, templates }; } async renderTemplate(templateName: string, variables: Record): Promise { const template = await this.loadTemplate(templateName); let rendered = template.template; // Simple template variable substitution for (const [key, value] of Object.entries(variables)) { const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g'); rendered = rendered.replace(regex, String(value)); } return rendered; } async saveDocument(content: string, filename: string, projectDir: string): Promise { const artifactsDir = path.join(projectDir, '.vcsys', 'artifacts'); await fs.ensureDir(artifactsDir); const filePath = path.join(artifactsDir, filename); // Normalize line endings to Unix format (\n) for SQL files to prevent PostgreSQL parsing issues let normalizedContent = content; if (filename.endsWith('.sql')) { normalizedContent = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); } await fs.writeFile(filePath, normalizedContent, 'utf-8'); return filePath; } getResourcesDirectory(): string { return this.resourcesDir; } async listAvailablePersonas(): Promise { const personasDir = path.join(this.resourcesDir, 'personas'); if (!await fs.pathExists(personasDir)) { return []; } const files = await fs.readdir(personasDir); return files .filter(file => file.endsWith('.yaml') || file.endsWith('.yml')) .map(file => path.parse(file).name); } async createResourcesDirectory(): Promise { const directories = [ this.resourcesDir, path.join(this.resourcesDir, 'personas'), path.join(this.resourcesDir, 'tasks'), path.join(this.resourcesDir, 'templates'), path.join(this.resourcesDir, 'checklists'), path.join(this.resourcesDir, 'data'), path.join(this.resourcesDir, 'utils') ]; for (const dir of directories) { await fs.ensureDir(dir); } console.log(chalk.green('✅ Resources directory structure created')); } }