@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.
- package/README.md +551 -0
- package/bin/wundr.js +39 -0
- package/dist/ai/ai-service.d.ts +152 -0
- package/dist/ai/ai-service.d.ts.map +1 -0
- package/dist/ai/ai-service.js +430 -0
- package/dist/ai/ai-service.js.map +1 -0
- package/dist/ai/claude-client.d.ts +130 -0
- package/dist/ai/claude-client.d.ts.map +1 -0
- package/dist/ai/claude-client.js +339 -0
- package/dist/ai/claude-client.js.map +1 -0
- package/dist/ai/conversation-manager.d.ts +164 -0
- package/dist/ai/conversation-manager.d.ts.map +1 -0
- package/dist/ai/conversation-manager.js +612 -0
- package/dist/ai/conversation-manager.js.map +1 -0
- package/dist/ai/index.d.ts +5 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +8 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ai.d.ts +89 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +735 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/analyze-optimized.d.ts +14 -0
- package/dist/commands/analyze-optimized.d.ts.map +1 -0
- package/dist/commands/analyze-optimized.js +437 -0
- package/dist/commands/analyze-optimized.js.map +1 -0
- package/dist/commands/analyze.d.ts +65 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +435 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/batch.d.ts +71 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +738 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/chat.d.ts +71 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +674 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/claude-init.d.ts +28 -0
- package/dist/commands/claude-init.d.ts.map +1 -0
- package/dist/commands/claude-init.js +587 -0
- package/dist/commands/claude-init.js.map +1 -0
- package/dist/commands/claude-setup.d.ts +32 -0
- package/dist/commands/claude-setup.d.ts.map +1 -0
- package/dist/commands/claude-setup.js +570 -0
- package/dist/commands/claude-setup.js.map +1 -0
- package/dist/commands/computer-setup-commands.d.ts +39 -0
- package/dist/commands/computer-setup-commands.d.ts.map +1 -0
- package/dist/commands/computer-setup-commands.js +563 -0
- package/dist/commands/computer-setup-commands.js.map +1 -0
- package/dist/commands/computer-setup.d.ts +7 -0
- package/dist/commands/computer-setup.d.ts.map +1 -0
- package/dist/commands/computer-setup.js +481 -0
- package/dist/commands/computer-setup.js.map +1 -0
- package/dist/commands/create-command.d.ts +7 -0
- package/dist/commands/create-command.d.ts.map +1 -0
- package/dist/commands/create-command.js +158 -0
- package/dist/commands/create-command.js.map +1 -0
- package/dist/commands/create.d.ts +74 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +556 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dashboard.d.ts +91 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +537 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/govern.d.ts +70 -0
- package/dist/commands/govern.d.ts.map +1 -0
- package/dist/commands/govern.js +480 -0
- package/dist/commands/govern.js.map +1 -0
- package/dist/commands/init.d.ts +55 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +584 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/performance-optimizer.d.ts +30 -0
- package/dist/commands/performance-optimizer.d.ts.map +1 -0
- package/dist/commands/performance-optimizer.js +649 -0
- package/dist/commands/performance-optimizer.js.map +1 -0
- package/dist/commands/plugins.d.ts +87 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +685 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/setup.d.ts +29 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +399 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/test-init.d.ts +9 -0
- package/dist/commands/test-init.d.ts.map +1 -0
- package/dist/commands/test-init.js +222 -0
- package/dist/commands/test-init.js.map +1 -0
- package/dist/commands/test.d.ts +25 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +217 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/watch.d.ts +76 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +610 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/context/context-manager.d.ts +155 -0
- package/dist/context/context-manager.d.ts.map +1 -0
- package/dist/context/context-manager.js +383 -0
- package/dist/context/context-manager.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +6 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/session-manager.d.ts +207 -0
- package/dist/context/session-manager.d.ts.map +1 -0
- package/dist/context/session-manager.js +682 -0
- package/dist/context/session-manager.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive/interactive-mode.d.ts +76 -0
- package/dist/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/interactive/interactive-mode.js +730 -0
- package/dist/interactive/interactive-mode.js.map +1 -0
- package/dist/nlp/command-mapper.d.ts +174 -0
- package/dist/nlp/command-mapper.d.ts.map +1 -0
- package/dist/nlp/command-mapper.js +623 -0
- package/dist/nlp/command-mapper.js.map +1 -0
- package/dist/nlp/command-parser.d.ts +106 -0
- package/dist/nlp/command-parser.d.ts.map +1 -0
- package/dist/nlp/command-parser.js +416 -0
- package/dist/nlp/command-parser.js.map +1 -0
- package/dist/nlp/index.d.ts +5 -0
- package/dist/nlp/index.d.ts.map +1 -0
- package/dist/nlp/index.js +8 -0
- package/dist/nlp/index.js.map +1 -0
- package/dist/nlp/intent-classifier.d.ts +59 -0
- package/dist/nlp/intent-classifier.d.ts.map +1 -0
- package/dist/nlp/intent-classifier.js +384 -0
- package/dist/nlp/intent-classifier.js.map +1 -0
- package/dist/nlp/intent-parser.d.ts +152 -0
- package/dist/nlp/intent-parser.d.ts.map +1 -0
- package/dist/nlp/intent-parser.js +739 -0
- package/dist/nlp/intent-parser.js.map +1 -0
- package/dist/plugins/plugin-manager.d.ts +120 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/plugins/plugin-manager.js +595 -0
- package/dist/plugins/plugin-manager.js.map +1 -0
- package/dist/types/index.d.ts +224 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/config-manager.d.ts +73 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +339 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/error-handler.d.ts +46 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +169 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/logger.d.ts +25 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +94 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +119 -0
- package/src/ai/ai-service.ts +595 -0
- package/src/ai/claude-client.ts +490 -0
- package/src/ai/conversation-manager.ts +907 -0
- package/src/ai/index.ts +8 -0
- package/src/cli.ts +202 -0
- package/src/commands/ai.ts +995 -0
- package/src/commands/analyze-optimized.ts +641 -0
- package/src/commands/analyze.ts +576 -0
- package/src/commands/batch.ts +935 -0
- package/src/commands/chat.ts +876 -0
- package/src/commands/claude-init.ts +715 -0
- package/src/commands/claude-setup.ts +697 -0
- package/src/commands/computer-setup-commands.ts +709 -0
- package/src/commands/computer-setup.ts +565 -0
- package/src/commands/create-command.ts +175 -0
- package/src/commands/create.ts +727 -0
- package/src/commands/dashboard.ts +691 -0
- package/src/commands/govern.ts +635 -0
- package/src/commands/init.ts +677 -0
- package/src/commands/performance-optimizer.ts +864 -0
- package/src/commands/plugins.ts +848 -0
- package/src/commands/setup.ts +508 -0
- package/src/commands/test-init.ts +242 -0
- package/src/commands/test.ts +264 -0
- package/src/commands/watch.ts +755 -0
- package/src/context/context-manager.ts +546 -0
- package/src/context/index.ts +9 -0
- package/src/context/session-manager.ts +1019 -0
- package/src/index.ts +64 -0
- package/src/interactive/interactive-mode.ts +830 -0
- package/src/nlp/command-mapper.ts +885 -0
- package/src/nlp/command-parser.ts +564 -0
- package/src/nlp/index.ts +4 -0
- package/src/nlp/intent-classifier.ts +458 -0
- package/src/nlp/intent-parser.ts +1101 -0
- package/src/plugins/plugin-manager.ts +744 -0
- package/src/types/index.ts +252 -0
- package/src/types/modules.d.ts +56 -0
- package/src/utils/config-manager.ts +391 -0
- package/src/utils/error-handler.ts +192 -0
- package/src/utils/logger.ts +104 -0
- package/templates/batch/ci-cd.yaml +62 -0
- package/templates/component/{{fileName}}.test.tsx +17 -0
- package/templates/component/{{fileName}}.tsx +21 -0
- package/templates/service/{{fileName}}.ts +98 -0
- package/templates/wundr-test.config.js +0 -0
- package/test-suites/api/health.spec.ts +134 -0
- package/test-suites/helpers/test-config.ts +84 -0
- package/test-suites/ui/accessibility.spec.ts +102 -0
- 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
|
+
}
|