ai-code-connect 1.0.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/README.md ADDED
@@ -0,0 +1,280 @@
1
+ # AIC² - AI Code Connect
2
+
3
+ ```
4
+ █████╗ ██╗ ██████╗ ^2
5
+ ██╔══██╗██║██╔════╝
6
+ ███████║██║██║
7
+ ██╔══██║██║██║
8
+ ██║ ██║██║╚██████╗
9
+ ╚═╝ ╚═╝╚═╝ ╚═════╝
10
+ ```
11
+
12
+ A CLI tool that connects **Claude Code** and **Gemini CLI**, eliminating manual copy-paste between AI coding assistants.
13
+
14
+ **AIC²** = **A**I **C**ode **C**onnect (the two C's = ²)
15
+
16
+ ## The Problem
17
+
18
+ When working with multiple AI coding tools:
19
+ 1. Ask Gemini for a proposal
20
+ 2. Copy the response
21
+ 3. Paste into Claude for review
22
+ 4. Copy Claude's feedback
23
+ 5. Paste back to Gemini...
24
+
25
+ This is tedious and breaks your flow.
26
+
27
+ ## The Solution
28
+
29
+ `aic` bridges both tools in a single interactive session with:
30
+ - **Persistent sessions** - Both tools remember context
31
+ - **One-command forwarding** - Send responses between tools instantly
32
+ - **Interactive mode** - Full access to slash commands and approvals
33
+ - **Detach/reattach** - Keep tools running in background
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ # Clone and install
39
+ git clone https://github.com/jacob-bd/ai-code-connect.git
40
+ cd ai-code-connect
41
+ npm install
42
+ npm run build
43
+
44
+ # Link globally
45
+ npm link
46
+ ```
47
+
48
+ ## Prerequisites
49
+
50
+ Install both AI CLI tools:
51
+
52
+ - **Claude Code**: `npm install -g @anthropic-ai/claude-code`
53
+ - **Gemini CLI**: `npm install -g @google/gemini-cli`
54
+
55
+ Verify:
56
+ ```bash
57
+ aic tools
58
+ # Should show both as "✓ available"
59
+ ```
60
+
61
+ ## Quick Start
62
+
63
+ ```bash
64
+ aic
65
+ ```
66
+
67
+ That's it! This launches the interactive session.
68
+
69
+ ## Usage
70
+
71
+ ### Basic Commands
72
+
73
+ | Command | Description |
74
+ |---------|-------------|
75
+ | `/claude` | Switch to Claude Code |
76
+ | `/gemini` | Switch to Gemini CLI |
77
+ | `/i` | Enter interactive mode (full tool access) |
78
+ | `/forward` | Forward last response to other tool (auto-selects if 2 tools) |
79
+ | `/forward [tool]` | Forward to specific tool (required if 3+ tools) |
80
+ | `/forward [tool] [msg]` | Forward with additional context |
81
+ | `/history` | Show conversation history |
82
+ | `/status` | Show running processes |
83
+ | `/clear` | Clear sessions and history |
84
+ | `/quit` or `/cya` | Exit |
85
+
86
+ ### Tool Slash Commands
87
+
88
+ Use double slash (`//`) to run tool-specific slash commands:
89
+
90
+ | Input | What Happens |
91
+ |-------|--------------|
92
+ | `//cost` | Opens interactive mode, runs `/cost`, you see output |
93
+ | `//status` | Opens interactive mode, runs `/status`, you can interact |
94
+ | `//config` | Opens interactive mode, runs `/config`, full control |
95
+
96
+ When you type `//command`:
97
+ 1. AIC enters interactive mode with the tool
98
+ 2. Sends the `/command` for you
99
+ 3. You see the full output and can interact
100
+ 4. Press `Ctrl+]` when done to return to AIC
101
+
102
+ This approach ensures you can fully view and interact with commands like `/status` that show interactive UIs.
103
+
104
+ ### Command Menu
105
+
106
+ Type `/` to see a command menu. Use ↓ arrow to select, or keep typing.
107
+
108
+ ### Example Session
109
+
110
+ ```
111
+ ❯ claude → How should I implement caching for this API?
112
+
113
+ ⠹ Claude is thinking...
114
+ I suggest implementing a Redis-based caching layer...
115
+
116
+ ❯ claude → /forward What do you think of this approach?
117
+
118
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
119
+ ↗ Forwarding from Claude Code → Gemini CLI
120
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
121
+ Gemini CLI responds:
122
+
123
+ The Redis approach is solid. I'd also consider...
124
+
125
+ ❯ gemini → /claude
126
+ ● Switched to Claude Code
127
+
128
+ ❯ claude → Can you implement that?
129
+ ```
130
+
131
+ ### Interactive Mode
132
+
133
+ For full tool access (approvals, multi-turn interactions, etc.):
134
+
135
+ ```bash
136
+ ❯ claude → /i
137
+
138
+ ▶ Starting Claude Code interactive mode...
139
+ Press Ctrl+] to detach • /exit to terminate
140
+
141
+ > (interact with Claude directly)
142
+ > (press Ctrl+]) # Detach back to aic
143
+
144
+ ⏸ Detached from Claude Code (still running)
145
+ Use /i to re-attach
146
+
147
+ ❯ claude → /i # Re-attach to same session
148
+ ↩ Re-attaching to Claude Code...
149
+ ```
150
+
151
+ **Key bindings in interactive mode:**
152
+ - `Ctrl+]` - Detach (tool keeps running)
153
+ - `/exit` - Terminate the tool session
154
+
155
+ > **Tip:** Use `//status` or `//cost` to quickly run tool commands—AIC will enter interactive mode, run the command, and you press `Ctrl+]` when done.
156
+
157
+ > **Note:** Messages exchanged while in interactive mode (after `/i`) are not captured for forwarding. Use regular mode for conversations you want to forward between tools.
158
+
159
+ ### Session Persistence
160
+
161
+ Sessions persist automatically:
162
+ - **Claude**: Uses `--continue` flag
163
+ - **Gemini**: Uses `--resume latest` flag
164
+
165
+ Your conversation context is maintained across messages.
166
+
167
+ ## CLI Options
168
+
169
+ ```bash
170
+ aic # Launch interactive session
171
+ aic tools # List available AI tools
172
+ aic config default # Show current default tool
173
+ aic config default gemini # Set Gemini as default tool
174
+ aic --version # Show version
175
+ aic --help # Show help
176
+ ```
177
+
178
+ ## Configuration
179
+
180
+ ### Default Tool
181
+
182
+ Set which tool loads by default when you start AIC²:
183
+
184
+ **Option 1: CLI command**
185
+ ```bash
186
+ aic config default gemini
187
+ ```
188
+
189
+ **Option 2: Inside AIC²**
190
+ ```
191
+ ❯ claude → /default gemini
192
+ ✓ Default tool set to "gemini". Will be used on next launch.
193
+ ```
194
+
195
+ **Option 3: Environment variable (temporary override)**
196
+ ```bash
197
+ AIC_DEFAULT_TOOL=gemini aic
198
+ ```
199
+
200
+ Configuration is stored in `~/.aic/config.json`.
201
+
202
+ ## Architecture
203
+
204
+ ```
205
+ src/
206
+ ├── adapters/
207
+ │ ├── base.ts # ToolAdapter interface
208
+ │ ├── claude.ts # Claude Code adapter
209
+ │ ├── gemini.ts # Gemini CLI adapter
210
+ │ └── template.ts.example # Template for new adapters
211
+ ├── sdk-session.ts # Interactive session logic
212
+ ├── index.ts # CLI entry point
213
+ └── utils.ts # Utilities
214
+ ```
215
+
216
+ ## Adding New Tools
217
+
218
+ AIC² is modular. To add a new AI CLI (e.g., OpenAI Codex):
219
+
220
+ 1. Copy the template: `cp src/adapters/template.ts.example src/adapters/codex.ts`
221
+ 2. Implement the `ToolAdapter` interface
222
+ 3. Register in `src/adapters/index.ts` and `src/index.ts`
223
+ 4. Add to `src/sdk-session.ts`
224
+
225
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed instructions.
226
+
227
+ ## Features
228
+
229
+ - ✅ **Colorful UI** - ASCII banner, colored prompts, status indicators
230
+ - ✅ **Rainbow animations** - Animated rainbow effect on slash commands
231
+ - ✅ **Spinner** - Visual feedback while waiting for responses
232
+ - ✅ **Session persistence** - Context maintained across messages
233
+ - ✅ **Interactive mode** - Full tool access with detach/reattach
234
+ - ✅ **Command menu** - Type `/` for autocomplete suggestions
235
+ - ✅ **Forward responses** - One command to send between tools
236
+ - ✅ **Modular adapters** - Easy to add new AI tools
237
+
238
+ ## Development
239
+
240
+ ```bash
241
+ # Development mode
242
+ npm run dev
243
+
244
+ # Build
245
+ npm run build
246
+
247
+ # Run
248
+ aic
249
+ ```
250
+
251
+ ## Testing
252
+
253
+ AIC² uses [Vitest](https://vitest.dev/) for testing.
254
+
255
+ ```bash
256
+ # Run tests once
257
+ npm test
258
+
259
+ # Run tests in watch mode (re-runs on file changes)
260
+ npm run test:watch
261
+ ```
262
+
263
+ ### What's Tested
264
+
265
+ | File | Tests | Description |
266
+ |------|-------|-------------|
267
+ | `src/utils.test.ts` | 17 | Pure utility functions: `stripAnsi`, `truncate`, `formatResponse` |
268
+ | `src/config.test.ts` | 18 | Config loading, saving, defaults, environment variable handling |
269
+
270
+ ### Adding Tests
271
+
272
+ Test files live alongside source files with a `.test.ts` suffix:
273
+ - `src/utils.ts` → `src/utils.test.ts`
274
+ - `src/config.ts` → `src/config.test.ts`
275
+
276
+ Tests are excluded from the build output (`dist/`) but are committed to git.
277
+
278
+ ## License
279
+
280
+ MIT
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Base interface for AI CLI tool adapters
3
+ */
4
+ export interface SendOptions {
5
+ /** Working directory for the tool */
6
+ cwd?: string;
7
+ /** Whether to continue the previous session (default: true) */
8
+ continueSession?: boolean;
9
+ /** Timeout in milliseconds */
10
+ timeout?: number;
11
+ /** Keep stdin open after command (for interactive sessions) */
12
+ keepStdinOpen?: boolean;
13
+ }
14
+ export interface ToolAdapter {
15
+ /** Unique name identifier for the tool */
16
+ readonly name: string;
17
+ /** Display name for the tool */
18
+ readonly displayName: string;
19
+ /** Check if the tool is installed and available */
20
+ isAvailable(): Promise<boolean>;
21
+ /** Send a prompt to the tool and get a response */
22
+ send(prompt: string, options?: SendOptions): Promise<string>;
23
+ /** Reset conversation context */
24
+ resetContext(): void;
25
+ /** Get the command that would be executed (for debugging) */
26
+ getCommand(prompt: string, options?: SendOptions): string[];
27
+ /** Check if there's an active session with this tool */
28
+ hasSession(): boolean;
29
+ /** Set whether there's an active session (for persistence) */
30
+ setHasSession(value: boolean): void;
31
+ }
32
+ /**
33
+ * Registry of available tool adapters
34
+ */
35
+ export declare class AdapterRegistry {
36
+ private adapters;
37
+ register(adapter: ToolAdapter): void;
38
+ get(name: string): ToolAdapter | undefined;
39
+ getAll(): ToolAdapter[];
40
+ getNames(): string[];
41
+ getAvailable(): Promise<ToolAdapter[]>;
42
+ }
43
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,gCAAgC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,mDAAmD;IACnD,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhC,mDAAmD;IACnD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7D,iCAAiC;IACjC,YAAY,IAAI,IAAI,CAAC;IAErB,6DAA6D;IAC7D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE,CAAC;IAE5D,wDAAwD;IACxD,UAAU,IAAI,OAAO,CAAC;IAEtB,8DAA8D;IAC9D,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAuC;IAEvD,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIpC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI1C,MAAM,IAAI,WAAW,EAAE;IAIvB,QAAQ,IAAI,MAAM,EAAE;IAId,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;CAS7C"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Registry of available tool adapters
3
+ */
4
+ export class AdapterRegistry {
5
+ adapters = new Map();
6
+ register(adapter) {
7
+ this.adapters.set(adapter.name, adapter);
8
+ }
9
+ get(name) {
10
+ return this.adapters.get(name);
11
+ }
12
+ getAll() {
13
+ return Array.from(this.adapters.values());
14
+ }
15
+ getNames() {
16
+ return Array.from(this.adapters.keys());
17
+ }
18
+ async getAvailable() {
19
+ const available = [];
20
+ for (const adapter of this.adapters.values()) {
21
+ if (await adapter.isAvailable()) {
22
+ available.push(adapter);
23
+ }
24
+ }
25
+ return available;
26
+ }
27
+ }
28
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAwCA;;GAEG;AACH,MAAM,OAAO,eAAe;IAClB,QAAQ,GAA6B,IAAI,GAAG,EAAE,CAAC;IAEvD,QAAQ,CAAC,OAAoB;QAC3B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,QAAQ;QACN,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,SAAS,GAAkB,EAAE,CAAC;QACpC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,MAAM,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;gBAChC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import { ToolAdapter, SendOptions } from './base.js';
2
+ /**
3
+ * Adapter for Claude Code CLI
4
+ *
5
+ * Claude Code supports:
6
+ * - Non-interactive mode via -p/--print flag
7
+ * - Output formats: text, json, stream-json
8
+ * - Session continuation via -c/--continue or -r/--resume
9
+ */
10
+ export declare class ClaudeAdapter implements ToolAdapter {
11
+ readonly name = "claude";
12
+ readonly displayName = "Claude Code";
13
+ private hasActiveSession;
14
+ isAvailable(): Promise<boolean>;
15
+ getCommand(prompt: string, options?: SendOptions): string[];
16
+ send(prompt: string, options?: SendOptions): Promise<string>;
17
+ resetContext(): void;
18
+ /** Check if there's an active session */
19
+ hasSession(): boolean;
20
+ /** Mark that a session exists (for loading from persisted state) */
21
+ setHasSession(value: boolean): void;
22
+ }
23
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/adapters/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGrD;;;;;;;GAOG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,WAAW,iBAAiB;IAErC,OAAO,CAAC,gBAAgB,CAAS;IAE3B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE;IA4BrD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBlE,YAAY,IAAI,IAAI;IAIpB,yCAAyC;IACzC,UAAU,IAAI,OAAO;IAIrB,oEAAoE;IACpE,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;CAGpC"}
@@ -0,0 +1,68 @@
1
+ import { runCommandPty, commandExists, stripAnsi } from '../utils.js';
2
+ /**
3
+ * Adapter for Claude Code CLI
4
+ *
5
+ * Claude Code supports:
6
+ * - Non-interactive mode via -p/--print flag
7
+ * - Output formats: text, json, stream-json
8
+ * - Session continuation via -c/--continue or -r/--resume
9
+ */
10
+ export class ClaudeAdapter {
11
+ name = 'claude';
12
+ displayName = 'Claude Code';
13
+ hasActiveSession = false;
14
+ async isAvailable() {
15
+ return commandExists('claude');
16
+ }
17
+ getCommand(prompt, options) {
18
+ // For slash commands, run without -p to access Claude's internal commands
19
+ const isSlashCommand = prompt.startsWith('/');
20
+ const args = [];
21
+ if (!isSlashCommand) {
22
+ args.push('-p'); // Print mode for regular prompts
23
+ }
24
+ // Continue previous session if we've already made a call
25
+ const shouldContinue = options?.continueSession !== false && this.hasActiveSession;
26
+ if (shouldContinue) {
27
+ args.push('--continue');
28
+ }
29
+ if (options?.cwd) {
30
+ args.push('--add-dir', options.cwd);
31
+ }
32
+ // Add the prompt as the last argument (only for non-slash commands in print mode)
33
+ if (!isSlashCommand) {
34
+ args.push(prompt);
35
+ }
36
+ return ['claude', ...args];
37
+ }
38
+ async send(prompt, options) {
39
+ const isSlashCommand = prompt.startsWith('/');
40
+ const args = this.getCommand(prompt, options).slice(1); // Remove 'claude' from start
41
+ console.log(''); // Add newline before output
42
+ const result = await runCommandPty('claude', args, {
43
+ cwd: options?.cwd || process.cwd(),
44
+ keepStdinOpen: options?.keepStdinOpen,
45
+ // For slash commands, we'll write the command after Claude starts
46
+ initialInput: isSlashCommand ? prompt + '\n' : undefined,
47
+ });
48
+ if (result.exitCode !== 0 && !isSlashCommand) {
49
+ throw new Error(`Claude Code exited with code ${result.exitCode}`);
50
+ }
51
+ // Mark that we now have an active session
52
+ this.hasActiveSession = true;
53
+ // Return the captured output (strip ANSI for storage, but it was displayed with colors)
54
+ return stripAnsi(result.output).trim();
55
+ }
56
+ resetContext() {
57
+ this.hasActiveSession = false;
58
+ }
59
+ /** Check if there's an active session */
60
+ hasSession() {
61
+ return this.hasActiveSession;
62
+ }
63
+ /** Mark that a session exists (for loading from persisted state) */
64
+ setHasSession(value) {
65
+ this.hasActiveSession = value;
66
+ }
67
+ }
68
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/adapters/claude.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEtE;;;;;;;GAOG;AACH,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,QAAQ,CAAC;IAChB,WAAW,GAAG,aAAa,CAAC;IAE7B,gBAAgB,GAAG,KAAK,CAAC;IAEjC,KAAK,CAAC,WAAW;QACf,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,MAAc,EAAE,OAAqB;QAC9C,0EAA0E;QAC1E,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAE9C,MAAM,IAAI,GAAa,EAAE,CAAC;QAE1B,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,iCAAiC;QACpD,CAAC;QAED,yDAAyD;QACzD,MAAM,cAAc,GAAG,OAAO,EAAE,eAAe,KAAK,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC;QACnF,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,OAAO,EAAE,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QAED,kFAAkF;QAClF,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;QAED,OAAO,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,OAAqB;QAC9C,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B;QAErF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,4BAA4B;QAE7C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE;YACjD,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;YAClC,aAAa,EAAE,OAAO,EAAE,aAAa;YACrC,kEAAkE;YAClE,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS;SACzD,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,0CAA0C;QAC1C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAE7B,wFAAwF;QACxF,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAED,YAAY;QACV,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;IAED,yCAAyC;IACzC,UAAU;QACR,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED,oEAAoE;IACpE,aAAa,CAAC,KAAc;QAC1B,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ import { ToolAdapter, SendOptions } from './base.js';
2
+ /**
3
+ * Adapter for Gemini CLI
4
+ *
5
+ * Gemini CLI supports:
6
+ * - Non-interactive mode via positional query argument
7
+ * - Output formats: text, json, stream-json (via -o/--output-format)
8
+ * - Session resume via -r/--resume
9
+ * - YOLO mode via -y/--yolo for auto-approval
10
+ */
11
+ export declare class GeminiAdapter implements ToolAdapter {
12
+ readonly name = "gemini";
13
+ readonly displayName = "Gemini CLI";
14
+ private hasActiveSession;
15
+ isAvailable(): Promise<boolean>;
16
+ getCommand(prompt: string, options?: SendOptions): string[];
17
+ send(prompt: string, options?: SendOptions): Promise<string>;
18
+ resetContext(): void;
19
+ /** Check if there's an active session */
20
+ hasSession(): boolean;
21
+ /** Mark that a session exists (for loading from persisted state) */
22
+ setHasSession(value: boolean): void;
23
+ }
24
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGrD;;;;;;;;GAQG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,WAAW,gBAAgB;IAEpC,OAAO,CAAC,gBAAgB,CAAS;IAE3B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,EAAE;IAmBrD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAqBlE,YAAY,IAAI,IAAI;IAIpB,yCAAyC;IACzC,UAAU,IAAI,OAAO;IAIrB,oEAAoE;IACpE,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;CAGpC"}
@@ -0,0 +1,59 @@
1
+ import { runCommandPty, commandExists, stripAnsi } from '../utils.js';
2
+ /**
3
+ * Adapter for Gemini CLI
4
+ *
5
+ * Gemini CLI supports:
6
+ * - Non-interactive mode via positional query argument
7
+ * - Output formats: text, json, stream-json (via -o/--output-format)
8
+ * - Session resume via -r/--resume
9
+ * - YOLO mode via -y/--yolo for auto-approval
10
+ */
11
+ export class GeminiAdapter {
12
+ name = 'gemini';
13
+ displayName = 'Gemini CLI';
14
+ hasActiveSession = false;
15
+ async isAvailable() {
16
+ return commandExists('gemini');
17
+ }
18
+ getCommand(prompt, options) {
19
+ const args = [];
20
+ // Resume previous session if we've already made a call
21
+ const shouldContinue = options?.continueSession !== false && this.hasActiveSession;
22
+ if (shouldContinue) {
23
+ args.push('--resume', 'latest');
24
+ }
25
+ if (options?.cwd) {
26
+ args.push('--include-directories', options.cwd);
27
+ }
28
+ // Add the prompt as the last argument (positional)
29
+ args.push(prompt);
30
+ return ['gemini', ...args];
31
+ }
32
+ async send(prompt, options) {
33
+ const args = this.getCommand(prompt, options).slice(1); // Remove 'gemini' from start
34
+ console.log(''); // Add newline before output
35
+ const result = await runCommandPty('gemini', args, {
36
+ cwd: options?.cwd || process.cwd(),
37
+ keepStdinOpen: options?.keepStdinOpen,
38
+ });
39
+ if (result.exitCode !== 0) {
40
+ throw new Error(`Gemini CLI exited with code ${result.exitCode}`);
41
+ }
42
+ // Mark that we now have an active session
43
+ this.hasActiveSession = true;
44
+ // Return the captured output (strip ANSI for storage, but it was displayed with colors)
45
+ return stripAnsi(result.output).trim();
46
+ }
47
+ resetContext() {
48
+ this.hasActiveSession = false;
49
+ }
50
+ /** Check if there's an active session */
51
+ hasSession() {
52
+ return this.hasActiveSession;
53
+ }
54
+ /** Mark that a session exists (for loading from persisted state) */
55
+ setHasSession(value) {
56
+ this.hasActiveSession = value;
57
+ }
58
+ }
59
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEtE;;;;;;;;GAQG;AACH,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,QAAQ,CAAC;IAChB,WAAW,GAAG,YAAY,CAAC;IAE5B,gBAAgB,GAAG,KAAK,CAAC;IAEjC,KAAK,CAAC,WAAW;QACf,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED,UAAU,CAAC,MAAc,EAAE,OAAqB;QAC9C,MAAM,IAAI,GAAa,EAAE,CAAC;QAE1B,uDAAuD;QACvD,MAAM,cAAc,GAAG,OAAO,EAAE,eAAe,KAAK,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC;QACnF,IAAI,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;QAED,IAAI,OAAO,EAAE,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;QAED,mDAAmD;QACnD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAElB,OAAO,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,OAAqB;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B;QAErF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,4BAA4B;QAE7C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE;YACjD,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;YAClC,aAAa,EAAE,OAAO,EAAE,aAAa;SACtC,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,0CAA0C;QAC1C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAE7B,wFAAwF;QACxF,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,CAAC;IAED,YAAY;QACV,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;IAED,yCAAyC;IACzC,UAAU;QACR,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED,oEAAoE;IACpE,aAAa,CAAC,KAAc;QAC1B,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAChC,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export { ToolAdapter, SendOptions, AdapterRegistry } from './base.js';
2
+ export { ClaudeAdapter } from './claude.js';
3
+ export { GeminiAdapter } from './gemini.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { AdapterRegistry } from './base.js';
2
+ export { ClaudeAdapter } from './claude.js';
3
+ export { GeminiAdapter } from './gemini.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,eAAe,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,42 @@
1
+ export interface ToolConfig {
2
+ command?: string;
3
+ defaultFlags?: string[];
4
+ }
5
+ export interface Config {
6
+ defaultTool: string;
7
+ tools: {
8
+ [name: string]: ToolConfig;
9
+ };
10
+ }
11
+ /**
12
+ * Get the config directory path
13
+ */
14
+ export declare function getConfigDir(): string;
15
+ /**
16
+ * Get the config file path
17
+ */
18
+ export declare function getConfigPath(): string;
19
+ /**
20
+ * Ensure the config directory exists
21
+ */
22
+ export declare function ensureConfigDir(): void;
23
+ /**
24
+ * Load configuration from disk, or return defaults
25
+ */
26
+ export declare function loadConfig(): Config;
27
+ /**
28
+ * Save configuration to disk
29
+ */
30
+ export declare function saveConfig(config: Config): void;
31
+ /**
32
+ * Get the default tool (checks env var first, then config)
33
+ */
34
+ export declare function getDefaultTool(): string;
35
+ /**
36
+ * Set the default tool and save to config
37
+ */
38
+ export declare function setDefaultTool(tool: string): {
39
+ success: boolean;
40
+ message: string;
41
+ };
42
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QACL,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;KAC5B,CAAC;CACH;AAgBD;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAKtC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAsBnC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAI/C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAUvC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAmBlF"}