cocoindex code

AST-based semantic code search that just works

![effect](https://github.com/user-attachments/assets/cb3a4cae-0e1f-49c4-890b-7bb93317ab60) A lightweight, effective **(AST-based)** semantic code search tool for your codebase. Built on [CocoIndex](https://github.com/cocoindex-io/cocoindex) β€” a Rust-based ultra performant data transformation engine. Use it from the CLI, or integrate with Claude, Codex, Cursor β€” any coding agent β€” via [Skill](#skill-recommended) or [MCP](#mcp-server). - Instant token saving by 70%. - **1 min setup** β€” install and go, zero config needed!
[![Discord](https://img.shields.io/discord/1314801574169673738?logo=discord&color=5B5BD6&logoColor=white)](https://discord.com/invite/zpA9S2DR7s) [![GitHub](https://img.shields.io/github/stars/cocoindex-io/cocoindex?color=5B5BD6)](https://github.com/cocoindex-io/cocoindex) [![Documentation](https://img.shields.io/badge/Documentation-394e79?logo=readthedocs&logoColor=00B9FF)](https://cocoindex.io/docs/getting_started/quickstart) [![License](https://img.shields.io/badge/license-Apache%202.0-5B5BD6?logoColor=white)](https://opensource.org/licenses/Apache-2.0) [![PyPI Downloads](https://static.pepy.tech/badge/cocoindex/month)](https://pepy.tech/projects/cocoindex) [![CI](https://github.com/cocoindex-io/cocoindex/actions/workflows/CI.yml/badge.svg?event=push&color=5B5BD6)](https://github.com/cocoindex-io/cocoindex/actions/workflows/CI.yml) [![release](https://github.com/cocoindex-io/cocoindex/actions/workflows/release.yml/badge.svg?event=push&color=5B5BD6)](https://github.com/cocoindex-io/cocoindex/actions/workflows/release.yml) 🌟 Please help star [CocoIndex](https://github.com/cocoindex-io/cocoindex) if you like this project! [Deutsch](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=de) | [English](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=en) | [EspaΓ±ol](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=es) | [franΓ§ais](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=fr) | [ζ—₯本θͺž](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=ja) | [ν•œκ΅­μ–΄](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=ko) | [PortuguΓͺs](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=pt) | [Русский](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=ru) | [δΈ­ζ–‡](https://readme-i18n.com/cocoindex-io/cocoindex-code?lang=zh)
## Get Started β€” zero config, let's go! ### Install Using [pipx](https://pipx.pypa.io/stable/installation/): ```bash pipx install 'cocoindex-code[full]' # batteries included (local embeddings) pipx upgrade cocoindex-code # upgrade ``` Using [uv](https://docs.astral.sh/uv/getting-started/installation/): ```bash uv tool install --upgrade 'cocoindex-code[full]' ``` Two install styles β€” they mirror the Docker image variants of the same names: - `cocoindex-code[full]` β€” batteries-included. Pulls in `sentence-transformers` so local embeddings (no API key required) work out of the box. The `ccc init` interactive prompt defaults to [Snowflake/snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs). - `cocoindex-code` (slim) β€” LiteLLM-only; requires a cloud embedding provider and API key. Use when you don't want the local-embedding deps (~1 GB of torch + transformers). Next, set up your [coding agent integration](#coding-agent-integration) β€” or jump to [Manual CLI Usage](#manual-cli-usage) if you prefer direct control. ## Coding Agent Integration This repository is a **single plugin marketplace** (`.claude-plugin/marketplace.json`) consumed by both **Claude Code** and **Grok** β€” same plugin id `cocoindex-code`, same `ccc` skill. Grok optionally activates the bundled hooks and MCP server with `--trust`; Claude Code users can install the same marketplace and rely on the skill alone or load hooks/MCP from the plugin as needed. ### Skill (Recommended) Install the `ccc` skill so your coding agent automatically uses semantic search when needed: ```bash npx skills add cocoindex-io/cocoindex-code ``` That's it β€” no `ccc init` or `ccc index` needed. The skill teaches the agent to handle initialization, indexing, and searching on its own. It will automatically keep the index up to date as you work. The agent uses semantic search automatically when it would be helpful. You can also nudge it explicitly β€” just ask it to search the codebase, e.g. *"find how user sessions are managed"*, or type `/ccc` to invoke the skill directly. Works with [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and other skill-compatible agents. #### Claude Code plugin marketplace For Claude Code users, this repository is also a [plugin marketplace](https://code.claude.com/docs/en/plugin-marketplaces). Install the skill from inside Claude Code with: ```text /plugin marketplace add cocoindex-io/cocoindex-code /plugin install cocoindex-code@cocoindex-code ``` This bundles the same `ccc` skill, with version pinning and `/plugin marketplace update` for updates. The repository also ships `hooks/hooks.json` and `.mcp.json` for Grok (and Claude Code plugin installs that load those files); Claude users who want skill-only search can rely on the skill alone and add MCP manually in the [MCP Server](#mcp-server) section below instead of using the bundled `.mcp.json`. #### Grok plugin For [Grok](https://github.com/xai-org/grok) users, install via Grok's plugin system. The plugin bundles three components: | Component | Purpose | |-----------|---------| | **Skill** (`skills/ccc/`) | Agent runs `ccc search` / `ccc index` via the CLI (same as Claude Code above) | | **Hook** (`hooks/hooks.json`) | `SessionStart` + `PostToolUse` (Edit/Write/…) β†’ incremental `ccc index` when `.cocoindex_code/settings.yml` exists (never in the directory holding ccc's user settings, `~` by default) | | **MCP** (`.mcp.json`) | `ccc mcp` stdio server β€” `search` tool with `refresh_index=true` by default | Grok does **not** import Claude's `enabledPlugins` or plugin cache; install separately even if you already use cocoindex in Claude Code. **Full install** (skill + hook + MCP): ```bash grok plugin marketplace add cocoindex-io/cocoindex-code grok plugin install cocoindex-io/cocoindex-code --trust grok plugin enable cocoindex-code ``` Prefer the GitHub shorthand (`cocoindex-io/cocoindex-code`) for install β€” `grok plugin install cocoindex-code` can fail when no marketplace plugin matches that bare name. `--trust` is required so Grok activates the plugin's hooks and MCP server (skills load when the plugin is enabled). **Skill-only** (match Claude Code β€” no auto-index hook, no MCP tool): Install and enable as above, then disable the optional components: 1. **Hooks** β€” open `/hooks`, select the `SessionStart` / `PostToolUse` hooks from `cocoindex-code`, press `Space` to disable. 2. **MCP** β€” open `/mcps`, select `cocoindex-code`, press `Space` to disable; or persist in `~/.grok/config.toml`: ```toml [mcp_servers.cocoindex-code] enabled = false ``` The agent still owns indexing via the `ccc` skill (`ccc index` / `ccc search --refresh` when stale), same as Claude Code. To avoid importing MCP servers from your Claude/Cursor user config (unrelated to this plugin): ```toml [compat.claude] mcps = false [compat.cursor] mcps = false ``` #### Oh My Pi plugin [Oh My Pi](https://github.com/can1357/oh-my-pi) reads `.omp-plugin/marketplace.json` (preferred) and falls back to `.claude-plugin/marketplace.json`. ```bash omp plugin marketplace add cocoindex-io/cocoindex-code omp plugin install cocoindex-code@cocoindex-code --scope project ``` Then restart the session (`/reload-plugins` does not reload extensions). Requires `ccc` on `PATH` (`uv tool install --upgrade 'cocoindex-code[full]'`). | Component | Purpose | |-----------|---------| | **Skill** (`skills/ccc/`) | Agent runs `ccc search` / `ccc index` via the CLI | | **Hook** (`extensions/ccc-index.ts` via `package.json#omp.extensions`) | `session_start` + post-edit `tool_result` β†’ incremental `ccc index` when `.cocoindex_code/settings.yml` exists (never in the directory holding ccc's user settings, `~` by default) | | **MCP** (`.mcp.json`) | `ccc mcp` stdio server | OMP does **not** execute Claude `hooks/hooks.json` command hooks. The TypeScript extension is the OMP equivalent of the Claude/Grok SessionStart + PostToolUse pair. Skill-only (no MCP, no auto-index): install the skill via `npx skills add cocoindex-io/cocoindex-code`, or run `ccc search` / `ccc index` from the shell. ### MCP Server Alternatively, use `ccc mcp` to run as an MCP server:
Claude Code ```bash claude mcp add cocoindex-code -- ccc mcp ```
Codex ```bash codex mcp add cocoindex-code -- ccc mcp ```
OpenCode ```bash opencode mcp add ``` Enter MCP server name: `cocoindex-code` Select MCP server type: `local` Enter command to run: `ccc mcp` Or use opencode.json: ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "cocoindex-code": { "type": "local", "command": [ "ccc", "mcp" ] } } } ```
Kilo Code Add a local MCP server in `~/.config/kilo/kilo.jsonc`, `kilo.jsonc`, or `.kilo/kilo.jsonc`: ```json { "mcp": { "cocoindex-code": { "type": "local", "command": ["ccc", "mcp"], "enabled": true } } } ```
Oh My Pi Prefer the marketplace install above. Manual project MCP (`.omp/mcp.json`): ```json { "mcpServers": { "cocoindex-code": { "command": "ccc", "args": ["mcp"] } } } ```
Once configured, the agent automatically decides when semantic code search is helpful β€” finding code by description, exploring unfamiliar codebases, fuzzy/conceptual matches, or locating implementations without knowing exact names. > **Note:** The `cocoindex-code` command (without subcommand) still works as an MCP server for backward compatibility. It auto-creates settings from environment variables on first run.
MCP Tool Reference When running as an MCP server (`ccc mcp`), the following tool is exposed: **`search`** β€” Search the codebase using semantic similarity. ``` search( query: str, # Natural language query or code snippet limit: int = 5, # Maximum results (1-100) offset: int = 0, # Pagination offset refresh_index: bool = True, # Refresh index before querying languages: list[str] | None = None, # Filter by language (e.g. ["python", "typescript"]) paths: list[str] | None = None, # Filter by path glob (e.g. ["src/utils/*"]) ) ``` Returns matching code chunks with file path, language, code content, line numbers, and similarity score.
## Manual CLI Usage You can also use the CLI directly β€” useful for manual control, running indexing after changing settings, checking status, or searching outside an agent. ```bash ccc init # initialize project (creates settings) ccc index # build the index ccc search "authentication logic" # search! ``` The background daemon starts automatically on first use. > **Tip:** You can skip `ccc init` and go straight to `ccc index` β€” it auto-initializes new projects with default settings. If global settings are missing too (first use on the machine), it walks you through the same model setup as `ccc init` when run interactively; non-interactive runs (scripts, hooks) still require a one-time `ccc init` first. > **Note:** `ccc index` never auto-initializes the directory that holds ccc's user settings directory: your home directory by default (`~/.cocoindex_code` holds the global settings and daemon files), or the parent of `$COCOINDEX_CODE_DIR` when that directory is named `.cocoindex_code`. A project there would cover every repo below it. The editor hooks skip that directory even if it is already a project. If an older ccc made your home directory a project (`~/.cocoindex_code/settings.yml` exists), run `ccc reset --all` in your home directory: it removes that project's settings and indexes and keeps your global settings. To index your whole home directory on purpose, run `ccc init` there. ### CLI Reference | Command | Description | |---------|-------------| | `ccc init` | Initialize a project β€” creates settings files, adds `.cocoindex_code/` to `.gitignore` | | `ccc index` | Build or update the index (auto-inits if needed). Shows streaming progress. | | `ccc search ` | Semantic search across the codebase | | `ccc grep [path]` | Structural code search by example (no index needed) | | `ccc status` | Show index stats (chunk count, file count, language breakdown) | | `ccc mcp` | Run as MCP server in stdio mode | | `ccc doctor` | Run diagnostics β€” checks settings, daemon, model, file matching, and index health | | `ccc reset` | Delete index databases. `--all` also removes settings. `-f` skips confirmation. | | `ccc version` | Print the CLI version | | `ccc daemon status` | Show daemon version, uptime, and loaded projects | | `ccc daemon restart` | Restart the background daemon | | `ccc daemon stop` | Stop the daemon | ### Search Options ```bash ccc search database schema # basic search ccc search --lang python --lang markdown schema # filter by language ccc search --path 'src/utils/*' query handler # filter by path ccc search --offset 10 --limit 5 database schema # pagination ccc search --refresh database schema # update index first, then search ``` By default, `ccc search` scopes results to your current working directory (relative to the project root). Use `--path` to override. ### Structural Search (`ccc grep`) `ccc grep` finds code by **structure**, not text β€” you write a by-example pattern and it matches the syntax tree (via cocoindex's `code_match`), so formatting, whitespace, and intervening tokens don't matter. It runs entirely locally: no index, daemon, or embeddings required. ```bash ccc grep 'def \NAME(\(ARGS*\)):' # every Python function def under the cwd ccc grep 'foo(\(ARGS*\))' src/ # calls to foo(...) anywhere under src/ ccc grep 'fn \NAME(\(A*\))' --lang rust # restrict to one language ccc grep 'class \NAME:' --path 'tests/**' # restrict to a path glob ccc grep 'TODO(\(A*\))' path/to/file.py # a single file ``` Metavariables use the `\` sigil: `\NAME` captures one node, `\(NAME*\)` a run of siblings, `\_`/`\*` match anonymously. The pattern is matched per language, so a single invocation scans every supported source file (others are skipped). Inside an initialized project, `ccc grep` honors the project's include/exclude patterns and `.gitignore`; otherwise it scans all supported source files under the path. Results stream to the terminal file-by-file as each match is found (in completion order, since files are matched in parallel) rather than all at once at the end. Each matching file shows its matched line range; under a TTY the path is colored, line numbers are dimmed, and the unmatched context around a match is dimmed so the match stands out. > **Note:** `ccc grep` relies on cocoindex's structural `code_match` feature. > Until it ships in a released cocoindex, run against a local cocoindex build. ## Docker A Docker image is available for teams who want a reproducible, dependency-free setup β€” no Python, `uv`, or system dependencies required on the host. The recommended approach is a **persistent container**: start it once, and use `docker exec` to run CLI commands or connect MCP sessions to it. The daemon inside stays warm across sessions, so the embedding model is loaded only once. ### Choosing an image Two variants are published from each release: | Tag | Size | Embedding backends | When to pick | |---|---|---|---| | `cocoindex/cocoindex-code:latest` (slim, default) | ~450 MB | LiteLLM (cloud: OpenAI, Voyage, Gemini, Ollama, …) | Most users. Cloud-backed embeddings, smaller image, fast pulls. | | `cocoindex/cocoindex-code:full` | ~5 GB | sentence-transformers (local) + LiteLLM | When you want local embeddings without an API key, or an offline-ready container. Heavier because of torch + transformers. | The rest of this section uses `:latest` β€” substitute `:full` in the `image:` / `docker run` commands if you want the full variant. > **Mac users running the `:full` variant:** local embedding inference is > CPU-only inside Docker, because Docker on macOS can't access Apple's Metal > (MPS) GPU. If you want local embeddings and fast inference, install > natively instead: `pipx install 'cocoindex-code[full]'`. The `:latest` > (slim) variant is unaffected β€” LiteLLM runs the model on the provider's > side, so Docker vs. native makes no difference. ### Quick start β€” `docker compose up -d` Bring it up in one line β€” no clone needed (bash / zsh): ```bash # macOS / Windows docker compose -f <(curl -L https://raw.githubusercontent.com/cocoindex-io/cocoindex-code/refs/heads/main/docker/docker-compose.yml) up -d # Linux (aligns file ownership on bind-mounted paths with your host user) PUID=$(id -u) PGID=$(id -g) docker compose -f <(curl -L https://raw.githubusercontent.com/cocoindex-io/cocoindex-code/refs/heads/main/docker/docker-compose.yml) up -d ``` Or grab [`docker/docker-compose.yml`](./docker/docker-compose.yml) and run `docker compose up -d` next to it (works on any shell, including Windows cmd / PowerShell). By default your home directory is mounted into the container (set `COCOINDEX_HOST_WORKSPACE` to narrow this to a specific code folder). Index data and the embedding model cache persist in a Docker volume across restarts. Your global settings file at `$HOME/.cocoindex_code/global_settings.yml` is visible and editable on the host; edits take effect on your next `ccc` command. > **Pick a different image:** set `COCOINDEX_CODE_IMAGE` to override the > default. For example, the `:full` variant or GHCR: > ```bash > COCOINDEX_CODE_IMAGE=cocoindex/cocoindex-code:full docker compose up -d > COCOINDEX_CODE_IMAGE=ghcr.io/cocoindex-io/cocoindex-code:latest docker compose up -d > ``` ### Or: `docker run`
Docker Desktop (macOS / Windows) ```bash docker run -d --name cocoindex-code \ --volume "$HOME:/workspace" \ --volume cocoindex-data:/var/cocoindex \ -e COCOINDEX_CODE_HOST_PATH_MAPPING="/workspace=$HOME" \ cocoindex/cocoindex-code:latest ```
Linux (with PUID/PGID) ```bash docker run -d --name cocoindex-code \ -e PUID=$(id -u) -e PGID=$(id -g) \ --volume "$HOME:/workspace" \ --volume cocoindex-data:/var/cocoindex \ -e COCOINDEX_CODE_HOST_PATH_MAPPING="/workspace=$HOME" \ cocoindex/cocoindex-code:latest ```
### Shell wrapper for `ccc` commands Paste this into `~/.bashrc` / `~/.zshrc` so `ccc` feels native on the host and picks up the right project based on your current directory: ```bash ccc() { docker exec -it -e COCOINDEX_CODE_HOST_CWD="$PWD" cocoindex-code ccc "$@" } ``` Now `cd` into any project under your workspace and run `ccc init`, `ccc index`, `ccc search ...`, `ccc status`, etc. β€” it just works. ### Connect your coding agent
Claude Code Register MCP from inside the target project so `$PWD` points there: ```bash claude mcp add cocoindex-code -- docker exec -i \ -e COCOINDEX_CODE_HOST_CWD="$PWD" cocoindex-code ccc mcp ``` Or via `.mcp.json`: ```json { "mcpServers": { "cocoindex-code": { "type": "stdio", "command": "docker", "args": [ "exec", "-i", "-e", "COCOINDEX_CODE_HOST_CWD=${PWD}", "cocoindex-code", "ccc", "mcp" ] } } } ``` > Note: use `-i` (not `-it`). The `-t` flag allocates a terminal, which > interferes with MCP's JSON messaging over stdin/stdout β€” only add it for > interactive `ccc` commands like `ccc init`.
Codex ```bash codex mcp add cocoindex-code -- docker exec -i \ -e COCOINDEX_CODE_HOST_CWD="$PWD" cocoindex-code ccc mcp ```
### Upgrading from an older image Earlier images used separate `cocoindex-db` and `cocoindex-model-cache` volumes; the current image consolidates them into a single `cocoindex-data` volume. Before pulling the new image, drop the old container and volumes β€” indexes rebuild on your next `ccc index`, and the embedding model is re-populated automatically on first start: ```bash docker rm -f cocoindex-code docker volume rm cocoindex-db cocoindex-model-cache ``` ### Configuration via environment variables Pass configuration to `docker run` / compose with `-e`: ```bash # Extra extensions (e.g. Typesafe Config, SBT build files) -e COCOINDEX_CODE_EXTRA_EXTENSIONS="conf,sbt" # Exclude build artefacts (Scala/SBT example) -e COCOINDEX_CODE_EXCLUDE_PATTERNS='["**/target/**","**/.bloop/**","**/.metals/**"]' # Set an API key -e VOYAGE_API_KEY=your-key ``` > **Security note:** mounting `$HOME` gives the container read/write access > to everything under it. If that's too broad, bind-mount a narrower > directory instead (`COCOINDEX_HOST_WORKSPACE=/path/to/code`). ### Build the image locally ```bash docker build -t cocoindex-code:local -f docker/Dockerfile . ``` ## Features - **Semantic Code Search**: Find relevant code using natural language queries when grep doesn't work well, and save tokens immediately. - **Ultra Performant**: ⚑ Built on top of ultra performant [Rust indexing engine](https://github.com/cocoindex-io/cocoindex). Only re-indexes changed files for fast updates. - **Multi-Language Support**: Python, JavaScript/TypeScript, Rust, Go, Java, C/C++, C#, SQL, Shell, and more. - **Embedded**: Portable and just works, no database setup required! - **Flexible Embeddings**: Local SentenceTransformers via the `[full]` extra (free, no API key!) or 100+ cloud providers via LiteLLM. ## Configuration For a detailed guide on choosing and configuring embedding models, see [EMBEDDINGS.md](EMBEDDINGS.md). Configuration lives in two YAML files, both created automatically by `ccc init`. ### User Settings (`~/.cocoindex_code/global_settings.yml`) Shared across all projects. Controls the embedding model and environment variables for the daemon. ```yaml embedding: provider: sentence-transformers # or "litellm" model: Snowflake/snowflake-arctic-embed-xs device: mps # optional: cpu, cuda, mps (auto-detected if omitted) min_interval_ms: 300 # optional: pace LiteLLM embedding requests to reduce 429s; defaults to 5 for LiteLLM mps_low_watermark_ratio: 0.4 # optional: PyTorch allocator soft limit mps_high_watermark_ratio: 0.5 # optional: PyTorch allocator hard limit # Optional extra kwargs passed to the embedder, separately for indexing vs query. # `ccc init` auto-populates these for known models (e.g. Cohere, Voyage, Nvidia NIM, # nomic-ai code-retrieval models, Snowflake arctic-embed). # indexing_params: # input_type: search_document # litellm: input_type # query_params: # input_type: search_query # sentence-transformers: prompt_name envs: # extra environment variables for the daemon OPENAI_API_KEY: your-key # only needed if not already in your shell environment daemon: idle_timeout_minutes: 180 # optional: exit the daemon after this long without client activity (default 180, 0 = never) keep_alive_with_mcp: true # optional: keep the daemon warm while an MCP client is connected (default true) ``` > **Note:** The daemon inherits your shell environment. If an API key (e.g. `OPENAI_API_KEY`) is already set as an environment variable, you don't need to duplicate it in `envs`. The `envs` field is only for values that aren't in your environment. > **Apple Silicon memory safety:** MPS SentenceTransformer calls use [CocoIndex's isolated GPU subprocess](https://github.com/cocoindex-io/cocoindex/blob/v1.0.18/python/cocoindex/_internal/runner.py), keeping the model loaded while isolating Metal allocations from the daemon. The low and high watermarks are ratios of PyTorch's recommended maximum working set; they default here to `0.4` and `0.5`. CocoIndex retries MPS out-of-memory failures with progressively smaller batches, and cocoindex-code [releases unused allocator cache](https://docs.pytorch.org/docs/stable/generated/torch.mps.empty_cache.html) after each index run. Explicit `COCOINDEX_RUN_GPU_IN_SUBPROCESS`, `PYTORCH_MPS_LOW_WATERMARK_RATIO`, and `PYTORCH_MPS_HIGH_WATERMARK_RATIO` environment variables take precedence over these defaults. > **Indexing concurrency:** Multiple projects may prepare indexes concurrently, while CocoIndex serializes their GPU calls through its single MPS subprocess. A search waits only when its own project still needs the initial index. > **Idle timeout:** the background daemon holds the embedding model in RAM, so it exits after `daemon.idle_timeout_minutes` without client activity and is restarted automatically on your next `ccc` command or MCP search. By default, a live MCP session sends periodic heartbeats so the daemon remains warm while your coding agent is connected. Set `daemon.keep_alive_with_mcp: false` to let the daemon idle-exit during long-lived MCP sessions and release the model between real requests. Set `idle_timeout_minutes: 0` to keep the daemon running forever. > **Custom location:** set `COCOINDEX_CODE_DIR` to place `global_settings.yml` somewhere other than `~/.cocoindex_code/` β€” useful if you want the file to live alongside your projects (e.g. on a synced folder). #### `indexing_params` / `query_params` Some embedding models expose different modes for documents vs queries (asymmetric retrieval). For example, Cohere's v3 models want `input_type: search_document` when embedding corpus content and `input_type: search_query` when embedding a user query; several SentenceTransformers models use `prompt_name: passage` / `prompt_name: query` for the same purpose. These knobs live under `indexing_params` and `query_params`: ```yaml embedding: provider: litellm model: cohere/embed-english-v3.0 indexing_params: input_type: search_document query_params: input_type: search_query ``` `ccc init` populates these automatically for models it recognizes β€” including all Cohere v3, Voyage, Nvidia NIM, Gemini embedding (`gemini/gemini-embedding-*`, `gemini/text-embedding-*`, `gemini/embedding-*` β€” LiteLLM auto-maps `input_type` to Gemini's `task_type`), `nomic-ai/CodeRankEmbed`, `nomic-ai/nomic-embed-code`, `nomic-ai/nomic-embed-text-v1`/`v1.5`, `mixedbread-ai/mxbai-embed-large-v1`, and the `Snowflake/snowflake-arctic-embed-*` family β€” and prints the chosen defaults. For other models, it leaves a commented-out template under `embedding:` so you can fill it in by hand. OpenAI embeddings (`text-embedding-3-*`, `text-embedding-ada-002`) are intentionally not in the list: they're symmetric and have no equivalent knob. **Accepted keys:** `prompt_name` (sentence-transformers) and `input_type` (litellm). Other keys are rejected at daemon startup with a clear error. Note: `dimensions` is intentionally not exposed here β€” output dimension must be identical for indexing and query, so it's a model-wide setting rather than a per-side knob. **Doctor checks both sides.** `ccc doctor` exercises the model once with `indexing_params` and once with `query_params`, reporting each as a separate `Model Check (indexing)` / `Model Check (query)` entry β€” so a misconfiguration on one side is diagnosable without hiding behind the other. **Legacy-bridge warning:** if you're upgrading from an earlier version and your `global_settings.yml` uses `nomic-ai/CodeRankEmbed` or `nomic-ai/nomic-embed-code` without `indexing_params` / `query_params`, the daemon continues to apply the previous behavior (`prompt_name: query` at query time) and prints a one-time warning asking you to make the setting explicit. You can silence the warning by adding an empty block such as `query_params: {}`. ### Project Settings (`/.cocoindex_code/settings.yml`) Per-project. Controls which files to index. ```yaml include_patterns: - "**/*.py" - "**/*.js" - "**/*.ts" - "**/*.rs" - "**/*.go" # ... (sensible defaults for 28+ file types) exclude_patterns: - "**/.*" # hidden directories - "**/__pycache__" - "**/node_modules" - "**/dist" # ... max_file_size: 500KB # skip files larger than this (default: no limit) language_overrides: - ext: inc # treat .inc files as PHP lang: php chunkers: - ext: toml # use a custom chunker for .toml files module: example_toml_chunker:toml_chunker ``` > `.cocoindex_code/` is automatically added to `.gitignore` during init. `max_file_size` keeps bundled or generated files out of the index without having to enumerate them in `exclude_patterns`. It accepts a plain byte count (`1048576`) or a size with a binary unit suffix: `B`, `KB`, `MB`, `GB` (case-insensitive, so `500KB` and `500 kb` are the same). The limit is inclusive, and omitting the key indexes files of any size. It applies wherever project file matching does, so `ccc grep` skips the same files. After editing `include_patterns`, `exclude_patterns`, `max_file_size`, or `language_overrides`: - Run `ccc doctor` to preview which files match. - Run `ccc index` or `ccc search --refresh ...` to update the existing index. - You do not need to delete the index or restart the daemon for these file-matching changes. - After any `language_overrides` change, the next run re-chunks all files, including files whose content did not change; the run reports them as reprocessed. Chunks whose text did not change keep their cached embeddings. If you add or change custom `chunkers`, or edit a chunker module, restart the daemon (`ccc daemon restart`), then run `ccc index`. The daemon imports chunkers once at startup, so until it restarts it keeps running the old chunker code. The first run after the restart re-chunks all files, not only the ones the changed entry applies to; chunks whose text did not change keep their cached embeddings. A chunker counts as changed when its `module:` value changes or when the file of the module it names changes. Edits to other modules that file imports are not detected: after such an edit, also change the chunker's own module file (for example, bump a version comment in it). Use `chunkers` when you want to control how a file type is split into chunks before indexing. `module: example_toml_chunker:toml_chunker` means: - `example_toml_chunker` is a local Python module - `toml_chunker` is the function inside that module In practice, this usually means: - you create a Python file in your project, for example `example_toml_chunker.py` - you add a function in that file - you point `settings.yml` at it with `module.path:function_name` The function should use this signature: ```python from pathlib import Path from cocoindex_code.chunking import Chunk def my_chunker(path: Path, content: str) -> tuple[str | None, list[Chunk]]: ... ``` - `path` is the file being indexed - `content` is the full text of that file - return `language_override` as a string like `"toml"` if you want to override language detection - return `None` as `language_override` if you want to keep the detected language - return a `list[Chunk]` with the chunks you want stored in the index See [`src/cocoindex_code/chunking.py`](./src/cocoindex_code/chunking.py) for the public types and [`tests/example_toml_chunker.py`](./tests/example_toml_chunker.py) for a complete example. ## Embedding Models With the `[full]` extra installed, `ccc init` defaults to a local SentenceTransformers model ([Snowflake/snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs)) β€” no API key required. To use a different model, edit `~/.cocoindex_code/global_settings.yml`. > The `envs` entries below are only needed if the key isn't already in your shell environment β€” the daemon inherits your environment automatically.
Ollama (Local) ```yaml embedding: model: ollama/nomic-embed-text ``` Set `OLLAMA_API_BASE` in `envs:` if your Ollama server is not at `http://localhost:11434`.
OpenAI ```yaml embedding: model: text-embedding-3-small min_interval_ms: 300 # optional: override the 5ms LiteLLM default envs: OPENAI_API_KEY: your-api-key ```
OpenAI-compatible (custom endpoint) Many providers (vLLM, LM Studio, LocalAI, Together, Fireworks, DeepInfra, …) expose an OpenAI-compatible embedding API. Use the `openai/` prefix and point `OPENAI_BASE_URL` at your endpoint: ```yaml embedding: model: openai/your-model-name envs: OPENAI_BASE_URL: https://your-endpoint/v1 OPENAI_API_KEY: your-api-key ``` Don't append `/embeddings` to the base URL β€” LiteLLM handles that.
Azure OpenAI ```yaml embedding: model: azure/your-deployment-name envs: AZURE_API_KEY: your-api-key AZURE_API_BASE: https://your-resource.openai.azure.com AZURE_API_VERSION: "2024-06-01" ```
Gemini ```yaml embedding: model: gemini/gemini-embedding-001 envs: GEMINI_API_KEY: your-api-key ```
Mistral ```yaml embedding: model: mistral/mistral-embed envs: MISTRAL_API_KEY: your-api-key ```
Voyage (Code-Optimized) ```yaml embedding: model: voyage/voyage-code-3 envs: VOYAGE_API_KEY: your-api-key ```
Cohere ```yaml embedding: model: cohere/embed-v4.0 envs: COHERE_API_KEY: your-api-key ```
AWS Bedrock ```yaml embedding: model: bedrock/amazon.titan-embed-text-v2:0 envs: AWS_ACCESS_KEY_ID: your-access-key AWS_SECRET_ACCESS_KEY: your-secret-key AWS_REGION_NAME: us-east-1 ```
Nebius ```yaml embedding: model: nebius/BAAI/bge-en-icl envs: NEBIUS_API_KEY: your-api-key ```
Any [LiteLLM-supported model](https://docs.litellm.ai/docs/embedding/supported_embedding) works. When using a LiteLLM model, set `provider: litellm` (or omit `provider` β€” LiteLLM is the default for non-`sentence-transformers` models). For the full list of env vars each provider reads (API keys, base URLs, regions, …), see LiteLLM's [Setting API Keys](https://docs.litellm.ai/docs/set_keys). ### Local SentenceTransformers Models Set `provider: sentence-transformers` and use any [SentenceTransformers](https://www.sbert.net/) model (no API key required). **Example β€” general purpose text model:** ```yaml embedding: provider: sentence-transformers model: nomic-ai/nomic-embed-text-v1.5 ``` **GPU-optimised code retrieval:** [`nomic-ai/CodeRankEmbed`](https://huggingface.co/nomic-ai/CodeRankEmbed) delivers significantly better code retrieval than the default model. It is 137M parameters, requires ~1 GB VRAM, and has an 8192-token context window. ```yaml embedding: provider: sentence-transformers model: nomic-ai/CodeRankEmbed ``` **Note:** Switching models requires re-indexing your codebase (`ccc reset && ccc index`) since the vector dimensions differ. ## Supported Languages | Language | Aliases | File Extensions | |----------|---------|-----------------| | c | | `.c` | | cpp | c++ | `.cpp`, `.cc`, `.cxx`, `.h`, `.hpp` | | csharp | csharp, cs | `.cs` | | css | | `.css`, `.scss` | | dart | | `.dart` | | dtd | | `.dtd` | | elixir | | `.ex`, `.exs` | | fortran | f, f90, f95, f03 | `.f`, `.f90`, `.f95`, `.f03` | | go | golang | `.go` | | html | | `.html`, `.htm` | | java | | `.java` | | javascript | js | `.js` | | json | | `.json` | | kotlin | | `.kt`, `.kts` | | lua | | `.lua` | | markdown | md | `.md`, `.mdx` | | pascal | pas, dpr, delphi | `.pas`, `.dpr` | | php | | `.php` | | python | | `.py` | | r | | `.r` | | ruby | | `.rb` | | rust | rs | `.rs` | | scala | | `.scala` | | solidity | | `.sol` | | sql | | `.sql` | | svelte | | `.svelte` | | swift | | `.swift` | | toml | | `.toml` | | tsx | | `.tsx` | | typescript | ts | `.ts` | | vue | | `.vue` | | xml | | `.xml` | | yaml | | `.yaml`, `.yml` | ### Custom Database Location By default, index databases (`cocoindex.db` and `target_sqlite.db`) live alongside settings in `/.cocoindex_code/`. When running in Docker, you may want the databases on the container's native filesystem for performance (LMDB doesn't work well on mounted volumes) while keeping the source code and settings on a mounted volume. Set `COCOINDEX_CODE_DB_PATH_MAPPING` to remap database locations by path prefix: ```bash COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files ``` With this mapping, a project at `/workspace/myrepo` stores its databases in `/db-files/myrepo/` instead of `/workspace/myrepo/.cocoindex_code/`. Settings files remain in the original location. Multiple mappings are comma-separated and resolved in order (first match wins): ```bash COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files,/workspace2=/db-files2 ``` Both source and target must be absolute paths. If no mapping matches, the default location is used. ## Troubleshooting Run `ccc doctor` to diagnose common issues. It checks your settings, daemon health, embedding model, file matching, and index status β€” all in one command. ### `sqlite3.Connection object has no attribute enable_load_extension` Some Python installations (e.g. the one pre-installed on macOS) ship with a SQLite library that doesn't enable extensions. **macOS fix:** Install Python through [Homebrew](https://brew.sh/): ```bash brew install python3 ``` Then re-install cocoindex-code (see [Get Started](#get-started--zero-config-lets-go) for install options): Using pipx: ```bash pipx install cocoindex-code # first install pipx upgrade cocoindex-code # upgrade ``` Using uv (install or upgrade): ```bash uv tool install --upgrade cocoindex-code ``` ### `MDB_MAP_FULL: Environment mapsize limit reached` The index is stored in an LMDB database whose maximum size is fixed when the daemon starts. The default ceiling is **4 GiB**, which is plenty for most projects but can be exhausted by very large codebases (tens of thousands of files), especially with high-dimensional embedding models like `nomic-ai/CodeRankEmbed`. Raise the ceiling with the `COCOINDEX_LMDB_MAP_SIZE` environment variable (value in **bytes**). LMDB only grows the file as data is written, so a high limit doesn't pre-allocate disk β€” it's safe to set it generously: ```yaml # ~/.cocoindex_code/global_settings.yml envs: COCOINDEX_LMDB_MAP_SIZE: "34359738368" # 32 GiB (= 32 * 1024^3) ``` Or, if you prefer to set it in your shell environment (the daemon inherits it): ```bash export COCOINDEX_LMDB_MAP_SIZE=$((32 * 1024 * 1024 * 1024)) # 32 GiB ``` The map size is read when the daemon starts, so restart it to pick up the change, then re-index: ```bash ccc daemon restart ccc index ``` > This manual step is temporary. Once [cocoindex#2108](https://github.com/cocoindex-io/cocoindex/issues/2108) lands, the map size grows automatically when needed and `COCOINDEX_LMDB_MAP_SIZE` won't be necessary. ## Legacy: Environment Variables If you previously configured `cocoindex-code` via environment variables, the `cocoindex-code` MCP command still reads them and auto-migrates to YAML settings on first run. We recommend switching to the YAML settings for new setups. | Environment Variable | YAML Equivalent | |---------------------|-----------------| | `COCOINDEX_CODE_EMBEDDING_MODEL` | `embedding.model` in `global_settings.yml` | | `COCOINDEX_CODE_DEVICE` | `embedding.device` in `global_settings.yml` | | `COCOINDEX_CODE_ROOT_PATH` | Run `ccc init` in your project root instead | | `COCOINDEX_CODE_EXCLUDED_PATTERNS` | `exclude_patterns` in project `settings.yml` | | `COCOINDEX_CODE_EXTRA_EXTENSIONS` | `include_patterns` + `language_overrides` in project `settings.yml` | ## Telemetry `cocoindex-code` sends anonymous usage telemetry through CocoIndex so we can see how the tool is used in aggregate and prioritize improvements. The events identify themselves as `application: cocoindex-code`. We **do not** collect your source code, file paths, queries, search results, embeddings, settings, or any other content from your codebase or environment. To opt out, set: ```bash export COCOINDEX_DISABLE_USAGE_TRACKING=1 ``` ## Large codebase / Enterprise [CocoIndex](https://github.com/cocoindex-io/cocoindex) is an ultra efficient indexing engine that also works on large codebases at scale for enterprises. In enterprise scenarios it is a lot more efficient to share indexes with teammates when there are large or many repos. We also have advanced features like branch dedupe etc designed for enterprise users. > Indexing a very large codebase and hitting `MDB_MAP_FULL`? Raise the LMDB map size β€” see [`MDB_MAP_FULL: Environment mapsize limit reached`](#mdb_map_full-environment-mapsize-limit-reached) under Troubleshooting. If you need help with remote setup, please email our maintainer linghua@cocoindex.io, happy to help! ## Contributing We welcome contributions! This project uses [uv](https://docs.astral.sh/uv/getting-started/installation/) for development, and every PR is gated on the same lint, format, type-check, and test suite in CI. **Please run these checks locally before opening a PR** β€” failing pre-commit checks are the most common cause of red CI on incoming PRs. ### 1. Install the dev dependencies After installing [uv](https://docs.astral.sh/uv/getting-started/installation/), sync the project. This installs everything the checks need β€” including [prek](https://github.com/j178/prek), a fast pre-commit runner, plus Ruff, mypy, and pytest: ```bash uv sync ``` ### 2. Run all checks before every PR Run the full hook suite across all files β€” this is exactly what CI runs: ```bash uv run prek run --all-files ``` It runs trailing-whitespace/end-of-file fixes, Ruff lint (`--fix`) and format, `uv.lock` validation, mypy type checking, and the pytest suite. Fix anything it reports (Ruff auto-fixes most lint/format issues for you), re-run until it passes, then push. ### 3. (Optional) Run automatically on each commit To have the same checks run on every `git commit`, install the git hook once: ```bash uv run prek install ``` For more details, see our [contributing guide](https://cocoindex.io/docs/contributing/guide). ## License Apache-2.0