Gemini CLI SDK

The Gemini CLI SDK provides Java integration with Google’s Gemini command-line coding agent, bringing Google’s AI coding capabilities to Spring applications.

1. Overview

Gemini CLI is Google’s autonomous coding agent that can understand and modify codebases using Google’s Gemini language models. The Spring AI Gemini integration makes these capabilities accessible from Java applications.

2. Installation

2.1. Prerequisites

  1. Install Gemini CLI:

    npm install -g @google/generative-ai-cli
  2. Set API Key:

    export GEMINI_API_KEY="your-api-key-here"

    Get your API key from the Google AI Studio.

  3. Verify Installation:

    gemini --version

2.2. Maven Dependencies

Add the following dependencies to your pom.xml:

<dependencies>
    <!-- Core AgentClient API -->
    <dependency>
        <groupId>org.springaicommunity.agents</groupId>
        <artifactId>spring-ai-agent-client</artifactId>
        <version>0.1.0-SNAPSHOT</version>
    </dependency>

    <!-- Gemini CLI integration -->
    <dependency>
        <groupId>org.springaicommunity.agents</groupId>
        <artifactId>spring-ai-gemini</artifactId>
        <version>0.1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

3. Basic Usage

3.1. Quick Start

import org.springaicommunity.agents.client.AgentClient;
import org.springaicommunity.agents.client.AgentClientResponse;
import org.springaicommunity.agents.gemini.GeminiAgentModel;
import org.springaicommunity.agents.gemini.GeminiAgentOptions;
import org.springaicommunity.agents.gemini.sdk.GeminiClient;

import java.nio.file.Paths;

public class GeminiExample {
    public static void main(String[] args) {
        // 1. Create the Gemini client
        GeminiClient geminiClient = GeminiClient.create(
            Paths.get(System.getProperty("user.dir"))
        );

        // 2. Configure agent options
        GeminiAgentOptions options = GeminiAgentOptions.builder()
            .model("gemini-1.5-pro")
            .yolo(true)
            .build();

        // 3. Create the agent model
        GeminiAgentModel agentModel = new GeminiAgentModel(geminiClient, options);

        // 4. Create AgentClient
        AgentClient agentClient = AgentClient.create(agentModel);

        // 5. Execute a goal
        AgentClientResponse response = agentClient.run(
            "Create a simple Spring Boot REST controller for managing books"
        );

        System.out.println("Result: " + response.getResult());
        System.out.println("Success: " + response.isSuccessful());
    }
}

4. Configuration

4.1. GeminiClient

The GeminiClient manages communication with the Gemini CLI:

// Create with default working directory
GeminiClient client = GeminiClient.create();

// Create with specific working directory
Path projectPath = Paths.get("/path/to/project");
GeminiClient client = GeminiClient.create(projectPath);

// Create with custom Gemini command path
GeminiClient client = GeminiClient.create(
    projectPath,
    "/custom/path/to/gemini"
);

4.2. GeminiAgentOptions

Configure Gemini-specific behavior:

GeminiAgentOptions options = GeminiAgentOptions.builder()
    // Model selection
    .model("gemini-1.5-pro")                // or "gemini-1.5-flash"

    // Execution settings
    .yolo(true)                             // Allow modifications
    .timeout(Duration.ofMinutes(10))        // Execution timeout
    .maxTokens(8192)                        // Response length limit

    // Output preferences
    .verbose(true)                          // Detailed logging
    .outputFormat("json")                   // Structured output

    // Google-specific options
    .temperature(0.3)                       // Creativity level
    .candidateCount(1)                      // Number of response candidates

    .build();

4.3. Model Options

Available Gemini models:

Model Description Best For

gemini-1.5-pro

Most capable model with 1M token context window

Large codebases, complex refactoring, architectural changes

gemini-1.5-flash

Faster model optimized for speed and efficiency

Quick fixes, simple tasks, rapid iteration

gemini-1.0-pro

Previous generation model

Legacy support, specific use cases

5. Advanced Features

5.1. Working Directory Management

Gemini CLI operates within a specific directory context:

// Configure working directory via client
GeminiClient client = GeminiClient.create(
    Paths.get("/path/to/project")
);

// Or via AgentClient fluent API
AgentClientResponse response = agentClient
    .goal("Add unit tests to the ProductService")
    .workingDirectory("/path/to/project")
    .run();

5.2. YOLO Mode

Control whether Gemini can make changes without confirmation:

// Development mode - allow changes
GeminiAgentOptions devOptions = GeminiAgentOptions.builder()
    .yolo(true)
    .build();

// Analysis mode - read-only
GeminiAgentOptions analysisOptions = GeminiAgentOptions.builder()
    .yolo(false)
    .build();

5.3. Temperature and Creativity

Control the creativity level of responses:

// Conservative (more deterministic)
GeminiAgentOptions conservativeOptions = GeminiAgentOptions.builder()
    .temperature(0.1)
    .build();

// Balanced (default)
GeminiAgentOptions balancedOptions = GeminiAgentOptions.builder()
    .temperature(0.3)
    .build();

