@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,546 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { logger } from '../utils/logger';
4
+ import { ConversationContext } from '../ai/ai-service';
5
+
6
+ /**
7
+ * Project context information
8
+ */
9
+ export interface ProjectContext {
10
+ path: string;
11
+ type: string;
12
+ packageJson?: any;
13
+ tsConfig?: any;
14
+ gitInfo?: GitInfo;
15
+ dependencies?: string[];
16
+ devDependencies?: string[];
17
+ scripts?: Record<string, string>;
18
+ lastAnalysis?: AnalysisContext;
19
+ }
20
+
21
+ /**
22
+ * Git repository information
23
+ */
24
+ export interface GitInfo {
25
+ branch: string;
26
+ remote?: string;
27
+ hasChanges: boolean;
28
+ lastCommit?: string;
29
+ }
30
+
31
+ /**
32
+ * Analysis context from previous runs
33
+ */
34
+ export interface AnalysisContext {
35
+ timestamp: Date;
36
+ findings: number;
37
+ quality: number;
38
+ duplicates: number;
39
+ dependencies: number;
40
+ recommendations: string[];
41
+ }
42
+
43
+ /**
44
+ * User session context
45
+ */
46
+ export interface SessionContext {
47
+ sessionId: string;
48
+ startTime: Date;
49
+ lastActivity: Date;
50
+ commandHistory: CommandHistoryEntry[];
51
+ currentGoal?: string;
52
+ preferences: UserPreferences;
53
+ projectContext?: ProjectContext;
54
+ }
55
+
56
+ /**
57
+ * Command history entry
58
+ */
59
+ export interface CommandHistoryEntry {
60
+ command: string;
61
+ timestamp: Date;
62
+ success: boolean;
63
+ duration: number;
64
+ output?: string;
65
+ error?: string;
66
+ }
67
+
68
+ /**
69
+ * User preferences
70
+ */
71
+ export interface UserPreferences {
72
+ verbosity: 'minimal' | 'normal' | 'verbose';
73
+ confirmCommands: boolean;
74
+ autoSuggest: boolean;
75
+ theme: 'light' | 'dark' | 'auto';
76
+ language: string;
77
+ modelPreference?: string;
78
+ }
79
+
80
+ /**
81
+ * Context manager for maintaining conversation and project context
82
+ */
83
+ export class ContextManager {
84
+ private sessionsDir: string;
85
+ private currentSession?: SessionContext;
86
+ private projectContextCache: Map<string, ProjectContext> = new Map();
87
+
88
+ constructor(baseDir: string = '.wundr') {
89
+ this.sessionsDir = path.join(process.cwd(), baseDir, 'contexts');
90
+ this.ensureDirectories();
91
+ }
92
+
93
+ /**
94
+ * Initialize or resume a session
95
+ */
96
+ async initializeSession(sessionId?: string): Promise<SessionContext> {
97
+ if (sessionId) {
98
+ const existingSession = await this.loadSession(sessionId);
99
+ if (existingSession) {
100
+ this.currentSession = existingSession;
101
+ this.currentSession.lastActivity = new Date();
102
+ await this.saveSession(this.currentSession);
103
+ return this.currentSession;
104
+ }
105
+ }
106
+
107
+ // Create new session
108
+ const newSessionId = sessionId || `session-${Date.now()}`;
109
+ this.currentSession = {
110
+ sessionId: newSessionId,
111
+ startTime: new Date(),
112
+ lastActivity: new Date(),
113
+ commandHistory: [],
114
+ preferences: this.getDefaultPreferences(),
115
+ projectContext: await this.detectProjectContext(),
116
+ };
117
+
118
+ await this.saveSession(this.currentSession);
119
+ logger.info(`Initialized session: ${newSessionId}`);
120
+
121
+ return this.currentSession;
122
+ }
123
+
124
+ /**
125
+ * Get current session context
126
+ */
127
+ getCurrentSession(): SessionContext | undefined {
128
+ return this.currentSession;
129
+ }
130
+
131
+ /**
132
+ * Add command to history
133
+ */
134
+ async addCommandToHistory(
135
+ command: string,
136
+ success: boolean,
137
+ duration: number,
138
+ output?: string,
139
+ error?: string
140
+ ): Promise<void> {
141
+ if (!this.currentSession) {
142
+ throw new Error('No active session');
143
+ }
144
+
145
+ const entry: CommandHistoryEntry = {
146
+ command,
147
+ timestamp: new Date(),
148
+ success,
149
+ duration,
150
+ output,
151
+ error,
152
+ };
153
+
154
+ this.currentSession.commandHistory.push(entry);
155
+ this.currentSession.lastActivity = new Date();
156
+
157
+ // Keep only last 50 commands
158
+ if (this.currentSession.commandHistory.length > 50) {
159
+ this.currentSession.commandHistory =
160
+ this.currentSession.commandHistory.slice(-50);
161
+ }
162
+
163
+ await this.saveSession(this.currentSession);
164
+ }
165
+
166
+ /**
167
+ * Update user goal
168
+ */
169
+ async updateCurrentGoal(goal: string): Promise<void> {
170
+ if (!this.currentSession) {
171
+ throw new Error('No active session');
172
+ }
173
+
174
+ this.currentSession.currentGoal = goal;
175
+ this.currentSession.lastActivity = new Date();
176
+ await this.saveSession(this.currentSession);
177
+ }
178
+
179
+ /**
180
+ * Update user preferences
181
+ */
182
+ async updatePreferences(
183
+ preferences: Partial<UserPreferences>
184
+ ): Promise<void> {
185
+ if (!this.currentSession) {
186
+ throw new Error('No active session');
187
+ }
188
+
189
+ this.currentSession.preferences = {
190
+ ...this.currentSession.preferences,
191
+ ...preferences,
192
+ };
193
+
194
+ this.currentSession.lastActivity = new Date();
195
+ await this.saveSession(this.currentSession);
196
+ }
197
+
198
+ /**
199
+ * Get conversation context for AI
200
+ */
201
+ getConversationContext(): ConversationContext {
202
+ if (!this.currentSession) {
203
+ return {};
204
+ }
205
+
206
+ const recentCommands = this.currentSession.commandHistory
207
+ .slice(-5)
208
+ .map(entry => entry.command);
209
+
210
+ return {
211
+ projectPath: this.currentSession.projectContext?.path,
212
+ projectType: this.currentSession.projectContext?.type,
213
+ recentCommands,
214
+ currentGoal: this.currentSession.currentGoal,
215
+ userPreferences: this.currentSession.preferences,
216
+ sessionMetadata: {
217
+ sessionId: this.currentSession.sessionId,
218
+ duration: Date.now() - this.currentSession.startTime.getTime(),
219
+ commandCount: this.currentSession.commandHistory.length,
220
+ },
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Detect project context
226
+ */
227
+ async detectProjectContext(
228
+ projectPath?: string
229
+ ): Promise<ProjectContext | undefined> {
230
+ const targetPath = projectPath || process.cwd();
231
+
232
+ // Check cache first
233
+ if (this.projectContextCache.has(targetPath)) {
234
+ return this.projectContextCache.get(targetPath);
235
+ }
236
+
237
+ try {
238
+ const context: ProjectContext = {
239
+ path: targetPath,
240
+ type: 'unknown',
241
+ };
242
+
243
+ // Read package.json
244
+ const packageJsonPath = path.join(targetPath, 'package.json');
245
+ if (await fs.pathExists(packageJsonPath)) {
246
+ context.packageJson = await fs.readJson(packageJsonPath);
247
+ context.dependencies = Object.keys(
248
+ context.packageJson.dependencies || {}
249
+ );
250
+ context.devDependencies = Object.keys(
251
+ context.packageJson.devDependencies || {}
252
+ );
253
+ context.scripts = context.packageJson.scripts || {};
254
+ context.type = this.detectProjectType(context.packageJson);
255
+ }
256
+
257
+ // Read tsconfig.json
258
+ const tsConfigPath = path.join(targetPath, 'tsconfig.json');
259
+ if (await fs.pathExists(tsConfigPath)) {
260
+ context.tsConfig = await fs.readJson(tsConfigPath);
261
+ }
262
+
263
+ // Get git info
264
+ context.gitInfo = await this.getGitInfo(targetPath);
265
+
266
+ // Cache the context
267
+ this.projectContextCache.set(targetPath, context);
268
+
269
+ return context;
270
+ } catch (error) {
271
+ logger.debug('Failed to detect project context:', error);
272
+ return undefined;
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Update analysis context
278
+ */
279
+ async updateAnalysisContext(
280
+ findings: number,
281
+ quality: number,
282
+ duplicates: number,
283
+ dependencies: number,
284
+ recommendations: string[]
285
+ ): Promise<void> {
286
+ if (!this.currentSession?.projectContext) {
287
+ return;
288
+ }
289
+
290
+ this.currentSession.projectContext.lastAnalysis = {
291
+ timestamp: new Date(),
292
+ findings,
293
+ quality,
294
+ duplicates,
295
+ dependencies,
296
+ recommendations,
297
+ };
298
+
299
+ await this.saveSession(this.currentSession);
300
+ }
301
+
302
+ /**
303
+ * Get recent command patterns
304
+ */
305
+ getRecentCommandPatterns(): { command: string; frequency: number }[] {
306
+ if (!this.currentSession) {
307
+ return [];
308
+ }
309
+
310
+ const commandFreq: Record<string, number> = {};
311
+
312
+ this.currentSession.commandHistory.forEach(entry => {
313
+ const baseCommand = entry.command.split(' ').slice(0, 2).join(' ');
314
+ commandFreq[baseCommand] = (commandFreq[baseCommand] || 0) + 1;
315
+ });
316
+
317
+ return Object.entries(commandFreq)
318
+ .map(([command, frequency]) => ({ command, frequency }))
319
+ .sort((a, b) => b.frequency - a.frequency);
320
+ }
321
+
322
+ /**
323
+ * End current session
324
+ */
325
+ async endSession(): Promise<void> {
326
+ if (!this.currentSession) {
327
+ return;
328
+ }
329
+
330
+ this.currentSession.lastActivity = new Date();
331
+ await this.saveSession(this.currentSession);
332
+
333
+ logger.info(`Session ended: ${this.currentSession.sessionId}`);
334
+ this.currentSession = undefined;
335
+ }
336
+
337
+ /**
338
+ * List all sessions
339
+ */
340
+ async listSessions(): Promise<
341
+ { id: string; startTime: Date; lastActivity: Date; commandCount: number }[]
342
+ > {
343
+ try {
344
+ const files = await fs.readdir(this.sessionsDir);
345
+ const sessions: Array<{
346
+ id: string;
347
+ startTime: Date;
348
+ lastActivity: Date;
349
+ commandCount: number;
350
+ }> = [];
351
+
352
+ for (const file of files) {
353
+ if (file.endsWith('.json')) {
354
+ try {
355
+ const sessionData = await fs.readJson(
356
+ path.join(this.sessionsDir, file)
357
+ );
358
+ sessions.push({
359
+ id: sessionData.sessionId,
360
+ startTime: new Date(sessionData.startTime),
361
+ lastActivity: new Date(sessionData.lastActivity),
362
+ commandCount: sessionData.commandHistory.length,
363
+ });
364
+ } catch (error) {
365
+ logger.debug(`Failed to read session file ${file}:`, error);
366
+ }
367
+ }
368
+ }
369
+
370
+ return sessions.sort(
371
+ (a, b) => b.lastActivity.getTime() - a.lastActivity.getTime()
372
+ );
373
+ } catch (error) {
374
+ logger.debug('Failed to list sessions:', error);
375
+ return [];
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Load session from disk
381
+ */
382
+ private async loadSession(sessionId: string): Promise<SessionContext | null> {
383
+ try {
384
+ const sessionPath = path.join(this.sessionsDir, `${sessionId}.json`);
385
+
386
+ if (await fs.pathExists(sessionPath)) {
387
+ const data = await fs.readJson(sessionPath);
388
+
389
+ // Convert date strings back to Date objects
390
+ data.startTime = new Date(data.startTime);
391
+ data.lastActivity = new Date(data.lastActivity);
392
+ data.commandHistory = data.commandHistory.map((entry: any) => ({
393
+ ...entry,
394
+ timestamp: new Date(entry.timestamp),
395
+ }));
396
+
397
+ if (data.projectContext?.lastAnalysis) {
398
+ data.projectContext.lastAnalysis.timestamp = new Date(
399
+ data.projectContext.lastAnalysis.timestamp
400
+ );
401
+ }
402
+
403
+ return data;
404
+ }
405
+ } catch (error) {
406
+ logger.debug(`Failed to load session ${sessionId}:`, error);
407
+ }
408
+
409
+ return null;
410
+ }
411
+
412
+ /**
413
+ * Save session to disk
414
+ */
415
+ private async saveSession(session: SessionContext): Promise<void> {
416
+ try {
417
+ const sessionPath = path.join(
418
+ this.sessionsDir,
419
+ `${session.sessionId}.json`
420
+ );
421
+ await fs.writeJson(sessionPath, session, { spaces: 2 });
422
+ } catch (error) {
423
+ logger.error(`Failed to save session ${session.sessionId}:`, error);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Ensure required directories exist
429
+ */
430
+ private async ensureDirectories(): Promise<void> {
431
+ await fs.ensureDir(this.sessionsDir);
432
+ }
433
+
434
+ /**
435
+ * Get default user preferences
436
+ */
437
+ private getDefaultPreferences(): UserPreferences {
438
+ return {
439
+ verbosity: 'normal',
440
+ confirmCommands: false,
441
+ autoSuggest: true,
442
+ theme: 'auto',
443
+ language: 'en',
444
+ };
445
+ }
446
+
447
+ /**
448
+ * Detect project type from package.json
449
+ */
450
+ private detectProjectType(packageJson: any): string {
451
+ if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {
452
+ return 'react';
453
+ }
454
+
455
+ if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {
456
+ return 'next';
457
+ }
458
+
459
+ if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
460
+ return 'vue';
461
+ }
462
+
463
+ if (
464
+ packageJson.dependencies?.express ||
465
+ packageJson.devDependencies?.express
466
+ ) {
467
+ return 'express';
468
+ }
469
+
470
+ if (
471
+ packageJson.dependencies?.typescript ||
472
+ packageJson.devDependencies?.typescript
473
+ ) {
474
+ return 'typescript';
475
+ }
476
+
477
+ if (packageJson.type === 'module' || packageJson.main?.endsWith('.mjs')) {
478
+ return 'esm';
479
+ }
480
+
481
+ return 'node';
482
+ }
483
+
484
+ /**
485
+ * Get git repository information
486
+ */
487
+ private async getGitInfo(projectPath: string): Promise<GitInfo | undefined> {
488
+ try {
489
+ const { execSync } = require('child_process');
490
+ const cwd = projectPath;
491
+
492
+ // Check if it's a git repository
493
+ try {
494
+ execSync('git rev-parse --git-dir', { cwd, stdio: 'ignore' });
495
+ } catch {
496
+ return undefined;
497
+ }
498
+
499
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
500
+ cwd,
501
+ encoding: 'utf8',
502
+ }).trim();
503
+
504
+ let remote: string | undefined;
505
+ try {
506
+ remote = execSync('git config --get remote.origin.url', {
507
+ cwd,
508
+ encoding: 'utf8',
509
+ }).trim();
510
+ } catch {
511
+ // No remote configured
512
+ }
513
+
514
+ let hasChanges = false;
515
+ try {
516
+ const status = execSync('git status --porcelain', {
517
+ cwd,
518
+ encoding: 'utf8',
519
+ });
520
+ hasChanges = status.trim().length > 0;
521
+ } catch {
522
+ // Can't determine changes
523
+ }
524
+
525
+ let lastCommit: string | undefined;
526
+ try {
527
+ lastCommit = execSync('git log -1 --format="%h %s"', {
528
+ cwd,
529
+ encoding: 'utf8',
530
+ }).trim();
531
+ } catch {
532
+ // No commits yet
533
+ }
534
+
535
+ return {
536
+ branch,
537
+ remote,
538
+ hasChanges,
539
+ lastCommit,
540
+ };
541
+ } catch (error) {
542
+ logger.debug('Failed to get git info:', error);
543
+ return undefined;
544
+ }
545
+ }
546
+ }
@@ -0,0 +1,9 @@
1
+ export { ContextManager } from './context-manager';
2
+ export type {
3
+ ProjectContext,
4
+ SessionContext,
5
+ CommandHistoryEntry,
6
+ UserPreferences,
7
+ GitInfo,
8
+ AnalysisContext,
9
+ } from './context-manager';