claude-flow-multilang
Version:
Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture
391 lines (337 loc) • 10.6 kB
JavaScript
import { promises as fs } from 'fs';
// post-init-validator.js - Post-initialization verification checks
export class PostInitValidator {
constructor(workingDir) {
this.workingDir = workingDir;
}
/**
* Check file integrity (existence, size, readability)
*/
async checkFileIntegrity() {
const result = {
success: true,
errors: [],
warnings: [],
files: {},
};
const expectedFiles = [
{ path: 'CLAUDE.md', minSize: 100 },
{ path: 'memory-bank.md', minSize: 50 },
{ path: 'coordination.md', minSize: 50 },
{ path: 'memory/claude-flow-data.json', minSize: 10 },
{ path: 'memory/agents/README.md', minSize: 10 },
{ path: 'memory/sessions/README.md', minSize: 10 },
{ path: 'claude-flow', minSize: 50, executable: true },
];
for (const file of expectedFiles) {
const filePath = `${this.workingDir}/${file.path}`;
try {
const stat = await fs.stat(filePath);
// Check if it exists and is a file
if (!stat.isFile) {
result.success = false;
result.errors.push(`Expected file but found directory: ${file.path}`);
result.files[file.path] = { status: 'not_file' };
continue;
}
// Check file size
if (stat.size < file.minSize) {
result.success = false;
result.errors.push(
`File too small: ${file.path} (${stat.size} bytes, expected >= ${file.minSize})`,
);
result.files[file.path] = { status: 'too_small', size: stat.size };
continue;
}
// Check if executable (if required)
if (file.executable && Deno.build.os !== 'windows') {
const isExecutable = (stat.mode & 0o111) !== 0;
if (!isExecutable) {
result.warnings.push(`File not executable: ${file.path}`);
result.files[file.path] = { status: 'not_executable', size: stat.size };
continue;
}
}
// Try to read the file
try {
await fs.readFile(filePath, 'utf8');
result.files[file.path] = { status: 'ok', size: stat.size };
} catch (readError) {
result.success = false;
result.errors.push(`Cannot read file: ${file.path} - ${readError.message}`);
result.files[file.path] = { status: 'unreadable', size: stat.size };
}
} catch (error) {
result.success = false;
result.errors.push(`File not found: ${file.path}`);
result.files[file.path] = { status: 'missing' };
}
}
return result;
}
/**
* Check completeness of initialization
*/
async checkCompleteness() {
const result = {
success: true,
errors: [],
warnings: [],
missing: [],
};
const requiredDirs = [
'memory',
'memory/agents',
'memory/sessions',
'coordination',
'coordination/memory_bank',
'coordination/subtasks',
'coordination/orchestration',
'.claude',
'.claude/commands',
'.claude/logs',
];
const optionalDirs = [
'.roo',
'.roo/templates',
'.roo/workflows',
'.roo/modes',
'.roo/configs',
'.claude/commands/sparc',
];
// Check required directories
for (const dir of requiredDirs) {
const dirPath = `${this.workingDir}/${dir}`;
try {
const stat = await fs.stat(dirPath);
if (!stat.isDirectory) {
result.success = false;
result.errors.push(`Expected directory but found file: ${dir}`);
result.missing.push(dir);
}
} catch {
result.success = false;
result.errors.push(`Required directory missing: ${dir}`);
result.missing.push(dir);
}
}
// Check optional directories (SPARC-related)
for (const dir of optionalDirs) {
const dirPath = `${this.workingDir}/${dir}`;
try {
await fs.stat(dirPath);
} catch {
if (dir.includes('.roo') || dir.includes('sparc')) {
result.warnings.push(`Optional SPARC directory missing: ${dir}`);
}
}
}
return result;
}
/**
* Validate directory structure and organization
*/
async validateStructure() {
const result = {
success: true,
errors: [],
warnings: [],
structure: {},
};
try {
// Check memory structure
const memoryStructure = await this.validateMemoryStructure();
result.structure.memory = memoryStructure;
if (!memoryStructure.valid) {
result.warnings.push('Memory directory structure is incomplete');
}
// Check coordination structure
const coordinationStructure = await this.validateCoordinationStructure();
result.structure.coordination = coordinationStructure;
if (!coordinationStructure.valid) {
result.warnings.push('Coordination directory structure is incomplete');
}
// Check Claude integration structure
const claudeStructure = await this.validateClaudeStructure();
result.structure.claude = claudeStructure;
if (!claudeStructure.valid) {
result.warnings.push('Claude integration structure is incomplete');
}
// Check SPARC structure (if present)
const sparcExists = await this.checkSparcExists();
if (sparcExists) {
const sparcStructure = await this.validateSparcStructure();
result.structure.sparc = sparcStructure;
if (!sparcStructure.valid) {
result.warnings.push('SPARC structure is incomplete');
}
}
} catch (error) {
result.success = false;
result.errors.push(`Structure validation failed: ${error.message}`);
}
return result;
}
/**
* Check permissions on created files and directories
*/
async checkPermissions() {
const result = {
success: true,
errors: [],
warnings: [],
permissions: {},
};
const itemsToCheck = [
{ path: 'CLAUDE.md', type: 'file', requiredMode: 0o644 },
{ path: 'memory-bank.md', type: 'file', requiredMode: 0o644 },
{ path: 'coordination.md', type: 'file', requiredMode: 0o644 },
{ path: 'claude-flow', type: 'file', requiredMode: 0o755 },
{ path: 'memory', type: 'dir', requiredMode: 0o755 },
{ path: 'coordination', type: 'dir', requiredMode: 0o755 },
{ path: '.claude', type: 'dir', requiredMode: 0o755 },
];
// Skip permission checks on Windows
if (Deno.build.os === 'windows') {
result.warnings.push('Permission checks skipped on Windows');
return result;
}
for (const item of itemsToCheck) {
const itemPath = `${this.workingDir}/${item.path}`;
try {
const stat = await fs.stat(itemPath);
const actualMode = stat.mode & 0o777;
const expectedMode = item.requiredMode;
result.permissions[item.path] = {
actual: actualMode.toString(8),
expected: expectedMode.toString(8),
correct: actualMode === expectedMode,
};
if (actualMode !== expectedMode) {
result.warnings.push(
`Incorrect permissions on ${item.path}: ` +
`${actualMode.toString(8)} (expected ${expectedMode.toString(8)})`,
);
}
} catch (error) {
result.warnings.push(`Could not check permissions for ${item.path}: ${error.message}`);
}
}
return result;
}
// Helper methods
async validateMemoryStructure() {
const structure = {
valid: true,
dirs: [],
files: [],
};
const expectedDirs = ['agents', 'sessions'];
const expectedFiles = ['claude-flow-data.json', 'agents/README.md', 'sessions/README.md'];
for (const dir of expectedDirs) {
try {
await fs.stat(`${this.workingDir}/memory/${dir}`);
structure.dirs.push(dir);
} catch {
structure.valid = false;
}
}
for (const file of expectedFiles) {
try {
await fs.stat(`${this.workingDir}/memory/${file}`);
structure.files.push(file);
} catch {
structure.valid = false;
}
}
return structure;
}
async validateCoordinationStructure() {
const structure = {
valid: true,
dirs: [],
};
const expectedDirs = ['memory_bank', 'subtasks', 'orchestration'];
for (const dir of expectedDirs) {
try {
await fs.stat(`${this.workingDir}/coordination/${dir}`);
structure.dirs.push(dir);
} catch {
structure.valid = false;
}
}
return structure;
}
async validateClaudeStructure() {
const structure = {
valid: true,
dirs: [],
hasCommands: false,
};
const expectedDirs = ['commands', 'logs'];
for (const dir of expectedDirs) {
try {
await fs.stat(`${this.workingDir}/.claude/${dir}`);
structure.dirs.push(dir);
} catch {
structure.valid = false;
}
}
// Check if there are any command files
try {
const entries = [];
for await (const entry of fs.readdir(`${this.workingDir}/.claude/commands`)) {
if (entry.isFile && entry.name.endsWith('.js')) {
entries.push(entry.name);
}
}
structure.hasCommands = entries.length > 0;
structure.commandCount = entries.length;
} catch {
structure.hasCommands = false;
}
return structure;
}
async checkSparcExists() {
try {
await fs.stat(`${this.workingDir}/.roomodes`);
return true;
} catch {
return false;
}
}
async validateSparcStructure() {
const structure = {
valid: true,
hasRoomodes: false,
hasRooDir: false,
dirs: [],
};
// Check .roomodes file
try {
const stat = await fs.stat(`${this.workingDir}/.roomodes`);
structure.hasRoomodes = stat.isFile;
} catch {
structure.valid = false;
}
// Check .roo directory
try {
const stat = await fs.stat(`${this.workingDir}/.roo`);
structure.hasRooDir = stat.isDirectory;
if (structure.hasRooDir) {
const expectedDirs = ['templates', 'workflows', 'modes', 'configs'];
for (const dir of expectedDirs) {
try {
await fs.stat(`${this.workingDir}/.roo/${dir}`);
structure.dirs.push(dir);
} catch {
// Optional subdirectories
}
}
}
} catch {
// .roo directory is optional
}
return structure;
}
}