// Creative (more diverse)
GeminiAgentOptions creativeOptions = GeminiAgentOptions.builder()
    .temperature(0.7)
    .build();

5.4. Context Window Optimization

Leverage Gemini’s large context window for complex projects:

GeminiAgentOptions largeContextOptions = GeminiAgentOptions.builder()
    .model("gemini-1.5-pro")  // 1M token context
    .maxTokens(100000)        // Large output
    .build();

// Handle entire project refactoring
AgentClientResponse response = agentClient
    .goal("Refactor this entire Spring Boot application to use reactive programming")
    .options(largeContextOptions)
    .run();

6. Error Handling

6.1. Common Exceptions

try {
    AgentClientResponse response = agentClient.run("Complex refactoring goal");

    if (!response.isSuccessful()) {
        System.err.println("Goal failed: " + response.getResult());
    }

} catch (GeminiExecutionException e) {
    // Gemini CLI process failed
    System.err.println("Gemini execution error: " + e.getMessage());

} catch (GeminiNotFoundException e) {
    // Gemini CLI not installed or not in PATH
    System.err.println("Gemini CLI not found: " + e.getMessage());

} catch (AgentTimeoutException e) {
    // Goal exceeded configured timeout
    System.err.println("Goal timed out: " + e.getTimeout());

} catch (AgentAuthenticationException e) {
    // Invalid or missing API key
    System.err.println("Authentication failed: " + e.getMessage());

} catch (GeminiQuotaExceededException e) {
    // API quota exceeded
    System.err.println("Quota exceeded: " + e.getMessage());
}

6.2. Retry Logic with Exponential Backoff

@Service
public class GeminiServiceWithRetry {

    private final AgentClient agentClient;
    private final RetryTemplate retryTemplate;

    public GeminiServiceWithRetry(AgentClient agentClient) {
        this.agentClient = agentClient;
        this.retryTemplate = RetryTemplate.builder()
            .maxAttempts(3)
            .exponentialBackoff(1000, 2, 10000)
            .retryOn(GeminiQuotaExceededException.class)
            .retryOn(GeminiExecutionException.class)
            .build();
    }

    public String generateCode(String requirements) {
        return retryTemplate.execute(context -> {
            AgentClientResponse response = agentClient.run(requirements);

            if (!response.isSuccessful()) {
                throw new GeminiExecutionException("Goal failed: " + response.getResult());
            }

            return response.getResult();
        });
    }
}

7. Spring Boot Integration

7.1. Auto-Configuration

Create a configuration class for Gemini:

@Configuration
@ConditionalOnProperty(name = "spring.ai.agent.gemini.enabled", havingValue = "true", matchIfMissing = true)
public class GeminiConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public GeminiClient geminiClient(@Value("${spring.ai.agent.gemini.working-directory:#{systemProperties['user.dir']}}") String workingDir) {
        return GeminiClient.create(Paths.get(workingDir));
    }

    @Bean
    @ConditionalOnMissingBean
    public GeminiAgentModel geminiAgentModel(
            GeminiClient client,
            GeminiAgentOptions options) {
        return new GeminiAgentModel(client, options);
    }

    @Bean
    @ConditionalOnMissingBean
    public GeminiAgentOptions geminiAgentOptions(GeminiProperties properties) {
        return GeminiAgentOptions.builder()
            .model(properties.getModel())
            .yolo(properties.isYolo())
            .timeout(properties.getTimeout())
            .maxTokens(properties.getMaxTokens())
            .temperature(properties.getTemperature())
            .verbose(properties.isVerbose())
            .build();
    }

    @Bean
    public AgentClient agentClient(GeminiAgentModel agentModel) {
        return AgentClient.create(agentModel);
    }
}

7.2. Application Properties

Configure Gemini via application.yml:

spring:
  ai:
    agent:
      gemini:
        enabled: true
        model: gemini-1.5-pro
        working-directory: /path/to/project
        yolo: false
        timeout: PT10M
        max-tokens: 8192
        temperature: 0.3
        verbose: true

Or application.properties:

spring.ai.agent.gemini.enabled=true
spring.ai.agent.gemini.model=gemini-1.5-pro
spring.ai.agent.gemini.working-directory=/path/to/project
spring.ai.agent.gemini.yolo=false
spring.ai.agent.gemini.timeout=PT10M
spring.ai.agent.gemini.max-tokens=8192
spring.ai.agent.gemini.temperature=0.3
spring.ai.agent.gemini.verbose=true

8. Google Cloud Integration

8.1. Service Account Authentication

For production deployments using Google Cloud:

spring:
  ai:
    agent:
      gemini:
        authentication:
          type: service-account
          service-account-file: /path/to/service-account.json
          project-id: your-gcp-project-id
@Configuration
@ConditionalOnProperty(name = "spring.ai.agent.gemini.authentication.type", havingValue = "service-account")
public class GeminiCloudConfiguration {

    @Bean
    public GoogleCredentials geminiCredentials(
            @Value("${spring.ai.agent.gemini.authentication.service-account-file}") String serviceAccountFile)
            throws IOException {
        return ServiceAccountCredentials.fromStream(
            new FileInputStream(serviceAccountFile)
        );
    }

