mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
311 lines (295 loc) • 12.1 kB
JavaScript
/**
* 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 `
**🔴 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
**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(/^
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