Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

coding-agent-adapters

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

coding-agent-adapters

CLI adapters for AI coding agents - Claude Code, Gemini CLI, OpenAI Codex, Aider, and Hermes Agent

latest
Source
npmnpm
Version
0.16.4
Version published
Weekly downloads
7.1K
21.77%
Maintainers
1
Weekly downloads
 
Created
Source

coding-agent-adapters

CLI adapters for AI coding agents. Works with pty-manager to spawn and manage coding agents like Claude Code, Gemini CLI, OpenAI Codex, Aider, and Hermes Agent.

Each adapter provides source-derived detection patterns for the full session lifecycle: login/auth, blocking prompts, ready state, exit conditions, and auto-response rules — all based on deep analysis of each CLI's open-source codebase.

Installation

npm install coding-agent-adapters pty-manager

Quick Start

import { PTYManager, AdapterRegistry } from 'pty-manager';
import { ClaudeAdapter, GeminiAdapter, AiderAdapter, HermesAdapter } from 'coding-agent-adapters';

// Create adapter registry and register the adapters you need
const registry = new AdapterRegistry();
registry.register(new ClaudeAdapter());
registry.register(new GeminiAdapter());
registry.register(new AiderAdapter());
registry.register(new HermesAdapter());

// Create PTY manager with the registry
const manager = new PTYManager({ adapters: registry });

// Spawn a Claude Code session
const session = await manager.spawn({
  name: 'code-assistant',
  type: 'claude',
  workdir: '/path/to/project',
  adapterConfig: {
    anthropicKey: process.env.ANTHROPIC_API_KEY,
  },
});

// Listen for output
session.on('output', (data) => console.log(data));

// Send a task
session.send('Help me refactor this function to use async/await');

Available Adapters

AdapterCLITypeInput StyleAuto-Response RulesReady Settle
ClaudeAdapterClaude CodeclaudeTUI menus5 rules500ms
GeminiAdapterGemini CLIgeminiTUI menus3 rules300ms
CodexAdapterOpenAI CodexcodexTUI menus6 rules300ms
AiderAdapterAideraiderText (Y)es/(N)o17 rules200ms
HermesAdapterHermes AgenthermesTUI prompts0 rules400ms

Session Lifecycle Detection

Each adapter implements detection for every stage of a CLI session.

All detection methods use stripAnsi() which strips ANSI escape sequences, cursor positioning codes, bare control characters, TUI spinner/box-drawing characters, and collapses whitespace — ensuring regex patterns match through raw terminal output. Prompt indicators (, , ) are preserved.

Login / Auth Detection

Adapters detect various auth states and methods:

const adapter = new GeminiAdapter();
const login = adapter.detectLogin(output);
// { required: true, type: 'browser', url: 'https://...', instructions: '...' }
AdapterAuth TypesSource Files
ClaudeAPI key, OAuth browserCLI runtime
GeminiGoogle OAuth, API key entry, auth in-progress (ignores "Both keys set" success messages)AuthDialog.tsx, ApiAuthDialog.tsx, AuthInProgress.tsx
CodexDevice code flow, onboarding auth menuauth.rs, headless_chatgpt_login.rs
AiderAPI key missing/invalid, OpenRouter OAuthonboarding.py, models.py
HermesFirst-run setup gate (no API keys/providers found, Run setup now?)hermes_cli/setup.py, hermes_cli/main.py

Ready State Detection

Each adapter knows exactly what "ready for input" looks like:

AdapterReady IndicatorsSource
Claude$ promptCLI runtime
GeminiPrompt glyphs (>, !, *, (r:)), composer placeholderInputPrompt.tsx, Composer.tsx
Codex> glyph, placeholder suggestionschat_composer.rs
Aiderask>, code>, architect>, help>, multi>, startup bannerio.py, base_coder.py
HermesIdle prompt when not working; completion response boxcli.py, agent/display.py

Ready Settle Delay

Each adapter sets readySettleMs to control how long pty-manager waits after detectReady() matches before emitting session_ready. This prevents the orchestrator from sending input while the TUI is still rendering (status bar, shortcuts, update notices). The base default is 300ms; adapters override based on their rendering weight.

AdapterreadySettleMsRationale
Claude Code500msHeaviest TUI — status bar, shortcuts, update notices, /ide suggestions
Gemini CLI300msModerate Ink TUI (inherits base default)
Codex300msModerate Rust TUI (inherits base default)
Aider200msMinimal TUI, mostly text output
Hermes Agent400msPrompt-toolkit UI with spinner + activity feed that settles after render

