@probelabs/probe-chat 0.6.0-rc100

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.
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Implementation tool wrapper that integrates with the backend system
3
+ * @module ImplementTool
4
+ */
5
+
6
+ import BackendManager from './BackendManager.js';
7
+ import { createBackend, listBackendNames } from '../backends/registry.js';
8
+ import { BackendError, ErrorTypes } from './utils.js';
9
+ import { configManager } from './config.js';
10
+
11
+ /**
12
+ * Implementation tool that uses pluggable backends
13
+ * @class
14
+ */
15
+ class ImplementTool {
16
+ /**
17
+ * @param {Object} config - Tool configuration
18
+ * @param {boolean} [config.enabled=false] - Whether the tool is enabled
19
+ * @param {Object} [config.backendConfig] - Backend manager configuration
20
+ */
21
+ constructor(config = {}) {
22
+ this.enabled = config.enabled || false;
23
+ this.backendManager = null;
24
+ this.config = config;
25
+ this.initialized = false;
26
+ }
27
+
28
+ /**
29
+ * Initialize the implementation tool
30
+ * @returns {Promise<void>}
31
+ */
32
+ async initialize() {
33
+ if (this.initialized) return;
34
+
35
+ if (!this.enabled) {
36
+ throw new Error('Implementation tool is not enabled. Use --allow-edit flag to enable.');
37
+ }
38
+
39
+ // Initialize configuration manager
40
+ await configManager.initialize(this.config.configPath);
41
+
42
+ // Get configuration from manager
43
+ const implementConfig = configManager.getImplementConfig();
44
+ const backendConfigs = configManager.get('backends') || {};
45
+
46
+ // Create backend manager with configuration
47
+ const backendManagerConfig = {
48
+ ...implementConfig,
49
+ backends: backendConfigs,
50
+ ...this.config.backendConfig
51
+ };
52
+
53
+ this.backendManager = new BackendManager(backendManagerConfig);
54
+
55
+ // Register available backends
56
+ await this.registerBackends();
57
+
58
+ // Initialize backend manager
59
+ await this.backendManager.initialize();
60
+
61
+ // Validate configuration
62
+ const configValidation = configManager.validate();
63
+ if (!configValidation.valid) {
64
+ console.error('Configuration errors:', configValidation.errors.join(', '));
65
+ if (configValidation.warnings.length > 0) {
66
+ console.warn('Configuration warnings:', configValidation.warnings.join(', '));
67
+ }
68
+ }
69
+
70
+ const backendValidation = await this.backendManager.validateConfiguration();
71
+ if (!backendValidation.valid) {
72
+ console.warn('Backend configuration warnings:', backendValidation.errors.join(', '));
73
+ }
74
+
75
+ // Listen for configuration changes
76
+ configManager.onChange(async (newConfig) => {
77
+ console.error('Configuration changed, reinitializing backends...');
78
+ await this.reinitialize(newConfig);
79
+ });
80
+
81
+ this.initialized = true;
82
+ }
83
+
84
+ /**
85
+ * Register all available backends
86
+ * @private
87
+ */
88
+ async registerBackends() {
89
+ const backendNames = listBackendNames();
90
+
91
+ for (const name of backendNames) {
92
+ try {
93
+ const backend = createBackend(name);
94
+ if (backend) {
95
+ await this.backendManager.registerBackend(backend);
96
+ console.error(`Registered backend: ${name}`);
97
+ }
98
+ } catch (error) {
99
+ console.warn(`Failed to register backend '${name}':`, error.message);
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Reinitialize with new configuration
106
+ * @param {Object} newConfig - New configuration
107
+ * @private
108
+ */
109
+ async reinitialize(newConfig) {
110
+ try {
111
+ // Clean up existing backend manager
112
+ if (this.backendManager) {
113
+ await this.backendManager.cleanup();
114
+ }
115
+
116
+ // Create new backend manager with updated configuration
117
+ const implementConfig = newConfig.implement || {};
118
+ const backendConfigs = newConfig.backends || {};
119
+
120
+ const backendManagerConfig = {
121
+ ...implementConfig,
122
+ backends: backendConfigs,
123
+ ...this.config.backendConfig
124
+ };
125
+
126
+ this.backendManager = new BackendManager(backendManagerConfig);
127
+
128
+ // Re-register backends
129
+ await this.registerBackends();
130
+
131
+ // Re-initialize
132
+ await this.backendManager.initialize();
133
+
134
+ console.error('Backend reinitialization completed');
135
+ } catch (error) {
136
+ console.error('Failed to reinitialize backends:', error);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Get tool definition for AI models
142
+ * @returns {Object}
143
+ */
144
+ getToolDefinition() {
145
+ return {
146
+ name: 'implement',
147
+ description: 'Implement a feature or fix a bug using AI-powered code generation. Only available when --allow-edit is enabled.',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ task: {
152
+ type: 'string',
153
+ description: 'The task description for implementation'
154
+ },
155
+ backend: {
156
+ type: 'string',
157
+ description: 'Optional: Specific backend to use (aider, claude-code)',
158
+ enum: listBackendNames()
159
+ },
160
+ autoCommit: {
161
+ type: 'boolean',
162
+ description: 'Whether to auto-commit changes (default: false)'
163
+ },
164
+ generateTests: {
165
+ type: 'boolean',
166
+ description: 'Whether to generate tests for the implementation'
167
+ },
168
+ dryRun: {
169
+ type: 'boolean',
170
+ description: 'Perform a dry run without making actual changes'
171
+ }
172
+ },
173
+ required: ['task']
174
+ }
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Execute implementation task
180
+ * @param {Object} params - Execution parameters
181
+ * @param {string} params.task - Task description
182
+ * @param {string} [params.backend] - Specific backend to use
183
+ * @param {boolean} [params.autoCommit] - Auto-commit changes
184
+ * @param {boolean} [params.generateTests] - Generate tests
185
+ * @param {boolean} [params.dryRun] - Dry run mode
186
+ * @param {string} [params.sessionId] - Session ID
187
+ * @returns {Promise<Object>}
188
+ */
189
+ async execute(params) {
190
+ if (!this.enabled) {
191
+ throw new Error('Implementation tool is not enabled. Use --allow-edit flag to enable.');
192
+ }
193
+
194
+ // Ensure initialized
195
+ if (!this.initialized) {
196
+ await this.initialize();
197
+ }
198
+
199
+ const { task, backend, autoCommit, generateTests, dryRun, sessionId, ...rest } = params;
200
+
201
+ // Build implementation request
202
+ const request = {
203
+ sessionId: sessionId || `implement-${Date.now()}`,
204
+ task,
205
+ context: {
206
+ workingDirectory: process.cwd(),
207
+ ...rest.context
208
+ },
209
+ options: {
210
+ backend,
211
+ autoCommit: autoCommit || false,
212
+ generateTests: generateTests || false,
213
+ dryRun: dryRun || false,
214
+ ...rest.options
215
+ },
216
+ callbacks: {
217
+ onProgress: (update) => {
218
+ // Log progress to stderr for visibility
219
+ if (update.message) {
220
+ const prefix = update.type === 'stderr' ? '[STDERR]' : '[INFO]';
221
+ console.error(`${prefix} ${update.message}`);
222
+ }
223
+ },
224
+ onError: (error) => {
225
+ console.error('[ERROR]', error.message);
226
+ }
227
+ }
228
+ };
229
+
230
+ try {
231
+ console.error(`Executing implementation task: ${task.substring(0, 100)}${task.length > 100 ? '...' : ''}`);
232
+ console.error(`Using backend selection strategy: ${this.backendManager.config.selectionStrategy}`);
233
+
234
+ if (backend) {
235
+ console.error(`Requested backend: ${backend}`);
236
+ }
237
+
238
+ // Execute implementation
239
+ const result = await this.backendManager.executeImplementation(request);
240
+
241
+ console.error(`Implementation completed using backend: ${result.backend}`);
242
+
243
+ if (result.fallback) {
244
+ console.error('Note: Used fallback backend due to primary backend failure');
245
+ }
246
+
247
+ // Format result for compatibility with existing code
248
+ return {
249
+ success: result.success,
250
+ output: result.output,
251
+ error: result.error?.message || null,
252
+ command: `[${result.backend}] ${task}`,
253
+ timestamp: new Date().toISOString(),
254
+ prompt: task,
255
+ backend: result.backend,
256
+ metrics: result.metrics,
257
+ changes: result.changes
258
+ };
259
+
260
+ } catch (error) {
261
+ console.error(`Implementation failed:`, error.message);
262
+
263
+ // Format error response
264
+ return {
265
+ success: false,
266
+ output: null,
267
+ error: error.message,
268
+ command: `[failed] ${task}`,
269
+ timestamp: new Date().toISOString(),
270
+ prompt: task,
271
+ backend: null,
272
+ errorDetails: error instanceof BackendError ? error.toJSON() : { message: error.message }
273
+ };
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Cancel an implementation session
279
+ * @param {string} sessionId - Session ID to cancel
280
+ * @returns {Promise<void>}
281
+ */
282
+ async cancel(sessionId) {
283
+ if (!this.backendManager) {
284
+ throw new Error('Implementation tool not initialized');
285
+ }
286
+
287
+ await this.backendManager.cancelImplementation(sessionId);
288
+ }
289
+
290
+ /**
291
+ * Get backend information
292
+ * @returns {Promise<Object>}
293
+ */
294
+ async getBackendInfo() {
295
+ if (!this.initialized) {
296
+ await this.initialize();
297
+ }
298
+
299
+ const health = await this.backendManager.checkBackendHealth();
300
+ const availableBackends = this.backendManager.getAvailableBackends();
301
+
302
+ return {
303
+ enabled: this.enabled,
304
+ defaultBackend: this.backendManager.config.defaultBackend,
305
+ fallbackBackends: this.backendManager.config.fallbackBackends,
306
+ availableBackends,
307
+ health
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Clean up resources
313
+ * @returns {Promise<void>}
314
+ */
315
+ async cleanup() {
316
+ if (this.backendManager) {
317
+ await this.backendManager.cleanup();
318
+ }
319
+
320
+ // Clean up configuration manager
321
+ configManager.cleanup();
322
+
323
+ this.initialized = false;
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Create a singleton instance of the implementation tool
329
+ * This maintains compatibility with the existing code structure
330
+ */
331
+ function createImplementTool(config = {}) {
332
+ const tool = new ImplementTool(config);
333
+
334
+ // Return a tool object compatible with the existing interface
335
+ return {
336
+ ...tool.getToolDefinition(),
337
+ execute: async (params) => {
338
+ return await tool.execute(params);
339
+ },
340
+ cancel: async (sessionId) => {
341
+ return await tool.cancel(sessionId);
342
+ },
343
+ getInfo: async () => {
344
+ return await tool.getBackendInfo();
345
+ },
346
+ cleanup: async () => {
347
+ return await tool.cleanup();
348
+ },
349
+ // Expose the tool instance for advanced usage
350
+ instance: tool
351
+ };
352
+ }
353
+
354
+ export { ImplementTool, createImplementTool };