Skip to main content

Overview

Headless mode enables CCS to run in non-interactive environments:
  • CI/CD Pipelines - GitHub Actions, GitLab CI, Jenkins
  • Automated Scripts - Cron jobs, batch processing
  • Server-Side Tasks - Background workers, scheduled analysis
  • No Browser Required - 7-day session tokens eliminate OAuth flow

How It Works

Session Persistence

When you authenticate via browser, CCS:
  1. Obtains OAuth token (expires in ~1 hour)
  2. Stores refresh token (expires in 7 days)
  3. Creates session file (~/.ccs/cliproxy/sessions.json)
  4. Automatically refreshes tokens 5min before expiry
Result: Once authenticated locally, CI/CD can run for 7 days without re-auth.

Headless Delegation Mode

ccs -p "Your prompt here"
This mode:
  • Skips interactive prompts - Returns error if auth needed
  • Uses default profile - Or specify with first arg: ccs gemini -p "prompt"
  • Outputs to stdout - JSON-parsable responses
  • Exits with code - 0 = success, non-zero = failure

Prerequisites

  • CCS installed on CI runner
  • Initial authentication done (see Step 1)
  • Session files accessible to CI environment
1

Authenticate Locally First

You cannot authenticate directly in CI - OAuth requires browser.On your local machine:
ccs gemini --auth
This creates session files in ~/.ccs/cliproxy/:
  • sessions.json - Session metadata
  • gemini-{account}.json - OAuth tokens
Session expiry: 7 days from last token refresh.
2

Copy Session Files to CI

Option A: GitHub Actions SecretsBase64-encode session files:
# On local machine
tar -czf ccs-sessions.tar.gz -C ~/.ccs/cliproxy sessions.json gemini-*.json
base64 ccs-sessions.tar.gz > ccs-sessions.b64
Add to GitHub secrets:
  • Go to repo Settings → Secrets → Actions
  • Create secret CCS_SESSIONS with contents of ccs-sessions.b64
Option B: GitLab CI Variables
# Add as file-type CI/CD variable
cat ccs-sessions.tar.gz | base64
Settings → CI/CD → Variables → Add variable:
  • Key: CCS_SESSIONS
  • Type: File
  • Value: (paste base64 content)
Option C: Self-Hosted RunnerCopy directly to runner home:
scp -r ~/.ccs/cliproxy runner-host:~/.ccs/
3

Restore Sessions in CI

GitHub Actions:
- name: Setup CCS Sessions
  run: |
    mkdir -p ~/.ccs/cliproxy
    echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy
GitLab CI:
before_script:
  - mkdir -p ~/.ccs/cliproxy
  - base64 -d $CCS_SESSIONS | tar -xzf - -C ~/.ccs/cliproxy
Docker:
COPY ccs-sessions.tar.gz /tmp/
RUN mkdir -p ~/.ccs/cliproxy && \
    tar -xzf /tmp/ccs-sessions.tar.gz -C ~/.ccs/cliproxy && \
    rm /tmp/ccs-sessions.tar.gz
4

Use Headless Mode in Pipeline

GitHub Actions Example:
name: AI Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install CCS
        run: npm install -g @kaidu/ccs@latest

      - name: Setup Sessions
        run: |
          mkdir -p ~/.ccs/cliproxy
          echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy

      - name: Run AI Review
        run: |
          ccs gemini -p "Review the changes in this PR and suggest improvements" > review.txt

      - name: Post Comment
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const review = fs.readFileSync('review.txt', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: review
            });
GitLab CI Example:
review:
  stage: test
  image: node:20
  before_script:
    - npm install -g @kaidu/ccs@latest
    - mkdir -p ~/.ccs/cliproxy
    - base64 -d $CCS_SESSIONS | tar -xzf - -C ~/.ccs/cliproxy
  script:
    - ccs codex -p "Analyze code quality and suggest refactorings" | tee review.txt
  artifacts:
    paths:
      - review.txt
