From 5f841ab3d8ec082a5137bf8ce86d4568034fdf19 Mon Sep 17 00:00:00 2001 From: ankita jha Date: Tue, 28 Jul 2026 00:16:12 -0700 Subject: [PATCH 1/3] feat: add noWait parameter to start_debugging and continue_execution --- src/controlServer.ts | 2 +- src/debugMCPServer.ts | 10 ++++++++-- src/debuggingHandler.ts | 17 +++++++++++++---- src/routingDebuggingHandler.ts | 5 +++-- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/controlServer.ts b/src/controlServer.ts index 215938f..ec85e8c 100644 --- a/src/controlServer.ts +++ b/src/controlServer.ts @@ -90,7 +90,7 @@ export class ControlServer { case 'handleStepOut': return this.handler.handleStepOut(); case 'handleContinue': - return this.handler.handleContinue(); + return this.handler.handleContinue(args); case 'handlePause': return this.handler.handlePause(); case 'handleRestart': diff --git a/src/debugMCPServer.ts b/src/debugMCPServer.ts index 4de9287..677b0ea 100644 --- a/src/debugMCPServer.ts +++ b/src/debugMCPServer.ts @@ -220,8 +220,11 @@ export class DebugMCPServer { 'Optional debug configuration name from launch.json. ' + 'If omitted, DebugMCP uses its default generated configuration.' ), + noWait: z.boolean().optional().describe( + 'If true, returns immediately after starting instead of waiting for a breakpoint.' + ), }, - }, async (args: { fileFullPath: string; workingDirectory: string; testName?: string; configurationName?: string }) => + }, async (args: { fileFullPath: string; workingDirectory: string; testName?: string; configurationName?: string; noWait?: boolean }) => this.runTool('start_debugging', () => debuggingHandler.handleStartDebugging(args))); // Stop debugging tool @@ -247,7 +250,10 @@ export class DebugMCPServer { // Continue execution tool server.registerTool('continue_execution', { description: 'Resume program execution until the next breakpoint is hit or the program completes.', - }, async () => this.runTool('continue_execution', () => debuggingHandler.handleContinue())); + inputSchema: { + noWait: z.boolean().optional().describe('If true, returns immediately instead of waiting for the next breakpoint.'), + }, + }, async (args: { noWait?: boolean } = {}) => this.runTool('continue_execution', () => debuggingHandler.handleContinue(args))); // Pause execution tool server.registerTool('pause_execution', { diff --git a/src/debuggingHandler.ts b/src/debuggingHandler.ts index 08551b1..c415f3d 100644 --- a/src/debuggingHandler.ts +++ b/src/debuggingHandler.ts @@ -10,12 +10,12 @@ import { logger } from './utils/logger'; * Interface for debugging handler operations */ export interface IDebuggingHandler { - handleStartDebugging(args: { fileFullPath: string; workingDirectory: string; testName?: string; configurationName?: string }): Promise; + handleStartDebugging(args: { fileFullPath: string; workingDirectory: string; testName?: string; configurationName?: string; noWait?: boolean }): Promise; handleStopDebugging(): Promise; handleStepOver(): Promise; handleStepInto(): Promise; handleStepOut(): Promise; - handleContinue(): Promise; + handleContinue(args?: { noWait?: boolean }): Promise; handlePause(): Promise; handleRestart(): Promise; handleAddBreakpoint(args: { fileFullPath: string; line: number; condition?: string }): Promise; @@ -51,8 +51,9 @@ export class DebuggingHandler implements IDebuggingHandler { workingDirectory: string; testName?: string; configurationName?: string; + noWait?: boolean; }): Promise { - const { fileFullPath, workingDirectory, testName, configurationName } = args; + const { fileFullPath, workingDirectory, testName, configurationName, noWait } = args; const hasExplicitConfig = !!configurationName && configurationName.trim() !== '' && configurationName !== DebugConfigurationManager.getAutoLaunchConfigName(); @@ -90,6 +91,10 @@ export class DebuggingHandler implements IDebuggingHandler { } if (started) { + if (noWait) { + return `Debug session started (noWait=true) for: ${fileFullPath} using ${configDescription}. The session is running in the background.`; + } + // Race the readiness signal against the test run completion. For .NET // (and any runner where onDidTerminateDebugSession doesn't fire // reliably for parent/child sessions), the test-run-complete signal @@ -232,7 +237,7 @@ export class DebuggingHandler implements IDebuggingHandler { /** * Continue execution */ - public async handleContinue(): Promise { + public async handleContinue(args?: { noWait?: boolean }): Promise { try { if (!(await this.executor.hasActiveSession())) { throw new Error('Debug session is not ready. Please wait for initialization to complete.'); @@ -243,6 +248,10 @@ export class DebuggingHandler implements IDebuggingHandler { await this.executor.continue(); + if (args?.noWait) { + return "Execution continued (noWait=true). The program is running in the background."; + } + // Wait for debugger state to change const afterState = await this.waitForStateChange(beforeState); diff --git a/src/routingDebuggingHandler.ts b/src/routingDebuggingHandler.ts index 507fb44..c9859b0 100644 --- a/src/routingDebuggingHandler.ts +++ b/src/routingDebuggingHandler.ts @@ -168,6 +168,7 @@ export class RoutingDebuggingHandler implements IDebuggingHandler { workingDirectory: string; testName?: string; configurationName?: string; + noWait?: boolean; }): Promise { return this.forward('handleStartDebugging', args, args.workingDirectory || args.fileFullPath); } @@ -188,8 +189,8 @@ export class RoutingDebuggingHandler implements IDebuggingHandler { return this.forward('handleStepOut', {}); } - public handleContinue(): Promise { - return this.forward('handleContinue', {}); + public handleContinue(args?: { noWait?: boolean }): Promise { + return this.forward('handleContinue', args || {}); } public handlePause(): Promise { From d7a5b0ca268d0ff22e0f1bed14a363b4337d4214 Mon Sep 17 00:00:00 2001 From: ankita jha Date: Tue, 28 Jul 2026 01:38:01 -0700 Subject: [PATCH 2/3] Add get_debug_state tool to DebugMCP --- src/debugMCPServer.ts | 5 +++++ src/debuggingHandler.ts | 15 ++++++++++++++- src/routingDebuggingHandler.ts | 4 ++++ src/test/routing.test.ts | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/debugMCPServer.ts b/src/debugMCPServer.ts index 677b0ea..eaaf477 100644 --- a/src/debugMCPServer.ts +++ b/src/debugMCPServer.ts @@ -232,6 +232,11 @@ export class DebugMCPServer { description: 'Stop the current debug session', }, async () => this.runTool('stop_debugging', () => debuggingHandler.handleStopDebugging())); + // Get debug state tool + server.registerTool('get_debug_state', { + description: 'Get the current state of the debug session (running, paused, or inactive), including current breakpoint location and pause reason if paused.', + }, async () => this.runTool('get_debug_state', () => debuggingHandler.handleGetDebugState())); + // Step over tool server.registerTool('step_over', { description: 'Execute the current line of code without diving into it.', diff --git a/src/debuggingHandler.ts b/src/debuggingHandler.ts index c415f3d..4943808 100644 --- a/src/debuggingHandler.ts +++ b/src/debuggingHandler.ts @@ -25,6 +25,7 @@ export interface IDebuggingHandler { handleListBreakpoints(): Promise; handleGetVariables(args: { scope?: 'local' | 'global' | 'all' }): Promise; handleEvaluateExpression(args: { expression: string }): Promise; + handleGetDebugState(): Promise; } /** @@ -514,7 +515,19 @@ export class DebuggingHandler implements IDebuggingHandler { } /** - * Get current debug state + * Get current debug state as string + */ + public async handleGetDebugState(): Promise { + try { + const state = await this.executor.getCurrentDebugState(this.numNextLines); + return state.toString(); + } catch (error) { + throw new Error(`Error getting debug state: ${error}`); + } + } + + /** + * Get current debug state object */ public async getCurrentDebugState(): Promise { return await this.executor.getCurrentDebugState(this.numNextLines); diff --git a/src/routingDebuggingHandler.ts b/src/routingDebuggingHandler.ts index c9859b0..e9a3e64 100644 --- a/src/routingDebuggingHandler.ts +++ b/src/routingDebuggingHandler.ts @@ -228,4 +228,8 @@ export class RoutingDebuggingHandler implements IDebuggingHandler { public handleEvaluateExpression(args: { expression: string }): Promise { return this.forward('handleEvaluateExpression', args); } + + public handleGetDebugState(): Promise { + return this.forward('handleGetDebugState', {}); + } } diff --git a/src/test/routing.test.ts b/src/test/routing.test.ts index 2f34343..30402bc 100644 --- a/src/test/routing.test.ts +++ b/src/test/routing.test.ts @@ -34,6 +34,7 @@ class RecordingHandler implements IDebuggingHandler { handleListBreakpoints() { return this.record('listBp', {}); } handleGetVariables(args: any) { return this.record('vars', args); } handleEvaluateExpression(args: any) { return this.record('eval', args); } + handleGetDebugState() { return this.record('getDebugState', {}); } } suite('Multi-window routing', () => { From 7321462bc8e84d4e5dcbf9d71005be7d2a41a731 Mon Sep 17 00:00:00 2001 From: ankita jha Date: Tue, 28 Jul 2026 02:32:13 -0700 Subject: [PATCH 3/3] feat: add get_debug_state and route handler --- AGENTS.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 43 +++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 8 -------- src/controlServer.ts | 2 ++ 4 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md index 90f2bf3..b540dfa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -91,3 +91,47 @@ The `docs/` folder contains two types of documentation: | `agent-resources/troubleshooting/*.md` | Language-specific debugging tips (Python, JavaScript, Java, C#) | These resource files are loaded by `DebugMCPServer` and exposed as MCP resources that AI agents can read to learn how to use the debugging tools effectively. + + +# GitNexus — Code Intelligence + +This project is indexed by GitNexus as **DebugMCP** (1057 symbols, 2730 relationships, 82 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. + +> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. + +## Always Do + +- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user. +- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. +- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits. +- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. +- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`. + +## Never Do + +- NEVER edit a function, class, or method without first running `gitnexus_impact` on it. +- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis. +- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph. +- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope. + +## Resources + +| Resource | Use for | +|----------|---------| +| `gitnexus://repo/DebugMCP/context` | Codebase overview, check index freshness | +| `gitnexus://repo/DebugMCP/clusters` | All functional areas | +| `gitnexus://repo/DebugMCP/processes` | All execution flows | +| `gitnexus://repo/DebugMCP/process/{name}` | Step-by-step execution trace | + +## CLI + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` | +| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` | +| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` | + + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5d46c3a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,43 @@ + +# GitNexus — Code Intelligence + +This project is indexed by GitNexus as **DebugMCP** (1057 symbols, 2730 relationships, 82 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. + +> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. + +## Always Do + +- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user. +- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. +- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits. +- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. +- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`. + +## Never Do + +- NEVER edit a function, class, or method without first running `gitnexus_impact` on it. +- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis. +- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph. +- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope. + +## Resources + +| Resource | Use for | +|----------|---------| +| `gitnexus://repo/DebugMCP/context` | Codebase overview, check index freshness | +| `gitnexus://repo/DebugMCP/clusters` | All functional areas | +| `gitnexus://repo/DebugMCP/processes` | All execution flows | +| `gitnexus://repo/DebugMCP/process/{name}` | Step-by-step execution trace | + +## CLI + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` | +| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` | +| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` | + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0f89920..2c9fd4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -942,7 +942,6 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -1201,7 +1200,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2002,7 +2000,6 @@ "integrity": "sha512-20MV9SUdeN6Jd84xESsKhRly+/vxI+hwvpBMA93s+9dAcjdCuCojn4IqUGS3lvVaqjVYGYHSRMCpeFtF2rQYxQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -2233,7 +2230,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -2638,7 +2634,6 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.26.tgz", "integrity": "sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -4222,7 +4217,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -4298,7 +4292,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4614,7 +4607,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/controlServer.ts b/src/controlServer.ts index ec85e8c..284c211 100644 --- a/src/controlServer.ts +++ b/src/controlServer.ts @@ -109,6 +109,8 @@ export class ControlServer { return this.handler.handleGetVariables(args); case 'handleEvaluateExpression': return this.handler.handleEvaluateExpression(args); + case 'handleGetDebugState': + return this.handler.handleGetDebugState(); default: return Promise.reject(new Error(`Unknown control op: ${op}`)); }