UNPKG

@vfarcic/dot-ai

Version:

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

209 lines (208 loc) 9.94 kB
"use strict"; /** * Impact Analysis Tool - Dependency & Blast Radius Analysis * * Accepts free-text input (kubectl commands, YAML manifests, or plain-English * descriptions) and uses AI reasoning to discover dependencies and assess * whether the operation is safe to proceed. * * PRD #405: Dependency & Impact Analysis */ Object.defineProperty(exports, "__esModule", { value: true }); exports.IMPACT_ANALYSIS_TOOL_INPUT_SCHEMA = exports.IMPACT_ANALYSIS_TOOL_DESCRIPTION = exports.IMPACT_ANALYSIS_TOOL_NAME = void 0; exports.handleImpactAnalysisTool = handleImpactAnalysisTool; const zod_1 = require("zod"); const error_handling_1 = require("../core/error-handling"); const ai_provider_factory_1 = require("../core/ai-provider-factory"); const capability_tools_1 = require("../core/capability-tools"); const resource_tools_1 = require("../core/resource-tools"); const generic_session_manager_1 = require("../core/generic-session-manager"); const shared_prompt_loader_1 = require("../core/shared-prompt-loader"); const internal_tools_1 = require("../core/internal-tools"); // Tool metadata for MCP registration exports.IMPACT_ANALYSIS_TOOL_NAME = 'impact_analysis'; exports.IMPACT_ANALYSIS_TOOL_DESCRIPTION = 'Analyze the blast radius of a proposed Kubernetes operation. Accepts free-text input: kubectl commands (e.g., "kubectl delete pvc data-postgres-0 -n production"), YAML manifests, or plain-English descriptions (e.g., "what happens if I delete the postgres database?"). Returns whether the operation is safe and a detailed dependency analysis with confidence levels.'; // Zod schema for MCP registration exports.IMPACT_ANALYSIS_TOOL_INPUT_SCHEMA = { input: zod_1.z.string().min(1).max(5000).describe('The operation to analyze. Accepts kubectl commands, YAML manifests, or plain-English descriptions.'), interaction_id: zod_1.z.string().optional().describe('INTERNAL ONLY - Do not populate. Used for evaluation dataset generation.'), }; /** * Parse the AI's final JSON response to extract safe and summary fields. */ function parseImpactAnalysis(aiResponse) { try { const firstBraceIndex = aiResponse.indexOf('{'); if (firstBraceIndex === -1) { return { safe: false, summary: aiResponse.trim() || 'Analysis completed but no structured output was produced.' }; } // Track brace depth to find complete JSON object let braceCount = 0; let inString = false; let escapeNext = false; let jsonEndIndex = -1; for (let i = firstBraceIndex; i < aiResponse.length; i++) { const char = aiResponse[i]; if (escapeNext) { escapeNext = false; continue; } if (char === '\\') { escapeNext = true; continue; } if (char === '"') { inString = !inString; continue; } if (inString) continue; if (char === '{') braceCount++; if (char === '}') { braceCount--; if (braceCount === 0) { jsonEndIndex = i + 1; break; } } } if (jsonEndIndex === -1) { return { safe: false, summary: aiResponse.trim() || 'Analysis completed but no structured output was produced.' }; } const jsonString = aiResponse.substring(firstBraceIndex, jsonEndIndex); const parsed = JSON.parse(jsonString); return { safe: typeof parsed.safe === 'boolean' ? parsed.safe : false, summary: parsed.summary || 'No summary provided', }; } catch { return { safe: false, summary: aiResponse.trim() || 'Analysis completed but response could not be parsed.' }; } } async function handleImpactAnalysisTool(args, pluginManager) { const requestId = `impact_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; const logger = new error_handling_1.ConsoleLogger('ImpactAnalysisTool'); try { // Validate input const input = args.input; if (!input || typeof input !== 'string') { throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.MEDIUM, 'Input is required and must be a string', { operation: 'input_validation', component: 'ImpactAnalysisTool' }); } logger.info('Processing impact analysis', { requestId, inputLength: input.length }); // Initialize AI provider const aiProvider = (0, ai_provider_factory_1.createAIProvider)(); // Load system prompt const systemPrompt = (0, shared_prompt_loader_1.loadPrompt)('impact-analysis-system'); // Local executor for non-plugin tools const localToolExecutor = async (toolName, toolInput) => { if (toolName.startsWith('search_capabilities') || toolName.startsWith('query_capabilities')) { return (0, capability_tools_1.executeCapabilityTools)(toolName, toolInput); } if (toolName.startsWith('search_resources') || toolName.startsWith('query_resources')) { return (0, resource_tools_1.executeResourceTools)(toolName, toolInput); } return { success: false, error: `Unknown tool: ${toolName}`, message: `Tool '${toolName}' is not implemented in impact analysis tool` }; }; // Read-only kubectl tools from plugin const KUBECTL_READONLY_TOOL_NAMES = [ 'kubectl_api_resources', 'kubectl_get', 'kubectl_describe', 'kubectl_events', 'kubectl_get_crd_schema', ]; const pluginKubectlTools = pluginManager ? pluginManager.getDiscoveredTools().filter(t => KUBECTL_READONLY_TOOL_NAMES.includes(t.name)) : []; // Build tool list (kubectl + knowledge base + git/fs for GitOps verification) const tools = [...capability_tools_1.CAPABILITY_TOOLS, ...resource_tools_1.RESOURCE_TOOLS, ...pluginKubectlTools, ...(0, internal_tools_1.getInternalTools)()]; // Clean up old clone directories (non-blocking) (0, internal_tools_1.cleanupOldClones)(); // Chain executors: plugin tools (kubectl) → internal tools (git/fs) → local tools (capabilities/resources) // Internal executor handles git_clone, fs_read, fs_list; falls back to localToolExecutor for capability/resource tools const internalToolNames = new Set(['git_clone', 'fs_list', 'fs_read']); const internalExecutor = (0, internal_tools_1.createInternalToolExecutor)(requestId); const combinedLocalExecutor = async (toolName, toolInput) => { if (internalToolNames.has(toolName)) { return internalExecutor(toolName, toolInput); } return localToolExecutor(toolName, toolInput); }; const toolExecutor = pluginManager ? pluginManager.createToolExecutor(combinedLocalExecutor) : combinedLocalExecutor; // Execute tool loop const result = await aiProvider.toolLoop({ systemPrompt, userMessage: input, tools, toolExecutor, maxIterations: 30, operation: 'impact-analysis', evaluationContext: { user_intent: input }, interaction_id: args.interaction_id }); // Guard: if the AI call did not succeed, surface the real error instead of trying to parse if (result.status && result.status !== 'success') { throw new Error(`Impact analysis ${result.status}: ${result.finalMessage}`); } // Parse AI response const { safe, summary } = parseImpactAnalysis(result.finalMessage); // Extract tools used from execution record const toolsUsed = [...new Set(result.toolCallsExecuted.map(tc => tc.tool))]; logger.info('Impact analysis completed', { requestId, safe, iterations: result.iterations, toolsUsed, }); // Store session const sessionManager = new generic_session_manager_1.GenericSessionManager('imp'); const session = sessionManager.createSession({ toolName: 'impact_analysis', input, safe, summary, toolsUsed, iterations: result.iterations, toolCallsExecuted: result.toolCallsExecuted, }); const output = { success: true, safe, summary, sessionId: session.sessionId, agentInstructions: safe ? 'Present the impact analysis summary to the user. The operation appears safe to proceed.' : 'Present the impact analysis summary to the user. The operation is NOT safe — highlight the risks and affected resources before the user proceeds.', }; return { content: [ { type: 'text', text: JSON.stringify(output, null, 2) } ] }; } catch (error) { logger.error('Impact analysis failed', error, { requestId }); if (error instanceof Error && 'category' in error) { throw error; } throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.UNKNOWN, error_handling_1.ErrorSeverity.HIGH, `Impact analysis tool failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { operation: 'impact_analysis_tool_execution', component: 'ImpactAnalysisTool', requestId, input: { input: args.input } }); } }