    @Bean
    public GeminiClient geminiCloudClient(GoogleCredentials credentials) {
        return GeminiClient.builder()
            .credentials(credentials)
            .build();
    }
}

9. Best Practices

9.1. Goal Formulation for Google AI

Leverage Gemini’s strengths in understanding context and patterns:

// Good: Leverage Google's pattern recognition
agentClient.run("Apply Google's Java style guide to this codebase and fix all violations");

// Good: Use Gemini's architectural understanding
agentClient.run("Convert this monolithic application to microservices following Google Cloud best practices");

// Good: Leverage large context window
agentClient.run("Analyze this entire codebase and identify all security vulnerabilities");

9.2. Model Selection Strategy

Choose models based on goal complexity:

@Service
public class GeminiTaskRouter {

    public String executeGoal(String goal, GoalComplexity complexity) {
        GeminiAgentOptions options = switch (complexity) {
            case SIMPLE -> GeminiAgentOptions.builder()
                .model("gemini-1.5-flash")  // Fast for simple tasks
                .temperature(0.1)
                .build();

            case MODERATE -> GeminiAgentOptions.builder()
                .model("gemini-1.5-pro")    // Balanced
                .temperature(0.3)
                .build();

            case COMPLEX -> GeminiAgentOptions.builder()
                .model("gemini-1.5-pro")    // Full power for complex tasks
                .temperature(0.1)           // Conservative for accuracy
                .maxTokens(50000)           // Large output
                .timeout(Duration.ofMinutes(30))
                .build();
        };

        return agentClient.goal(goal).options(options).run().getResult();
    }
}

9.3. Quota Management

Monitor and manage API quotas:

@Component
public class GeminiQuotaManager {

    private final AtomicInteger requestCount = new AtomicInteger(0);
    private final AtomicLong lastResetTime = new AtomicLong(System.currentTimeMillis());

    @Value("${spring.ai.agent.gemini.quota.requests-per-minute:60}")
    private int requestsPerMinute;

    public boolean canMakeRequest() {
        long currentTime = System.currentTimeMillis();
        long timeSinceReset = currentTime - lastResetTime.get();

        // Reset counter every minute
        if (timeSinceReset > 60000) {
            requestCount.set(0);
            lastResetTime.set(currentTime);
        }

        return requestCount.get() < requestsPerMinute;
    }

    public void recordRequest() {
        requestCount.incrementAndGet();
    }
}

10. Monitoring and Observability

10.1. Metrics Collection

@Component
public class GeminiMetrics {

    private final MeterRegistry meterRegistry;
    private final Counter requestCounter;
    private final Timer requestTimer;
    private final Gauge quotaGauge;

    public GeminiMetrics(MeterRegistry meterRegistry, GeminiQuotaManager quotaManager) {
        this.meterRegistry = meterRegistry;
        this.requestCounter = Counter.builder("gemini.requests.total")
            .tag("provider", "gemini")
            .register(meterRegistry);
        this.requestTimer = Timer.builder("gemini.request.duration")
            .register(meterRegistry);
        this.quotaGauge = Gauge.builder("gemini.quota.remaining")
            .register(meterRegistry, quotaManager, GeminiQuotaManager::getRemainingRequests);
    }

    public AgentClientResponse executeWithMetrics(String goal) {
        return Timer.Sample.start(meterRegistry)
            .stop(requestTimer)
            .recordCallable(() -> {
                requestCounter.increment();
                return agentClient.run(goal);
            });
    }
}

11. Troubleshooting

11.1. Common Issues

Gemini CLI Not Found

# Verify installation
gemini --version

# Check PATH
which gemini  # macOS/Linux
where gemini  # Windows

# Reinstall if needed
npm install -g @google/generative-ai-cli

API Key Issues

# Check environment variable
echo $GEMINI_API_KEY

# Test with Gemini CLI directly
gemini --help

Quota Exceeded

Monitor your usage in Google AI Studio and implement quota management:

@Component
public class GeminiHealthIndicator implements HealthIndicator {

    private final GeminiQuotaManager quotaManager;

    @Override
    public Health health() {
        if (quotaManager.canMakeRequest()) {
            return Health.up()
                .withDetail("quota", "Available")
                .withDetail("remaining", quotaManager.getRemainingRequests())
                .build();
        } else {
            return Health.down()
                .withDetail("quota", "Exceeded")
                .withDetail("resetTime", quotaManager.getNextResetTime())
                .build();
        }
    }
}

11.2. Debugging

Enable debug logging:

logging:
  level:
    org.springaicommunity.agents.gemini: DEBUG
    org.springaicommunity.agents.client: DEBUG

12. Comparison with Claude Code

While both are autonomous coding agents, they have different strengths:

Feature Gemini CLI Claude Code

Context Window

1M tokens (gemini-1.5-pro)

~200K tokens

Speed

Very fast with gemini-1.5-flash

Consistent performance

Integration

Google Cloud ecosystem

Anthropic ecosystem

Best For

Large codebases, Google Cloud

General development, detailed reasoning

13. Next Steps