// Comprehensive guide for AIDB debug adapter development. Covers component-based
| name | adapter-development |
| description | Comprehensive guide for AIDB debug adapter development. Covers component-based architecture, language-specific patterns (Python/debugpy, JavaScript/vscode-js-debug, Java/java-debug), lifecycle hooks, process management, port management, launch orchestration, resource cleanup, child sessions, and common pitfalls. Essential for developing or maintaining AIDB debug adapters. |
AIDB debug adapters provide language-agnostic debugging capabilities through the Debug Adapter Protocol (DAP). The architecture is component-based - adapters delegate to specialized components rather than implementing everything in monolithic classes.
For comprehensive architecture details, see docs/developer-guide/overview.md for the complete system architecture and data flow diagrams.
DebugAdapter (base class)
├── ProcessManager - Process lifecycle (launch, monitor, stop, cleanup)
├── PortManager - Port acquisition, verification, release
├── LaunchOrchestrator - Launch sequence coordination
├── TargetResolver - Target type detection and normalization
├── SourcePathResolver - Source path resolution for remote debugging
└── AdapterHooksMixin - Lifecycle hooks for extension points
This skill is organized into focused resource files for language-specific patterns:
When working on adapter development, you may also need:
from aidb.adapters.base import DebugAdapter
from aidb.adapters.base.config import AdapterConfig
class MyLanguageAdapter(DebugAdapter):
"""My language debug adapter using component architecture."""
def __init__(self, session, ctx=None, config=None, **kwargs):
if config is None:
config = MyLanguageConfig()
super().__init__(
session=session,
ctx=ctx,
config=config,
**kwargs
)
# Register language-specific hooks
self._register_my_language_hooks()
def _register_my_language_hooks(self):
"""Register language-specific lifecycle hooks."""
self.register_hook(
LifecycleHook.PRE_LAUNCH,
self._validate_environment,
priority=90 # High priority = runs first
)
async def _validate_environment(self, context: HookContext):
"""Pre-launch hook to validate environment."""
# Validation logic here
pass
async def _build_launch_command(self, target, adapter_host,
adapter_port, args=None):
"""Build the command to launch the debug adapter."""
return [
"/path/to/debug/adapter",
"--host", adapter_host,
"--port", str(adapter_port),
target
]
def _add_adapter_specific_vars(self, env: dict) -> dict:
"""Add language-specific environment variables."""
env["MY_LANG_DEBUG"] = "1"
return env
def _create_target_resolver(self) -> "TargetResolver":
"""Create language-specific target resolver."""
return MyLanguageTargetResolver(adapter=self, ctx=self.ctx)
def _create_source_path_resolver(self) -> "SourcePathResolver":
"""Create language-specific source path resolver for remote debugging."""
return MyLanguageSourcePathResolver(adapter=self, ctx=self.ctx)
def _get_process_name_pattern(self) -> str:
"""Get process name pattern for cleanup."""
return "my-language-debug"
See ProcessManager in src/aidb/adapters/base/components/process_manager.py for full implementation.
from aidb.resources.process_tags import ProcessType
# Launch subprocess with session tagging
proc = await self._process_manager.launch_subprocess(
cmd=cmd,
env=env,
session_id=self.session.id,
language=self.config.language,
process_type=ProcessType.ADAPTER, # String constant: "adapter", "debuggee", or "lsp_server"
kwargs={}
)
# Wait for adapter readiness
await self._process_manager.wait_for_adapter_ready(port, start_time)
# Get process status
if self._process_manager.is_alive:
pid = self._process_manager.pid
See PortManager in src/aidb/adapters/base/components/port_manager.py for full implementation. Key methods:
acquire(requested_port=None) - Acquire and verify port availabilityrelease() - Release the portport property - Get current portSee LaunchOrchestrator in src/aidb/adapters/base/components/launch_orchestrator.py for full implementation. Key methods:
launch(target, port, args) - Simple launch with target filelaunch_with_config(launch_config, port, workspace_root) - Launch with VS Code launch.json configurationSee SourcePathResolver in src/aidb/adapters/base/source_path_resolver.py for base implementation. Each language adapter implements its own resolver:
src/aidb/adapters/lang/python/source_path_resolver.py - Handles site-packages, venv, egg pathssrc/aidb/adapters/lang/javascript/source_path_resolver.py - Handles node_modules, webpack pathssrc/aidb/adapters/lang/java/source_path_resolver.py - Handles JAR notation, Maven layoutsKey methods:
extract_relative_path(file_path) - Extract language-specific relative path from adapter-returned pathresolve(file_path, source_paths) - Resolve remote path to local source fileHooks execute in priority order (lower number = runs first):
# Pre-launch validation
self.register_hook(
LifecycleHook.PRE_LAUNCH,
self._validate_environment,
priority=90
)
# Post-launch setup
self.register_hook(
LifecycleHook.POST_LAUNCH,
self._wait_for_ready,
priority=20
)
# Post-stop cleanup
self.register_hook(
LifecycleHook.POST_STOP,
self._cleanup_resources,
priority=10
)
Before implementing new functionality, check these shared resources:
aidb/adapters/base/adapter.py) - Base adapter with component architectureaidb/adapters/base/config.py) - Configuration base classaidb/adapters/base/launch.py) - VS Code launch.json supportaidb/adapters/base/components/process_manager.py)aidb/adapters/base/components/port_manager.py)aidb/adapters/base/components/launch_orchestrator.py)aidb/adapters/utils/binary_locator.py) - Find adapter binariesaidb/adapters/utils/output_capture.py) - Capture stdout/stderraidb/adapters/utils/trace_log.py) - Trace log managementaidb_common/)See source code docstrings in src/aidb_common/ for detailed API documentation.
aidb_common/patterns/) - Base class with context supportaidb_common/path.py) - Path normalizationaidb_common/config/) - Environment variable readingaidb_common/constants.py) - Supported language constantsAdapters can support VS Code launch.json configurations:
def get_launch_configuration(self) -> dict[str, Any]:
"""Get launch configuration for DAP Launch request."""
config = {
"type": "mylang",
"request": "launch",
"name": "Debug MyLang",
"program": self._target_file,
"args": self._target_args,
"cwd": self._target_cwd,
"env": self._target_env,
}
return config
See src/aidb_common/env.py for environment handling utilities.
Use the template method pattern for environment preparation:
def _prepare_environment(self) -> dict[str, str]:
"""Prepare environment (base implementation)."""
env = self._load_base_environment()
env = self._add_trace_configuration(env)
return self._add_adapter_specific_vars(env)
def _add_adapter_specific_vars(self, env: dict) -> dict:
"""Add language-specific variables (override this)."""
env["MY_LANG_HOME"] = "/path/to/lang"
return env
Always implement proper cleanup in hooks:
async def stop(self) -> None:
"""Stop the debug adapter and clean up."""
context = await self.execute_hook(LifecycleHook.PRE_STOP)
if not context.cancelled:
# Stop process manager
await self._process_manager.stop()
# Release port
if self.port:
self._port_manager.release()
# Cleanup auxiliary components
if self._trace_manager:
self._trace_manager.cleanup()
await self.execute_hook(LifecycleHook.POST_STOP)
All AIDB-spawned processes are tagged with environment variables for safe cleanup:
from aidb.resources.process_tags import ProcessType
# Automatically handled by ProcessManager
proc = await self.process_manager.launch_subprocess(
cmd=cmd,
env=env,
session_id=self.session.id, # Tags process with session ID
language=self.config.language, # Tags with language
process_type=ProcessType.ADAPTER, # String constant: "adapter", "debuggee", or "lsp_server"
kwargs={}
)
Tags enable:
The authoritative DAP protocol reference is in src/aidb/dap/protocol.py - fully typed and always up-to-date.
from aidb.dap.protocol.types import (
Capabilities,
InitializeRequest,
LaunchRequest,
SetBreakpointsRequest,
)
Use the shared test infrastructure:
# Test with API directly
session = await client.start_session(
target="/path/to/file",
language="mylang",
breakpoints=[{"line": 10}]
)
# Test with launch.json
session = await client.start_session(
target="/path/to/file",
language="mylang",
launch_config_name="Debug MyLang",
workspace_root="/path/to/project"
)
For detailed language-specific patterns and examples:
resources/python-adapter-patterns.md
resources/javascript-adapter-patterns.md
resources/java-adapter-patterns.md
All file paths mentioned in this skill are relative to the repo root:
src/aidb/adapters/base/adapter.pysrc/aidb/adapters/base/components/src/aidb/adapters/base/source_path_resolver.pysrc/aidb/adapters/lang/python/python.pysrc/aidb/adapters/lang/python/source_path_resolver.pysrc/aidb/adapters/lang/javascript/javascript.pysrc/aidb/adapters/lang/javascript/source_path_resolver.pysrc/aidb/adapters/lang/java/java.pysrc/aidb/adapters/lang/java/source_path_resolver.pysrc/aidb/dap/protocol.py