5

Handle Session Expiry

Sessions expire after 7 days. Options:Option A: Scheduled Re-AuthenticationCreate GitHub Action that runs weekly:
name: Refresh CCS Sessions

on:
  schedule:
    - cron: '0 0 * * 0'  # Every Sunday
  workflow_dispatch:  # Manual trigger

jobs:
  refresh:
    runs-on: self-hosted  # Must have browser
    steps:
      - name: Re-authenticate
        run: |
          ccs gemini --auth  # Opens browser on self-hosted runner

      - name: Archive Sessions
        run: |
          cd ~/.ccs/cliproxy
          tar -czf ccs-sessions.tar.gz sessions.json gemini-*.json
          base64 ccs-sessions.tar.gz

      - name: Update Secret
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh secret set CCS_SESSIONS < ccs-sessions.tar.gz
Option B: Check and Exit Gracefully
#!/bin/bash
ccs gemini -p "Test prompt" || {
  echo "[!] CCS session expired. Please re-authenticate locally and update CI secrets."
  exit 1
}
Option C: Remote Proxy (Recommended)Use Remote Proxy to centralize auth on server.
6

Configure Environment Variables

Control behavior via env vars:
env:
  CCS_DEBUG: "0"                    # Disable debug logs in CI
  CCS_SKIP_PREFLIGHT: "1"           # Skip API key validation
  CCS_WEBSEARCH_SKIP: "1"           # Disable WebSearch hook
  CCS_PROXY_FALLBACK_ENABLED: "0"   # Fail fast if proxy down
  DISABLE_TELEMETRY: "1"            # Disable telemetry
  DISABLE_ERROR_REPORTING: "1"      # Disable error reporting
Security tip: Never log CCS_PROXY_AUTH_TOKEN in CI output.
7

Parse Responses (Advanced)

Headless mode outputs raw AI response. Parse for CI:
# Extract JSON from response
ccs gemini -p "Analyze this code and return JSON: {score: number, issues: string[]}" | \
  jq -r '.score'

# Fail pipeline if score below threshold
SCORE=$(ccs agy -p "Rate code quality 1-10" | grep -oE '[0-9]+' | head -1)
if [ "$SCORE" -lt 7 ]; then
  echo "[X] Code quality score $SCORE below threshold"
  exit 1
fi
Example: Extract action items
ccs codex -p "List action items from meeting notes in MEETING.md" | \
  grep -E '^\s*[-*]' > action-items.txt

Complete CI/CD Examples

GitHub Actions: Automated Documentation

name: Generate Docs

on:
  push:
    branches: [main]
    paths:
      - 'src/**'

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install CCS
        run: npm install -g @kaidu/ccs@latest

      - name: Setup Sessions
        run: |
          mkdir -p ~/.ccs/cliproxy
          echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy

      - name: Generate API Docs
        run: |
          ccs gemini -p "Generate API documentation from src/ directory in Markdown format" > docs/api.md

      - name: Commit Docs
        run: |
          git config user.name "AI Docs Bot"
          git config user.email "bot@example.com"
          git add docs/api.md
          git commit -m "docs: update API documentation [skip ci]" || exit 0
          git push

GitLab CI: Code Quality Gate

stages:
  - test
  - review

quality-check:
  stage: test
  image: node:20
  before_script:
    - npm install -g @kaidu/ccs@latest
    - mkdir -p ~/.ccs/cliproxy
    - base64 -d $CCS_SESSIONS | tar -xzf - -C ~/.ccs/cliproxy
  script:
    - |
      ccs agy -p "Analyze code in src/ and rate quality 1-10. Output only the number." > score.txt
      SCORE=$(cat score.txt | grep -oE '[0-9]+' | head -1)
      echo "Code quality score: $SCORE/10"
      if [ "$SCORE" -lt 7 ]; then
        echo "Quality gate failed: score below 7"
        exit 1
      fi
  only:
    - merge_requests

