code-context-mcp
Version:
MCP server for semantic code search powered by MongoDB Atlas Vector Search and Voyage AI embeddings
192 lines • 9.4 kB
JavaScript
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
export class SnapshotManager {
snapshotFilePath;
indexedCodebases = [];
indexingCodebases = new Map(); // Map of codebase path to progress percentage
constructor() {
// Initialize snapshot file path
this.snapshotFilePath = path.join(os.homedir(), '.context', 'mcp-codebase-snapshot.json');
}
getIndexedCodebases() {
// Read from JSON file to ensure consistency and persistence
try {
if (!fs.existsSync(this.snapshotFilePath)) {
return [];
}
const snapshotData = fs.readFileSync(this.snapshotFilePath, 'utf8');
const snapshot = JSON.parse(snapshotData);
return snapshot.indexedCodebases || [];
}
catch (error) {
console.warn(`[SNAPSHOT-DEBUG] Error reading indexed codebases from file:`, error);
// Fallback to memory if file reading fails
return [...this.indexedCodebases];
}
}
getIndexingCodebases() {
// Read from JSON file to ensure consistency and persistence
try {
if (!fs.existsSync(this.snapshotFilePath)) {
return [];
}
const snapshotData = fs.readFileSync(this.snapshotFilePath, 'utf8');
const snapshot = JSON.parse(snapshotData);
// Handle both legacy array format and new object format
if (Array.isArray(snapshot.indexingCodebases)) {
// Legacy format: return the array directly
return snapshot.indexingCodebases;
}
else if (snapshot.indexingCodebases && typeof snapshot.indexingCodebases === 'object') {
// New format: return the keys of the object
return Object.keys(snapshot.indexingCodebases);
}
return [];
}
catch (error) {
console.warn(`[SNAPSHOT-DEBUG] Error reading indexing codebases from file:`, error);
// Fallback to memory if file reading fails
return Array.from(this.indexingCodebases.keys());
}
}
getIndexingCodebasesWithProgress() {
return new Map(this.indexingCodebases);
}
getIndexingProgress(codebasePath) {
// Read from JSON file to ensure consistency and persistence
try {
if (!fs.existsSync(this.snapshotFilePath)) {
return undefined;
}
const snapshotData = fs.readFileSync(this.snapshotFilePath, 'utf8');
const snapshot = JSON.parse(snapshotData);
// Handle both legacy array format and new object format
if (Array.isArray(snapshot.indexingCodebases)) {
// Legacy format: if path exists in array, assume 0% progress
return snapshot.indexingCodebases.includes(codebasePath) ? 0 : undefined;
}
else if (snapshot.indexingCodebases && typeof snapshot.indexingCodebases === 'object') {
// New format: return the actual progress percentage
return snapshot.indexingCodebases[codebasePath];
}
return undefined;
}
catch (error) {
console.warn(`[SNAPSHOT-DEBUG] Error reading progress from file for ${codebasePath}:`, error);
// Fallback to memory if file reading fails
return this.indexingCodebases.get(codebasePath);
}
}
addIndexingCodebase(codebasePath, progress = 0) {
this.indexingCodebases.set(codebasePath, progress);
}
updateIndexingProgress(codebasePath, progress) {
if (this.indexingCodebases.has(codebasePath)) {
this.indexingCodebases.set(codebasePath, progress);
}
}
removeIndexingCodebase(codebasePath) {
this.indexingCodebases.delete(codebasePath);
}
addIndexedCodebase(codebasePath) {
if (!this.indexedCodebases.includes(codebasePath)) {
this.indexedCodebases.push(codebasePath);
}
}
removeIndexedCodebase(codebasePath) {
this.indexedCodebases = this.indexedCodebases.filter(path => path !== codebasePath);
}
moveFromIndexingToIndexed(codebasePath) {
this.removeIndexingCodebase(codebasePath);
this.addIndexedCodebase(codebasePath);
}
loadCodebaseSnapshot() {
console.log('[SNAPSHOT-DEBUG] Loading codebase snapshot from:', this.snapshotFilePath);
try {
if (!fs.existsSync(this.snapshotFilePath)) {
console.log('[SNAPSHOT-DEBUG] Snapshot file does not exist. Starting with empty codebase list.');
return;
}
const snapshotData = fs.readFileSync(this.snapshotFilePath, 'utf8');
const snapshot = JSON.parse(snapshotData);
console.log('[SNAPSHOT-DEBUG] Loaded snapshot:', snapshot);
// Validate that the codebases still exist
const validCodebases = [];
for (const codebasePath of snapshot.indexedCodebases) {
if (fs.existsSync(codebasePath)) {
validCodebases.push(codebasePath);
console.log(`[SNAPSHOT-DEBUG] Validated codebase: ${codebasePath}`);
}
else {
console.warn(`[SNAPSHOT-DEBUG] Codebase no longer exists, removing: ${codebasePath}`);
}
}
// Handle indexing codebases - treat them as not indexed since they were interrupted
// Support both legacy array format and new object format
let indexingCodebasesList = [];
if (Array.isArray(snapshot.indexingCodebases)) {
// Legacy format: string[]
indexingCodebasesList = snapshot.indexingCodebases;
console.log(`[SNAPSHOT-DEBUG] Found legacy indexingCodebases array format with ${indexingCodebasesList.length} entries`);
}
else if (snapshot.indexingCodebases && typeof snapshot.indexingCodebases === 'object') {
// New format: Record<string, number>
indexingCodebasesList = Object.keys(snapshot.indexingCodebases);
console.log(`[SNAPSHOT-DEBUG] Found new indexingCodebases object format with ${indexingCodebasesList.length} entries`);
}
for (const codebasePath of indexingCodebasesList) {
if (fs.existsSync(codebasePath)) {
console.warn(`[SNAPSHOT-DEBUG] Found interrupted indexing codebase: ${codebasePath}. Treating as not indexed.`);
// Don't add to validIndexingCodebases - treat as not indexed
}
else {
console.warn(`[SNAPSHOT-DEBUG] Interrupted indexing codebase no longer exists: ${codebasePath}`);
}
}
// Restore state - only fully indexed codebases
this.indexedCodebases = validCodebases;
this.indexingCodebases = new Map(); // Reset indexing codebases since they were interrupted
console.log(`[SNAPSHOT-DEBUG] Restored ${validCodebases.length} fully indexed codebases.`);
console.log(`[SNAPSHOT-DEBUG] Reset ${indexingCodebasesList.length} interrupted indexing codebases.`);
// Save updated snapshot if we removed any invalid paths or reset indexing codebases
const originalIndexingCount = Array.isArray(snapshot.indexingCodebases)
? snapshot.indexingCodebases.length
: Object.keys(snapshot.indexingCodebases || {}).length;
if (validCodebases.length !== snapshot.indexedCodebases.length || originalIndexingCount > 0) {
this.saveCodebaseSnapshot();
}
}
catch (error) {
console.error('[SNAPSHOT-DEBUG] Error loading snapshot:', error);
console.log('[SNAPSHOT-DEBUG] Starting with empty codebase list due to snapshot error.');
}
}
saveCodebaseSnapshot() {
console.log('[SNAPSHOT-DEBUG] Saving codebase snapshot to:', this.snapshotFilePath);
try {
// Ensure directory exists
const snapshotDir = path.dirname(this.snapshotFilePath);
if (!fs.existsSync(snapshotDir)) {
fs.mkdirSync(snapshotDir, { recursive: true });
console.log('[SNAPSHOT-DEBUG] Created snapshot directory:', snapshotDir);
}
// Convert Map to object for JSON serialization
const indexingCodebasesObject = {};
this.indexingCodebases.forEach((progress, path) => {
indexingCodebasesObject[path] = progress;
});
const snapshot = {
indexedCodebases: this.indexedCodebases,
indexingCodebases: indexingCodebasesObject,
lastUpdated: new Date().toISOString()
};
fs.writeFileSync(this.snapshotFilePath, JSON.stringify(snapshot, null, 2));
console.log('[SNAPSHOT-DEBUG] Snapshot saved successfully. Indexed codebases:', this.indexedCodebases.length, 'Indexing codebases:', this.indexingCodebases.size);
}
catch (error) {
console.error('[SNAPSHOT-DEBUG] Error saving snapshot:', error);
}
}
}
//# sourceMappingURL=snapshot.js.map