claude-flow-multilang
Version:
Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture
583 lines (496 loc) • 19.9 kB
JavaScript
// template-copier.js - Copy template files instead of generating them dynamically
import { existsSync } from '../../node-compat.js';
import { promises as fs } from 'fs';
import { dirname, join, relative } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Copy template files from the templates directory to the target directory
* @param {string} targetDir - The directory to copy templates to
* @param {Object} options - Options for template copying
* @param {boolean} options.sparc - Whether to include SPARC templates
* @param {boolean} options.enhanced - Whether to use enhanced templates
* @param {boolean} options.minimal - Whether to use minimal templates
* @param {boolean} options.optimized - Whether to use optimized templates
* @param {boolean} options.dryRun - Whether to perform a dry run
* @param {boolean} options.force - Whether to overwrite existing files
* @param {string[]} options.selectedModes - Selected SPARC modes to copy
* @returns {Promise<{success: boolean, copiedFiles: string[], errors: string[]}>}
*/
export async function copyTemplates(targetDir, options = {}) {
const results = {
success: true,
copiedFiles: [],
errors: [],
};
try {
const templatesDir = join(__dirname, 'templates');
// Determine which template variants to use
const templateVariant = options.optimized ? 'optimized' :
options.enhanced ? 'enhanced' :
options.minimal ? 'minimal' :
options.sparc ? 'sparc' : 'full';
// Core files to copy
const coreFiles = [
{
source: 'CLAUDE.md',
destination: 'CLAUDE.md',
useVariant: true
},
{
source: 'memory-bank.md',
destination: 'memory-bank.md',
useVariant: true
},
{
source: 'coordination.md',
destination: 'coordination.md',
useVariant: true
},
];
// Copy core files
for (const file of coreFiles) {
const sourceFile = file.useVariant && existsSync(join(templatesDir, `${file.source}.${templateVariant}`))
? `${file.source}.${templateVariant}`
: file.source;
const sourcePath = join(templatesDir, sourceFile);
const destPath = join(targetDir, file.destination);
if (await copyFile(sourcePath, destPath, options)) {
results.copiedFiles.push(file.destination);
} else if (!options.dryRun) {
results.errors.push(`Failed to copy ${file.destination}`);
}
}
// Copy .claude directory structure
if (options.enhanced || !options.minimal) {
const claudeDir = join(targetDir, '.claude');
// Copy settings.json
const settingsSource = options.enhanced ? 'settings.json.enhanced' : 'settings.json';
const settingsPath = join(templatesDir, settingsSource);
const settingsDest = join(claudeDir, 'settings.json');
if (!options.dryRun) {
await fs.mkdir(claudeDir, { recursive: true });
}
if (await copyFile(settingsPath, settingsDest, options)) {
results.copiedFiles.push('.claude/settings.json');
}
// Copy command templates
if (options.sparc || options.enhanced) {
await copyCommandTemplates(templatesDir, targetDir, options, results);
}
// Copy helper scripts (enhanced mode only)
if (options.enhanced) {
await copyHelperScripts(templatesDir, targetDir, options, results);
}
}
// Copy SPARC-specific files
if (options.sparc) {
await copySparcTemplates(templatesDir, targetDir, options, results);
}
// Copy wrapper scripts
await copyWrapperScripts(templatesDir, targetDir, options, results);
// Create directory structure
await createDirectoryStructure(targetDir, options);
// Create README files for memory directories
await createMemoryReadmeFiles(targetDir, options, results);
} catch (err) {
results.success = false;
results.errors.push(`Template copy failed: ${err.message}`);
}
return results;
}
/**
* Copy a single file with options
*/
async function copyFile(source, destination, options) {
try {
// Check if source exists
if (!existsSync(source)) {
// Try reading from templates directory as fallback
const templateContent = await getTemplateContent(source);
if (templateContent) {
if (!options.dryRun) {
await fs.writeFile(destination, templateContent);
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} ${relative(process.cwd(), destination)}`);
return true;
}
console.log(` ⚠️ Template not found: ${relative(process.cwd(), source)}`);
return false;
}
// Check if destination exists and handle force flag
if (existsSync(destination) && !options.force) {
console.log(` ⚠️ File already exists: ${relative(process.cwd(), destination)} (use --force to overwrite)`);
return false;
}
if (!options.dryRun) {
// Ensure destination directory exists
await fs.mkdir(dirname(destination), { recursive: true });
// Copy the file
const content = await fs.readFile(source, 'utf8');
await fs.writeFile(destination, content);
// Preserve file permissions for executable scripts
if (source.endsWith('.sh') || source.includes('claude-flow')) {
await fs.chmod(destination, 0o755);
}
}
console.log(` ${options.dryRun ? '[DRY RUN] Would copy' : '✓ Copied'} ${relative(process.cwd(), destination)}`);
return true;
} catch (err) {
console.log(` ❌ Failed to copy ${relative(process.cwd(), destination)}: ${err.message}`);
return false;
}
}
/**
* Copy command templates
*/
async function copyCommandTemplates(templatesDir, targetDir, options, results) {
const commandsSourceDir = join(templatesDir, 'commands');
const commandsDestDir = join(targetDir, '.claude', 'commands');
if (!existsSync(commandsSourceDir)) {
// Use generated command templates as fallback
return await generateCommandTemplates(targetDir, options, results);
}
try {
if (!options.dryRun) {
await fs.mkdir(commandsDestDir, { recursive: true });
}
// Copy command categories
const categories = await fs.readdir(commandsSourceDir);
for (const category of categories) {
const categoryPath = join(commandsSourceDir, category);
const stat = await fs.stat(categoryPath);
if (stat.isDirectory()) {
const destCategoryPath = join(commandsDestDir, category);
if (!options.dryRun) {
await fs.mkdir(destCategoryPath, { recursive: true });
}
// Copy files in category
const files = await fs.readdir(categoryPath);
for (const file of files) {
const sourcePath = join(categoryPath, file);
const destPath = join(destCategoryPath, file);
if (await copyFile(sourcePath, destPath, options)) {
results.copiedFiles.push(join('.claude', 'commands', category, file));
}
}
}
}
} catch (err) {
results.errors.push(`Failed to copy command templates: ${err.message}`);
}
}
/**
* Copy SPARC templates
*/
async function copySparcTemplates(templatesDir, targetDir, options, results) {
const sparcDir = join(targetDir, '.claude', 'commands', 'sparc');
try {
if (!options.dryRun) {
await fs.mkdir(sparcDir, { recursive: true });
}
// Get SPARC mode templates
const { createSparcModeTemplates, createSparcModesOverview } = await import('./templates/sparc-modes.js');
const sparcTemplates = createSparcModeTemplates();
// Filter templates if selectedModes is specified
const templatesToCreate = options.selectedModes
? Object.entries(sparcTemplates).filter(([filename]) => {
const mode = filename.replace('.md', '');
return options.selectedModes.includes(mode);
})
: Object.entries(sparcTemplates);
// Write SPARC mode files
for (const [filename, content] of templatesToCreate) {
const destPath = join(sparcDir, filename);
if (!options.dryRun) {
await fs.writeFile(destPath, content);
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} .claude/commands/sparc/${filename}`);
results.copiedFiles.push(join('.claude', 'commands', 'sparc', filename));
}
// Create sparc-modes.md overview
const overviewPath = join(sparcDir, 'sparc-modes.md');
if (!options.dryRun) {
await fs.writeFile(overviewPath, createSparcModesOverview());
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} .claude/commands/sparc/sparc-modes.md`);
results.copiedFiles.push('.claude/commands/sparc/sparc-modes.md');
// Copy swarm templates
await copySwarmTemplates(templatesDir, targetDir, options, results);
} catch (err) {
results.errors.push(`Failed to copy SPARC templates: ${err.message}`);
}
}
/**
* Copy swarm strategy templates
*/
async function copySwarmTemplates(templatesDir, targetDir, options, results) {
const swarmDir = join(targetDir, '.claude', 'commands', 'swarm');
try {
if (!options.dryRun) {
await fs.mkdir(swarmDir, { recursive: true });
}
// Get swarm strategy templates
const { createSwarmStrategyTemplates } = await import('./templates/sparc-modes.js');
const swarmTemplates = createSwarmStrategyTemplates();
// Write swarm strategy files
for (const [filename, content] of Object.entries(swarmTemplates)) {
const destPath = join(swarmDir, filename);
if (!options.dryRun) {
await fs.writeFile(destPath, content);
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} .claude/commands/swarm/${filename}`);
results.copiedFiles.push(join('.claude', 'commands', 'swarm', filename));
}
} catch (err) {
results.errors.push(`Failed to copy swarm templates: ${err.message}`);
}
}
/**
* Copy helper scripts (enhanced mode)
*/
async function copyHelperScripts(templatesDir, targetDir, options, results) {
const helpersDir = join(targetDir, '.claude', 'helpers');
try {
if (!options.dryRun) {
await fs.mkdir(helpersDir, { recursive: true });
}
const helpers = ['setup-mcp.sh', 'quick-start.sh', 'github-setup.sh', 'github-safe.js', 'checkpoint-manager.sh', 'standard-checkpoint-hooks.sh'];
const { createHelperScript } = await import('./templates/enhanced-templates.js');
for (const helper of helpers) {
const content = createHelperScript(helper);
if (content) {
const destPath = join(helpersDir, helper);
if (!options.dryRun) {
await fs.writeFile(destPath, content);
await fs.chmod(destPath, 0o755);
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} .claude/helpers/${helper}`);
results.copiedFiles.push(join('.claude', 'helpers', helper));
}
}
} catch (err) {
results.errors.push(`Failed to copy helper scripts: ${err.message}`);
}
}
/**
* Copy wrapper scripts
*/
async function copyWrapperScripts(templatesDir, targetDir, options, results) {
try {
// Unix wrapper
const unixWrapperPath = join(targetDir, 'claude-flow');
const unixWrapperSource = join(templatesDir, 'claude-flow-universal');
if (await copyFile(unixWrapperSource, unixWrapperPath, options)) {
if (!options.dryRun) {
await fs.chmod(unixWrapperPath, 0o755);
}
results.copiedFiles.push('claude-flow');
}
// Windows batch wrapper
const batchWrapperPath = join(targetDir, 'claude-flow.bat');
const batchWrapperSource = join(templatesDir, 'claude-flow.bat');
if (await copyFile(batchWrapperSource, batchWrapperPath, options)) {
results.copiedFiles.push('claude-flow.bat');
}
// PowerShell wrapper
const psWrapperPath = join(targetDir, 'claude-flow.ps1');
const psWrapperSource = join(templatesDir, 'claude-flow.ps1');
if (await copyFile(psWrapperSource, psWrapperPath, options)) {
results.copiedFiles.push('claude-flow.ps1');
}
} catch (err) {
results.errors.push(`Failed to copy wrapper scripts: ${err.message}`);
}
}
/**
* Create directory structure
*/
async function createDirectoryStructure(targetDir, options) {
const directories = [
'memory',
'memory/agents',
'memory/sessions',
'coordination',
'coordination/memory_bank',
'coordination/subtasks',
'coordination/orchestration',
'.claude',
'.claude/commands',
'.claude/logs',
'.swarm', // For memory persistence
];
if (options.sparc) {
directories.push(
'.claude/commands/sparc',
'.claude/commands/swarm'
);
}
for (const dir of directories) {
const dirPath = join(targetDir, dir);
try {
if (!options.dryRun) {
await fs.mkdir(dirPath, { recursive: true });
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} ${dir}/ directory`);
} catch (err) {
if (err.code !== 'EEXIST') {
console.log(` ❌ Failed to create ${dir}/: ${err.message}`);
}
}
}
}
/**
* Create README files for memory directories
*/
async function createMemoryReadmeFiles(targetDir, options, results) {
const { createAgentsReadme, createSessionsReadme } = await import('./templates/readme-files.js');
const readmeFiles = [
{ path: 'memory/agents/README.md', content: createAgentsReadme() },
{ path: 'memory/sessions/README.md', content: createSessionsReadme() },
];
for (const { path, content } of readmeFiles) {
const fullPath = join(targetDir, path);
try {
if (!options.dryRun) {
await fs.mkdir(dirname(fullPath), { recursive: true });
await fs.writeFile(fullPath, content);
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} ${path}`);
results.copiedFiles.push(path);
} catch (err) {
results.errors.push(`Failed to create ${path}: ${err.message}`);
}
}
// Initialize persistence database
const dbPath = join(targetDir, 'memory', 'claude-flow-data.json');
const initialData = {
agents: [],
tasks: [],
lastUpdated: Date.now(),
};
try {
if (!options.dryRun) {
await fs.writeFile(dbPath, JSON.stringify(initialData, null, 2));
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} memory/claude-flow-data.json (persistence database)`);
results.copiedFiles.push('memory/claude-flow-data.json');
} catch (err) {
results.errors.push(`Failed to create persistence database: ${err.message}`);
}
}
/**
* Get template content as fallback (for backwards compatibility)
*/
async function getTemplateContent(templatePath) {
const filename = templatePath.split('/').pop();
// Map template files to their generator functions
const templateGenerators = {
'CLAUDE.md': async () => {
const { createFullClaudeMd } = await import('./templates/claude-md.js');
return createFullClaudeMd();
},
'CLAUDE.md.sparc': async () => {
const { createSparcClaudeMd } = await import('./templates/claude-md.js');
return createSparcClaudeMd();
},
'CLAUDE.md.minimal': async () => {
const { createMinimalClaudeMd } = await import('./templates/claude-md.js');
return createMinimalClaudeMd();
},
'CLAUDE.md.optimized': async () => {
const { createOptimizedSparcClaudeMd } = await import('./templates/claude-md.js');
return createOptimizedSparcClaudeMd();
},
'CLAUDE.md.enhanced': async () => {
const { createEnhancedClaudeMd } = await import('./templates/enhanced-templates.js');
return createEnhancedClaudeMd();
},
'memory-bank.md': async () => {
const { createFullMemoryBankMd } = await import('./templates/memory-bank-md.js');
return createFullMemoryBankMd();
},
'memory-bank.md.minimal': async () => {
const { createMinimalMemoryBankMd } = await import('./templates/memory-bank-md.js');
return createMinimalMemoryBankMd();
},
'memory-bank.md.optimized': async () => {
const { createOptimizedMemoryBankMd } = await import('./templates/memory-bank-md.js');
return createOptimizedMemoryBankMd();
},
'coordination.md': async () => {
const { createFullCoordinationMd } = await import('./templates/coordination-md.js');
return createFullCoordinationMd();
},
'coordination.md.minimal': async () => {
const { createMinimalCoordinationMd } = await import('./templates/coordination-md.js');
return createMinimalCoordinationMd();
},
'coordination.md.optimized': async () => {
const { createOptimizedCoordinationMd } = await import('./templates/coordination-md.js');
return createOptimizedCoordinationMd();
},
'settings.json': async () => {
return await fs.readFile(join(__dirname, 'templates', 'settings.json'), 'utf8');
},
'settings.json.enhanced': async () => {
const { createEnhancedSettingsJson } = await import('./templates/enhanced-templates.js');
return createEnhancedSettingsJson();
},
'claude-flow-universal': async () => {
return await fs.readFile(join(__dirname, 'templates', 'claude-flow-universal'), 'utf8');
},
'claude-flow.bat': async () => {
return await fs.readFile(join(__dirname, 'templates', 'claude-flow.bat'), 'utf8');
},
'claude-flow.ps1': async () => {
return await fs.readFile(join(__dirname, 'templates', 'claude-flow.ps1'), 'utf8');
},
};
const generator = templateGenerators[filename] || templateGenerators[filename.replace(/\.(sparc|minimal|optimized|enhanced)$/, '')];
if (generator) {
try {
return await generator();
} catch (err) {
console.log(` ⚠️ Failed to generate template content for ${filename}: ${err.message}`);
return null;
}
}
return null;
}
/**
* Generate command templates as fallback
*/
async function generateCommandTemplates(targetDir, options, results) {
const { COMMAND_STRUCTURE, createCommandDoc } = await import('./templates/enhanced-templates.js');
for (const [category, commands] of Object.entries(COMMAND_STRUCTURE)) {
const categoryDir = join(targetDir, '.claude', 'commands', category);
try {
if (!options.dryRun) {
await fs.mkdir(categoryDir, { recursive: true });
// Create category README
const categoryReadme = `# ${category.charAt(0).toUpperCase() + category.slice(1)} Commands
Commands for ${category} operations in Claude Flow.
## Available Commands
${commands.map(cmd => `- [${cmd}](./${cmd}.md)`).join('\n')}
`;
await fs.writeFile(join(categoryDir, 'README.md'), categoryReadme);
}
// Create individual command docs
for (const command of commands) {
const doc = createCommandDoc(category, command);
if (doc) {
const docPath = join(categoryDir, `${command}.md`);
if (!options.dryRun) {
await fs.writeFile(docPath, doc);
}
results.copiedFiles.push(join('.claude', 'commands', category, `${command}.md`));
}
}
console.log(` ${options.dryRun ? '[DRY RUN] Would create' : '✓ Created'} ${commands.length} ${category} command docs`);
} catch (err) {
results.errors.push(`Failed to generate ${category} command templates: ${err.message}`);
}
}
}