A local, offline AI dev assistant for any repo and any language — exposed as a single MCP server. It pairs a local LLM (the brain) with codebase-grounded retrieval (cocoindex + code-specialized embeddings) so its answers are based on your actual code.
Works with Claude Code, Cursor, Windsurf, and Claude Desktop. Fully offline once set up — no cloud API, no code leaves your machine.
All four are grounded in the indexed repository via retrieval.
| Tool | What it does |
|---|---|
develop |
Write a feature / refactor / fix — including tests, docstrings, and small accompanying changes — matching repo conventions, and writes the files to disk |
review |
Review code against the repo's conventions, for bugs/security, PRs, or pasted changes |
explain |
Explain how something works, or brainstorm an approach — grounded in real files |
search |
Semantic code search over the repo (re-ranked to favor implementation over tests/docs); returns raw chunks for the host model to read |
develop refuses to run when retrieval returns no chunks (avoids silent FastAPI/Flask guesses). Root README.md is demoted when AGENTS.md exists at repo root. API/endpoint tasks pull core/** implementation first, demote OpenAPI skill templates, and block writes to .cursor/ / SKILL.md.
Claude Code / Cursor / Claude Desktop
│ (one registered MCP server)
▼
┌─ devmate ───────────────────────────┐
│ develop · review · explain · search │
│ │
│ retrieval ──► ccc (cocoindex) │ ← backend, not a separate MCP
│ inference ──► local LLM (Ollama) │
└──────────────────────────────────────┘
│
cocoindex daemon → jina code embeddings → local SQLite index
- Brain (reasoning): a local model via Ollama (default
codestral:22b), or vLLM on a shared GPU. - Apply model: a small, fast model (default Kortix
FastApply-7B) that mergesdevelop's lazy edit sketches into full files — the two-model split. Pulled bysetup.sh; see Howdevelopedits files. - Retrieval (RAG): cocoindex-code
with
jinaai/jina-embeddings-v2-base-codeembeddings, indexed per-repo into local SQLite.setup.shpatches index settings so.cursor/rules,.claude/skills, and invariants are searchable (defaultccc initexcludes all dot-dirs via**/.*). Retrieval uses multiple passes — agent knowledge → implementation (core/**by default) → data layer (models/repositories on write tasks) → broad — and re-ranks so rules beat tests/docs. Ondevelop, a sibling-test pass also derives name stems from the task (e.g.StatementBuilder→statement_builder) and pins the matching test file(s) into context so generated code follows the repo's test conventions — these chunks are additive and exempt from the tests/* demotion. - One MCP server — cocoindex runs as devmate's backend, not a separate server.
One command does everything — installs Ollama + a model, installs cocoindex with the code-embedding model, indexes your repo, and registers the MCP server:
git clone https://github.com/learncoder4848/devmate
cd devmate
./setup.sh /path/to/your-repo # or run with no arg to use the current dirWhat setup.sh does (6 steps, idempotent — safe to re-run):
1/6 Ollama install + start (skips if present)
2/6 Model picker choose a reasoning model + pull the apply model
3/6 cocoindex-code install on Python 3.12 (the retrieval backend)
4/6 Embeddings pin known-good deps + set jina code embeddings
5/6 Index repo ccc init + ccc index the target repo
6/6 Register build + register the devmate MCP (with REPO_ROOT)
Then restart your IDE and use the tools naturally:
"develop a retry-with-backoff wrapper for the ledger client"
"review this diff against our conventions"
"explain how interest is computed here"
"search for where SCRA adjustments are posted"
setup.sh is dual-mode. To point the assistant at a different repo later, just run
it again with that path — it skips everything already installed and only indexes +
re-points devmate at the new repo:
./setup.sh /path/to/other-repoIndexing is fast and one-time per repo (~10s for a small service, ~1–2 min for a large one; incremental re-index after that is near-instant). First run also downloads the jina embedding model (~160 MB) once.
Requirements the script checks for: macOS, Homebrew, Node.js, and Python 3.12 (cocoindex's deps don't support 3.13+). It installs Ollama, pipx, and cocoindex-code for you.
develop uses a two-model split so a local model can safely modify
existing files, not just create new ones:
- The reasoning model plans the change and emits change blocks:
### FILE: <path>— a brand-new file, with its full content in a fence.### EDIT: <path>— an existing file, as a lazy sketch: only the changed regions, with... existing code ...markers standing in for everything else.
- For each
### EDIT:, the apply model merges the sketch into the full original file (it is purpose-built for this and avoids the anchor/diff-boundary failures of making one model emit exact diffs).
Safety guarantees (a bad edit never silently corrupts a file):
- New files are only created, never overwrite an existing file;
### EDIT:only touches files that exist. - A merge that would drop an existing top-level
def/class/function(GUARD_SYMBOL_LOSS) or a top-level import (GUARD_IMPORT_LOSS) is rejected (imports are repaired when possible), as is a merge that looks truncated or that leaks an unexpanded... existing code ...marker (GUARD_MARKER_LEAK). developnever writes to.cursor/,.claude/,.agents/, orSKILL.md.- Every change in a run is revertible, which powers the verify loop below.
Verify-and-retry loop (optional). Set VERIFY_CMD (e.g. poetry run pytest -x -q
or make lint) and develop runs it after writing. On failure it reverts the
changes and re-prompts the model with the error output, up to DEVELOP_MAX_ATTEMPTS
(default 3 when VERIFY_CMD is set, else single-shot). If the final attempt still
fails, the last attempt is kept on disk so you can inspect and fix it (set
REVERT_ON_FINAL_FAILURE=true to revert to a clean repo instead). Either way,
every mid-loop attempt is reverted before the next retry so attempts never stack.
Live progress. Because MCP is request/response (a tool call can't stream while
it runs), develop writes a step-by-step log to .devmate/progress.log and opens
it in your editor at the start — retrieval, each file written, each verify attempt
and result — so you can watch it work instead of staring at a silent foreground.
Toggle with PROGRESS_ENABLED / PROGRESS_FILE.
Editor open. Created/edited files are opened in your editor (cursor by default)
so you see them immediately. Toggle with OPEN_EDITED / EDITOR_CMD.
All configuration is via the env block of the devmate entry in your MCP host
config (written by setup.sh):
| Variable | Default | Description |
|---|---|---|
LLM_BASE_URL |
http://localhost:11434/v1 |
Ollama or vLLM endpoint (reasoning model) |
LLM_MODEL |
codestral:22b |
Reasoning model (plans changes, emits edit sketches) |
LLM_TEMPERATURE |
0.1 |
Sampling temperature (low = deterministic, convention-following) |
LLM_NUM_CTX |
32768 |
Context window (Ollama num_ctx); must hold CONTEXT + prompt. 0 = server default |
LLM_TIMEOUT |
180 |
Reasoning request timeout (seconds) |
LLM_MAX_TOKENS |
4096 |
Max tokens per reasoning response |
REPO_ROOT |
(set by setup) | Repo the tools operate on & retrieve from |
CCC_BIN |
~/.local/bin/ccc |
Path to the cocoindex CLI |
| Apply model | (merges develop edit sketches into files) |
|
APPLY_MODEL |
hf.co/QuantFactory/FastApply-7B-v1.0-GGUF:Q4_K_M |
Apply model name |
APPLY_BASE_URL |
(= LLM_BASE_URL) |
Apply model endpoint |
APPLY_TEMPERATURE |
0 |
Apply temperature (0 = deterministic merge) |
APPLY_NUM_CTX |
32768 |
Apply context window; must hold full file + sketch |
APPLY_TIMEOUT |
300 |
Apply merge timeout (seconds) |
APPLY_MAX_TOKENS |
16384 |
Max tokens for a merged file (apply re-emits the whole file; too low → merge truncated & the file skipped). Raise for very large files |
| Retrieval | ||
RETRIEVAL_K |
12 |
Chunks of grounding context per query |
RETRIEVAL_AGENT_K |
3 |
Max chunks from .cursor / .claude / docs/dev per query |
RETRIEVAL_IMPL_GLOB |
core/** |
Implementation-biased pass; set empty to disable |
RETRIEVAL_RELATED_GLOBS |
core/models/**,core/repositories/** |
Data-layer pass on write tasks (comma-separated) |
RETRIEVAL_RELATED_K |
4 |
Chunks per data-layer glob |
RETRIEVAL_TEST_GLOBS |
tests/**,**/tests/**,**/test_*.py,**/*_test.py,**/*.test.*,**/*.spec.*,**/*_test.go |
Globs for the sibling-test pass (develop only, comma-separated) |
RETRIEVAL_TEST_K |
3 |
Reserved, additive chunks of sibling-test context pinned into develop runs (0 disables) |
| Edit safety / loop | ||
GUARD_SYMBOL_LOSS |
true |
Reject a merge that drops an existing def/class/function |
GUARD_IMPORT_LOSS |
true |
Repair (or reject) a merge that drops a top-level import |
GUARD_MARKER_LEAK |
true |
Reject a merge whose output still contains an unexpanded ... existing code ... marker (corrupts the file) |
VERIFY_CMD |
(empty) | Command run after a write; failure reverts + re-prompts |
VERIFY_TIMEOUT |
300 |
Verify command timeout (seconds) |
DEVELOP_MAX_ATTEMPTS |
3 if VERIFY_CMD set, else 1 |
Max develop attempts |
REVERT_ON_FINAL_FAILURE |
false |
On the final failed verify attempt: false keeps the last attempt on disk for inspection; true reverts to a clean repo. Mid-loop attempts always revert before retrying |
PROGRESS_ENABLED |
true |
Write a live step-by-step progress log during develop and open it in the editor (watch it work — MCP can't stream inside one tool call) |
PROGRESS_FILE |
.devmate/progress.log |
Repo-relative path for the live progress log (auto-gitignored) |
OPEN_EDITED |
true |
Open created/edited files in the editor |
EDITOR_CMD |
cursor |
Editor CLI used to open files (e.g. code -r) |
Switching repos = change REPO_ROOT (or re-run setup.sh <repo>) and restart
the host. devmate drives cocoindex with that path, so retrieval and file-writing
follow automatically.
Moving to a shared GPU (vLLM) — change two values, no code changes:
"env": {
"LLM_BASE_URL": "http://gpu-server.internal:8000/v1",
"LLM_MODEL": "Qwen/Qwen2.5-Coder-32B-Instruct"
}If you'd rather not use setup.sh, register the built server yourself:
claude mcp add devmate \
-e LLM_BASE_URL=http://localhost:11434/v1 \
-e LLM_MODEL=codestral:22b \
-e APPLY_MODEL=hf.co/QuantFactory/FastApply-7B-v1.0-GGUF:Q4_K_M \
-e REPO_ROOT=/path/to/your-repo \
-e CCC_BIN=$HOME/.local/bin/ccc \
-- node /path/to/devmate/dist/index.js(You still need cocoindex installed and the repo indexed, and both models pulled
(ollama pull codestral:22b and the apply model) — see the steps in setup.sh.)
src/
├── index.ts # MCP server: develop / review / explain / search (+ verify loop)
├── retrieval.ts # cocoindex multi-pass retrieval + language-agnostic re-rank
├── grounding.ts # grounding guards (abort/warn when no repo context)
├── writer.ts # parses FILE/EDIT blocks, merges sketches, write guards, revert
├── verify.ts # runs VERIFY_CMD after a write (drives the retry loop)
├── editor.ts # opens created/edited files in the editor
├── llm.ts # OpenAI-compatible clients: reasoning (complete) + apply (applyMerge)
├── config.ts # env-based configuration
└── prompts.ts # system prompts per tool
templates/ # cocoindex settings template applied to target repos
scripts/ # settings patcher + manual test harness
setup.sh # one-command, idempotent setup / add-a-repo
dist/ # compiled JS (npm run build)
eval/ # retrieval + model evaluation harness and scorecards