UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

311 lines (295 loc) 12.1 kB
/** * Claude File Injector * =================== * * Handles automatic injection and management of MIRA integration sections * in CLAUDE.md files for distribution and setup. * * Features: * - Detects existing CLAUDE.md files * - Injects minimal MIRA integration section * - Preserves user content * - Updates existing MIRA sections * - Uses Claude Code SDK for intelligent modification */ import { promises as fs } from 'fs'; import { join, dirname } from 'path'; import { existsSync } from 'fs'; export class ClaudeFileInjector { static MIRA_SECTION_MARKER = '## 🚨 MIRA Integration - CRITICAL FIRST ACTION'; static MIRA_END_MARKER = '---'; /** * Inject MIRA integration into CLAUDE.md file */ async injectMIRASection(targetPath, config = {}) { try { const claudeFilePath = join(targetPath, 'CLAUDE.md'); const exists = existsSync(claudeFilePath); if (exists) { return await this.updateExistingClaudeFile(claudeFilePath, config); } else { return await this.createNewClaudeFile(claudeFilePath, config); } } catch (error) { return { success: false, message: `Failed to inject MIRA section: ${error instanceof Error ? error.message : 'Unknown error'}`, created: false }; } } /** * Create new CLAUDE.md with MIRA integration */ async createNewClaudeFile(filePath, config) { const template = await this.generateMIRATemplate(config); await fs.writeFile(filePath, template, 'utf8'); return { success: true, message: 'Created new CLAUDE.md with MIRA integration', created: true }; } /** * Update existing CLAUDE.md with MIRA integration */ async updateExistingClaudeFile(filePath, config) { const content = await fs.readFile(filePath, 'utf8'); // Check if MIRA section already exists if (this.hasMIRASection(content)) { if (!config.forceUpdate) { return { success: true, message: 'MIRA section already exists in CLAUDE.md', created: false }; } // Replace existing MIRA section const updatedContent = await this.replaceMIRASection(content, config); await fs.writeFile(filePath, updatedContent, 'utf8'); return { success: true, message: 'Updated existing MIRA section in CLAUDE.md', created: false }; } else { // Inject MIRA section at the top const injectedContent = await this.injectMIRASectionAtTop(content, config); await fs.writeFile(filePath, injectedContent, 'utf8'); return { success: true, message: 'Injected MIRA section into existing CLAUDE.md', created: false }; } } /** * Check if CLAUDE.md already has MIRA section */ hasMIRASection(content) { return content.includes(ClaudeFileInjector.MIRA_SECTION_MARKER); } /** * Replace existing MIRA section with updated version */ async replaceMIRASection(content, config) { const startIndex = content.indexOf(ClaudeFileInjector.MIRA_SECTION_MARKER); if (startIndex === -1) return content; // Find the end of MIRA section (first --- after MIRA section) const searchFrom = startIndex + ClaudeFileInjector.MIRA_SECTION_MARKER.length; const endMarkerIndex = content.indexOf(ClaudeFileInjector.MIRA_END_MARKER, searchFrom); if (endMarkerIndex === -1) { // No end marker found, replace until end of file const beforeMIRA = content.substring(0, startIndex); const miraTemplate = await this.generateMIRATemplate(config); return beforeMIRA + miraTemplate; } else { // Replace section between markers const beforeMIRA = content.substring(0, startIndex); const afterMIRA = content.substring(endMarkerIndex + ClaudeFileInjector.MIRA_END_MARKER.length); const miraTemplate = await this.generateMIRATemplate(config); return beforeMIRA + miraTemplate + '\n' + afterMIRA; } } /** * Inject MIRA section at the top of existing content */ async injectMIRASectionAtTop(content, config) { const lines = content.split('\n'); const titleLineIndex = lines.findIndex(line => line.startsWith('# ')); const miraTemplate = await this.generateMIRATemplate(config); if (titleLineIndex !== -1) { // Insert after title const beforeTitle = lines.slice(0, titleLineIndex + 1); const afterTitle = lines.slice(titleLineIndex + 1); return [ ...beforeTitle, '', miraTemplate, '', ...afterTitle ].join('\n'); } else { // No title found, prepend to content return miraTemplate + '\n\n' + content; } } /** * Generate MIRA integration template */ async generateMIRATemplate(config) { const projectName = config.projectName || 'Your Project'; return `## 🚨 MIRA Integration - CRITICAL FIRST ACTION **🔴 EXECUTE THIS COMMAND RIGHT NOW BEFORE DOING ANYTHING ELSE:** \`\`\`bash mira startup \`\`\` **⛔ DO NOT PROCEED WITHOUT RUNNING \`mira startup\` FIRST! ⛔** This command: - **LOADS YOUR MEMORY**: Initializes the MIRA memory system with full cognitive continuity - **BACKGROUNDS DAEMON**: Starts background intelligence services - **RESTORES CONTEXT**: Displays memory highlights and recent development context - **PROVIDES GUIDANCE**: Shows available analytical perspectives and commands - **ENSURES CONTINUITY**: Makes all context from previous sessions accessible ## 🔧 MIRA MCP Functions (Primary Interface) **After running \`mira startup\`, use these MCP functions directly:** \`\`\`javascript // Memory Operations mcp__mira__mira_smart_search({query: "your search"}) mcp__mira__mira_store_memory({content: "insight", memory_type: "general"}) mcp__mira__mira_search_memories({query: "recent decisions", limit: 10}) // Intelligence & Analysis mcp__mira__mira_analyze_behavior({message: "current context"}) mcp__mira__mira_analyze_work_context({hours_back: 24}) mcp__mira__mira_system_status({include_metrics: true}) // Code Analysis mcp__mira__mira_search_codebase({query: "authentication functions"}) mcp__mira__mira_analyze_code({analyzer_type: "comprehensive"}) // Private Memory (Claude-only) mcp__mira__mira_store_private_memory({content: "private insight"}) mcp__mira__mira_recall_private_memory({query: "recent thoughts"}) \`\`\` **📚 Complete Function Reference**: Use \`mira help mcp\` for full documentation **🔄 Fallback**: If MCP fails, use CLI commands: \`mira search\`, \`mira store\`, etc. ---`; } /** * Use Claude Code SDK to intelligently modify CLAUDE.md * This method uses the Claude CLI to make intelligent modifications */ async injectViaClaudeCode(targetPath, config = {}) { try { const claudeFilePath = join(targetPath, 'CLAUDE.md'); // Check if Claude Code CLI is available const { spawn } = await import('child_process'); const prompt = this.generateClaudeCodePrompt(config); return new Promise((resolve) => { const process = spawn('claude', ['modify', claudeFilePath], { stdio: ['pipe', 'pipe', 'pipe'] }); let output = ''; let error = ''; process.stdout.on('data', (data) => { output += data.toString(); }); process.stderr.on('data', (data) => { error += data.toString(); }); process.on('close', (code) => { if (code === 0) { resolve({ success: true, message: 'Successfully modified CLAUDE.md via Claude Code' }); } else { resolve({ success: false, message: `Claude Code modification failed: ${error}` }); } }); // Send the prompt to Claude Code process.stdin.write(prompt); process.stdin.end(); }); } catch (error) { return { success: false, message: `Claude Code SDK unavailable: ${error instanceof Error ? error.message : 'Unknown error'}` }; } } /** * Generate detailed prompt for Claude Code modification */ generateClaudeCodePrompt(config) { return `Please inject the following MIRA integration section at the very top of this CLAUDE.md file, right after the main title: ${this.generateMIRATemplate(config)} Rules: 1. If a MIRA integration section already exists (look for "## 🚨 MIRA Integration"), replace it entirely 2. If no MIRA section exists, add it as the first section after the main title (# ...) 3. Preserve all other content exactly as-is 4. Maintain proper markdown formatting 5. Ensure there's proper spacing between sections The MIRA section should be the very first thing users see after the title, as it's critical for proper system initialization.`; } /** * Auto-detect project configuration for better templating */ async detectProjectConfig(targetPath) { const config = {}; try { // Try to read package.json const packageJsonPath = join(targetPath, 'package.json'); if (existsSync(packageJsonPath)) { const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); config.projectName = packageJson.name || 'Unknown Project'; config.projectType = 'Node.js Application'; // Detect primary language from dependencies if (packageJson.dependencies || packageJson.devDependencies) { const deps = { ...packageJson.dependencies, ...packageJson.devDependencies }; if (deps.typescript || deps['@types/node']) { config.primaryLanguage = 'TypeScript'; } else if (deps.react) { config.primaryLanguage = 'JavaScript (React)'; } else { config.primaryLanguage = 'JavaScript'; } } } // Try to read existing CLAUDE.md for project name const claudeFilePath = join(targetPath, 'CLAUDE.md'); if (existsSync(claudeFilePath)) { const content = await fs.readFile(claudeFilePath, 'utf8'); const titleMatch = content.match(/^# (.+)$/m); if (titleMatch) { config.projectName = titleMatch[1]; } } // Default fallback if (!config.projectName) { const dirName = dirname(targetPath).split('/').pop() || 'Unknown Project'; config.projectName = dirName; } } catch (error) { // Use defaults if detection fails config.projectName = 'Your Project'; config.projectType = 'General Project'; config.primaryLanguage = 'Not detected'; } return config; } } export default ClaudeFileInjector; //# sourceMappingURL=ClaudeFileInjector.js.map