Blocking Prompt Detection

Adapters detect prompts that block the session and require user action:

AdapterDetected Prompts
ClaudePermission requests, update notices
GeminiFolder trust, tool execution, validation dialogs, privacy consent
CodexDirectory trust, tool approval, update available, model migration, CWD selection
AiderFile operations, shell commands, git init, pip install, destructive operations
HermesClarify prompts, sudo password prompts, dangerous command approval choices

Loading / Active-Work Detection

Each adapter implements detectLoading(output) to detect when the CLI is actively processing — thinking spinners, file reading, model streaming. When loading is detected, pty-manager suppresses stall detection entirely, avoiding unnecessary LLM classifier calls during normal operation.

AdapterLoading IndicatorsSource Patterns
Claudeesc to interrupt, Reading N filesclaude_active_reading_files
Geminiesc to cancel, Waiting for user confirmationgemini_active_loading_line
Codexesc to interrupt, Booting MCP server, Searching the webcodex_active_status_row, codex_active_booting_mcp
AiderWaiting for LLM/<model>, Generating commit message withaider_active_waiting_model, aider_active_waiting_llm_default
HermesThinking spinner verb + elapsed time (deliberating... (2.4s)), working prompt (⚕ ❯)HermesAdapter.detectLoading()
const claude = new ClaudeAdapter();
claude.detectLoading('• Working (5s • esc to interrupt)');  // true
claude.detectLoading('Reading 42 files…');                   // true
claude.detectLoading('❯ ');                                  // false

const aider = new AiderAdapter();
aider.detectLoading('Waiting for claude-sonnet-4-20250514'); // true
aider.detectLoading('code> ');                                // false

Task Completion Detection

Each adapter implements detectTaskComplete(output) to recognize when the CLI has finished a task and returned to its idle prompt. This is more specific than detectReady() — it matches high-confidence completion indicators (duration summaries, explicit done messages) that short-circuit the LLM stall classifier in pty-manager. Patterns match through raw ANSI-laden TUI output including spinner characters and cursor positioning codes.

AdapterCompletion IndicatorsSource Patterns
ClaudeTurn duration (Cooked for 3m 12s, custom verb) + prompt (tolerates trailing status bar)claude_completed_turn_duration
Gemini◇ Ready window title, Type your message composergemini_ready_title
CodexWorked for 1m 05s separator + promptcodex_completed_worked_for_separator, codex_ready_prompt
AiderAider is waiting for your input, mode prompts (including plain >) with edit/cost markersaider_completed_llm_response_ready
HermesFinal response box (╭─ ⚕ Hermes ... ╰), or tool-feed line + idle promptHermesAdapter.detectTaskComplete()
const claude = new ClaudeAdapter();
claude.detectTaskComplete('Cooked for 3m 12s\n❯ ');  // true
claude.detectTaskComplete('Reading 5 files…');         // false

const aider = new AiderAdapter();
aider.detectTaskComplete('Applied edit to main.ts\nTokens: 1234\ncode> ');  // true
aider.detectTaskComplete('Waiting for claude-sonnet-4-20250514');            // false

Exit Detection

Adapters detect when a CLI session has ended:

AdapterExit Conditions
ClaudeBase exit detection
GeminiFolder trust rejection, logout confirmation
CodexSession end, update completion
AiderCtrl+C / KeyboardInterrupt, version update requiring restart
HermesGoodbye! ⚕

Auto-Response Rules

Adapters include pre-configured rules to automatically handle known prompts. Rules use two response modes depending on the CLI's input style.

TUI Menu CLIs (Gemini, Codex, Claude, Hermes)

These CLIs use arrow-key menus rendered with Ink/Ratatui. Rules send key sequences:

const codex = new CodexAdapter();
codex.autoResponseRules;
// [
//   { pattern: /update.?available/i, responseType: 'keys', keys: ['down', 'enter'], once: true, ... },
//   { pattern: /trust.?this.?directory/i, responseType: 'keys', keys: ['enter'], once: true, ... },
//   { pattern: /model.?migration/i, responseType: 'keys', keys: ['enter'], once: true, ... },
//   ...
// ]

Text Prompt CLIs (Aider)

Aider uses plain text (Y)es/(N)o prompts via io.py. Rules send typed text:

