Skip to content

Repository files navigation

@google/gemini-cli-context-reducer

Domain-specific context reduction for gemini-cli.

Prunes the files sent to the Gemini API from an entire monorepo (~1.8 M tokens) down to a focused, import-graph-centred window (~28 K tokens) before any API call. Zero additional LLM inference; all analysis is local and SQLite-cached.


Why

Large TypeScript monorepos (Rocket.Chat, NestJS apps, Next.js projects) easily exceed Gemini's free-tier context window. Naively loading every file the user @-mentions forces gemini-cli to either fail or send an enormous, unfocused prompt.

This package inserts a BeforeModel hook that rewrites the request in-process:

Without context-reducer With context-reducer
~8 000 files, ~1.8 M tokens ~15–25 files, ~28 K tokens
2–4 s just to tokenise < 1 s (SQLite cache populated)

How it works – Ripple Scope BFS

Given one or more seed files (the files the user @-mentioned):

  1. Outward traversal — walk the import graph from the seed outward (files it imports), up to maxOutwardDepth hops (default: 1).
  2. Inward traversal — walk the reverse import graph from the seed inward (files that import it), with no depth limit.
  3. Render mode assignment:
    • Seed → full (sent verbatim)
    • Inward, depth 1, budget ample → full
    • Inward, depth ≥ 2 or outward → stub (only export declare signatures)
    • Budget overflow → downgrade full → stub → omit
  4. Stub generation — files in stub mode get concise export declare signatures so the model knows what is available without paying for the full body.
  5. System annotation — a one-line annotation is prepended to inform the model that context reduction is active and how to request omitted files.

Installation

This package is part of the gemini-cli monorepo. After cloning:

npm install          # from repo root – installs all workspaces
cd packages/context-reducer
npm run build

Native dependency

better-sqlite3 is a native Node module. It is pre-built for common platforms via the node-gyp-build mechanism. If you see build errors, install the platform build tools:

  • macOS: xcode-select --install
  • Linux: sudo apt-get install build-essential python3
  • Windows: npm install --global windows-build-tools (run as Administrator)

Usage

Quick start (CLI flags)

# Enable context reduction (defaults: 28k token budget, depth 1)
gemini --reduce --prompt "explain the auth login flow" @src/api/user.controller.ts

# Dry-run: print scope log to stderr, do NOT call the model
gemini --reduce --reduce-dry-run --reduce-verbose \
  --prompt "add input validation" @src/api/user.controller.ts

# Explicit budget and depth
gemini --reduce --reduce-budget 40000 --reduce-depth 2 \
  --prompt "refactor auth" @src/auth/auth.service.ts

# Force a specific domain (overrides auto-detection)
gemini --reduce --reduce-domain server-core \
  --prompt "update startup sequence" @src/main.ts

CLI flags reference

Flag Type Default Description
--reduce boolean false Enable context reduction
--reduce-domain string auto Force domain (overrides auto-detect)
--reduce-budget number 28000 Token budget (sum across full + stub files)
--reduce-depth number 1 Max outward BFS depth (imports-of-seed)
--reduce-verbose boolean false Print scope log to stderr
--reduce-dry-run boolean false Print scope log, skip model call

.gemini-context configuration file

Drop a .gemini-context YAML file at the repo root to define domains — named groups that tell the reducer which files belong together and which to exclude.

version: 1

budget:
  tokens: 28000
  maxOutwardDepth: 1
  maxTotalFiles: 80

domains:
  - name: auth
    description: "Authentication and JWT utilities"
    entryPatterns:
      - "src/auth/**/*.ts"
    excludePatterns:
      - "**/*.spec.ts"
    alwaysInclude:
      - "src/auth/auth.module.ts"

  - name: api
    description: "REST API controllers"
    entryPatterns:
      - "src/api/**/*.ts"
    excludePatterns:
      - "**/*.spec.ts"
    alwaysInclude: []

