@wundr.io/cli 1.0.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.
Files changed (213) hide show
  1. package/README.md +551 -0
  2. package/bin/wundr.js +39 -0
  3. package/dist/ai/ai-service.d.ts +152 -0
  4. package/dist/ai/ai-service.d.ts.map +1 -0
  5. package/dist/ai/ai-service.js +430 -0
  6. package/dist/ai/ai-service.js.map +1 -0
  7. package/dist/ai/claude-client.d.ts +130 -0
  8. package/dist/ai/claude-client.d.ts.map +1 -0
  9. package/dist/ai/claude-client.js +339 -0
  10. package/dist/ai/claude-client.js.map +1 -0
  11. package/dist/ai/conversation-manager.d.ts +164 -0
  12. package/dist/ai/conversation-manager.d.ts.map +1 -0
  13. package/dist/ai/conversation-manager.js +612 -0
  14. package/dist/ai/conversation-manager.js.map +1 -0
  15. package/dist/ai/index.d.ts +5 -0
  16. package/dist/ai/index.d.ts.map +1 -0
  17. package/dist/ai/index.js +8 -0
  18. package/dist/ai/index.js.map +1 -0
  19. package/dist/cli.d.ts +36 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +173 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands/ai.d.ts +89 -0
  24. package/dist/commands/ai.d.ts.map +1 -0
  25. package/dist/commands/ai.js +735 -0
  26. package/dist/commands/ai.js.map +1 -0
  27. package/dist/commands/analyze-optimized.d.ts +14 -0
  28. package/dist/commands/analyze-optimized.d.ts.map +1 -0
  29. package/dist/commands/analyze-optimized.js +437 -0
  30. package/dist/commands/analyze-optimized.js.map +1 -0
  31. package/dist/commands/analyze.d.ts +65 -0
  32. package/dist/commands/analyze.d.ts.map +1 -0
  33. package/dist/commands/analyze.js +435 -0
  34. package/dist/commands/analyze.js.map +1 -0
  35. package/dist/commands/batch.d.ts +71 -0
  36. package/dist/commands/batch.d.ts.map +1 -0
  37. package/dist/commands/batch.js +738 -0
  38. package/dist/commands/batch.js.map +1 -0
  39. package/dist/commands/chat.d.ts +71 -0
  40. package/dist/commands/chat.d.ts.map +1 -0
  41. package/dist/commands/chat.js +674 -0
  42. package/dist/commands/chat.js.map +1 -0
  43. package/dist/commands/claude-init.d.ts +28 -0
  44. package/dist/commands/claude-init.d.ts.map +1 -0
  45. package/dist/commands/claude-init.js +587 -0
  46. package/dist/commands/claude-init.js.map +1 -0
  47. package/dist/commands/claude-setup.d.ts +32 -0
  48. package/dist/commands/claude-setup.d.ts.map +1 -0
  49. package/dist/commands/claude-setup.js +570 -0
  50. package/dist/commands/claude-setup.js.map +1 -0
  51. package/dist/commands/computer-setup-commands.d.ts +39 -0
  52. package/dist/commands/computer-setup-commands.d.ts.map +1 -0
  53. package/dist/commands/computer-setup-commands.js +563 -0
  54. package/dist/commands/computer-setup-commands.js.map +1 -0
  55. package/dist/commands/computer-setup.d.ts +7 -0
  56. package/dist/commands/computer-setup.d.ts.map +1 -0
  57. package/dist/commands/computer-setup.js +481 -0
  58. package/dist/commands/computer-setup.js.map +1 -0
  59. package/dist/commands/create-command.d.ts +7 -0
  60. package/dist/commands/create-command.d.ts.map +1 -0
  61. package/dist/commands/create-command.js +158 -0
  62. package/dist/commands/create-command.js.map +1 -0
  63. package/dist/commands/create.d.ts +74 -0
  64. package/dist/commands/create.d.ts.map +1 -0
  65. package/dist/commands/create.js +556 -0
  66. package/dist/commands/create.js.map +1 -0
  67. package/dist/commands/dashboard.d.ts +91 -0
  68. package/dist/commands/dashboard.d.ts.map +1 -0
  69. package/dist/commands/dashboard.js +537 -0
  70. package/dist/commands/dashboard.js.map +1 -0
  71. package/dist/commands/govern.d.ts +70 -0
  72. package/dist/commands/govern.d.ts.map +1 -0
  73. package/dist/commands/govern.js +480 -0
  74. package/dist/commands/govern.js.map +1 -0
  75. package/dist/commands/init.d.ts +55 -0
  76. package/dist/commands/init.d.ts.map +1 -0
  77. package/dist/commands/init.js +584 -0
  78. package/dist/commands/init.js.map +1 -0
  79. package/dist/commands/performance-optimizer.d.ts +30 -0
  80. package/dist/commands/performance-optimizer.d.ts.map +1 -0
  81. package/dist/commands/performance-optimizer.js +649 -0
  82. package/dist/commands/performance-optimizer.js.map +1 -0
  83. package/dist/commands/plugins.d.ts +87 -0
  84. package/dist/commands/plugins.d.ts.map +1 -0
  85. package/dist/commands/plugins.js +685 -0
  86. package/dist/commands/plugins.js.map +1 -0
  87. package/dist/commands/setup.d.ts +29 -0
  88. package/dist/commands/setup.d.ts.map +1 -0
  89. package/dist/commands/setup.js +399 -0
  90. package/dist/commands/setup.js.map +1 -0
  91. package/dist/commands/test-init.d.ts +9 -0
  92. package/dist/commands/test-init.d.ts.map +1 -0
  93. package/dist/commands/test-init.js +222 -0
  94. package/dist/commands/test-init.js.map +1 -0
  95. package/dist/commands/test.d.ts +25 -0
  96. package/dist/commands/test.d.ts.map +1 -0
  97. package/dist/commands/test.js +217 -0
  98. package/dist/commands/test.js.map +1 -0
  99. package/dist/commands/watch.d.ts +76 -0
  100. package/dist/commands/watch.d.ts.map +1 -0
  101. package/dist/commands/watch.js +610 -0
  102. package/dist/commands/watch.js.map +1 -0
  103. package/dist/context/context-manager.d.ts +155 -0
  104. package/dist/context/context-manager.d.ts.map +1 -0
  105. package/dist/context/context-manager.js +383 -0
  106. package/dist/context/context-manager.js.map +1 -0
  107. package/dist/context/index.d.ts +3 -0
  108. package/dist/context/index.d.ts.map +1 -0
  109. package/dist/context/index.js +6 -0
  110. package/dist/context/index.js.map +1 -0
  111. package/dist/context/session-manager.d.ts +207 -0
  112. package/dist/context/session-manager.d.ts.map +1 -0
  113. package/dist/context/session-manager.js +682 -0
  114. package/dist/context/session-manager.js.map +1 -0
  115. package/dist/index.d.ts +8 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +51 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/interactive/interactive-mode.d.ts +76 -0
  120. package/dist/interactive/interactive-mode.d.ts.map +1 -0
  121. package/dist/interactive/interactive-mode.js +730 -0
  122. package/dist/interactive/interactive-mode.js.map +1 -0
  123. package/dist/nlp/command-mapper.d.ts +174 -0
  124. package/dist/nlp/command-mapper.d.ts.map +1 -0
  125. package/dist/nlp/command-mapper.js +623 -0
  126. package/dist/nlp/command-mapper.js.map +1 -0
  127. package/dist/nlp/command-parser.d.ts +106 -0
  128. package/dist/nlp/command-parser.d.ts.map +1 -0
  129. package/dist/nlp/command-parser.js +416 -0
  130. package/dist/nlp/command-parser.js.map +1 -0
  131. package/dist/nlp/index.d.ts +5 -0
  132. package/dist/nlp/index.d.ts.map +1 -0
  133. package/dist/nlp/index.js +8 -0
  134. package/dist/nlp/index.js.map +1 -0
  135. package/dist/nlp/intent-classifier.d.ts +59 -0
  136. package/dist/nlp/intent-classifier.d.ts.map +1 -0
  137. package/dist/nlp/intent-classifier.js +384 -0
  138. package/dist/nlp/intent-classifier.js.map +1 -0
  139. package/dist/nlp/intent-parser.d.ts +152 -0
  140. package/dist/nlp/intent-parser.d.ts.map +1 -0
  141. package/dist/nlp/intent-parser.js +739 -0
  142. package/dist/nlp/intent-parser.js.map +1 -0
  143. package/dist/plugins/plugin-manager.d.ts +120 -0
  144. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  145. package/dist/plugins/plugin-manager.js +595 -0
  146. package/dist/plugins/plugin-manager.js.map +1 -0
  147. package/dist/types/index.d.ts +224 -0
  148. package/dist/types/index.d.ts.map +1 -0
  149. package/dist/types/index.js +3 -0
  150. package/dist/types/index.js.map +1 -0
  151. package/dist/utils/config-manager.d.ts +73 -0
  152. package/dist/utils/config-manager.d.ts.map +1 -0
  153. package/dist/utils/config-manager.js +339 -0
  154. package/dist/utils/config-manager.js.map +1 -0
  155. package/dist/utils/error-handler.d.ts +46 -0
  156. package/dist/utils/error-handler.d.ts.map +1 -0
  157. package/dist/utils/error-handler.js +169 -0
  158. package/dist/utils/error-handler.js.map +1 -0
  159. package/dist/utils/logger.d.ts +25 -0
  160. package/dist/utils/logger.d.ts.map +1 -0
  161. package/dist/utils/logger.js +94 -0
  162. package/dist/utils/logger.js.map +1 -0
  163. package/package.json +119 -0
  164. package/src/ai/ai-service.ts +595 -0
  165. package/src/ai/claude-client.ts +490 -0
  166. package/src/ai/conversation-manager.ts +907 -0
  167. package/src/ai/index.ts +8 -0
  168. package/src/cli.ts +202 -0
  169. package/src/commands/ai.ts +995 -0
  170. package/src/commands/analyze-optimized.ts +641 -0
  171. package/src/commands/analyze.ts +576 -0
  172. package/src/commands/batch.ts +935 -0
  173. package/src/commands/chat.ts +876 -0
  174. package/src/commands/claude-init.ts +715 -0
  175. package/src/commands/claude-setup.ts +697 -0
  176. package/src/commands/computer-setup-commands.ts +709 -0
  177. package/src/commands/computer-setup.ts +565 -0
  178. package/src/commands/create-command.ts +175 -0
  179. package/src/commands/create.ts +727 -0
  180. package/src/commands/dashboard.ts +691 -0
  181. package/src/commands/govern.ts +635 -0
  182. package/src/commands/init.ts +677 -0
  183. package/src/commands/performance-optimizer.ts +864 -0
  184. package/src/commands/plugins.ts +848 -0
  185. package/src/commands/setup.ts +508 -0
  186. package/src/commands/test-init.ts +242 -0
  187. package/src/commands/test.ts +264 -0
  188. package/src/commands/watch.ts +755 -0
  189. package/src/context/context-manager.ts +546 -0
  190. package/src/context/index.ts +9 -0
  191. package/src/context/session-manager.ts +1019 -0
  192. package/src/index.ts +64 -0
  193. package/src/interactive/interactive-mode.ts +830 -0
  194. package/src/nlp/command-mapper.ts +885 -0
  195. package/src/nlp/command-parser.ts +564 -0
  196. package/src/nlp/index.ts +4 -0
  197. package/src/nlp/intent-classifier.ts +458 -0
  198. package/src/nlp/intent-parser.ts +1101 -0
  199. package/src/plugins/plugin-manager.ts +744 -0
  200. package/src/types/index.ts +252 -0
  201. package/src/types/modules.d.ts +56 -0
  202. package/src/utils/config-manager.ts +391 -0
  203. package/src/utils/error-handler.ts +192 -0
  204. package/src/utils/logger.ts +104 -0
  205. package/templates/batch/ci-cd.yaml +62 -0
  206. package/templates/component/{{fileName}}.test.tsx +17 -0
  207. package/templates/component/{{fileName}}.tsx +21 -0
  208. package/templates/service/{{fileName}}.ts +98 -0
  209. package/templates/wundr-test.config.js +0 -0
  210. package/test-suites/api/health.spec.ts +134 -0
  211. package/test-suites/helpers/test-config.ts +84 -0
  212. package/test-suites/ui/accessibility.spec.ts +102 -0
  213. package/test-suites/ui/smoke.spec.ts +92 -0
