import path from 'path'; import fs from 'fs-extra'; import yaml from 'yaml'; import chalk from 'chalk'; export interface ProjectConfig { name: string; template: string; version: string; createdAt: string; updatedAt: string; currentPhase: 'initialization' | 'validation' | 'architecture' | 'implementation' | 'deployment'; personas: { [personaName: string]: { lastInteraction: string; artifacts: string[]; }; }; artifacts: { [artifactName: string]: { type: 'document' | 'schema' | 'configuration'; path: string; createdBy: string; createdAt: string; }; }; } export class ProjectState { private projectRoot: string; private vcsysDir: string; private configPath: string; constructor() { this.projectRoot = process.cwd(); this.vcsysDir = path.join(this.projectRoot, '.vcsys'); this.configPath = path.join(this.vcsysDir, 'project.yaml'); } async initialize(projectName: string, template: string = 'saas-starter'): Promise { console.log(chalk.gray('📁 Creating project structure...')); // Create .vcsys directory structure await fs.ensureDir(this.vcsysDir); await fs.ensureDir(path.join(this.vcsysDir, 'personas')); await fs.ensureDir(path.join(this.vcsysDir, 'artifacts')); await fs.ensureDir(path.join(this.vcsysDir, 'templates')); // Create initial project configuration const config: ProjectConfig = { name: projectName, template, version: '1.0.0', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), currentPhase: 'initialization', personas: {}, artifacts: {} }; await this.saveConfig(config); console.log(chalk.green('✅ Project state initialized')); } async loadConfig(): Promise { if (!await fs.pathExists(this.configPath)) { throw new Error('No VC-SYS project found. Run: vcsys init '); } const configData = await fs.readFile(this.configPath, 'utf-8'); return yaml.parse(configData) as ProjectConfig; } async saveConfig(config: ProjectConfig): Promise { config.updatedAt = new Date().toISOString(); const yamlData = yaml.stringify(config, { indent: 2 }); await fs.writeFile(this.configPath, yamlData, 'utf-8'); } async updatePhase(phase: ProjectConfig['currentPhase']): Promise { const config = await this.loadConfig(); config.currentPhase = phase; await this.saveConfig(config); } async recordPersonaInteraction(personaName: string, artifacts: string[] = []): Promise { const config = await this.loadConfig(); if (!config.personas[personaName]) { config.personas[personaName] = { lastInteraction: new Date().toISOString(), artifacts: [] }; } config.personas[personaName].lastInteraction = new Date().toISOString(); config.personas[personaName].artifacts.push(...artifacts); await this.saveConfig(config); } async recordArtifact(name: string, type: ProjectConfig['artifacts'][string]['type'], relativePath: string, createdBy: string): Promise { const config = await this.loadConfig(); config.artifacts[name] = { type, path: relativePath, createdBy, createdAt: new Date().toISOString() }; await this.saveConfig(config); } async getStatus(): Promise { const config = await this.loadConfig(); const statusLines = [ chalk.bold('Project:') + ` ${config.name}`, chalk.bold('Template:') + ` ${config.template}`, chalk.bold('Phase:') + ` ${config.currentPhase}`, chalk.bold('Created:') + ` ${new Date(config.createdAt).toLocaleDateString()}`, chalk.bold('Updated:') + ` ${new Date(config.updatedAt).toLocaleDateString()}`, '', chalk.bold('Personas Interacted:') + ` ${Object.keys(config.personas).length}`, chalk.bold('Artifacts Created:') + ` ${Object.keys(config.artifacts).length}` ]; if (Object.keys(config.personas).length > 0) { statusLines.push('', chalk.bold('Recent Persona Activity:')); Object.entries(config.personas).forEach(([name, data]) => { statusLines.push(` • ${name}: ${new Date(data.lastInteraction).toLocaleDateString()}`); }); } return statusLines.join('\n'); } async exists(): Promise { return await fs.pathExists(this.configPath); } getProjectRoot(): string { return this.projectRoot; } getVcsysDir(): string { return this.vcsysDir; } }