Jenkins: Nightly Analysis

pipeline {
  agent any

  triggers {
    cron('H 2 * * *')  // 2 AM daily
  }

  environment {
    CCS_DEBUG = '0'
    CCS_SKIP_PREFLIGHT = '1'
  }

  stages {
    stage('Setup') {
      steps {
        sh 'npm install -g @kaidu/ccs@latest'
        sh 'mkdir -p ~/.ccs/cliproxy'
        withCredentials([file(credentialsId: 'ccs-sessions', variable: 'SESSIONS_FILE')]) {
          sh 'tar -xzf $SESSIONS_FILE -C ~/.ccs/cliproxy'
        }
      }
    }

    stage('Analyze') {
      steps {
        sh '''
          ccs codex -p "Analyze codebase for security vulnerabilities and performance issues" > report.md
        '''
      }
    }

    stage('Publish') {
      steps {
        archiveArtifacts artifacts: 'report.md'
        emailext(
          subject: "Nightly Code Analysis Report",
          body: readFile('report.md'),
          to: 'dev-team@example.com'
        )
      }
    }
  }
}

Environment Variables Reference

VariableDefaultPurpose
CCS_DEBUG0Enable verbose logging
CCS_SKIP_PREFLIGHT0Skip API key validation
CCS_WEBSEARCH_SKIP0Disable WebSearch hook
CCS_PROXY_FALLBACK_ENABLED1Fallback to local proxy
CCS_UNIFIED_CONFIG1Use unified config mode
CCS_MIGRATE0Trigger auto-migration
DISABLE_TELEMETRYunsetDisable Claude telemetry
DISABLE_ERROR_REPORTINGunsetDisable error reporting
DISABLE_BUG_COMMANDunsetDisable bug reporting

Troubleshooting

Authentication Required

Symptom: [X] Auth error: No valid session found Causes:
  • Sessions not restored correctly
  • Session files expired (>7 days)
  • Wrong provider in headless command
Solutions:
# Verify session files exist
ls -la ~/.ccs/cliproxy/sessions.json

# Check session age
stat -c %y ~/.ccs/cliproxy/sessions.json

# Re-authenticate locally and update CI secrets

Token Refresh Failed

Symptom: [X] Network error: UND_ERR_SOCKET Causes:
  • Firewall blocking OAuth endpoints
  • Proxy configuration issues
  • Network timeout
Solutions:
# Increase timeout
export CCS_PROXY_TIMEOUT=5000

# Check network access
curl -I https://accounts.google.com

Session File Permissions

Symptom: [X] Config error: EACCES Cause: Session files not readable by CI user Solution:
chmod 600 ~/.ccs/cliproxy/sessions.json
chmod 600 ~/.ccs/cliproxy/*.json

Prompt Too Long

Symptom: Command line argument limit exceeded Solution: Use file redirection:
# Instead of:
ccs gemini -p "$(cat large-file.txt)"

# Use:
ccs gemini -p "Analyze the content of large-file.txt" < large-file.txt

Security Best Practices

Protect Session Files

  • Encrypt in transit: Use secrets/variables, not environment variables
  • Rotate regularly: Re-authenticate every 7 days maximum
  • Scope access: Limit CI job permissions to necessary secrets
  • Audit logs: Monitor secret access in GitHub/GitLab audit logs

Minimize Token Exposure

# ❌ BAD: Token in logs
- run: echo "Token: ${{ secrets.CCS_SESSIONS }}"

# ✅ GOOD: Silent extraction
- run: echo "${{ secrets.CCS_SESSIONS }}" | base64 -d | tar -xzf - -C ~/.ccs/cliproxy

Use Self-Hosted Runners

For sensitive repos, use self-hosted runners:
  • Sessions stay on your infrastructure
  • No need to upload to GitHub secrets
  • Easier re-authentication workflow

Next Steps