Skip to content

Repository files navigation

CodeLens CLI

A multi-agent LLM code-review tool. A context agent builds a RAG index over your codebase and feeds a review agent that reasons over your git diff — grounding every comment in the surrounding code. The review streams back token-by-token, on the terminal or over Server-Sent Events.

  git diff  ─┐
             ├──►  context agent (RAG)  ──►  review agent  ──►  streamed review
  codebase  ─┘     embeds + retrieves        grounded prompt     (stdout / SSE)
  • TypeScript / Node.js, zero global state, fully typed.
  • Pluggable providers — Ollama, Gemini, Mistral — swappable via one config key.
  • RAG retrieval so the reviewer sees related code, not just the diff.
  • Retry + fallback on every remote LLM call.
  • Token streaming to the terminal and over SSE.

How it works

The review runs as a four-stage pipeline coordinated by an orchestrator. State threads through every stage, and the orchestrator emits a stream of events so the CLI and the SSE server consume the same run.

Stage Owner What happens
1. indexing Context agent Walks the repo, chunks files into overlapping windows, embeds them, stores vectors in an in-memory store.
2. diffing Git module Extracts the diff (uncommitted changes or a ref range) and splits it per file.
3. retrieving Context agent For each changed file, embeds the diff and retrieves the top-k most similar chunks (cosine similarity).
4. reviewing Review agent Builds one grounded prompt (diff + retrieved context), then streams the model's review token-by-token.

The two agents are intentionally separate: the context agent is the only part that touches the filesystem and the embedding model; the review agent is pure prompt-assembly + generation. They never call each other directly — the orchestrator threads state between them.


Project layout

src/
├── index.ts                 CLI entry (commander): `review` and `serve`
├── config/
│   └── config.ts            Config schema, defaults, JSON loader, API-key resolution
├── providers/
│   ├── provider.ts          AIProvider interface (embed + streaming generate) + ProviderError
│   ├── ollama.ts            Local Ollama (newline-delimited JSON stream)
│   ├── gemini.ts            Google Generative Language API (SSE stream)
│   ├── mistral.ts           Mistral (OpenAI-compatible SSE stream)
│   └── factory.ts           Builds a provider from config — the ONLY place that knows concrete classes
├── rag/
│   ├── chunker.ts           Line-aligned overlapping chunking
│   └── vectorStore.ts       In-memory cosine-similarity top-k store
├── git/
│   └── diff.ts              git diff extraction (simple-git) + per-file split
├── agents/
│   ├── contextAgent.ts      Stage 1 & 3: index the repo, retrieve context per diff
│   └── reviewAgent.ts       Stage 4: build grounded prompt, stream the review
├── pipeline/
│   ├── state.ts             PipelineState + PipelineEvent types
│   └── orchestrator.ts      Coordinates all stages; async generator of events
├── server/
│   └── sseServer.ts         Express SSE endpoint that forwards pipeline events
└── utils/
    ├── logger.ts            Leveled logger (writes to stderr, never pollutes streamed output)
    └── retry.ts             Exponential backoff + provider fallback

Requirements

  • Node.js >= 18.17 (uses native fetch and web streams)
  • A provider:
    • Ollama running locally (ollama serve) with a chat model + an embedding model, or
    • a Gemini API key, or
    • a Mistral API key

Install & build

npm install
npm run build      # compiles TypeScript to dist/
npm link           # optional: makes `codelens` available globally

Run without building during development:

npm run dev -- review --repo /path/to/repo

Usage

Review the current repo's uncommitted changes

codelens review
# or explicitly
codelens review --repo /path/to/repo

Review a commit range

codelens review --range "main..HEAD"
codelens review --range "HEAD~3..HEAD"

Run the streaming server

codelens serve
# then, from another terminal — note -N to disable curl buffering so you see tokens live:
curl -N "http://localhost:4000/review?repo=$(pwd)&range=main..HEAD"

The server emits named SSE events: stage, token, done, error.

Options

codelens [--log-level debug|info|warn|error] <command>

review   -r, --repo <path>     repository to review        (default: cwd)
         -c, --range <range>   git range                   (default: uncommitted changes)
serve                          start the SSE server on the configured port

Configuration

Drop a codelens.config.json in the repo you're reviewing (an example is included). The whole pipeline is driven by the single provider key — change it and every stage re-routes to that backend:

{
  "provider": "mistral",
  "fallbackProvider": "gemini",
  "providers": {
    "ollama":  { "model": "llama3.1",            "embedModel": "nomic-embed-text", "baseUrl": "http://localhost:11434" },
    "gemini":  { "model": "gemini-1.5-flash",    "embedModel": "text-embedding-004", "apiKeyEnv": "GEMINI_API_KEY" },
    "mistral": { "model": "mistral-small-latest","embedModel": "mistral-embed",     "apiKeyEnv": "MISTRAL_API_KEY" }
  },
  "rag":   { "chunkSize": 1200, "chunkOverlap": 200, "topK": 6, "include": [".ts", ".js"], "exclude": ["node_modules", "dist"] },
  "retry": { "maxAttempts": 3, "baseDelayMs": 500 },
  "server":{ "port": 4000 }
}

API keys are read from environment variables named by apiKeyEnv — never stored in the config file:

export GEMINI_API_KEY=...
export MISTRAL_API_KEY=...
Key Meaning
provider The active backend. This one key swaps the whole pipeline.
fallbackProvider Secondary backend the retry layer escalates to if the primary is down.
rag.chunkSize / chunkOverlap Chunk window and overlap (characters).
rag.topK Chunks retrieved per changed file.
rag.include / exclude File extensions to index / path segments to skip.
retry.maxAttempts / baseDelayMs Backoff schedule for remote calls.

Design notes

  • One interface, three backends. Everything depends on AIProvider (embed + streaming generate). The provider factory is the only module that imports concrete provider classes, so adding a backend is a new file plus one case.
  • Streaming is uniform. Each provider normalizes its wire format (Ollama's NDJSON, Gemini/Mistral SSE) into the same AsyncIterable<GenerateChunk>. The orchestrator yields those tokens as events, and both stdout and SSE forward them unchanged.
  • The orchestrator is an async generator of events. That is what lets the CLI and the HTTP server share one code path — neither owns the pipeline.
  • Logs go to stderr. Streamed review text owns stdout, so piping/redirecting the review never mixes in log lines.
  • The vector store is swappable. It exposes only add and search, so the in-memory cosine store can be replaced with pgvector / Chroma / Pinecone without touching the agents.

See ARCHITECTURE.md for the data flow in depth and the rationale behind each decision.

About

LLM-Powered Git Diff Code Reviewer

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages