Skip to main content
Glama

AI Code Toolkit

by AgiFlow
ScaffoldProcessingService.ts5.52 kB
import path from 'node:path'; import { log } from '@agiflowai/aicode-utils'; import type { IFileSystemService, IVariableReplacementService } from '../types/interfaces'; /** * Shared service for common scaffolding operations like processing templates and tracking files */ export class ScaffoldProcessingService { constructor( private fileSystem: IFileSystemService, private variableReplacer: IVariableReplacementService, ) {} /** * Process a target path for variable replacement, handling both files and directories */ async processTargetForVariableReplacement( targetPath: string, variables: Record<string, any>, ): Promise<void> { const targetStat = await this.fileSystem.stat(targetPath); if (targetStat.isDirectory()) { // Process all files in the directory recursively await this.variableReplacer.processFilesForVariableReplacement(targetPath, variables); } else { // Process single file await this.variableReplacer.replaceVariablesInFile(targetPath, variables); } } /** * Track all created files, handling both single files and directories */ async trackCreatedFiles(targetPath: string, createdFiles: string[]): Promise<void> { const targetStat = await this.fileSystem.stat(targetPath); if (targetStat.isDirectory()) { // Track all files in the directory recursively await this.trackCreatedFilesRecursive(targetPath, createdFiles); } else { // Track single file createdFiles.push(targetPath); } } /** * Track all existing files, handling both single files and directories */ async trackExistingFiles(targetPath: string, existingFiles: string[]): Promise<void> { const targetStat = await this.fileSystem.stat(targetPath); if (targetStat.isDirectory()) { // Track all files in the directory recursively await this.trackExistingFilesRecursive(targetPath, existingFiles); } else { // Track single file existingFiles.push(targetPath); } } /** * Copy source to target, then process templates and track files * Now supports tracking existing files separately from created files * Automatically handles .liquid template files by stripping the extension */ async copyAndProcess( sourcePath: string, targetPath: string, variables: Record<string, any>, createdFiles: string[], existingFiles?: string[], ): Promise<void> { // Ensure target directory exists await this.fileSystem.ensureDir(path.dirname(targetPath)); // Check if target already exists const targetExists = await this.fileSystem.pathExists(targetPath); if (targetExists && existingFiles) { // Track existing files but don't overwrite await this.trackExistingFiles(targetPath, existingFiles); return; // Skip copying and processing for existing files } // Check if source file exists, if not try with .liquid extension let actualSourcePath = sourcePath; const sourceExists = await this.fileSystem.pathExists(sourcePath); if (!sourceExists) { // Try with .liquid extension const liquidSourcePath = `${sourcePath}.liquid`; const liquidExists = await this.fileSystem.pathExists(liquidSourcePath); if (liquidExists) { actualSourcePath = liquidSourcePath; } else { throw new Error(`Source file not found: ${sourcePath} (also tried ${liquidSourcePath})`); } } // Copy source to target (only if it doesn't exist) await this.fileSystem.copy(actualSourcePath, targetPath); // Process for variable replacement await this.processTargetForVariableReplacement(targetPath, variables); // Track created files await this.trackCreatedFiles(targetPath, createdFiles); } /** * Recursively collect all file paths in a directory for created files */ private async trackCreatedFilesRecursive(dirPath: string, createdFiles: string[]): Promise<void> { let items: string[] = []; try { items = await this.fileSystem.readdir(dirPath); } catch (error) { log.warn(`Cannot read directory ${dirPath}: ${error}`); return; } for (const item of items) { if (!item) continue; const itemPath = path.join(dirPath, item); try { const stat = await this.fileSystem.stat(itemPath); if (stat.isDirectory()) { await this.trackCreatedFilesRecursive(itemPath, createdFiles); } else if (stat.isFile()) { createdFiles.push(itemPath); } } catch (error) { log.warn(`Cannot stat ${itemPath}: ${error}`); } } } /** * Recursively collect all file paths in a directory for existing files */ private async trackExistingFilesRecursive( dirPath: string, existingFiles: string[], ): Promise<void> { let items: string[] = []; try { items = await this.fileSystem.readdir(dirPath); } catch (error) { log.warn(`Cannot read directory ${dirPath}: ${error}`); return; } for (const item of items) { if (!item) continue; const itemPath = path.join(dirPath, item); try { const stat = await this.fileSystem.stat(itemPath); if (stat.isDirectory()) { await this.trackExistingFilesRecursive(itemPath, existingFiles); } else if (stat.isFile()) { existingFiles.push(itemPath); } } catch (error) { log.warn(`Cannot stat ${itemPath}: ${error}`); } } } }

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