@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,876 @@
1
+ import { Command } from 'commander';
2
+ import inquirer from 'inquirer';
3
+ import fs from 'fs-extra';
4
+ import path from 'path';
5
+ import chalk from 'chalk';
6
+ import { ConfigManager } from '../utils/config-manager';
7
+ import { PluginManager } from '../plugins/plugin-manager';
8
+ import { logger } from '../utils/logger';
9
+ import { errorHandler } from '../utils/error-handler';
10
+ import { ChatSession, ChatMessage } from '../types';
11
+
12
+ /**
13
+ * Chat commands for natural language interface
14
+ */
15
+ export class ChatCommands {
16
+ private activeSessions: Map<string, ChatSession> = new Map();
17
+
18
+ constructor(
19
+ private program: Command,
20
+ private configManager: ConfigManager,
21
+ private pluginManager: PluginManager
22
+ ) {
23
+ this.registerCommands();
24
+ }
25
+
26
+ private registerCommands(): void {
27
+ const chatCmd = this.program
28
+ .command('chat')
29
+ .alias('c')
30
+ .description('natural language chat interface');
31
+
32
+ // Start chat session
33
+ chatCmd
34
+ .command('start')
35
+ .description('start a new chat session')
36
+ .option('--model <model>', 'AI model to use', 'claude-3')
37
+ .option('--context <path>', 'include project context from path')
38
+ .option(
39
+ '--persona <persona>',
40
+ 'AI persona (developer, architect, reviewer)',
41
+ 'developer'
42
+ )
43
+ .option('--session-name <name>', 'custom session name')
44
+ .action(async options => {
45
+ await this.startChatSession(options);
46
+ });
47
+
48
+ // Resume chat session
49
+ chatCmd
50
+ .command('resume <sessionId>')
51
+ .description('resume existing chat session')
52
+ .action(async sessionId => {
53
+ await this.resumeChatSession(sessionId);
54
+ });
55
+
56
+ // List chat sessions
57
+ chatCmd
58
+ .command('list')
59
+ .alias('ls')
60
+ .description('list chat sessions')
61
+ .option('--active-only', 'show only active sessions')
62
+ .action(async options => {
63
+ await this.listChatSessions(options);
64
+ });
65
+
66
+ // Send single message
67
+ chatCmd
68
+ .command('ask <message>')
69
+ .description('send a single message and get response')
70
+ .option('--session <sessionId>', 'use specific session')
71
+ .option('--model <model>', 'AI model to use')
72
+ .option('--context <path>', 'include context from path')
73
+ .action(async (message, options) => {
74
+ await this.askSingleQuestion(message, options);
75
+ });
76
+
77
+ // Export chat session
78
+ chatCmd
79
+ .command('export <sessionId>')
80
+ .description('export chat session')
81
+ .option(
82
+ '--format <format>',
83
+ 'export format (json, markdown, txt)',
84
+ 'markdown'
85
+ )
86
+ .option('--output <path>', 'output file path')
87
+ .action(async (sessionId, options) => {
88
+ await this.exportChatSession(sessionId, options);
89
+ });
90
+
91
+ // Import chat session
92
+ chatCmd
93
+ .command('import <file>')
94
+ .description('import chat session')
95
+ .option('--format <format>', 'import format (json, markdown)')
96
+ .action(async (file, options) => {
97
+ await this.importChatSession(file, options);
98
+ });
99
+
100
+ // Delete chat session
101
+ chatCmd
102
+ .command('delete <sessionId>')
103
+ .description('delete chat session')
104
+ .option('--force', 'skip confirmation')
105
+ .action(async (sessionId, options) => {
106
+ await this.deleteChatSession(sessionId, options);
107
+ });
108
+
109
+ // Configure chat
110
+ chatCmd.command('config').description('configure chat settings');
111
+
112
+ chatCmd
113
+ .command('config set <key> <value>')
114
+ .description('set chat configuration')
115
+ .action(async (key, value) => {
116
+ await this.setChatConfig(key, value);
117
+ });
118
+
119
+ chatCmd
120
+ .command('config get [key]')
121
+ .description('get chat configuration')
122
+ .action(async key => {
123
+ await this.getChatConfig(key);
124
+ });
125
+
126
+ // Chat with files
127
+ chatCmd
128
+ .command('file <file>')
129
+ .description('chat about specific file')
130
+ .option(
131
+ '--action <action>',
132
+ 'action to perform (explain, review, improve)',
133
+ 'explain'
134
+ )
135
+ .option('--model <model>', 'AI model to use')
136
+ .action(async (file, options) => {
137
+ await this.chatWithFile(file, options);
138
+ });
139
+
140
+ // Chat with code
141
+ chatCmd
142
+ .command('code')
143
+ .description('chat about code from clipboard or input')
144
+ .option('--clipboard', 'read code from clipboard')
145
+ .option(
146
+ '--action <action>',
147
+ 'action to perform (explain, review, improve)',
148
+ 'explain'
149
+ )
150
+ .action(async options => {
151
+ await this.chatWithCode(options);
152
+ });
153
+
154
+ // Chat templates
155
+ chatCmd.command('template').description('manage chat templates');
156
+
157
+ chatCmd
158
+ .command('template list')
159
+ .description('list available chat templates')
160
+ .action(async () => {
161
+ await this.listChatTemplates();
162
+ });
163
+
164
+ chatCmd
165
+ .command('template use <name>')
166
+ .description('start chat with template')
167
+ .option('--vars <vars>', 'template variables (JSON or key=value)')
168
+ .action(async (name, options) => {
169
+ await this.useChatTemplate(name, options);
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Start a new chat session
175
+ */
176
+ private async startChatSession(options: any): Promise<void> {
177
+ try {
178
+ logger.info('Starting new chat session...');
179
+
180
+ const session = await this.createNewSession(options);
181
+ this.activeSessions.set(session.id, session);
182
+
183
+ console.log(chalk.green(`\n🤖 Chat Session Started (${session.model})`));
184
+ console.log(chalk.gray(`Session ID: ${session.id}`));
185
+ if (session.context) {
186
+ console.log(chalk.gray(`Context: ${session.context}`));
187
+ }
188
+ console.log(
189
+ chalk.gray('Type "exit" to end the session, "help" for commands\n')
190
+ );
191
+
192
+ await this.runChatLoop(session);
193
+ } catch (error) {
194
+ throw errorHandler.createError(
195
+ 'WUNDR_CHAT_START_FAILED',
196
+ 'Failed to start chat session',
197
+ { options },
198
+ true
199
+ );
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Resume existing chat session
205
+ */
206
+ private async resumeChatSession(sessionId: string): Promise<void> {
207
+ try {
208
+ logger.info(`Resuming chat session: ${sessionId}`);
209
+
210
+ const session = await this.loadChatSession(sessionId);
211
+ if (!session) {
212
+ throw new Error(`Chat session not found: ${sessionId}`);
213
+ }
214
+
215
+ this.activeSessions.set(sessionId, session);
216
+
217
+ console.log(chalk.green(`\n🤖 Chat Session Resumed (${session.model})`));
218
+ console.log(chalk.gray(`Session ID: ${session.id}`));
219
+ console.log(chalk.gray(`Messages: ${session.history.length}`));
220
+ console.log(
221
+ chalk.gray(`Last updated: ${session.updated.toLocaleString()}\n`)
222
+ );
223
+
224
+ // Show recent messages
225
+ if (session.history.length > 0) {
226
+ console.log(chalk.blue('Recent messages:'));
227
+ session.history.slice(-3).forEach(msg => {
228
+ const role = msg.role === 'user' ? 'You' : 'AI';
229
+ const content =
230
+ msg.content.length > 100
231
+ ? msg.content.substring(0, 100) + '...'
232
+ : msg.content;
233
+ console.log(` ${chalk.cyan(role)}: ${content}`);
234
+ });
235
+ console.log();
236
+ }
237
+
238
+ await this.runChatLoop(session);
239
+ } catch (error) {
240
+ throw errorHandler.createError(
241
+ 'WUNDR_CHAT_RESUME_FAILED',
242
+ 'Failed to resume chat session',
243
+ { sessionId },
244
+ true
245
+ );
246
+ }
247
+ }
248
+
249
+ /**
250
+ * List chat sessions
251
+ */
252
+ private async listChatSessions(options: any): Promise<void> {
253
+ try {
254
+ const sessions = await this.getAllChatSessions();
255
+ const filteredSessions = options.activeOnly
256
+ ? sessions.filter(s => this.activeSessions.has(s.id))
257
+ : sessions;
258
+
259
+ if (filteredSessions.length === 0) {
260
+ logger.info('No chat sessions found');
261
+ return;
262
+ }
263
+
264
+ logger.info(`Chat sessions (${filteredSessions.length}):`);
265
+
266
+ const sessionData = filteredSessions.map(session => ({
267
+ ID: session.id,
268
+ Model: session.model,
269
+ Messages: session.history.length,
270
+ Created: session.created.toLocaleDateString(),
271
+ Updated: session.updated.toLocaleDateString(),
272
+ Active: this.activeSessions.has(session.id) ? '✓' : '✗',
273
+ }));
274
+
275
+ console.table(sessionData);
276
+ } catch (error) {
277
+ throw errorHandler.createError(
278
+ 'WUNDR_CHAT_LIST_FAILED',
279
+ 'Failed to list chat sessions',
280
+ { options },
281
+ true
282
+ );
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Ask a single question
288
+ */
289
+ private async askSingleQuestion(
290
+ message: string,
291
+ options: any
292
+ ): Promise<void> {
293
+ try {
294
+ logger.debug('Processing single question...');
295
+
296
+ let session: ChatSession | null;
297
+
298
+ if (options.session) {
299
+ session = await this.loadChatSession(options.session);
300
+ if (!session) {
301
+ throw new Error(`Chat session not found: ${options.session}`);
302
+ }
303
+ } else {
304
+ session = await this.createNewSession({
305
+ model: options.model,
306
+ context: options.context,
307
+ });
308
+ }
309
+
310
+ const response = await this.sendMessage(session, message);
311
+
312
+ console.log(chalk.cyan('\nAI Response:'));
313
+ console.log(response);
314
+ console.log();
315
+
316
+ // Save session if it was created for this question
317
+ if (!options.session) {
318
+ await this.saveChatSession(session);
319
+ logger.debug(`Session saved: ${session.id}`);
320
+ }
321
+ } catch (error) {
322
+ throw errorHandler.createError(
323
+ 'WUNDR_CHAT_ASK_FAILED',
324
+ 'Failed to process question',
325
+ { message, options },
326
+ true
327
+ );
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Export chat session
333
+ */
334
+ private async exportChatSession(
335
+ sessionId: string,
336
+ options: any
337
+ ): Promise<void> {
338
+ try {
339
+ logger.info(`Exporting chat session: ${sessionId}`);
340
+
341
+ const session = await this.loadChatSession(sessionId);
342
+ if (!session) {
343
+ throw new Error(`Chat session not found: ${sessionId}`);
344
+ }
345
+
346
+ let exportedContent: string;
347
+
348
+ switch (options.format) {
349
+ case 'json':
350
+ exportedContent = JSON.stringify(session, null, 2);
351
+ break;
352
+ case 'markdown':
353
+ exportedContent = this.convertToMarkdown(session);
354
+ break;
355
+ case 'txt':
356
+ exportedContent = this.convertToText(session);
357
+ break;
358
+ default:
359
+ throw new Error(`Unsupported export format: ${options.format}`);
360
+ }
361
+
362
+ const outputPath =
363
+ options.output || `chat-${sessionId}.${options.format}`;
364
+ await fs.writeFile(outputPath, exportedContent);
365
+
366
+ logger.success(`Chat session exported: ${outputPath}`);
367
+ } catch (error) {
368
+ throw errorHandler.createError(
369
+ 'WUNDR_CHAT_EXPORT_FAILED',
370
+ 'Failed to export chat session',
371
+ { sessionId, options },
372
+ true
373
+ );
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Import chat session
379
+ */
380
+ private async importChatSession(file: string, options: any): Promise<void> {
381
+ try {
382
+ logger.info(`Importing chat session: ${file}`);
383
+
384
+ if (!(await fs.pathExists(file))) {
385
+ throw new Error(`File not found: ${file}`);
386
+ }
387
+
388
+ let session: ChatSession;
389
+
390
+ switch (options.format) {
391
+ case 'json':
392
+ session = await fs.readJson(file);
393
+ break;
394
+ case 'markdown':
395
+ session = await this.parseMarkdown(file);
396
+ break;
397
+ default:
398
+ throw new Error(`Unsupported import format: ${options.format}`);
399
+ }
400
+
401
+ // Generate new ID for imported session
402
+ session.id = `imported-${Date.now()}`;
403
+ session.created = new Date();
404
+ session.updated = new Date();
405
+
406
+ await this.saveChatSession(session);
407
+ logger.success(`Chat session imported: ${session.id}`);
408
+ } catch (error) {
409
+ throw errorHandler.createError(
410
+ 'WUNDR_CHAT_IMPORT_FAILED',
411
+ 'Failed to import chat session',
412
+ { file, options },
413
+ true
414
+ );
415
+ }
416
+ }
417
+
418
+ /**
419
+ * Delete chat session
420
+ */
421
+ private async deleteChatSession(
422
+ sessionId: string,
423
+ options: any
424
+ ): Promise<void> {
425
+ try {
426
+ const session = await this.loadChatSession(sessionId);
427
+ if (!session) {
428
+ throw new Error(`Chat session not found: ${sessionId}`);
429
+ }
430
+
431
+ if (!options.force) {
432
+ const { confirm } = await inquirer.prompt([
433
+ {
434
+ type: 'confirm',
435
+ name: 'confirm',
436
+ message: `Delete chat session ${sessionId}? (${session.history.length} messages)`,
437
+ default: false,
438
+ },
439
+ ]);
440
+
441
+ if (!confirm) {
442
+ logger.info('Deletion cancelled');
443
+ return;
444
+ }
445
+ }
446
+
447
+ await this.deleteChatSessionFile(sessionId);
448
+ this.activeSessions.delete(sessionId);
449
+
450
+ logger.success(`Chat session deleted: ${sessionId}`);
451
+ } catch (error) {
452
+ throw errorHandler.createError(
453
+ 'WUNDR_CHAT_DELETE_FAILED',
454
+ 'Failed to delete chat session',
455
+ { sessionId, options },
456
+ true
457
+ );
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Chat with specific file
463
+ */
464
+ private async chatWithFile(file: string, options: any): Promise<void> {
465
+ try {
466
+ if (!(await fs.pathExists(file))) {
467
+ throw new Error(`File not found: ${file}`);
468
+ }
469
+
470
+ const fileContent = await fs.readFile(file, 'utf8');
471
+ const fileName = path.basename(file);
472
+
473
+ const actionPrompts = {
474
+ explain: `Please explain this code from ${fileName}:`,
475
+ review: `Please review this code from ${fileName} for potential issues:`,
476
+ improve: `Please suggest improvements for this code from ${fileName}:`,
477
+ };
478
+
479
+ const prompt =
480
+ actionPrompts[options.action as keyof typeof actionPrompts] ||
481
+ actionPrompts.explain;
482
+ const message = `${prompt}\n\n\`\`\`\n${fileContent}\n\`\`\``;
483
+
484
+ await this.askSingleQuestion(message, { model: options.model });
485
+ } catch (error) {
486
+ throw errorHandler.createError(
487
+ 'WUNDR_CHAT_FILE_FAILED',
488
+ 'Failed to chat with file',
489
+ { file, options },
490
+ true
491
+ );
492
+ }
493
+ }
494
+
495
+ /**
496
+ * Chat with code
497
+ */
498
+ private async chatWithCode(options: any): Promise<void> {
499
+ try {
500
+ let code: string;
501
+
502
+ if (options.clipboard) {
503
+ // Read from clipboard (would need clipboard library)
504
+ code = 'Code from clipboard'; // Placeholder
505
+ } else {
506
+ const { codeInput } = await inquirer.prompt([
507
+ {
508
+ type: 'editor',
509
+ name: 'codeInput',
510
+ message: 'Enter your code:',
511
+ },
512
+ ]);
513
+ code = codeInput;
514
+ }
515
+
516
+ const actionPrompts = {
517
+ explain: 'Please explain this code:',
518
+ review: 'Please review this code for potential issues:',
519
+ improve: 'Please suggest improvements for this code:',
520
+ };
521
+
522
+ const prompt =
523
+ actionPrompts[options.action as keyof typeof actionPrompts] ||
524
+ actionPrompts.explain;
525
+ const message = `${prompt}\n\n\`\`\`\n${code}\n\`\`\``;
526
+
527
+ await this.askSingleQuestion(message, {});
528
+ } catch (error) {
529
+ throw errorHandler.createError(
530
+ 'WUNDR_CHAT_CODE_FAILED',
531
+ 'Failed to chat with code',
532
+ { options },
533
+ true
534
+ );
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Helper methods for chat operations
540
+ */
541
+ private async createNewSession(options: any): Promise<ChatSession> {
542
+ const config = this.configManager.getConfig();
543
+
544
+ return {
545
+ id: options.sessionName || `session-${Date.now()}`,
546
+ model: options.model || config.ai.model,
547
+ context: options.context,
548
+ history: [],
549
+ created: new Date(),
550
+ updated: new Date(),
551
+ };
552
+ }
553
+
554
+ private async runChatLoop(session: ChatSession): Promise<void> {
555
+ while (true) {
556
+ try {
557
+ const { message } = await inquirer.prompt([
558
+ {
559
+ type: 'input',
560
+ name: 'message',
561
+ message: chalk.green('You:'),
562
+ validate: input => input.length > 0 || 'Message cannot be empty',
563
+ },
564
+ ]);
565
+
566
+ if (message.toLowerCase() === 'exit') {
567
+ break;
568
+ }
569
+
570
+ if (message.toLowerCase() === 'help') {
571
+ this.showChatHelp();
572
+ continue;
573
+ }
574
+
575
+ if (message.startsWith('/')) {
576
+ await this.handleChatCommand(session, message);
577
+ continue;
578
+ }
579
+
580
+ const response = await this.sendMessage(session, message);
581
+ console.log(chalk.cyan(`\nAI: ${response}\n`));
582
+ } catch (error) {
583
+ logger.error('Chat error:', error);
584
+ console.log(
585
+ chalk.red('Sorry, there was an error processing your message.\n')
586
+ );
587
+ }
588
+ }
589
+
590
+ // Save session when exiting
591
+ await this.saveChatSession(session);
592
+ this.activeSessions.delete(session.id);
593
+ logger.success('Chat session ended and saved');
594
+ }
595
+
596
+ private async sendMessage(
597
+ session: ChatSession,
598
+ message: string
599
+ ): Promise<string> {
600
+ // Add user message to history
601
+ const userMessage: ChatMessage = {
602
+ role: 'user',
603
+ content: message,
604
+ timestamp: new Date(),
605
+ };
606
+ session.history.push(userMessage);
607
+
608
+ // Call AI service (mock implementation)
609
+ const response = await this.callAI(session, message);
610
+
611
+ // Add AI response to history
612
+ const aiMessage: ChatMessage = {
613
+ role: 'assistant',
614
+ content: response,
615
+ timestamp: new Date(),
616
+ };
617
+ session.history.push(aiMessage);
618
+
619
+ session.updated = new Date();
620
+
621
+ return response;
622
+ }
623
+
624
+ private async callAI(session: ChatSession, message: string): Promise<string> {
625
+ // Mock AI service call
626
+ // In a real implementation, this would call Claude, GPT, etc.
627
+ return `This is a mock response to: "${message}". The AI would provide a helpful response here based on the context and conversation history.`;
628
+ }
629
+
630
+ private showChatHelp(): void {
631
+ console.log(chalk.blue('\nChat Commands:'));
632
+ console.log(' exit - End the chat session');
633
+ console.log(' help - Show this help message');
634
+ console.log(' /clear - Clear chat history');
635
+ console.log(' /save - Save chat session');
636
+ console.log(' /export - Export chat session');
637
+ console.log(' /context - Show current context');
638
+ console.log(' /model - Change AI model');
639
+ console.log();
640
+ }
641
+
642
+ private async handleChatCommand(
643
+ session: ChatSession,
644
+ command: string
645
+ ): Promise<void> {
646
+ const [cmd, ...args] = command.slice(1).split(' ');
647
+
648
+ switch (cmd) {
649
+ case 'clear':
650
+ session.history = [];
651
+ console.log(chalk.green('Chat history cleared\n'));
652
+ break;
653
+
654
+ case 'save':
655
+ await this.saveChatSession(session);
656
+ console.log(chalk.green(`Session saved: ${session.id}\n`));
657
+ break;
658
+
659
+ case 'export':
660
+ const format = args[0] || 'markdown';
661
+ await this.exportChatSession(session.id, {
662
+ format,
663
+ output: `${session.id}.${format}`,
664
+ });
665
+ break;
666
+
667
+ case 'context':
668
+ console.log(chalk.blue('Current Context:'));
669
+ console.log(` Model: ${session.model}`);
670
+ console.log(` Messages: ${session.history.length}`);
671
+ console.log(` Context: ${session.context || 'None'}\n`);
672
+ break;
673
+
674
+ case 'model':
675
+ if (args[0]) {
676
+ session.model = args[0];
677
+ console.log(chalk.green(`Model changed to: ${args[0]}\n`));
678
+ } else {
679
+ console.log(chalk.yellow('Current model: ' + session.model + '\n'));
680
+ }
681
+ break;
682
+
683
+ default:
684
+ console.log(chalk.red(`Unknown command: /${cmd}\n`));
685
+ }
686
+ }
687
+
688
+ private async saveChatSession(session: ChatSession): Promise<void> {
689
+ const sessionPath = path.join(
690
+ process.cwd(),
691
+ '.wundr',
692
+ 'chat',
693
+ `${session.id}.json`
694
+ );
695
+ await fs.ensureDir(path.dirname(sessionPath));
696
+ await fs.writeJson(sessionPath, session, { spaces: 2 });
697
+ }
698
+
699
+ private async loadChatSession(
700
+ sessionId: string
701
+ ): Promise<ChatSession | null> {
702
+ const sessionPath = path.join(
703
+ process.cwd(),
704
+ '.wundr',
705
+ 'chat',
706
+ `${sessionId}.json`
707
+ );
708
+ if (await fs.pathExists(sessionPath)) {
709
+ const data = await fs.readJson(sessionPath);
710
+ data.created = new Date(data.created);
711
+ data.updated = new Date(data.updated);
712
+ data.history = data.history.map((msg: any) => ({
713
+ ...msg,
714
+ timestamp: new Date(msg.timestamp),
715
+ }));
716
+ return data;
717
+ }
718
+ return null;
719
+ }
720
+
721
+ private async getAllChatSessions(): Promise<ChatSession[]> {
722
+ const chatDir = path.join(process.cwd(), '.wundr', 'chat');
723
+ if (!(await fs.pathExists(chatDir))) {
724
+ return [];
725
+ }
726
+
727
+ const files = await fs.readdir(chatDir);
728
+ const jsonFiles = files.filter(f => f.endsWith('.json'));
729
+
730
+ const sessions: ChatSession[] = [];
731
+ for (const file of jsonFiles) {
732
+ try {
733
+ const sessionId = path.basename(file, '.json');
734
+ const session = await this.loadChatSession(sessionId);
735
+ if (session) {
736
+ sessions.push(session);
737
+ }
738
+ } catch (error) {
739
+ logger.debug(`Failed to load session ${file}:`, error);
740
+ }
741
+ }
742
+
743
+ return sessions;
744
+ }
745
+
746
+ private async deleteChatSessionFile(sessionId: string): Promise<void> {
747
+ const sessionPath = path.join(
748
+ process.cwd(),
749
+ '.wundr',
750
+ 'chat',
751
+ `${sessionId}.json`
752
+ );
753
+ if (await fs.pathExists(sessionPath)) {
754
+ await fs.remove(sessionPath);
755
+ }
756
+ }
757
+
758
+ private convertToMarkdown(session: ChatSession): string {
759
+ let markdown = `# Chat Session: ${session.id}\n\n`;
760
+ markdown += `**Model:** ${session.model}\n`;
761
+ markdown += `**Created:** ${session.created.toLocaleString()}\n`;
762
+ markdown += `**Updated:** ${session.updated.toLocaleString()}\n`;
763
+ if (session.context) {
764
+ markdown += `**Context:** ${session.context}\n`;
765
+ }
766
+ markdown += `**Messages:** ${session.history.length}\n\n`;
767
+ markdown += '---\n\n';
768
+
769
+ session.history.forEach(msg => {
770
+ const role = msg.role === 'user' ? '**You**' : '**AI**';
771
+ markdown += `${role} (${msg.timestamp.toLocaleTimeString()}):\n\n`;
772
+ markdown += `${msg.content}\n\n`;
773
+ markdown += '---\n\n';
774
+ });
775
+
776
+ return markdown;
777
+ }
778
+
779
+ private convertToText(session: ChatSession): string {
780
+ let text = `Chat Session: ${session.id}\n`;
781
+ text += `Model: ${session.model}\n`;
782
+ text += `Created: ${session.created.toLocaleString()}\n`;
783
+ text += `Updated: ${session.updated.toLocaleString()}\n`;
784
+ text += `Messages: ${session.history.length}\n\n`;
785
+ text += '='.repeat(50) + '\n\n';
786
+
787
+ session.history.forEach(msg => {
788
+ const role = msg.role === 'user' ? 'You' : 'AI';
789
+ text += `[${msg.timestamp.toLocaleTimeString()}] ${role}:\n`;
790
+ text += `${msg.content}\n\n`;
791
+ text += '-'.repeat(30) + '\n\n';
792
+ });
793
+
794
+ return text;
795
+ }
796
+
797
+ private async parseMarkdown(file: string): Promise<ChatSession> {
798
+ // Implementation to parse markdown format back to session
799
+ const content = await fs.readFile(file, 'utf8');
800
+
801
+ // This is a simplified parser - a real implementation would be more robust
802
+ return {
803
+ id: `parsed-${Date.now()}`,
804
+ model: 'claude-3',
805
+ history: [],
806
+ created: new Date(),
807
+ updated: new Date(),
808
+ };
809
+ }
810
+
811
+ // Configuration methods
812
+ private async setChatConfig(key: string, value: string): Promise<void> {
813
+ this.configManager.set(`chat.${key}`, value);
814
+ await this.configManager.saveConfig();
815
+ logger.success(`Chat configuration updated: ${key} = ${value}`);
816
+ }
817
+
818
+ private async getChatConfig(key?: string): Promise<void> {
819
+ if (key) {
820
+ const value = this.configManager.get(`chat.${key}`);
821
+ console.log(`${key}: ${value}`);
822
+ } else {
823
+ const chatConfig = this.configManager.get('chat') || {};
824
+ console.log(JSON.stringify(chatConfig, null, 2));
825
+ }
826
+ }
827
+
828
+ // Template methods
829
+ private async listChatTemplates(): Promise<void> {
830
+ const templates = [
831
+ 'code-review',
832
+ 'explain-code',
833
+ 'debug-help',
834
+ 'architecture-review',
835
+ 'performance-optimization',
836
+ ];
837
+
838
+ console.log('Available chat templates:');
839
+ templates.forEach(template => {
840
+ console.log(` - ${template}`);
841
+ });
842
+ }
843
+
844
+ private async useChatTemplate(name: string, options: any): Promise<void> {
845
+ const templates: Record<string, string> = {
846
+ 'code-review':
847
+ 'Please review the following code for best practices, potential bugs, and improvements:',
848
+ 'explain-code': 'Please explain what this code does and how it works:',
849
+ 'debug-help':
850
+ "I'm having trouble with this code. Can you help me debug it?",
851
+ 'architecture-review':
852
+ 'Please review this architectural design and suggest improvements:',
853
+ 'performance-optimization':
854
+ 'Please suggest performance optimizations for this code:',
855
+ };
856
+
857
+ const template = templates[name];
858
+ if (!template) {
859
+ throw new Error(`Template not found: ${name}`);
860
+ }
861
+
862
+ // Process template variables if provided
863
+ let processedTemplate = template;
864
+ if (options.vars) {
865
+ const variables = JSON.parse(options.vars);
866
+ Object.entries(variables).forEach(([key, value]) => {
867
+ processedTemplate = processedTemplate.replace(
868
+ `{{${key}}}`,
869
+ String(value)
870
+ );
871
+ });
872
+ }
873
+
874
+ await this.askSingleQuestion(processedTemplate, {});
875
+ }
876
+ }