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