@vfarcic/dot-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +51 -0
- package/dist/core/claude.d.ts +42 -0
- package/dist/core/claude.d.ts.map +1 -0
- package/dist/core/claude.js +229 -0
- package/dist/core/deploy-operation.d.ts +38 -0
- package/dist/core/deploy-operation.d.ts.map +1 -0
- package/dist/core/deploy-operation.js +101 -0
- package/dist/core/discovery.d.ts +162 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +758 -0
- package/dist/core/error-handling.d.ts +167 -0
- package/dist/core/error-handling.d.ts.map +1 -0
- package/dist/core/error-handling.js +399 -0
- package/dist/core/index.d.ts +42 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +123 -0
- package/dist/core/kubernetes-utils.d.ts +38 -0
- package/dist/core/kubernetes-utils.d.ts.map +1 -0
- package/dist/core/kubernetes-utils.js +177 -0
- package/dist/core/memory.d.ts +45 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +113 -0
- package/dist/core/schema.d.ts +187 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +655 -0
- package/dist/core/session-utils.d.ts +29 -0
- package/dist/core/session-utils.d.ts.map +1 -0
- package/dist/core/session-utils.js +121 -0
- package/dist/core/workflow.d.ts +70 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +161 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/interfaces/cli.d.ts +74 -0
- package/dist/interfaces/cli.d.ts.map +1 -0
- package/dist/interfaces/cli.js +769 -0
- package/dist/interfaces/mcp.d.ts +30 -0
- package/dist/interfaces/mcp.d.ts.map +1 -0
- package/dist/interfaces/mcp.js +105 -0
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +151 -0
- package/dist/tools/answer-question.d.ts +27 -0
- package/dist/tools/answer-question.d.ts.map +1 -0
- package/dist/tools/answer-question.js +696 -0
- package/dist/tools/choose-solution.d.ts +23 -0
- package/dist/tools/choose-solution.d.ts.map +1 -0
- package/dist/tools/choose-solution.js +171 -0
- package/dist/tools/deploy-manifests.d.ts +25 -0
- package/dist/tools/deploy-manifests.d.ts.map +1 -0
- package/dist/tools/deploy-manifests.js +74 -0
- package/dist/tools/generate-manifests.d.ts +23 -0
- package/dist/tools/generate-manifests.d.ts.map +1 -0
- package/dist/tools/generate-manifests.js +424 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/recommend.d.ts +23 -0
- package/dist/tools/recommend.d.ts.map +1 -0
- package/dist/tools/recommend.js +332 -0
- package/package.json +124 -0
- package/prompts/intent-validation.md +65 -0
- package/prompts/manifest-generation.md +79 -0
- package/prompts/question-generation.md +128 -0
- package/prompts/resource-analysis.md +127 -0
- package/prompts/resource-selection.md +55 -0
- package/prompts/resource-solution-ranking.md +77 -0
- package/prompts/solution-enhancement.md +129 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Recommend Tool - AI-powered Kubernetes resource recommendations
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.RECOMMEND_TOOL_INPUT_SCHEMA = exports.RECOMMEND_TOOL_DESCRIPTION = exports.RECOMMEND_TOOL_NAME = void 0;
|
|
40
|
+
exports.handleRecommendTool = handleRecommendTool;
|
|
41
|
+
const zod_1 = require("zod");
|
|
42
|
+
const error_handling_1 = require("../core/error-handling");
|
|
43
|
+
const schema_1 = require("../core/schema");
|
|
44
|
+
const claude_1 = require("../core/claude");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const crypto = __importStar(require("crypto"));
|
|
48
|
+
const session_utils_1 = require("../core/session-utils");
|
|
49
|
+
// Tool metadata for direct MCP registration
|
|
50
|
+
exports.RECOMMEND_TOOL_NAME = 'recommend';
|
|
51
|
+
exports.RECOMMEND_TOOL_DESCRIPTION = 'Deploy, create, run, or setup applications on Kubernetes with AI-powered recommendations. Ask the user to describe their application first, then use their response here.';
|
|
52
|
+
// Zod schema for MCP registration
|
|
53
|
+
exports.RECOMMEND_TOOL_INPUT_SCHEMA = {
|
|
54
|
+
intent: zod_1.z.string().min(1).max(1000).describe('What the user wants to deploy, create, run, or setup on Kubernetes (based on their description). Ask the user to describe their application first, then use their response here. Examples: "deploy a web application", "create a database cluster", "run my Node.js API", "setup a Redis cache", "launch a microservice", "build a CI/CD pipeline", "deploy a WordPress site", "create a monitoring stack", "run a Python Flask app", "setup MongoDB", "deploy a React frontend", "create a load balancer"')
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Validate intent meaningfulness using AI
|
|
58
|
+
*/
|
|
59
|
+
async function validateIntentWithAI(intent, claudeIntegration) {
|
|
60
|
+
try {
|
|
61
|
+
// Load prompt template
|
|
62
|
+
const promptPath = path.join(process.cwd(), 'prompts', 'intent-validation.md');
|
|
63
|
+
const template = fs.readFileSync(promptPath, 'utf8');
|
|
64
|
+
// Replace template variables
|
|
65
|
+
const validationPrompt = template.replace('{intent}', intent);
|
|
66
|
+
// Send to Claude for validation
|
|
67
|
+
const response = await claudeIntegration.sendMessage(validationPrompt);
|
|
68
|
+
// Parse JSON response with robust error handling
|
|
69
|
+
let jsonContent = response.content;
|
|
70
|
+
// Try to find JSON object wrapped in code blocks
|
|
71
|
+
const codeBlockMatch = response.content.match(/```(?:json)?\s*(\{[\s\S]*?\})\s*```/);
|
|
72
|
+
if (codeBlockMatch) {
|
|
73
|
+
jsonContent = codeBlockMatch[1];
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Try to find JSON object that starts with { and find the matching closing }
|
|
77
|
+
const startIndex = response.content.indexOf('{');
|
|
78
|
+
if (startIndex !== -1) {
|
|
79
|
+
let braceCount = 0;
|
|
80
|
+
let endIndex = startIndex;
|
|
81
|
+
for (let i = startIndex; i < response.content.length; i++) {
|
|
82
|
+
if (response.content[i] === '{')
|
|
83
|
+
braceCount++;
|
|
84
|
+
if (response.content[i] === '}')
|
|
85
|
+
braceCount--;
|
|
86
|
+
if (braceCount === 0) {
|
|
87
|
+
endIndex = i;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (braceCount === 0) {
|
|
92
|
+
jsonContent = response.content.substring(startIndex, endIndex + 1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const validation = JSON.parse(jsonContent.trim());
|
|
97
|
+
// Validate response structure
|
|
98
|
+
if (typeof validation.isSpecific !== 'boolean' ||
|
|
99
|
+
typeof validation.reason !== 'string' ||
|
|
100
|
+
!Array.isArray(validation.suggestions)) {
|
|
101
|
+
throw new Error('AI response has invalid structure');
|
|
102
|
+
}
|
|
103
|
+
// If intent is not specific enough, throw error with suggestions
|
|
104
|
+
if (!validation.isSpecific) {
|
|
105
|
+
const suggestions = validation.suggestions.length
|
|
106
|
+
? validation.suggestions.map((s) => `• ${s}`).join('\n')
|
|
107
|
+
: '• Include specific technology (Node.js, PostgreSQL, React, etc.)\n• Describe the purpose or function\n• Add context about requirements';
|
|
108
|
+
throw new Error(`Intent needs more specificity: ${validation.reason}\n\n` +
|
|
109
|
+
`Suggestions to improve your intent:\n${suggestions}\n\n` +
|
|
110
|
+
`Original intent: "${intent}"`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
// If it's our validation error, re-throw it
|
|
115
|
+
if (error instanceof Error && error.message.includes('Intent needs more specificity')) {
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
// For other errors (AI service issues, JSON parsing, etc.),
|
|
119
|
+
// continue without blocking the user - log the issue but don't fail
|
|
120
|
+
console.warn('Intent validation failed, continuing with original intent:', error);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate unique solution ID with timestamp and random component
|
|
126
|
+
*/
|
|
127
|
+
function generateSolutionId() {
|
|
128
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '').split('T');
|
|
129
|
+
const dateTime = timestamp[0] + 'T' + timestamp[1].substring(0, 6);
|
|
130
|
+
const randomHex = crypto.randomBytes(6).toString('hex');
|
|
131
|
+
return `sol_${dateTime}_${randomHex}`;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Write solution data to file atomically (temp file + rename)
|
|
135
|
+
*/
|
|
136
|
+
function writeSolutionFile(sessionDir, solutionId, solutionData) {
|
|
137
|
+
const fileName = `${solutionId}.json`;
|
|
138
|
+
const filePath = path.join(sessionDir, fileName);
|
|
139
|
+
const tempPath = filePath + '.tmp';
|
|
140
|
+
try {
|
|
141
|
+
// Write to temporary file first
|
|
142
|
+
fs.writeFileSync(tempPath, JSON.stringify(solutionData, null, 2));
|
|
143
|
+
// Atomically rename to final location
|
|
144
|
+
fs.renameSync(tempPath, filePath);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
// Clean up temp file if it exists
|
|
148
|
+
try {
|
|
149
|
+
if (fs.existsSync(tempPath)) {
|
|
150
|
+
fs.unlinkSync(tempPath);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (cleanupError) {
|
|
154
|
+
// Ignore cleanup errors
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`Failed to write solution file ${fileName}: ${error}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Direct MCP tool handler for recommend functionality
|
|
161
|
+
*/
|
|
162
|
+
async function handleRecommendTool(args, dotAI, logger, requestId) {
|
|
163
|
+
return await error_handling_1.ErrorHandler.withErrorHandling(async () => {
|
|
164
|
+
logger.debug('Handling recommend request', { requestId, intent: args?.intent });
|
|
165
|
+
// Input validation is handled automatically by MCP SDK with Zod schema
|
|
166
|
+
// args are already validated and typed when we reach this point
|
|
167
|
+
// Check for Claude API key
|
|
168
|
+
const claudeApiKey = dotAI.getAnthropicApiKey();
|
|
169
|
+
if (!claudeApiKey) {
|
|
170
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.AI_SERVICE, error_handling_1.ErrorSeverity.HIGH, 'ANTHROPIC_API_KEY environment variable must be set for AI-powered resource recommendations', {
|
|
171
|
+
operation: 'api_key_check',
|
|
172
|
+
component: 'RecommendTool',
|
|
173
|
+
requestId,
|
|
174
|
+
suggestedActions: [
|
|
175
|
+
'Set ANTHROPIC_API_KEY environment variable',
|
|
176
|
+
'Verify the API key is valid and active',
|
|
177
|
+
'Check that the API key has sufficient credits'
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// Validate session directory configuration
|
|
182
|
+
let sessionDir;
|
|
183
|
+
try {
|
|
184
|
+
sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(args, true); // requireWrite=true
|
|
185
|
+
logger.debug('Session directory validated', { requestId, sessionDir });
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Session directory validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, {
|
|
189
|
+
operation: 'session_directory_validation',
|
|
190
|
+
component: 'RecommendTool',
|
|
191
|
+
requestId,
|
|
192
|
+
suggestedActions: [
|
|
193
|
+
'Ensure session directory exists and is writable',
|
|
194
|
+
'Set --session-dir parameter or DOT_AI_SESSION_DIR environment variable',
|
|
195
|
+
'Check directory permissions'
|
|
196
|
+
]
|
|
197
|
+
}, error instanceof Error ? error : new Error(String(error)));
|
|
198
|
+
}
|
|
199
|
+
logger.info('Starting resource recommendation process', {
|
|
200
|
+
requestId,
|
|
201
|
+
intent: args.intent,
|
|
202
|
+
hasApiKey: !!claudeApiKey
|
|
203
|
+
});
|
|
204
|
+
// Validate intent specificity with AI before expensive resource discovery
|
|
205
|
+
logger.debug('Validating intent specificity', { requestId, intent: args.intent });
|
|
206
|
+
try {
|
|
207
|
+
const claudeIntegration = new claude_1.ClaudeIntegration(claudeApiKey);
|
|
208
|
+
await validateIntentWithAI(args.intent, claudeIntegration);
|
|
209
|
+
logger.debug('Intent validation passed', { requestId });
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
if (error instanceof Error && error.message.includes('Intent needs more specificity')) {
|
|
213
|
+
// This is a validation error that should be returned to the user
|
|
214
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.MEDIUM, error.message, {
|
|
215
|
+
operation: 'intent_validation',
|
|
216
|
+
component: 'RecommendTool',
|
|
217
|
+
requestId,
|
|
218
|
+
input: { intent: args.intent },
|
|
219
|
+
suggestedActions: [
|
|
220
|
+
'Provide more specific details about your deployment',
|
|
221
|
+
'Include technology stack information',
|
|
222
|
+
'Describe the purpose or function of what you want to deploy'
|
|
223
|
+
]
|
|
224
|
+
}, error);
|
|
225
|
+
}
|
|
226
|
+
// For other errors, log but continue (don't block user due to AI service issues)
|
|
227
|
+
logger.warn('Intent validation failed, continuing with recommendation', { requestId, error: error instanceof Error ? error.message : 'Unknown error' });
|
|
228
|
+
}
|
|
229
|
+
// Initialize AI-powered ResourceRecommender
|
|
230
|
+
const rankingConfig = { claudeApiKey };
|
|
231
|
+
const recommender = new schema_1.ResourceRecommender(rankingConfig);
|
|
232
|
+
// Create discovery functions
|
|
233
|
+
const discoverResourcesFn = async () => {
|
|
234
|
+
logger.debug('Discovering cluster resources', { requestId });
|
|
235
|
+
return await dotAI.discovery.discoverResources();
|
|
236
|
+
};
|
|
237
|
+
const explainResourceFn = async (resource) => {
|
|
238
|
+
logger.debug(`Explaining resource: ${resource}`, { requestId });
|
|
239
|
+
return await dotAI.discovery.explainResource(resource);
|
|
240
|
+
};
|
|
241
|
+
// Find best solutions for the user intent
|
|
242
|
+
logger.debug('Generating recommendations with AI', { requestId });
|
|
243
|
+
const solutions = await recommender.findBestSolutions(args.intent, discoverResourcesFn, explainResourceFn);
|
|
244
|
+
logger.info('Recommendation process completed', {
|
|
245
|
+
requestId,
|
|
246
|
+
solutionCount: solutions.length,
|
|
247
|
+
topScore: solutions[0]?.score
|
|
248
|
+
});
|
|
249
|
+
// Create solution files and build response
|
|
250
|
+
const solutionSummaries = [];
|
|
251
|
+
const timestamp = new Date().toISOString();
|
|
252
|
+
// Limit to top 5 solutions (respecting quality thresholds from AI ranking)
|
|
253
|
+
const topSolutions = solutions.slice(0, 5);
|
|
254
|
+
for (const solution of topSolutions) {
|
|
255
|
+
const solutionId = generateSolutionId();
|
|
256
|
+
// Create complete solution file with all data
|
|
257
|
+
const solutionFileData = {
|
|
258
|
+
solutionId,
|
|
259
|
+
intent: args.intent,
|
|
260
|
+
type: solution.type,
|
|
261
|
+
score: solution.score,
|
|
262
|
+
description: solution.description,
|
|
263
|
+
reasons: solution.reasons,
|
|
264
|
+
analysis: solution.analysis,
|
|
265
|
+
resources: solution.resources.map(r => ({
|
|
266
|
+
kind: r.kind,
|
|
267
|
+
apiVersion: r.apiVersion,
|
|
268
|
+
group: r.group,
|
|
269
|
+
description: r.description
|
|
270
|
+
})),
|
|
271
|
+
questions: solution.questions,
|
|
272
|
+
answers: {}, // Empty initially - will be filled by answerQuestion tool
|
|
273
|
+
timestamp
|
|
274
|
+
};
|
|
275
|
+
// Write solution to file
|
|
276
|
+
try {
|
|
277
|
+
writeSolutionFile(sessionDir, solutionId, solutionFileData);
|
|
278
|
+
logger.debug('Solution file created', { requestId, solutionId, fileName: `${solutionId}.json` });
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.STORAGE, error_handling_1.ErrorSeverity.HIGH, `Failed to store solution file: ${error instanceof Error ? error.message : 'Unknown error'}`, {
|
|
282
|
+
operation: 'solution_file_creation',
|
|
283
|
+
component: 'RecommendTool',
|
|
284
|
+
requestId,
|
|
285
|
+
input: { solutionId },
|
|
286
|
+
suggestedActions: [
|
|
287
|
+
'Check session directory write permissions',
|
|
288
|
+
'Ensure sufficient disk space',
|
|
289
|
+
'Verify session directory is accessible'
|
|
290
|
+
]
|
|
291
|
+
}, error instanceof Error ? error : new Error(String(error)));
|
|
292
|
+
}
|
|
293
|
+
// Add to response summary (decision-making data only)
|
|
294
|
+
solutionSummaries.push({
|
|
295
|
+
solutionId,
|
|
296
|
+
type: solution.type,
|
|
297
|
+
score: solution.score,
|
|
298
|
+
description: solution.description,
|
|
299
|
+
primaryResources: solution.resources.slice(0, 3).map(r => r.kind),
|
|
300
|
+
reasons: solution.reasons,
|
|
301
|
+
analysis: solution.analysis
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
// Build new response format
|
|
305
|
+
const response = {
|
|
306
|
+
intent: args.intent,
|
|
307
|
+
solutions: solutionSummaries,
|
|
308
|
+
nextAction: "Call chooseSolution with your preferred solutionId",
|
|
309
|
+
guidance: "🛑 NEVER choose automatically - Present ALL solutions to user and ask them to choose by calling chooseSolution(solutionId)",
|
|
310
|
+
timestamp
|
|
311
|
+
};
|
|
312
|
+
logger.info('Solution files created and response prepared', {
|
|
313
|
+
requestId,
|
|
314
|
+
solutionCount: solutionSummaries.length,
|
|
315
|
+
sessionDir
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
content: [{
|
|
319
|
+
type: 'text',
|
|
320
|
+
text: JSON.stringify(response, null, 2)
|
|
321
|
+
}]
|
|
322
|
+
};
|
|
323
|
+
}, {
|
|
324
|
+
operation: 'recommend_tool',
|
|
325
|
+
component: 'RecommendTool',
|
|
326
|
+
requestId,
|
|
327
|
+
input: args
|
|
328
|
+
}, {
|
|
329
|
+
convertToMcp: true,
|
|
330
|
+
retryCount: 1
|
|
331
|
+
});
|
|
332
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vfarcic/dot-ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Universal Kubernetes application deployment agent with CLI and MCP interfaces",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dot-ai": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.js",
|
|
12
|
+
"./mcp": "./dist/mcp/server.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"pretest": "npm run build",
|
|
16
|
+
"test": "jest --silent",
|
|
17
|
+
"test:verbose": "jest --verbose",
|
|
18
|
+
"test:watch": "jest --watch --silent",
|
|
19
|
+
"test:coverage": "jest --coverage --silent",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"prebuild": "npm run clean && npm run lint",
|
|
22
|
+
"build": "tsc --sourceMap false",
|
|
23
|
+
"build:prod": "npm run clean && tsc --sourceMap false --removeComments true",
|
|
24
|
+
"build:dev": "tsc --sourceMap true",
|
|
25
|
+
"build:watch": "tsc --watch",
|
|
26
|
+
"build:cli": "npm run build && chmod +x dist/cli.js",
|
|
27
|
+
"build:mcp": "npm run build && echo 'MCP server built successfully'",
|
|
28
|
+
"dev": "ts-node src/index.ts",
|
|
29
|
+
"start:cli": "node dist/cli.js",
|
|
30
|
+
"start:mcp": "node dist/mcp/server.js",
|
|
31
|
+
"lint": "eslint src/ --ext .ts",
|
|
32
|
+
"format": "prettier --write src/",
|
|
33
|
+
"ci": "npm run lint && npm run ci:build && npm run ci:test && npm run ci:security",
|
|
34
|
+
"ci:test": "npm run test",
|
|
35
|
+
"ci:build": "npm run build:prod",
|
|
36
|
+
"ci:security": "npm audit --audit-level moderate",
|
|
37
|
+
"audit": "npm audit",
|
|
38
|
+
"version:dev": "npm version patch --preid=devel --prerelease",
|
|
39
|
+
"version:prod": "npm version patch"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"kubernetes",
|
|
43
|
+
"deployment",
|
|
44
|
+
"cli",
|
|
45
|
+
"mcp",
|
|
46
|
+
"devops",
|
|
47
|
+
"containers",
|
|
48
|
+
"ai",
|
|
49
|
+
"automation",
|
|
50
|
+
"cloud-native"
|
|
51
|
+
],
|
|
52
|
+
"author": "Viktor Farcic",
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/vfarcic/dot-ai.git"
|
|
57
|
+
},
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/vfarcic/dot-ai/issues"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/vfarcic/dot-ai#readme",
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.0.0"
|
|
67
|
+
},
|
|
68
|
+
"files": [
|
|
69
|
+
"dist",
|
|
70
|
+
"prompts",
|
|
71
|
+
"README.md",
|
|
72
|
+
"LICENSE"
|
|
73
|
+
],
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/jest": "^29.5.0",
|
|
76
|
+
"@types/node": "^20.0.0",
|
|
77
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
78
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
79
|
+
"eslint": "^8.0.0",
|
|
80
|
+
"jest": "^29.5.0",
|
|
81
|
+
"prettier": "^3.0.0",
|
|
82
|
+
"ts-jest": "^29.1.0",
|
|
83
|
+
"ts-node": "^10.9.0",
|
|
84
|
+
"typescript": "^5.0.0"
|
|
85
|
+
},
|
|
86
|
+
"dependencies": {
|
|
87
|
+
"@anthropic-ai/sdk": "^0.27.0",
|
|
88
|
+
"@kubernetes/client-node": "^1.3.0",
|
|
89
|
+
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
90
|
+
"cli-table3": "^0.6.5",
|
|
91
|
+
"commander": "^11.1.0",
|
|
92
|
+
"yaml": "^2.8.0"
|
|
93
|
+
},
|
|
94
|
+
"jest": {
|
|
95
|
+
"preset": "ts-jest",
|
|
96
|
+
"testEnvironment": "node",
|
|
97
|
+
"silent": false,
|
|
98
|
+
"verbose": false,
|
|
99
|
+
"roots": [
|
|
100
|
+
"<rootDir>/src",
|
|
101
|
+
"<rootDir>/tests"
|
|
102
|
+
],
|
|
103
|
+
"testMatch": [
|
|
104
|
+
"**/__tests__/**/*.ts",
|
|
105
|
+
"**/*.test.ts",
|
|
106
|
+
"**/*.spec.ts"
|
|
107
|
+
],
|
|
108
|
+
"collectCoverageFrom": [
|
|
109
|
+
"src/**/*.ts",
|
|
110
|
+
"!src/**/*.d.ts",
|
|
111
|
+
"!src/**/index.ts"
|
|
112
|
+
],
|
|
113
|
+
"coverageDirectory": "coverage",
|
|
114
|
+
"coverageReporters": [
|
|
115
|
+
"text",
|
|
116
|
+
"lcov",
|
|
117
|
+
"html"
|
|
118
|
+
],
|
|
119
|
+
"moduleNameMapper": {
|
|
120
|
+
"@kubernetes/client-node": "<rootDir>/tests/__mocks__/@kubernetes/client-node.ts"
|
|
121
|
+
},
|
|
122
|
+
"setupFilesAfterEnv": ["<rootDir>/tests/setup.ts"]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Intent Validation for Kubernetes Deployment Recommendations
|
|
2
|
+
|
|
3
|
+
You are a Kubernetes expert analyzing user intents for deployment recommendations. Evaluate whether this intent provides enough specificity to generate meaningful Kubernetes deployment recommendations.
|
|
4
|
+
|
|
5
|
+
## User Intent
|
|
6
|
+
{intent}
|
|
7
|
+
|
|
8
|
+
## Evaluation Criteria
|
|
9
|
+
|
|
10
|
+
An intent is **TOO VAGUE OR GENERIC** if it:
|
|
11
|
+
- Completely generic terms without context (e.g., just "app", "something", "service" alone)
|
|
12
|
+
- Single action words without objects (e.g., "deploy", "create", "setup", "run" alone)
|
|
13
|
+
- Meaningless requests (e.g., "help", "please", "can you")
|
|
14
|
+
- Extremely short without any technical context (1-2 words like "database", "container")
|
|
15
|
+
|
|
16
|
+
An intent is **SPECIFIC ENOUGH** if it includes ANY of:
|
|
17
|
+
- Specific technology or framework (Node.js, PostgreSQL, Redis, React, Python, etc.)
|
|
18
|
+
- Clear architectural pattern (stateless app, microservice, web server, REST API, etc.)
|
|
19
|
+
- Application type or purpose (frontend, backend, database, cache, queue, etc.)
|
|
20
|
+
- Deployment context that helps understand Kubernetes resource needs
|
|
21
|
+
|
|
22
|
+
**IMPORTANT**: Be generous in accepting intents. Focus on rejecting only truly meaningless requests. Terms like "stateless app", "web application", "microservice", "database cluster" provide sufficient Kubernetes deployment context.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
**TOO VAGUE:**
|
|
27
|
+
- "create an app" (no architectural or technical context)
|
|
28
|
+
- "deploy something" (completely generic)
|
|
29
|
+
- "database" (single word, no context)
|
|
30
|
+
- "help" (not a deployment request)
|
|
31
|
+
- "app" (single word)
|
|
32
|
+
- "setup" (action without object)
|
|
33
|
+
|
|
34
|
+
**SPECIFIC ENOUGH:**
|
|
35
|
+
- "stateless app" (architectural pattern + type)
|
|
36
|
+
- "web application" (clear application type)
|
|
37
|
+
- "microservice" (architectural pattern)
|
|
38
|
+
- "REST API" (clear service type)
|
|
39
|
+
- "database cluster" (type + deployment pattern)
|
|
40
|
+
- "Node.js application" (technology + type)
|
|
41
|
+
- "frontend service" (purpose + type)
|
|
42
|
+
- "cache service" (purpose + type)
|
|
43
|
+
|
|
44
|
+
## Response Format
|
|
45
|
+
|
|
46
|
+
Respond with ONLY a JSON object in this exact format:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"isSpecific": boolean,
|
|
51
|
+
"reason": "brief explanation of why it is or isn't specific enough",
|
|
52
|
+
"suggestions": [
|
|
53
|
+
"specific suggestion 1 to improve the intent",
|
|
54
|
+
"specific suggestion 2 to improve the intent",
|
|
55
|
+
"specific suggestion 3 to improve the intent"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**IMPORTANT:**
|
|
61
|
+
- Your response must be ONLY the JSON object, nothing else
|
|
62
|
+
- If `isSpecific` is true, still provide 3 suggestions for making it even more detailed
|
|
63
|
+
- If `isSpecific` is false, provide 3 concrete suggestions to make it deployable
|
|
64
|
+
- Keep suggestions practical and actionable
|
|
65
|
+
- Focus on what technologies, purposes, or contexts are missing
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Kubernetes Manifest Generation
|
|
2
|
+
|
|
3
|
+
## Solution Configuration
|
|
4
|
+
{solution}
|
|
5
|
+
|
|
6
|
+
## Resource Schemas
|
|
7
|
+
The following schemas are available for the resources selected in the solution:
|
|
8
|
+
{schemas}
|
|
9
|
+
|
|
10
|
+
## Previous Attempt (if retry)
|
|
11
|
+
{previous_attempt}
|
|
12
|
+
|
|
13
|
+
## Validation Error Details (if retry)
|
|
14
|
+
{error_details}
|
|
15
|
+
|
|
16
|
+
## Instructions
|
|
17
|
+
|
|
18
|
+
Generate production-ready Kubernetes YAML manifests from the complete solution configuration. The solution contains all necessary context including discovered resource schemas, user answers, cluster capabilities, and selected resource types.
|
|
19
|
+
|
|
20
|
+
### Core Strategy:
|
|
21
|
+
|
|
22
|
+
1. **Analyze Solution Data**:
|
|
23
|
+
- Use the selected resource types from the solution
|
|
24
|
+
- Review discovered resource schemas and cluster capabilities
|
|
25
|
+
- Understand the complete context of what's available
|
|
26
|
+
|
|
27
|
+
2. **Apply User Configuration**:
|
|
28
|
+
- Map all question answers to appropriate manifest fields
|
|
29
|
+
- **CRITICAL**: Use ONLY the fields defined in the provided resource schemas
|
|
30
|
+
- Do not invent or guess field names - refer to the schema section above
|
|
31
|
+
- Apply configuration values appropriately for the specific resource type
|
|
32
|
+
|
|
33
|
+
3. **Cross-Resource Field Mapping**:
|
|
34
|
+
- Many user answers apply to multiple resources in the solution
|
|
35
|
+
- Use the same values consistently across related resources where appropriate
|
|
36
|
+
- Ensure proper relationships between resources through consistent naming and labeling
|
|
37
|
+
- Apply configuration values to all relevant fields across different resource types
|
|
38
|
+
|
|
39
|
+
4. **Process Open Requirements**:
|
|
40
|
+
- If user provided open requirements, analyze their specific needs
|
|
41
|
+
- Use available cluster resources to fulfill those requirements intelligently
|
|
42
|
+
- Make enhancement decisions based on actual cluster capabilities
|
|
43
|
+
- **CRITICAL**: Add any additional resources needed to fulfill open requirements:
|
|
44
|
+
* **Hostname/Domain access** → Add Ingress resource with appropriate rules
|
|
45
|
+
* **External configuration** → Add ConfigMap resources
|
|
46
|
+
* **Secrets/credentials** → Add Secret resources
|
|
47
|
+
* **SSL/TLS requirements** → Add TLS configuration to Ingress
|
|
48
|
+
* **Persistent storage needs** → Add PersistentVolumeClaim resources
|
|
49
|
+
* **Network policies** → Add NetworkPolicy resources
|
|
50
|
+
* **Resource limits** → Add ResourceQuota or LimitRange resources
|
|
51
|
+
|
|
52
|
+
5. **Generate Appropriate Manifests**:
|
|
53
|
+
- Create manifests for the selected resource types
|
|
54
|
+
- **IMPORTANT**: Include any additional supporting resources needed to fulfill open requirements
|
|
55
|
+
- Use correct API versions and schemas from the solution data
|
|
56
|
+
- Ensure all resources work together to meet user's complete requirements
|
|
57
|
+
|
|
58
|
+
### For Retry Attempts:
|
|
59
|
+
If this is a retry (previous attempt and error details provided above):
|
|
60
|
+
- Analyze the previous manifests to understand what was generated
|
|
61
|
+
- Study the validation error to identify the specific problem
|
|
62
|
+
- Make targeted corrections to fix the identified issues
|
|
63
|
+
- Preserve parts of the manifests that didn't cause validation errors
|
|
64
|
+
|
|
65
|
+
### Response Requirements:
|
|
66
|
+
|
|
67
|
+
1. **Use Solution Context**: Base all decisions on the provided solution data, not assumptions
|
|
68
|
+
2. **Valid Manifests**: Generate syntactically correct YAML that passes Kubernetes validation
|
|
69
|
+
3. **Complete Configuration**: Include all resources needed for deployment
|
|
70
|
+
4. **Production Ready**: Follow Kubernetes best practices for the specific resource types
|
|
71
|
+
5. **Error-Free**: If this is a retry, specifically address the validation errors
|
|
72
|
+
|
|
73
|
+
## Response Format
|
|
74
|
+
|
|
75
|
+
**CRITICAL**: Return ONLY valid YAML manifests. NO explanations, NO markdown blocks, NO additional text.
|
|
76
|
+
|
|
77
|
+
Separate multiple resources with `---`.
|
|
78
|
+
|
|
79
|
+
**RETURN ONLY THE YAML MANIFESTS**
|