Skip to main content
Glama

RAG Documentation MCP Server

repository-watcher.ts4.92 kB
import fs from 'fs'; import path from 'path'; import { glob } from 'glob'; import crypto from 'crypto'; import { RepositoryConfig } from '../types.js'; interface FileState { path: string; hash: string; lastModified: number; } export class RepositoryWatcher { private config: RepositoryConfig; private fileStates: Map<string, FileState> = new Map(); private watchInterval: NodeJS.Timeout | null = null; private onFileChanged: (changedFiles: string[], removedFiles: string[]) => Promise<void>; constructor( config: RepositoryConfig, onFileChanged: (changedFiles: string[], removedFiles: string[]) => Promise<void> ) { this.config = config; this.onFileChanged = onFileChanged; } /** * Start watching the repository for changes */ async start(): Promise<void> { // Initialize the file states await this.initializeFileStates(); // Start the watch interval this.watchInterval = setInterval( () => this.checkForChanges(), this.config.watchInterval ); console.log(`Started watching repository: ${this.config.name} (${this.config.path})`); } /** * Stop watching the repository */ stop(): void { if (this.watchInterval) { clearInterval(this.watchInterval); this.watchInterval = null; console.log(`Stopped watching repository: ${this.config.name}`); } } /** * Initialize the file states by scanning the repository */ private async initializeFileStates(): Promise<void> { const files = await glob(this.config.include, { cwd: this.config.path, ignore: this.config.exclude, absolute: true, nodir: true, }); for (const file of files) { try { const stats = fs.statSync(file); const content = fs.readFileSync(file, 'utf-8'); const hash = this.hashContent(content); this.fileStates.set(file, { path: file, hash, lastModified: stats.mtimeMs, }); } catch (error) { console.error(`Error initializing file state for ${file}:`, error); } } console.log(`Initialized file states for ${this.fileStates.size} files in repository: ${this.config.name}`); } /** * Check for changes in the repository */ private async checkForChanges(): Promise<void> { try { const currentFiles = await glob(this.config.include, { cwd: this.config.path, ignore: this.config.exclude, absolute: true, nodir: true, }); const currentFilePaths = new Set(currentFiles); const previousFilePaths = new Set(this.fileStates.keys()); // Find added or modified files const changedFiles: string[] = []; for (const file of currentFiles) { try { const stats = fs.statSync(file); const previousState = this.fileStates.get(file); // If the file is new or the modification time has changed if (!previousState || previousState.lastModified !== stats.mtimeMs) { const content = fs.readFileSync(file, 'utf-8'); const hash = this.hashContent(content); // If the file is new or the content has changed if (!previousState || previousState.hash !== hash) { changedFiles.push(file); // Update the file state this.fileStates.set(file, { path: file, hash, lastModified: stats.mtimeMs, }); } else if (previousState) { // Update just the modification time if only that changed this.fileStates.set(file, { ...previousState, lastModified: stats.mtimeMs, }); } } } catch (error) { console.error(`Error checking file ${file}:`, error); } } // Find removed files const removedFiles: string[] = []; for (const file of previousFilePaths) { if (!currentFilePaths.has(file)) { removedFiles.push(file); this.fileStates.delete(file); } } // If there are changes, notify the callback if (changedFiles.length > 0 || removedFiles.length > 0) { console.log(`Detected changes in repository ${this.config.name}:`); if (changedFiles.length > 0) { console.log(`- Changed files: ${changedFiles.length}`); } if (removedFiles.length > 0) { console.log(`- Removed files: ${removedFiles.length}`); } await this.onFileChanged(changedFiles, removedFiles); } } catch (error) { console.error(`Error checking for changes in repository ${this.config.name}:`, error); } } /** * Generate a hash of the file content */ private hashContent(content: string): string { return crypto.createHash('md5').update(content).digest('hex'); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rahulretnan/mcp-ragdocs'

If you have feedback or need assistance with the MCP directory API, please join our Discord server