const aider = new AiderAdapter();
aider.autoResponseRules;
// [
//   { pattern: /allow collection of anonymous analytics/i, response: 'n', responseType: 'text', once: true, ... },
//   { pattern: /add .+ to the chat\?/i, response: 'y', responseType: 'text', ... },
//   { pattern: /create new file\?/i, response: 'y', responseType: 'text', ... },
//   { pattern: /run shell commands?\?/i, response: 'y', responseType: 'text', ... },
//   ...17 rules total
// ]

The usesTuiMenus Flag

Adapters declare their input style via usesTuiMenus. This affects how auto-response rules with no explicit responseType are delivered:

  • usesTuiMenus: true (Gemini, Codex, Claude, Hermes) — defaults to sendKeys('enter')
  • usesTuiMenus: false (Aider) — defaults to writeRaw(response + '\r')

Model Recommendations

Each adapter provides model recommendations based on available credentials:

const aider = new AiderAdapter();

aider.getRecommendedModels({ anthropicKey: 'sk-ant-...' });
// { powerful: 'anthropic/claude-sonnet-4-20250514', fast: 'anthropic/claude-haiku-4-5-20251001' }

aider.getRecommendedModels({ googleKey: 'AIza...' });
// { powerful: 'gemini/gemini-3-pro', fast: 'gemini/gemini-3-flash' }

Workspace Files & Memory

Each coding agent CLI has its own convention for project-level memory files (instructions the agent reads on startup) and config files. Adapters expose this knowledge so orchestration systems can write context to the correct files before spawning an agent.

Discovering Workspace Files

import { ClaudeAdapter, AiderAdapter } from 'coding-agent-adapters';

const claude = new ClaudeAdapter();
claude.getWorkspaceFiles();
// [
//   { relativePath: 'CLAUDE.md', type: 'memory', autoLoaded: true, format: 'markdown', ... },
//   { relativePath: '.claude/settings.json', type: 'config', autoLoaded: true, format: 'json', ... },
//   { relativePath: '.claude/commands', type: 'config', autoLoaded: false, format: 'markdown', ... },
// ]

claude.memoryFilePath; // 'CLAUDE.md'

const aider = new AiderAdapter();
aider.memoryFilePath; // '.aider.conventions.md'

Per-Adapter File Mappings

AdapterMemory FileConfigOther
ClaudeCLAUDE.md.claude/settings.json.claude/commands
GeminiGEMINI.md.gemini/settings.json.gemini/styles
CodexAGENTS.md.codex/config.jsoncodex.md
Aider.aider.conventions.md.aider.conf.yml.aiderignore
HermesAGENTS.mdcli-config.yamlSOUL.md

Writing Memory Files

Use writeMemoryFile() to write instructions into a workspace before spawning an agent. Parent directories are created automatically.

const adapter = new ClaudeAdapter();

// Write to the adapter's default memory file (CLAUDE.md)
await adapter.writeMemoryFile('/path/to/workspace', `# Project Context
This is a TypeScript monorepo using pnpm workspaces.
Always run tests before committing.
`);

// Append to an existing memory file
await adapter.writeMemoryFile('/path/to/workspace', '\n## Additional Rules\nUse snake_case.\n', {
  append: true,
});

// Write to a custom file (e.g., template-specific context for sub-agents)
await adapter.writeMemoryFile('/path/to/workspace', '# Task-Specific Context\n...', {
  fileName: 'TASK_CONTEXT.md',
});

Approval Presets

Each coding agent CLI has its own config format for controlling tool permissions. The approval preset system provides 4 named levels that translate to the correct per-CLI config files and CLI flags.

Preset Levels

PresetDescriptionAuto-approveRequire approvalBlocked
readonlyRead-only. Safe for auditing.file_read, planning, user_interactionfile_write, shell, web, agent
standardStandard dev. Reads + web auto, writes/shell prompt.file_read, planning, user_interaction, webfile_write, shell, agent
permissiveFile ops auto-approved, shell still prompts.file_read, file_write, planning, user_interaction, web, agentshell
autonomousEverything auto-approved. Use with sandbox.all categories

Generating Configs

import { generateApprovalConfig, listPresets, getPresetDefinition } from 'coding-agent-adapters';

// List all available presets
const presets = listPresets();

// Generate CLI-specific config for a preset
const config = generateApprovalConfig('claude', 'permissive');
// {
//   preset: 'permissive',
//   cliFlags: [],
//   workspaceFiles: [{ relativePath: '.claude/settings.json', content: '...', format: 'json' }],
//   envVars: {},
//   summary: 'Claude Code: File ops auto-approved, shell still prompts.',
// }

