Skip to main content
Glama

AI Code Toolkit

by AgiFlow
TemplatesManagerService.ts10.3 kB
/** * TemplatesManagerService * * DESIGN PATTERNS: * - Class-based service pattern for encapsulating business logic * - Static methods for utility-like functionality * - File system traversal for workspace detection * - Configuration-driven template path resolution * * CODING STANDARDS: * - Service class names use PascalCase with 'Service' suffix * - Method names use camelCase with descriptive verbs * - Return types should be explicit (never use implicit any) * - Use async/await for asynchronous operations * - Handle errors with try-catch and throw descriptive Error objects * - Document public methods with JSDoc comments * * AVOID: * - Side effects in constructors (keep them lightweight) * - Mixing concerns (keep services focused on single domain) * - Direct coupling to other services (use dependency injection) * - Exposing internal implementation details */ import path from 'node:path'; import * as fs from 'fs-extra'; import type { ToolkitConfig } from '../types'; export class TemplatesManagerService { private static SCAFFOLD_CONFIG_FILE = 'scaffold.yaml'; private static TEMPLATES_FOLDER = 'templates'; private static TOOLKIT_CONFIG_FILE = 'toolkit.yaml'; /** * Find the templates directory by searching upwards from the starting path. * * Algorithm: * 1. Start from the provided path (default: current working directory) * 2. Search upwards to find the workspace root (where .git exists or filesystem root) * 3. Check if toolkit.yaml exists at workspace root * - If yes, read templatesPath from toolkit.yaml * - If no, default to 'templates' folder in workspace root * 4. Verify the templates directory exists * * @param startPath - The path to start searching from (defaults to process.cwd()) * @returns The absolute path to the templates directory * @throws Error if templates directory is not found */ static async findTemplatesPath(startPath: string = process.cwd()): Promise<string> { // First, find the workspace root const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath); // Check if toolkit.yaml exists const toolkitConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE); if (await fs.pathExists(toolkitConfigPath)) { // Read toolkit.yaml to get templatesPath const yaml = await import('js-yaml'); const content = await fs.readFile(toolkitConfigPath, 'utf-8'); const config = yaml.load(content) as any; if (config?.templatesPath) { const templatesPath = path.isAbsolute(config.templatesPath) ? config.templatesPath : path.join(workspaceRoot, config.templatesPath); if (await fs.pathExists(templatesPath)) { return templatesPath; } else { throw new Error( `Templates path specified in toolkit.yaml does not exist: ${templatesPath}`, ); } } } // Default to templates folder in workspace root const templatesPath = path.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER); if (await fs.pathExists(templatesPath)) { return templatesPath; } throw new Error( `Templates folder not found at ${templatesPath}.\n` + `Either create a 'templates' folder or specify templatesPath in toolkit.yaml`, ); } /** * Find the workspace root by searching upwards for .git folder */ private static async findWorkspaceRoot(startPath: string): Promise<string> { let currentPath = path.resolve(startPath); const rootPath = path.parse(currentPath).root; while (true) { // Check if .git folder exists (repository root) const gitPath = path.join(currentPath, '.git'); if (await fs.pathExists(gitPath)) { return currentPath; } // Check if we've reached the filesystem root if (currentPath === rootPath) { // No .git found, return current working directory as workspace root return process.cwd(); } // Move up to parent directory currentPath = path.dirname(currentPath); } } /** * Get the templates path synchronously. * Use this when you need immediate access and are sure templates exist. * * @param startPath - The path to start searching from (defaults to process.cwd()) * @returns The absolute path to the templates directory * @throws Error if templates directory is not found */ static findTemplatesPathSync(startPath: string = process.cwd()): string { // First, find the workspace root const workspaceRoot = TemplatesManagerService.findWorkspaceRootSync(startPath); // Check if toolkit.yaml exists const toolkitConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE); if (fs.pathExistsSync(toolkitConfigPath)) { // Read toolkit.yaml to get templatesPath const yaml = require('js-yaml'); const content = fs.readFileSync(toolkitConfigPath, 'utf-8'); const config = yaml.load(content) as any; if (config?.templatesPath) { const templatesPath = path.isAbsolute(config.templatesPath) ? config.templatesPath : path.join(workspaceRoot, config.templatesPath); if (fs.pathExistsSync(templatesPath)) { return templatesPath; } else { throw new Error( `Templates path specified in toolkit.yaml does not exist: ${templatesPath}`, ); } } } // Default to templates folder in workspace root const templatesPath = path.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER); if (fs.pathExistsSync(templatesPath)) { return templatesPath; } throw new Error( `Templates folder not found at ${templatesPath}.\n` + `Either create a 'templates' folder or specify templatesPath in toolkit.yaml`, ); } /** * Find the workspace root synchronously by searching upwards for .git folder */ private static findWorkspaceRootSync(startPath: string): string { let currentPath = path.resolve(startPath); const rootPath = path.parse(currentPath).root; while (true) { // Check if .git folder exists (repository root) const gitPath = path.join(currentPath, '.git'); if (fs.pathExistsSync(gitPath)) { return currentPath; } // Check if we've reached the filesystem root if (currentPath === rootPath) { // No .git found, return current working directory as workspace root return process.cwd(); } // Move up to parent directory currentPath = path.dirname(currentPath); } } /** * Check if templates are initialized at the given path * * @param templatesPath - Path to check for templates * @returns true if templates folder exists and is a directory */ static async isInitialized(templatesPath: string): Promise<boolean> { if (!(await fs.pathExists(templatesPath))) { return false; } const stat = await fs.stat(templatesPath); return stat.isDirectory(); } /** * Get the scaffold config file name */ static getConfigFileName(): string { return TemplatesManagerService.SCAFFOLD_CONFIG_FILE; } /** * Get the templates folder name */ static getTemplatesFolderName(): string { return TemplatesManagerService.TEMPLATES_FOLDER; } /** * Read toolkit.yaml configuration from workspace root * * @param startPath - The path to start searching from (defaults to process.cwd()) * @returns The toolkit configuration object or null if not found */ static async readToolkitConfig(startPath: string = process.cwd()): Promise<ToolkitConfig | null> { const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath); const toolkitConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE); if (!(await fs.pathExists(toolkitConfigPath))) { return null; } const yaml = await import('js-yaml'); const content = await fs.readFile(toolkitConfigPath, 'utf-8'); const config = yaml.load(content) as ToolkitConfig; return config; } /** * Read toolkit.yaml configuration from workspace root (sync) * * @param startPath - The path to start searching from (defaults to process.cwd()) * @returns The toolkit configuration object or null if not found */ static readToolkitConfigSync(startPath: string = process.cwd()): ToolkitConfig | null { const workspaceRoot = TemplatesManagerService.findWorkspaceRootSync(startPath); const toolkitConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE); if (!fs.pathExistsSync(toolkitConfigPath)) { return null; } const yaml = require('js-yaml'); const content = fs.readFileSync(toolkitConfigPath, 'utf-8'); const config = yaml.load(content) as ToolkitConfig; return config; } /** * Write toolkit.yaml configuration to workspace root * * @param config - The toolkit configuration to write * @param startPath - The path to start searching from (defaults to process.cwd()) */ static async writeToolkitConfig( config: ToolkitConfig, startPath: string = process.cwd(), ): Promise<void> { const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath); const toolkitConfigPath = path.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE); const yaml = await import('js-yaml'); const content = yaml.dump(config, { indent: 2 }); await fs.writeFile(toolkitConfigPath, content, 'utf-8'); } /** * Get the workspace root directory * * @param startPath - The path to start searching from (defaults to process.cwd()) * @returns The workspace root directory path */ static async getWorkspaceRoot(startPath: string = process.cwd()): Promise<string> { return TemplatesManagerService.findWorkspaceRoot(startPath); } /** * Get the workspace root directory (sync) * * @param startPath - The path to start searching from (defaults to process.cwd()) * @returns The workspace root directory path */ static getWorkspaceRootSync(startPath: string = process.cwd()): string { return TemplatesManagerService.findWorkspaceRootSync(startPath); } }

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