codebakers 1.0.45

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 (53) hide show
  1. package/.vscodeignore +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +88 -0
  4. package/codebakers-1.0.0.vsix +0 -0
  5. package/codebakers-1.0.10.vsix +0 -0
  6. package/codebakers-1.0.11.vsix +0 -0
  7. package/codebakers-1.0.12.vsix +0 -0
  8. package/codebakers-1.0.13.vsix +0 -0
  9. package/codebakers-1.0.14.vsix +0 -0
  10. package/codebakers-1.0.15.vsix +0 -0
  11. package/codebakers-1.0.16.vsix +0 -0
  12. package/codebakers-1.0.17.vsix +0 -0
  13. package/codebakers-1.0.18.vsix +0 -0
  14. package/codebakers-1.0.19.vsix +0 -0
  15. package/codebakers-1.0.20.vsix +0 -0
  16. package/codebakers-1.0.21.vsix +0 -0
  17. package/codebakers-1.0.22.vsix +0 -0
  18. package/codebakers-1.0.23.vsix +0 -0
  19. package/codebakers-1.0.24.vsix +0 -0
  20. package/codebakers-1.0.25.vsix +0 -0
  21. package/codebakers-1.0.26.vsix +0 -0
  22. package/codebakers-1.0.27.vsix +0 -0
  23. package/codebakers-1.0.28.vsix +0 -0
  24. package/codebakers-1.0.29.vsix +0 -0
  25. package/codebakers-1.0.30.vsix +0 -0
  26. package/codebakers-1.0.31.vsix +0 -0
  27. package/codebakers-1.0.32.vsix +0 -0
  28. package/codebakers-1.0.35.vsix +0 -0
  29. package/codebakers-1.0.36.vsix +0 -0
  30. package/codebakers-1.0.37.vsix +0 -0
  31. package/codebakers-1.0.38.vsix +0 -0
  32. package/codebakers-1.0.39.vsix +0 -0
  33. package/codebakers-1.0.40.vsix +0 -0
  34. package/codebakers-1.0.41.vsix +0 -0
  35. package/codebakers-1.0.42.vsix +0 -0
  36. package/codebakers-1.0.43.vsix +0 -0
  37. package/codebakers-1.0.44.vsix +0 -0
  38. package/codebakers-1.0.45.vsix +0 -0
  39. package/dist/extension.js +1394 -0
  40. package/esbuild.js +63 -0
  41. package/media/icon.png +0 -0
  42. package/media/icon.svg +7 -0
  43. package/nul +1 -0
  44. package/package.json +127 -0
  45. package/preview.html +547 -0
  46. package/src/ChatPanelProvider.ts +1815 -0
  47. package/src/ChatViewProvider.ts +749 -0
  48. package/src/CodeBakersClient.ts +1146 -0
  49. package/src/CodeValidator.ts +645 -0
  50. package/src/FileOperations.ts +410 -0
  51. package/src/ProjectContext.ts +526 -0
  52. package/src/extension.ts +332 -0
  53. package/tsconfig.json +19 -0
