Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"typescript.tsc.autoDetect": "off",
"liveServer.settings.port": 5501,
"editor.fontSize": 14,
"generativeAi.option": "Gemini",
"generativeAi.option": "Groq",
"mcp.servers": {
"docker-gateway": {
"command": "docker",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"publisher": "fiatinnovations",
"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.",
"version": "3.7.31",
"version": "4.0.1",
"engines": {
"vscode": "^1.78.0"
},
Expand Down Expand Up @@ -291,7 +291,7 @@
"GLM",
"Local"
],
"default": "Gemini",
"default": "Groq",
"description": "Select Model"
},
"tavily.apiKey": {
Expand Down
182 changes: 182 additions & 0 deletions src/agents/langgraph/tools/debugger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { z } from "zod";
import { StructuredTool } from "@langchain/core/tools";
import { DebuggerService } from "../../../services/debugger.service";

export class DebugGetStateTool extends StructuredTool<any> {
name = "debug_get_state";
description = "Get the current debug session state, including threads.";
schema = z.object({});

constructor() {
super();
}

async _call(_input: Record<string, never>): Promise<string> {
try {
const service = DebuggerService.getInstance();
const threads = await service.getThreads();
return JSON.stringify({ threads });
} catch (error: any) {
return `Error: ${error.message}`;
}
}
}

export class DebugGetStackTraceTool extends StructuredTool<any> {
name = "debug_get_stack_trace";
description = "Get the stack trace for a specific thread.";
schema = z.object({
threadId: z
.number()
.describe("The ID of the thread to get stack trace for"),
startFrame: z.number().optional().default(0),
levels: z.number().optional().default(20),
});

constructor() {
super();
}

async _call(input: {
threadId: number;
startFrame: number;
levels: number;
}): Promise<string> {
try {
const service = DebuggerService.getInstance();
const stackFrames = await service.getStackTrace(
input.threadId,
input.startFrame,
input.levels,
);
return JSON.stringify({ stackFrames });
} catch (error: any) {
return `Error: ${error.message}`;
}
}
}

export class DebugGetVariablesTool extends StructuredTool<any> {
name = "debug_get_variables";
description =
"Get variables for a specific stack frame. If frameId is not provided, tries to get variables for the top frame of the first thread (or active thread).";
schema = z.object({
frameId: z.number().optional().describe("The ID of the stack frame"),
threadId: z
.number()
.optional()
.describe(
"The ID of the thread (if frameId is not known, will use top frame)",
),
});

constructor() {
super();
}

async _call(input: { frameId?: number; threadId?: number }): Promise<string> {
try {
const service = DebuggerService.getInstance();
let frameId = input.frameId;

if (frameId === undefined) {
// If no frameId, try to find one
let threadId = input.threadId;
if (threadId === undefined) {
const threads = await service.getThreads();
if (threads.length > 0) threadId = threads[0].id;
}
if (threadId !== undefined) {
const stack = await service.getStackTrace(threadId, 0, 1);
if (stack.length > 0) frameId = stack[0].id;
}
}

if (frameId === undefined) {
return "Error: Could not determine stack frame ID. Please provide frameId or threadId.";
}

const scopes = await service.getScopes(frameId);
const variablesResult: Record<string, any> = {};

for (const scope of scopes) {
const vars = await service.getVariables(scope.variablesReference);
variablesResult[scope.name] = vars;
}

return JSON.stringify(variablesResult);
} catch (error: any) {
return `Error: ${error.message}`;
}
}
}

export class DebugEvaluateTool extends StructuredTool<any> {
name = "debug_evaluate";
description = "Evaluate an expression in the context of a stack frame.";
schema = z.object({
expression: z.string().describe("The expression to evaluate"),
frameId: z.number().optional().describe("The ID of the stack frame"),
});

constructor() {
super();
}

async _call(input: {
expression: string;
frameId?: number;
}): Promise<string> {
try {
const service = DebuggerService.getInstance();
const result = await service.evaluate(input.expression, input.frameId);
return JSON.stringify(result);
} catch (error: any) {
return `Error: ${error.message}`;
}
}
}

export class DebugControlTool extends StructuredTool<any> {
name = "debug_control";
description = "Control the debugger execution (step, continue, pause).";
schema = z.object({
action: z
.enum(["stepOver", "stepInto", "stepOut", "continue", "pause"])
.describe("The action to perform"),
threadId: z.number().describe("The ID of the thread to control"),
});

constructor() {
super();
}

async _call(input: {
action: "stepOver" | "stepInto" | "stepOut" | "continue" | "pause";
threadId: number;
}): Promise<string> {
try {
const service = DebuggerService.getInstance();
switch (input.action) {
case "stepOver":
await service.stepOver(input.threadId);
break;
case "stepInto":
await service.stepInto(input.threadId);
break;
case "stepOut":
await service.stepOut(input.threadId);
break;
case "continue":
await service.continue(input.threadId);
break;
case "pause":
await service.pause(input.threadId);
break;
}
return `Action ${input.action} executed on thread ${input.threadId}.`;
} catch (error: any) {
return `Error: ${error.message}`;
}
}
}
59 changes: 59 additions & 0 deletions src/agents/langgraph/tools/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ import { LangChainWebPreviewTool } from "./web_preview";
import { LangChainSearchTool } from "./search";
import { LangChainTodoTool } from "./todo";
import { LangChainMemoryTool } from "./memory";
import {
DebugControlTool,
DebugEvaluateTool,
DebugGetStackTraceTool,
DebugGetStateTool,
DebugGetVariablesTool,
} from "./debugger";

const logger = Logger.initialize("ToolProvider", {
minLevel: LogLevel.DEBUG,
Expand Down Expand Up @@ -150,6 +157,36 @@ class SearchToolFactory implements IToolFactory {
}
}

class DebugGetStateToolFactory implements IToolFactory {
createTool(): StructuredTool<any> {
return new DebugGetStateTool();
}
}

class DebugGetStackTraceToolFactory implements IToolFactory {
createTool(): StructuredTool<any> {
return new DebugGetStackTraceTool();
}
}

class DebugGetVariablesToolFactory implements IToolFactory {
createTool(): StructuredTool<any> {
return new DebugGetVariablesTool();
}
}

class DebugEvaluateToolFactory implements IToolFactory {
createTool(): StructuredTool<any> {
return new DebugEvaluateTool();
}
}

class DebugControlToolFactory implements IToolFactory {
createTool(): StructuredTool<any> {
return new DebugControlTool();
}
}

class MCPToolFactory implements IToolFactory {
constructor(
private readonly mcpService: MCPService,
Expand Down Expand Up @@ -282,6 +319,23 @@ const TOOL_ROLE_MAPPING: Record<string, string[]> = {
"search_vector_db",
"manage_terminal",
],
debugger: [
"debug_get_state",
"debug_get_stack_trace",
"debug_get_variables",
"debug_evaluate",
"debug_control",
"analyze",
"read",
"search",
"terminal",
"run",
"command",
"list_files",
"edit_file",
"ripgrep_search",
"get_diagnostics",
],
};

export class ToolProvider {
Expand Down Expand Up @@ -313,6 +367,11 @@ export class ToolProvider {
new SearchToolFactory(this.contextRetriever),
new TodoToolFactory(),
new MemoryToolFactory(),
new DebugGetStateToolFactory(),
new DebugGetStackTraceToolFactory(),
new DebugGetVariablesToolFactory(),
new DebugEvaluateToolFactory(),
new DebugControlToolFactory(),
];

// Deduplicate tools during initialization
Expand Down
25 changes: 20 additions & 5 deletions src/commands/fixError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,35 @@ export class FixError extends CodeCommandHandler {
try {
const agentService = CodebuddyAgentService.getInstance();

// Check for active debug session
const activeSession = vscode.debug.activeDebugSession;
let debugContext = "";
if (activeSession) {
debugContext = `
ACTIVE DEBUG SESSION DETECTED:
Session Name: ${activeSession.name}
ID: ${activeSession.id}

You have access to Debugger Tools (debug_get_state, debug_get_stack_trace, debug_get_variables, debug_evaluate, debug_control).
Use them to inspect the running application state to better understand the error.
`;
}

const prompt = `Task: Fix the following error in the current file.

Error Message:
${errorMsg}

${debugContext}
Code Context:
${selectedCode}

Instructions:
1. Analyze the error and the code.
2. If you can fix it confidently, use the 'edit_file' tool to apply the fix directly.
3. If you apply a fix, verify it if possible (or at least ensure syntax is correct).
4. If you cannot fix it, or if it requires user input, provide a detailed explanation.
5. End your response with a summary of what you did.
2. If a debug session is active, use debug tools to inspect variables and stack trace.
3. If you can fix it confidently, use the 'edit_file' tool to apply the fix directly.
4. If you apply a fix, verify it if possible (or at least ensure syntax is correct).
5. If you cannot fix it, or if it requires user input, provide a detailed explanation.
6. End your response with a summary of what you did.
`;

let toolUsed = false;
Expand Down
Loading