automagik-genie
Version:
Universal AI development companion that can be initialized in any codebase
1,393 lines (1,157 loc) โข 70.9 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
const {
generateProjectVariables,
copyTemplateDirectory,
processTemplateFile,
fileExists
} = require('./template-processor.js');
/**
* UNIFIED INITIALIZATION SYSTEM
* Combines legacy and smart merge functionality into a single module
*/
// ========================================
// COMMON UTILITIES AND SHARED FUNCTIONS
// ========================================
/**
* Analyze backup CLAUDE.md files for valuable project information
* @param {string} projectPath - Path to project
* @returns {Promise<Object>} Enhanced variables with backup analysis
*/
const analyzeBackupClaude = async (projectPath) => {
console.log('๐ Analyzing backup CLAUDE.md files for project information...');
const backupAnalysis = {
buildCommands: [],
testingFrameworks: [],
codeStylePrefs: [],
projectConfigs: [],
customAgents: [],
developmentNotes: [],
projectDescription: '',
originalTimestamp: null,
preservedSections: {}
};
try {
// Search for backup directories
const projectEntries = await fs.readdir(projectPath, { withFileTypes: true });
const backupDirs = projectEntries
.filter(entry => entry.isDirectory() && entry.name.includes('.claude.backup'))
.map(entry => path.join(projectPath, entry.name))
.sort() // Most recent first based on timestamp
.reverse();
if (backupDirs.length === 0) {
console.log('โน๏ธ No backup directories found');
return backupAnalysis;
}
console.log(`๐ Found ${backupDirs.length} backup director${backupDirs.length === 1 ? 'y' : 'ies'}`);
// Analyze each backup directory
for (const backupDir of backupDirs) {
const claudeMdPath = path.join(backupDir, 'CLAUDE.md');
if (await fileExists(claudeMdPath)) {
console.log(`๐ Analyzing ${path.basename(backupDir)}/CLAUDE.md`);
try {
const claudeContent = await fs.readFile(claudeMdPath, 'utf-8');
const analysis = await parseBackupClaude(claudeContent);
// Merge analysis results
backupAnalysis.buildCommands.push(...analysis.buildCommands);
backupAnalysis.testingFrameworks.push(...analysis.testingFrameworks);
backupAnalysis.codeStylePrefs.push(...analysis.codeStylePrefs);
backupAnalysis.projectConfigs.push(...analysis.projectConfigs);
backupAnalysis.customAgents.push(...analysis.customAgents);
backupAnalysis.developmentNotes.push(...analysis.developmentNotes);
// Keep the most recent project description and timestamp
if (analysis.projectDescription && !backupAnalysis.projectDescription) {
backupAnalysis.projectDescription = analysis.projectDescription;
}
if (analysis.originalTimestamp && !backupAnalysis.originalTimestamp) {
backupAnalysis.originalTimestamp = analysis.originalTimestamp;
}
// Preserve important sections
Object.assign(backupAnalysis.preservedSections, analysis.preservedSections);
} catch (parseError) {
console.log(`โ ๏ธ Could not parse ${path.basename(backupDir)}/CLAUDE.md: ${parseError.message}`);
}
}
}
// Dedupe and clean results
backupAnalysis.buildCommands = [...new Set(backupAnalysis.buildCommands)];
backupAnalysis.testingFrameworks = [...new Set(backupAnalysis.testingFrameworks)];
backupAnalysis.codeStylePrefs = [...new Set(backupAnalysis.codeStylePrefs)];
backupAnalysis.customAgents = [...new Set(backupAnalysis.customAgents)];
// Log what was recovered
logBackupAnalysisResults(backupAnalysis);
} catch (error) {
console.log(`โ ๏ธ Error during backup analysis: ${error.message}`);
console.log(' Continuing with standard initialization...');
}
return backupAnalysis;
};
/**
* Parse backup CLAUDE.md content for valuable information
* @param {string} content - CLAUDE.md file content
* @returns {Promise<Object>} Parsed information
*/
const parseBackupClaude = async (content) => {
const analysis = {
buildCommands: [],
testingFrameworks: [],
codeStylePrefs: [],
projectConfigs: [],
customAgents: [],
developmentNotes: [],
projectDescription: '',
originalTimestamp: null,
preservedSections: {}
};
const lines = content.split('\n');
let currentSection = '';
let sectionContent = [];
for (const line of lines) {
const trimmedLine = line.trim();
// Detect sections
if (trimmedLine.startsWith('#')) {
// Process previous section
if (currentSection && sectionContent.length > 0) {
await processSectionContent(currentSection, sectionContent, analysis);
}
currentSection = trimmedLine.toLowerCase();
sectionContent = [];
} else {
sectionContent.push(line);
}
// Extract build commands (clean up line prefixes)
if (trimmedLine.includes('npm run') || trimmedLine.includes('yarn') ||
trimmedLine.includes('make') || trimmedLine.includes('gradle') ||
trimmedLine.includes('cargo') || trimmedLine.includes('go build') ||
trimmedLine.includes('mvn') || trimmedLine.includes('pip install')) {
// Clean up line formatting (remove leading dashes, bullets, etc.)
const cleanCommand = trimmedLine.replace(/^[-*โข\s]*/, '').replace(/`/g, '');
if (cleanCommand) {
analysis.buildCommands.push(cleanCommand);
}
}
// Extract testing frameworks (case-insensitive, multiple per line)
const lowerLine = trimmedLine.toLowerCase();
const testFrameworks = ['cargo test', 'go test', 'jest', 'pytest', 'junit', 'mocha', 'vitest', 'cypress'];
for (const framework of testFrameworks) {
if (lowerLine.includes(framework)) {
analysis.testingFrameworks.push(framework);
}
}
// Extract code style preferences (case-insensitive, multiple per line)
const styleTools = ['eslint', 'prettier', 'ruff', 'black', 'rustfmt', 'gofmt', 'checkstyle'];
for (const tool of styleTools) {
if (lowerLine.includes(tool)) {
analysis.codeStylePrefs.push(tool);
}
}
// Extract custom agents (improved regex for both old and new naming patterns)
if (trimmedLine.includes('-genie-') || trimmedLine.includes('agent') ||
/\w+(-\w+)+-\w+(-\w+)*/.test(trimmedLine)) {
// Match both old pattern (-genie-) and new pattern (project-name-role)
const oldAgentMatch = trimmedLine.match(/(\w+(-\w+)*-genie-[\w-]+)/g);
// More flexible pattern for new naming convention: project-name-anything
const newAgentMatch = trimmedLine.match(/(\w+(-\w+)+-[\w-]+)/g);
if (oldAgentMatch) {
oldAgentMatch.forEach(agent => analysis.customAgents.push(agent));
}
if (newAgentMatch) {
// Filter out common false positives and ensure it looks like an agent name
newAgentMatch.forEach(agent => {
// Skip if it's a common non-agent pattern
if (!agent.includes('http') && !agent.includes('www') && agent.split('-').length >= 3) {
analysis.customAgents.push(agent);
}
});
}
}
// Extract original timestamp
if (trimmedLine.includes('**Initialized**:') || trimmedLine.includes('Initialized:')) {
const timestampMatch = trimmedLine.match(/(\d{4}-\d{2}-\d{2}T[\d:.Z-]+)/);
if (timestampMatch && !analysis.originalTimestamp) {
analysis.originalTimestamp = timestampMatch[1];
}
}
// Extract project description from first paragraph
if (!analysis.projectDescription && trimmedLine.length > 20 &&
!trimmedLine.startsWith('#') && !trimmedLine.startsWith('**') &&
!trimmedLine.startsWith('-') && !trimmedLine.startsWith('*')) {
analysis.projectDescription = trimmedLine;
}
}
// Process final section
if (currentSection && sectionContent.length > 0) {
await processSectionContent(currentSection, sectionContent, analysis);
}
return analysis;
};
/**
* Process section content for preservation
* @param {string} section - Section header
* @param {Array} content - Section content lines
* @param {Object} analysis - Analysis object to update
*/
const processSectionContent = async (section, content, analysis) => {
const contentStr = content.join('\n').trim();
// Preserve important custom sections
if (section.includes('development') || section.includes('workflow') ||
section.includes('pattern') || section.includes('convention') ||
section.includes('standard') || section.includes('guideline')) {
analysis.preservedSections[section] = contentStr;
analysis.developmentNotes.push(`From ${section}: ${contentStr.substring(0, 200)}...`);
}
// Preserve project-specific configurations
if (section.includes('config') || section.includes('setting') ||
section.includes('environment') || section.includes('setup')) {
analysis.projectConfigs.push(`${section}: ${contentStr.substring(0, 150)}...`);
}
};
/**
* Log backup analysis results
* @param {Object} analysis - Backup analysis results
*/
const logBackupAnalysisResults = (analysis) => {
console.log('๐ Backup Analysis Results:');
if (analysis.buildCommands.length > 0) {
console.log(` ๐ฆ Build Commands: ${analysis.buildCommands.length} found`);
analysis.buildCommands.slice(0, 3).forEach(cmd =>
console.log(` โข ${cmd.substring(0, 60)}${cmd.length > 60 ? '...' : ''}`));
}
if (analysis.testingFrameworks.length > 0) {
console.log(` ๐งช Testing Frameworks: ${analysis.testingFrameworks.join(', ')}`);
}
if (analysis.codeStylePrefs.length > 0) {
console.log(` โจ Code Style Tools: ${analysis.codeStylePrefs.join(', ')}`);
}
if (analysis.customAgents.length > 0) {
console.log(` ๐ค Custom Agents: ${analysis.customAgents.length} found`);
analysis.customAgents.slice(0, 3).forEach(agent =>
console.log(` โข ${agent}`));
}
if (analysis.originalTimestamp) {
console.log(` ๐
Original Setup: ${analysis.originalTimestamp}`);
}
if (Object.keys(analysis.preservedSections).length > 0) {
console.log(` ๐ Preserved Sections: ${Object.keys(analysis.preservedSections).length} found`);
}
console.log('โ
Backup analysis complete - information will be integrated into new configuration');
};
/**
* Create a unified backup directory with timestamp for all configuration files
* @param {string} projectPath - Path to project
* @param {Object} options - Backup options
* @param {boolean} options.claudeDir - Whether to backup .claude directory
* @param {boolean} options.claudeMd - Whether to backup CLAUDE.md file
* @param {boolean} options.mcpConfig - Whether to backup .mcp.json file
* @param {string} options.mcpConfigPath - Path to MCP config file
* @param {boolean} options.malformedMcp - Whether MCP config is malformed
* @returns {Promise<string|null>} Backup directory path or null if failed
*/
const createUnifiedBackup = async (projectPath, options = {}) => {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupDir = path.join(projectPath, `.claude.backup-${timestamp}`);
try {
// Create backup directory first
await fs.mkdir(backupDir, { recursive: true });
console.log(`๐ Created unified backup directory: ${path.basename(backupDir)}`);
// Backup .claude directory if it exists and requested
if (options.claudeDir) {
const claudeDir = path.join(projectPath, '.claude');
if (await fileExists(claudeDir)) {
const backupClaudeDir = path.join(backupDir, '.claude');
await fs.cp ?
fs.cp(claudeDir, backupClaudeDir, { recursive: true }) :
fs.rename(claudeDir, backupClaudeDir);
console.log('โ
Backed up .claude/ directory');
}
}
// Backup CLAUDE.md if it exists and requested
if (options.claudeMd) {
const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
if (await fileExists(claudeMdPath)) {
const backupClaudeMd = path.join(backupDir, 'CLAUDE.md');
await fs.copyFile(claudeMdPath, backupClaudeMd);
console.log('โ
Backed up CLAUDE.md file');
}
}
// Backup MCP config if it exists and requested
if (options.mcpConfig && options.mcpConfigPath) {
if (await fileExists(options.mcpConfigPath)) {
const mcpFileName = options.malformedMcp ? '.mcp.json.malformed' : '.mcp.json';
const backupMcpPath = path.join(backupDir, mcpFileName);
await fs.copyFile(options.mcpConfigPath, backupMcpPath);
console.log(`โ
Backed up ${mcpFileName}`);
}
}
console.log(`๐ฆ Unified backup completed: ${path.basename(backupDir)}`);
// Add helpful instructions for users to check their backup
console.log('');
console.log('๐ก Pro Tip: Check your backup for existing patterns and references!');
console.log(` ๐ View backup: ${path.basename(backupDir)}`);
console.log(' ๐ Review your existing agents, hooks, and configurations');
console.log(' ๐ Reference previous work to enhance new development patterns');
console.log('');
return backupDir;
} catch (error) {
console.log('โ ๏ธ Could not create unified backup:');
console.log(` Error: ${error.message}`);
console.log(` Code: ${error.code || 'Unknown'}`);
// Enhanced error handling with specific guidance
if (error.code === 'ENOENT') {
console.log(' This usually means a file or directory was not found.');
} else if (error.code === 'EACCES') {
console.log(' This usually means insufficient permissions.');
console.log(' Try running with appropriate permissions or manually backup files.');
} else if (error.code === 'EEXIST') {
console.log(' Backup directory already exists - using timestamp to avoid conflicts.');
}
console.log('');
console.log('๐ก Manual backup recommended:');
console.log(` cp -r .claude .claude.backup-manual-$(date +%s)`);
console.log(` cp CLAUDE.md CLAUDE.md.backup-manual-$(date +%s)`);
console.log(` cp .mcp.json .mcp.json.backup-manual-$(date +%s)`);
return null;
}
};
/**
* Create standard .mcp.json configuration with smart merging and backup
* @param {string} projectPath - Path to project directory
*/
const createMcpConfig = async (projectPath) => {
const mcpConfigPath = path.join(projectPath, '.mcp.json');
// Standard MCP servers that we want to ensure are available
const standardMcpServers = {
"ask-repo-agent": {
type: "sse",
url: "https://mcp.deepwiki.com/sse"
},
"search-repo-docs": {
command: "npx",
args: ["-y", "@upstash/context7-mcp"]
}
};
let existingConfig = null;
let shouldMerge = false;
let needsBackup = false;
let isMalformed = false;
// Check if .mcp.json already exists and handle it
if (await fileExists(mcpConfigPath)) {
console.log('๐ Found existing .mcp.json configuration');
try {
// Read and parse existing configuration
const existingContent = await fs.readFile(mcpConfigPath, 'utf-8');
existingConfig = JSON.parse(existingContent);
// Validate structure
if (!existingConfig.mcpServers) {
existingConfig.mcpServers = {};
}
// Check if we need to add any standard servers
const missingServers = [];
for (const [serverName, serverConfig] of Object.entries(standardMcpServers)) {
if (!existingConfig.mcpServers[serverName]) {
missingServers.push(serverName);
}
}
if (missingServers.length > 0) {
shouldMerge = true;
needsBackup = true;
console.log(`๐ง Will add missing standard MCP servers: ${missingServers.join(', ')}`);
} else {
console.log('โ
All standard MCP servers already configured, no changes needed');
return;
}
} catch (parseError) {
console.log(`โ ๏ธ Existing .mcp.json appears to be malformed: ${parseError.message}`);
console.log('๐ง Will create backup and generate new configuration');
// Mark for backup and reset config
needsBackup = true;
isMalformed = true;
existingConfig = null;
shouldMerge = false;
}
// Create single unified backup if needed
if (needsBackup) {
await createUnifiedBackup(projectPath, {
mcpConfig: true,
mcpConfigPath: mcpConfigPath,
malformedMcp: isMalformed
});
}
}
let finalConfig;
if (shouldMerge && existingConfig) {
// Merge configurations - preserve existing servers, add missing standard ones
finalConfig = {
...existingConfig,
mcpServers: {
...existingConfig.mcpServers,
...standardMcpServers
}
};
console.log('๐ Merging configurations:');
const existingServerNames = Object.keys(existingConfig.mcpServers);
const addedServerNames = Object.keys(standardMcpServers).filter(
name => !existingConfig.mcpServers[name]
);
if (existingServerNames.length > 0) {
console.log(` โข Preserved existing servers: ${existingServerNames.join(', ')}`);
}
if (addedServerNames.length > 0) {
console.log(` โข Added standard servers: ${addedServerNames.join(', ')}`);
}
} else {
// Create new configuration
finalConfig = {
mcpServers: standardMcpServers
};
console.log('๐ Creating new .mcp.json configuration');
}
// Write the final configuration
try {
await fs.writeFile(mcpConfigPath, JSON.stringify(finalConfig, null, 2), 'utf-8');
if (shouldMerge) {
console.log('โ
Successfully merged .mcp.json configuration');
} else {
console.log('โ
Created .mcp.json configuration with standard MCP servers');
}
// Log the configured servers
const serverNames = Object.keys(finalConfig.mcpServers);
console.log(`๐ง Configured MCP servers: ${serverNames.join(', ')}`);
} catch (writeError) {
console.log(`โ Failed to write .mcp.json: ${writeError.message}`);
throw writeError;
}
};
// ========================================
// CONTENT GENERATION FUNCTIONS
// ========================================
/**
* Generate agent template content
* @param {string} agentType - Type of agent
* @param {string} projectName - Project name
* @returns {string} Agent template content
*/
const generateAgentTemplate = (agentType, projectName) => {
const baseTemplate = `---
name: ${projectName}-${agentType}
description: ${getAgentDescription(agentType)} specifically tailored for the ${projectName} project.
Examples:
- <example>
Context: User needs ${agentType}-specific assistance for the ${projectName} project.
user: "${getUsageExample(agentType)}"
assistant: "I'll handle this ${agentType} task using project-specific patterns and tech stack awareness"
<commentary>
This agent leverages ${projectName}-analyzer findings for informed decision-making.
</commentary>
</example>
tools: Glob, Grep, LS, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, TodoWrite, WebSearch, mcp__search-repo-docs__resolve-library-id, mcp__search-repo-docs__get-library-docs, mcp__ask-repo-agent__read_wiki_structure, mcp__ask-repo-agent__read_wiki_contents, mcp__ask-repo-agent__ask_question
model: sonnet
color: ${getAgentColor(agentType)}
---
You are a ${agentType} agent for the **${projectName}** project. ${getAgentDescription(agentType)} with tech-stack-aware assistance tailored specifically for this project.
Your characteristics:
- Project-specific expertise with ${projectName} codebase understanding
- Tech stack awareness through analyzer integration
- Adaptive recommendations based on detected patterns
- Seamless coordination with other ${projectName} agents
- Professional and systematic approach to ${agentType} tasks
Your operational guidelines:
- Leverage insights from the ${projectName}-analyzer agent for context
- Follow project-specific patterns and conventions detected in the codebase
- Coordinate with other specialized agents for complex workflows
- Provide tech-stack-appropriate solutions and recommendations
- Maintain consistency with the overall ${projectName} development approach
When working on tasks:
1. **Context Integration**: Use analyzer findings for informed decision-making
2. **Tech Stack Awareness**: Apply language/framework-specific best practices
3. **Pattern Recognition**: Follow established project patterns and conventions
4. **Agent Coordination**: Work seamlessly with other ${projectName} agents
5. **Adaptive Assistance**: Adjust recommendations based on project evolution
## ๐ Capabilities
${getAgentCapabilities(agentType)}
## ๐ง Integration with ${projectName}-analyzer
- **Tech Stack Awareness**: Uses analyzer findings for language/framework-specific guidance
- **Context Sharing**: Leverages stored analysis results for informed decision-making
- **Adaptive Recommendations**: Adjusts suggestions based on detected project patterns
${getIntegrationNotes(agentType, projectName)}
Your specialized ${agentType} companion for **${projectName}**! ๐งโจ`;
return baseTemplate;
};
// Helper functions for agent template generation
const getAgentDescription = (agentType) => {
const descriptions = {
'analyzer': 'Universal codebase analysis and tech stack detection',
'dev-planner': 'Requirements analysis and technical specification creation',
'dev-designer': 'System architecture design and technical documentation',
'dev-coder': 'Code implementation based on design documents',
'dev-fixer': 'Systematic debugging and issue resolution',
'agent-creator': 'Creation of new specialized agents',
'agent-enhancer': 'Enhancement and optimization of existing agents',
'claudemd': 'CLAUDE.md documentation management and maintenance',
'clone': 'Multi-task coordination and complex workflow orchestration'
};
return descriptions[agentType] || 'Specialized development assistance';
};
const getAgentCapabilities = (agentType) => {
const capabilities = {
'analyzer': '- Universal language detection (Go, Rust, Java, Python, JS, etc.)\n- Framework identification and analysis\n- Architecture pattern recognition\n- Code quality assessment\n- Tech stack recommendations',
'dev-planner': '- Requirements gathering and analysis\n- Technical specification creation\n- User story decomposition\n- Acceptance criteria definition\n- Project planning and estimation',
'dev-designer': '- System architecture design\n- Component interaction modeling\n- Database schema design\n- API specification creation\n- Technical documentation',
'dev-coder': '- Code implementation from design specs\n- Tech-stack-specific best practices\n- Pattern implementation\n- Integration development\n- Code review and optimization',
'dev-fixer': '- Bug diagnosis and resolution\n- Performance issue identification\n- Code quality improvements\n- Testing and validation\n- Root cause analysis',
'agent-creator': '- Custom agent specification\n- Agent architecture design\n- Capability definition\n- Integration planning\n- Documentation creation',
'agent-enhancer': '- Agent capability analysis\n- Performance optimization\n- Feature enhancement\n- Integration improvements\n- Quality assurance',
'claudemd': '- Documentation structure management\n- Content consistency maintenance\n- Template processing\n- Version control integration\n- Style guide enforcement',
'clone': '- Complex task decomposition\n- Multi-agent coordination\n- Workflow orchestration\n- Context preservation\n- Progress tracking'
};
return capabilities[agentType] || '- Specialized development assistance';
};
const getUsageExample = (agentType) => {
const examples = {
'analyzer': 'analyze this codebase and identify optimization opportunities',
'dev-planner': 'create technical specification for user authentication system',
'dev-designer': 'design architecture for microservices-based API',
'dev-coder': 'implement the user registration feature based on the design document',
'dev-fixer': 'debug the failing database connection tests',
'agent-creator': 'create a specialized agent for handling payment processing',
'agent-enhancer': 'enhance the dev-coder agent with advanced error handling',
'claudemd': 'update documentation to reflect new project structure',
'clone': 'coordinate implementation of complete authentication system across multiple components'
};
return examples[agentType] || 'provide specialized assistance';
};
const getIntegrationNotes = (agentType, projectName) => {
return `- Coordinates with **${projectName}-analyzer** for tech stack context
- Integrates with other **${projectName}** agents for complex workflows
- Shares findings through memory system for cross-agent intelligence
- Adapts to project-specific patterns and conventions`;
};
const getAgentColor = (agentType) => {
const colors = {
'analyzer': 'purple',
'dev-planner': 'blue',
'dev-designer': 'green',
'dev-coder': 'yellow',
'dev-fixer': 'red',
'agent-creator': 'cyan',
'agent-enhancer': 'magenta',
'claudemd': 'orange',
'clone': 'gray'
};
return colors[agentType] || 'blue';
};
/**
* Create agent template files from existing agents
* @param {string} agentsDir - Path to agents directory
* @param {Object} variables - Template variables
*/
const createAgentTemplates = async (agentsDir, variables) => {
// Agent templates to create
const agentTemplates = [
'analyzer',
'dev-planner',
'dev-designer',
'dev-coder',
'dev-fixer',
'agent-creator',
'agent-enhancer',
'claudemd',
'clone'
];
for (const agentType of agentTemplates) {
const templateContent = generateAgentTemplate(agentType, variables.PROJECT_NAME);
const agentPath = path.join(agentsDir, `${variables.PROJECT_NAME}-${agentType}.md`);
await fs.mkdir(agentsDir, { recursive: true });
await fs.writeFile(agentPath, templateContent, 'utf-8');
}
};
/**
* Create hook examples from templates
* @param {string} hooksDir - Path to hooks directory
* @param {Object} variables - Template variables
*/
const createHookExamples = async (hooksDir, variables = {}) => {
const examplesDir = path.join(hooksDir, 'examples');
await fs.mkdir(examplesDir, { recursive: true });
// Simple hook examples (inline templates)
const hookExamples = {
'README.md': `# Hook Examples
These are example hooks for the ${variables.PROJECT_NAME} project.
## TDD Hook
- \`tdd-hook.sh\`: Enforces test-driven development
- \`tdd-validator.py\`: Validates TDD compliance
## Quality Hooks
- \`pre-commit-quality.sh\`: Runs quality checks before commits
## Configuration
- \`settings.json\`: Hook configuration settings
`,
'tdd-hook.sh': `#!/bin/bash
# TDD enforcement hook for ${variables.PROJECT_NAME}
echo "๐งช TDD Guard: Checking test-driven development compliance..."
# Check if tests exist and pass
if [ -f "package.json" ]; then
npm test
elif [ -f "pytest.ini" ] || [ -f "pyproject.toml" ]; then
python -m pytest
elif [ -f "go.mod" ]; then
go test ./...
elif [ -f "Cargo.toml" ]; then
cargo test
else
echo "โ ๏ธ No test framework detected"
exit 1
fi
echo "โ
TDD compliance verified"
`,
'settings.json': `{
"project": "${variables.PROJECT_NAME}",
"tdd": {
"enabled": true,
"testRequired": true
},
"quality": {
"enabled": true,
"autofix": true
}
}`
};
// Write hook examples
for (const [filename, content] of Object.entries(hookExamples)) {
const filePath = path.join(examplesDir, filename);
await fs.writeFile(filePath, content, 'utf-8');
// Make shell scripts executable
if (filename.endsWith('.sh')) {
await fs.chmod(filePath, 0o755);
}
}
console.log('โ
Hook examples created from templates');
};
/**
* Copy statusline files from templates
* @param {string} claudeDir - .claude directory path
*/
const copyStatuslineFiles = async (claudeDir) => {
const templateDir = path.join(__dirname, '..', 'templates', '.claude');
const statuslineFiles = ['genie-statusline.js', 'genie-statusline.ps1', 'genie-statusline.sh'];
for (const file of statuslineFiles) {
const srcPath = path.join(templateDir, file);
const destPath = path.join(claudeDir, file);
try {
if (await fileExists(srcPath)) {
await fs.copyFile(srcPath, destPath);
// Make shell scripts executable
if (file.endsWith('.sh') || file.endsWith('.js')) {
await fs.chmod(destPath, 0o755);
}
}
} catch (error) {
console.log(`โ ๏ธ Could not copy ${file}: ${error.message}`);
}
}
};
/**
* Create minimal hook file for current platform
* @param {string} claudeDir - .claude directory path
*/
const createMinimalHooks = async (claudeDir) => {
console.log('๐ช Creating minimal hook example...');
const platform = process.platform;
try {
let hookFile, hookContent, hookCommand;
if (platform === 'win32') {
// Windows PowerShell version
hookFile = 'minimal-hook.ps1';
hookCommand = 'powershell -ExecutionPolicy Bypass -File .claude/minimal-hook.ps1';
hookContent = `# ๐ง Minimal Hook Example for Windows PowerShell
# This demonstrates how hooks work with Genie
param(
[string]\$Operation = "Write"
)
# Log to a file for demonstration (optional)
# Add-Content -Path ".claude\\hook.log" -Value "[\$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] Hook triggered for: \$Operation"
# Exit silently - this is just a minimal showcase
exit 0`;
} else {
// Unix/Linux/Mac version
hookFile = 'minimal-hook.sh';
hookCommand = './.claude/minimal-hook.sh';
hookContent = `#!/bin/bash
# ๐ง Minimal Hook Example for Unix Systems (Linux/Mac)
# This demonstrates how hooks work with Genie
# Get the operation being performed (Write, Edit, MultiEdit)
OPERATION="\${1:-Write}"
# Log to a file for demonstration (optional)
# echo "[\$(date '+%Y-%m-%d %H:%M:%S')] Hook triggered for: \$OPERATION" >> .claude/hook.log
# Exit silently - this is just a minimal showcase
exit 0`;
}
const hookPath = path.join(claudeDir, hookFile);
await fs.writeFile(hookPath, hookContent, 'utf-8');
// Make executable on Unix systems
if (platform !== 'win32') {
await fs.chmod(hookPath, 0o755);
}
console.log(` โ
Created ${hookFile} for ${platform === 'win32' ? 'Windows' : 'Unix/Linux/Mac'}`);
// Return the command for settings.json
return hookCommand;
} catch (error) {
console.log(` โ ๏ธ Could not create minimal hook: ${error.message}`);
// Fallback to cross-platform JavaScript version
const jsHook = `#!/usr/bin/env node
// ๐ง Minimal Hook Example - Cross-platform JavaScript
// This demonstrates how hooks work with Genie
const operation = process.argv[2] || 'Write';
// Exit silently - this is just a minimal showcase
process.exit(0);`;
try {
const jsPath = path.join(claudeDir, 'minimal-hook.js');
await fs.writeFile(jsPath, jsHook, 'utf-8');
await fs.chmod(jsPath, 0o755);
console.log(' โ
Created fallback minimal-hook.js');
return 'node .claude/minimal-hook.js';
} catch (fallbackError) {
console.log(` โ ๏ธ Could not create fallback hook: ${fallbackError.message}`);
return null;
}
}
};
/**
* Create settings.json with platform-aware configuration
* @param {string} claudeDir - .claude directory path
* @param {string} minimalHookCommand - Command for the minimal hook from createMinimalHooks
*/
const createSettingsJson = async (claudeDir, minimalHookCommand = null) => {
console.log('โ๏ธ Creating settings.json...');
const settingsPath = path.join(claudeDir, 'settings.json');
// Only create if it doesn't exist
if (await fileExists(settingsPath)) {
console.log(' โน๏ธ settings.json already exists, preserving it');
return;
}
const settings = {
hooks: {
PreToolUse: [
{
matcher: "Write|Edit|MultiEdit",
hooks: []
}
]
},
enableHooks: true,
statusLine: {
type: "command",
command: "node .claude/genie-statusline.js"
}
};
// Add minimal hook if command was provided
if (minimalHookCommand) {
settings.hooks.PreToolUse[0].hooks.push({
type: "command",
command: minimalHookCommand,
timeout: 5000
});
}
// Check if TDD hook exists and add it
const tddHookPath = path.join(claudeDir, 'tdd_hook.sh');
if (await fileExists(tddHookPath)) {
settings.hooks.PreToolUse[0].hooks.push({
type: "command",
command: "./.claude/tdd_hook.sh",
timeout: 10000
});
}
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
console.log(' โ
Created settings.json with platform-specific hooks');
};
/**
* Create or update genie version tracking file
* @param {string} claudeDir - .claude directory path
*/
const createGenieVersion = async (claudeDir) => {
console.log('๐ Creating version tracking...');
const packageJsonPath = path.join(__dirname, '..', 'package.json');
let currentVersion = '1.0.0';
try {
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
currentVersion = packageJson.version;
} catch (error) {
console.log(' โ ๏ธ Could not read package.json version, using default');
}
const versionPath = path.join(claudeDir, 'genie-version.json');
const now = new Date().toISOString();
let versionData = {
version: currentVersion,
installedAt: now,
lastUpdated: now,
platform: process.platform
};
// If version file exists, preserve installedAt timestamp but update version and lastUpdated
if (await fileExists(versionPath)) {
try {
const existingVersion = JSON.parse(await fs.readFile(versionPath, 'utf-8'));
versionData.installedAt = existingVersion.installedAt || now;
console.log(' ๐ Updated version tracking');
} catch (error) {
console.log(' โ ๏ธ Could not parse existing version file, recreating');
}
} else {
console.log(' ๐ Created version tracking');
}
await fs.writeFile(versionPath, JSON.stringify(versionData, null, 2), 'utf-8');
};
/**
* Create the universal wish command
* @param {string} commandsDir - Path to commands directory
*/
const createWishCommand = async (commandsDir) => {
await fs.mkdir(commandsDir, { recursive: true });
const wishCommand = `# /wish - Universal Development Wish Fulfillment
---
allowed-tools: Task(*), Read(*), Write(*), Edit(*), MultiEdit(*), Glob(*), Grep(*), Bash(*), LS(*), TodoWrite(*)
description: ๐งโจ Transform any development wish into reality through intelligent agent orchestration
---
## ๐ฏ Purpose
Transform ANY development wish into perfectly orchestrated reality through intelligent agent delegation and tech-stack-aware execution.
## ๐ง Wish Fulfillment Flow
\`\`\`
/wish โ ๐ง Analysis โ ๐ฏ Agent Selection โ โก Execution โ โจ Wish Granted
\`\`\`
## ๐ Usage Examples
### Codebase Analysis
\`\`\`
/wish "analyze this codebase and provide development recommendations"
\`\`\`
### Feature Development
\`\`\`
/wish "add user authentication with JWT tokens"
/wish "implement payment processing integration"
/wish "create REST API for user management"
\`\`\`
### Debugging & Fixing
\`\`\`
/wish "fix the failing database tests"
/wish "optimize slow query performance"
/wish "resolve memory leak in background service"
\`\`\`
### Quality & Testing
\`\`\`
/wish "improve test coverage to 90%"
/wish "set up automated code quality checks"
/wish "create integration tests for API endpoints"
\`\`\`
## ๐ฏ Agent Routing Intelligence
The system automatically routes wishes to appropriate project-specific agents:
- **Analysis requests** โ project-analyzer agent
- **Planning needs** โ project-dev-planner agent
- **Architecture design** โ project-dev-designer agent
- **Implementation tasks** โ project-dev-coder agent
- **Debugging issues** โ project-dev-fixer agent
- **Complex coordination** โ project-clone agent
## ๐ง Tech Stack Intelligence
All agents leverage the analyzer's findings to provide:
- Language-specific best practices
- Framework-appropriate patterns
- Tool-specific recommendations
- Architecture-aware solutions
## โจ Wish Fulfillment Process
1. **Intelligent Analysis**: Understand wish intent and complexity
2. **Agent Selection**: Route to most appropriate project agent
3. **Context Integration**: Use analyzer findings for tech-stack awareness
4. **Execution**: Specialized agent handles the wish with full autonomy
5. **Coordination**: Multi-agent coordination for complex wishes
## ๐ก Pro Tips
- **Be specific**: "Add JWT authentication" vs "add auth"
- **Include context**: "Fix the React component rendering issue in UserProfile"
- **State your goal**: "Optimize database queries to reduce response time under 200ms"
- **Trust the analyzer**: Let it detect your tech stack and provide appropriate guidance
## ๐ The Magic
Every wish is fulfilled through intelligent agent orchestration, with full awareness of your project's tech stack, patterns, and context. No manual configuration needed - just state your wish!
**Your development wishes are our command!** ๐งโจ`;
await fs.writeFile(path.join(commandsDir, 'wish.md'), wishCommand);
};
/**
* Create fallback CLAUDE.md template with backup integration
* @param {Object} variables - Template variables
* @param {Object} backupAnalysis - Backup analysis results
*/
const createFallbackClaudeTemplate = async (variables, backupAnalysis = null) => {
let backupSection = '';
if (variables.HAS_BACKUP_DATA && backupAnalysis) {
backupSection = `
## ๐ Recovered Project Information
**Original Setup**: ${backupAnalysis.originalTimestamp || variables.TIMESTAMP}
**Configuration Enhanced**: Information recovered from backup and integrated
### ๐ฆ Build & Development Commands
${backupAnalysis.buildCommands.length > 0 ?
backupAnalysis.buildCommands.map(cmd => `- \`${cmd}\``).join('\n') :
'- No build commands found in backup'}
### ๐งช Testing & Quality
${backupAnalysis.testingFrameworks.length > 0 ?
`**Testing Frameworks**: ${backupAnalysis.testingFrameworks.join(', ')}` :
'**Testing Frameworks**: Will be detected by analyzer'}
${backupAnalysis.codeStylePrefs.length > 0 ?
`\n**Code Style Tools**: ${backupAnalysis.codeStylePrefs.join(', ')}` :
'\n**Code Style Tools**: Will be detected by analyzer'}
### ๐ค Custom Agents Found
${backupAnalysis.customAgents.length > 0 ?
backupAnalysis.customAgents.map(agent => `- ${agent}`).join('\n') :
'- No custom agents found in backup'}
${Object.keys(backupAnalysis.preservedSections).length > 0 ?
`### ๐ Preserved Sections\n${Object.entries(backupAnalysis.preservedSections)
.map(([section, content]) => `#### ${section}\n${content.substring(0, 300)}${content.length > 300 ? '...\n\n*[Section preserved from backup - full content available in agent memory]*' : ''}`)
.join('\n\n')}` :
''}`;
}
return `# ${variables.PROJECT_NAME} - Automagik Genie Configuration
## ๐ง Project-Specific Genie Instance
**Project**: ${variables.PROJECT_NAME}
**Initialized**: ${variables.TIMESTAMP}
**Path**: ${variables.PROJECT_PATH}
${backupAnalysis && backupAnalysis.originalTimestamp ? `**Previous Setup**: ${backupAnalysis.originalTimestamp}` : ''}
${backupAnalysis && backupAnalysis.projectDescription ? `\n**Project Description**: ${backupAnalysis.projectDescription}` : ''}${backupSection}
## ๐ Available Agents
### Universal Analysis
- **${variables.PROJECT_NAME}-analyzer**: Universal codebase analysis and tech stack detection
### Core Development
- **${variables.PROJECT_NAME}-dev-planner**: Requirements analysis and technical specifications
- **${variables.PROJECT_NAME}-dev-designer**: Architecture design and system patterns
- **${variables.PROJECT_NAME}-dev-coder**: Code implementation with tech-stack awareness
- **${variables.PROJECT_NAME}-dev-fixer**: Debugging and systematic issue resolution
### Agent Management
- **${variables.PROJECT_NAME}-agent-creator**: Create new specialized agents
- **${variables.PROJECT_NAME}-agent-enhancer**: Enhance and improve existing agents
- **${variables.PROJECT_NAME}-clone**: Multi-task coordination with context preservation
### Documentation
- **${variables.PROJECT_NAME}-claudemd**: CLAUDE.md documentation management
## ๐ ๏ธ Tech Stack Detection
**The ${variables.PROJECT_NAME}-analyzer agent will automatically detect:**
- Programming languages (Go, Rust, Java, Python, JavaScript, TypeScript, etc.)
- Frameworks (React, Vue, Django, FastAPI, Spring Boot, Gin, etc.)
- Build systems (Maven, Gradle, Cargo, Go modules, npm/yarn, etc.)
- Testing frameworks (Jest, pytest, Go test, Cargo test, etc.)
- Quality tools (ESLint, Ruff, rustfmt, gofmt, etc.)
**No manual configuration needed** - the analyzer handles tech stack adaptation!
## ๐ Getting Started
Run your first wish to let the analyzer understand your project:
\`\`\`
/wish "analyze this codebase and provide development recommendations"
\`\`\`
The analyzer will auto-detect your tech stack and provide customized guidance!
${variables.HAS_BACKUP_DATA ?
'๐ก **Enhanced Configuration**: This setup includes recovered information from your backup files for a more personalized experience!' :
''}
Your specialized development companion for **${variables.PROJECT_NAME}**! ๐งโจ`;
};
// ========================================
// SMART MERGE FUNCTIONALITY
// ========================================
/**
* Analyze existing .claude directory to identify user content vs genie content
* @param {string} claudeDir - Path to .claude directory
* @returns {Promise<Object>} Analysis of existing content
*/
const analyzeExistingClaude = async (claudeDir) => {
const analysis = {
userAgents: [],
genieAgents: [],
userHooks: [],
genieHooks: [],
userCommands: [],
genieCommands: [],
userFiles: [],
hasUserContent: false,
needsGenieUpdate: false
};
if (!await fileExists(claudeDir)) {
return analysis;
}
try {
// Analyze agents directory
const agentsDir = path.join(claudeDir, 'agents');
if (await fileExists(agentsDir)) {
const agentFiles = await fs.readdir(agentsDir);
for (const file of agentFiles) {
if (file.endsWith('.md')) {
const agentName = file.replace('.md', '');
// Check if it's a genie agent (matches our naming pattern)
if (isGenieAgent(agentName)) {
analysis.genieAgents.push(file);
analysis.needsGenieUpdate = await needsAgentUpdate(path.join(agentsDir, file));
} else {
analysis.userAgents.push(file);
analysis.hasUserContent = true;
}
}
}
}
// Analyze hooks directory
const hooksDir = path.join(claudeDir, 'hooks');
if (await fileExists(hooksDir)) {
const hookEntries = await fs.readdir(hooksDir, { withFileTypes: true });
for (const entry of hookEntries) {
if (entry.isFile()) {
if (isGenieHook(entry.name)) {
analysis.genieHooks.push(entry.name);
} else {
analysis.userHooks.push(entry.name);
analysis.hasUserContent = true;
}
} else if (entry.isDirectory() && entry.name === 'examples') {
// Examples directory is genie-managed
analysis.genieHooks.push('examples/');
} else {
// Other directories are user content
analysis.userHooks.push(entry.name + '/');
analysis.hasUserContent = true;
}
}
}
// Analyze commands directory
const commandsDir = path.join(claudeDir, 'commands');
if (await fileExists(commandsDir)) {
const commandFiles = await fs.readdir(commandsDir);
for (const file of commandFiles) {
if (isGenieCommand(file)) {
analysis.genieCommands.push(file);
} else {
analysis.userCommands.push(file);
analysis.hasUserContent = true;
}
}
}
// Find other user files at root level
const entries = await fs.readdir(claudeDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isFile() && !['agents', 'hooks', 'commands'].includes(entry.name)) {
analysis.userFiles.push(entry.name);
analysis.hasUserContent = true;
}
}
} catch (error) {
console.log(`โ ๏ธ Error analyzing existing .claude directory: ${error.message}`);
}
return analysis;
};
/**
* Check if agent name matches genie naming pattern
* @param {string} agentName - Agent name without .md extension
* @returns {boolean} True if it's a genie agent
*/
const isGenieAgent = (agentName) => {
// Genie agents follow pattern: project-name-role
const genieRoles = [
'analyzer', 'dev-planner', 'dev-designer', 'dev-coder', 'dev-fixer',
'agent-creator', 'agent-enhancer', 'claudemd', 'clone',
'testing-maker', 'testing-fixer', 'quality-ruff', 'quality-mypy'
];
return genieRoles.some(role => agentName.endsWith('-' + role));
};
/**
* Check if hook is genie-managed
* @param {string} hookName - Hook filename
* @returns {boolean} True if it's a genie hook
*/
const isGenieHook = (hookName) => {
const genieHooks = [
'examples', // Directory
'tdd-hook.sh',
'tdd-validator.py',
'pre-commit-quality.sh',
'settings.json',
'README.md'
];
return genieHooks.includes(hookName);
};
/**
* Check if command is genie-managed
* @param {string} commandName - Command filename
* @returns {boolean} True if it's a genie command
*/
const isGenieCommand = (commandName) => {
const genieCommands = [
'wish.md'
];
return genieCommands.includes(commandName);
};
/**
* Check if agent needs updating (simplified version check)
* @param {string} agentPath - Path to agent file
* @returns {Promise<boolean>} True if needs update
*/
const needsAgentUpdate = async (agentPath) => {
try {
const content = await fs.readFile(agentPath, 'utf-8');
// Check for outdated patterns or missing features
// This is a simplified check - in production you'd want version headers
const hasModernFeatures = content.includes('mcp__search-repo-docs__') &&
content.includes('TodoWrite') &&
content.includes('Genie Hive');
return !hasModernFeatures;
} catch (error) {
return true; // If we can't read it, assume it needs update
}
};
/**
* Smart merge existing .claude content with new genie content
* @param {string} projectPath - Project path
* @param {Object} analysis - Analysis of existing content
* @param {Object} variables - Template variables
*/
const smartMergeClaude = async (projectPath, analysis, variables) => {
const claudeDir = path.join(projectPath, '.claude');
console.log('๐ Smart merging .claude directory...');
// Ensure base directories exist
const agentsDir = path.join(claudeDir, 'agents');
const hooksDir = path.join(claudeDir, 'hooks');
const commandsDir = path.join(claudeDir, 'commands');
await fs.mkdir(agentsDir, { recursive: true });
await fs.mkdir(hooksDir, { recursive: true });
await fs.mkdir(commandsDir, { recursive: true });
// 1. Handle agents - preserve user agents, update/create genie agents
await mergeAgents(agentsDir, analysis, variables);
// 2. Handle hooks - preserve user hooks, update genie hooks
await mergeHooks(hooksDir, analysis, variables);
// 3. Handle commands - preserve user commands, update genie commands
await mergeCommands(commandsDir, analysis, variables);
// 4. Copy statusline files
console.log('๐ง Updating statusline files...');
await copyStatuslineFiles(claudeDir);
// 5. Create minimal hooks if they don't exist
const minimalHookCommand = await createMinimalHooks(claudeDir);
// 6. Create settings.json if it doesn't exist
await createSettingsJson(claudeDir, minimalHookCommand);
// 7. Create/update version tracking
await createGenieVersion(claudeDir);
console.log('โ
Smart merge completed');
};
/**
* Merge agents directory
* @param {string} agentsDir - Agents directory path
* @param {Object} analysis - Content analysis
* @param {Object} variables - Template variables
*/
const mergeAgents = async (agentsDir, analysis, variables) => {
console.log('๐ค Merging agents...');
// User agents are preserved automatically (no action needed)
if (analysis.userAgents && analysis.userAgents.length > 0) {
console.log(` โ
Preserved ${analysis.userAgents.length} user agents: ${analysis.userAgents.join(', ')}`);
}
// Create/update genie agents
const genieAgentTypes = [
'analyzer', 'dev-planner', 'dev-designer', 'dev-coder', 'dev-fixer',
'agent-creator', 'agent-enhancer', 'claudemd', 'clone'
];
const newAgents = [];
const updatedAgents = [];
for (const agentType of genieAgentTypes) {
const agentFileName = `${variables.PROJECT_NAME}-${agentType}.md`;
const agentPath = path.join(agentsDir, agentFileName);
const exists = await fileExists(agentPath);
if (!exists) {
// Create new agent
const agentContent = generateAgentTemplate(agentType, variables.PROJECT_NAME);
await fs.writeFile(agentPath, agentContent, 'utf-8');
newAgents.push(agentFileName);
} else {
// Check if needs update
if (analysis.needsGenieUpdate || (analysis.genieAgent