@vfarcic/dot-ai 0.4.9 → 0.5.1

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.
Files changed (145) hide show
  1. package/.claude/commands/context-load.md +11 -0
  2. package/.claude/commands/context-save.md +16 -0
  3. package/.claude/commands/prd-done.md +115 -0
  4. package/.claude/commands/prd-get.md +25 -0
  5. package/.claude/commands/prd-start.md +87 -0
  6. package/.claude/commands/task-done.md +77 -0
  7. package/.claude/commands/tests-reminder.md +32 -0
  8. package/.claude/settings.local.json +20 -0
  9. package/.eslintrc.json +25 -0
  10. package/.github/workflows/ci.yml +170 -0
  11. package/.prettierrc.json +10 -0
  12. package/.teller.yml +8 -0
  13. package/CLAUDE.md +162 -0
  14. package/assets/images/logo.png +0 -0
  15. package/bin/dot-ai.ts +47 -0
  16. package/destroy.sh +45 -0
  17. package/devbox.json +13 -0
  18. package/devbox.lock +225 -0
  19. package/docs/API.md +449 -0
  20. package/docs/CONTEXT.md +49 -0
  21. package/docs/DEVELOPMENT.md +203 -0
  22. package/docs/NEXT_STEPS.md +97 -0
  23. package/docs/STAGE_BASED_API.md +97 -0
  24. package/docs/cli-guide.md +798 -0
  25. package/docs/design.md +750 -0
  26. package/docs/discovery-engine.md +515 -0
  27. package/docs/error-handling.md +429 -0
  28. package/docs/function-registration.md +157 -0
  29. package/docs/mcp-guide.md +416 -0
  30. package/package.json +2 -123
  31. package/renovate.json +51 -0
  32. package/setup.sh +111 -0
  33. package/{dist/cli.js → src/cli.ts} +26 -19
  34. package/src/core/claude.ts +280 -0
  35. package/src/core/deploy-operation.ts +127 -0
  36. package/src/core/discovery.ts +900 -0
  37. package/src/core/error-handling.ts +562 -0
  38. package/src/core/index.ts +143 -0
  39. package/src/core/kubernetes-utils.ts +218 -0
  40. package/src/core/memory.ts +148 -0
  41. package/src/core/schema.ts +830 -0
  42. package/src/core/session-utils.ts +97 -0
  43. package/src/core/workflow.ts +234 -0
  44. package/src/index.ts +18 -0
  45. package/src/interfaces/cli.ts +872 -0
  46. package/src/interfaces/mcp.ts +183 -0
  47. package/src/mcp/server.ts +131 -0
  48. package/src/tools/answer-question.ts +807 -0
  49. package/src/tools/choose-solution.ts +169 -0
  50. package/src/tools/deploy-manifests.ts +94 -0
  51. package/src/tools/generate-manifests.ts +502 -0
  52. package/src/tools/index.ts +41 -0
  53. package/src/tools/recommend.ts +370 -0
  54. package/tests/__mocks__/@kubernetes/client-node.ts +106 -0
  55. package/tests/build-system.test.ts +345 -0
  56. package/tests/configuration.test.ts +226 -0
  57. package/tests/core/deploy-operation.test.ts +38 -0
  58. package/tests/core/discovery.test.ts +1648 -0
  59. package/tests/core/error-handling.test.ts +632 -0
  60. package/tests/core/schema.test.ts +1658 -0
  61. package/tests/core/session-utils.test.ts +245 -0
  62. package/tests/core.test.ts +439 -0
  63. package/tests/fixtures/configmap-no-labels.yaml +8 -0
  64. package/tests/fixtures/crossplane-app-configuration.yaml +6 -0
  65. package/tests/fixtures/crossplane-providers.yaml +45 -0
  66. package/tests/fixtures/crossplane-rbac.yaml +48 -0
  67. package/tests/fixtures/invalid-configmap.yaml +8 -0
  68. package/tests/fixtures/invalid-deployment.yaml +17 -0
  69. package/tests/fixtures/test-deployment.yaml +28 -0
  70. package/tests/fixtures/valid-configmap.yaml +15 -0
  71. package/tests/infrastructure.test.ts +426 -0
  72. package/tests/interfaces/cli.test.ts +1036 -0
  73. package/tests/interfaces/mcp.test.ts +139 -0
  74. package/tests/kubernetes-utils.test.ts +200 -0
  75. package/tests/mcp/server.test.ts +126 -0
  76. package/tests/setup.ts +31 -0
  77. package/tests/tools/answer-question.test.ts +367 -0
  78. package/tests/tools/choose-solution.test.ts +481 -0
  79. package/tests/tools/deploy-manifests.test.ts +185 -0
  80. package/tests/tools/generate-manifests.test.ts +441 -0
  81. package/tests/tools/index.test.ts +111 -0
  82. package/tests/tools/recommend.test.ts +180 -0
  83. package/tsconfig.json +34 -0
  84. package/dist/cli.d.ts +0 -3
  85. package/dist/cli.d.ts.map +0 -1
  86. package/dist/core/claude.d.ts +0 -42
  87. package/dist/core/claude.d.ts.map +0 -1
  88. package/dist/core/claude.js +0 -229
  89. package/dist/core/deploy-operation.d.ts +0 -38
  90. package/dist/core/deploy-operation.d.ts.map +0 -1
  91. package/dist/core/deploy-operation.js +0 -101
  92. package/dist/core/discovery.d.ts +0 -162
  93. package/dist/core/discovery.d.ts.map +0 -1
  94. package/dist/core/discovery.js +0 -758
  95. package/dist/core/error-handling.d.ts +0 -167
  96. package/dist/core/error-handling.d.ts.map +0 -1
  97. package/dist/core/error-handling.js +0 -399
  98. package/dist/core/index.d.ts +0 -42
  99. package/dist/core/index.d.ts.map +0 -1
  100. package/dist/core/index.js +0 -123
  101. package/dist/core/kubernetes-utils.d.ts +0 -38
  102. package/dist/core/kubernetes-utils.d.ts.map +0 -1
  103. package/dist/core/kubernetes-utils.js +0 -177
  104. package/dist/core/memory.d.ts +0 -45
  105. package/dist/core/memory.d.ts.map +0 -1
  106. package/dist/core/memory.js +0 -113
  107. package/dist/core/schema.d.ts +0 -187
  108. package/dist/core/schema.d.ts.map +0 -1
  109. package/dist/core/schema.js +0 -655
  110. package/dist/core/session-utils.d.ts +0 -29
  111. package/dist/core/session-utils.d.ts.map +0 -1
  112. package/dist/core/session-utils.js +0 -121
  113. package/dist/core/workflow.d.ts +0 -70
  114. package/dist/core/workflow.d.ts.map +0 -1
  115. package/dist/core/workflow.js +0 -161
  116. package/dist/index.d.ts +0 -15
  117. package/dist/index.d.ts.map +0 -1
  118. package/dist/index.js +0 -32
  119. package/dist/interfaces/cli.d.ts +0 -74
  120. package/dist/interfaces/cli.d.ts.map +0 -1
  121. package/dist/interfaces/cli.js +0 -769
  122. package/dist/interfaces/mcp.d.ts +0 -30
  123. package/dist/interfaces/mcp.d.ts.map +0 -1
  124. package/dist/interfaces/mcp.js +0 -105
  125. package/dist/mcp/server.d.ts +0 -9
  126. package/dist/mcp/server.d.ts.map +0 -1
  127. package/dist/mcp/server.js +0 -151
  128. package/dist/tools/answer-question.d.ts +0 -27
  129. package/dist/tools/answer-question.d.ts.map +0 -1
  130. package/dist/tools/answer-question.js +0 -696
  131. package/dist/tools/choose-solution.d.ts +0 -23
  132. package/dist/tools/choose-solution.d.ts.map +0 -1
  133. package/dist/tools/choose-solution.js +0 -171
  134. package/dist/tools/deploy-manifests.d.ts +0 -25
  135. package/dist/tools/deploy-manifests.d.ts.map +0 -1
  136. package/dist/tools/deploy-manifests.js +0 -74
  137. package/dist/tools/generate-manifests.d.ts +0 -23
  138. package/dist/tools/generate-manifests.d.ts.map +0 -1
  139. package/dist/tools/generate-manifests.js +0 -424
  140. package/dist/tools/index.d.ts +0 -11
  141. package/dist/tools/index.d.ts.map +0 -1
  142. package/dist/tools/index.js +0 -34
  143. package/dist/tools/recommend.d.ts +0 -23
  144. package/dist/tools/recommend.d.ts.map +0 -1
  145. package/dist/tools/recommend.js +0 -332
@@ -1,332 +0,0 @@
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
- }