git-contextor
Version:
A code context tool with vector search and real-time monitoring, with optional Git integration.
145 lines (119 loc) • 5.37 kB
JavaScript
const ConfigManager = require('../../core/ConfigManager');
const logger =require('../utils/logger');
const simpleGit = require('simple-git');
// Import all necessary services
const Indexer = require('../../core/Indexer');
const VectorStore = require('../../core/VectorStore');
const MemoryVectorStore = require('../../core/MemoryVectorStore');
const ContextOptimizer = require('../../core/ContextOptimizer');
const FileWatcher = require('../../core/FileWatcher');
const apiServer = require('../../api/server');
const SharingService = require('../../core/SharingService');
async function mcp() {
const repoPath = process.cwd();
const configManager = new ConfigManager(repoPath);
try {
// Suppress regular info logs during startup for cleaner MCP stdio operation.
// Errors will still be logged to stderr.
const originalInfo = logger.info;
logger.info = (message, ...args) => logger.debug(`[MCP Startup] ${message}`, ...args);
await configManager.load();
const config = configManager.config;
// --- Service Initialization and Startup Logic (inspired by ServiceManager) ---
// 1. Environment validation
let isGitRepo = false;
try {
await simpleGit(repoPath).status();
isGitRepo = true;
logger.info('Git repository detected.');
} catch (error) {
logger.warn('No Git repository detected. Operating in non-Git mode.');
}
// 2. Initialize services
const vectorStoreProvider = config.vectorStore.provider;
const qdrantHost = config.vectorStore.qdrant?.host;
let vectorStore;
if (vectorStoreProvider === 'qdrant' || (vectorStoreProvider === 'auto' && qdrantHost)) {
vectorStore = new VectorStore(config);
} else {
vectorStore = new MemoryVectorStore(config);
}
const indexer = new Indexer(repoPath, vectorStore, config);
indexer.isGitRepo = isGitRepo;
const contextOptimizer = new ContextOptimizer(vectorStore, config);
const fileWatcher = new FileWatcher(repoPath, indexer, config);
fileWatcher.isGitRepo = isGitRepo;
const sharingService = new SharingService(repoPath, config);
await sharingService.init();
const services = { indexer, vectorStore, contextOptimizer, fileWatcher, sharingService };
// 3. Initial indexing logic
const isConfigValid = await vectorStore.validateCollectionConfig();
const vectorStoreStatus = await vectorStore.getStatus();
const needsReindex = !isConfigValid;
const needsInitialIndex = isConfigValid && (!vectorStoreStatus.vectorCount || vectorStoreStatus.vectorCount === 0);
if (needsReindex) {
logger.info('Configuration mismatch. Triggering a full re-index...');
await indexer.reindexAll();
} else if (needsInitialIndex) {
logger.info('Vector store is empty. Performing initial repository index...');
await indexer.reindexAll();
} else {
logger.info(`Found ${vectorStoreStatus.vectorCount} vectors. Skipping initial index.`);
}
// 4. Start file watcher if enabled
if (config.monitoring.watchEnabled) {
fileWatcher.start();
} else {
logger.info('File watcher is disabled by configuration.');
}
// 5. Start idle summary updater
let isUpdatingSummary = false;
let lastSummaryUpdateTime = null;
setInterval(async () => {
if (isUpdatingSummary) return;
const indexerStatus = await indexer.getStatus();
if (indexerStatus.status !== 'idle' || !indexerStatus.lastActivity) return;
const lastActivityTime = new Date(indexerStatus.lastActivity).getTime();
if (lastSummaryUpdateTime && lastActivityTime < lastSummaryUpdateTime) return;
const idleTime = Date.now() - lastActivityTime;
if (idleTime > 20000) { // 20 seconds idle
isUpdatingSummary = true;
logger.info('Indexer idle. Automatically updating collection summary...');
try {
await contextOptimizer.summarizeCollection();
lastSummaryUpdateTime = Date.now();
} catch (error) {
logger.error('Automatic collection summary update failed:', error);
} finally {
isUpdatingSummary = false;
}
}
}, 5000);
// --- API Server Start ---
const serviceManager = {
getStatus: async () => {
const indexerStatus = await indexer.getStatus();
const watcherStatusValue = config.monitoring.watchEnabled ? 'enabled' : 'disabled';
return {
status: 'running',
repository: config.repository,
watcher: { status: watcherStatusValue },
indexer: indexerStatus,
fileWatcher: { latestActivity: fileWatcher.getActivityLog() }
};
},
configManager // Make config manager available to API routes
};
// Restore original logger for the server's own messages
logger.info = originalInfo;
logger.info('Starting API server...');
// The API server promise will keep the process alive.
// The MCP stdio server is not started here, as this command now only runs the service.
await apiServer.start(config, services, serviceManager);
} catch (error) {
logger.error('Failed to start MCP server:', error.message);
logger.debug(error.stack);
process.exit(1);
}
}
module.exports = mcp;