autoScope:
  - pattern: "src/auth/**"
    domain: auth
  - pattern: "src/api/**"
    domain: api

The autoScope rules determine which domain activates automatically based on the seed file paths. If no rule matches, the full repo is crawled without a domain filter.

Template configs

Ready-made templates for popular project types live in templates/:

Template Description
rocketchat.gemini-context Rocket.Chat monorepo
nextjs.gemini-context Next.js app (App Router + Pages Router)
nestjs.gemini-context NestJS monorepo

Copy and rename to .gemini-context at your repo root.


Programmatic API

import { ContextReducerEngine } from '@google/gemini-cli-context-reducer';

const engine = ContextReducerEngine.create('/path/to/repo');

const result = await engine.reduce({
  repoRoot: '/path/to/repo',
  entryFiles: ['/path/to/repo/src/api/user.controller.ts'],
  tokenBudget: 28_000,
  maxOutwardDepth: 1,
  maxTotalFiles: 80,
});

console.log(result.scopeLog);
// Full files (1):
//   [full]  src/api/user.controller.ts  (~4200 tok)
// Stub files (2):
//   [stub]  src/auth/auth.service.ts  (~320 tok)
//   [stub]  src/models/user.model.ts  (~180 tok)
// Omitted files: 0

engine.close();

ReductionResult shape

interface ReductionResult {
  included: FileNode[];       // renderMode === 'full'
  stubbed:  FileNode[];       // renderMode === 'stub'
  omitted:  FileNode[];       // renderMode === 'omit'
  totalTokensBefore: number;  // estimated tokens without reduction
  totalTokensAfter:  number;  // estimated tokens after reduction
  scopeLog:          string;  // human-readable scope summary
  systemAnnotation:  string;  // one-line annotation for the model system prompt
}

Architecture

ContextReducerEngine
  │
  ├─ ImportExtractor   ──► ASTCache (SQLite, better-sqlite3)
  ├─ SignatureExtractor ─► ASTCache
  │
  ├─ ReducerPipeline
  │    ├─ PatternReducer     (priority 100) – exclude node_modules, dist, *.d.ts …
  │    ├─ ManifestReducer    (priority  75) – .gemini-context domain filtering
  │    └─ ImportGraphReducer (priority  50) – BFS Ripple Scope
  │
  └─ StubGenerator  – produces `export declare` stubs for stub-mode files

GeminiCliHook
  └─ Registered as a ProgrammaticBeforeModelHandler in HookSystem
       → fires before any external shell hooks
       → rewrites GenerateContentParameters in-process
       → fail-open: errors fall back to unmodified request

Testing

cd packages/context-reducer
npm test

Four test suites using Vitest:

Suite Coverage
tests/ast-cache.test.ts Cache miss/hit, hash mismatch, round-trip, persistence
tests/stub-generator.test.ts Function, class, type, const stubs; header/footer
tests/ripple-scope.test.ts BFS traversal, render modes, depth limits, disconnected files
tests/engine.test.ts End-to-end reduce(), budget enforcement, empty inputs

Benchmarks

# Against the mock-monorepo fixture (always available)
npm run bench

# Against a real large repo (e.g. Rocket.Chat)
BENCH_REPO=/path/to/Rocket.Chat npm run bench

Expected warm-cache results on a real monorepo:

Scenario Median latency
Cold (first call, no SQLite cache) ~1–2 s
Warm (subsequent calls) ~50–200 ms
Depth-0 (seed only) ~10–30 ms

Key design constraints

  1. Never calls the Gemini API — 100% local static analysis.
  2. Fail-open — any unhandled error returns the original (unreduced) request.
  3. Seed files are never omitted — entry files always appear at minimum as stubs.
  4. alwaysInclude files are never omitted — domain-declared pinned files survive budget overflow.
  5. Synchronous SQLitebetter-sqlite3 keeps the fast-path latency predictable.
  6. NodeNext module resolution — all internal imports use .js extensions.

License

Apache 2.0 — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages