Skip to content

Commit b5f76ca

Browse files
Merge branch 'main' into remove_libSQL
2 parents eb807a3 + 37cbbe3 commit b5f76ca

File tree

15 files changed

+217
-166
lines changed

15 files changed

+217
-166
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"publisher": "fiatinnovations",
1010
"description": "CodeBuddy is a Visual Studio Code extension that enhances developer productivity through AI-powered code assistance. It provides intelligent code review, refactoring suggestions, optimization tips, and interactive chat capabilities powered by multiple AI models including Gemini, Groq, Anthropic, and Deepseek.",
11-
"version": "2.0.0",
11+
"version": "2.1.0",
1212
"engines": {
1313
"vscode": "^1.78.0"
1414
},

repo/dev.db

Whitespace-only changes.

src/application/constant.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ export const LLM_CONFIGS: Record<string, ILLMConfig> = {
386386
},
387387
};
388388

389+
export type FormattedMessage =
390+
| { role: string; parts: [{ text: string }] }
391+
| { role: string; content: string };
392+
389393
//Note, this kind of configuration can be used by orchestrator on where to direct the requests
390394
// export const LLM_CONFIGS: Record<string, LLMConfig> = {
391395
// gemini: {

src/commands/handler.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
398398
}
399399
}
400400

401-
async execute(message?: string, action?: string): Promise<void> {
401+
async execute(action?: string, message?: string): Promise<void> {
402402
try {
403403
let prompt: string | undefined;
404404
const response = (await this.generateResponse(
@@ -415,20 +415,12 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
415415
}
416416
switch (this.generativeAi) {
417417
case generativeAiModels.GROQ:
418-
await GroqWebViewProvider.webView?.webview.postMessage({
419-
type: "codebuddy-commands",
420-
message: action,
421-
});
422418
await GroqWebViewProvider.webView?.webview.postMessage({
423419
type: "bot-response",
424420
message: formattedResponse,
425421
});
426422
break;
427423
case generativeAiModels.GEMINI:
428-
await GeminiWebViewProvider.webView?.webview.postMessage({
429-
type: "codebuddy-commands",
430-
message: action,
431-
});
432424
await GeminiWebViewProvider.webView?.webview.postMessage({
433425
type: "bot-response",
434426
message: formattedResponse,

src/extension.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as vscode from "vscode";
2-
import { Orchestrator } from "./agents/orchestrator";
32
import {
43
APP_CONFIG,
54
CODEBUDDY_ACTIONS,
@@ -20,7 +19,6 @@ import { EventEmitter } from "./emitter/publisher";
2019
import { Logger, LogLevel } from "./infrastructure/logger/logger";
2120
import { dbManager } from "./infrastructure/repository/db-manager";
2221
import { Memory } from "./memory/base";
23-
import { FileManager } from "./services/file-manager";
2422
import { FileUploadService } from "./services/file-upload";
2523
import { FileWatcherService } from "./services/file-watcher";
2624
import { Credentials } from "./services/github-authentication";
@@ -32,6 +30,7 @@ import { DeepseekWebViewProvider } from "./webview-providers/deepseek";
3230
import { GeminiWebViewProvider } from "./webview-providers/gemini";
3331
import { GroqWebViewProvider } from "./webview-providers/groq";
3432
import { WebViewProviderManager } from "./webview-providers/manager";
33+
import { InitDatabaseManager } from "./infrastructure/repository/init-db-manager";
3534

3635
const {
3736
geminiKey,
@@ -84,8 +83,9 @@ async function createFileDB(context: vscode.ExtensionContext) {
8483
export async function activate(context: vscode.ExtensionContext) {
8584
try {
8685
const secretStorageService = new SecretStorageService(context);
87-
await createFileDB(context);
88-
await connectToDatabase(context);
86+
const dbManager = InitDatabaseManager.getInstance();
87+
await dbManager.initialize(context);
88+
8989
const credentials = new Credentials();
9090
const { apiKey, model } = getAPIKeyAndModel("gemini");
9191
FileUploadService.initialize(apiKey);
@@ -152,31 +152,26 @@ export async function activate(context: vscode.ExtensionContext) {
152152
const actionMap = {
153153
[comment]: async () => {
154154
await getComment.execute(
155-
undefined,
156155
"💭 Add a helpful comment to explain the code logic",
157156
);
158157
},
159158
[review]: async () => {
160159
await generateReview.execute(
161-
undefined,
162160
"🔍 Perform a thorough code review to ensure best practices",
163161
);
164162
},
165163
[refactor]: async () => {
166164
await generateRefactoredCode.execute(
167-
undefined,
168165
" 🔄 Improve code readability and maintainability",
169166
);
170167
},
171168
[optimize]: async () => {
172169
await generateOptimizeCode.execute(
173-
undefined,
174170
"⚡ optimize for performance and efficiency",
175171
);
176172
},
177173
[interviewMe]: async () => {
178174
await generateInterviewQuestions.execute(
179-
undefined,
180175
"📚 Prepare for technical interviews with relevant questions",
181176
);
182177
},
@@ -189,25 +184,19 @@ export async function activate(context: vscode.ExtensionContext) {
189184
},
190185
[explain]: async () => {
191186
await explainCode.execute(
192-
undefined,
193187
"💬 Get a clear and concise explanation of the code concept",
194188
);
195189
},
196190
[commitMessage]: async () => {
197-
await generateCommitMessage.execute(
198-
undefined,
199-
"🧪 generating commit message",
200-
);
191+
await generateCommitMessage.execute(undefined, "commitMessage");
201192
},
202193
[generateDiagram]: async () => {
203194
await generateMermaidDiagram.execute(
204-
undefined,
205195
"📈 Visualize the code with a Mermaid diagram",
206196
);
207197
},
208198
[inlineChat]: async () => {
209199
await getInLineChat.execute(
210-
undefined,
211200
"💬 Discuss and reason about your code with me",
212201
);
213202
},

src/infrastructure/repository/database-manager.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ class DatabaseManager {
104104
return false;
105105
}
106106
}
107+
108+
public async close(): Promise<void> {
109+
if (this.client) {
110+
await this.client.close();
111+
this.client = undefined;
112+
}
113+
}
107114
}
108115

109116
export const dbManager = DatabaseManager.getInstance();
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import * as vscode from "vscode";
2+
import * as fs from "fs";
3+
import * as path from "path";
4+
import { Logger, LogLevel } from "../logger/logger";
5+
import { FileManager } from "../../services/file-manager";
6+
import { dbManager } from "./database-manager";
7+
8+
export class InitDatabaseManager implements vscode.Disposable {
9+
private static instance: InitDatabaseManager;
10+
private isConnected: boolean = false;
11+
private connectionType: "file" | "memory" = "file";
12+
private readonly logger: Logger;
13+
14+
private constructor() {
15+
this.logger = Logger.initialize("InitDatabaseManager", {
16+
minLevel: LogLevel.DEBUG,
17+
});
18+
}
19+
20+
/**
21+
* Returns a singleton instance of the DatabaseManager, lazy-loading it if necessary.
22+
* This ensures that only one instance of the DatabaseManager is created throughout the application.
23+
*/
24+
public static getInstance(): InitDatabaseManager {
25+
InitDatabaseManager.instance ??= new InitDatabaseManager();
26+
27+
return InitDatabaseManager.instance;
28+
}
29+
30+
async initialize(context: vscode.ExtensionContext): Promise<boolean> {
31+
try {
32+
await this.createFileDB(context);
33+
await this.connectToDatabase(context);
34+
this.isConnected = true;
35+
return true;
36+
} catch (error) {
37+
this.logger.error(
38+
"Database initialization failed, continuing with limited functionality",
39+
error,
40+
);
41+
vscode.window.showWarningMessage(
42+
"Some features requiring database access will be limited. The extension will continue to function.",
43+
);
44+
return false;
45+
}
46+
}
47+
48+
async createFileDB(context: vscode.ExtensionContext) {
49+
try {
50+
const fileUploader = new FileManager(context, "repo");
51+
const files = await fileUploader.getFiles();
52+
if (!files?.find((file) => file.includes("dev.db"))) {
53+
await fileUploader.createFile("dev.db");
54+
}
55+
} catch (error: any) {
56+
this.logger.warn("Unable to create DB file, will try to continue", error);
57+
// Don't throw, just log and continue
58+
}
59+
}
60+
61+
async connectToDatabase(context: vscode.ExtensionContext) {
62+
const dbDir = path.join(context.extensionPath, "repo");
63+
const dbPath = path.join(dbDir, "dev.db");
64+
const isWindows = dbPath.includes("\\");
65+
const filePath = isWindows ? `file:/${dbPath}` : `file:${dbPath}`;
66+
67+
try {
68+
if (!fs.existsSync(dbDir)) {
69+
fs.mkdirSync(dbDir, { recursive: true });
70+
}
71+
} catch (error) {
72+
this.logger.warn("Failed to create database directory", error);
73+
return this.setupInMemoryDatabase();
74+
}
75+
76+
const maxRetries = 3;
77+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
78+
try {
79+
await dbManager.connect(filePath);
80+
this.connectionType = "file";
81+
return;
82+
} catch (error: any) {
83+
this.logger.warn(`Attempt ${attempt} to connect to DB failed`, error);
84+
if (attempt === maxRetries) {
85+
this.logger.warn("Switching to in-memory database");
86+
return this.setupInMemoryDatabase();
87+
}
88+
await new Promise((resolve) => setTimeout(resolve, 500));
89+
}
90+
}
91+
}
92+
93+
async setupInMemoryDatabase() {
94+
try {
95+
await dbManager.connect(":memory:");
96+
this.connectionType = "memory";
97+
vscode.window.showInformationMessage(
98+
"Using in-memory database due to connection issues.",
99+
);
100+
} catch (error) {
101+
this.logger.error("Failed to set up in-memory database", error);
102+
// Don't throw, just set as not connected
103+
this.isConnected = false;
104+
}
105+
}
106+
107+
isDbConnected(): boolean {
108+
return this.isConnected;
109+
}
110+
111+
dispose() {
112+
try {
113+
if (this.isConnected) {
114+
dbManager.close();
115+
this.isConnected = false;
116+
}
117+
} catch (error) {
118+
this.logger.error("Error closing database connection", error);
119+
}
120+
}
121+
122+
public async close(): Promise<void> {
123+
try {
124+
if (this.isConnected) {
125+
await dbManager.close();
126+
this.isConnected = false;
127+
}
128+
} catch (error) {
129+
this.logger.error("Error closing database connection", error);
130+
}
131+
}
132+
}
133+
134+
export const dbManagerInstance = InitDatabaseManager.getInstance();

src/memory/base.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ export class Memory {
1010
}
1111

1212
public static getInstance(): Memory {
13-
if (!Memory.instance) {
14-
return (Memory.instance = new Memory());
15-
}
13+
Memory.instance ??= new Memory();
1614
return Memory.instance;
1715
}
1816

src/services/chat-history-manager.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { LLM_CONFIGS } from "./../application/constant";
1+
import { Memory } from "../memory/base";
2+
import { FormattedMessage, LLM_CONFIGS } from "./../application/constant";
23
import { AgentService } from "./agent-state";
34

45
interface ChatMessage {
@@ -58,31 +59,25 @@ export class ChatHistoryManager {
5859
key: string,
5960
): Promise<any[]> {
6061
const config = LLM_CONFIGS[model];
61-
const chatHistory =
62-
(await new Promise<any[]>((resolve) =>
63-
setTimeout(async () => resolve(await this.getHistory(key)), 1000),
64-
)) ?? [];
62+
const existingHistory = Memory.get(key) ?? [];
6563

66-
this.chatHistory = [];
64+
const formattedHistory: Record<string, ILLMConfig>[] = existingHistory.map(
65+
(historyItem: FormattedMessage) => {
66+
const currentRole =
67+
historyItem.role === "user" ? config.userRole : config.botRole;
68+
return config.formatMessage(
69+
currentRole,
70+
"content" in historyItem
71+
? historyItem.content
72+
: historyItem.parts[0].text,
73+
);
74+
},
75+
);
6776

68-
if (!chatHistory || chatHistory.length === 0) {
69-
this.chatHistory.push(config.formatMessage(role, message));
70-
return this.chatHistory;
71-
}
72-
if (chatHistory?.length) {
73-
const recentHistory = chatHistory[chatHistory.length - 1];
74-
if (recentHistory?.content) {
75-
recentHistory.content = message;
76-
}
77-
}
77+
const formattedNewMessage = config.formatMessage(role, message);
78+
this.chatHistory = [...formattedHistory, formattedNewMessage];
7879

79-
for (const historyItem of chatHistory) {
80-
const currentRole =
81-
historyItem.type === "bot" ? config.botRole : config.userRole;
82-
this.chatHistory.push(
83-
config.formatMessage(currentRole, historyItem.content),
84-
);
85-
}
80+
Memory.set(key, this.chatHistory);
8681

8782
return this.chatHistory;
8883
}

src/services/code-indexing.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// import { ResultSet } from "@libsql/client/.";
21
import { IFunctionData } from "../application/interfaces";
32
import { Logger } from "../infrastructure/logger/logger";
43
import { CodeRepository } from "../infrastructure/repository/code";

0 commit comments

Comments
 (0)