ai-sdk-provider-gemini-cli-agentic 0.1.0 → 0.1.2

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,296 @@
1
+ # ai-sdk-provider-gemini-cli-agentic
2
+
3
+ [![npm version](https://img.shields.io/npm/v/ai-sdk-provider-gemini-cli-agentic.svg)](https://www.npmjs.com/package/ai-sdk-provider-gemini-cli-agentic)
4
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
5
+
6
+ [AI SDK](https://sdk.vercel.ai/docs) v6 provider for [Google Gemini CLI](https://github.com/google-gemini/gemini-cli) agentic mode.
7
+
8
+ This provider spawns `gemini` as a subprocess with `--output-format stream-json`, enabling full agentic capabilities like file system access, code editing, and tool execution through the AI SDK interface.
9
+
10
+ ## Features
11
+
12
+ - **Full Agentic Support**: Access all Gemini CLI tools (file system, code editing, shell commands, etc.)
13
+ - **Streaming**: Real-time streaming of text and tool calls
14
+ - **Tool Streaming**: Watch tool calls and results as they happen
15
+ - **Approval Modes**: Control tool approval behavior (`default`, `auto_edit`, `yolo`)
16
+ - **Sandbox Mode**: Run in a sandboxed environment for safety
17
+ - **Session Resume**: Resume previous sessions
18
+ - **MCP Server Support**: Integrate with MCP servers
19
+ - **Custom Logging**: Built-in logging with customization support
20
+ - **AI SDK v6 Compatible**: Works with `generateText`, `streamText`, and `streamObject`
21
+
22
+ ## Requirements
23
+
24
+ - [Gemini CLI](https://github.com/google-gemini/gemini-cli) installed and authenticated (or use `allowNpx: true`)
25
+ - Node.js 18+
26
+ - AI SDK v6+
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install ai-sdk-provider-gemini-cli-agentic ai
32
+ ```
33
+
34
+ ### Install Gemini CLI (Optional)
35
+
36
+ **Option 1**: Install globally (recommended for frequent use):
37
+
38
+ ```bash
39
+ npm install -g @google/gemini-cli
40
+ ```
41
+
42
+ **Option 2**: Use `allowNpx: true` to run via npx (no global install needed):
43
+
44
+ ```javascript
45
+ geminiCli('auto', { allowNpx: true })
46
+ ```
47
+
48
+ Then authenticate:
49
+
50
+ ```bash
51
+ gemini auth login
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ ```javascript
57
+ import { geminiCli } from 'ai-sdk-provider-gemini-cli-agentic';
58
+ import { generateText, streamText } from 'ai';
59
+
60
+ // Basic usage
61
+ const { text } = await generateText({
62
+ model: geminiCli('gemini-2.5-flash'),
63
+ prompt: 'List files in the current directory',
64
+ });
65
+
66
+ // Streaming
67
+ const result = streamText({
68
+ model: geminiCli('auto', { cwd: process.cwd() }),
69
+ prompt: 'Explain the structure of this project',
70
+ });
71
+
72
+ for await (const chunk of result.textStream) {
73
+ process.stdout.write(chunk);
74
+ }
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ ### Provider Settings
80
+
81
+ ```javascript
82
+ import { createGeminiCli } from 'ai-sdk-provider-gemini-cli-agentic';
83
+
84
+ const provider = createGeminiCli({
85
+ defaultSettings: {
86
+ geminiPath: '/usr/local/bin/gemini', // Custom CLI path
87
+ cwd: '/path/to/project', // Working directory
88
+ approvalMode: 'auto_edit', // Approval mode
89
+ sandbox: true, // Enable sandbox
90
+ verbose: true, // Verbose output
91
+ },
92
+ });
93
+
94
+ const model = provider('gemini-2.5-flash');
95
+ ```
96
+
97
+ ### Per-Model Settings
98
+
99
+ ```javascript
100
+ const model = geminiCli('gemini-2.5-flash', {
101
+ cwd: process.cwd(),
102
+ approvalMode: 'yolo',
103
+ sandbox: false,
104
+ includeDirectories: ['../shared-lib'],
105
+ allowedTools: ['read_file', 'write_file', 'list_directory'],
106
+ allowedMcpServerNames: ['filesystem'],
107
+ resume: 'latest', // or session index number
108
+ env: {
109
+ MY_VAR: 'value',
110
+ },
111
+ });
112
+ ```
113
+
114
+ ### Settings Reference
115
+
116
+ | Setting | Type | Description |
117
+ |---------|------|-------------|
118
+ | `geminiPath` | `string` | Path to Gemini CLI executable (default: `'gemini'`) |
119
+ | `allowNpx` | `boolean` | Allow falling back to `npx @google/gemini-cli` if CLI not found |
120
+ | `cwd` | `string` | Working directory for CLI operations |
121
+ | `approvalMode` | `'default' \| 'auto_edit' \| 'yolo'` | Tool approval behavior |
122
+ | `yolo` | `boolean` | Auto-approve all operations (alias for `approvalMode: 'yolo'`) |
123
+ | `sandbox` | `boolean` | Enable sandbox mode |
124
+ | `includeDirectories` | `string[]` | Additional directories to include |
125
+ | `allowedTools` | `string[]` | Tools allowed without confirmation |
126
+ | `allowedMcpServerNames` | `string[]` | Allowed MCP server names |
127
+ | `resume` | `string \| boolean` | Resume session (`'latest'`, index, or `true`) |
128
+ | `model` | `string` | Override model name |
129
+ | `env` | `Record<string, string>` | Environment variables |
130
+ | `verbose` | `boolean` | Enable verbose logging |
131
+ | `logger` | `Logger \| false` | Custom logger or `false` to disable |
132
+
133
+ ## Approval Modes
134
+
135
+ | Mode | Description |
136
+ |------|-------------|
137
+ | `default` | Prompt for approval on each tool operation |
138
+ | `auto_edit` | Auto-approve file editing tools |
139
+ | `yolo` | Auto-approve all tools (use with caution) |
140
+
141
+ ```javascript
142
+ // Conservative (default)
143
+ geminiCli('auto', { approvalMode: 'default' })
144
+
145
+ // Auto-approve edits only
146
+ geminiCli('auto', { approvalMode: 'auto_edit' })
147
+
148
+ // Full automation (dangerous!)
149
+ geminiCli('auto', { approvalMode: 'yolo' })
150
+ // or
151
+ geminiCli('auto', { yolo: true })
152
+ ```
153
+
154
+ ## Streaming with Tool Calls
155
+
156
+ ```javascript
157
+ import { geminiCli } from 'ai-sdk-provider-gemini-cli-agentic';
158
+ import { streamText } from 'ai';
159
+
160
+ const result = streamText({
161
+ model: geminiCli('auto', { cwd: process.cwd() }),
162
+ prompt: 'Read package.json and explain the dependencies',
163
+ });
164
+
165
+ for await (const part of result.fullStream) {
166
+ switch (part.type) {
167
+ case 'text-delta':
168
+ process.stdout.write(part.textDelta);
169
+ break;
170
+ case 'tool-call':
171
+ console.log(`\n🔧 Tool: ${part.toolName}`);
172
+ console.log(` Args: ${JSON.stringify(part.args)}`);
173
+ break;
174
+ case 'tool-result':
175
+ console.log(` Result: ${part.result?.slice(0, 100)}...`);
176
+ break;
177
+ }
178
+ }
179
+ ```
180
+
181
+ ## Logging
182
+
183
+ ### Default Logger
184
+
185
+ ```javascript
186
+ const model = geminiCli('auto', {
187
+ verbose: true, // Enable info-level logging
188
+ });
189
+ ```
190
+
191
+ ### Custom Logger
192
+
193
+ ```javascript
194
+ const model = geminiCli('auto', {
195
+ logger: {
196
+ debug: (msg) => console.debug('[DEBUG]', msg),
197
+ info: (msg) => console.info('[INFO]', msg),
198
+ warn: (msg) => console.warn('[WARN]', msg),
199
+ error: (msg) => console.error('[ERROR]', msg),
200
+ },
201
+ });
202
+ ```
203
+
204
+ ### Disable Logging
205
+
206
+ ```javascript
207
+ const model = geminiCli('auto', {
208
+ logger: false,
209
+ });
210
+ ```
211
+
212
+ ## Error Handling
213
+
214
+ ```javascript
215
+ import { geminiCli, isAuthenticationError } from 'ai-sdk-provider-gemini-cli-agentic';
216
+ import { generateText } from 'ai';
217
+
218
+ try {
219
+ const { text } = await generateText({
220
+ model: geminiCli('auto'),
221
+ prompt: 'Hello',
222
+ });
223
+ } catch (error) {
224
+ if (isAuthenticationError(error)) {
225
+ console.error('Please run: gemini auth login');
226
+ } else {
227
+ console.error('Error:', error.message);
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Models
233
+
234
+ The model ID is passed directly to Gemini CLI's `-m` flag:
235
+
236
+ ```javascript
237
+ // Use auto model selection
238
+ geminiCli('auto')
239
+
240
+ // Specific models
241
+ geminiCli('gemini-2.5-flash')
242
+ geminiCli('gemini-2.5-pro')
243
+ geminiCli('gemini-3')
244
+ ```
245
+
246
+ ## Examples
247
+
248
+ See the [`examples/`](./examples/) directory for more usage examples:
249
+
250
+ - `basic-usage.mjs` - Simple text generation
251
+ - `streaming.mjs` - Streaming responses
252
+ - `streaming-tool-calls.mjs` - Watching tool execution
253
+ - `conversation-history.mjs` - Multi-turn conversations
254
+ - `custom-config.mjs` - Advanced configuration
255
+ - `permissions-and-sandbox.mjs` - Approval modes and sandbox
256
+ - `error-handling.mjs` - Error handling patterns
257
+ - `logging-*.mjs` - Various logging configurations
258
+
259
+ Run examples:
260
+
261
+ ```bash
262
+ cd examples
263
+ node basic-usage.mjs
264
+ ```
265
+
266
+ ## Limitations
267
+
268
+ - **Interactive Prompts**: The provider cannot handle Gemini CLI's interactive approval prompts. Use `approvalMode: 'yolo'` or `approvalMode: 'auto_edit'` for automation.
269
+ - **No Image Input**: Image/multimodal input is not supported (text only).
270
+ - **No Embedding/Image Models**: Only language model is supported (`embeddingModel` and `imageModel` throw errors).
271
+ - **Subprocess Overhead**: Each call spawns a new subprocess; not suitable for high-frequency requests.
272
+
273
+ ## CLI Flags Mapping
274
+
275
+ | Provider Setting | CLI Flag |
276
+ |-----------------|----------|
277
+ | `cwd` | Process working directory |
278
+ | `approvalMode` | `--approval-mode` |
279
+ | `yolo` | `-y` / `--yolo` |
280
+ | `sandbox` | `-s` / `--sandbox` |
281
+ | `includeDirectories` | `--include-directories` |
282
+ | `allowedTools` | `--allowed-tools` |
283
+ | `allowedMcpServerNames` | `--allowed-mcp-server-names` |
284
+ | `resume` | `-r` / `--resume` |
285
+ | `model` | `-m` / `--model` |
286
+ | *(always set)* | `--output-format stream-json` |
287
+
288
+ ## License
289
+
290
+ MIT
291
+
292
+ ## Related
293
+
294
+ - [AI SDK](https://sdk.vercel.ai/docs) - The AI SDK by Vercel
295
+ - [Gemini CLI](https://github.com/google-gemini/gemini-cli) - Google's Gemini CLI
296
+ - [ai-sdk-provider-codex-cli](https://github.com/J3n5en/ai-sdk-provider-codex-cli) - Similar provider for OpenAI Codex CLI
package/dist/index.cjs CHANGED
@@ -216,6 +216,17 @@ function mapGeminiFinishReason(status) {
216
216
  return { unified: "stop", raw: status };
217
217
  }
218
218
  }
219
+ function resolveGeminiPath(explicitPath, allowNpx) {
220
+ if (explicitPath) {
221
+ const lower = explicitPath.toLowerCase();
222
+ if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
223
+ return { cmd: "node", args: [explicitPath] };
224
+ }
225
+ return { cmd: explicitPath, args: [] };
226
+ }
227
+ if (allowNpx) return { cmd: "npx", args: ["-y", "@google/gemini-cli"] };
228
+ return { cmd: "gemini", args: [] };
229
+ }
219
230
  var GeminiCliLanguageModel = class {
220
231
  specificationVersion = "v3";
221
232
  provider = "gemini-cli";
@@ -248,9 +259,11 @@ var GeminiCliLanguageModel = class {
248
259
  allowedMcpServerNames: providerOptions.allowedMcpServerNames ?? this.settings.allowedMcpServerNames
249
260
  };
250
261
  }
251
- buildArgs(promptText, settings = this.settings) {
252
- const cmd = settings.geminiPath ?? "gemini";
253
- const args = [];
262
+ buildArgs(settings = this.settings) {
263
+ const base = resolveGeminiPath(settings.geminiPath, settings.allowNpx);
264
+ const cmd = base.cmd;
265
+ const args = [...base.args];
266
+ const shell = process.platform === "win32" && (base.cmd === "npx" || base.cmd === "gemini");
254
267
  args.push("--output-format", "stream-json");
255
268
  if (this.modelId && this.modelId !== "auto") {
256
269
  args.push("-m", this.modelId);
@@ -281,12 +294,12 @@ var GeminiCliLanguageModel = class {
281
294
  args.push("--resume", "latest");
282
295
  }
283
296
  }
284
- args.push(promptText);
297
+ args.push("-");
285
298
  const env = {
286
299
  ...process.env,
287
300
  ...settings.env || {}
288
301
  };
289
- return { cmd, args, env, cwd: settings.cwd };
302
+ return { cmd, args, env, cwd: settings.cwd, shell };
290
303
  }
291
304
  mapWarnings(options) {
292
305
  const unsupported = [];
@@ -333,14 +346,16 @@ var GeminiCliLanguageModel = class {
333
346
  schema: geminiCliProviderOptionsSchema
334
347
  });
335
348
  const effectiveSettings = this.mergeSettings(providerOptions);
336
- const { cmd, args, env, cwd } = this.buildArgs(promptText, effectiveSettings);
337
- this.logger.debug(`[gemini-cli] Executing: ${cmd} ${args.slice(0, -1).join(" ")} [prompt]`);
349
+ const { cmd, args, env, cwd, shell } = this.buildArgs(effectiveSettings);
350
+ this.logger.debug(`[gemini-cli] Executing: ${cmd} ${args.join(" ")}`);
338
351
  let text = "";
339
352
  let usage = createEmptyUsage();
340
353
  let finishReason = { unified: "stop", raw: void 0 };
341
354
  const content = [];
342
355
  const toolResults = /* @__PURE__ */ new Map();
343
- const child = child_process.spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
356
+ const child = child_process.spawn(cmd, args, { env, cwd, shell, stdio: ["pipe", "pipe", "pipe"] });
357
+ child.stdin.write(promptText);
358
+ child.stdin.end();
344
359
  let onAbort;
345
360
  if (options.abortSignal) {
346
361
  if (options.abortSignal.aborted) {
@@ -465,14 +480,16 @@ var GeminiCliLanguageModel = class {
465
480
  schema: geminiCliProviderOptionsSchema
466
481
  });
467
482
  const effectiveSettings = this.mergeSettings(providerOptions);
468
- const { cmd, args, env, cwd } = this.buildArgs(promptText, effectiveSettings);
469
- this.logger.debug(`[gemini-cli] Streaming: ${cmd} ${args.slice(0, -1).join(" ")} [prompt]`);
483
+ const { cmd, args, env, cwd, shell } = this.buildArgs(effectiveSettings);
484
+ this.logger.debug(`[gemini-cli] Streaming: ${cmd} ${args.join(" ")}`);
470
485
  const model = this;
471
486
  const abortSignal = options.abortSignal;
472
487
  const stream = new ReadableStream({
473
488
  start(controller) {
474
489
  const startTime = Date.now();
475
- const child = child_process.spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
490
+ const child = child_process.spawn(cmd, args, { env, cwd, shell, stdio: ["pipe", "pipe", "pipe"] });
491
+ child.stdin.write(promptText);
492
+ child.stdin.end();
476
493
  controller.enqueue({ type: "stream-start", warnings });
477
494
  let stderr = "";
478
495
  let lastUsage;
@@ -609,6 +626,7 @@ var loggerFunctionSchema = zod.z.object({
609
626
  });
610
627
  var settingsSchema = zod.z.object({
611
628
  geminiPath: zod.z.string().optional(),
629
+ allowNpx: zod.z.boolean().optional(),
612
630
  cwd: zod.z.string().optional(),
613
631
  includeDirectories: zod.z.array(zod.z.string().min(1)).optional(),
614
632
  approvalMode: zod.z.enum(["default", "auto_edit", "yolo"]).optional(),
package/dist/index.d.cts CHANGED
@@ -25,6 +25,10 @@ interface GeminiCliSettings {
25
25
  * @default 'gemini' (uses system PATH)
26
26
  */
27
27
  geminiPath?: string;
28
+ /**
29
+ * Allow falling back to `npx @google/gemini-cli` if the binary cannot be resolved.
30
+ */
31
+ allowNpx?: boolean;
28
32
  /**
29
33
  * Working directory for CLI operations.
30
34
  */
package/dist/index.d.ts CHANGED
@@ -25,6 +25,10 @@ interface GeminiCliSettings {
25
25
  * @default 'gemini' (uses system PATH)
26
26
  */
27
27
  geminiPath?: string;
28
+ /**
29
+ * Allow falling back to `npx @google/gemini-cli` if the binary cannot be resolved.
30
+ */
31
+ allowNpx?: boolean;
28
32
  /**
29
33
  * Working directory for CLI operations.
30
34
  */
package/dist/index.js CHANGED
@@ -214,6 +214,17 @@ function mapGeminiFinishReason(status) {
214
214
  return { unified: "stop", raw: status };
215
215
  }
216
216
  }
217
+ function resolveGeminiPath(explicitPath, allowNpx) {
218
+ if (explicitPath) {
219
+ const lower = explicitPath.toLowerCase();
220
+ if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
221
+ return { cmd: "node", args: [explicitPath] };
222
+ }
223
+ return { cmd: explicitPath, args: [] };
224
+ }
225
+ if (allowNpx) return { cmd: "npx", args: ["-y", "@google/gemini-cli"] };
226
+ return { cmd: "gemini", args: [] };
227
+ }
217
228
  var GeminiCliLanguageModel = class {
218
229
  specificationVersion = "v3";
219
230
  provider = "gemini-cli";
@@ -246,9 +257,11 @@ var GeminiCliLanguageModel = class {
246
257
  allowedMcpServerNames: providerOptions.allowedMcpServerNames ?? this.settings.allowedMcpServerNames
247
258
  };
248
259
  }
249
- buildArgs(promptText, settings = this.settings) {
250
- const cmd = settings.geminiPath ?? "gemini";
251
- const args = [];
260
+ buildArgs(settings = this.settings) {
261
+ const base = resolveGeminiPath(settings.geminiPath, settings.allowNpx);
262
+ const cmd = base.cmd;
263
+ const args = [...base.args];
264
+ const shell = process.platform === "win32" && (base.cmd === "npx" || base.cmd === "gemini");
252
265
  args.push("--output-format", "stream-json");
253
266
  if (this.modelId && this.modelId !== "auto") {
254
267
  args.push("-m", this.modelId);
@@ -279,12 +292,12 @@ var GeminiCliLanguageModel = class {
279
292
  args.push("--resume", "latest");
280
293
  }
281
294
  }
282
- args.push(promptText);
295
+ args.push("-");
283
296
  const env = {
284
297
  ...process.env,
285
298
  ...settings.env || {}
286
299
  };
287
- return { cmd, args, env, cwd: settings.cwd };
300
+ return { cmd, args, env, cwd: settings.cwd, shell };
288
301
  }
289
302
  mapWarnings(options) {
290
303
  const unsupported = [];
@@ -331,14 +344,16 @@ var GeminiCliLanguageModel = class {
331
344
  schema: geminiCliProviderOptionsSchema
332
345
  });
333
346
  const effectiveSettings = this.mergeSettings(providerOptions);
334
- const { cmd, args, env, cwd } = this.buildArgs(promptText, effectiveSettings);
335
- this.logger.debug(`[gemini-cli] Executing: ${cmd} ${args.slice(0, -1).join(" ")} [prompt]`);
347
+ const { cmd, args, env, cwd, shell } = this.buildArgs(effectiveSettings);
348
+ this.logger.debug(`[gemini-cli] Executing: ${cmd} ${args.join(" ")}`);
336
349
  let text = "";
337
350
  let usage = createEmptyUsage();
338
351
  let finishReason = { unified: "stop", raw: void 0 };
339
352
  const content = [];
340
353
  const toolResults = /* @__PURE__ */ new Map();
341
- const child = spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
354
+ const child = spawn(cmd, args, { env, cwd, shell, stdio: ["pipe", "pipe", "pipe"] });
355
+ child.stdin.write(promptText);
356
+ child.stdin.end();
342
357
  let onAbort;
343
358
  if (options.abortSignal) {
344
359
  if (options.abortSignal.aborted) {
@@ -463,14 +478,16 @@ var GeminiCliLanguageModel = class {
463
478
  schema: geminiCliProviderOptionsSchema
464
479
  });
465
480
  const effectiveSettings = this.mergeSettings(providerOptions);
466
- const { cmd, args, env, cwd } = this.buildArgs(promptText, effectiveSettings);
467
- this.logger.debug(`[gemini-cli] Streaming: ${cmd} ${args.slice(0, -1).join(" ")} [prompt]`);
481
+ const { cmd, args, env, cwd, shell } = this.buildArgs(effectiveSettings);
482
+ this.logger.debug(`[gemini-cli] Streaming: ${cmd} ${args.join(" ")}`);
468
483
  const model = this;
469
484
  const abortSignal = options.abortSignal;
470
485
  const stream = new ReadableStream({
471
486
  start(controller) {
472
487
  const startTime = Date.now();
473
- const child = spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
488
+ const child = spawn(cmd, args, { env, cwd, shell, stdio: ["pipe", "pipe", "pipe"] });
489
+ child.stdin.write(promptText);
490
+ child.stdin.end();
474
491
  controller.enqueue({ type: "stream-start", warnings });
475
492
  let stderr = "";
476
493
  let lastUsage;
@@ -607,6 +624,7 @@ var loggerFunctionSchema = z.object({
607
624
  });
608
625
  var settingsSchema = z.object({
609
626
  geminiPath: z.string().optional(),
627
+ allowNpx: z.boolean().optional(),
610
628
  cwd: z.string().optional(),
611
629
  includeDirectories: z.array(z.string().min(1)).optional(),
612
630
  approvalMode: z.enum(["default", "auto_edit", "yolo"]).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-sdk-provider-gemini-cli-agentic",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "AI SDK v6 provider for Google Gemini CLI agentic mode",
5
5
  "keywords": [
6
6
  "ai-sdk",