@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,490 @@
1
+ import axios from 'axios';
2
+ import { logger } from '../utils/logger';
3
+ import { WundrError } from '../types';
4
+
5
+ /**
6
+ * Claude API client configuration
7
+ */
8
+ export interface ClaudeConfig {
9
+ apiKey: string;
10
+ model: string;
11
+ maxTokens: number;
12
+ temperature: number;
13
+ baseUrl?: string;
14
+ }
15
+
16
+ /**
17
+ * Claude API message structure
18
+ */
19
+ export interface ClaudeMessage {
20
+ role: 'user' | 'assistant' | 'system';
21
+ content: string;
22
+ }
23
+
24
+ /**
25
+ * Claude API response structure
26
+ */
27
+ export interface ClaudeResponse {
28
+ id: string;
29
+ object: string;
30
+ created: number;
31
+ model: string;
32
+ choices: {
33
+ index: number;
34
+ message: ClaudeMessage;
35
+ finish_reason: string;
36
+ }[];
37
+ usage: {
38
+ prompt_tokens: number;
39
+ completion_tokens: number;
40
+ total_tokens: number;
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Claude API streaming response
46
+ */
47
+ export interface ClaudeStreamChunk {
48
+ id: string;
49
+ object: string;
50
+ created: number;
51
+ model: string;
52
+ choices: {
53
+ index: number;
54
+ delta: {
55
+ role?: string;
56
+ content?: string;
57
+ };
58
+ finish_reason?: string;
59
+ }[];
60
+ }
61
+
62
+ /**
63
+ * Claude API client for Claude-Opus-4.1 integration
64
+ */
65
+ export class ClaudeClient {
66
+ private client: any;
67
+ private config: ClaudeConfig;
68
+
69
+ constructor(config: ClaudeConfig) {
70
+ this.config = {
71
+ baseUrl: 'https://api.anthropic.com',
72
+ ...config,
73
+ };
74
+
75
+ this.client = axios.create({
76
+ baseURL: this.config.baseUrl,
77
+ headers: {
78
+ 'Content-Type': 'application/json',
79
+ 'x-api-key': this.config.apiKey,
80
+ 'anthropic-version': '2023-06-01',
81
+ },
82
+ timeout: 30000,
83
+ });
84
+
85
+ // Add request/response interceptors for logging and error handling
86
+ this.setupInterceptors();
87
+ }
88
+
89
+ /**
90
+ * Send a single message to Claude
91
+ */
92
+ async sendMessage(
93
+ message: string,
94
+ systemPrompt?: string,
95
+ options?: Partial<ClaudeConfig>
96
+ ): Promise<string> {
97
+ try {
98
+ const messages: ClaudeMessage[] = [];
99
+
100
+ if (systemPrompt) {
101
+ messages.push({ role: 'system', content: systemPrompt });
102
+ }
103
+
104
+ messages.push({ role: 'user', content: message });
105
+
106
+ const response = await this.client.post('/v1/messages', {
107
+ model: options?.model || this.config.model,
108
+ max_tokens: options?.maxTokens || this.config.maxTokens,
109
+ temperature: options?.temperature ?? this.config.temperature,
110
+ messages,
111
+ });
112
+
113
+ return this.extractContent(response.data);
114
+ } catch (error) {
115
+ throw this.handleError(error, 'Failed to send message to Claude');
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Send conversation history to Claude
121
+ */
122
+ async sendConversation(
123
+ messages: ClaudeMessage[],
124
+ systemPrompt?: string,
125
+ options?: Partial<ClaudeConfig>
126
+ ): Promise<string> {
127
+ try {
128
+ const conversationMessages = [...messages];
129
+
130
+ if (systemPrompt) {
131
+ conversationMessages.unshift({ role: 'system', content: systemPrompt });
132
+ }
133
+
134
+ const response = await this.client.post('/v1/messages', {
135
+ model: options?.model || this.config.model,
136
+ max_tokens: options?.maxTokens || this.config.maxTokens,
137
+ temperature: options?.temperature ?? this.config.temperature,
138
+ messages: conversationMessages,
139
+ });
140
+
141
+ return this.extractContent(response.data);
142
+ } catch (error) {
143
+ throw this.handleError(error, 'Failed to send conversation to Claude');
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Stream a conversation with Claude
149
+ */
150
+ async *streamConversation(
151
+ messages: ClaudeMessage[],
152
+ systemPrompt?: string,
153
+ options?: Partial<ClaudeConfig>
154
+ ): AsyncGenerator<string, void, unknown> {
155
+ try {
156
+ const conversationMessages = [...messages];
157
+
158
+ if (systemPrompt) {
159
+ conversationMessages.unshift({ role: 'system', content: systemPrompt });
160
+ }
161
+
162
+ const response = await this.client.post(
163
+ '/v1/messages',
164
+ {
165
+ model: options?.model || this.config.model,
166
+ max_tokens: options?.maxTokens || this.config.maxTokens,
167
+ temperature: options?.temperature ?? this.config.temperature,
168
+ messages: conversationMessages,
169
+ stream: true,
170
+ },
171
+ {
172
+ responseType: 'stream',
173
+ }
174
+ );
175
+
176
+ let buffer = '';
177
+
178
+ for await (const chunk of response.data) {
179
+ buffer += chunk.toString();
180
+ const lines = buffer.split('\n');
181
+ buffer = lines.pop() || '';
182
+
183
+ for (const line of lines) {
184
+ if (line.startsWith('data: ')) {
185
+ const data = line.slice(6);
186
+ if (data === '[DONE]') return;
187
+
188
+ try {
189
+ const parsed: ClaudeStreamChunk = JSON.parse(data);
190
+ const content = parsed.choices[0]?.delta?.content;
191
+ if (content) {
192
+ yield content;
193
+ }
194
+ } catch (e) {
195
+ // Skip malformed chunks
196
+ }
197
+ }
198
+ }
199
+ }
200
+ } catch (error) {
201
+ throw this.handleError(
202
+ error,
203
+ 'Failed to stream conversation with Claude'
204
+ );
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Analyze intent from natural language input
210
+ */
211
+ async analyzeIntent(
212
+ input: string,
213
+ availableCommands: string[]
214
+ ): Promise<{
215
+ intent: string;
216
+ confidence: number;
217
+ command?: string;
218
+ parameters?: Record<string, any>;
219
+ clarification?: string;
220
+ }> {
221
+ const systemPrompt = `You are a CLI intent analyzer. Analyze user input and determine:
222
+ 1. The intended CLI command from available options: ${availableCommands.join(', ')}
223
+ 2. Extracted parameters
224
+ 3. Confidence level (0-1)
225
+ 4. Whether clarification is needed
226
+
227
+ Respond with JSON only in this format:
228
+ {
229
+ "intent": "command_name",
230
+ "confidence": 0.9,
231
+ "command": "wundr analyze --path ./src",
232
+ "parameters": {"path": "./src"},
233
+ "clarification": "optional clarification question"
234
+ }`;
235
+
236
+ try {
237
+ const response = await this.sendMessage(input, systemPrompt, {
238
+ temperature: 0.2,
239
+ maxTokens: 1024,
240
+ });
241
+
242
+ return JSON.parse(response);
243
+ } catch (error) {
244
+ throw this.handleError(error, 'Failed to analyze intent');
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Generate command suggestions based on context
250
+ */
251
+ async suggestCommands(
252
+ projectContext: string,
253
+ userGoal: string,
254
+ availableCommands: string[]
255
+ ): Promise<{
256
+ suggestions: Array<{
257
+ command: string;
258
+ description: string;
259
+ confidence: number;
260
+ }>;
261
+ }> {
262
+ const systemPrompt = `You are a CLI assistant. Based on the project context and user goal, suggest the most appropriate CLI commands.
263
+
264
+ Project Context: ${projectContext}
265
+ User Goal: ${userGoal}
266
+ Available Commands: ${availableCommands.join(', ')}
267
+
268
+ Respond with JSON only:
269
+ {
270
+ "suggestions": [
271
+ {
272
+ "command": "wundr analyze --focus dependencies",
273
+ "description": "Analyze project dependencies",
274
+ "confidence": 0.9
275
+ }
276
+ ]
277
+ }`;
278
+
279
+ try {
280
+ const response = await this.sendMessage('', systemPrompt, {
281
+ temperature: 0.3,
282
+ maxTokens: 1024,
283
+ });
284
+
285
+ return JSON.parse(response);
286
+ } catch (error) {
287
+ throw this.handleError(error, 'Failed to generate command suggestions');
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Explain command results in natural language
293
+ */
294
+ async explainResults(
295
+ command: string,
296
+ output: string,
297
+ context?: string
298
+ ): Promise<string> {
299
+ const systemPrompt = `You are a helpful CLI assistant. Explain the results of a CLI command in clear, natural language. Focus on:
300
+ 1. What the command did
301
+ 2. Key findings or results
302
+ 3. Next steps or recommendations
303
+ 4. Any issues or warnings
304
+
305
+ Be concise but informative.`;
306
+
307
+ const message = `Command: ${command}
308
+ ${context ? `Context: ${context}\n` : ''}Output:
309
+ ${output}`;
310
+
311
+ try {
312
+ return await this.sendMessage(message, systemPrompt, {
313
+ temperature: 0.4,
314
+ maxTokens: 2048,
315
+ });
316
+ } catch (error) {
317
+ throw this.handleError(error, 'Failed to explain results');
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Generate help content for commands
323
+ */
324
+ async generateHelp(
325
+ command: string,
326
+ userContext: string,
327
+ difficulty: 'beginner' | 'intermediate' | 'advanced' = 'intermediate'
328
+ ): Promise<string> {
329
+ const systemPrompt = `You are a CLI documentation assistant. Generate helpful, contextual guidance for CLI commands. Adapt the explanation level to: ${difficulty}
330
+
331
+ Provide:
332
+ 1. What the command does
333
+ 2. When to use it
334
+ 3. Common options and examples
335
+ 4. Tips and best practices
336
+ 5. Related commands
337
+
338
+ Keep it practical and actionable.`;
339
+
340
+ const message = `Command: ${command}
341
+ User Context: ${userContext}`;
342
+
343
+ try {
344
+ return await this.sendMessage(message, systemPrompt, {
345
+ temperature: 0.3,
346
+ maxTokens: 2048,
347
+ });
348
+ } catch (error) {
349
+ throw this.handleError(error, 'Failed to generate help content');
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Validate and test the API connection
355
+ */
356
+ async validateConnection(): Promise<boolean> {
357
+ try {
358
+ await this.sendMessage(
359
+ 'Hello',
360
+ 'Respond with just "OK" if you can understand this message.',
361
+ {
362
+ maxTokens: 10,
363
+ }
364
+ );
365
+ return true;
366
+ } catch (error) {
367
+ logger.error('Claude API connection validation failed:', error);
368
+ return false;
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Get model information
374
+ */
375
+ getModelInfo(): {
376
+ model: string;
377
+ maxTokens: number;
378
+ temperature: number;
379
+ } {
380
+ return {
381
+ model: this.config.model,
382
+ maxTokens: this.config.maxTokens,
383
+ temperature: this.config.temperature,
384
+ };
385
+ }
386
+
387
+ /**
388
+ * Update configuration
389
+ */
390
+ updateConfig(updates: Partial<ClaudeConfig>): void {
391
+ this.config = { ...this.config, ...updates };
392
+
393
+ if (updates.apiKey) {
394
+ this.client.defaults.headers['x-api-key'] = updates.apiKey;
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Extract content from Claude API response
400
+ */
401
+ private extractContent(response: ClaudeResponse): string {
402
+ if (!response.choices || response.choices.length === 0) {
403
+ throw new Error('No choices in Claude API response');
404
+ }
405
+
406
+ return response.choices?.[0]?.message?.content || '';
407
+ }
408
+
409
+ /**
410
+ * Setup request/response interceptors
411
+ */
412
+ private setupInterceptors(): void {
413
+ // Request interceptor
414
+ this.client.interceptors.request.use(
415
+ (config: any) => {
416
+ logger.debug(
417
+ `Claude API Request: ${config.method?.toUpperCase()} ${config.url}`
418
+ );
419
+ return config;
420
+ },
421
+ (error: any) => {
422
+ logger.error('Claude API Request Error:', error);
423
+ return Promise.reject(error);
424
+ }
425
+ );
426
+
427
+ // Response interceptor
428
+ this.client.interceptors.response.use(
429
+ (response: any) => {
430
+ logger.debug(
431
+ `Claude API Response: ${response.status} ${response.statusText}`
432
+ );
433
+ return response;
434
+ },
435
+ (error: any) => {
436
+ logger.error('Claude API Response Error:', error);
437
+ return Promise.reject(error);
438
+ }
439
+ );
440
+ }
441
+
442
+ /**
443
+ * Handle API errors and convert to WundrError
444
+ */
445
+ private handleError(error: any, message: string): WundrError {
446
+ logger.error(`Claude API Error: ${message}`, error);
447
+
448
+ let errorCode = 'CLAUDE_API_ERROR';
449
+ let errorMessage = message;
450
+ let recoverable = false;
451
+
452
+ if (error.response) {
453
+ const status = error.response.status;
454
+ const data = error.response.data;
455
+
456
+ switch (status) {
457
+ case 401:
458
+ errorCode = 'CLAUDE_AUTH_ERROR';
459
+ errorMessage =
460
+ 'Invalid API key. Please check your Claude API configuration.';
461
+ recoverable = true;
462
+ break;
463
+ case 429:
464
+ errorCode = 'CLAUDE_RATE_LIMIT';
465
+ errorMessage = 'Rate limit exceeded. Please try again in a moment.';
466
+ recoverable = true;
467
+ break;
468
+ case 500:
469
+ errorCode = 'CLAUDE_SERVER_ERROR';
470
+ errorMessage = 'Claude API server error. Please try again later.';
471
+ recoverable = true;
472
+ break;
473
+ default:
474
+ errorMessage = `${message}: ${data?.error?.message || error.message}`;
475
+ }
476
+ } else if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
477
+ errorCode = 'CLAUDE_CONNECTION_ERROR';
478
+ errorMessage =
479
+ 'Unable to connect to Claude API. Please check your internet connection.';
480
+ recoverable = true;
481
+ }
482
+
483
+ const wundrError = new Error(errorMessage) as WundrError;
484
+ wundrError.code = errorCode;
485
+ wundrError.context = { originalError: error };
486
+ wundrError.recoverable = recoverable;
487
+
488
+ return wundrError;
489
+ }
490
+ }