@@ -0,0 +1,1101 @@
1
+ import { EventEmitter } from 'events';
2
+ import { ClaudeClient, ClaudeMessage } from '../ai/claude-client';
3
+ import { logger } from '../utils/logger';
4
+
5
+ /**
6
+ * Intent classification result
7
+ */
8
+ export interface IntentResult {
9
+ intent: string;
10
+ confidence: number;
11
+ command?: string;
12
+ parameters?: Record<string, any>;
13
+ clarification?: string;
14
+ alternatives?: Array<{
15
+ command: string;
16
+ confidence: number;
17
+ description: string;
18
+ }>;
19
+ reasoning?: string;
20
+ context?: {
21
+ entities: Entity[];
22
+ keywords: string[];
23
+ sentiment: 'positive' | 'neutral' | 'negative';
24
+ };
25
+ }
26
+
27
+ /**
28
+ * Named entity recognition result
29
+ */
30
+ export interface Entity {
31
+ text: string;
32
+ type:
33
+ | 'file_path'
34
+ | 'package_name'
35
+ | 'command'
36
+ | 'option'
37
+ | 'value'
38
+ | 'technology';
39
+ confidence: number;
40
+ startIndex: number;
41
+ endIndex: number;
42
+ }
43
+
44
+ /**
45
+ * Command pattern for intent matching
46
+ */
47
+ export interface CommandPattern {
48
+ intent: string;
49
+ patterns: string[];
50
+ parameters: ParameterPattern[];
51
+ examples: string[];
52
+ category: string;
53
+ destructive?: boolean;
54
+ }
55
+
56
+ /**
57
+ * Parameter extraction pattern
58
+ */
59
+ export interface ParameterPattern {
60
+ name: string;
61
+ type: 'string' | 'number' | 'boolean' | 'path' | 'enum';
62
+ required: boolean;
63
+ aliases?: string[];
64
+ validation?: RegExp;
65
+ enumValues?: string[];
66
+ defaultValue?: any;
67
+ }
68
+
69
+ /**
70
+ * Context for intent analysis
71
+ */
72
+ export interface IntentContext {
73
+ projectType?: string;
74
+ recentCommands?: string[];
75
+ workingDirectory?: string;
76
+ gitStatus?: string;
77
+ conversationHistory?: ClaudeMessage[];
78
+ userPreferences?: Record<string, any>;
79
+ }
80
+
81
+ /**
82
+ * Intent parser configuration
83
+ */
84
+ export interface IntentParserConfig {
85
+ confidenceThreshold: number;
86
+ maxAlternatives: number;
87
+ enableNER: boolean;
88
+ enableContextAwareness: boolean;
89
+ cacheDuration: number; // milliseconds
90
+ }
91
+
92
+ /**
93
+ * Advanced NLP Intent Parser for converting natural language to CLI commands
94
+ */
95
+ export class IntentParser extends EventEmitter {
96
+ private claudeClient: ClaudeClient;
97
+ private config: IntentParserConfig;
98
+ private commandPatterns: Map<string, CommandPattern>;
99
+ private intentCache: Map<string, { result: IntentResult; timestamp: number }>;
100
+ private entityPatterns: Map<string, RegExp>;
101
+
102
+ constructor(
103
+ claudeClient: ClaudeClient,
104
+ config: Partial<IntentParserConfig> = {}
105
+ ) {
106
+ super();
107
+
108
+ this.claudeClient = claudeClient;
109
+ this.config = {
110
+ confidenceThreshold: 0.7,
111
+ maxAlternatives: 3,
112
+ enableNER: true,
113
+ enableContextAwareness: true,
114
+ cacheDuration: 5 * 60 * 1000, // 5 minutes
115
+ ...config,
116
+ };
117
+
118
+ this.commandPatterns = new Map();
119
+ this.intentCache = new Map();
120
+ this.entityPatterns = new Map();
121
+
122
+ this.initializePatterns();
123
+ this.initializeEntityRecognition();
124
+ }
125
+
126
+ /**
127
+ * Parse natural language input and extract intent
128
+ */
129
+ async parseIntent(
130
+ input: string,
131
+ availableCommands: string[],
132
+ context: IntentContext = {}
133
+ ): Promise<IntentResult> {
134
+ const normalizedInput = this.normalizeInput(input);
135
+ const cacheKey = this.getCacheKey(
136
+ normalizedInput,
137
+ availableCommands,
138
+ context
139
+ );
140
+
141
+ // Check cache first
142
+ const cached = this.intentCache.get(cacheKey);
143
+ if (cached && Date.now() - cached.timestamp < this.config.cacheDuration) {
144
+ logger.debug('Returning cached intent result');
145
+ return cached.result;
146
+ }
147
+
148
+ try {
149
+ // Step 1: Quick pattern matching for common commands
150
+ const patternResult = await this.patternMatching(
151
+ normalizedInput,
152
+ availableCommands
153
+ );
154
+ if (patternResult.confidence >= 0.9) {
155
+ this.cacheResult(cacheKey, patternResult);
156
+ return patternResult;
157
+ }
158
+
159
+ // Step 2: Named Entity Recognition
160
+ const entities = this.config.enableNER
161
+ ? await this.extractEntities(normalizedInput)
162
+ : [];
163
+
164
+ // Step 3: AI-powered intent analysis
165
+ const aiResult = await this.aiIntentAnalysis(
166
+ normalizedInput,
167
+ availableCommands,
168
+ context,
169
+ entities
170
+ );
171
+
172
+ // Step 4: Combine results and validate
173
+ const finalResult = this.combineResults(
174
+ patternResult,
175
+ aiResult,
176
+ entities
177
+ );
178
+
179
+ // Step 5: Post-processing and validation
180
+ const validatedResult = await this.validateAndEnrich(
181
+ finalResult,
182
+ availableCommands,
183
+ context
184
+ );
185
+
186
+ this.cacheResult(cacheKey, validatedResult);
187
+ this.emit('intent_parsed', {
188
+ input: normalizedInput,
189
+ result: validatedResult,
190
+ });
191
+
192
+ return validatedResult;
193
+ } catch (error) {
194
+ logger.error('Intent parsing failed:', error);
195
+ return this.fallbackIntentParsing(
196
+ normalizedInput,
197
+ availableCommands,
198
+ context
199
+ );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Extract command parameters from natural language
205
+ */
206
+ async extractParameters(
207
+ input: string,
208
+ command: string,
209
+ commandPattern?: CommandPattern
210
+ ): Promise<Record<string, any>> {
211
+ const parameters: Record<string, any> = {};
212
+
213
+ if (!commandPattern) {
214
+ commandPattern = this.commandPatterns.get(command);
215
+ }
216
+
217
+ if (!commandPattern) {
218
+ logger.debug(`No pattern found for command: ${command}`);
219
+ return parameters;
220
+ }
221
+
222
+ // Extract using AI
223
+ const systemPrompt = `You are a parameter extraction system. Extract parameters from user input for the command "${command}".
224
+
225
+ Command parameters:
226
+ ${commandPattern.parameters.map(p => `- ${p.name} (${p.type}${p.required ? ', required' : ', optional'}): ${p.aliases?.join(', ') || ''}`).join('\n')}
227
+
228
+ Extract parameters from the user input and respond with JSON only:
229
+ {
230
+ "parameters": {
231
+ "paramName": "value"
232
+ },
233
+ "confidence": 0.9
234
+ }`;
235
+
236
+ try {
237
+ const response = await this.claudeClient.sendMessage(
238
+ input,
239
+ systemPrompt,
240
+ {
241
+ temperature: 0.1,
242
+ maxTokens: 1024,
243
+ }
244
+ );
245
+
246
+ const result = JSON.parse(response.trim());
247
+
248
+ // Validate extracted parameters
249
+ for (const param of commandPattern.parameters) {
250
+ if (param.required && !result.parameters[param.name]) {
251
+ logger.warn(`Missing required parameter: ${param.name}`);
252
+ }
253
+
254
+ if (result.parameters[param.name] && param.validation) {
255
+ if (!param.validation.test(result.parameters[param.name])) {
256
+ logger.warn(
257
+ `Parameter validation failed for ${param.name}: ${result.parameters[param.name]}`
258
+ );
259
+ delete result.parameters[param.name];
260
+ }
261
+ }
262
+
263
+ // Apply default values
264
+ if (
265
+ !result.parameters[param.name] &&
266
+ param.defaultValue !== undefined
267
+ ) {
268
+ result.parameters[param.name] = param.defaultValue;
269
+ }
270
+ }
271
+
272
+ return result.parameters;
273
+ } catch (error) {
274
+ logger.error('Parameter extraction failed:', error);
275
+ return this.fallbackParameterExtraction(input, commandPattern);
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Suggest commands based on partial input
281
+ */
282
+ async suggestCommands(
283
+ partialInput: string,
284
+ availableCommands: string[],
285
+ context: IntentContext = {},
286
+ limit: number = 5
287
+ ): Promise<
288
+ Array<{
289
+ command: string;
290
+ description: string;
291
+ confidence: number;
292
+ completion: string;
293
+ }>
294
+ > {
295
+ const suggestions: Array<{
296
+ command: string;
297
+ description: string;
298
+ confidence: number;
299
+ completion: string;
300
+ }> = [];
301
+
302
+ // Pattern-based suggestions
303
+ const patternSuggestions = this.getPatternSuggestions(
304
+ partialInput,
305
+ availableCommands
306
+ );
307
+ suggestions.push(...patternSuggestions);
308
+
309
+ // AI-powered suggestions if we need more
310
+ if (suggestions.length < limit) {
311
+ try {
312
+ const aiSuggestions = await this.getAISuggestions(
313
+ partialInput,
314
+ availableCommands,
315
+ context,
316
+ limit - suggestions.length
317
+ );
318
+ suggestions.push(...aiSuggestions);
319
+ } catch (error) {
320
+ logger.error('AI suggestions failed:', error);
321
+ }
322
+ }
323
+
324
+ // Sort by confidence and limit results
325
+ suggestions.sort((a, b) => b.confidence - a.confidence);
326
+ return suggestions.slice(0, limit);
327
+ }
328
+
329
+ /**
330
+ * Register a custom command pattern
331
+ */
332
+ registerCommandPattern(pattern: CommandPattern): void {
333
+ this.commandPatterns.set(pattern.intent, pattern);
334
+ logger.debug(`Registered command pattern: ${pattern.intent}`);
335
+ }
336
+
337
+ /**
338
+ * Batch register multiple command patterns
339
+ */
340
+ registerCommandPatterns(patterns: CommandPattern[]): void {
341
+ patterns.forEach(pattern => this.registerCommandPattern(pattern));
342
+ }
343
+
344
+ /**
345
+ * Get registered command patterns
346
+ */
347
+ getCommandPatterns(): CommandPattern[] {
348
+ return Array.from(this.commandPatterns.values());
349
+ }
350
+
351
+ /**
352
+ * Clear intent cache
353
+ */
354
+ clearCache(): void {
355
+ this.intentCache.clear();
356
+ this.emit('cache_cleared');
357
+ }
358
+
359
+ /**
360
+ * Get cache statistics
361
+ */
362
+ getCacheStats(): {
363
+ size: number;
364
+ hitRate: number;
365
+ oldestEntry: Date | null;
366
+ } {
367
+ const now = Date.now();
368
+ const validEntries = Array.from(this.intentCache.values()).filter(
369
+ entry => now - entry.timestamp < this.config.cacheDuration
370
+ );
371
+
372
+ const oldestEntry =
373
+ validEntries.length > 0
374
+ ? new Date(Math.min(...validEntries.map(e => e.timestamp)))
375
+ : null;
376
+
377
+ return {
378
+ size: validEntries.length,
379
+ hitRate:
380
+ this.intentCache.size > 0
381
+ ? validEntries.length / this.intentCache.size
382
+ : 0,
383
+ oldestEntry,
384
+ };
385
+ }
386
+
387
+ // Private methods
388
+
389
+ private initializePatterns(): void {
390
+ const defaultPatterns: CommandPattern[] = [
391
+ {
392
+ intent: 'analyze',
393
+ patterns: [
394
+ 'analyze {path?}',
395
+ 'check {path?}',
396
+ 'scan {path?}',
397
+ 'examine {path?}',
398
+ 'review {path?}',
399
+ ],
400
+ parameters: [
401
+ { name: 'path', type: 'path', required: false, defaultValue: '.' },
402
+ {
403
+ name: 'focus',
404
+ type: 'enum',
405
+ required: false,
406
+ enumValues: ['dependencies', 'quality', 'security', 'performance'],
407
+ },
408
+ {
409
+ name: 'format',
410
+ type: 'enum',
411
+ required: false,
412
+ enumValues: ['json', 'table', 'csv'],
413
+ },
414
+ ],
415
+ examples: [
416
+ 'analyze the project',
417
+ 'check dependencies in src folder',
418
+ 'scan for security issues',
419
+ ],
420
+ category: 'analysis',
421
+ },
422
+ {
423
+ intent: 'create',
424
+ patterns: [
425
+ 'create {type} {name}',
426
+ 'new {type} {name}',
427
+ 'generate {type} {name}',
428
+ 'make {type} {name}',
429
+ ],
430
+ parameters: [
431
+ {
432
+ name: 'type',
433
+ type: 'enum',
434
+ required: true,
435
+ enumValues: ['component', 'service', 'test', 'config'],
436
+ },
437
+ { name: 'name', type: 'string', required: true },
438
+ { name: 'template', type: 'string', required: false },
439
+ ],
440
+ examples: [
441
+ 'create component UserProfile',
442
+ 'new service AuthService',
443
+ 'generate test for UserService',
444
+ ],
445
+ category: 'generation',
446
+ },
447
+ {
448
+ intent: 'init',
449
+ patterns: [
450
+ 'init {project?}',
451
+ 'initialize {project?}',
452
+ 'setup {project?}',
453
+ 'configure {project?}',
454
+ ],
455
+ parameters: [
456
+ { name: 'project', type: 'string', required: false },
457
+ { name: 'template', type: 'string', required: false },
458
+ { name: 'force', type: 'boolean', required: false },
459
+ ],
460
+ examples: [
461
+ 'init new project',
462
+ 'initialize with React template',
463
+ 'setup the workspace',
464
+ ],
465
+ category: 'setup',
466
+ },
467
+ {
468
+ intent: 'help',
469
+ patterns: [
470
+ 'help {command?}',
471
+ 'how {action?}',
472
+ 'what {question?}',
473
+ 'explain {topic?}',
474
+ ],
475
+ parameters: [
476
+ { name: 'command', type: 'string', required: false },
477
+ { name: 'topic', type: 'string', required: false },
478
+ ],
479
+ examples: [
480
+ 'help with analyze command',
481
+ 'how to create components',
482
+ 'what does this do',
483
+ ],
484
+ category: 'help',
485
+ },
486
+ {
487
+ intent: 'dashboard',
488
+ patterns: [
489
+ 'dashboard',
490
+ 'ui',
491
+ 'interface',
492
+ 'show {view?}',
493
+ 'open {view?}',
494
+ ],
495
+ parameters: [
496
+ {
497
+ name: 'view',
498
+ type: 'enum',
499
+ required: false,
500
+ enumValues: ['overview', 'metrics', 'reports'],
501
+ },
502
+ { name: 'port', type: 'number', required: false, defaultValue: 3000 },
503
+ ],
504
+ examples: ['open dashboard', 'show metrics view', 'start ui'],
505
+ category: 'interface',
506
+ },
507
+ ];
508
+
509
+ defaultPatterns.forEach(pattern => this.registerCommandPattern(pattern));
510
+ }
511
+
512
+ private initializeEntityRecognition(): void {
513
+ this.entityPatterns.set('file_path', /(?:\.\/|\/|~\/)[^\s]+/g);
514
+ this.entityPatterns.set(
515
+ 'package_name',
516
+ /(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*/g
517
+ );
518
+ this.entityPatterns.set(
519
+ 'command',
520
+ /(?:wundr\s+)?(?:analyze|create|init|help|dashboard|batch|watch)\b/g
521
+ );
522
+ this.entityPatterns.set('option', /--?[a-zA-Z][a-zA-Z0-9-]*/g);
523
+ this.entityPatterns.set(
524
+ 'technology',
525
+ /\b(?:react|angular|vue|nodejs|typescript|javascript|python|java|docker|kubernetes)\b/gi
526
+ );
527
+ }
528
+
529
+ private normalizeInput(input: string): string {
530
+ return input
531
+ .trim()
532
+ .toLowerCase()
533
+ .replace(/[^\w\s\-\.\/]/g, ' ')
534
+ .replace(/\s+/g, ' ');
535
+ }
536
+
537
+ private getCacheKey(
538
+ input: string,
539
+ commands: string[],
540
+ context: IntentContext
541
+ ): string {
542
+ const contextKey = JSON.stringify({
543
+ projectType: context.projectType,
544
+ workingDirectory: context.workingDirectory,
545
+ recentCommands: context.recentCommands?.slice(0, 3), // Only recent commands for cache key
546
+ });
547
+
548
+ return `${input}:${commands.sort().join(',')}:${contextKey}`;
549
+ }
550
+
551
+ private async patternMatching(
552
+ input: string,
553
+ availableCommands: string[]
554
+ ): Promise<IntentResult> {
555
+ let bestMatch: IntentResult = {
556
+ intent: 'unknown',
557
+ confidence: 0,
558
+ context: { entities: [], keywords: [], sentiment: 'neutral' },
559
+ };
560
+
561
+ for (const [intent, pattern] of this.commandPatterns) {
562
+ if (!availableCommands.includes(intent)) continue;
563
+
564
+ const confidence = this.calculatePatternScore(input, pattern);
565
+
566
+ if (confidence > bestMatch.confidence) {
567
+ bestMatch = {
568
+ intent,
569
+ confidence,
570
+ command: `wundr ${intent}`,
571
+ parameters: {},
572
+ reasoning: `Pattern match for "${intent}" command`,
573
+ context: { entities: [], keywords: [], sentiment: 'neutral' },
574
+ };
575
+ }
576
+ }
577
+
578
+ return bestMatch;
579
+ }
580
+
581
+ private calculatePatternScore(
582
+ input: string,
583
+ pattern: CommandPattern
584
+ ): number {
585
+ let score = 0;
586
+ let totalWeight = 0;
587
+
588
+ // Check pattern matches
589
+ for (const patternStr of pattern.patterns) {
590
+ const patternWords = patternStr.toLowerCase().split(/\s+/);
591
+ const inputWords = input.split(/\s+/);
592
+
593
+ let matchCount = 0;
594
+ for (const word of patternWords) {
595
+ if (word.startsWith('{') && word.endsWith('}')) continue; // Skip parameters
596
+ if (inputWords.includes(word)) {
597
+ matchCount++;
598
+ }
599
+ }
600
+
601
+ const patternScore =
602
+ matchCount / patternWords.filter(w => !w.startsWith('{')).length;
603
+ score = Math.max(score, patternScore);
604
+ }
605
+
606
+ totalWeight += 0.6;
607
+
608
+ // Check examples
609
+ let exampleScore = 0;
610
+ for (const example of pattern.examples) {
611
+ const similarity = this.calculateStringSimilarity(
612
+ input,
613
+ example.toLowerCase()
614
+ );
615
+ exampleScore = Math.max(exampleScore, similarity);
616
+ }
617
+
618
+ score += exampleScore * 0.4;
619
+ totalWeight += 0.4;
620
+
621
+ return score / totalWeight;
622
+ }
623
+
624
+ private calculateStringSimilarity(str1: string, str2: string): number {
625
+ const words1 = str1.split(/\s+/);
626
+ const words2 = str2.split(/\s+/);
627
+
628
+ let matches = 0;
629
+ for (const word1 of words1) {
630
+ if (words2.includes(word1)) {
631
+ matches++;
632
+ }
633
+ }
634
+
635
+ return matches / Math.max(words1.length, words2.length);
636
+ }
637
+
638
+ private async extractEntities(input: string): Promise<Entity[]> {
639
+ const entities: Entity[] = [];
640
+
641
+ for (const [type, pattern] of this.entityPatterns) {
642
+ const matches = Array.from(input.matchAll(pattern));
643
+
644
+ for (const match of matches) {
645
+ if (match.index !== undefined) {
646
+ entities.push({
647
+ text: match[0],
648
+ type: type as any,
649
+ confidence: 0.8, // Static confidence for regex patterns
650
+ startIndex: match.index,
651
+ endIndex: match.index + match[0].length,
652
+ });
653
+ }
654
+ }
655
+ }
656
+
657
+ return entities;
658
+ }
659
+
660
+ private async aiIntentAnalysis(
661
+ input: string,
662
+ availableCommands: string[],
663
+ context: IntentContext,
664
+ entities: Entity[]
665
+ ): Promise<IntentResult> {
666
+ const contextInfo = this.buildContextInfo(context);
667
+ const entityInfo =
668
+ entities.length > 0
669
+ ? `\nDetected entities: ${entities.map(e => `${e.text} (${e.type})`).join(', ')}`
670
+ : '';
671
+
672
+ const systemPrompt = `You are an expert CLI intent analyzer. Analyze user input and map it to appropriate commands.
673
+
674
+ Available commands: ${availableCommands.join(', ')}
675
+ ${contextInfo}${entityInfo}
676
+
677
+ User input: "${input}"
678
+
679
+ Analyze and respond with JSON only:
680
+ {
681
+ "intent": "command_name",
682
+ "confidence": 0.95,
683
+ "command": "wundr analyze --path ./src",
684
+ "parameters": {"path": "./src"},
685
+ "alternatives": [
686
+ {"command": "wundr scan", "confidence": 0.7, "description": "Alternative interpretation"}
687
+ ],
688
+ "reasoning": "User wants to analyze source code based on path mention",
689
+ "clarification": null
690
+ }`;
691
+
692
+ try {
693
+ const response = await this.claudeClient.sendMessage(
694
+ input,
695
+ systemPrompt,
696
+ {
697
+ temperature: 0.1,
698
+ maxTokens: 1536,
699
+ }
700
+ );
701
+
702
+ const cleanResponse = response.trim();
703
+ const jsonMatch = cleanResponse.match(/\{[\s\S]*\}/);
704
+
705
+ if (!jsonMatch) {
706
+ throw new Error('No valid JSON in AI response');
707
+ }
708
+
709
+ const result = JSON.parse(jsonMatch[0]);
710
+
711
+ // Add context information
712
+ result.context = {
713
+ entities,
714
+ keywords: this.extractKeywords(input),
715
+ sentiment: this.analyzeSentiment(input),
716
+ };
717
+
718
+ return result;
719
+ } catch (error) {
720
+ logger.error('AI intent analysis failed:', error);
721
+ throw error;
722
+ }
723
+ }
724
+
725
+ private buildContextInfo(context: IntentContext): string {
726
+ const parts: string[] = [];
727
+
728
+ if (context.projectType) {
729
+ parts.push(`Project type: ${context.projectType}`);
730
+ }
731
+
732
+ if (context.recentCommands?.length) {
733
+ parts.push(
734
+ `Recent commands: ${context.recentCommands.slice(0, 3).join(', ')}`
735
+ );
736
+ }
737
+
738
+ if (context.workingDirectory) {
739
+ parts.push(`Working directory: ${context.workingDirectory}`);
740
+ }
741
+
742
+ return parts.length > 0 ? `\nContext: ${parts.join(', ')}` : '';
743
+ }
744
+
745
+ private combineResults(
746
+ patternResult: IntentResult,
747
+ aiResult: IntentResult,
748
+ entities: Entity[]
749
+ ): IntentResult {
750
+ // Use AI result as base, but boost confidence if pattern matching agrees
751
+ let finalResult = { ...aiResult };
752
+
753
+ if (patternResult.intent === aiResult.intent) {
754
+ finalResult.confidence = Math.min(0.98, aiResult.confidence + 0.1);
755
+ finalResult.reasoning += ` (confirmed by pattern matching)`;
756
+ } else if (patternResult.confidence > 0.8) {
757
+ // High-confidence pattern match might override AI
758
+ if (patternResult.confidence > aiResult.confidence) {
759
+ finalResult = patternResult;
760
+ finalResult.reasoning += ` (overridden by high-confidence pattern match)`;
761
+ }
762
+ }
763
+
764
+ // Ensure entities are included
765
+ if (finalResult.context) {
766
+ finalResult.context.entities = entities;
767
+ }
768
+
769
+ return finalResult;
770
+ }
771
+
772
+ private async validateAndEnrich(
773
+ result: IntentResult,
774
+ availableCommands: string[],
775
+ context: IntentContext
776
+ ): Promise<IntentResult> {
777
+ // Validate that the intent command is available
778
+ if (!availableCommands.includes(result.intent)) {
779
+ logger.warn(`Intent "${result.intent}" not in available commands`);
780
+ result.confidence *= 0.5;
781
+ result.clarification = `The command "${result.intent}" is not available. Did you mean one of: ${availableCommands.join(', ')}?`;
782
+ }
783
+
784
+ // Add safety warnings for destructive commands
785
+ const commandPattern = this.commandPatterns.get(result.intent);
786
+ if (commandPattern?.destructive) {
787
+ result.clarification = `This is a destructive operation. Are you sure you want to proceed?`;
788
+ }
789
+
790
+ // Enrich with context-aware suggestions
791
+ if (result.confidence < this.config.confidenceThreshold) {
792
+ result.alternatives = await this.getContextualAlternatives(
793
+ result,
794
+ availableCommands,
795
+ context
796
+ );
797
+ }
798
+
799
+ return result;
800
+ }
801
+
802
+ private async getContextualAlternatives(
803
+ result: IntentResult,
804
+ availableCommands: string[],
805
+ context: IntentContext
806
+ ): Promise<IntentResult['alternatives']> {
807
+ const alternatives: NonNullable<IntentResult['alternatives']> = [];
808
+
809
+ // Add pattern-based alternatives
810
+ for (const command of availableCommands) {
811
+ if (command !== result.intent) {
812
+ const pattern = this.commandPatterns.get(command);
813
+ if (pattern) {
814
+ const confidence = this.calculatePatternScore(
815
+ result.command || '',
816
+ pattern
817
+ );
818
+ if (confidence > 0.3) {
819
+ alternatives.push({
820
+ command: `wundr ${command}`,
821
+ confidence,
822
+ description: `Alternative: ${pattern.examples[0] || command}`,
823
+ });
824
+ }
825
+ }
826
+ }
827
+ }
828
+
829
+ // Sort and limit alternatives
830
+ alternatives.sort((a, b) => b.confidence - a.confidence);
831
+ return alternatives.slice(0, this.config.maxAlternatives);
832
+ }
833
+
834
+ private fallbackIntentParsing(
835
+ input: string,
836
+ availableCommands: string[],
837
+ context: IntentContext
838
+ ): IntentResult {
839
+ // Simple keyword matching as last resort
840
+ const keywords = input.split(/\s+/);
841
+ const bestMatch = 'help';
842
+ const bestScore = 0;
843
+
844
+ for (const command of availableCommands) {
845
+ if (keywords.includes(command)) {
846
+ return {
847
+ intent: command,
848
+ confidence: 0.6,
849
+ command: `wundr ${command}`,
850
+ parameters: {},
851
+ reasoning: 'Fallback keyword matching',
852
+ context: { entities: [], keywords, sentiment: 'neutral' },
853
+ };
854
+ }
855
+ }
856
+
857
+ return {
858
+ intent: bestMatch,
859
+ confidence: 0.3,
860
+ command: `wundr ${bestMatch}`,
861
+ parameters: {},
862
+ clarification: `I couldn't understand "${input}". Type "help" to see available commands.`,
863
+ reasoning: 'Fallback to help command',
864
+ context: { entities: [], keywords, sentiment: 'neutral' },
865
+ };
866
+ }
867
+
868
+ private fallbackParameterExtraction(
869
+ input: string,
870
+ pattern: CommandPattern
871
+ ): Record<string, any> {
872
+ const parameters: Record<string, any> = {};
873
+ const words = input.split(/\s+/);
874
+
875
+ // Simple heuristic parameter extraction
876
+ for (const param of pattern.parameters) {
877
+ if (param.type === 'path') {
878
+ const pathMatch = input.match(/(?:\.\/|\/|~\/)[^\s]+/);
879
+ if (pathMatch) {
880
+ parameters[param.name] = pathMatch[0];
881
+ }
882
+ } else if (param.type === 'boolean') {
883
+ if (
884
+ words.some(word =>
885
+ ['yes', 'true', 'on', 'enable'].includes(word.toLowerCase())
886
+ )
887
+ ) {
888
+ parameters[param.name] = true;
889
+ } else if (
890
+ words.some(word =>
891
+ ['no', 'false', 'off', 'disable'].includes(word.toLowerCase())
892
+ )
893
+ ) {
894
+ parameters[param.name] = false;
895
+ }
896
+ }
897
+
898
+ // Apply default values
899
+ if (!parameters[param.name] && param.defaultValue !== undefined) {
900
+ parameters[param.name] = param.defaultValue;
901
+ }
902
+ }
903
+
904
+ return parameters;
905
+ }
906
+
907
+ private getPatternSuggestions(
908
+ partialInput: string,
909
+ availableCommands: string[]
910
+ ): Array<{
911
+ command: string;
912
+ description: string;
913
+ confidence: number;
914
+ completion: string;
915
+ }> {
916
+ const suggestions: Array<{
917
+ command: string;
918
+ description: string;
919
+ confidence: number;
920
+ completion: string;
921
+ }> = [];
922
+
923
+ for (const command of availableCommands) {
924
+ const pattern = this.commandPatterns.get(command);
925
+ if (!pattern) continue;
926
+
927
+ // Check if partial input matches command or examples
928
+ const confidence = Math.max(
929
+ this.calculateStringSimilarity(partialInput, command),
930
+ ...pattern.examples.map(ex =>
931
+ this.calculateStringSimilarity(partialInput, ex)
932
+ )
933
+ );
934
+
935
+ if (confidence > 0.3) {
936
+ suggestions.push({
937
+ command: `wundr ${command}`,
938
+ description: pattern.examples[0] || `Run ${command} command`,
939
+ confidence,
940
+ completion: pattern.examples[0] || `wundr ${command}`,
941
+ });
942
+ }
943
+ }
944
+
945
+ return suggestions;
946
+ }
947
+
948
+ private async getAISuggestions(
949
+ partialInput: string,
950
+ availableCommands: string[],
951
+ context: IntentContext,
952
+ limit: number
953
+ ): Promise<
954
+ Array<{
955
+ command: string;
956
+ description: string;
957
+ confidence: number;
958
+ completion: string;
959
+ }>
960
+ > {
961
+ const systemPrompt = `You are a CLI auto-completion assistant. Based on partial user input, suggest the most likely command completions.
962
+
963
+ Available commands: ${availableCommands.join(', ')}
964
+ Partial input: "${partialInput}"
965
+
966
+ Provide ${limit} suggestions in JSON format:
967
+ {
968
+ "suggestions": [
969
+ {
970
+ "command": "wundr analyze",
971
+ "description": "Analyze the project for issues",
972
+ "confidence": 0.9,
973
+ "completion": "wundr analyze --path ."
974
+ }
975
+ ]
976
+ }`;
977
+
978
+ try {
979
+ const response = await this.claudeClient.sendMessage('', systemPrompt, {
980
+ temperature: 0.3,
981
+ maxTokens: 1024,
982
+ });
983
+
984
+ const result = JSON.parse(response.trim());
985
+ return result.suggestions || [];
986
+ } catch (error) {
987
+ logger.error('AI suggestions failed:', error);
988
+ return [];
989
+ }
990
+ }
991
+
992
+ private extractKeywords(input: string): string[] {
993
+ // Simple keyword extraction
994
+ const stopWords = [
995
+ 'the',
996
+ 'and',
997
+ 'or',
998
+ 'but',
999
+ 'in',
1000
+ 'on',
1001
+ 'at',
1002
+ 'to',
1003
+ 'for',
1004
+ 'of',
1005
+ 'with',
1006
+ 'by',
1007
+ 'is',
1008
+ 'are',
1009
+ 'was',
1010
+ 'were',
1011
+ 'be',
1012
+ 'been',
1013
+ 'have',
1014
+ 'has',
1015
+ 'had',
1016
+ 'do',
1017
+ 'does',
1018
+ 'did',
1019
+ 'will',
1020
+ 'would',
1021
+ 'could',
1022
+ 'should',
1023
+ ];
1024
+
1025
+ return input
1026
+ .toLowerCase()
1027
+ .split(/\s+/)
1028
+ .filter(word => word.length > 2 && !stopWords.includes(word))
1029
+ .slice(0, 10); // Limit to 10 keywords
1030
+ }
1031
+
1032
+ private analyzeSentiment(input: string): 'positive' | 'neutral' | 'negative' {
1033
+ const positiveWords = [
1034
+ 'good',
1035
+ 'great',
1036
+ 'excellent',
1037
+ 'awesome',
1038
+ 'perfect',
1039
+ 'love',
1040
+ 'like',
1041
+ 'thanks',
1042
+ 'help',
1043
+ 'please',
1044
+ ];
1045
+ const negativeWords = [
1046
+ 'bad',
1047
+ 'terrible',
1048
+ 'awful',
1049
+ 'hate',
1050
+ 'problem',
1051
+ 'error',
1052
+ 'issue',
1053
+ 'broken',
1054
+ 'fail',
1055
+ 'wrong',
1056
+ ];
1057
+
1058
+ const words = input.toLowerCase().split(/\s+/);
1059
+ const positiveCount = words.filter(word =>
1060
+ positiveWords.includes(word)
1061
+ ).length;
1062
+ const negativeCount = words.filter(word =>
1063
+ negativeWords.includes(word)
1064
+ ).length;
1065
+
1066
+ if (positiveCount > negativeCount) return 'positive';
1067
+ if (negativeCount > positiveCount) return 'negative';
1068
+ return 'neutral';
1069
+ }
1070
+
1071
+ private cacheResult(key: string, result: IntentResult): void {
1072
+ this.intentCache.set(key, {
1073
+ result,
1074
+ timestamp: Date.now(),
1075
+ });
1076
+
1077
+ // Clean old entries periodically
1078
+ if (this.intentCache.size % 100 === 0) {
1079
+ this.cleanCache();
1080
+ }
1081
+ }
1082
+
1083
+ private cleanCache(): void {
1084
+ const now = Date.now();
1085
+ const expiredKeys: string[] = [];
1086
+
1087
+ for (const [key, entry] of this.intentCache) {
1088
+ if (now - entry.timestamp > this.config.cacheDuration) {
1089
+ expiredKeys.push(key);
1090
+ }
1091
+ }
1092
+
1093
+ expiredKeys.forEach(key => this.intentCache.delete(key));
1094
+
1095
+ if (expiredKeys.length > 0) {
1096
+ logger.debug(`Cleaned ${expiredKeys.length} expired cache entries`);
1097
+ }
1098
+ }
1099
+ }
1100
+
1101
+ export default IntentParser;