UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

793 lines (792 loc) • 33.7 kB
"use strict"; /** * Core Capability Operations * * Handles resource capability management operations including CRUD operations * and capability discovery workflow management */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.getCapabilityService = getCapabilityService; exports.handleCapabilityList = handleCapabilityList; exports.handleCapabilityGet = handleCapabilityGet; exports.handleCapabilityDelete = handleCapabilityDelete; exports.handleCapabilityDeleteAll = handleCapabilityDeleteAll; exports.handleCapabilityProgress = handleCapabilityProgress; exports.handleCapabilitySearch = handleCapabilitySearch; exports.handleCapabilityCRUD = handleCapabilityCRUD; const capability_vector_service_1 = require("./capability-vector-service"); const capabilities_1 = require("./capabilities"); const session_utils_1 = require("./session-utils"); const validation_1 = require("./constants/validation"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); /** * Get initialized capability service * @param collection - Collection name (default: 'capabilities') */ async function getCapabilityService(collection) { const capabilityService = new capability_vector_service_1.CapabilityVectorService(collection); // Always ensure proper collection initialization try { await capabilityService.initialize(); } catch (error) { // If initialization fails, try to provide helpful error context const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Vector DB collection initialization failed: ${errorMessage}. This may be due to dimension mismatch or collection configuration issues.`, { cause: error }); } return capabilityService; } /** * Handle capability list operation */ async function handleCapabilityList(args, logger, requestId, capabilityService) { try { // Get all capabilities with validated limit const rawLimit = Number(args.limit); const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.min(rawLimit, 100) : 10; const capabilities = await capabilityService.getAllCapabilities(limit); const count = await capabilityService.getCapabilitiesCount(); logger.info('Capabilities listed successfully', { requestId, count: capabilities.length, totalCount: count, limit, }); return { success: true, operation: 'list', dataType: 'capabilities', data: { capabilities: capabilities.map(cap => { const desc = typeof cap.description === 'string' ? cap.description : ''; return { id: cap.id ?? capabilities_1.CapabilityInferenceEngine.generateCapabilityId(cap.resourceName), resourceName: cap.resourceName, apiVersion: cap.apiVersion, version: cap.version, group: cap.group, capabilities: cap.capabilities, description: desc.length > 100 ? desc.slice(0, 100) + '...' : desc, complexity: cap.complexity, confidence: cap.confidence, analyzedAt: cap.analyzedAt, }; }), totalCount: count, returnedCount: capabilities.length, limit, }, message: `Retrieved ${capabilities.length} capabilities (${count} total)`, clientInstructions: { behavior: 'Display capability list with IDs prominently visible for user reference', requirement: 'Each capability must show: ID, resource name, main capabilities, and description', format: 'List format with ID clearly labeled (e.g., "ID: abc123") so users can reference specific capabilities', prohibit: 'Do not hide or omit capability IDs from the display - users need them for get operations', }, }; } catch (error) { logger.error('Failed to list capabilities', error, { requestId, }); return { success: false, operation: 'list', dataType: 'capabilities', error: { message: 'Failed to list capabilities', details: error instanceof Error ? error.message : String(error), }, }; } } /** * Handle capability get operation * Supports two ID formats: * - Hashed ID: "a1b2c3d4-..." (existing format) * - JSON format: '{"kind":"Deployment","apiVersion":"apps/v1"}' (new format for dashboard UI) */ async function handleCapabilityGet(args, logger, requestId, capabilityService) { try { // Validate required parameters if (!args.id) { return { success: false, operation: 'get', dataType: 'capabilities', error: { message: validation_1.VALIDATION_MESSAGES.MISSING_PARAMETER('id'), details: 'Specify id to retrieve capability data', example: { id: 'capability-id-example' }, alternativeFormat: { description: 'You can also use JSON format for kind/apiVersion lookup', example: '{"kind":"Deployment","apiVersion":"apps/v1"}', }, }, }; } let capability = null; // Check if id is JSON format for kind/apiVersion lookup if (typeof args.id === 'string' && args.id.trim().startsWith('{')) { try { const lookup = JSON.parse(args.id); if (!lookup.kind || !lookup.apiVersion) { return { success: false, operation: 'get', dataType: 'capabilities', error: { message: 'Invalid JSON lookup format', details: 'JSON format requires both "kind" and "apiVersion" fields', example: { id: '{"kind":"Deployment","apiVersion":"apps/v1"}' }, }, }; } logger.info('Looking up capability by kind/apiVersion', { requestId, kind: lookup.kind, apiVersion: lookup.apiVersion, }); capability = await capabilityService.getCapabilityByKindApiVersion(lookup.kind, lookup.apiVersion); } catch (parseError) { return { success: false, operation: 'get', dataType: 'capabilities', error: { message: 'Invalid JSON format in id parameter', details: parseError instanceof Error ? parseError.message : String(parseError), example: { id: '{"kind":"Deployment","apiVersion":"apps/v1"}' }, }, }; } } else { // Existing hashed ID lookup capability = await capabilityService.getCapability(args.id); } if (!capability) { logger.warn('Capability not found', { requestId, capabilityId: args.id, }); return { success: false, operation: 'get', dataType: 'capabilities', error: { message: `Capability not found for ID: ${args.id}`, details: 'Resource capability may not have been scanned yet', suggestion: 'Use scan operation to analyze this resource first', }, }; } logger.info('Capability retrieved successfully', { requestId, capabilityId: args.id, resourceName: capability.resourceName, capabilitiesFound: capability.capabilities.length, confidence: capability.confidence, }); return { success: true, operation: 'get', dataType: 'capabilities', data: capability, message: `Retrieved capability data for ${capability.resourceName} (ID: ${args.id})`, clientInstructions: { behavior: 'Display comprehensive capability details in organized sections', requirement: 'Show resource name, capabilities, providers, complexity, use case, and confidence prominently', format: 'Structured display with clear sections: Resource Info, Capabilities, Technical Details, and Analysis Results', sections: { resourceInfo: 'Resource name and description with use case', capabilities: 'List all capabilities, providers, and abstractions clearly', technicalDetails: 'Complexity level and provider information', analysisResults: 'Confidence score, analysis timestamp, and ID for reference', }, }, }; } catch (error) { logger.error('Failed to get capability', error, { requestId, capabilityId: args.id, }); return { success: false, operation: 'get', dataType: 'capabilities', error: { message: 'Failed to retrieve capability', details: error instanceof Error ? error.message : String(error), }, }; } } /** * Handle capability delete operation */ async function handleCapabilityDelete(args, logger, requestId, capabilityService) { try { // Validate required parameters if (!args.id) { return { success: false, operation: 'delete', dataType: 'capabilities', error: { message: validation_1.VALIDATION_MESSAGES.MISSING_PARAMETER('id'), details: 'Specify id to delete capability data', example: { id: 'capability-id-example' }, }, }; } // Check if capability exists before deletion const capability = await capabilityService.getCapability(args.id); if (!capability) { logger.warn('Capability not found for deletion', { requestId, capabilityId: args.id, }); return { success: false, operation: 'delete', dataType: 'capabilities', error: { message: `Capability not found for ID: ${args.id}`, details: 'Cannot delete capability that does not exist', }, }; } // Delete capability by ID await capabilityService.deleteCapabilityById(args.id); logger.info('Capability deleted successfully', { requestId, capabilityId: args.id, resourceName: capability.resourceName, }); return { success: true, operation: 'delete', dataType: 'capabilities', deletedCapability: { id: args.id, resourceName: capability.resourceName, }, message: `Capability deleted: ${capability.resourceName}`, }; } catch (error) { logger.error('Failed to delete capability', error, { requestId, capabilityId: args.id, }); return { success: false, operation: 'delete', dataType: 'capabilities', error: { message: 'Failed to delete capability', details: error instanceof Error ? error.message : String(error), }, }; } } /** * Handle capability delete all operation */ async function handleCapabilityDeleteAll(_args, logger, requestId, capabilityService) { try { // Get count first to provide feedback (but don't retrieve all data) const totalCount = await capabilityService.getCapabilitiesCount(); if (totalCount === 0) { logger.info('No capabilities found to delete', { requestId }); return { success: true, operation: 'deleteAll', dataType: 'capabilities', deletedCount: 0, totalCount: 0, message: 'No capabilities found to delete', }; } logger.info('Starting efficient bulk capability deletion', { requestId, totalCapabilities: totalCount, method: 'collection recreation', }); // Efficiently delete all capabilities by recreating collection await capabilityService.deleteAllCapabilities(); logger.info('Bulk capability deletion completed successfully', { requestId, deleted: totalCount, method: 'collection recreation', }); return { success: true, operation: 'deleteAll', dataType: 'capabilities', deletedCount: totalCount, totalCount, errorCount: 0, message: `Successfully deleted all ${totalCount} capabilities`, confirmation: 'All capability data has been permanently removed from the Vector DB', method: 'Efficient collection recreation (no individual record retrieval)', }; } catch (error) { logger.error('Failed to delete all capabilities', error, { requestId, }); return { success: false, operation: 'deleteAll', dataType: 'capabilities', error: { message: 'Failed to delete all capabilities', details: error instanceof Error ? error.message : String(error), }, }; } } /** * Handle capability progress query (check progress of running scan) */ async function handleCapabilityProgress(args, logger, requestId) { try { logger.info('Capability progress query requested', { requestId, sessionId: args.sessionId, }); // Get session directory first const sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(false); let sessionId = args.sessionId; let sessionFilePath; // If no sessionId provided, auto-discover the latest session if (!sessionId) { logger.info('No sessionId provided, searching for latest session file', { requestId, }); try { // Check if capability-sessions subdirectory exists const sessionSubDir = path.join(sessionDir, 'capability-sessions'); if (!fs.existsSync(sessionSubDir)) { logger.info('No capability-sessions directory found', { requestId, sessionDir, }); return { success: false, operation: 'progress', dataType: 'capabilities', error: { message: 'No capability scan sessions found', details: 'No capability-sessions directory found in the session directory', help: 'Start a new capability scan with the scan operation first', sessionDirectory: sessionDir, }, }; } // Find all capability scan session files in the subdirectory const sessionFiles = fs .readdirSync(sessionSubDir) .filter(file => file.endsWith('.json')) .map(file => { const filePath = path.join(sessionSubDir, file); const stats = fs.statSync(filePath); return { filename: file, path: filePath, mtime: stats.mtime, sessionId: file.replace('.json', ''), // Remove .json extension to get sessionId }; }) .sort((a, b) => b.mtime.getTime() - a.mtime.getTime()); // Sort by newest first if (sessionFiles.length === 0) { logger.info('No capability scan session files found', { requestId, sessionDir, }); return { success: false, operation: 'progress', dataType: 'capabilities', error: { message: 'No capability scan sessions found', details: 'No active or recent capability scans found in the session directory', help: 'Start a new capability scan with the scan operation first', sessionDirectory: sessionDir, }, }; } // Use the latest session (first after sorting) const latestSession = sessionFiles[0]; sessionId = latestSession.sessionId; sessionFilePath = latestSession.path; logger.info('Using latest session file', { requestId, sessionId, totalSessions: sessionFiles.length, sessionFile: latestSession.filename, }); } catch (error) { logger.error('Failed to discover session files', error, { requestId, sessionDir, }); return { success: false, operation: 'progress', dataType: 'capabilities', error: { message: 'Failed to discover session files', details: error instanceof Error ? error.message : String(error), sessionDirectory: sessionDir, }, }; } } else { // Use provided sessionId - look in capability-sessions subdirectory const sessionSubDir = path.join(sessionDir, 'capability-sessions'); sessionFilePath = path.join(sessionSubDir, `${sessionId}.json`); if (!fs.existsSync(sessionFilePath)) { logger.warn('Session file not found for provided sessionId', { requestId, sessionId, filePath: sessionFilePath, }); return { success: false, operation: 'progress', dataType: 'capabilities', error: { message: 'Session not found', details: `No capability scan found for session: ${sessionId}`, help: 'Use the scan operation to start a new capability scan, or omit sessionId to use the latest session', }, }; } } // Read and parse session file const sessionData = JSON.parse(fs.readFileSync(sessionFilePath, 'utf8')); // Extract progress information const progress = sessionData.progress; if (!progress) { return { success: true, operation: 'progress', dataType: 'capabilities', sessionId: sessionId, status: 'no-progress-data', message: 'Session exists but no progress tracking is active', currentStep: sessionData.currentStep, startedAt: sessionData.startedAt, lastActivity: sessionData.lastActivity, }; } // Build comprehensive progress response const response = { success: true, operation: 'progress', dataType: 'capabilities', sessionId: sessionId, progress: { status: progress.status, current: progress.current, total: progress.total, percentage: progress.percentage, currentResource: progress.currentResource, startedAt: progress.startedAt, lastUpdated: progress.lastUpdated, }, sessionInfo: { currentStep: sessionData.currentStep, processingMode: sessionData.processingMode, resourceCount: Array.isArray(sessionData.selectedResources) ? sessionData.selectedResources.length : sessionData.selectedResources === 'all' ? 'all resources' : 'unknown', startedAt: sessionData.startedAt, lastActivity: sessionData.lastActivity, }, }; // Add completion information if scan is done if (progress.status === 'completed') { response.progress.completedAt = progress.completedAt; response.progress.totalProcessingTime = progress.totalProcessingTime; response.message = 'Capability scan completed successfully'; } else { response.progress.estimatedTimeRemaining = progress.estimatedTimeRemaining; response.message = `Capability scan in progress: ${progress.current}/${progress.total} resources processed`; } // Add user-friendly display information with client formatting instructions response.display = { summary: progress.status === 'completed' ? `āœ… Scan complete: processed ${progress.total} resources in ${progress.totalProcessingTime}` : `ā³ Processing: ${progress.current}/${progress.total} (${progress.percentage}%) - ${progress.estimatedTimeRemaining} remaining`, currentResource: progress.currentResource, timeline: { started: progress.startedAt, lastUpdate: progress.lastUpdated, ...(progress.completedAt && { completed: progress.completedAt }), }, }; // Add client instructions for readable formatting response.clientInstructions = { behavior: 'Display capability scan progress in a clean, readable format', requirement: 'Show progress information in separate lines, not as a single condensed line', format: progress.status === 'completed' ? 'Completion format: Status, total processed, processing time on separate lines' : 'Progress format: Status line, current resource line, time estimates line, timestamps line', example: progress.status === 'completed' ? 'āœ… Capability Scan Complete\nšŸ“Š Processed: X resources\nā° Processing time: X minutes' : 'ā³ Progress: X/Y resources (Z%)\nšŸ“Š Current resource: ResourceName\nā° Est. remaining: X minutes\nšŸ•’ Started: timestamp\nšŸ”„ Last updated: timestamp', prohibit: 'Do not display all progress information on a single line', }; logger.info('Progress query completed successfully', { requestId, sessionId: args.sessionId, status: progress.status, percentage: progress.percentage, }); return response; } catch (error) { logger.error('Failed to query capability progress', error, { requestId, sessionId: args.sessionId, }); return { success: false, operation: 'progress', dataType: 'capabilities', error: { message: 'Failed to query scan progress', details: error instanceof Error ? error.message : String(error), }, }; } } /** * Handle capability search operation */ async function handleCapabilitySearch(args, logger, requestId, capabilityService) { try { // Validate required search query (stored in id field) if (!args.id || typeof args.id !== 'string' || args.id.trim() === '') { return { success: false, operation: 'search', dataType: 'capabilities', error: { message: 'Search query required', details: 'The id field must contain a search query (e.g., "postgresql database in azure")', }, }; } const searchQuery = args.id.trim(); const limit = args.limit || 25; logger.info('Searching capabilities', { requestId, query: searchQuery, limit, }); // Perform the search using existing service const searchResults = await capabilityService.searchCapabilities(searchQuery, { limit }); // Format results for user display const formattedResults = searchResults.map((result, index) => ({ rank: index + 1, score: Math.round(result.score * 100) / 100, // Round to 2 decimal places id: capabilities_1.CapabilityInferenceEngine.generateCapabilityId(result.data.resourceName), resourceName: result.data.resourceName, capabilities: result.data.capabilities, providers: result.data.providers, complexity: result.data.complexity, description: result.data.description, useCase: result.data.useCase, confidence: result.data.confidence, analyzedAt: result.data.analyzedAt, })); logger.info('Capability search completed', { requestId, query: searchQuery, resultsFound: searchResults.length, limit, }); return { success: true, operation: 'search', dataType: 'capabilities', data: { query: searchQuery, results: formattedResults, resultCount: searchResults.length, limit, }, message: `Found ${searchResults.length} capabilities matching "${searchQuery}"`, clientInstructions: { behavior: 'Display search results with relevance scores and capability details', sections: { searchSummary: 'Show query and result count prominently', resultsList: 'Display each result with rank, score, resource name, and capabilities', capabilityDetails: 'Include providers, complexity, and description for context', actionGuidance: 'Show IDs for get operations and suggest refinement if needed', }, format: 'Ranked list with scores (higher scores = better matches)', emphasize: 'Resource names and main capabilities for easy scanning', }, }; } catch (error) { logger.error('Failed to search capabilities', error, { requestId, query: args.id, }); return { success: false, operation: 'search', dataType: 'capabilities', error: { message: 'Capability search failed', details: error instanceof Error ? error.message : String(error), }, }; } } /** * Consolidated CRUD operations handler with service initialization * Handles list, get, search, delete, deleteAll operations with proper Vector DB setup */ async function handleCapabilityCRUD(operation, args, logger, requestId) { // Create capability service for CRUD operations // Use collection from args if provided, otherwise defaults to 'capabilities' const capabilityService = new capability_vector_service_1.CapabilityVectorService(args.collection); const isReadOperation = ['list', 'get', 'search'].includes(operation); try { const vectorDBHealthy = await capabilityService.healthCheck(); if (!vectorDBHealthy) { return { success: false, operation, dataType: 'capabilities', error: { message: 'Vector DB (Qdrant) connection required', details: 'Capability operations require a working Qdrant connection.', setup: { docker: 'docker run -p 6333:6333 qdrant/qdrant', config: 'export QDRANT_URL=http://localhost:6333', }, }, }; } // For read operations, check if collection exists without creating it // For write operations, initialize (create if needed) if (isReadOperation) { const exists = await capabilityService.collectionExists(); if (!exists) { // Return empty results for read operations on non-existent collection if (operation === 'list') { return { success: true, operation: 'list', dataType: 'capabilities', data: { capabilities: [], totalCount: 0, returnedCount: 0 }, message: 'No capabilities found (collection not initialized)', }; } else if (operation === 'get') { return { success: false, operation: 'get', dataType: 'capabilities', error: { message: 'Capability not found' }, }; } else if (operation === 'search') { return { success: true, operation: 'search', dataType: 'capabilities', data: { query: args.id, results: [], resultCount: 0 }, message: 'No capabilities found (collection not initialized)', }; } } } else { await capabilityService.initialize(); } // Route to specific CRUD operation switch (operation) { case 'list': return await handleCapabilityList(args, logger, requestId, capabilityService); case 'get': return await handleCapabilityGet(args, logger, requestId, capabilityService); case 'search': return await handleCapabilitySearch(args, logger, requestId, capabilityService); case 'delete': return await handleCapabilityDelete(args, logger, requestId, capabilityService); case 'deleteAll': return await handleCapabilityDeleteAll(args, logger, requestId, capabilityService); default: throw new Error(`Unexpected CRUD operation: ${operation}`); } } catch (error) { logger.error(`Capability ${operation} operation failed`, error, { requestId, }); return { success: false, operation, dataType: 'capabilities', error: { message: `Capability ${operation} failed`, details: error instanceof Error ? error.message : String(error), }, }; } }