@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
@@ -0,0 +1,562 @@
1
+ /**
2
+ * Comprehensive Error Handling System for DevOps AI Toolkit
3
+ *
4
+ * Provides centralized error handling, logging, and context management
5
+ * with support for MCP protocol, CLI operations, and core functionality.
6
+ */
7
+
8
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
9
+
10
+ /**
11
+ * Error categories for systematic error classification
12
+ */
13
+ export enum ErrorCategory {
14
+ // Infrastructure errors
15
+ KUBERNETES = 'kubernetes',
16
+ NETWORK = 'network',
17
+ AUTHENTICATION = 'authentication',
18
+ AUTHORIZATION = 'authorization',
19
+
20
+ // Application errors
21
+ VALIDATION = 'validation',
22
+ CONFIGURATION = 'configuration',
23
+ OPERATION = 'operation',
24
+
25
+ // External service errors
26
+ AI_SERVICE = 'ai_service',
27
+ STORAGE = 'storage',
28
+
29
+ // Protocol errors
30
+ MCP_PROTOCOL = 'mcp_protocol',
31
+ CLI_INTERFACE = 'cli_interface',
32
+
33
+ // System errors
34
+ INTERNAL = 'internal',
35
+ UNKNOWN = 'unknown'
36
+ }
37
+
38
+ /**
39
+ * Error severity levels
40
+ */
41
+ export enum ErrorSeverity {
42
+ LOW = 'low', // Non-critical, operation can continue
43
+ MEDIUM = 'medium', // Important but recoverable
44
+ HIGH = 'high', // Significant impact, requires attention
45
+ CRITICAL = 'critical' // System-threatening, immediate action required
46
+ }
47
+
48
+ /**
49
+ * Error context interface for comprehensive error tracking
50
+ */
51
+ export interface ErrorContext {
52
+ // Operation details
53
+ operation: string;
54
+ component: string;
55
+
56
+ // User context
57
+ userId?: string;
58
+ sessionId?: string;
59
+ requestId?: string;
60
+
61
+ // Technical context
62
+ timestamp: Date;
63
+ version: string;
64
+
65
+ // Input context
66
+ input?: any;
67
+ parameters?: Record<string, any>;
68
+
69
+ // Stack trace and debugging
70
+ originalError?: Error;
71
+ stackTrace?: string;
72
+
73
+ // Recovery information
74
+ suggestedActions?: string[];
75
+ isRetryable?: boolean;
76
+ retryCount?: number;
77
+ }
78
+
79
+ /**
80
+ * Structured error interface
81
+ */
82
+ export interface AppError {
83
+ // Core identification
84
+ id: string;
85
+ code: string;
86
+ category: ErrorCategory;
87
+ severity: ErrorSeverity;
88
+
89
+ // User-facing information
90
+ message: string;
91
+ userMessage?: string;
92
+ technicalDetails?: string;
93
+
94
+ // Context and debugging
95
+ context: ErrorContext;
96
+
97
+ // Timing
98
+ timestamp: Date;
99
+
100
+ // Recovery guidance
101
+ suggestedActions: string[];
102
+ isRetryable: boolean;
103
+
104
+ // Chaining
105
+ cause?: AppError;
106
+ }
107
+
108
+ /**
109
+ * Log levels for structured logging
110
+ */
111
+ export enum LogLevel {
112
+ DEBUG = 'debug',
113
+ INFO = 'info',
114
+ WARN = 'warn',
115
+ ERROR = 'error',
116
+ FATAL = 'fatal'
117
+ }
118
+
119
+ /**
120
+ * Structured log entry interface
121
+ */
122
+ export interface LogEntry {
123
+ level: LogLevel;
124
+ timestamp: Date;
125
+ message: string;
126
+ component: string;
127
+ operation?: string;
128
+ requestId?: string;
129
+ sessionId?: string;
130
+ data?: any;
131
+ error?: AppError;
132
+ duration?: number;
133
+ }
134
+
135
+ /**
136
+ * Logger interface for dependency injection
137
+ */
138
+ export interface Logger {
139
+ debug(message: string, data?: any): void;
140
+ info(message: string, data?: any): void;
141
+ warn(message: string, data?: any): void;
142
+ error(message: string, error?: Error | AppError, data?: any): void;
143
+ fatal(message: string, error?: Error | AppError, data?: any): void;
144
+ }
145
+
146
+ /**
147
+ * Default console logger implementation
148
+ */
149
+ export class ConsoleLogger implements Logger {
150
+ private component: string;
151
+ private minLevel: LogLevel;
152
+
153
+ constructor(component: string, minLevel: LogLevel = LogLevel.INFO) {
154
+ this.component = component;
155
+ this.minLevel = minLevel;
156
+ }
157
+
158
+ private shouldLog(level: LogLevel): boolean {
159
+ const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL];
160
+ return levels.indexOf(level) >= levels.indexOf(this.minLevel);
161
+ }
162
+
163
+ private formatMessage(level: LogLevel, message: string, data?: any): string {
164
+ const timestamp = new Date().toISOString();
165
+ const baseMessage = `[${timestamp}] ${level.toUpperCase()} [${this.component}] ${message}`;
166
+
167
+ if (data) {
168
+ return `${baseMessage} ${JSON.stringify(data, null, 2)}`;
169
+ }
170
+
171
+ return baseMessage;
172
+ }
173
+
174
+ debug(message: string, data?: any): void {
175
+ if (this.shouldLog(LogLevel.DEBUG)) {
176
+ console.debug(this.formatMessage(LogLevel.DEBUG, message, data));
177
+ }
178
+ }
179
+
180
+ info(message: string, data?: any): void {
181
+ if (this.shouldLog(LogLevel.INFO)) {
182
+ console.info(this.formatMessage(LogLevel.INFO, message, data));
183
+ }
184
+ }
185
+
186
+ warn(message: string, data?: any): void {
187
+ if (this.shouldLog(LogLevel.WARN)) {
188
+ console.warn(this.formatMessage(LogLevel.WARN, message, data));
189
+ }
190
+ }
191
+
192
+ error(message: string, error?: Error | AppError, data?: any): void {
193
+ if (this.shouldLog(LogLevel.ERROR)) {
194
+ const errorData = error ? { error: this.serializeError(error), ...data } : data;
195
+ console.error(this.formatMessage(LogLevel.ERROR, message, errorData));
196
+ }
197
+ }
198
+
199
+ fatal(message: string, error?: Error | AppError, data?: any): void {
200
+ if (this.shouldLog(LogLevel.FATAL)) {
201
+ const errorData = error ? { error: this.serializeError(error), ...data } : data;
202
+ console.error(this.formatMessage(LogLevel.FATAL, message, errorData));
203
+ }
204
+ }
205
+
206
+ private serializeError(error: Error | AppError): any {
207
+ if ('category' in error) {
208
+ // AppError
209
+ return {
210
+ id: error.id,
211
+ code: error.code,
212
+ category: error.category,
213
+ severity: error.severity,
214
+ message: error.message,
215
+ context: error.context
216
+ };
217
+ } else {
218
+ // Native Error
219
+ return {
220
+ name: error.name,
221
+ message: error.message,
222
+ stack: error.stack
223
+ };
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Error handler factory and utilities
230
+ */
231
+ export class ErrorHandler {
232
+ private static requestIdCounter = 0;
233
+ private static logger: Logger = new ConsoleLogger('ErrorHandler');
234
+
235
+ /**
236
+ * Set custom logger implementation
237
+ */
238
+ static setLogger(logger: Logger): void {
239
+ this.logger = logger;
240
+ }
241
+
242
+ /**
243
+ * Generate unique request ID
244
+ */
245
+ static generateRequestId(): string {
246
+ return `req_${Date.now()}_${++this.requestIdCounter}`;
247
+ }
248
+
249
+ /**
250
+ * Create comprehensive AppError from various error sources
251
+ */
252
+ static createError(
253
+ category: ErrorCategory,
254
+ severity: ErrorSeverity,
255
+ message: string,
256
+ context: Partial<ErrorContext>,
257
+ originalError?: Error
258
+ ): AppError {
259
+ const errorId = `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
260
+ const timestamp = new Date();
261
+
262
+ const fullContext: ErrorContext = {
263
+ operation: context.operation || 'unknown',
264
+ component: context.component || 'unknown',
265
+ timestamp,
266
+ version: process.env.npm_package_version || '0.1.0',
267
+ originalError,
268
+ stackTrace: originalError?.stack || new Error().stack,
269
+ isRetryable: context.isRetryable || false,
270
+ retryCount: context.retryCount || 0,
271
+ ...context
272
+ };
273
+
274
+ const suggestedActions = context.suggestedActions || this.getDefaultSuggestedActions(category);
275
+
276
+ const appError: AppError = {
277
+ id: errorId,
278
+ code: this.generateErrorCode(category, severity),
279
+ category,
280
+ severity,
281
+ message,
282
+ userMessage: this.getUserFriendlyMessage(category),
283
+ technicalDetails: originalError?.message,
284
+ context: fullContext,
285
+ timestamp,
286
+ suggestedActions,
287
+ isRetryable: fullContext.isRetryable || false,
288
+ // Don't wrap the original error to prevent circular references
289
+ cause: undefined
290
+ };
291
+
292
+ // Log the error
293
+ this.logger.error(`Error created: ${message}`, appError, {
294
+ category,
295
+ severity,
296
+ operation: fullContext.operation,
297
+ component: fullContext.component
298
+ });
299
+
300
+ return appError;
301
+ }
302
+
303
+ /**
304
+ * Convert AppError to McpError for MCP protocol
305
+ */
306
+ static toMcpError(appError: AppError): McpError {
307
+ const errorCode = this.mapToMcpErrorCode(appError.category);
308
+ const message = `${appError.message}${appError.technicalDetails ? ` - ${appError.technicalDetails}` : ''}`;
309
+
310
+ return new McpError(errorCode, message);
311
+ }
312
+
313
+ /**
314
+ * Handle error with automatic logging and context enhancement
315
+ */
316
+ static handleError(
317
+ error: Error | AppError,
318
+ context: Partial<ErrorContext>,
319
+ options: {
320
+ rethrow?: boolean;
321
+ convertToMcp?: boolean;
322
+ logLevel?: LogLevel;
323
+ } = {}
324
+ ): AppError | McpError {
325
+ let appError: AppError;
326
+
327
+ if ('category' in error) {
328
+ // Already an AppError
329
+ appError = error;
330
+ } else {
331
+ // Convert native Error to AppError
332
+ appError = this.createError(
333
+ this.categorizeError(error),
334
+ this.assessSeverity(error),
335
+ error.message,
336
+ context,
337
+ error
338
+ );
339
+ }
340
+
341
+ // Log the handled error
342
+ const logLevel = options.logLevel || LogLevel.ERROR;
343
+ this.logger[logLevel](`Error handled in ${context.component || 'unknown'}`, appError);
344
+
345
+ if (options.convertToMcp) {
346
+ const mcpError = this.toMcpError(appError);
347
+ if (options.rethrow) {
348
+ throw mcpError;
349
+ }
350
+ return mcpError;
351
+ }
352
+
353
+ if (options.rethrow) {
354
+ throw appError;
355
+ }
356
+
357
+ return appError;
358
+ }
359
+
360
+ /**
361
+ * Wrap operation with error handling
362
+ */
363
+ static async withErrorHandling<T>(
364
+ operation: () => Promise<T>,
365
+ context: Partial<ErrorContext>,
366
+ options: {
367
+ retryCount?: number;
368
+ convertToMcp?: boolean;
369
+ } = {}
370
+ ): Promise<T> {
371
+ const maxRetries = options.retryCount || 0;
372
+ let lastError: Error;
373
+
374
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
375
+ try {
376
+ this.logger.debug(`Executing operation: ${context.operation}`, {
377
+ attempt: attempt + 1,
378
+ maxRetries: maxRetries + 1
379
+ });
380
+
381
+ return await operation();
382
+ } catch (error) {
383
+ lastError = error as Error;
384
+
385
+ const enhancedContext = {
386
+ ...context,
387
+ retryCount: attempt,
388
+ isRetryable: attempt < maxRetries
389
+ };
390
+
391
+ const appError = this.handleError(lastError, enhancedContext, {
392
+ logLevel: attempt < maxRetries ? LogLevel.WARN : LogLevel.ERROR
393
+ }) as AppError;
394
+
395
+ // Retry if we haven't exceeded max retries and the error is retryable
396
+ // For retry logic, we consider errors retryable by default unless explicitly marked as not retryable
397
+ const shouldRetry = attempt < maxRetries && (appError.isRetryable || enhancedContext.isRetryable);
398
+
399
+ if (shouldRetry) {
400
+ this.logger.info(`Retrying operation: ${context.operation}`, {
401
+ attempt: attempt + 1,
402
+ maxRetries: maxRetries + 1,
403
+ reason: appError.message
404
+ });
405
+ continue;
406
+ }
407
+
408
+ // Final attempt failed or not retryable
409
+ if (options.convertToMcp) {
410
+ throw this.toMcpError(appError);
411
+ }
412
+ throw appError;
413
+ }
414
+ }
415
+
416
+ // This should never be reached, but TypeScript requires it
417
+ throw lastError!;
418
+ }
419
+
420
+ private static generateErrorCode(category: ErrorCategory, severity: ErrorSeverity): string {
421
+ const categoryCode = category.toUpperCase().replace('_', '');
422
+ const severityCode = severity.charAt(0).toUpperCase();
423
+ const timestamp = Date.now().toString().slice(-6);
424
+ const random = Math.random().toString(36).substring(2, 5);
425
+ return `${categoryCode}_${severityCode}_${timestamp}_${random}`;
426
+ }
427
+
428
+ private static mapToMcpErrorCode(category: ErrorCategory): ErrorCode {
429
+ switch (category) {
430
+ case ErrorCategory.VALIDATION:
431
+ return ErrorCode.InvalidParams;
432
+ case ErrorCategory.AUTHENTICATION:
433
+ case ErrorCategory.AUTHORIZATION:
434
+ return ErrorCode.InvalidParams;
435
+ case ErrorCategory.MCP_PROTOCOL:
436
+ return ErrorCode.MethodNotFound;
437
+ case ErrorCategory.OPERATION:
438
+ case ErrorCategory.CLI_INTERFACE:
439
+ return ErrorCode.InvalidRequest;
440
+ default:
441
+ return ErrorCode.InternalError;
442
+ }
443
+ }
444
+
445
+ private static categorizeError(error: Error): ErrorCategory {
446
+ const message = error.message.toLowerCase();
447
+
448
+ if (message.includes('kubeconfig') || message.includes('kubernetes')) {
449
+ return ErrorCategory.KUBERNETES;
450
+ }
451
+ if (message.includes('network') || message.includes('connection')) {
452
+ return ErrorCategory.NETWORK;
453
+ }
454
+ if (message.includes('authentication') || message.includes('unauthorized')) {
455
+ return ErrorCategory.AUTHENTICATION;
456
+ }
457
+ if (message.includes('anthropic') || message.includes('ai') || message.includes('claude') || message.includes('api key invalid')) {
458
+ return ErrorCategory.AI_SERVICE;
459
+ }
460
+ if (message.includes('validation') || message.includes('invalid')) {
461
+ return ErrorCategory.VALIDATION;
462
+ }
463
+
464
+ return ErrorCategory.UNKNOWN;
465
+ }
466
+
467
+ private static assessSeverity(error: Error): ErrorSeverity {
468
+ const message = error.message.toLowerCase();
469
+
470
+ if (message.includes('critical') || message.includes('fatal')) {
471
+ return ErrorSeverity.CRITICAL;
472
+ }
473
+ if (message.includes('authentication') || message.includes('authorization')) {
474
+ return ErrorSeverity.HIGH;
475
+ }
476
+ if (message.includes('validation') || message.includes('invalid')) {
477
+ return ErrorSeverity.MEDIUM;
478
+ }
479
+
480
+ return ErrorSeverity.LOW;
481
+ }
482
+
483
+ private static getUserFriendlyMessage(category: ErrorCategory): string {
484
+ switch (category) {
485
+ case ErrorCategory.KUBERNETES:
486
+ return 'Unable to connect to Kubernetes cluster. Please check your kubeconfig and cluster connectivity.';
487
+ case ErrorCategory.AUTHENTICATION:
488
+ return 'Authentication failed. Please verify your credentials.';
489
+ case ErrorCategory.VALIDATION:
490
+ return 'Input validation failed. Please check your parameters and try again.';
491
+ case ErrorCategory.AI_SERVICE:
492
+ return 'AI service is temporarily unavailable. Please try again later.';
493
+ default:
494
+ return 'An unexpected error occurred. Please try again or contact support.';
495
+ }
496
+ }
497
+
498
+ private static getDefaultSuggestedActions(category: ErrorCategory): string[] {
499
+ switch (category) {
500
+ case ErrorCategory.KUBERNETES:
501
+ return [
502
+ 'Verify kubeconfig file exists and is valid',
503
+ 'Check cluster connectivity with kubectl cluster-info',
504
+ 'Ensure proper authentication credentials'
505
+ ];
506
+ case ErrorCategory.VALIDATION:
507
+ return [
508
+ 'Review input parameters for correct format',
509
+ 'Check required fields are provided',
510
+ 'Verify data types match expected schema'
511
+ ];
512
+ case ErrorCategory.AI_SERVICE:
513
+ return [
514
+ 'Check ANTHROPIC_API_KEY environment variable',
515
+ 'Verify API key is valid and has sufficient credits',
516
+ 'Try again after a short delay'
517
+ ];
518
+ default:
519
+ return [
520
+ 'Try the operation again',
521
+ 'Check system logs for more details',
522
+ 'Contact support if problem persists'
523
+ ];
524
+ }
525
+ }
526
+
527
+ private static wrapNativeError(error: Error): AppError {
528
+ const errorId = `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
529
+ const timestamp = new Date();
530
+ const category = this.categorizeError(error);
531
+ const severity = this.assessSeverity(error);
532
+
533
+ const context: ErrorContext = {
534
+ operation: 'error_wrapping',
535
+ component: 'ErrorHandler',
536
+ timestamp,
537
+ version: process.env.npm_package_version || '0.1.0',
538
+ originalError: error,
539
+ stackTrace: error.stack,
540
+ isRetryable: false,
541
+ retryCount: 0
542
+ };
543
+
544
+ const appError: AppError = {
545
+ id: errorId,
546
+ code: this.generateErrorCode(category, severity),
547
+ category,
548
+ severity,
549
+ message: error.message,
550
+ userMessage: this.getUserFriendlyMessage(category),
551
+ technicalDetails: error.message,
552
+ context,
553
+ timestamp,
554
+ suggestedActions: this.getDefaultSuggestedActions(category),
555
+ isRetryable: false,
556
+ // No cause to prevent circular reference
557
+ cause: undefined
558
+ };
559
+
560
+ return appError;
561
+ }
562
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Core Intelligence Module
3
+ *
4
+ * Shared intelligence for both CLI and MCP interfaces
5
+ */
6
+
7
+ import { KubernetesDiscovery } from './discovery';
8
+ import { MemorySystem } from './memory';
9
+ import { WorkflowEngine } from './workflow';
10
+ import { ClaudeIntegration } from './claude';
11
+ import { SchemaParser, ManifestValidator, ResourceRecommender } from './schema';
12
+
13
+ export interface CoreConfig {
14
+ kubernetesConfig?: string;
15
+ anthropicApiKey?: string;
16
+ }
17
+
18
+ export class DotAI {
19
+ private config: CoreConfig;
20
+ private initialized: boolean = false;
21
+
22
+ public readonly discovery: KubernetesDiscovery;
23
+ public readonly memory: MemorySystem;
24
+ public readonly workflow: WorkflowEngine;
25
+ public readonly claude: ClaudeIntegration;
26
+ public readonly schema: {
27
+ parser: SchemaParser;
28
+ validator: ManifestValidator;
29
+ ranker: ResourceRecommender | null;
30
+ parseResource: (resourceName: string) => Promise<any>;
31
+ rankResources: (intent: string) => Promise<any>;
32
+ };
33
+
34
+ constructor(config: CoreConfig = {}) {
35
+ this.validateConfig(config);
36
+ // Centralize environment variable reading
37
+ this.config = {
38
+ kubernetesConfig: config.kubernetesConfig || process.env.KUBECONFIG,
39
+ anthropicApiKey: config.anthropicApiKey || process.env.ANTHROPIC_API_KEY
40
+ };
41
+
42
+ // Initialize modules
43
+ this.discovery = new KubernetesDiscovery({
44
+ kubeconfigPath: this.config.kubernetesConfig
45
+ });
46
+ this.memory = new MemorySystem();
47
+ this.workflow = new WorkflowEngine();
48
+ this.claude = new ClaudeIntegration(this.config.anthropicApiKey || 'test-key');
49
+
50
+ // Initialize schema components
51
+ const parser = new SchemaParser();
52
+ const validator = new ManifestValidator();
53
+ const ranker = this.config.anthropicApiKey ?
54
+ new ResourceRecommender({ claudeApiKey: this.config.anthropicApiKey }) :
55
+ null;
56
+
57
+ this.schema = {
58
+ parser,
59
+ validator,
60
+ ranker,
61
+ parseResource: async (resourceName: string) => {
62
+ // Get raw resource explanation from discovery
63
+ const explanation = await this.discovery.explainResource(resourceName);
64
+
65
+ // Parse GROUP, KIND, VERSION from kubectl explain output
66
+ const lines = explanation.split('\n');
67
+ const groupLine = lines.find((line: string) => line.startsWith('GROUP:'));
68
+ const kindLine = lines.find((line: string) => line.startsWith('KIND:'));
69
+ const versionLine = lines.find((line: string) => line.startsWith('VERSION:'));
70
+
71
+ const group = groupLine ? groupLine.replace('GROUP:', '').trim() : '';
72
+ const kind = kindLine ? kindLine.replace('KIND:', '').trim() : resourceName;
73
+ const version = versionLine ? versionLine.replace('VERSION:', '').trim() : 'v1';
74
+
75
+ // Build apiVersion from group and version
76
+ const apiVersion = group ? `${group}/${version}` : version;
77
+
78
+ // Return raw explanation for AI processing
79
+ return {
80
+ kind: kind,
81
+ rawExplanation: explanation,
82
+ apiVersion: apiVersion,
83
+ group: group,
84
+ description: explanation.split('\n').find((line: string) => line.startsWith('DESCRIPTION:'))?.replace('DESCRIPTION:', '').trim() || '',
85
+ properties: new Map() // Raw explanation contains all field info for AI
86
+ };
87
+ },
88
+ rankResources: async (intent: string) => {
89
+ if (!ranker) {
90
+ throw new Error('ResourceRanker not available. ANTHROPIC_API_KEY is required for AI-powered ranking.');
91
+ }
92
+
93
+ // Create discovery functions with proper binding
94
+ const discoverResourcesFn = async () => await this.discovery.discoverResources();
95
+ const explainResourceFn = async (resource: string) => await this.discovery.explainResource(resource);
96
+
97
+ return await ranker.findBestSolutions(intent, discoverResourcesFn, explainResourceFn);
98
+ }
99
+ };
100
+ }
101
+
102
+ private validateConfig(config: CoreConfig): void {
103
+ if (config.anthropicApiKey === '') {
104
+ throw new Error('Invalid configuration: Empty API key provided');
105
+ }
106
+ }
107
+
108
+ async initialize(): Promise<void> {
109
+ try {
110
+ // Initialize all modules
111
+ await this.discovery.connect();
112
+ await this.memory.initialize();
113
+ await this.workflow.initialize();
114
+
115
+ this.initialized = true;
116
+ } catch (error) {
117
+ this.initialized = false;
118
+ throw error;
119
+ }
120
+ }
121
+
122
+ isInitialized(): boolean {
123
+ return this.initialized;
124
+ }
125
+
126
+ getVersion(): string {
127
+ return '0.1.0';
128
+ }
129
+
130
+ getAnthropicApiKey(): string | undefined {
131
+ return this.config.anthropicApiKey;
132
+ }
133
+ }
134
+
135
+ // Re-export all modules for convenience
136
+ export { KubernetesDiscovery } from './discovery';
137
+ export { MemorySystem } from './memory';
138
+ export { WorkflowEngine } from './workflow';
139
+ export { ClaudeIntegration } from './claude';
140
+ export { SchemaParser, ManifestValidator, ResourceRecommender } from './schema';
141
+
142
+ // Default export
143
+ export default DotAI;