// Each CLI gets its own config format
generateApprovalConfig('gemini', 'readonly');   // → .gemini/settings.json + --approval-mode plan
generateApprovalConfig('codex', 'autonomous');  // → .codex/config.json + --full-auto
generateApprovalConfig('aider', 'permissive');  // → .aider.conf.yml + --yes-always

Using Presets with Adapters

When approvalPreset is set in adapterConfig, the adapter's getArgs() automatically appends the correct CLI flags:

const session = await manager.spawn({
  name: 'sandboxed-agent',
  type: 'claude',
  workdir: '/path/to/project',
  adapterConfig: {
    anthropicKey: process.env.ANTHROPIC_API_KEY,
    approvalPreset: 'autonomous',
  },
});

You can also write the config files to a workspace manually using writeApprovalConfig():

const adapter = new ClaudeAdapter();
const writtenFiles = await adapter.writeApprovalConfig('/path/to/workspace', {
  adapterConfig: { approvalPreset: 'permissive' },
});
// writtenFiles: ['/path/to/workspace/.claude/settings.json']

Per-CLI Output

CLIConfig FileKey Controls
Claude Code.claude/settings.jsonpermissions.allow, permissions.deny, sandbox.*
Gemini CLI.gemini/settings.jsongeneral.defaultApprovalMode, tools.allowed, tools.exclude
Codex.codex/config.jsonapproval_policy, sandbox_mode, tools.web_search
Aider.aider.conf.ymlyes-always, no-auto-commits
Hermes Agentnone (CLI-managed)approvals currently handled in-session by Hermes safety prompts

Preflight Check

Before spawning agents, check if the required CLIs are installed:

import { checkAdapters, checkAllAdapters, printMissingAdapters } from 'coding-agent-adapters';

// Check specific adapters
const results = await checkAdapters(['claude', 'aider']);
for (const result of results) {
  if (result.installed) {
    console.log(`${result.adapter} v${result.version}`);
  } else {
    console.log(`${result.adapter} - Install: ${result.installCommand}`);
  }
}

// Check all adapters
const allResults = await checkAllAdapters();

// Print formatted installation instructions for missing tools
await printMissingAdapters(['claude', 'gemini']);

Passing Credentials

You can pass API keys either via environment variables or through adapterConfig:

// Via environment variables (recommended for production)
process.env.ANTHROPIC_API_KEY = 'sk-ant-...';

const session = await manager.spawn({
  name: 'claude-agent',
  type: 'claude',
  workdir: '/project',
});

// Via adapterConfig (useful for multi-tenant scenarios)
const session = await manager.spawn({
  name: 'claude-agent',
  type: 'claude',
  workdir: '/project',
  adapterConfig: {
    anthropicKey: 'sk-ant-...',
    openaiKey: 'sk-...',
    googleKey: 'AIza...',
  },
});

Claude Hook Telemetry (Optional)

Claude Code hooks can emit deterministic lifecycle markers so PTY orchestration does not rely only on screen-text heuristics.

ClaudeAdapter supports this as an opt-in mode:

const session = await manager.spawn({
  name: 'claude-agent',
  type: 'claude',
  workdir: '/project',
  adapterConfig: {
    anthropicKey: process.env.ANTHROPIC_API_KEY,
    claudeHookTelemetry: true,
  },
});

When enabled, the adapter sets:

  • PARALLAX_CLAUDE_HOOK_TELEMETRY=1
  • PARALLAX_CLAUDE_HOOK_MARKER_PREFIX=PARALLAX_CLAUDE_HOOK (or your override)

Marker Protocol

Hook scripts should print single-line markers:

PARALLAX_CLAUDE_HOOK {"event":"Notification","notification_type":"permission_prompt","message":"..."}

Supported high-value events:

  • Notification (permission_prompt, elicitation_dialog, idle_prompt)
  • PreToolUse (includes tool_name)
  • TaskCompleted
  • SessionEnd

The adapter consumes these markers in:

  • detectBlockingPrompt()
  • detectLoading()
  • detectToolRunning()
  • detectTaskComplete()
  • detectReady()
  • detectExit()

Generate Hook Config Template

const claude = new ClaudeAdapter();
const protocol = claude.getHookTelemetryProtocol();

// protocol.scriptPath
// protocol.scriptContent
// protocol.settingsHooks

settingsHooks is ready to merge into .claude/settings.json under hooks.

Gemini Hook Telemetry (Optional)