@@ -0,0 +1,410 @@
1
+ import * as vscode from 'vscode';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+
5
+ export interface FileChange {
6
+ path: string;
7
+ action: 'create' | 'edit' | 'delete';
8
+ content?: string;
9
+ description?: string;
10
+ }
11
+
12
+ export interface CommandToRun {
13
+ command: string;
14
+ description?: string;
15
+ }
16
+
17
+ export interface OperationResult {
18
+ success: boolean;
19
+ error?: string;
20
+ backup?: FileBackup;
21
+ }
22
+
23
+ export interface FileBackup {
24
+ path: string;
25
+ originalContent: string | null; // null if file didn't exist
26
+ timestamp: number;
27
+ }
28
+
29
+ export class FileOperations {
30
+ private workspaceRoot: string | undefined;
31
+ private _backups: Map<string, FileBackup> = new Map();
32
+ private _operationLock: Set<string> = new Set(); // Prevent concurrent operations on same file
33
+
34
+ constructor() {
35
+ this.workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
36
+ }
37
+
38
+ /**
39
+ * Check if a file exists
40
+ */
41
+ async fileExists(relativePath: string): Promise<boolean> {
42
+ if (!this.workspaceRoot) return false;
43
+
44
+ try {
45
+ const fullPath = path.join(this.workspaceRoot, relativePath);
46
+ await vscode.workspace.fs.stat(vscode.Uri.file(fullPath));
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Get a lock on a file path to prevent concurrent operations
55
+ */
56
+ private async acquireLock(relativePath: string, timeoutMs: number = 5000): Promise<boolean> {
57
+ const startTime = Date.now();
58
+
59
+ while (this._operationLock.has(relativePath)) {
60
+ if (Date.now() - startTime > timeoutMs) {
61
+ console.error(`FileOperations: Timeout waiting for lock on ${relativePath}`);
62
+ return false;
63
+ }
64
+ await new Promise(resolve => setTimeout(resolve, 50));
65
+ }
66
+
67
+ this._operationLock.add(relativePath);
68
+ return true;
69
+ }
70
+
71
+ private releaseLock(relativePath: string): void {
72
+ this._operationLock.delete(relativePath);
73
+ }
74
+
75
+ /**
76
+ * Create a backup of a file before modifying it
77
+ */
78
+ private async createBackup(relativePath: string): Promise<FileBackup> {
79
+ const content = await this.readFile(relativePath);
80
+ const backup: FileBackup = {
81
+ path: relativePath,
82
+ originalContent: content,
83
+ timestamp: Date.now()
84
+ };
85
+ this._backups.set(relativePath, backup);
86
+ return backup;
87
+ }
88
+
89
+ /**
90
+ * Restore a file from backup
91
+ */
92
+ async restoreFromBackup(relativePath: string): Promise<boolean> {
93
+ const backup = this._backups.get(relativePath);
94
+ if (!backup) {
95
+ vscode.window.showWarningMessage(`No backup found for ${relativePath}`);
96
+ return false;
97
+ }
98
+
99
+ try {
100
+ if (backup.originalContent === null) {
101
+ // File didn't exist before - delete it
102
+ await this.deleteFile(relativePath);
103
+ } else {
104
+ // Restore original content
105
+ await this.writeFile(relativePath, backup.originalContent);
106
+ }
107
+ this._backups.delete(relativePath);
108
+ vscode.window.showInformationMessage(`✅ Restored ${relativePath}`);
109
+ return true;
110
+ } catch (error) {
111
+ vscode.window.showErrorMessage(`Failed to restore ${relativePath}: ${error}`);
112
+ return false;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Get all available backups
118
+ */
119
+ getBackups(): FileBackup[] {
120
+ return Array.from(this._backups.values());
121
+ }
122
+
123
+ /**
124
+ * Clear old backups (older than 1 hour)
125
+ */
126
+ cleanupOldBackups(): void {
127
+ const oneHourAgo = Date.now() - (60 * 60 * 1000);
128
+ for (const [path, backup] of this._backups) {
129
+ if (backup.timestamp < oneHourAgo) {
130
+ this._backups.delete(path);
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Read a file from the workspace
137
+ */
138
+ async readFile(relativePath: string): Promise<string | null> {
139
+ if (!this.workspaceRoot) return null;
140
+
141
+ try {
142
+ const fullPath = path.join(this.workspaceRoot, relativePath);
143
+ const uri = vscode.Uri.file(fullPath);
144
+ const content = await vscode.workspace.fs.readFile(uri);
145
+ return Buffer.from(content).toString('utf-8');
146
+ } catch (error) {
147
+ console.error(`Failed to read file ${relativePath}:`, error);
148
+ return null;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Write content to a file
154
+ */
155
+ async writeFile(relativePath: string, content: string): Promise<boolean> {
156
+ if (!this.workspaceRoot) {
157
+ vscode.window.showErrorMessage('No workspace folder open');
158
+ return false;
159
+ }
160
+
161
+ try {
162
+ const fullPath = path.join(this.workspaceRoot, relativePath);
163
+ const uri = vscode.Uri.file(fullPath);
164
+
165
+ // Ensure directory exists
166
+ const dir = path.dirname(fullPath);
167
+ if (!fs.existsSync(dir)) {
168
+ fs.mkdirSync(dir, { recursive: true });
169
+ }
170
+
171
+ await vscode.workspace.fs.writeFile(uri, Buffer.from(content, 'utf-8'));
172
+ return true;
173
+ } catch (error) {
174
+ console.error(`Failed to write file ${relativePath}:`, error);
175
+ vscode.window.showErrorMessage(`Failed to write ${relativePath}: ${error}`);
176
+ return false;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Delete a file with proper error handling
182
+ */
183
+ async deleteFile(relativePath: string): Promise<boolean> {
184
+ if (!this.workspaceRoot) {
185
+ console.error('FileOperations: No workspace root');
186
+ return false;
187
+ }
188
+
189
+ try {
190
+ const fullPath = path.join(this.workspaceRoot, relativePath);
191
+ const uri = vscode.Uri.file(fullPath);
192
+
193
+ // Check if file exists first
194
+ const exists = await this.fileExists(relativePath);
195
+ if (!exists) {
196
+ console.log(`FileOperations: File ${relativePath} doesn't exist, treating as success`);
197
+ return true; // File doesn't exist - that's fine, we wanted it gone anyway
198
+ }
199
+
200
+ await vscode.workspace.fs.delete(uri);
201
+ console.log(`FileOperations: Deleted ${relativePath}`);
202
+ return true;
203
+ } catch (error: any) {
204
+ // Handle "file not found" as success (already deleted)
205
+ if (error?.code === 'FileNotFound' || error?.code === 'ENOENT') {
206
+ console.log(`FileOperations: File ${relativePath} already deleted`);
207
+ return true;
208
+ }
209
+ console.error(`FileOperations: Failed to delete ${relativePath}:`, error);
210
+ vscode.window.showErrorMessage(`Failed to delete ${relativePath}: ${error.message || error}`);
211
+ return false;
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Show diff between current file and proposed content
217
+ */
218
+ async showDiff(relativePath: string, newContent: string, title?: string): Promise<void> {
219
+ if (!this.workspaceRoot) return;
220
+
221
+ const fullPath = path.join(this.workspaceRoot, relativePath);
222
+ const originalUri = vscode.Uri.file(fullPath);
223
+
224
+ // Create a virtual document for the new content
225
+ const newUri = vscode.Uri.parse(`codebakers-diff:${relativePath}?content=${encodeURIComponent(newContent)}`);
226
+
227
+ const diffTitle = title || `CodeBakers: ${relativePath}`;
228
+
229
+ await vscode.commands.executeCommand('vscode.diff', originalUri, newUri, diffTitle);
230
+ }
231
+
232
+ /**
233
+ * Apply a single file change with locking and backup
234
+ */
235
+ async applyChange(change: FileChange): Promise<boolean> {
236
+ // Acquire lock to prevent concurrent operations on same file
237
+ const lockAcquired = await this.acquireLock(change.path);
238
+ if (!lockAcquired) {
239
+ vscode.window.showErrorMessage(`Cannot modify ${change.path} - another operation is in progress`);
240
+ return false;
241
+ }
242
+
243
+ try {
244
+ // Create backup before any modification
245
+ if (change.action === 'edit' || change.action === 'delete') {
246
+ await this.createBackup(change.path);
247
+ }
248
+
249
+ switch (change.action) {
250
+ case 'create':
251
+ if (!change.content) {
252
+ vscode.window.showErrorMessage(`Cannot create ${change.path} - no content provided`);
253
+ return false;
254
+ }
255
+ // Check if file already exists
256
+ const existsForCreate = await this.fileExists(change.path);
257
+ if (existsForCreate) {
258
+ // It's actually an edit, create backup
259
+ await this.createBackup(change.path);
260
+ }
261
+ return this.writeFile(change.path, change.content);
262
+
263
+ case 'edit':
264
+ if (!change.content) {
265
+ vscode.window.showErrorMessage(`Cannot edit ${change.path} - no content provided`);
266
+ return false;
267
+ }
268
+ // Check if file exists
269
+ const existsForEdit = await this.fileExists(change.path);
270
+ if (!existsForEdit) {
271
+ console.log(`FileOperations: File ${change.path} doesn't exist, creating instead of editing`);
272
+ }
273
+ return this.writeFile(change.path, change.content);
274
+
275
+ case 'delete':
276
+ return this.deleteFile(change.path);
277
+
278
+ default:
279
+ vscode.window.showErrorMessage(`Unknown action: ${(change as any).action}`);
280
+ return false;
281
+ }
282
+ } finally {
283
+ // Always release the lock
284
+ this.releaseLock(change.path);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Apply multiple file changes
290
+ */
291
+ async applyChanges(changes: FileChange[]): Promise<{ success: number; failed: number }> {
292
+ let success = 0;
293
+ let failed = 0;
294
+
295
+ for (const change of changes) {
296
+ const result = await this.applyChange(change);
297
+ if (result) {
298
+ success++;
299
+ } else {
300
+ failed++;
301
+ }
302
+ }
303
+
304
+ return { success, failed };
305
+ }
306
+
307
+ /**
308
+ * Run a command in the integrated terminal
309
+ */
310
+ async runCommand(command: string, name?: string): Promise<void> {
311
+ const terminal = vscode.window.createTerminal({
312
+ name: name || 'CodeBakers',
313
+ cwd: this.workspaceRoot
314
+ });
315
+
316
+ terminal.show();
317
+ terminal.sendText(command);
318
+ }
319
+
320
+ /**
321
+ * Run a command and capture output (for quick commands)
322
+ */
323
+ async runCommandWithOutput(command: string): Promise<string> {
324
+ return new Promise((resolve, reject) => {
325
+ const { exec } = require('child_process');
326
+
327
+ exec(command, { cwd: this.workspaceRoot, timeout: 30000 }, (error: any, stdout: string, stderr: string) => {
328
+ if (error) {
329
+ reject(new Error(stderr || error.message));
330
+ } else {
331
+ resolve(stdout);
332
+ }
333
+ });
334
+ });
335
+ }
336
+
337
+ /**
338
+ * Get list of files in workspace matching a pattern
339
+ */
340
+ async findFiles(pattern: string, exclude?: string): Promise<string[]> {
341
+ const files = await vscode.workspace.findFiles(pattern, exclude || '**/node_modules/**');
342
+ return files.map(f => vscode.workspace.asRelativePath(f));
343
+ }
344
+
345
+ /**
346
+ * Open a file in the editor
347
+ */
348
+ async openFile(relativePath: string, selection?: { line: number; column?: number }): Promise<void> {
349
+ if (!this.workspaceRoot) return;
350
+
351
+ const fullPath = path.join(this.workspaceRoot, relativePath);
352
+ const uri = vscode.Uri.file(fullPath);
353
+ const doc = await vscode.workspace.openTextDocument(uri);
354
+ const editor = await vscode.window.showTextDocument(doc);
355
+
356
+ if (selection) {
357
+ const pos = new vscode.Position(selection.line - 1, (selection.column || 1) - 1);
358
+ editor.selection = new vscode.Selection(pos, pos);
359
+ editor.revealRange(new vscode.Range(pos, pos), vscode.TextEditorRevealType.InCenter);
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Get the currently open file content and path
365
+ */
366
+ getCurrentFile(): { path: string; content: string; selection?: string } | null {
367
+ const editor = vscode.window.activeTextEditor;
368
+ if (!editor) return null;
369
+
370
+ const relativePath = vscode.workspace.asRelativePath(editor.document.uri);
371
+ const content = editor.document.getText();
372
+ const selection = editor.selection.isEmpty ? undefined : editor.document.getText(editor.selection);
373
+
374
+ return { path: relativePath, content, selection };
375
+ }
376
+
377
+ /**
378
+ * Get workspace file tree (top-level structure)
379
+ */
380
+ async getFileTree(maxDepth: number = 2): Promise<string[]> {
381
+ const files: string[] = [];
382
+
383
+ const addFiles = async (dir: string, depth: number) => {
384
+ if (depth > maxDepth) return;
385
+
386
+ const pattern = new vscode.RelativePattern(dir, '*');
387
+ const entries = await vscode.workspace.findFiles(pattern, '**/node_modules/**', 100);
388
+
389
+ for (const entry of entries) {
390
+ const relativePath = vscode.workspace.asRelativePath(entry);
391
+ files.push(relativePath);
392
+ }
393
+ };
394
+
395
+ if (this.workspaceRoot) {
396
+ await addFiles(this.workspaceRoot, 0);
397
+ }
398
+
399
+ return files.sort();
400
+ }
401
+ }
402
+
403
+ // Virtual document provider for diff view
404
+ export class DiffContentProvider implements vscode.TextDocumentContentProvider {
405
+ provideTextDocumentContent(uri: vscode.Uri): string {
406
+ const params = new URLSearchParams(uri.query);
407
+ const content = params.get('content');
408
+ return content ? decodeURIComponent(content) : '';
409
+ }
410
+ }