Adding New Coding Agents to the Coding System
This guide provides step-by-step instructions for integrating new AI coding assistants into the agent-agnostic Coding system.
- Overview
- Architecture
- Quick Start (1 File)
- Agent Config Reference
- Optional Enhancements
- API Contract
- Testing
- Examples
The Coding system is designed to support multiple AI coding assistants through a config-driven architecture. All agents share common infrastructure:
- Tmux Session Wrapping - Unified status bar rendering via
tmux-session-wrapper.sh - Live Session Logging (LSL) - Automatic transcript monitoring
- Knowledge Management - VKB server and semantic analysis
- Constraint Monitoring - Real-time code quality enforcement
- Health Monitoring - System status and recovery
- Browser Automation - Playwright integration
- Session Continuity - Cross-session context
- Pipe-Pane Capture - Optional I/O capture for non-native agents (prompt detection, hook firing)
Adding a new agent requires only a single config file. No changes to shared code.
The system follows a layered architecture:
- Agent Layer - Your AI coding assistant (Claude, CoPilot, OpenCode, etc.)
- Tmux Wrapper Layer - Shared
tmux-session-wrapper.shwraps all agents in tmux with unified status bar - Config Layer - Agent definitions in
config/agents/<name>.sh - Orchestration Layer -
launch-agent-common.shhandles all shared startup (Docker, services, monitoring) - Common Setup Layer - Shared initialization (LSL, monitoring, gitignore)
- Shared Services Layer - VKB, Semantic Analysis, Constraints, LSL
- Adapter Layer - Abstract interface + agent implementations (loaded dynamically)
When a new agent is launched:
bin/codingvalidates config exists inconfig/agents/<name>.sh- Agent detection checks CLI availability via
AGENT_REQUIRES_COMMANDS - Shared orchestrator (
launch-agent-common.sh) sources the config - Docker mode detection (3-tier: marker file, container check, env var)
- Services start (Docker compose or native, based on mode)
- Agent-specific hooks run (
agent_check_requirements,agent_pre_launch) - Agent launches wrapped in tmux session with unified status bar
- Optional: pipe-pane I/O capture with
capture-monitor.js
All agents share identical Docker mode logic via launch-agent-common.sh:
- Transition lock checking (waits for mode transitions)
- 3-tier Docker mode detection
- Conditional Docker/native service startup
- Container reuse (health check before starting new containers)
- Docker MCP config generation
Create config/agents/myagent.sh:
#!/bin/bash
# Agent definition: My Agent
# Sourced by launch-agent-common.sh
AGENT_NAME="myagent"
AGENT_DISPLAY_NAME="MyAgent"
AGENT_COMMAND="myagent"
AGENT_SESSION_PREFIX="myagent"
AGENT_SESSION_VAR="MYAGENT_SESSION_ID"
AGENT_TRANSCRIPT_FMT="myagent"
AGENT_ENABLE_PIPE_CAPTURE=true
AGENT_PROMPT_REGEX='>\s+([^\n\r]+)[\n\r]'
AGENT_REQUIRES_COMMANDS="myagent"
# Verify agent CLI is available
agent_check_requirements() {
if ! command -v myagent &>/dev/null; then
_agent_log "Error: myagent CLI is not installed or not in PATH"
exit 1
fi
_agent_log "myagent CLI detected"
}That's it. Now test:
# Verify config is discovered
coding --agent myagent --dry-run
# Launch (if myagent binary is installed)
coding --agent myagentThe agent will automatically get:
- Docker mode detection and service startup
- Monitoring verification
- LSL transcript monitoring
- Tmux session with status bar
- Pipe-pane I/O capture (since
AGENT_ENABLE_PIPE_CAPTURE=true) - Session registration and cleanup
- All shared infrastructure
| Variable | Description | Example |
|---|---|---|
AGENT_NAME |
Internal identifier (used in env vars) | "myagent" |
AGENT_COMMAND |
Binary/script to exec inside tmux | "myagent" or "$CODING_REPO/bin/my-wrapper" |
| Variable | Default | Description |
|---|---|---|
AGENT_DISPLAY_NAME |
$AGENT_NAME |
Human-readable name for log messages |
AGENT_SESSION_PREFIX |
$AGENT_NAME |
Prefix for session ID ({prefix}-{PID}-{timestamp}) |
AGENT_SESSION_VAR |
(none) | Env var to export session ID as (e.g. CLAUDE_SESSION_ID) |
AGENT_TRANSCRIPT_FMT |
$AGENT_NAME |
Transcript format identifier |
AGENT_ENABLE_PIPE_CAPTURE |
false |
Enable tmux pipe-pane I/O capture |
AGENT_PROMPT_REGEX |
(none) | Regex for prompt detection (required if capture enabled) |
AGENT_REQUIRES_COMMANDS |
$AGENT_COMMAND |
Space-separated list of required CLI binaries |
Define these functions in your config to customize behavior:
# Called after services start, before agent launch
# Use to verify agent-specific dependencies
agent_check_requirements() {
# Return non-zero or exit 1 to block launch
}
# Called just before tmux wrapping
# Use to start agent-specific services, log info
agent_pre_launch() {
# Start HTTP adapter, set env vars, etc.
}
# Called on EXIT/INT/TERM (before PSM cleanup)
# Use to stop agent-specific background processes
agent_cleanup() {
# Kill background servers, etc.
}All hooks have access to _agent_log for logging, $CODING_REPO, $TARGET_PROJECT_DIR, $DOCKER_MODE, $SESSION_ID, and all other env vars set by the orchestrator.
If other components need to interact with your agent programmatically, create lib/agent-api/adapters/myagent-adapter.js:
import { BaseAdapter } from '../base-adapter.js';
class MyAgentAdapter extends BaseAdapter {
constructor(config = {}) {
super(config);
this.capabilities = ['memory', 'browser', 'logging'];
}
async initialize() { this.initialized = true; }
async cleanup() { /* cleanup resources */ }
// Implement methods from API Contract below
}
export default MyAgentAdapter;The adapter is loaded automatically by convention: lib/agent-api/adapters/${agentType}-adapter.js.
If you need to customize the sourcing order or add pre-config logic, create scripts/launch-myagent.sh:
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CODING_REPO="$(dirname "$SCRIPT_DIR")"
export CODING_REPO
source "$SCRIPT_DIR/agent-common-setup.sh"
source "$SCRIPT_DIR/launch-agent-common.sh"
launch_agent "$CODING_REPO/config/agents/myagent.sh" "$@"If no launch-myagent.sh exists, bin/coding automatically falls back to launch-generic.sh.
All adapters MUST implement these methods:
async initialize(): Promise<void>
async cleanup(): Promise<void>async executeCommand(command: string, args: string[]): Promise<any>async memoryCreate(entities: Entity[]): Promise<CreateResult>
async memoryCreateRelations(relations: Relation[]): Promise<CreateResult>
async memorySearch(query: string): Promise<Entity[]>
async memoryRead(): Promise<GraphData>
async memoryDelete(entityNames: string[]): Promise<DeleteResult>async browserNavigate(url: string): Promise<NavigationResult>
async browserAct(action: string, variables: Record<string, any>): Promise<ActionResult>
async browserExtract(): Promise<string>
async browserScreenshot(options?: ScreenshotOptions): Promise<Buffer>async logConversation(data: ConversationEntry): Promise<LogResult>
async readConversationHistory(options?: HistoryOptions): Promise<ConversationEntry[]>hasCapability(capability: string): boolean
getCapabilities(): string[]
isInitialized(): booleaninterface Entity {
name: string;
entityType: string;
observations: string[];
significance?: number;
created?: string;
lastUpdated?: string;
metadata?: Record<string, any>;
}
interface Relation {
from: string;
to: string;
relationType: string;
created?: string;
metadata?: Record<string, any>;
}
interface GraphData {
nodes: Entity[];
edges: Relation[];
metadata?: { nodeCount: number; edgeCount: number; lastAccessed: string; };
}
interface CreateResult { success: boolean; created?: number; updated?: number; errors?: string[]; }
interface DeleteResult { success: boolean; deleted: number; notFound: number; }
interface NavigationResult { success: boolean; url?: string; error?: string; }
interface ActionResult { success: boolean; result?: any; error?: string; }
interface ConversationEntry { timestamp: string; type: string; content: any; metadata?: Record<string, any>; }
interface LogResult { success: boolean; logFile?: string; error?: string; }
interface HistoryOptions { limit?: number; startDate?: Date; endDate?: Date; type?: string; }- Config:
config/agents/<name>.shexists with required variables - Dry-run:
coding --agent <name> --dry-runsucceeds - Detection: Agent detected by
AgentDetector(check withcoding --help) - Launch: Agent starts via
coding --agent <name> - Tmux: Agent launches inside tmux session with status bar
- LSL: Transcripts monitored and classified
- Monitoring: StatusLine health monitoring active
- Cleanup: Graceful shutdown works (
agent_cleanupcalled) - E2E:
tests/integration/launcher-e2e.shpasses
# Dry-run (safe, no actual launch)
coding --agent myagent --dry-run
# Check detection
coding --help
# Launch agent
coding --agent myagent
# Check LSL status
coding --lsl-status
# Run full E2E test suite
./tests/integration/launcher-e2e.shAGENT_COMMAND="$CODING_REPO/bin/claude-mcp"— launches via MCP wrapperAGENT_ENABLE_PIPE_CAPTURE=false— Claude has native transcript supportagent_check_requirements()— checks MCP sync statusagent_pre_launch()— logs Docker/native mode info
AGENT_COMMAND="copilot"— launches copilot CLI directlyAGENT_ENABLE_PIPE_CAPTURE=true— captures I/O via tmux pipe-paneAGENT_PROMPT_REGEX— detects submitted prompts via❯markeragent_pre_launch()— starts HTTP adapter serveragent_cleanup()— stops HTTP adapter on exit
- 25-line config file, zero shared code changes
- Demonstrates the minimum integration:
AGENT_NAME+AGENT_COMMAND+agent_check_requirements()
- Does
config/agents/<name>.shexist? - Is
AGENT_REQUIRES_COMMANDSset to the correct binary name? - Is the binary in PATH? (
which <binary>)
- Check
config/agents/<name>.shdefinesAGENT_NAMEandAGENT_COMMAND - Check for syntax errors:
bash -n config/agents/<name>.sh
- Are required ports available (8080, etc.)?
- Is Docker running (for Docker mode)?
- Does
start-services.shcomplete successfully?
- Is
AGENT_ENABLE_PIPE_CAPTURE=trueset in config? - Is
AGENT_PROMPT_REGEXa valid regex? - Check capture file exists:
ls $CODING_REPO/.logs/capture/ - Check capture-monitor.js logs


