vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
121 lines (120 loc) • 4.08 kB
JavaScript
import { TieredCache } from './tieredCache.js';
import logger from '../../../logger.js';
import crypto from 'crypto';
import fs from 'fs/promises';
import path from 'path';
export class MetadataCache {
cache;
name;
constructor(options) {
this.name = options.name;
this.cache = new TieredCache({
name: options.name,
cacheDir: options.cacheDir,
maxEntries: options.maxEntries || 10000,
maxAge: options.maxAge || 24 * 60 * 60 * 1000,
useMemoryCache: options.useMemoryCache !== false,
memoryMaxEntries: options.memoryMaxEntries || 5000,
memoryMaxAge: options.memoryMaxAge || 60 * 60 * 1000,
memoryThreshold: options.memoryThreshold || 0.5,
serialize: (metadata) => {
if (metadata && typeof metadata === 'object' && 'content' in metadata && metadata.content) {
const { content: _content, ...rest } = metadata;
void _content;
return JSON.stringify(rest);
}
return JSON.stringify(metadata);
},
deserialize: (serialized) => {
return JSON.parse(serialized);
}
});
}
async init() {
await this.cache.init();
logger.info(`Initialized ${this.name} metadata cache`);
}
async get(key) {
return this.cache.get(key);
}
async set(key, value) {
await this.cache.set(key, value);
}
async has(key) {
return this.cache.has(key);
}
async delete(key) {
await this.cache.delete(key);
}
async clear() {
await this.cache.clear();
}
getStats() {
return this.cache.getStats();
}
static async createSourceCodeMetadata(filePath, content, stats) {
if (!content) {
try {
content = await fs.readFile(filePath, 'utf-8');
}
catch (error) {
logger.error({ err: error, filePath }, 'Error reading file for metadata creation');
throw error;
}
}
if (!stats) {
try {
stats = await fs.stat(filePath);
}
catch (error) {
logger.error({ err: error, filePath }, 'Error getting file stats for metadata creation');
throw error;
}
}
const hash = crypto.createHash('md5').update(content).digest('hex');
return {
filePath,
hash,
size: stats.size,
lastModified: stats.mtimeMs,
language: path.extname(filePath).toLowerCase(),
processed: false,
content
};
}
static createASTMetadata(filePath, sourceHash, rootNode) {
return {
filePath,
sourceHash,
rootType: rootNode.type,
rootStartByte: rootNode.startByte,
rootEndByte: rootNode.endByte,
structure: MetadataCache.extractMinimalStructure(rootNode) || undefined
};
}
static extractMinimalStructure(node, options = {}) {
const maxDepth = options.maxDepth || 3;
const maxChildren = options.maxChildren || 10;
function extract(node, depth) {
if (!node || depth > maxDepth) {
return null;
}
const result = {
type: node.type,
startByte: node.startByte,
endByte: node.endByte
};
if (node.children && node.children.length > 0) {
result.children = node.children
.slice(0, maxChildren)
.map((child) => extract(child, depth + 1))
.filter((child) => child !== null);
if (node.children.length > maxChildren) {
result.childrenCount = node.children.length;
}
}
return result;
}
return extract(node, 0);
}
}