coding-agent-adapters 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,807 @@
1
+ 'use strict';
2
+
3
+ var ptyManager = require('pty-manager');
4
+
5
+ // src/base-coding-adapter.ts
6
+ var BaseCodingAdapter = class extends ptyManager.BaseCLIAdapter {
7
+ /**
8
+ * Get credentials from config
9
+ */
10
+ getCredentials(config) {
11
+ const adapterConfig = config.adapterConfig;
12
+ return adapterConfig || {};
13
+ }
14
+ /**
15
+ * Override detectExit to include installation instructions
16
+ */
17
+ detectExit(output) {
18
+ if (output.includes("Command not found") || output.includes("command not found")) {
19
+ return {
20
+ exited: true,
21
+ code: 127,
22
+ error: `${this.displayName} CLI not found. Install with: ${this.installation.command}
23
+ Docs: ${this.installation.docsUrl}`
24
+ };
25
+ }
26
+ return super.detectExit(output);
27
+ }
28
+ /**
29
+ * Get formatted installation instructions
30
+ */
31
+ getInstallInstructions() {
32
+ const lines = [
33
+ `${this.displayName} Installation`,
34
+ `${"=".repeat(this.displayName.length + 13)}`,
35
+ "",
36
+ `Primary: ${this.installation.command}`
37
+ ];
38
+ if (this.installation.alternatives?.length) {
39
+ lines.push("");
40
+ lines.push("Alternatives:");
41
+ for (const alt of this.installation.alternatives) {
42
+ lines.push(` - ${alt}`);
43
+ }
44
+ }
45
+ lines.push("");
46
+ lines.push(`Docs: ${this.installation.docsUrl}`);
47
+ if (this.installation.minVersion) {
48
+ lines.push(`Minimum version: ${this.installation.minVersion}`);
49
+ }
50
+ return lines.join("\n");
51
+ }
52
+ /**
53
+ * Check if response appears complete based on common patterns
54
+ */
55
+ isResponseComplete(output) {
56
+ const completionIndicators = [
57
+ /\n>\s*$/,
58
+ // Ends with prompt
59
+ /\n\s*$/,
60
+ // Ends with newline
61
+ /Done\./i,
62
+ // Explicit done
63
+ /completed/i,
64
+ // Task completed
65
+ /finished/i,
66
+ // Finished
67
+ /```\s*$/
68
+ // Code block ended
69
+ ];
70
+ return completionIndicators.some((pattern) => pattern.test(output));
71
+ }
72
+ /**
73
+ * Extract the main content from CLI output, removing common artifacts
74
+ */
75
+ extractContent(output, promptPattern) {
76
+ let content = output;
77
+ content = content.replace(promptPattern, "");
78
+ content = content.replace(/^(Thinking|Working|Reading|Writing|Processing|Generating)\.+$/gm, "");
79
+ content = content.trim();
80
+ return content;
81
+ }
82
+ };
83
+
84
+ // src/claude-adapter.ts
85
+ var ClaudeAdapter = class extends BaseCodingAdapter {
86
+ adapterType = "claude";
87
+ displayName = "Claude Code";
88
+ installation = {
89
+ command: "npm install -g @anthropic-ai/claude-code",
90
+ alternatives: [
91
+ "npx @anthropic-ai/claude-code (run without installing)",
92
+ "brew install claude-code (macOS with Homebrew)"
93
+ ],
94
+ docsUrl: "https://docs.anthropic.com/en/docs/claude-code",
95
+ minVersion: "1.0.0"
96
+ };
97
+ /**
98
+ * Auto-response rules for Claude Code CLI.
99
+ * These handle common prompts that can be safely auto-responded.
100
+ */
101
+ autoResponseRules = [
102
+ {
103
+ pattern: /update available.*\[y\/n\]/i,
104
+ type: "update",
105
+ response: "n",
106
+ description: "Decline Claude Code update to continue execution",
107
+ safe: true
108
+ },
109
+ {
110
+ pattern: /new version.*available.*\[y\/n\]/i,
111
+ type: "update",
112
+ response: "n",
113
+ description: "Decline version upgrade prompt",
114
+ safe: true
115
+ },
116
+ {
117
+ pattern: /would you like to enable.*telemetry.*\[y\/n\]/i,
118
+ type: "config",
119
+ response: "n",
120
+ description: "Decline telemetry prompt",
121
+ safe: true
122
+ },
123
+ {
124
+ pattern: /send anonymous usage data.*\[y\/n\]/i,
125
+ type: "config",
126
+ response: "n",
127
+ description: "Decline anonymous usage data",
128
+ safe: true
129
+ },
130
+ {
131
+ pattern: /continue without.*\[y\/n\]/i,
132
+ type: "config",
133
+ response: "y",
134
+ description: "Continue without optional feature",
135
+ safe: true
136
+ }
137
+ ];
138
+ getCommand() {
139
+ return "claude";
140
+ }
141
+ getArgs(config) {
142
+ const args = [];
143
+ args.push("--print");
144
+ if (config.workdir) {
145
+ args.push("--cwd", config.workdir);
146
+ }
147
+ return args;
148
+ }
149
+ getEnv(config) {
150
+ const env = {};
151
+ const credentials = this.getCredentials(config);
152
+ if (credentials.anthropicKey) {
153
+ env.ANTHROPIC_API_KEY = credentials.anthropicKey;
154
+ }
155
+ if (config.env?.ANTHROPIC_MODEL) {
156
+ env.ANTHROPIC_MODEL = config.env.ANTHROPIC_MODEL;
157
+ }
158
+ env.CLAUDE_CODE_DISABLE_INTERACTIVE = "true";
159
+ return env;
160
+ }
161
+ detectLogin(output) {
162
+ const stripped = this.stripAnsi(output);
163
+ if (stripped.includes("API key not found") || stripped.includes("ANTHROPIC_API_KEY") || stripped.includes("authentication required") || stripped.includes("Please sign in") || stripped.includes("Invalid API key")) {
164
+ return {
165
+ required: true,
166
+ type: "api_key",
167
+ instructions: "Set ANTHROPIC_API_KEY environment variable or provide credentials in adapterConfig"
168
+ };
169
+ }
170
+ if (stripped.includes("Open this URL") || stripped.includes("browser to authenticate")) {
171
+ const urlMatch = stripped.match(/https?:\/\/[^\s]+/);
172
+ return {
173
+ required: true,
174
+ type: "browser",
175
+ url: urlMatch ? urlMatch[0] : void 0,
176
+ instructions: "Browser authentication required"
177
+ };
178
+ }
179
+ return { required: false };
180
+ }
181
+ /**
182
+ * Detect blocking prompts specific to Claude Code CLI
183
+ */
184
+ detectBlockingPrompt(output) {
185
+ const stripped = this.stripAnsi(output);
186
+ const loginDetection = this.detectLogin(output);
187
+ if (loginDetection.required) {
188
+ return {
189
+ detected: true,
190
+ type: "login",
191
+ prompt: loginDetection.instructions,
192
+ url: loginDetection.url,
193
+ canAutoRespond: false,
194
+ instructions: loginDetection.instructions
195
+ };
196
+ }
197
+ if (/choose.*model|select.*model|available models/i.test(stripped) && /\d+\)|claude-/i.test(stripped)) {
198
+ return {
199
+ detected: true,
200
+ type: "model_select",
201
+ prompt: "Claude model selection",
202
+ canAutoRespond: false,
203
+ instructions: "Please select a Claude model or set ANTHROPIC_MODEL env var"
204
+ };
205
+ }
206
+ if (/which.*tier|select.*plan|api.*tier/i.test(stripped)) {
207
+ return {
208
+ detected: true,
209
+ type: "config",
210
+ prompt: "API tier selection",
211
+ canAutoRespond: false,
212
+ instructions: "Please select an API tier"
213
+ };
214
+ }
215
+ if (/welcome to claude|first time setup|initial configuration/i.test(stripped)) {
216
+ return {
217
+ detected: true,
218
+ type: "config",
219
+ prompt: "First-time setup",
220
+ canAutoRespond: false,
221
+ instructions: "Claude Code requires initial configuration"
222
+ };
223
+ }
224
+ if (/allow.*access|grant.*permission|access to .* files/i.test(stripped) && /\[y\/n\]/i.test(stripped)) {
225
+ return {
226
+ detected: true,
227
+ type: "permission",
228
+ prompt: "File/directory access permission",
229
+ options: ["y", "n"],
230
+ suggestedResponse: "y",
231
+ canAutoRespond: true,
232
+ instructions: "Claude Code requesting file access permission"
233
+ };
234
+ }
235
+ return super.detectBlockingPrompt(output);
236
+ }
237
+ detectReady(output) {
238
+ const stripped = this.stripAnsi(output);
239
+ return stripped.includes("Claude Code") || stripped.includes("How can I help") || stripped.includes("What would you like") || // Check for the typical prompt pattern
240
+ />\s*$/.test(stripped) || // Or a clear ready indicator
241
+ stripped.includes("Ready");
242
+ }
243
+ parseOutput(output) {
244
+ const stripped = this.stripAnsi(output);
245
+ const isComplete = this.isResponseComplete(stripped);
246
+ if (!isComplete) {
247
+ return null;
248
+ }
249
+ const isQuestion = this.containsQuestion(stripped);
250
+ const content = this.extractContent(stripped, /^.*>\s*/gm);
251
+ return {
252
+ type: isQuestion ? "question" : "response",
253
+ content,
254
+ isComplete: true,
255
+ isQuestion,
256
+ metadata: {
257
+ raw: output
258
+ }
259
+ };
260
+ }
261
+ getPromptPattern() {
262
+ return /(?:claude|>)\s*$/i;
263
+ }
264
+ getHealthCheckCommand() {
265
+ return "claude --version";
266
+ }
267
+ };
268
+
269
+ // src/gemini-adapter.ts
270
+ var GeminiAdapter = class extends BaseCodingAdapter {
271
+ adapterType = "gemini";
272
+ displayName = "Google Gemini";
273
+ installation = {
274
+ command: "npm install -g @anthropics/gemini-cli",
275
+ alternatives: [
276
+ "See documentation for latest installation method"
277
+ ],
278
+ docsUrl: "https://github.com/anthropics/gemini-cli#installation"
279
+ };
280
+ getCommand() {
281
+ return "gemini";
282
+ }
283
+ getArgs(config) {
284
+ const args = [];
285
+ args.push("--non-interactive");
286
+ if (config.workdir) {
287
+ args.push("--cwd", config.workdir);
288
+ }
289
+ args.push("--output-format", "text");
290
+ return args;
291
+ }
292
+ getEnv(config) {
293
+ const env = {};
294
+ const credentials = this.getCredentials(config);
295
+ if (credentials.googleKey) {
296
+ env.GOOGLE_API_KEY = credentials.googleKey;
297
+ env.GEMINI_API_KEY = credentials.googleKey;
298
+ }
299
+ if (config.env?.GEMINI_MODEL) {
300
+ env.GEMINI_MODEL = config.env.GEMINI_MODEL;
301
+ }
302
+ env.NO_COLOR = "1";
303
+ return env;
304
+ }
305
+ detectLogin(output) {
306
+ const stripped = this.stripAnsi(output);
307
+ if (stripped.includes("API key not found") || stripped.includes("GOOGLE_API_KEY") || stripped.includes("GEMINI_API_KEY") || stripped.includes("authentication required") || stripped.includes("Invalid API key") || stripped.includes("API key is not valid")) {
308
+ return {
309
+ required: true,
310
+ type: "api_key",
311
+ instructions: "Set GOOGLE_API_KEY or GEMINI_API_KEY environment variable"
312
+ };
313
+ }
314
+ if (stripped.includes("Sign in with Google") || stripped.includes("OAuth") || stripped.includes("accounts.google.com")) {
315
+ const urlMatch = stripped.match(/https?:\/\/[^\s]+/);
316
+ return {
317
+ required: true,
318
+ type: "oauth",
319
+ url: urlMatch ? urlMatch[0] : "https://accounts.google.com",
320
+ instructions: "Google OAuth authentication required"
321
+ };
322
+ }
323
+ if (stripped.includes("Application Default Credentials") || stripped.includes("gcloud auth")) {
324
+ return {
325
+ required: true,
326
+ type: "browser",
327
+ instructions: "Run: gcloud auth application-default login"
328
+ };
329
+ }
330
+ return { required: false };
331
+ }
332
+ detectBlockingPrompt(output) {
333
+ const stripped = this.stripAnsi(output);
334
+ const loginDetection = this.detectLogin(output);
335
+ if (loginDetection.required) {
336
+ return {
337
+ detected: true,
338
+ type: "login",
339
+ prompt: loginDetection.instructions,
340
+ url: loginDetection.url,
341
+ canAutoRespond: false,
342
+ instructions: loginDetection.instructions
343
+ };
344
+ }
345
+ if (/select.*model|choose.*model|gemini-/i.test(stripped) && /\d+\)/i.test(stripped)) {
346
+ return {
347
+ detected: true,
348
+ type: "model_select",
349
+ prompt: "Gemini model selection",
350
+ canAutoRespond: false,
351
+ instructions: "Please select a model or set GEMINI_MODEL env var"
352
+ };
353
+ }
354
+ if (/select.*project|choose.*project|google cloud project/i.test(stripped)) {
355
+ return {
356
+ detected: true,
357
+ type: "project_select",
358
+ prompt: "Google Cloud project selection",
359
+ canAutoRespond: false,
360
+ instructions: "Please select a Google Cloud project"
361
+ };
362
+ }
363
+ if (/safety.*filter|content.*blocked|unsafe.*content/i.test(stripped)) {
364
+ return {
365
+ detected: true,
366
+ type: "unknown",
367
+ prompt: "Safety filter triggered",
368
+ canAutoRespond: false,
369
+ instructions: "Content was blocked by safety filters"
370
+ };
371
+ }
372
+ return super.detectBlockingPrompt(output);
373
+ }
374
+ detectReady(output) {
375
+ const stripped = this.stripAnsi(output);
376
+ return stripped.includes("Gemini") || stripped.includes("Ready") || stripped.includes("How can I help") || stripped.includes("What would you like") || /(?:gemini|>)\s*$/i.test(stripped);
377
+ }
378
+ parseOutput(output) {
379
+ const stripped = this.stripAnsi(output);
380
+ const isComplete = this.isResponseComplete(stripped);
381
+ if (!isComplete) {
382
+ return null;
383
+ }
384
+ const isQuestion = this.containsQuestion(stripped);
385
+ let content = this.extractContent(stripped, /^.*(?:gemini|>)\s*/gim);
386
+ content = content.replace(/^\[Safety[^\]]*\].*$/gm, "");
387
+ return {
388
+ type: isQuestion ? "question" : "response",
389
+ content,
390
+ isComplete: true,
391
+ isQuestion,
392
+ metadata: {
393
+ raw: output
394
+ }
395
+ };
396
+ }
397
+ getPromptPattern() {
398
+ return /(?:gemini|>)\s*$/i;
399
+ }
400
+ getHealthCheckCommand() {
401
+ return "gemini --version";
402
+ }
403
+ };
404
+
405
+ // src/codex-adapter.ts
406
+ var CodexAdapter = class extends BaseCodingAdapter {
407
+ adapterType = "codex";
408
+ displayName = "OpenAI Codex";
409
+ installation = {
410
+ command: "npm install -g @openai/codex",
411
+ alternatives: [
412
+ "pip install openai (Python SDK)"
413
+ ],
414
+ docsUrl: "https://github.com/openai/codex"
415
+ };
416
+ /**
417
+ * Auto-response rules for OpenAI Codex CLI.
418
+ */
419
+ autoResponseRules = [
420
+ {
421
+ pattern: /update available.*\[y\/n\]/i,
422
+ type: "update",
423
+ response: "n",
424
+ description: "Decline Codex update to continue execution",
425
+ safe: true
426
+ },
427
+ {
428
+ pattern: /new version.*\[y\/n\]/i,
429
+ type: "update",
430
+ response: "n",
431
+ description: "Decline version upgrade",
432
+ safe: true
433
+ },
434
+ {
435
+ pattern: /send.*telemetry.*\[y\/n\]/i,
436
+ type: "config",
437
+ response: "n",
438
+ description: "Decline telemetry",
439
+ safe: true
440
+ },
441
+ {
442
+ pattern: /enable.*beta.*features.*\[y\/n\]/i,
443
+ type: "config",
444
+ response: "n",
445
+ description: "Decline beta features",
446
+ safe: true
447
+ }
448
+ ];
449
+ getCommand() {
450
+ return "codex";
451
+ }
452
+ getArgs(config) {
453
+ const args = [];
454
+ args.push("--quiet");
455
+ if (config.workdir) {
456
+ args.push("--cwd", config.workdir);
457
+ }
458
+ return args;
459
+ }
460
+ getEnv(config) {
461
+ const env = {};
462
+ const credentials = this.getCredentials(config);
463
+ if (credentials.openaiKey) {
464
+ env.OPENAI_API_KEY = credentials.openaiKey;
465
+ }
466
+ if (config.env?.OPENAI_MODEL) {
467
+ env.OPENAI_MODEL = config.env.OPENAI_MODEL;
468
+ }
469
+ env.NO_COLOR = "1";
470
+ return env;
471
+ }
472
+ detectLogin(output) {
473
+ const stripped = this.stripAnsi(output);
474
+ if (stripped.includes("API key not found") || stripped.includes("OPENAI_API_KEY") || stripped.includes("authentication required") || stripped.includes("Invalid API key") || stripped.includes("Unauthorized") || stripped.includes("API key is invalid")) {
475
+ return {
476
+ required: true,
477
+ type: "api_key",
478
+ instructions: "Set OPENAI_API_KEY environment variable or provide credentials in adapterConfig"
479
+ };
480
+ }
481
+ if (stripped.includes("device code") || stripped.includes("Enter the code")) {
482
+ const codeMatch = stripped.match(/code[:\s]+([A-Z0-9-]+)/i);
483
+ const urlMatch = stripped.match(/https?:\/\/[^\s]+/);
484
+ return {
485
+ required: true,
486
+ type: "device_code",
487
+ url: urlMatch ? urlMatch[0] : void 0,
488
+ instructions: codeMatch ? `Enter code ${codeMatch[1]} at the URL` : "Device code authentication required"
489
+ };
490
+ }
491
+ return { required: false };
492
+ }
493
+ /**
494
+ * Detect blocking prompts specific to OpenAI Codex CLI
495
+ */
496
+ detectBlockingPrompt(output) {
497
+ const stripped = this.stripAnsi(output);
498
+ const loginDetection = this.detectLogin(output);
499
+ if (loginDetection.required) {
500
+ return {
501
+ detected: true,
502
+ type: "login",
503
+ prompt: loginDetection.instructions,
504
+ url: loginDetection.url,
505
+ canAutoRespond: false,
506
+ instructions: loginDetection.instructions
507
+ };
508
+ }
509
+ if (/select.*model|choose.*model|gpt-4|gpt-3\.5/i.test(stripped) && /\d+\)/i.test(stripped)) {
510
+ return {
511
+ detected: true,
512
+ type: "model_select",
513
+ prompt: "OpenAI model selection",
514
+ canAutoRespond: false,
515
+ instructions: "Please select a model or set OPENAI_MODEL env var"
516
+ };
517
+ }
518
+ if (/select.*organization|choose.*org|multiple organizations/i.test(stripped)) {
519
+ return {
520
+ detected: true,
521
+ type: "config",
522
+ prompt: "Organization selection",
523
+ canAutoRespond: false,
524
+ instructions: "Please select an OpenAI organization"
525
+ };
526
+ }
527
+ if (/rate limit|too many requests/i.test(stripped) && /retry|wait/i.test(stripped)) {
528
+ return {
529
+ detected: true,
530
+ type: "unknown",
531
+ prompt: "Rate limit reached",
532
+ canAutoRespond: false,
533
+ instructions: "OpenAI rate limit reached - please wait"
534
+ };
535
+ }
536
+ return super.detectBlockingPrompt(output);
537
+ }
538
+ detectReady(output) {
539
+ const stripped = this.stripAnsi(output);
540
+ return stripped.includes("Codex") || stripped.includes("Ready") || stripped.includes("How can I help") || /(?:codex|>)\s*$/i.test(stripped);
541
+ }
542
+ parseOutput(output) {
543
+ const stripped = this.stripAnsi(output);
544
+ const isComplete = this.isResponseComplete(stripped);
545
+ if (!isComplete) {
546
+ return null;
547
+ }
548
+ const isQuestion = this.containsQuestion(stripped);
549
+ const content = this.extractContent(stripped, /^.*(?:codex|>)\s*/gim);
550
+ return {
551
+ type: isQuestion ? "question" : "response",
552
+ content,
553
+ isComplete: true,
554
+ isQuestion,
555
+ metadata: {
556
+ raw: output
557
+ }
558
+ };
559
+ }
560
+ getPromptPattern() {
561
+ return /(?:codex|>)\s*$/i;
562
+ }
563
+ getHealthCheckCommand() {
564
+ return "codex --version";
565
+ }
566
+ };
567
+
568
+ // src/aider-adapter.ts
569
+ var AiderAdapter = class extends BaseCodingAdapter {
570
+ adapterType = "aider";
571
+ displayName = "Aider";
572
+ installation = {
573
+ command: "pip install aider-chat",
574
+ alternatives: [
575
+ "pipx install aider-chat (isolated install)",
576
+ "brew install aider (macOS with Homebrew)"
577
+ ],
578
+ docsUrl: "https://aider.chat/docs/install.html",
579
+ minVersion: "0.50.0"
580
+ };
581
+ /**
582
+ * Auto-response rules for Aider CLI.
583
+ */
584
+ autoResponseRules = [
585
+ {
586
+ pattern: /Add .+ to the chat\?.*\[y\/n\]/i,
587
+ type: "permission",
588
+ response: "y",
589
+ description: "Allow Aider to add files to chat context",
590
+ safe: true
591
+ },
592
+ {
593
+ pattern: /Create new file.*\[y\/n\]/i,
594
+ type: "permission",
595
+ response: "y",
596
+ description: "Allow Aider to create new files",
597
+ safe: true
598
+ },
599
+ {
600
+ pattern: /Apply.*changes.*\[y\/n\]/i,
601
+ type: "permission",
602
+ response: "y",
603
+ description: "Apply proposed changes",
604
+ safe: true
605
+ }
606
+ ];
607
+ getCommand() {
608
+ return "aider";
609
+ }
610
+ getArgs(config) {
611
+ const args = [];
612
+ args.push("--auto-commits");
613
+ args.push("--no-pretty");
614
+ args.push("--no-show-diffs");
615
+ const credentials = this.getCredentials(config);
616
+ if (config.env?.AIDER_MODEL) {
617
+ args.push("--model", config.env.AIDER_MODEL);
618
+ }
619
+ if (credentials.anthropicKey && !config.env?.AIDER_MODEL) {
620
+ args.push("--model", "claude-3-5-sonnet-20241022");
621
+ }
622
+ return args;
623
+ }
624
+ getEnv(config) {
625
+ const env = {};
626
+ const credentials = this.getCredentials(config);
627
+ if (credentials.anthropicKey) {
628
+ env.ANTHROPIC_API_KEY = credentials.anthropicKey;
629
+ }
630
+ if (credentials.openaiKey) {
631
+ env.OPENAI_API_KEY = credentials.openaiKey;
632
+ }
633
+ if (credentials.googleKey) {
634
+ env.GOOGLE_API_KEY = credentials.googleKey;
635
+ }
636
+ env.NO_COLOR = "1";
637
+ if (config.env?.AIDER_NO_GIT === "true") {
638
+ env.AIDER_NO_GIT = "true";
639
+ }
640
+ return env;
641
+ }
642
+ detectLogin(output) {
643
+ const stripped = this.stripAnsi(output);
644
+ if (stripped.includes("No API key") || stripped.includes("API key not found") || stripped.includes("ANTHROPIC_API_KEY") || stripped.includes("OPENAI_API_KEY") || stripped.includes("Missing API key")) {
645
+ return {
646
+ required: true,
647
+ type: "api_key",
648
+ instructions: "Set ANTHROPIC_API_KEY or OPENAI_API_KEY environment variable"
649
+ };
650
+ }
651
+ if (stripped.includes("Invalid API key") || stripped.includes("Authentication failed") || stripped.includes("Unauthorized")) {
652
+ return {
653
+ required: true,
654
+ type: "api_key",
655
+ instructions: "API key is invalid - please check your credentials"
656
+ };
657
+ }
658
+ return { required: false };
659
+ }
660
+ detectBlockingPrompt(output) {
661
+ const stripped = this.stripAnsi(output);
662
+ const loginDetection = this.detectLogin(output);
663
+ if (loginDetection.required) {
664
+ return {
665
+ detected: true,
666
+ type: "login",
667
+ prompt: loginDetection.instructions,
668
+ canAutoRespond: false,
669
+ instructions: loginDetection.instructions
670
+ };
671
+ }
672
+ if (/select.*model|choose.*model|which model/i.test(stripped)) {
673
+ return {
674
+ detected: true,
675
+ type: "model_select",
676
+ prompt: "Model selection required",
677
+ canAutoRespond: false,
678
+ instructions: "Please select a model or set AIDER_MODEL env var"
679
+ };
680
+ }
681
+ if (/not.*git.*repo|git.*not.*found|initialize.*git/i.test(stripped)) {
682
+ return {
683
+ detected: true,
684
+ type: "config",
685
+ prompt: "Git repository required",
686
+ canAutoRespond: false,
687
+ instructions: "Aider requires a git repository. Run git init or use --no-git"
688
+ };
689
+ }
690
+ if (/delete|remove|overwrite/i.test(stripped) && /\[y\/n\]/i.test(stripped)) {
691
+ return {
692
+ detected: true,
693
+ type: "permission",
694
+ prompt: "Destructive operation confirmation",
695
+ options: ["y", "n"],
696
+ canAutoRespond: false,
697
+ instructions: "Aider is asking to perform a potentially destructive operation"
698
+ };
699
+ }
700
+ return super.detectBlockingPrompt(output);
701
+ }
702
+ detectReady(output) {
703
+ const stripped = this.stripAnsi(output);
704
+ return stripped.includes("aider>") || stripped.includes("Aider") || /aider.*ready/i.test(stripped) || // Aider shows file list when ready
705
+ /Added.*to the chat/i.test(stripped) || // Or the prompt
706
+ />\s*$/.test(stripped);
707
+ }
708
+ parseOutput(output) {
709
+ const stripped = this.stripAnsi(output);
710
+ const isComplete = this.isResponseComplete(stripped);
711
+ if (!isComplete) {
712
+ return null;
713
+ }
714
+ const isQuestion = this.containsQuestion(stripped);
715
+ let content = this.extractContent(stripped, /^.*aider>\s*/gim);
716
+ content = content.replace(/^(Added|Removed|Created|Updated) .+ (to|from) the chat\.?$/gm, "");
717
+ return {
718
+ type: isQuestion ? "question" : "response",
719
+ content: content.trim(),
720
+ isComplete: true,
721
+ isQuestion,
722
+ metadata: {
723
+ raw: output
724
+ }
725
+ };
726
+ }
727
+ getPromptPattern() {
728
+ return /(?:aider>|>)\s*$/i;
729
+ }
730
+ getHealthCheckCommand() {
731
+ return "aider --version";
732
+ }
733
+ };
734
+
735
+ // src/index.ts
736
+ function createAllAdapters() {
737
+ return [
738
+ new ClaudeAdapter(),
739
+ new GeminiAdapter(),
740
+ new CodexAdapter(),
741
+ new AiderAdapter()
742
+ ];
743
+ }
744
+ var ADAPTER_TYPES = {
745
+ claude: ClaudeAdapter,
746
+ gemini: GeminiAdapter,
747
+ codex: CodexAdapter,
748
+ aider: AiderAdapter
749
+ };
750
+ function createAdapter(type) {
751
+ const AdapterClass = ADAPTER_TYPES[type];
752
+ if (!AdapterClass) {
753
+ throw new Error(`Unknown adapter type: ${type}`);
754
+ }
755
+ return new AdapterClass();
756
+ }
757
+ async function checkAdapters(types) {
758
+ const results = [];
759
+ for (const type of types) {
760
+ const adapter = createAdapter(type);
761
+ const validation = await adapter.validateInstallation();
762
+ results.push({
763
+ adapter: adapter.displayName,
764
+ installed: validation.installed,
765
+ version: validation.version,
766
+ error: validation.error,
767
+ installCommand: adapter.installation.command,
768
+ docsUrl: adapter.installation.docsUrl
769
+ });
770
+ }
771
+ return results;
772
+ }
773
+ async function checkAllAdapters() {
774
+ return checkAdapters(Object.keys(ADAPTER_TYPES));
775
+ }
776
+ async function printMissingAdapters(types) {
777
+ const results = types ? await checkAdapters(types) : await checkAllAdapters();
778
+ const missing = results.filter((r) => !r.installed);
779
+ if (missing.length === 0) {
780
+ console.log("All CLI tools are installed!");
781
+ return;
782
+ }
783
+ console.log("\nMissing CLI tools:\n");
784
+ for (const m of missing) {
785
+ console.log(`${m.adapter}`);
786
+ console.log(` Install: ${m.installCommand}`);
787
+ console.log(` Docs: ${m.docsUrl}`);
788
+ if (m.error) {
789
+ console.log(` Error: ${m.error}`);
790
+ }
791
+ console.log();
792
+ }
793
+ }
794
+
795
+ exports.ADAPTER_TYPES = ADAPTER_TYPES;
796
+ exports.AiderAdapter = AiderAdapter;
797
+ exports.BaseCodingAdapter = BaseCodingAdapter;
798
+ exports.ClaudeAdapter = ClaudeAdapter;
799
+ exports.CodexAdapter = CodexAdapter;
800
+ exports.GeminiAdapter = GeminiAdapter;
801
+ exports.checkAdapters = checkAdapters;
802
+ exports.checkAllAdapters = checkAllAdapters;
803
+ exports.createAdapter = createAdapter;
804
+ exports.createAllAdapters = createAllAdapters;
805
+ exports.printMissingAdapters = printMissingAdapters;
806
+ //# sourceMappingURL=index.cjs.map
807
+ //# sourceMappingURL=index.cjs.map