@vfarcic/dot-ai
Version:
Universal Kubernetes application deployment agent with CLI and MCP interfaces
172 lines (171 loc) • 7.92 kB
JavaScript
;
/**
* Choose Solution Tool - Select a solution and return its questions
*/
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.CHOOSESOLUTION_TOOL_INPUT_SCHEMA = exports.CHOOSESOLUTION_TOOL_DESCRIPTION = exports.CHOOSESOLUTION_TOOL_NAME = void 0;
exports.handleChooseSolutionTool = handleChooseSolutionTool;
const zod_1 = require("zod");
const error_handling_1 = require("../core/error-handling");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const session_utils_1 = require("../core/session-utils");
// Tool metadata for direct MCP registration
exports.CHOOSESOLUTION_TOOL_NAME = 'chooseSolution';
exports.CHOOSESOLUTION_TOOL_DESCRIPTION = 'Select a solution by ID and return its questions for configuration';
// Zod schema for MCP registration
exports.CHOOSESOLUTION_TOOL_INPUT_SCHEMA = {
solutionId: zod_1.z.string().regex(/^sol_[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{6}_[a-f0-9]+$/).describe('The solution ID to choose (e.g., sol_2025-07-01T154349_1e1e242592ff)')
};
/**
* Load solution file by ID
*/
function loadSolutionFile(solutionId, sessionDir) {
const solutionPath = path.join(sessionDir, `${solutionId}.json`);
if (!fs.existsSync(solutionPath)) {
throw new Error(`Solution file not found: ${solutionPath}. Available files: ${fs.readdirSync(sessionDir).filter(f => f.endsWith('.json')).join(', ')}`);
}
try {
const content = fs.readFileSync(solutionPath, 'utf8');
const solution = JSON.parse(content);
// Validate solution structure
if (!solution.solutionId || !solution.questions) {
throw new Error(`Invalid solution file structure: ${solutionId}. Missing required fields: solutionId or questions`);
}
return solution;
}
catch (error) {
if (error instanceof SyntaxError) {
throw new Error(`Invalid JSON in solution file: ${solutionId}. ${error.message}`);
}
throw error;
}
}
/**
* Direct MCP tool handler for chooseSolution functionality
*/
async function handleChooseSolutionTool(args, dotAI, logger, requestId) {
return await error_handling_1.ErrorHandler.withErrorHandling(async () => {
logger.debug('Handling chooseSolution request', { requestId, solutionId: args?.solutionId });
// Input validation is handled automatically by MCP SDK with Zod schema
// args are already validated and typed when we reach this point
// Get session directory from environment
let sessionDir;
try {
sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(args, false); // requireWrite=false
logger.debug('Session directory resolved and validated', { sessionDir });
}
catch (error) {
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, error instanceof Error ? error.message : 'Session directory validation failed', {
operation: 'session_directory_validation',
component: 'ChooseSolutionTool',
requestId,
suggestedActions: [
'Ensure session directory exists and is readable',
'Check directory permissions',
'Verify the directory path is correct',
'Verify DOT_AI_SESSION_DIR environment variable is correctly set'
]
});
}
// Load solution file
let solution;
try {
solution = loadSolutionFile(args.solutionId, sessionDir);
logger.debug('Solution file loaded successfully', {
solutionId: args.solutionId,
hasQuestions: !!solution.questions,
questionCategories: {
required: solution.questions?.required?.length || 0,
basic: solution.questions?.basic?.length || 0,
advanced: solution.questions?.advanced?.length || 0,
hasOpen: !!solution.questions?.open
}
});
}
catch (error) {
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.STORAGE, error_handling_1.ErrorSeverity.HIGH, error instanceof Error ? error.message : 'Failed to load solution file', {
operation: 'solution_file_load',
component: 'ChooseSolutionTool',
requestId,
input: { solutionId: args.solutionId, sessionDir },
suggestedActions: [
'Check that the solution ID is correct',
'Verify the solution file exists in the session directory',
'Ensure the solution was created by a recent recommend tool call',
'List available solution files in the session directory'
]
});
}
// Prepare response with solution details and questions
const response = {
status: 'stage_questions',
solutionId: solution.solutionId,
currentStage: 'required',
questions: solution.questions.required || [],
nextStage: 'basic',
message: 'Please provide the required configuration for your application.',
nextAction: 'answerQuestion',
guidance: 'Answer questions in this stage or skip to proceed to the next stage. Do NOT try to generate manifests yet.',
timestamp: new Date().toISOString()
};
logger.info('Choose solution completed successfully', {
solutionId: args.solutionId,
sessionDir,
questionCategories: {
required: solution.questions.required?.length || 0,
basic: solution.questions.basic?.length || 0,
advanced: solution.questions.advanced?.length || 0,
hasOpen: !!solution.questions.open
},
totalQuestions: (solution.questions.required?.length || 0) +
(solution.questions.basic?.length || 0) +
(solution.questions.advanced?.length || 0) +
(solution.questions.open ? 1 : 0)
});
return {
content: [{
type: 'text',
text: JSON.stringify(response, null, 2)
}]
};
}, {
operation: 'choose_solution',
component: 'ChooseSolutionTool',
requestId,
input: args
});
}