@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,682 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const events_1 = require("events");
6
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
7
+ const path_1 = tslib_1.__importDefault(require("path"));
8
+ const logger_1 = require("../utils/logger");
9
+ /**
10
+ * Session manager for persistent context, user preferences, and workspace management
11
+ */
12
+ class SessionManager extends events_1.EventEmitter {
13
+ config;
14
+ conversationManager;
15
+ commandMapper;
16
+ activeSessions;
17
+ workspaces;
18
+ currentSessionId;
19
+ backupTimer;
20
+ cleanupTimer;
21
+ constructor(conversationManager, commandMapper, config = {}) {
22
+ super();
23
+ this.conversationManager = conversationManager;
24
+ this.commandMapper = commandMapper;
25
+ this.config = {
26
+ persistencePath: path_1.default.join(process.cwd(), '.wundr', 'sessions'),
27
+ sessionTimeout: 24 * 60, // 24 hours
28
+ maxSessions: 10,
29
+ autoDetectProjects: true,
30
+ enableContextLearning: true,
31
+ backupInterval: 15, // 15 minutes
32
+ ...config,
33
+ };
34
+ this.activeSessions = new Map();
35
+ this.workspaces = new Map();
36
+ this.initialize();
37
+ }
38
+ /**
39
+ * Create or resume a session
40
+ */
41
+ async createSession(userId, workspacePath, sessionId) {
42
+ const id = sessionId ||
43
+ `session_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
44
+ // Check if session already exists
45
+ if (this.activeSessions.has(id)) {
46
+ await this.resumeSession(id);
47
+ return id;
48
+ }
49
+ const workspace = workspacePath || process.cwd();
50
+ const projectContext = await this.detectProjectContext(workspace);
51
+ const session = {
52
+ id,
53
+ userId,
54
+ currentWorkspace: workspace,
55
+ recentCommands: [],
56
+ contextStack: [],
57
+ userPreferences: await this.loadUserPreferences(userId),
58
+ projectContext,
59
+ temporaryData: {},
60
+ created: new Date(),
61
+ lastAccessed: new Date(),
62
+ };
63
+ // Load workspace if it exists
64
+ const workspaceConfig = await this.loadWorkspace(workspace);
65
+ if (workspaceConfig) {
66
+ workspaceConfig.lastAccessed = new Date();
67
+ this.workspaces.set(workspace, workspaceConfig);
68
+ }
69
+ this.activeSessions.set(id, session);
70
+ this.currentSessionId = id;
71
+ await this.persistSession(session);
72
+ this.emit('session_created', { sessionId: id, userId, workspace });
73
+ logger_1.logger.debug(`Created session: ${id} for user: ${userId}`);
74
+ return id;
75
+ }
76
+ /**
77
+ * Resume an existing session
78
+ */
79
+ async resumeSession(sessionId) {
80
+ let session = this.activeSessions.get(sessionId);
81
+ if (!session) {
82
+ // Try to load from persistence
83
+ session = (await this.loadSession(sessionId)) || undefined;
84
+ if (!session) {
85
+ throw new Error(`Session not found: ${sessionId}`);
86
+ }
87
+ }
88
+ session.lastAccessed = new Date();
89
+ this.currentSessionId = sessionId;
90
+ this.activeSessions.set(sessionId, session);
91
+ // Refresh project context
92
+ if (session.projectContext) {
93
+ session.projectContext = await this.detectProjectContext(session.currentWorkspace);
94
+ }
95
+ await this.persistSession(session);
96
+ this.emit('session_resumed', { sessionId, session });
97
+ logger_1.logger.debug(`Resumed session: ${sessionId}`);
98
+ return session;
99
+ }
100
+ /**
101
+ * Get current session
102
+ */
103
+ getCurrentSession() {
104
+ if (!this.currentSessionId)
105
+ return null;
106
+ return this.activeSessions.get(this.currentSessionId) || null;
107
+ }
108
+ /**
109
+ * Get session by ID
110
+ */
111
+ getSession(sessionId) {
112
+ return this.activeSessions.get(sessionId) || null;
113
+ }
114
+ /**
115
+ * Update session context
116
+ */
117
+ async updateSessionContext(sessionId, contextType, contextData) {
118
+ const session = this.activeSessions.get(sessionId);
119
+ if (!session) {
120
+ throw new Error(`Session not found: ${sessionId}`);
121
+ }
122
+ // Add to context stack
123
+ session.contextStack.push({
124
+ type: contextType,
125
+ context: contextData,
126
+ timestamp: new Date(),
127
+ });
128
+ // Limit context stack size
129
+ if (session.contextStack.length > 50) {
130
+ session.contextStack = session.contextStack.slice(-25);
131
+ }
132
+ // Update specific context based on type
133
+ switch (contextType) {
134
+ case 'project':
135
+ session.projectContext = { ...session.projectContext, ...contextData };
136
+ break;
137
+ case 'conversation':
138
+ session.activeConversation = contextData.conversationId;
139
+ break;
140
+ }
141
+ session.lastAccessed = new Date();
142
+ await this.persistSession(session);
143
+ this.emit('context_updated', { sessionId, contextType, contextData });
144
+ }
145
+ /**
146
+ * Add command to session history
147
+ */
148
+ async addCommandToHistory(sessionId, command, success, duration) {
149
+ const session = this.activeSessions.get(sessionId);
150
+ if (!session) {
151
+ throw new Error(`Session not found: ${sessionId}`);
152
+ }
153
+ session.recentCommands.push({
154
+ command,
155
+ timestamp: new Date(),
156
+ success,
157
+ duration,
158
+ });
159
+ // Limit history size
160
+ const maxHistory = session.userPreferences.maxHistoryLength || 100;
161
+ if (session.recentCommands.length > maxHistory) {
162
+ session.recentCommands = session.recentCommands.slice(-maxHistory / 2);
163
+ }
164
+ session.lastAccessed = new Date();
165
+ await this.persistSession(session);
166
+ // Learn from command patterns if enabled
167
+ if (this.config.enableContextLearning) {
168
+ await this.analyzeCommandPatterns(session);
169
+ }
170
+ this.emit('command_recorded', { sessionId, command, success, duration });
171
+ }
172
+ /**
173
+ * Update user preferences
174
+ */
175
+ async updateUserPreferences(userId, preferences) {
176
+ const defaultPreferences = this.getDefaultPreferences();
177
+ const currentPreferences = await this.loadUserPreferences(userId);
178
+ const updatedPreferences = { ...currentPreferences, ...preferences };
179
+ await this.saveUserPreferences(userId, updatedPreferences);
180
+ // Update all active sessions for this user
181
+ for (const [sessionId, session] of this.activeSessions) {
182
+ if (session.userId === userId) {
183
+ session.userPreferences = updatedPreferences;
184
+ await this.persistSession(session);
185
+ }
186
+ }
187
+ this.emit('preferences_updated', {
188
+ userId,
189
+ preferences: updatedPreferences,
190
+ });
191
+ logger_1.logger.debug(`Updated preferences for user: ${userId}`);
192
+ }
193
+ /**
194
+ * Register a workspace
195
+ */
196
+ async registerWorkspace(workspacePath, config) {
197
+ const workspace = {
198
+ name: config.name || path_1.default.basename(workspacePath),
199
+ path: workspacePath,
200
+ type: config.type || 'unknown',
201
+ settings: config.settings || {},
202
+ lastAccessed: new Date(),
203
+ bookmarked: config.bookmarked || false,
204
+ };
205
+ this.workspaces.set(workspacePath, workspace);
206
+ await this.saveWorkspace(workspace);
207
+ this.emit('workspace_registered', { workspace });
208
+ logger_1.logger.debug(`Registered workspace: ${workspacePath}`);
209
+ }
210
+ /**
211
+ * Get workspace suggestions based on recent activity
212
+ */
213
+ getWorkspaceSuggestions(limit = 5) {
214
+ const workspaces = Array.from(this.workspaces.values()).sort((a, b) => b.lastAccessed.getTime() - a.lastAccessed.getTime());
215
+ return workspaces.slice(0, limit);
216
+ }
217
+ /**
218
+ * Search session history
219
+ */
220
+ searchHistory(sessionId, query) {
221
+ const session = this.activeSessions.get(sessionId);
222
+ if (!session)
223
+ return [];
224
+ let history = session.recentCommands;
225
+ if (query.command) {
226
+ const searchTerm = query.command.toLowerCase();
227
+ history = history.filter(cmd => cmd.command.toLowerCase().includes(searchTerm));
228
+ }
229
+ if (query.timeRange) {
230
+ history = history.filter(cmd => cmd.timestamp >= query.timeRange.from &&
231
+ cmd.timestamp <= query.timeRange.to);
232
+ }
233
+ if (query.successOnly) {
234
+ history = history.filter(cmd => cmd.success);
235
+ }
236
+ if (query.limit) {
237
+ history = history.slice(-query.limit);
238
+ }
239
+ return history;
240
+ }
241
+ /**
242
+ * Get contextual suggestions based on current session
243
+ */
244
+ async getContextualSuggestions(sessionId, limit = 5) {
245
+ const session = this.activeSessions.get(sessionId);
246
+ if (!session)
247
+ return [];
248
+ const suggestions = [];
249
+ // Command suggestions based on recent history
250
+ const recentCommands = session.recentCommands
251
+ .filter(cmd => cmd.success)
252
+ .slice(-10);
253
+ const commandFrequency = new Map();
254
+ for (const cmd of recentCommands) {
255
+ const baseCommand = cmd.command.split(' ')[0];
256
+ if (baseCommand) {
257
+ commandFrequency.set(baseCommand, (commandFrequency.get(baseCommand) || 0) + 1);
258
+ }
259
+ }
260
+ for (const [command, frequency] of commandFrequency) {
261
+ suggestions.push({
262
+ type: 'command',
263
+ suggestion: command,
264
+ description: `Recently used command (${frequency} times)`,
265
+ confidence: Math.min(0.9, frequency * 0.2),
266
+ });
267
+ }
268
+ // Workspace suggestions
269
+ const workspaceSuggestions = this.getWorkspaceSuggestions(3);
270
+ for (const workspace of workspaceSuggestions) {
271
+ if (workspace.path !== session.currentWorkspace) {
272
+ suggestions.push({
273
+ type: 'workspace',
274
+ suggestion: workspace.path,
275
+ description: `Switch to ${workspace.name}`,
276
+ confidence: 0.6,
277
+ });
278
+ }
279
+ }
280
+ // Sort by confidence and limit
281
+ suggestions.sort((a, b) => b.confidence - a.confidence);
282
+ return suggestions.slice(0, limit);
283
+ }
284
+ /**
285
+ * Export session data
286
+ */
287
+ async exportSession(sessionId, format) {
288
+ const session = this.activeSessions.get(sessionId);
289
+ if (!session) {
290
+ throw new Error(`Session not found: ${sessionId}`);
291
+ }
292
+ switch (format) {
293
+ case 'json':
294
+ return JSON.stringify(session, null, 2);
295
+ case 'csv':
296
+ return this.sessionToCsv(session);
297
+ default:
298
+ throw new Error(`Unsupported export format: ${format}`);
299
+ }
300
+ }
301
+ /**
302
+ * Clean up expired sessions
303
+ */
304
+ async cleanupSessions() {
305
+ const now = new Date();
306
+ const timeoutMs = this.config.sessionTimeout * 60 * 1000;
307
+ const expiredSessions = [];
308
+ for (const [sessionId, session] of this.activeSessions) {
309
+ const timeSinceAccess = now.getTime() - session.lastAccessed.getTime();
310
+ if (timeSinceAccess > timeoutMs) {
311
+ expiredSessions.push(sessionId);
312
+ }
313
+ }
314
+ for (const sessionId of expiredSessions) {
315
+ const session = this.activeSessions.get(sessionId);
316
+ // Final persist before cleanup
317
+ await this.persistSession(session);
318
+ this.activeSessions.delete(sessionId);
319
+ this.emit('session_expired', { sessionId });
320
+ }
321
+ // Limit active sessions
322
+ if (this.activeSessions.size > this.config.maxSessions) {
323
+ const sessionsByAccess = Array.from(this.activeSessions.entries()).sort(([, a], [, b]) => a.lastAccessed.getTime() - b.lastAccessed.getTime());
324
+ const excessCount = this.activeSessions.size - this.config.maxSessions;
325
+ const toRemove = sessionsByAccess.slice(0, excessCount);
326
+ for (const [sessionId, session] of toRemove) {
327
+ await this.persistSession(session);
328
+ this.activeSessions.delete(sessionId);
329
+ this.emit('session_archived', { sessionId });
330
+ }
331
+ }
332
+ if (expiredSessions.length > 0) {
333
+ logger_1.logger.debug(`Cleaned up ${expiredSessions.length} expired sessions`);
334
+ }
335
+ }
336
+ /**
337
+ * Get session statistics
338
+ */
339
+ getSessionStats() {
340
+ const activeSessions = this.activeSessions.size;
341
+ const totalCommands = Array.from(this.activeSessions.values()).reduce((sum, session) => sum + session.recentCommands.length, 0);
342
+ const sessionDurations = Array.from(this.activeSessions.values()).map(session => session.lastAccessed.getTime() - session.created.getTime());
343
+ const averageSessionDuration = sessionDurations.length > 0
344
+ ? sessionDurations.reduce((sum, duration) => sum + duration, 0) /
345
+ sessionDurations.length
346
+ : 0;
347
+ // Command frequency analysis
348
+ const commandCounts = new Map();
349
+ for (const session of this.activeSessions.values()) {
350
+ for (const cmd of session.recentCommands) {
351
+ const baseCommand = cmd.command.split(' ')[0];
352
+ if (baseCommand) {
353
+ commandCounts.set(baseCommand, (commandCounts.get(baseCommand) || 0) + 1);
354
+ }
355
+ }
356
+ }
357
+ const mostUsedCommands = Array.from(commandCounts.entries())
358
+ .map(([command, count]) => ({ command, count }))
359
+ .sort((a, b) => b.count - a.count)
360
+ .slice(0, 10);
361
+ return {
362
+ activeSessions,
363
+ totalCommands,
364
+ averageSessionDuration,
365
+ mostUsedCommands,
366
+ workspaceCount: this.workspaces.size,
367
+ };
368
+ }
369
+ // Private methods
370
+ async initialize() {
371
+ // Ensure directories exist
372
+ await fs_extra_1.default.ensureDir(this.config.persistencePath);
373
+ await fs_extra_1.default.ensureDir(path_1.default.join(this.config.persistencePath, 'users'));
374
+ await fs_extra_1.default.ensureDir(path_1.default.join(this.config.persistencePath, 'workspaces'));
375
+ // Load existing workspaces
376
+ await this.loadAllWorkspaces();
377
+ // Start background tasks
378
+ this.startBackgroundTasks();
379
+ logger_1.logger.debug('Session manager initialized');
380
+ }
381
+ startBackgroundTasks() {
382
+ // Periodic backup
383
+ this.backupTimer = setInterval(async () => {
384
+ try {
385
+ await this.backupActiveSessions();
386
+ }
387
+ catch (error) {
388
+ logger_1.logger.error('Session backup failed:', error);
389
+ }
390
+ }, this.config.backupInterval * 60 * 1000);
391
+ // Periodic cleanup
392
+ this.cleanupTimer = setInterval(async () => {
393
+ try {
394
+ await this.cleanupSessions();
395
+ }
396
+ catch (error) {
397
+ logger_1.logger.error('Session cleanup failed:', error);
398
+ }
399
+ }, 30 * 60 * 1000); // Every 30 minutes
400
+ }
401
+ async detectProjectContext(workspacePath) {
402
+ if (!this.config.autoDetectProjects)
403
+ return undefined;
404
+ try {
405
+ const context = {
406
+ rootPath: workspacePath,
407
+ projectType: 'unknown',
408
+ packageManager: 'unknown',
409
+ dependencies: [],
410
+ devDependencies: [],
411
+ scripts: {},
412
+ };
413
+ // Detect project type and package manager
414
+ const packageJsonPath = path_1.default.join(workspacePath, 'package.json');
415
+ if (await fs_extra_1.default.pathExists(packageJsonPath)) {
416
+ const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
417
+ context.dependencies = Object.keys(packageJson.dependencies || {});
418
+ context.devDependencies = Object.keys(packageJson.devDependencies || {});
419
+ context.scripts = packageJson.scripts || {};
420
+ // Detect project type from dependencies
421
+ if (context.dependencies.includes('react')) {
422
+ context.projectType = 'react';
423
+ }
424
+ else if (context.dependencies.includes('vue')) {
425
+ context.projectType = 'vue';
426
+ }
427
+ else if (context.dependencies.includes('@angular/core')) {
428
+ context.projectType = 'angular';
429
+ }
430
+ else {
431
+ context.projectType = 'nodejs';
432
+ }
433
+ // Detect package manager
434
+ if (await fs_extra_1.default.pathExists(path_1.default.join(workspacePath, 'pnpm-lock.yaml'))) {
435
+ context.packageManager = 'pnpm';
436
+ }
437
+ else if (await fs_extra_1.default.pathExists(path_1.default.join(workspacePath, 'yarn.lock'))) {
438
+ context.packageManager = 'yarn';
439
+ }
440
+ else if (await fs_extra_1.default.pathExists(path_1.default.join(workspacePath, 'package-lock.json'))) {
441
+ context.packageManager = 'npm';
442
+ }
443
+ }
444
+ // Detect other project types
445
+ if (await fs_extra_1.default.pathExists(path_1.default.join(workspacePath, 'requirements.txt'))) {
446
+ context.projectType = 'python';
447
+ context.packageManager = 'pip';
448
+ }
449
+ else if (await fs_extra_1.default.pathExists(path_1.default.join(workspacePath, 'pom.xml'))) {
450
+ context.projectType = 'java';
451
+ context.packageManager = 'maven';
452
+ }
453
+ else if (await fs_extra_1.default.pathExists(path_1.default.join(workspacePath, 'build.gradle'))) {
454
+ context.projectType = 'java';
455
+ context.packageManager = 'gradle';
456
+ }
457
+ // Git information
458
+ const gitPath = path_1.default.join(workspacePath, '.git');
459
+ if (await fs_extra_1.default.pathExists(gitPath)) {
460
+ context.gitRepository = await this.getGitInfo(workspacePath);
461
+ }
462
+ return context;
463
+ }
464
+ catch (error) {
465
+ logger_1.logger.debug('Failed to detect project context:', error);
466
+ return undefined;
467
+ }
468
+ }
469
+ async getGitInfo(workspacePath) {
470
+ // Simplified git info extraction
471
+ return {
472
+ remote: 'origin',
473
+ branch: 'main',
474
+ status: 'clean',
475
+ hasUncommittedChanges: false,
476
+ };
477
+ }
478
+ getDefaultPreferences() {
479
+ return {
480
+ defaultModel: 'claude-3-5-sonnet-20241022',
481
+ preferredOutputFormat: 'table',
482
+ confirmDestructiveCommands: true,
483
+ enableStreamingResponses: true,
484
+ maxHistoryLength: 100,
485
+ autoSave: true,
486
+ theme: 'auto',
487
+ verbosity: 'normal',
488
+ aliases: {},
489
+ favorites: [],
490
+ workspacePreferences: {},
491
+ };
492
+ }
493
+ async loadUserPreferences(userId) {
494
+ const prefsPath = path_1.default.join(this.config.persistencePath, 'users', `${userId}.json`);
495
+ if (await fs_extra_1.default.pathExists(prefsPath)) {
496
+ try {
497
+ const saved = await fs_extra_1.default.readJson(prefsPath);
498
+ return { ...this.getDefaultPreferences(), ...saved };
499
+ }
500
+ catch (error) {
501
+ logger_1.logger.warn(`Failed to load preferences for ${userId}:`, error);
502
+ }
503
+ }
504
+ return this.getDefaultPreferences();
505
+ }
506
+ async saveUserPreferences(userId, preferences) {
507
+ const prefsPath = path_1.default.join(this.config.persistencePath, 'users', `${userId}.json`);
508
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(prefsPath));
509
+ await fs_extra_1.default.writeJson(prefsPath, preferences, { spaces: 2 });
510
+ }
511
+ async persistSession(session) {
512
+ const sessionPath = path_1.default.join(this.config.persistencePath, `${session.id}.json`);
513
+ const serialized = this.serializeSession(session);
514
+ await fs_extra_1.default.writeJson(sessionPath, serialized, { spaces: 2 });
515
+ }
516
+ async loadSession(sessionId) {
517
+ const sessionPath = path_1.default.join(this.config.persistencePath, `${sessionId}.json`);
518
+ if (await fs_extra_1.default.pathExists(sessionPath)) {
519
+ try {
520
+ const data = await fs_extra_1.default.readJson(sessionPath);
521
+ return this.deserializeSession(data);
522
+ }
523
+ catch (error) {
524
+ logger_1.logger.warn(`Failed to load session ${sessionId}:`, error);
525
+ }
526
+ }
527
+ return null;
528
+ }
529
+ serializeSession(session) {
530
+ return {
531
+ ...session,
532
+ created: session.created.toISOString(),
533
+ lastAccessed: session.lastAccessed.toISOString(),
534
+ recentCommands: session.recentCommands.map(cmd => ({
535
+ ...cmd,
536
+ timestamp: cmd.timestamp.toISOString(),
537
+ })),
538
+ contextStack: session.contextStack.map(ctx => ({
539
+ ...ctx,
540
+ timestamp: ctx.timestamp.toISOString(),
541
+ })),
542
+ projectContext: session.projectContext
543
+ ? {
544
+ ...session.projectContext,
545
+ lastAnalysis: session.projectContext.lastAnalysis
546
+ ? {
547
+ ...session.projectContext.lastAnalysis,
548
+ timestamp: session.projectContext.lastAnalysis.timestamp.toISOString(),
549
+ }
550
+ : undefined,
551
+ }
552
+ : undefined,
553
+ };
554
+ }
555
+ deserializeSession(data) {
556
+ return {
557
+ ...data,
558
+ created: new Date(data.created),
559
+ lastAccessed: new Date(data.lastAccessed),
560
+ recentCommands: data.recentCommands.map((cmd) => ({
561
+ ...cmd,
562
+ timestamp: new Date(cmd.timestamp),
563
+ })),
564
+ contextStack: data.contextStack.map((ctx) => ({
565
+ ...ctx,
566
+ timestamp: new Date(ctx.timestamp),
567
+ })),
568
+ projectContext: data.projectContext
569
+ ? {
570
+ ...data.projectContext,
571
+ lastAnalysis: data.projectContext.lastAnalysis
572
+ ? {
573
+ ...data.projectContext.lastAnalysis,
574
+ timestamp: new Date(data.projectContext.lastAnalysis.timestamp),
575
+ }
576
+ : undefined,
577
+ }
578
+ : undefined,
579
+ };
580
+ }
581
+ async loadWorkspace(workspacePath) {
582
+ const workspacesDir = path_1.default.join(this.config.persistencePath, 'workspaces');
583
+ const workspaceFile = path_1.default.join(workspacesDir, `${Buffer.from(workspacePath).toString('base64')}.json`);
584
+ if (await fs_extra_1.default.pathExists(workspaceFile)) {
585
+ try {
586
+ const data = await fs_extra_1.default.readJson(workspaceFile);
587
+ data.lastAccessed = new Date(data.lastAccessed);
588
+ return data;
589
+ }
590
+ catch (error) {
591
+ logger_1.logger.warn(`Failed to load workspace ${workspacePath}:`, error);
592
+ }
593
+ }
594
+ return null;
595
+ }
596
+ async saveWorkspace(workspace) {
597
+ const workspacesDir = path_1.default.join(this.config.persistencePath, 'workspaces');
598
+ const workspaceFile = path_1.default.join(workspacesDir, `${Buffer.from(workspace.path).toString('base64')}.json`);
599
+ await fs_extra_1.default.ensureDir(workspacesDir);
600
+ await fs_extra_1.default.writeJson(workspaceFile, {
601
+ ...workspace,
602
+ lastAccessed: workspace.lastAccessed.toISOString(),
603
+ }, { spaces: 2 });
604
+ }
605
+ async loadAllWorkspaces() {
606
+ const workspacesDir = path_1.default.join(this.config.persistencePath, 'workspaces');
607
+ if (await fs_extra_1.default.pathExists(workspacesDir)) {
608
+ const files = await fs_extra_1.default.readdir(workspacesDir);
609
+ for (const file of files) {
610
+ if (file.endsWith('.json')) {
611
+ try {
612
+ const data = await fs_extra_1.default.readJson(path_1.default.join(workspacesDir, file));
613
+ data.lastAccessed = new Date(data.lastAccessed);
614
+ this.workspaces.set(data.path, data);
615
+ }
616
+ catch (error) {
617
+ logger_1.logger.debug(`Failed to load workspace file ${file}:`, error);
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ async backupActiveSessions() {
624
+ for (const session of this.activeSessions.values()) {
625
+ await this.persistSession(session);
626
+ }
627
+ logger_1.logger.debug(`Backed up ${this.activeSessions.size} active sessions`);
628
+ }
629
+ async analyzeCommandPatterns(session) {
630
+ // Simple pattern analysis for learning user behavior
631
+ const recentCommands = session.recentCommands.slice(-10);
632
+ if (recentCommands.length >= 3) {
633
+ // Look for command sequences
634
+ const sequences = [];
635
+ for (let i = 0; i < recentCommands.length - 1; i++) {
636
+ const current = recentCommands[i];
637
+ const next = recentCommands[i + 1];
638
+ if (current?.command && next?.command) {
639
+ sequences.push(`${current.command} -> ${next.command}`);
640
+ }
641
+ }
642
+ // Store patterns in temporary data for future suggestions
643
+ if (!session.temporaryData['commandPatterns']) {
644
+ session.temporaryData['commandPatterns'] = [];
645
+ }
646
+ session.temporaryData['commandPatterns'].push(...sequences);
647
+ // Keep only recent patterns
648
+ session.temporaryData['commandPatterns'] =
649
+ session.temporaryData['commandPatterns'].slice(-20);
650
+ }
651
+ }
652
+ sessionToCsv(session) {
653
+ const headers = ['Timestamp', 'Command', 'Success', 'Duration'];
654
+ const rows = [headers];
655
+ session.recentCommands.forEach(cmd => {
656
+ rows.push([
657
+ cmd.timestamp.toISOString(),
658
+ `"${cmd.command.replace(/"/g, '""')}"`,
659
+ cmd.success.toString(),
660
+ cmd.duration.toString(),
661
+ ]);
662
+ });
663
+ return rows.map(row => row.join(',')).join('\n');
664
+ }
665
+ /**
666
+ * Cleanup resources
667
+ */
668
+ destroy() {
669
+ if (this.backupTimer) {
670
+ clearInterval(this.backupTimer);
671
+ }
672
+ if (this.cleanupTimer) {
673
+ clearInterval(this.cleanupTimer);
674
+ }
675
+ this.activeSessions.clear();
676
+ this.workspaces.clear();
677
+ this.removeAllListeners();
678
+ }
679
+ }
680
+ exports.SessionManager = SessionManager;
681
+ exports.default = SessionManager;
682
+ //# sourceMappingURL=session-manager.js.map