devibe
Version:
Intelligent repository cleanup with auto mode, AI learning, markdown consolidation, auto-consolidate workflow, context-aware classification, and cost optimization
274 lines • 9.92 kB
JavaScript
/**
* AI Learning Database
*
* Stores user corrections and learned patterns to improve
* AI classification accuracy over time.
*/
import * as fs from 'fs/promises';
import * as path from 'path';
import * as os from 'os';
export class AILearningDatabase {
dbPath;
data = {
corrections: [],
patterns: [],
version: '1.0.0',
};
constructor() {
const configDir = path.join(os.homedir(), '.devibe');
this.dbPath = path.join(configDir, 'ai-learning.json');
}
/**
* Load learning data from disk
*/
async load() {
try {
const content = await fs.readFile(this.dbPath, 'utf-8');
this.data = JSON.parse(content);
return this.data;
}
catch (error) {
// File doesn't exist, return empty data
this.data = {
corrections: [],
patterns: [],
version: '1.0.0',
};
return this.data;
}
}
/**
* Save learning data to disk
*/
async save() {
try {
const configDir = path.dirname(this.dbPath);
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(this.dbPath, JSON.stringify(this.data, null, 2), 'utf-8');
}
catch (error) {
console.error('Failed to save learning data:', error);
}
}
/**
* Add a user correction
*/
async addCorrection(correction) {
await this.load();
this.data.corrections.push(correction);
// Automatically learn patterns from this correction
await this.learnFromCorrection(correction);
await this.save();
}
/**
* Learn patterns from a correction
*/
async learnFromCorrection(correction) {
const { fileName, userCorrection, fileContent, imports } = correction;
// Pattern 1: File name patterns
const nameParts = fileName.split(/[-_.]/).filter(p => p.length > 2);
for (const part of nameParts) {
const existingPattern = this.data.patterns.find(p => p.pattern.includes(part) && p.rule.includes(userCorrection.category));
if (existingPattern) {
// Strengthen existing pattern
existingPattern.confidence = Math.min(0.95, existingPattern.confidence + 0.05);
existingPattern.usageCount++;
if (!existingPattern.examples.includes(fileName)) {
existingPattern.examples.push(fileName);
}
}
else {
// Create new pattern
this.data.patterns.push({
id: `pattern-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
pattern: `File name contains "${part}"`,
rule: `Likely category: ${userCorrection.category}, target: ${userCorrection.targetPath}`,
confidence: 0.6,
examples: [fileName],
createdAt: new Date().toISOString(),
usageCount: 1,
});
}
}
// Pattern 2: Import-based patterns
if (imports && imports.length > 0) {
const importPattern = imports.map(i => this.extractPackageName(i)).filter(Boolean);
if (importPattern.length > 0) {
const patternStr = importPattern.join(', ');
this.data.patterns.push({
id: `import-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
pattern: `Imports from: ${patternStr}`,
rule: `Should go to: ${userCorrection.targetPath}`,
confidence: 0.75,
examples: [fileName],
createdAt: new Date().toISOString(),
usageCount: 1,
});
}
}
// Pattern 3: Content-based patterns
if (fileContent) {
const keywords = this.extractKeywords(fileContent);
if (keywords.length > 0) {
const keywordPattern = keywords.slice(0, 3).join(', ');
this.data.patterns.push({
id: `content-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
pattern: `Contains keywords: ${keywordPattern}`,
rule: `Category: ${userCorrection.category}, Repository: ${userCorrection.repository || 'root'}`,
confidence: 0.7,
examples: [fileName],
createdAt: new Date().toISOString(),
usageCount: 1,
});
}
}
// Keep only top 100 patterns (most confident/used)
this.data.patterns.sort((a, b) => (b.confidence * b.usageCount) - (a.confidence * a.usageCount));
if (this.data.patterns.length > 100) {
this.data.patterns = this.data.patterns.slice(0, 100);
}
}
/**
* Extract package name from import statement
*/
extractPackageName(importStr) {
// Handle various import formats
const patterns = [
/from ['"](@[\w-]+\/[\w-]+)['"]/, // @scope/package
/from ['"]([^'".\/][^'"]+)['"]/, // package-name
/require\(['"](@[\w-]+\/[\w-]+)['"]\)/, // require @scope
/require\(['"]([^'".\/][^'"]+)['"]\)/, // require package
];
for (const pattern of patterns) {
const match = importStr.match(pattern);
if (match)
return match[1];
}
return null;
}
/**
* Extract significant keywords from content
*/
extractKeywords(content) {
const keywords = new Set();
const significantWords = [
'api', 'server', 'client', 'ios', 'android', 'web', 'mobile',
'test', 'spec', 'mock', 'fixture', 'database', 'migration',
'component', 'service', 'controller', 'model', 'view',
'auth', 'authentication', 'authorization', 'login', 'user',
];
const lowercaseContent = content.toLowerCase();
for (const word of significantWords) {
if (lowercaseContent.includes(word)) {
keywords.add(word);
}
}
return Array.from(keywords);
}
/**
* Get learned patterns for a file
*/
async getPatternsForFile(fileName, content, imports) {
await this.load();
const matches = [];
for (const pattern of this.data.patterns) {
let score = 0;
// Check file name match
const nameParts = fileName.split(/[-_.]/).filter(p => p.length > 2);
for (const part of nameParts) {
if (pattern.pattern.toLowerCase().includes(part.toLowerCase())) {
score += pattern.confidence * 0.5;
}
}
// Check import match
if (imports && pattern.pattern.includes('Imports from:')) {
for (const imp of imports) {
const pkg = this.extractPackageName(imp);
if (pkg && pattern.pattern.includes(pkg)) {
score += pattern.confidence * 0.8;
}
}
}
// Check content keywords
if (content && pattern.pattern.includes('Contains keywords:')) {
const keywords = this.extractKeywords(content);
for (const keyword of keywords) {
if (pattern.pattern.toLowerCase().includes(keyword)) {
score += pattern.confidence * 0.3;
}
}
}
if (score > 0) {
matches.push({ pattern, score });
}
}
// Sort by score and return top matches
matches.sort((a, b) => b.score - a.score);
return matches.slice(0, 5).map(m => m.pattern);
}
/**
* Store project structure analysis
*/
async storeProjectStructure(structure) {
await this.load();
this.data.projectStructure = structure;
await this.save();
}
/**
* Get stored project structure
*/
async getProjectStructure() {
await this.load();
return this.data.projectStructure;
}
/**
* Get all corrections
*/
async getCorrections() {
await this.load();
return this.data.corrections;
}
/**
* Get statistics
*/
async getStats() {
await this.load();
const categoryCount = new Map();
for (const correction of this.data.corrections) {
const cat = correction.userCorrection.category;
categoryCount.set(cat, (categoryCount.get(cat) || 0) + 1);
}
const mostCommon = Array.from(categoryCount.entries())
.sort((a, b) => b[1] - a[1])[0];
const avgConfidence = this.data.patterns.length > 0
? this.data.patterns.reduce((sum, p) => sum + p.confidence, 0) / this.data.patterns.length
: 0;
return {
totalCorrections: this.data.corrections.length,
totalPatterns: this.data.patterns.length,
hasProjectStructure: !!this.data.projectStructure,
mostCommonCategory: mostCommon ? mostCommon[0] : null,
avgConfidence,
};
}
/**
* Clear all learning data (for testing or reset)
*/
async clear() {
this.data = {
corrections: [],
patterns: [],
version: '1.0.0',
};
await this.save();
}
}
// Singleton instance
let instance = null;
export function getLearningDatabase() {
if (!instance) {
instance = new AILearningDatabase();
}
return instance;
}
//# sourceMappingURL=ai-learning-database.js.map