@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,885 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { spawn, ChildProcess } from 'child_process';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
import { IntentResult } from './intent-parser';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Command execution result
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandResult {
|
|
10
|
+
command: string;
|
|
11
|
+
success: boolean;
|
|
12
|
+
output: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
exitCode: number;
|
|
15
|
+
executionTime: number;
|
|
16
|
+
metadata?: Record<string, any>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Command validation result
|
|
21
|
+
*/
|
|
22
|
+
export interface ValidationResult {
|
|
23
|
+
valid: boolean;
|
|
24
|
+
errors: string[];
|
|
25
|
+
warnings: string[];
|
|
26
|
+
suggestions: string[];
|
|
27
|
+
safetyLevel: 'safe' | 'caution' | 'dangerous';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Command mapping configuration
|
|
32
|
+
*/
|
|
33
|
+
export interface CommandMapperConfig {
|
|
34
|
+
dryRun: boolean;
|
|
35
|
+
confirmDestructive: boolean;
|
|
36
|
+
timeout: number; // milliseconds
|
|
37
|
+
maxConcurrentCommands: number;
|
|
38
|
+
enableLogging: boolean;
|
|
39
|
+
safetyChecks: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Command template for mapping intents to executable commands
|
|
44
|
+
*/
|
|
45
|
+
export interface CommandTemplate {
|
|
46
|
+
intent: string;
|
|
47
|
+
commandTemplate: string;
|
|
48
|
+
parameterMapping: ParameterMapping[];
|
|
49
|
+
validation: ValidationRule[];
|
|
50
|
+
safetyLevel: 'safe' | 'caution' | 'dangerous';
|
|
51
|
+
requiresConfirmation: boolean;
|
|
52
|
+
examples: CommandExample[];
|
|
53
|
+
dependencies?: string[]; // Required tools/commands
|
|
54
|
+
category: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parameter mapping from intent to command
|
|
59
|
+
*/
|
|
60
|
+
export interface ParameterMapping {
|
|
61
|
+
intentParam: string;
|
|
62
|
+
commandFlag: string;
|
|
63
|
+
transform?: (value: any) => string;
|
|
64
|
+
validation?: (value: any) => boolean;
|
|
65
|
+
required?: boolean;
|
|
66
|
+
defaultValue?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validation rule for commands
|
|
71
|
+
*/
|
|
72
|
+
export interface ValidationRule {
|
|
73
|
+
type:
|
|
74
|
+
| 'parameter'
|
|
75
|
+
| 'file_exists'
|
|
76
|
+
| 'directory_exists'
|
|
77
|
+
| 'command_available'
|
|
78
|
+
| 'custom';
|
|
79
|
+
rule: string | ((params: Record<string, any>) => Promise<boolean>);
|
|
80
|
+
message: string;
|
|
81
|
+
severity: 'error' | 'warning';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Command example for documentation
|
|
86
|
+
*/
|
|
87
|
+
export interface CommandExample {
|
|
88
|
+
description: string;
|
|
89
|
+
intentInput: string;
|
|
90
|
+
expectedCommand: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Execution context for commands
|
|
95
|
+
*/
|
|
96
|
+
export interface ExecutionContext {
|
|
97
|
+
workingDirectory: string;
|
|
98
|
+
environment: Record<string, string>;
|
|
99
|
+
user: string;
|
|
100
|
+
interactive: boolean;
|
|
101
|
+
dryRun: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Command mapper that converts parsed intents into executable CLI commands
|
|
106
|
+
*/
|
|
107
|
+
export class CommandMapper extends EventEmitter {
|
|
108
|
+
private config: CommandMapperConfig;
|
|
109
|
+
private commandTemplates: Map<string, CommandTemplate>;
|
|
110
|
+
private runningCommands: Map<string, ChildProcess>;
|
|
111
|
+
private commandHistory: CommandResult[];
|
|
112
|
+
|
|
113
|
+
constructor(config: Partial<CommandMapperConfig> = {}) {
|
|
114
|
+
super();
|
|
115
|
+
|
|
116
|
+
this.config = {
|
|
117
|
+
dryRun: false,
|
|
118
|
+
confirmDestructive: true,
|
|
119
|
+
timeout: 300000, // 5 minutes
|
|
120
|
+
maxConcurrentCommands: 5,
|
|
121
|
+
enableLogging: true,
|
|
122
|
+
safetyChecks: true,
|
|
123
|
+
...config,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
this.commandTemplates = new Map();
|
|
127
|
+
this.runningCommands = new Map();
|
|
128
|
+
this.commandHistory = [];
|
|
129
|
+
|
|
130
|
+
this.initializeDefaultTemplates();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Map an intent result to an executable command
|
|
135
|
+
*/
|
|
136
|
+
async mapIntentToCommand(
|
|
137
|
+
intentResult: IntentResult,
|
|
138
|
+
context: ExecutionContext
|
|
139
|
+
): Promise<{
|
|
140
|
+
command: string;
|
|
141
|
+
args: string[];
|
|
142
|
+
validation: ValidationResult;
|
|
143
|
+
safetyLevel: 'safe' | 'caution' | 'dangerous';
|
|
144
|
+
requiresConfirmation: boolean;
|
|
145
|
+
}> {
|
|
146
|
+
const template = this.commandTemplates.get(intentResult.intent);
|
|
147
|
+
|
|
148
|
+
if (!template) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`No command template found for intent: ${intentResult.intent}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Build command from template
|
|
155
|
+
const { command, args } = await this.buildCommand(
|
|
156
|
+
template,
|
|
157
|
+
intentResult.parameters || {},
|
|
158
|
+
context
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Validate command
|
|
162
|
+
const validation = await this.validateCommand(
|
|
163
|
+
template,
|
|
164
|
+
intentResult.parameters || {},
|
|
165
|
+
context
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
this.emit('command_mapped', {
|
|
169
|
+
intent: intentResult.intent,
|
|
170
|
+
command,
|
|
171
|
+
args,
|
|
172
|
+
validation,
|
|
173
|
+
template: template.intent,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
command,
|
|
178
|
+
args,
|
|
179
|
+
validation,
|
|
180
|
+
safetyLevel: template.safetyLevel,
|
|
181
|
+
requiresConfirmation: template.requiresConfirmation,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Execute a mapped command
|
|
187
|
+
*/
|
|
188
|
+
async executeCommand(
|
|
189
|
+
command: string,
|
|
190
|
+
args: string[],
|
|
191
|
+
context: ExecutionContext,
|
|
192
|
+
options: {
|
|
193
|
+
streaming?: boolean;
|
|
194
|
+
onOutput?: (output: string) => void;
|
|
195
|
+
onError?: (error: string) => void;
|
|
196
|
+
} = {}
|
|
197
|
+
): Promise<CommandResult> {
|
|
198
|
+
const startTime = Date.now();
|
|
199
|
+
const executionId = `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
200
|
+
|
|
201
|
+
if (context.dryRun || this.config.dryRun) {
|
|
202
|
+
logger.info(`[DRY RUN] Would execute: ${command} ${args.join(' ')}`);
|
|
203
|
+
return {
|
|
204
|
+
command: `${command} ${args.join(' ')}`,
|
|
205
|
+
success: true,
|
|
206
|
+
output: '[DRY RUN] Command would execute successfully',
|
|
207
|
+
exitCode: 0,
|
|
208
|
+
executionTime: 0,
|
|
209
|
+
metadata: { dryRun: true },
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
const childProcess = spawn(command, args, {
|
|
215
|
+
cwd: context.workingDirectory,
|
|
216
|
+
env: { ...process.env, ...context.environment },
|
|
217
|
+
stdio: options.streaming ? 'pipe' : 'pipe',
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
this.runningCommands.set(executionId, childProcess);
|
|
221
|
+
|
|
222
|
+
let output = '';
|
|
223
|
+
let errorOutput = '';
|
|
224
|
+
|
|
225
|
+
// Handle stdout
|
|
226
|
+
if (childProcess.stdout) {
|
|
227
|
+
childProcess.stdout.on('data', (data: Buffer) => {
|
|
228
|
+
const chunk = data.toString();
|
|
229
|
+
output += chunk;
|
|
230
|
+
|
|
231
|
+
if (options.onOutput) {
|
|
232
|
+
options.onOutput(chunk);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (options.streaming) {
|
|
236
|
+
this.emit('command_output', {
|
|
237
|
+
executionId,
|
|
238
|
+
chunk,
|
|
239
|
+
stream: 'stdout',
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Handle stderr
|
|
246
|
+
if (childProcess.stderr) {
|
|
247
|
+
childProcess.stderr.on('data', (data: Buffer) => {
|
|
248
|
+
const chunk = data.toString();
|
|
249
|
+
errorOutput += chunk;
|
|
250
|
+
|
|
251
|
+
if (options.onError) {
|
|
252
|
+
options.onError(chunk);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (options.streaming) {
|
|
256
|
+
this.emit('command_output', {
|
|
257
|
+
executionId,
|
|
258
|
+
chunk,
|
|
259
|
+
stream: 'stderr',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Handle process exit
|
|
266
|
+
childProcess.on('close', (code: number | null) => {
|
|
267
|
+
const endTime = Date.now();
|
|
268
|
+
const executionTime = endTime - startTime;
|
|
269
|
+
|
|
270
|
+
this.runningCommands.delete(executionId);
|
|
271
|
+
|
|
272
|
+
const result: CommandResult = {
|
|
273
|
+
command: `${command} ${args.join(' ')}`,
|
|
274
|
+
success: (code || 0) === 0,
|
|
275
|
+
output,
|
|
276
|
+
error: errorOutput || undefined,
|
|
277
|
+
exitCode: code || 0,
|
|
278
|
+
executionTime,
|
|
279
|
+
metadata: {
|
|
280
|
+
executionId,
|
|
281
|
+
workingDirectory: context.workingDirectory,
|
|
282
|
+
startTime: new Date(startTime),
|
|
283
|
+
endTime: new Date(endTime),
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Log result
|
|
288
|
+
if (this.config.enableLogging) {
|
|
289
|
+
logger.debug(`Command executed: ${result.command}`, {
|
|
290
|
+
success: result.success,
|
|
291
|
+
exitCode: result.exitCode,
|
|
292
|
+
executionTime: result.executionTime,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Add to history
|
|
297
|
+
this.commandHistory.push(result);
|
|
298
|
+
|
|
299
|
+
// Limit history size
|
|
300
|
+
if (this.commandHistory.length > 1000) {
|
|
301
|
+
this.commandHistory = this.commandHistory.slice(-500);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this.emit('command_completed', { executionId, result });
|
|
305
|
+
|
|
306
|
+
resolve(result);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Handle process errors
|
|
310
|
+
childProcess.on('error', (error: Error) => {
|
|
311
|
+
this.runningCommands.delete(executionId);
|
|
312
|
+
|
|
313
|
+
const result: CommandResult = {
|
|
314
|
+
command: `${command} ${args.join(' ')}`,
|
|
315
|
+
success: false,
|
|
316
|
+
output,
|
|
317
|
+
error: error.message,
|
|
318
|
+
exitCode: -1,
|
|
319
|
+
executionTime: Date.now() - startTime,
|
|
320
|
+
metadata: { executionId, error: error.message },
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
this.emit('command_error', { executionId, error });
|
|
324
|
+
resolve(result); // Don't reject, return error result instead
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Set timeout
|
|
328
|
+
if (this.config.timeout > 0) {
|
|
329
|
+
setTimeout(() => {
|
|
330
|
+
if (this.runningCommands.has(executionId)) {
|
|
331
|
+
childProcess.kill('SIGTERM');
|
|
332
|
+
logger.warn(
|
|
333
|
+
`Command timed out after ${this.config.timeout}ms: ${command}`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}, this.config.timeout);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Execute a command from an intent result
|
|
343
|
+
*/
|
|
344
|
+
async executeFromIntent(
|
|
345
|
+
intentResult: IntentResult,
|
|
346
|
+
context: ExecutionContext,
|
|
347
|
+
options: {
|
|
348
|
+
skipConfirmation?: boolean;
|
|
349
|
+
streaming?: boolean;
|
|
350
|
+
onOutput?: (output: string) => void;
|
|
351
|
+
onError?: (error: string) => void;
|
|
352
|
+
} = {}
|
|
353
|
+
): Promise<CommandResult> {
|
|
354
|
+
// Map intent to command
|
|
355
|
+
const mappedCommand = await this.mapIntentToCommand(intentResult, context);
|
|
356
|
+
|
|
357
|
+
// Check validation
|
|
358
|
+
if (!mappedCommand.validation.valid) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Command validation failed: ${mappedCommand.validation.errors.join(', ')}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Handle confirmation for dangerous commands
|
|
365
|
+
if (
|
|
366
|
+
mappedCommand.requiresConfirmation &&
|
|
367
|
+
this.config.confirmDestructive &&
|
|
368
|
+
!options.skipConfirmation &&
|
|
369
|
+
context.interactive
|
|
370
|
+
) {
|
|
371
|
+
const confirmed = await this.confirmExecution(
|
|
372
|
+
mappedCommand,
|
|
373
|
+
intentResult
|
|
374
|
+
);
|
|
375
|
+
if (!confirmed) {
|
|
376
|
+
throw new Error('Command execution cancelled by user');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Execute command
|
|
381
|
+
return this.executeCommand(
|
|
382
|
+
mappedCommand.command,
|
|
383
|
+
mappedCommand.args,
|
|
384
|
+
context,
|
|
385
|
+
options
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Register a custom command template
|
|
391
|
+
*/
|
|
392
|
+
registerCommandTemplate(template: CommandTemplate): void {
|
|
393
|
+
this.commandTemplates.set(template.intent, template);
|
|
394
|
+
logger.debug(`Registered command template: ${template.intent}`);
|
|
395
|
+
this.emit('template_registered', template);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Validate a command before execution
|
|
400
|
+
*/
|
|
401
|
+
async validateCommand(
|
|
402
|
+
template: CommandTemplate,
|
|
403
|
+
parameters: Record<string, any>,
|
|
404
|
+
context: ExecutionContext
|
|
405
|
+
): Promise<ValidationResult> {
|
|
406
|
+
const result: ValidationResult = {
|
|
407
|
+
valid: true,
|
|
408
|
+
errors: [],
|
|
409
|
+
warnings: [],
|
|
410
|
+
suggestions: [],
|
|
411
|
+
safetyLevel: template.safetyLevel,
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
if (!this.config.safetyChecks) {
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Run validation rules
|
|
419
|
+
for (const rule of template.validation) {
|
|
420
|
+
try {
|
|
421
|
+
const isValid = await this.executeValidationRule(
|
|
422
|
+
rule,
|
|
423
|
+
parameters,
|
|
424
|
+
context
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
if (!isValid) {
|
|
428
|
+
if (rule.severity === 'error') {
|
|
429
|
+
result.errors.push(rule.message);
|
|
430
|
+
result.valid = false;
|
|
431
|
+
} else {
|
|
432
|
+
result.warnings.push(rule.message);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
result.errors.push(
|
|
437
|
+
`Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
438
|
+
);
|
|
439
|
+
result.valid = false;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Check parameter requirements
|
|
444
|
+
for (const mapping of template.parameterMapping) {
|
|
445
|
+
if (mapping.required && !parameters[mapping.intentParam]) {
|
|
446
|
+
result.errors.push(
|
|
447
|
+
`Required parameter missing: ${mapping.intentParam}`
|
|
448
|
+
);
|
|
449
|
+
result.valid = false;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (parameters[mapping.intentParam] && mapping.validation) {
|
|
453
|
+
if (!mapping.validation(parameters[mapping.intentParam])) {
|
|
454
|
+
result.errors.push(
|
|
455
|
+
`Invalid value for parameter: ${mapping.intentParam}`
|
|
456
|
+
);
|
|
457
|
+
result.valid = false;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Check dependencies
|
|
463
|
+
if (template.dependencies) {
|
|
464
|
+
for (const dep of template.dependencies) {
|
|
465
|
+
const available = await this.checkCommandAvailable(dep);
|
|
466
|
+
if (!available) {
|
|
467
|
+
result.errors.push(`Required dependency not available: ${dep}`);
|
|
468
|
+
result.valid = false;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Get command execution history
|
|
478
|
+
*/
|
|
479
|
+
getCommandHistory(
|
|
480
|
+
filters: {
|
|
481
|
+
limit?: number;
|
|
482
|
+
successOnly?: boolean;
|
|
483
|
+
intent?: string;
|
|
484
|
+
since?: Date;
|
|
485
|
+
} = {}
|
|
486
|
+
): CommandResult[] {
|
|
487
|
+
let history = [...this.commandHistory];
|
|
488
|
+
|
|
489
|
+
if (filters.since) {
|
|
490
|
+
history = history.filter(
|
|
491
|
+
cmd =>
|
|
492
|
+
cmd.metadata?.['startTime'] &&
|
|
493
|
+
new Date(cmd.metadata['startTime']) >= filters.since!
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (filters.successOnly) {
|
|
498
|
+
history = history.filter(cmd => cmd.success);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (filters.intent) {
|
|
502
|
+
history = history.filter(
|
|
503
|
+
cmd => cmd.metadata?.['intent'] === filters.intent
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (filters.limit) {
|
|
508
|
+
history = history.slice(-filters.limit);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return history;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Cancel a running command
|
|
516
|
+
*/
|
|
517
|
+
async cancelCommand(executionId: string): Promise<boolean> {
|
|
518
|
+
const childProcess = this.runningCommands.get(executionId);
|
|
519
|
+
|
|
520
|
+
if (childProcess) {
|
|
521
|
+
childProcess.kill('SIGTERM');
|
|
522
|
+
|
|
523
|
+
// Give it a moment to terminate gracefully
|
|
524
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
525
|
+
|
|
526
|
+
// Force kill if still running
|
|
527
|
+
if (!childProcess.killed) {
|
|
528
|
+
childProcess.kill('SIGKILL');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
this.runningCommands.delete(executionId);
|
|
532
|
+
this.emit('command_cancelled', { executionId });
|
|
533
|
+
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Get currently running commands
|
|
542
|
+
*/
|
|
543
|
+
getRunningCommands(): Array<{
|
|
544
|
+
executionId: string;
|
|
545
|
+
pid: number;
|
|
546
|
+
command: string;
|
|
547
|
+
}> {
|
|
548
|
+
return Array.from(this.runningCommands.entries()).map(([id, process]) => ({
|
|
549
|
+
executionId: id,
|
|
550
|
+
pid: process.pid || 0,
|
|
551
|
+
command: process.spawnargs.join(' '),
|
|
552
|
+
}));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Generate command preview without executing
|
|
557
|
+
*/
|
|
558
|
+
async previewCommand(
|
|
559
|
+
intentResult: IntentResult,
|
|
560
|
+
context: ExecutionContext
|
|
561
|
+
): Promise<{
|
|
562
|
+
command: string;
|
|
563
|
+
explanation: string;
|
|
564
|
+
warnings: string[];
|
|
565
|
+
safetyLevel: string;
|
|
566
|
+
}> {
|
|
567
|
+
const mappedCommand = await this.mapIntentToCommand(intentResult, context);
|
|
568
|
+
|
|
569
|
+
const explanation = this.generateCommandExplanation(
|
|
570
|
+
mappedCommand.command,
|
|
571
|
+
mappedCommand.args,
|
|
572
|
+
intentResult
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
command: `${mappedCommand.command} ${mappedCommand.args.join(' ')}`,
|
|
577
|
+
explanation,
|
|
578
|
+
warnings: mappedCommand.validation.warnings,
|
|
579
|
+
safetyLevel: mappedCommand.safetyLevel,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Private methods
|
|
584
|
+
|
|
585
|
+
private initializeDefaultTemplates(): void {
|
|
586
|
+
const defaultTemplates: CommandTemplate[] = [
|
|
587
|
+
{
|
|
588
|
+
intent: 'analyze',
|
|
589
|
+
commandTemplate: 'wundr analyze',
|
|
590
|
+
parameterMapping: [
|
|
591
|
+
{
|
|
592
|
+
intentParam: 'path',
|
|
593
|
+
commandFlag: '--path',
|
|
594
|
+
defaultValue: '.',
|
|
595
|
+
validation: (value: string) => typeof value === 'string',
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
intentParam: 'focus',
|
|
599
|
+
commandFlag: '--focus',
|
|
600
|
+
validation: (value: string) =>
|
|
601
|
+
['dependencies', 'quality', 'security', 'performance'].includes(
|
|
602
|
+
value
|
|
603
|
+
),
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
intentParam: 'format',
|
|
607
|
+
commandFlag: '--format',
|
|
608
|
+
defaultValue: 'table',
|
|
609
|
+
validation: (value: string) =>
|
|
610
|
+
['json', 'table', 'csv'].includes(value),
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
validation: [
|
|
614
|
+
{
|
|
615
|
+
type: 'directory_exists',
|
|
616
|
+
rule: 'path',
|
|
617
|
+
message: 'Target directory does not exist',
|
|
618
|
+
severity: 'error',
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
safetyLevel: 'safe',
|
|
622
|
+
requiresConfirmation: false,
|
|
623
|
+
examples: [
|
|
624
|
+
{
|
|
625
|
+
description: 'Analyze current directory',
|
|
626
|
+
intentInput: 'analyze the project',
|
|
627
|
+
expectedCommand: 'wundr analyze --path .',
|
|
628
|
+
},
|
|
629
|
+
],
|
|
630
|
+
category: 'analysis',
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
intent: 'create',
|
|
634
|
+
commandTemplate: 'wundr create',
|
|
635
|
+
parameterMapping: [
|
|
636
|
+
{
|
|
637
|
+
intentParam: 'type',
|
|
638
|
+
commandFlag: '',
|
|
639
|
+
required: true,
|
|
640
|
+
validation: (value: string) =>
|
|
641
|
+
['component', 'service', 'test', 'config'].includes(value),
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
intentParam: 'name',
|
|
645
|
+
commandFlag: '',
|
|
646
|
+
required: true,
|
|
647
|
+
validation: (value: string) =>
|
|
648
|
+
/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value),
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
intentParam: 'template',
|
|
652
|
+
commandFlag: '--template',
|
|
653
|
+
},
|
|
654
|
+
],
|
|
655
|
+
validation: [
|
|
656
|
+
{
|
|
657
|
+
type: 'custom',
|
|
658
|
+
rule: async params => {
|
|
659
|
+
// Check if file doesn't already exist
|
|
660
|
+
return true; // Simplified for example
|
|
661
|
+
},
|
|
662
|
+
message: 'File already exists',
|
|
663
|
+
severity: 'warning',
|
|
664
|
+
},
|
|
665
|
+
],
|
|
666
|
+
safetyLevel: 'caution',
|
|
667
|
+
requiresConfirmation: false,
|
|
668
|
+
examples: [
|
|
669
|
+
{
|
|
670
|
+
description: 'Create a new component',
|
|
671
|
+
intentInput: 'create component UserProfile',
|
|
672
|
+
expectedCommand: 'wundr create component UserProfile',
|
|
673
|
+
},
|
|
674
|
+
],
|
|
675
|
+
category: 'generation',
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
intent: 'init',
|
|
679
|
+
commandTemplate: 'wundr init',
|
|
680
|
+
parameterMapping: [
|
|
681
|
+
{
|
|
682
|
+
intentParam: 'project',
|
|
683
|
+
commandFlag: '',
|
|
684
|
+
defaultValue: '.',
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
intentParam: 'template',
|
|
688
|
+
commandFlag: '--template',
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
intentParam: 'force',
|
|
692
|
+
commandFlag: '--force',
|
|
693
|
+
transform: (value: boolean) => (value ? '--force' : ''),
|
|
694
|
+
},
|
|
695
|
+
],
|
|
696
|
+
validation: [
|
|
697
|
+
{
|
|
698
|
+
type: 'directory_exists',
|
|
699
|
+
rule: 'project',
|
|
700
|
+
message: 'Target directory does not exist',
|
|
701
|
+
severity: 'error',
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
safetyLevel: 'caution',
|
|
705
|
+
requiresConfirmation: true,
|
|
706
|
+
examples: [
|
|
707
|
+
{
|
|
708
|
+
description: 'Initialize new project',
|
|
709
|
+
intentInput: 'init new project',
|
|
710
|
+
expectedCommand: 'wundr init',
|
|
711
|
+
},
|
|
712
|
+
],
|
|
713
|
+
category: 'setup',
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
intent: 'dashboard',
|
|
717
|
+
commandTemplate: 'wundr dashboard',
|
|
718
|
+
parameterMapping: [
|
|
719
|
+
{
|
|
720
|
+
intentParam: 'port',
|
|
721
|
+
commandFlag: '--port',
|
|
722
|
+
defaultValue: '3000',
|
|
723
|
+
validation: (value: number) => value > 0 && value < 65536,
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
intentParam: 'view',
|
|
727
|
+
commandFlag: '--view',
|
|
728
|
+
},
|
|
729
|
+
],
|
|
730
|
+
validation: [],
|
|
731
|
+
safetyLevel: 'safe',
|
|
732
|
+
requiresConfirmation: false,
|
|
733
|
+
examples: [
|
|
734
|
+
{
|
|
735
|
+
description: 'Start dashboard',
|
|
736
|
+
intentInput: 'open dashboard',
|
|
737
|
+
expectedCommand: 'wundr dashboard --port 3000',
|
|
738
|
+
},
|
|
739
|
+
],
|
|
740
|
+
category: 'interface',
|
|
741
|
+
},
|
|
742
|
+
];
|
|
743
|
+
|
|
744
|
+
defaultTemplates.forEach(template =>
|
|
745
|
+
this.registerCommandTemplate(template)
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private async buildCommand(
|
|
750
|
+
template: CommandTemplate,
|
|
751
|
+
parameters: Record<string, any>,
|
|
752
|
+
context: ExecutionContext
|
|
753
|
+
): Promise<{ command: string; args: string[] }> {
|
|
754
|
+
const commandParts = template.commandTemplate.split(' ');
|
|
755
|
+
const baseCommand = commandParts[0] || '';
|
|
756
|
+
const args = commandParts.slice(1);
|
|
757
|
+
|
|
758
|
+
// Add parameter-based arguments
|
|
759
|
+
for (const mapping of template.parameterMapping) {
|
|
760
|
+
let value = parameters[mapping.intentParam];
|
|
761
|
+
|
|
762
|
+
// Apply default value
|
|
763
|
+
if (value === undefined && mapping.defaultValue !== undefined) {
|
|
764
|
+
value = mapping.defaultValue;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (value !== undefined && value !== '') {
|
|
768
|
+
// Transform value if needed
|
|
769
|
+
if (mapping.transform) {
|
|
770
|
+
const transformed = mapping.transform(value);
|
|
771
|
+
if (transformed) {
|
|
772
|
+
if (mapping.commandFlag) {
|
|
773
|
+
args.push(mapping.commandFlag);
|
|
774
|
+
args.push(transformed);
|
|
775
|
+
} else {
|
|
776
|
+
args.push(transformed);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
} else {
|
|
780
|
+
if (mapping.commandFlag) {
|
|
781
|
+
args.push(mapping.commandFlag);
|
|
782
|
+
args.push(String(value));
|
|
783
|
+
} else {
|
|
784
|
+
args.push(String(value));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return { command: baseCommand, args };
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
private async executeValidationRule(
|
|
794
|
+
rule: ValidationRule,
|
|
795
|
+
parameters: Record<string, any>,
|
|
796
|
+
context: ExecutionContext
|
|
797
|
+
): Promise<boolean> {
|
|
798
|
+
switch (rule.type) {
|
|
799
|
+
case 'parameter':
|
|
800
|
+
return parameters[rule.rule as string] !== undefined;
|
|
801
|
+
|
|
802
|
+
case 'file_exists':
|
|
803
|
+
const fs = await import('fs-extra');
|
|
804
|
+
const filePath = parameters[rule.rule as string];
|
|
805
|
+
return filePath ? await fs.pathExists(filePath) : false;
|
|
806
|
+
|
|
807
|
+
case 'directory_exists':
|
|
808
|
+
const fsDir = await import('fs-extra');
|
|
809
|
+
const dirPath = parameters[rule.rule as string];
|
|
810
|
+
if (!dirPath) return false;
|
|
811
|
+
const stats = await fsDir.stat(dirPath).catch(() => null);
|
|
812
|
+
return stats ? stats.isDirectory() : false;
|
|
813
|
+
|
|
814
|
+
case 'command_available':
|
|
815
|
+
return this.checkCommandAvailable(rule.rule as string);
|
|
816
|
+
|
|
817
|
+
case 'custom':
|
|
818
|
+
if (typeof rule.rule === 'function') {
|
|
819
|
+
return rule.rule(parameters);
|
|
820
|
+
}
|
|
821
|
+
return false;
|
|
822
|
+
|
|
823
|
+
default:
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
private async checkCommandAvailable(command: string): Promise<boolean> {
|
|
829
|
+
return new Promise(resolve => {
|
|
830
|
+
const child = spawn('which', [command], { stdio: 'ignore' });
|
|
831
|
+
child.on('close', code => resolve(code === 0));
|
|
832
|
+
child.on('error', () => resolve(false));
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
private async confirmExecution(
|
|
837
|
+
mappedCommand: any,
|
|
838
|
+
intentResult: IntentResult
|
|
839
|
+
): Promise<boolean> {
|
|
840
|
+
// In a real implementation, this would show a confirmation dialog
|
|
841
|
+
// For now, we'll assume confirmation based on safety level
|
|
842
|
+
|
|
843
|
+
if (mappedCommand.safetyLevel === 'dangerous') {
|
|
844
|
+
logger.warn(
|
|
845
|
+
`Dangerous command requires confirmation: ${mappedCommand.command} ${mappedCommand.args.join(' ')}`
|
|
846
|
+
);
|
|
847
|
+
// In a real CLI, this would prompt the user
|
|
848
|
+
return false; // Default to not confirmed for dangerous commands
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return true;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
private generateCommandExplanation(
|
|
855
|
+
command: string,
|
|
856
|
+
args: string[],
|
|
857
|
+
intentResult: IntentResult
|
|
858
|
+
): string {
|
|
859
|
+
const fullCommand = `${command} ${args.join(' ')}`;
|
|
860
|
+
|
|
861
|
+
return (
|
|
862
|
+
`This command (${fullCommand}) will execute the "${intentResult.intent}" operation` +
|
|
863
|
+
(intentResult.parameters
|
|
864
|
+
? ` with parameters: ${JSON.stringify(intentResult.parameters)}`
|
|
865
|
+
: '') +
|
|
866
|
+
`. ${intentResult.reasoning || 'No additional context provided.'}`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Cleanup resources
|
|
872
|
+
*/
|
|
873
|
+
destroy(): void {
|
|
874
|
+
// Cancel all running commands
|
|
875
|
+
for (const [id, process] of this.runningCommands) {
|
|
876
|
+
process.kill('SIGTERM');
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
this.runningCommands.clear();
|
|
880
|
+
this.commandHistory = [];
|
|
881
|
+
this.removeAllListeners();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
export default CommandMapper;
|