Skip to main content
Glama

AI Code Toolkit

by AgiFlow
ProjectFinderService.ts4.78 kB
/** * ProjectFinderService * * DESIGN PATTERNS: * - Class-based service pattern for encapsulating business logic * - Caching for performance optimization * - File system traversal for project detection * * CODING STANDARDS: * - Service class names use PascalCase with 'Service' suffix * - Method names use camelCase with descriptive verbs * - Return types should be explicit * - Use async/await for asynchronous operations * - Handle errors with try-catch and throw descriptive Error objects * * AVOID: * - Side effects in constructors * - Mixing concerns * - Direct coupling to other services */ import path from 'node:path'; import * as fs from 'fs-extra'; import type { ProjectConfig } from '../types'; export class ProjectFinderService { private projectCache: Map<string, ProjectConfig> = new Map(); private workspaceRoot: string; constructor(workspaceRoot?: string) { this.workspaceRoot = workspaceRoot || process.cwd(); } /** * Find the project containing a given file by walking up the directory tree * * @param filePath - Absolute path to the file * @returns Project configuration or null if not found */ async findProjectForFile(filePath: string): Promise<ProjectConfig | null> { // Normalize the file path const normalizedPath = path.isAbsolute(filePath) ? filePath : path.join(this.workspaceRoot, filePath); // Start from the file's directory and walk up let currentDir = path.dirname(normalizedPath); while (currentDir !== '/' && currentDir.startsWith(this.workspaceRoot)) { const projectJsonPath = path.join(currentDir, 'project.json'); try { const project = await this.loadProjectConfig(projectJsonPath); if (project) { return project; } } catch { // No project.json in this directory, continue searching } currentDir = path.dirname(currentDir); } return null; } /** * Find the project containing a given file (synchronous version) * * @param filePath - Absolute path to the file * @returns Project configuration or null if not found */ findProjectForFileSync(filePath: string): ProjectConfig | null { // Normalize the file path const normalizedPath = path.isAbsolute(filePath) ? filePath : path.join(this.workspaceRoot, filePath); // Start from the file's directory and walk up let currentDir = path.dirname(normalizedPath); while (currentDir !== '/' && currentDir.startsWith(this.workspaceRoot)) { const projectJsonPath = path.join(currentDir, 'project.json'); try { const project = this.loadProjectConfigSync(projectJsonPath); if (project) { return project; } } catch { // No project.json in this directory, continue searching } currentDir = path.dirname(currentDir); } return null; } /** * Load and parse a project.json file */ private async loadProjectConfig(projectJsonPath: string): Promise<ProjectConfig | null> { // Check cache first if (this.projectCache.has(projectJsonPath)) { return this.projectCache.get(projectJsonPath)!; } try { const content = await fs.readFile(projectJsonPath, 'utf-8'); const config = JSON.parse(content); const projectConfig: ProjectConfig = { name: config.name || path.basename(path.dirname(projectJsonPath)), root: path.dirname(projectJsonPath), sourceTemplate: config.sourceTemplate, projectType: config.projectType, }; // Cache the result this.projectCache.set(projectJsonPath, projectConfig); return projectConfig; } catch { return null; } } /** * Load and parse a project.json file (synchronous version) */ private loadProjectConfigSync(projectJsonPath: string): ProjectConfig | null { // Check cache first if (this.projectCache.has(projectJsonPath)) { return this.projectCache.get(projectJsonPath)!; } try { const content = fs.readFileSync(projectJsonPath, 'utf-8'); const config = JSON.parse(content); const projectConfig: ProjectConfig = { name: config.name || path.basename(path.dirname(projectJsonPath)), root: path.dirname(projectJsonPath), sourceTemplate: config.sourceTemplate, projectType: config.projectType, }; // Cache the result this.projectCache.set(projectJsonPath, projectConfig); return projectConfig; } catch { return null; } } /** * Get the workspace root */ getWorkspaceRoot(): string { return this.workspaceRoot; } /** * Clear the project cache */ clearCache(): void { this.projectCache.clear(); } }

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/AgiFlow/aicode-toolkit'

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