Skip to main content
Glama
workspaceConfig.ts18.1 kB
/** * @fileOverview: ⚠️ DEPRECATED - Workspace configuration tool (use manage_embeddings instead) * @module: WorkspaceConfig * @deprecated: This tool has been merged into manage_embeddings. Use manage_embeddings with actions: * - get_workspace (replaces action="get") * - set_workspace (replaces action="set") * - validate_workspace (replaces action="validate") * @keyFunctions: * - workspaceConfigTool: Tool definition for workspace configuration * - handleWorkspaceConfig(): Handler for workspace configuration requests * @context: Kept for backward compatibility only. New code should use manage_embeddings. */ import { logger } from '../../utils/logger'; import { initializeAutoIndexing } from '../../startup/autoIndexingStartup'; import { validateWorkspaceFolder, getCurrentWorkspaceFolder, setWorkspaceFolder, WorkspaceValidationOptions, } from '../utils/workspaceValidator'; import { getEmbeddingStatus } from './embeddingManagement'; import { LocalEmbeddingGenerator } from '../../local/embeddingGenerator'; import { ProjectIdentifier } from '../../local/projectIdentifier'; async function safeGetEmbeddingStatus(projectPath: string) { try { return await getEmbeddingStatus({ projectPath }); } catch (error) { logger.warn('�s��,? Could not check embedding status', { projectPath, error: error instanceof Error ? error.message : String(error), }); return { projectId: 'unknown', hasEmbeddings: false, totalEmbeddings: 0, compatible: false, error: 'Could not check embedding status', recommendations: [ 'dY"? Embedding status check failed - AI tools may still work but with reduced performance', ], }; } } /** * Check if we should proactively generate embeddings after workspace setup */ async function shouldGenerateEmbeddingsProactively( workspacePath: string, fileCount: number ): Promise<{ shouldGenerate: boolean; reason: string; maxFiles?: number; }> { // Check if proactive embedding generation is enabled if (process.env.WORKSPACE_PROACTIVE_EMBEDDINGS !== 'true') { return { shouldGenerate: false, reason: 'Proactive embedding generation disabled (set WORKSPACE_PROACTIVE_EMBEDDINGS=true)', }; } // Check if local embeddings are enabled if (process.env.USE_LOCAL_EMBEDDINGS !== 'true') { return { shouldGenerate: false, reason: 'Local embeddings not enabled (set USE_LOCAL_EMBEDDINGS=true)', }; } // Check if embeddings already exist try { const embeddingStatus = await safeGetEmbeddingStatus(workspacePath); if (embeddingStatus.hasEmbeddings && embeddingStatus.totalEmbeddings > 0) { return { shouldGenerate: false, reason: `Embeddings already exist (${embeddingStatus.totalEmbeddings} chunks)`, }; } } catch (error) { logger.debug('Could not check existing embeddings, proceeding with generation check'); } // Check file count limits to avoid long delays const maxFiles = parseInt(process.env.WORKSPACE_EMBEDDING_MAX_FILES || '500', 10); if (fileCount > maxFiles) { return { shouldGenerate: false, reason: `Too many files (${fileCount} > ${maxFiles} limit)`, maxFiles, }; } // Check if we're in a reasonable project size const minFiles = parseInt(process.env.WORKSPACE_EMBEDDING_MIN_FILES || '10', 10); if (fileCount < minFiles) { return { shouldGenerate: false, reason: `Too few files (${fileCount} < ${minFiles} minimum)`, }; } return { shouldGenerate: true, reason: `Ready for proactive embedding generation (${fileCount} files)`, }; } /** * Proactively generate embeddings for a newly configured workspace */ async function generateEmbeddingsProactively( workspacePath: string, fileCount: number ): Promise<{ success: boolean; message: string; stats?: any; }> { try { logger.info('🚀 Starting proactive embedding generation', { workspacePath, fileCount, }); // Identify the project const projectIdentifier = ProjectIdentifier.getInstance(); const projectInfo = await projectIdentifier.identifyProject(workspacePath); if (!projectInfo) { return { success: false, message: 'Could not identify project for embedding generation', }; } // Initialize embedding generator const embeddingGenerator = new LocalEmbeddingGenerator(); // Generate embeddings with reasonable limits for proactive generation const progress = await embeddingGenerator.generateProjectEmbeddings( projectInfo.id, workspacePath, { batchSize: 5, // Smaller batches for background processing rateLimit: 500, // Lower rate limit for background processing maxChunkSize: 1000, // Smaller chunks for faster processing filePatterns: [ '**/*.{ts,tsx,js,jsx,py,go,rs,java,cpp,c,h,hpp,cs,rb,php,swift,kt,scala,clj,hs,ml,r,sql,sh,bash,zsh,md}', ], } ); logger.info('✅ Proactive embedding generation completed', { projectId: projectInfo.id, filesProcessed: progress.processedFiles, chunksCreated: progress.totalChunks, embeddings: progress.embeddings, errors: progress.errors.length, }); return { success: true, message: `Generated ${progress.embeddings} embeddings from ${progress.processedFiles} files`, stats: { filesProcessed: progress.processedFiles, chunksCreated: progress.totalChunks, embeddings: progress.embeddings, errors: progress.errors.length, }, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error('❌ Proactive embedding generation failed', { workspacePath, error: errorMessage, }); return { success: false, message: `Embedding generation failed: ${errorMessage}`, }; } } /** * Tool definition for workspace configuration */ export const workspaceConfigTool = { name: 'workspace_config', description: '⚠️ DEPRECATED: Use manage_embeddings instead. This tool has been merged into manage_embeddings for unified workspace and embedding management. Use manage_embeddings with actions: get_workspace, set_workspace, or validate_workspace. This tool is kept for backward compatibility only.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['get', 'set', 'validate'], description: 'Action to perform: get current workspace (includes embedding status), set new workspace (triggers embedding check), or validate a path', default: 'get', }, path: { type: 'string', description: 'Path to set as workspace (required for set/validate actions). Can be absolute or relative.', }, maxFiles: { type: 'number', default: 5000, minimum: 100, maximum: 10000, description: 'Maximum number of analyzable files allowed in workspace (default: 5000)', }, excludePatterns: { type: 'array', items: { type: 'string' }, description: 'Additional glob patterns to exclude when counting files (e.g., ["*.tmp", "cache/**"])', default: [], }, allowHiddenFolders: { type: 'boolean', default: false, description: 'Whether to include hidden folders (starting with .) in file counting', }, force: { type: 'boolean', default: false, description: 'Force set workspace even with warnings (but not errors)', }, }, required: [], }, }; /** * Handle workspace configuration requests * @deprecated Use manage_embeddings with workspace actions instead */ export async function handleWorkspaceConfig(args: any): Promise<any> { const { action = 'get', path, maxFiles = 5000, excludePatterns = [], allowHiddenFolders = false, force = false, } = args; logger.warn('⚠️ workspace_config is deprecated - use manage_embeddings instead', { action, recommendation: `Use manage_embeddings with action="${action === 'get' ? 'get_workspace' : action === 'set' ? 'set_workspace' : 'validate_workspace'}"`, }); logger.info('🏠 Workspace configuration request (deprecated tool)', { action, path, maxFiles, excludePatterns, allowHiddenFolders, force, }); try { switch (action) { case 'get': return await handleGetWorkspace(); case 'validate': if (!path) { return { success: false, error: 'Path is required for validate action', }; } return handleValidateWorkspace(path, { maxFiles, excludePatterns, allowHiddenFolders }); case 'set': if (!path) { return { success: false, error: 'Path is required for set action', }; } return await handleSetWorkspace( path, { maxFiles, excludePatterns, allowHiddenFolders }, force ); default: return { success: false, error: `Unknown action: ${action}. Use 'get', 'validate', or 'set'`, }; } } catch (error) { logger.error('❌ Workspace configuration failed', { action, path, error: error instanceof Error ? error.message : String(error), }); return { success: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Get current workspace configuration */ async function handleGetWorkspace(): Promise<any> { const currentWorkspace = getCurrentWorkspaceFolder(); logger.info('📍 Current workspace status', { workspace: currentWorkspace || '(not set)', fromEnv: !!process.env.WORKSPACE_FOLDER, fromBaseDir: !!process.env.AMBIANCE_BASE_DIR, }); let embeddingStatus = null; if (currentWorkspace) { logger.info('🔍 Checking embedding status for current workspace', { workspace: currentWorkspace, }); embeddingStatus = await safeGetEmbeddingStatus(currentWorkspace); } return { success: true, workspace: currentWorkspace || null, source: process.env.WORKSPACE_FOLDER ? 'WORKSPACE_FOLDER' : process.env.AMBIANCE_BASE_DIR ? 'AMBIANCE_BASE_DIR' : 'none', embeddingStatus, message: currentWorkspace ? `Current workspace: ${currentWorkspace}` : 'No workspace folder configured. Use action "set" to configure one.', recommendations: embeddingStatus?.recommendations || [], }; } /** * Validate a workspace path */ async function handleValidateWorkspace( path: string, options: WorkspaceValidationOptions ): Promise<any> { logger.info('🔍 Validating workspace path', { path, options }); const validation = await validateWorkspaceFolder(path, options); return { success: true, validation, message: validation.isValid ? `✅ Path is valid for workspace: ${validation.path} (${validation.fileCount} files)` : `❌ Path is not suitable for workspace: ${validation.error}`, recommendations: validation.isValid ? [] : getWorkspaceRecommendations(validation), }; } /** * Set workspace folder */ async function handleSetWorkspace( path: string, options: WorkspaceValidationOptions, force: boolean ): Promise<any> { logger.info('🏠 Setting workspace folder', { path, options, force }); // First validate the path const validation = await validateWorkspaceFolder(path, options); if (!validation.isValid) { return { success: false, error: validation.error, validation, recommendations: getWorkspaceRecommendations(validation), message: `Cannot set workspace: ${validation.error}`, }; } // Check for warnings if (validation.warnings && validation.warnings.length > 0 && !force) { return { success: false, error: 'Workspace has warnings. Use force=true to override.', validation, warnings: validation.warnings, message: `Workspace has warnings: ${validation.warnings.join(', ')}. Use force=true if you want to proceed anyway.`, }; } // Set the workspace setWorkspaceFolder(validation.path!); logger.info('✅ Workspace folder set successfully', { workspace: validation.path, fileCount: validation.fileCount, hadWarnings: !!validation.warnings, }); // Mark workspace as initialized so startup can begin indexing on next cycle process.env.WORKSPACE_INITIALIZED = 'true'; logger.info('🔧 Workspace initialization flag set'); // Check if we should proactively generate embeddings const shouldGenerate = await shouldGenerateEmbeddingsProactively( validation.path!, validation.fileCount || 0 ); logger.info('📊 Proactive embedding generation check', { shouldGenerate: shouldGenerate.shouldGenerate, reason: shouldGenerate.reason, fileCount: validation.fileCount || 0, }); const embeddingGenerationResult = null; let embeddingMessage = ''; if (shouldGenerate.shouldGenerate) { // Start proactive embedding generation in the background setTimeout(async () => { try { const result = await generateEmbeddingsProactively( validation.path!, validation.fileCount || 0 ); if (result.success) { logger.info('✅ Background embedding generation completed', { workspace: validation.path, message: result.message, stats: result.stats, }); } else { logger.warn('⚠️ Background embedding generation failed', { workspace: validation.path, error: result.message, }); } } catch (error) { logger.error('❌ Background embedding generation error', { workspace: validation.path, error: error instanceof Error ? error.message : String(error), }); } }, 100); // Small delay to let workspace_config return first embeddingMessage = 'Starting background embedding generation for optimal AI tool performance...'; logger.info('🚀 Triggered proactive embedding generation after workspace setup'); } else { // Kick off auto-indexing in the background now that workspace is set try { setTimeout(() => { initializeAutoIndexing().catch(error => { logger.warn('⚠️ Failed to start auto-indexing after workspace setup', { error: error instanceof Error ? error.message : String(error), }); }); }, 0); logger.info('🚀 Triggered automatic indexing after workspace setup'); } catch (err) { logger.warn('⚠️ Could not trigger automatic indexing after workspace setup', { error: err instanceof Error ? err.message : String(err), }); } embeddingMessage = shouldGenerate.reason; } // Check embedding status for the newly set workspace logger.info('🔍 Checking embedding status for newly set workspace', { workspace: validation.path, }); const embeddingStatus = await safeGetEmbeddingStatus(validation.path!); // Log embedding status for visibility if (embeddingStatus.hasEmbeddings) { logger.info('📊 Embedding status for workspace', { workspace: validation.path, totalEmbeddings: embeddingStatus.totalEmbeddings, compatible: embeddingStatus.compatible, lastUpdated: embeddingStatus.lastUpdated, }); } else { logger.info('📝 No embeddings found for workspace - will be generated on first AI tool use', { workspace: validation.path, }); } // Notify about any compatibility issues if (!embeddingStatus.compatible) { logger.warn('⚠️ Embedding compatibility issues detected in new workspace', { workspace: validation.path, issues: embeddingStatus.issues, recommendations: embeddingStatus.recommendations, }); } return { success: true, workspace: validation.path, fileCount: validation.fileCount, warnings: validation.warnings, embeddingStatus, proactiveEmbeddingGeneration: { triggered: shouldGenerate.shouldGenerate, reason: shouldGenerate.reason, message: embeddingMessage, }, message: `✅ Workspace set successfully: ${validation.path} (${validation.fileCount} files)` + (validation.warnings ? ` [Warnings: ${validation.warnings.length}]` : '') + (shouldGenerate.shouldGenerate ? ' [Embedding generation started]' : ''), embeddingMessage: embeddingMessage, }; } /** * Generate recommendations for failed workspace validation */ function getWorkspaceRecommendations(validation: any): string[] { const recommendations: string[] = []; if (!validation.isValid && validation.error) { if (validation.error.includes('root drive')) { recommendations.push('Use a specific project directory instead of root drive'); recommendations.push('Example: C:\\Projects\\MyProject instead of C:\\'); } if (validation.error.includes('too many files')) { recommendations.push('Choose a more specific subdirectory'); recommendations.push('Add exclude patterns for build/dependency folders'); recommendations.push('Consider using folderPath parameter in other tools instead'); } if (validation.error.includes('does not exist')) { recommendations.push('Create the directory first'); recommendations.push('Check the path spelling and access permissions'); } if (validation.error.includes('not a directory')) { recommendations.push('Use the parent directory instead'); recommendations.push('Check that the path points to a folder, not a file'); } } if (recommendations.length === 0) { recommendations.push('Try a different path'); recommendations.push('Use action "validate" to check paths before setting'); } return recommendations; }

Latest Blog Posts

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/sbarron/AmbianceMCP'

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