mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
386 lines • 14.2 kB
JavaScript
/**
* Context Analyzer - Intelligent detection of current development context
* Enables context-aware command routing and smart defaults
*/
import { extname } from 'path';
import { spawn } from 'child_process';
export class MCPContext {
session_id;
project_path;
current_files = [];
recent_activity = [];
time_of_day = 'unknown';
last_activity;
project_state = 'unknown';
user_patterns = {};
is_debugging = false;
is_learning = false;
claude_context = {};
constructor(data = {}) {
Object.assign(this, data);
}
toMCPContext() {
return {
session_id: this.session_id,
project_path: this.project_path,
current_files: this.current_files,
recent_activity: this.recent_activity,
time_of_day: this.time_of_day,
last_activity: this.last_activity,
project_state: this.project_state,
user_patterns: this.user_patterns,
is_debugging: this.is_debugging,
is_learning: this.is_learning,
claude_context: this.claude_context
};
}
}
export class ContextAnalyzer {
cacheTimeout = 30000; // 30 seconds
contextCache = null;
/**
* Detect current development context with caching
*/
async detectContext() {
// Check cache
if (this.contextCache && (Date.now() - this.contextCache.timestamp) < this.cacheTimeout) {
return this.contextCache.context;
}
const context = new MCPContext();
// Detect basic context
await this.detectBasicContext(context);
// Detect time context
this.detectTimeContext(context);
// Detect project state
await this.detectProjectState(context);
// Detect session type
await this.detectSessionType(context);
// Detect recent activity
await this.detectRecentActivity(context);
// Cache the result
this.contextCache = {
context,
timestamp: Date.now()
};
return context;
}
async detectBasicContext(context) {
try {
// Generate session ID
context.session_id = `mira-session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// Detect project path
context.project_path = process.cwd();
// Detect current files from git status
const currentFiles = await this.getCurrentFiles();
context.current_files = currentFiles;
}
catch (error) {
// Silent failure, use defaults
}
}
detectTimeContext(context) {
const now = new Date();
const hour = now.getHours();
if (hour >= 5 && hour < 12) {
context.time_of_day = 'morning';
}
else if (hour >= 12 && hour < 17) {
context.time_of_day = 'afternoon';
}
else if (hour >= 17 && hour < 22) {
context.time_of_day = 'evening';
}
else {
context.time_of_day = 'night';
}
}
async detectProjectState(context) {
try {
const fileTypes = context.current_files.map(file => extname(file).toLowerCase());
// Analyze file types to determine project state
if (this.hasTestFiles(fileTypes)) {
context.project_state = 'testing';
}
else if (this.hasDocumentationFiles(fileTypes)) {
context.project_state = 'documentation';
}
else if (this.hasSourceFiles(fileTypes)) {
context.project_state = 'active_development';
}
else if (this.hasConfigFiles(fileTypes)) {
context.project_state = 'configuration';
}
else {
context.project_state = 'maintenance';
}
}
catch (error) {
context.project_state = 'unknown';
}
}
async detectSessionType(context) {
try {
// Get recent git commits for context clues
const recentCommits = await this.getRecentCommits();
const commitMessages = recentCommits.join(' ').toLowerCase();
// Detect debugging session
const debugKeywords = ['debug', 'fix', 'bug', 'error', 'issue', 'crash', 'exception'];
context.is_debugging = debugKeywords.some(keyword => commitMessages.includes(keyword));
// Detect learning session
const learningKeywords = ['learn', 'understand', 'explore', 'research', 'study', 'experiment'];
context.is_learning = learningKeywords.some(keyword => commitMessages.includes(keyword));
// Also check current files for context
const currentFileContent = context.current_files.join(' ').toLowerCase();
if (!context.is_debugging) {
context.is_debugging = debugKeywords.some(keyword => currentFileContent.includes(keyword));
}
if (!context.is_learning) {
context.is_learning = learningKeywords.some(keyword => currentFileContent.includes(keyword));
}
}
catch (error) {
// Silent failure, keep defaults
}
}
async detectRecentActivity(context) {
try {
// Get recent git activity
const recentCommits = await this.getRecentCommits(5);
const recentBranches = await this.getRecentBranches();
context.recent_activity = [
...recentCommits.map(commit => `commit: ${commit}`),
...recentBranches.map(branch => `branch: ${branch}`)
];
// Get last activity timestamp
const lastCommitTime = await this.getLastCommitTime();
if (lastCommitTime) {
context.last_activity = lastCommitTime;
}
}
catch (error) {
// Silent failure
}
}
// Helper methods for file type detection
hasTestFiles(fileTypes) {
const testExtensions = ['.test.ts', '.test.js', '.spec.ts', '.spec.js', '.test.py', '.spec.py'];
return this.contextCache?.context.current_files.some(file => testExtensions.some(ext => file.toLowerCase().includes(ext))) || false;
}
hasDocumentationFiles(fileTypes) {
const docExtensions = ['.md', '.rst', '.txt', '.doc', '.docx'];
return fileTypes.some(ext => docExtensions.includes(ext));
}
hasSourceFiles(fileTypes) {
const sourceExtensions = ['.ts', '.js', '.py', '.go', '.rs', '.java', '.cpp', '.c', '.cs'];
return fileTypes.some(ext => sourceExtensions.includes(ext));
}
hasConfigFiles(fileTypes) {
const configExtensions = ['.json', '.yaml', '.yml', '.toml', '.ini', '.conf'];
const configFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml'];
return fileTypes.some(ext => configExtensions.includes(ext)) ||
(this.contextCache?.context.current_files.some(file => configFiles.some(cfg => file.toLowerCase().includes(cfg))) || false);
}
// Git integration methods
async getCurrentFiles() {
return new Promise((resolve) => {
const git = spawn('git', ['status', '--porcelain'], { cwd: process.cwd() });
let output = '';
let resolved = false;
// Add timeout to prevent hanging
const timeout = setTimeout(() => {
if (!resolved) {
resolved = true;
git.kill('SIGTERM');
resolve([]);
}
}, 5000); // 5 second timeout
git.stdout.on('data', (data) => {
output += data.toString();
});
git.on('close', (code) => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
if (code === 0) {
const files = output
.split('\n')
.filter(line => line.trim())
.map(line => line.substring(3)) // Remove git status prefix
.filter(file => file.trim());
resolve(files);
}
else {
resolve([]);
}
}
});
git.on('error', () => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
resolve([]);
}
});
});
}
async getRecentCommits(limit = 10) {
return new Promise((resolve) => {
const git = spawn('git', ['log', '--oneline', `-${limit}`], { cwd: process.cwd() });
let output = '';
let resolved = false;
// Add timeout to prevent hanging
const timeout = setTimeout(() => {
if (!resolved) {
resolved = true;
git.kill('SIGTERM');
resolve([]);
}
}, 5000); // 5 second timeout
git.stdout.on('data', (data) => {
output += data.toString();
});
git.on('close', (code) => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
if (code === 0) {
const commits = output
.split('\n')
.filter(line => line.trim())
.map(line => line.substring(8)) // Remove commit hash
.filter(msg => msg.trim());
resolve(commits);
}
else {
resolve([]);
}
}
});
git.on('error', () => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
resolve([]);
}
});
});
}
async getRecentBranches() {
return new Promise((resolve) => {
const git = spawn('git', ['branch', '-a', '--sort=-committerdate'], { cwd: process.cwd() });
let output = '';
let resolved = false;
// Add timeout to prevent hanging - this command can be slow with many remote branches
const timeout = setTimeout(() => {
if (!resolved) {
resolved = true;
git.kill('SIGTERM');
resolve([]);
}
}, 3000); // 3 second timeout for branch listing
git.stdout.on('data', (data) => {
output += data.toString();
});
git.on('close', (code) => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
if (code === 0) {
const branches = output
.split('\n')
.filter(line => line.trim())
.map(line => line.replace(/^\*?\s*/, '').trim()) // Remove * and whitespace
.filter(branch => branch && !branch.startsWith('remotes/'))
.slice(0, 5); // Top 5 recent branches
resolve(branches);
}
else {
resolve([]);
}
}
});
git.on('error', () => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
resolve([]);
}
});
});
}
async getLastCommitTime() {
return new Promise((resolve) => {
const git = spawn('git', ['log', '-1', '--format=%cI'], { cwd: process.cwd() });
let output = '';
let resolved = false;
// Add timeout to prevent hanging
const timeout = setTimeout(() => {
if (!resolved) {
resolved = true;
git.kill('SIGTERM');
resolve(null);
}
}, 5000); // 5 second timeout
git.stdout.on('data', (data) => {
output += data.toString();
});
git.on('close', (code) => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
if (code === 0 && output.trim()) {
resolve(output.trim());
}
else {
resolve(null);
}
}
});
git.on('error', () => {
if (!resolved) {
resolved = true;
clearTimeout(timeout);
resolve(null);
}
});
});
}
/**
* Clear the context cache to force refresh
*/
clearCache() {
this.contextCache = null;
}
/**
* Update user patterns based on usage
*/
updateUserPatterns(patterns) {
if (this.contextCache) {
this.contextCache.context.user_patterns = {
...this.contextCache.context.user_patterns,
...patterns
};
}
}
}
// Global instance for easy access
const globalContextAnalyzer = new ContextAnalyzer();
/**
* Convenience function to detect current context
*/
export async function detectCurrentContext() {
return globalContextAnalyzer.detectContext();
}
/**
* Clear global context cache
*/
export function clearContextCache() {
globalContextAnalyzer.clearCache();
}
/**
* Update global user patterns
*/
export function updateGlobalUserPatterns(patterns) {
globalContextAnalyzer.updateUserPatterns(patterns);
}
//# sourceMappingURL=context-analyzer.js.map