coding-agent-adapters 0.2.13 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  CLI adapters for AI coding agents. Works with [pty-manager](https://www.npmjs.com/package/pty-manager) to spawn and manage coding agents like Claude Code, Gemini CLI, OpenAI Codex, and Aider.
4
4
 
5
+ 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.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -42,12 +44,122 @@ session.send('Help me refactor this function to use async/await');
42
44
 
43
45
  ## Available Adapters
44
46
 
45
- | Adapter | CLI | Type | Install Command |
46
- |---------|-----|------|-----------------|
47
- | `ClaudeAdapter` | Claude Code | `claude` | `npm install -g @anthropic-ai/claude-code` |
48
- | `GeminiAdapter` | Gemini CLI | `gemini` | `npm install -g @anthropics/gemini-cli` |
49
- | `CodexAdapter` | OpenAI Codex | `codex` | `npm install -g @openai/codex` |
50
- | `AiderAdapter` | Aider | `aider` | `pip install aider-chat` |
47
+ | Adapter | CLI | Type | Input Style | Auto-Response Rules |
48
+ |---------|-----|------|-------------|---------------------|
49
+ | `ClaudeAdapter` | Claude Code | `claude` | TUI menus | 5 rules |
50
+ | `GeminiAdapter` | Gemini CLI | `gemini` | TUI menus | 3 rules |
51
+ | `CodexAdapter` | OpenAI Codex | `codex` | TUI menus | 6 rules |
52
+ | `AiderAdapter` | Aider | `aider` | Text `(Y)es/(N)o` | 17 rules |
53
+
54
+ ## Session Lifecycle Detection
55
+
56
+ Each adapter implements detection for every stage of a CLI session:
57
+
58
+ ### Login / Auth Detection
59
+
60
+ Adapters detect various auth states and methods:
61
+
62
+ ```typescript
63
+ const adapter = new GeminiAdapter();
64
+ const login = adapter.detectLogin(output);
65
+ // { required: true, type: 'browser', url: 'https://...', instructions: '...' }
66
+ ```
67
+
68
+ | Adapter | Auth Types | Source Files |
69
+ |---------|-----------|-------------|
70
+ | Claude | API key, OAuth browser | CLI runtime |
71
+ | Gemini | Google OAuth, API key entry, auth in-progress | `AuthDialog.tsx`, `ApiAuthDialog.tsx`, `AuthInProgress.tsx` |
72
+ | Codex | Device code flow, onboarding auth menu | `auth.rs`, `headless_chatgpt_login.rs` |
73
+ | Aider | API key missing/invalid, OpenRouter OAuth | `onboarding.py`, `models.py` |
74
+
75
+ ### Ready State Detection
76
+
77
+ Each adapter knows exactly what "ready for input" looks like:
78
+
79
+ | Adapter | Ready Indicators | Source |
80
+ |---------|-----------------|--------|
81
+ | Claude | `$` prompt | CLI runtime |
82
+ | Gemini | Prompt glyphs (`>`, `!`, `*`, `(r:)`), composer placeholder | `InputPrompt.tsx`, `Composer.tsx` |
83
+ | Codex | `>` glyph, placeholder suggestions | `chat_composer.rs` |
84
+ | Aider | `ask>`, `code>`, `architect>`, `help>`, `multi>`, startup banner | `io.py`, `base_coder.py` |
85
+
86
+ ### Blocking Prompt Detection
87
+
88
+ Adapters detect prompts that block the session and require user action:
89
+
90
+ | Adapter | Detected Prompts |
91
+ |---------|-----------------|
92
+ | Claude | Permission requests, update notices |
93
+ | Gemini | Folder trust, tool execution, validation dialogs, privacy consent |
94
+ | Codex | Directory trust, tool approval, update available, model migration, CWD selection |
95
+ | Aider | File operations, shell commands, git init, pip install, destructive operations |
96
+
97
+ ### Exit Detection
98
+
99
+ Adapters detect when a CLI session has ended:
100
+
101
+ | Adapter | Exit Conditions |
102
+ |---------|----------------|
103
+ | Claude | Base exit detection |
104
+ | Gemini | Folder trust rejection, logout confirmation |
105
+ | Codex | Session end, update completion |
106
+ | Aider | Ctrl+C / KeyboardInterrupt, version update requiring restart |
107
+
108
+ ## Auto-Response Rules
109
+
110
+ Adapters include pre-configured rules to automatically handle known prompts. Rules use two response modes depending on the CLI's input style.
111
+
112
+ ### TUI Menu CLIs (Gemini, Codex, Claude)
113
+
114
+ These CLIs use arrow-key menus rendered with Ink/Ratatui. Rules send key sequences:
115
+
116
+ ```typescript
117
+ const codex = new CodexAdapter();
118
+ codex.autoResponseRules;
119
+ // [
120
+ // { pattern: /update.?available/i, responseType: 'keys', keys: ['down', 'enter'], once: true, ... },
121
+ // { pattern: /trust.?this.?directory/i, responseType: 'keys', keys: ['enter'], once: true, ... },
122
+ // { pattern: /model.?migration/i, responseType: 'keys', keys: ['enter'], once: true, ... },
123
+ // ...
124
+ // ]
125
+ ```
126
+
127
+ ### Text Prompt CLIs (Aider)
128
+
129
+ Aider uses plain text `(Y)es/(N)o` prompts via `io.py`. Rules send typed text:
130
+
131
+ ```typescript
132
+ const aider = new AiderAdapter();
133
+ aider.autoResponseRules;
134
+ // [
135
+ // { pattern: /allow collection of anonymous analytics/i, response: 'n', responseType: 'text', once: true, ... },
136
+ // { pattern: /add .+ to the chat\?/i, response: 'y', responseType: 'text', ... },
137
+ // { pattern: /create new file\?/i, response: 'y', responseType: 'text', ... },
138
+ // { pattern: /run shell commands?\?/i, response: 'y', responseType: 'text', ... },
139
+ // ...17 rules total
140
+ // ]
141
+ ```
142
+
143
+ ### The `usesTuiMenus` Flag
144
+
145
+ Adapters declare their input style via `usesTuiMenus`. This affects how auto-response rules with no explicit `responseType` are delivered:
146
+
147
+ - `usesTuiMenus: true` (Gemini, Codex, Claude) — defaults to `sendKeys('enter')`
148
+ - `usesTuiMenus: false` (Aider) — defaults to `writeRaw(response + '\r')`
149
+
150
+ ## Model Recommendations
151
+
152
+ Each adapter provides model recommendations based on available credentials:
153
+
154
+ ```typescript
155
+ const aider = new AiderAdapter();
156
+
157
+ aider.getRecommendedModels({ anthropicKey: 'sk-ant-...' });
158
+ // { powerful: 'anthropic/claude-sonnet-4-20250514', fast: 'anthropic/claude-haiku-4-5-20251001' }
159
+
160
+ aider.getRecommendedModels({ googleKey: 'AIza...' });
161
+ // { powerful: 'gemini/gemini-3-pro', fast: 'gemini/gemini-3-flash' }
162
+ ```
51
163
 
52
164
  ## Preflight Check
53
165
 
@@ -60,9 +172,9 @@ import { checkAdapters, checkAllAdapters, printMissingAdapters } from 'coding-ag
60
172
  const results = await checkAdapters(['claude', 'aider']);
61
173
  for (const result of results) {
62
174
  if (result.installed) {
63
- console.log(`✓ ${result.adapter} v${result.version}`);
175
+ console.log(`${result.adapter} v${result.version}`);
64
176
  } else {
65
- console.log(`✗ ${result.adapter} - Install: ${result.installCommand}`);
177
+ console.log(`${result.adapter} - Install: ${result.installCommand}`);
66
178
  }
67
179
  }
68
180
 
@@ -73,24 +185,6 @@ const allResults = await checkAllAdapters();
73
185
  await printMissingAdapters(['claude', 'gemini']);
74
186
  ```
75
187
 
76
- Each adapter also provides installation info:
77
-
78
- ```typescript
79
- import { ClaudeAdapter } from 'coding-agent-adapters';
80
-
81
- const claude = new ClaudeAdapter();
82
- console.log(claude.installation);
83
- // {
84
- // command: 'npm install -g @anthropic-ai/claude-code',
85
- // alternatives: ['npx @anthropic-ai/claude-code', 'brew install claude-code'],
86
- // docsUrl: 'https://docs.anthropic.com/en/docs/claude-code',
87
- // minVersion: '1.0.0'
88
- // }
89
-
90
- // Get formatted instructions
91
- console.log(claude.getInstallInstructions());
92
- ```
93
-
94
188
  ## Passing Credentials
95
189
 
96
190
  You can pass API keys either via environment variables or through `adapterConfig`:
@@ -112,93 +206,51 @@ const session = await manager.spawn({
112
206
  workdir: '/project',
113
207
  adapterConfig: {
114
208
  anthropicKey: 'sk-ant-...',
209
+ openaiKey: 'sk-...',
210
+ googleKey: 'AIza...',
115
211
  },
116
212
  });
117
213
  ```
118
214
 
119
- ## Adapter Features
120
-
121
- ### Auto-Response Rules
122
-
123
- Each adapter includes auto-response rules for common prompts (updates, telemetry, etc.):
124
-
125
- ```typescript
126
- const claude = new ClaudeAdapter();
127
- console.log(claude.autoResponseRules);
128
- // [
129
- // { pattern: /update available.*\[y\/n\]/i, response: 'n', ... },
130
- // { pattern: /telemetry.*\[y\/n\]/i, response: 'n', ... },
131
- // ...
132
- // ]
133
- ```
134
-
135
- ### Blocking Prompt Detection
136
-
137
- Adapters detect blocking prompts that require user intervention:
138
-
139
- ```typescript
140
- session.on('blocking_prompt', (promptInfo, autoResponded) => {
141
- if (!autoResponded) {
142
- console.log(`User action required: ${promptInfo.type}`);
143
- console.log(promptInfo.instructions);
144
- }
145
- });
146
- ```
147
-
148
- ### Login Detection
149
-
150
- Adapters detect when authentication is required:
151
-
152
- ```typescript
153
- session.on('login_required', (instructions, url) => {
154
- console.log('Authentication required:', instructions);
155
- if (url) {
156
- console.log('Open:', url);
157
- }
158
- });
159
- ```
160
-
161
215
  ## Creating Custom Adapters
162
216
 
163
217
  Extend `BaseCodingAdapter` to create adapters for other coding CLIs:
164
218
 
165
219
  ```typescript
166
220
  import { BaseCodingAdapter } from 'coding-agent-adapters';
167
- import type { SpawnConfig, ParsedOutput, LoginDetection } from 'pty-manager';
221
+ import type { SpawnConfig, ParsedOutput, LoginDetection, AutoResponseRule } from 'pty-manager';
168
222
 
169
223
  export class CursorAdapter extends BaseCodingAdapter {
170
224
  readonly adapterType = 'cursor';
171
225
  readonly displayName = 'Cursor';
172
226
 
173
- getCommand(): string {
174
- return 'cursor';
175
- }
227
+ // Set to false if the CLI uses text prompts instead of TUI menus
228
+ override readonly usesTuiMenus = false;
176
229
 
177
- getArgs(config: SpawnConfig): string[] {
178
- return ['--cli'];
179
- }
230
+ readonly autoResponseRules: AutoResponseRule[] = [
231
+ { pattern: /accept terms/i, type: 'tos', response: 'y', responseType: 'text', description: 'Accept TOS', safe: true, once: true },
232
+ ];
180
233
 
181
- getEnv(config: SpawnConfig): Record<string, string> {
182
- return {};
183
- }
234
+ getCommand(): string { return 'cursor'; }
235
+ getArgs(config: SpawnConfig): string[] { return ['--cli']; }
236
+ getEnv(config: SpawnConfig): Record<string, string> { return {}; }
184
237
 
185
238
  detectLogin(output: string): LoginDetection {
186
- // Implement login detection
239
+ if (/login required/i.test(output)) {
240
+ return { required: true, type: 'browser' };
241
+ }
187
242
  return { required: false };
188
243
  }
189
244
 
190
245
  detectReady(output: string): boolean {
191
- return output.includes('Cursor ready');
246
+ return /cursor>\s*$/m.test(output);
192
247
  }
193
248
 
194
249
  parseOutput(output: string): ParsedOutput | null {
195
- // Implement output parsing
196
- return null;
250
+ return { type: 'response', content: output.trim(), isComplete: true, isQuestion: output.includes('?') };
197
251
  }
198
252
 
199
- getPromptPattern(): RegExp {
200
- return /cursor>\s*$/;
201
- }
253
+ getPromptPattern(): RegExp { return /cursor>\s*$/; }
202
254
  }
203
255
  ```
204
256