Gemini CLI hooks can emit deterministic lifecycle markers so PTY orchestration does not rely only on screen-text heuristics.

GeminiAdapter supports this as an opt-in mode:

const session = await manager.spawn({
  name: 'gemini-agent',
  type: 'gemini',
  workdir: '/project',
  adapterConfig: {
    googleKey: process.env.GEMINI_API_KEY,
    geminiHookTelemetry: true,
  },
});

When enabled, the adapter sets:

  • PARALLAX_GEMINI_HOOK_TELEMETRY=1
  • PARALLAX_GEMINI_HOOK_MARKER_PREFIX=PARALLAX_GEMINI_HOOK (or your override)

Marker Protocol

Gemini hook command output must be valid JSON. The helper script emits marker lines via systemMessage:

PARALLAX_GEMINI_HOOK {"event":"BeforeTool","tool_name":"run_shell_command"}

Supported high-value events:

  • Notification (ToolPermission)
  • BeforeTool (includes tool_name)
  • AfterAgent
  • SessionEnd

The adapter consumes these markers in:

  • detectBlockingPrompt()
  • detectLoading()
  • detectToolRunning()
  • detectTaskComplete()
  • detectReady()
  • detectExit()

Generate Hook Config Template

const gemini = new GeminiAdapter();
const protocol = gemini.getHookTelemetryProtocol();

// protocol.scriptPath
// protocol.scriptContent
// protocol.settingsHooks

settingsHooks is ready to merge into .gemini/settings.json under hooks.

Creating Custom Adapters

Extend BaseCodingAdapter to create adapters for other coding CLIs:

import { BaseCodingAdapter } from 'coding-agent-adapters';
import type { AgentFileDescriptor, InstallationInfo, ModelRecommendations } from 'coding-agent-adapters';
import type { SpawnConfig, ParsedOutput, LoginDetection, AutoResponseRule } from 'pty-manager';

export class CursorAdapter extends BaseCodingAdapter {
  readonly adapterType = 'cursor';
  readonly displayName = 'Cursor';

  readonly installation: InstallationInfo = {
    command: 'npm install -g cursor-cli',
    docsUrl: 'https://cursor.sh/docs',
  };

  // Set to false if the CLI uses text prompts instead of TUI menus
  override readonly usesTuiMenus = false;

  // Tune ready settle delay for this CLI's rendering speed (base default: 300ms)
  override readonly readySettleMs = 250;

  readonly autoResponseRules: AutoResponseRule[] = [
    { pattern: /accept terms/i, type: 'tos', response: 'y', responseType: 'text', description: 'Accept TOS', safe: true, once: true },
  ];

  getWorkspaceFiles(): AgentFileDescriptor[] {
    return [
      { relativePath: '.cursor/rules', description: 'Project rules', autoLoaded: true, type: 'memory', format: 'markdown' },
    ];
  }

  getRecommendedModels(): ModelRecommendations {
    return { powerful: 'claude-sonnet-4', fast: 'gpt-4o-mini' };
  }

  getCommand(): string { return 'cursor'; }
  getArgs(config: SpawnConfig): string[] { return ['--cli']; }
  getEnv(config: SpawnConfig): Record<string, string> { return {}; }

  detectLogin(output: string): LoginDetection {
    if (/login required/i.test(output)) {
      return { required: true, type: 'browser' };
    }
    return { required: false };
  }

  detectReady(output: string): boolean {
    return /cursor>\s*$/m.test(output);
  }

  detectLoading(output: string): boolean {
    // Active loading indicator — suppresses stall detection
    return /processing|thinking/i.test(output);
  }

  detectTaskComplete(output: string): boolean {
    // High-confidence: task summary + idle prompt
    return /completed in \d+s/.test(output) && /cursor>\s*$/m.test(output);
  }

  parseOutput(output: string): ParsedOutput | null {
    return { type: 'response', content: output.trim(), isComplete: true, isQuestion: output.includes('?') };
  }

  getPromptPattern(): RegExp { return /cursor>\s*$/; }
}

Convenience Functions

import { createAllAdapters, createAdapter, ADAPTER_TYPES } from 'coding-agent-adapters';

// Create all adapters at once
const allAdapters = createAllAdapters();

// Create a specific adapter
const claude = createAdapter('claude');

// Available adapter types
console.log(Object.keys(ADAPTER_TYPES)); // ['claude', 'gemini', 'codex', 'aider', 'hermes']

License

MIT

Keywords

cli

FAQs

Package last updated on 26 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts