Skip to content

Developer workbook ​

Questions and answers for contributors to the duckdb-code project: prerequisites, setup, the Makefile workflow, testing, project principles, how to add an extractor, and how changes land.

You may also look at the user guide and the examples for more information.

What are good first contributions? ​

You do not need to understand the whole codebase to help:

  • Add an extractor — the main extension point; see below.
  • Improve the docs — clarify a page or add a worked example.
  • Report a rough edge — a clear issue with a minimal reproduction.

If unsure where to start, open an issue describing what you want to do.

What principles should a change follow? ​

Contributions land fastest when they fit the project's shape:

  • Do one thing well. This is a structural building block — impact, explain, report. Semantic and vector search belong in the sibling duckdb-vectors, not here.
  • Boring, composable technology. Prefer the standard library and DuckDB's own features over new dependencies.
  • Framework-agnostic. No hard dependency on a specific agent framework.
  • Read-mostly and fast. Queries never mutate the graph; writes happen only on index (including the mcp server's optional startup build).

How do I add an extractor? ​

The extractor registry is the main extension point. An extractor parses one language family with tree-sitter and emits the graph's nodes (file, module, class, function, import) and edges (defines, calls, inherits, references, imports). See src/duckdb_code/extractors/ for the base, the registry, and the Python and JavaScript/TypeScript examples. Register your extractor in the registry so the indexer picks it up for the file types it claims, and keep heavy parser dependencies in step with the project's existing tree-sitter bindings.

What are the documentation conventions? ​

Documentation lives in documentation/, GitHub-Flavored Markdown with mermaid (no ASCII art), ATX sentence-case headers, underscores for bold, and fenced code blocks. Pages are reachable from the VitePress sidebar in documentation/.vitepress/config.mts; add a sidebar entry when you add a page.

How do I open a good pull request? ​

  • Keep each pull request focused on a single change.
  • Write a clear description: what changed and why.
  • Match the surrounding style; Ruff decides formatting.
  • Update the README or docstrings when you change behavior.
  • Run make lint, make test, and make self-test first; all should pass.
  • Be kind in review.

How do I report an issue? ​

Open an issue with what you expected and what happened, a minimal snippet or steps to reproduce, and your Python version plus how the store was configured (database path, indexed root).

What are the prerequisites? ​

Install these before running make setup. The first two are genuine prerequisites make setup cannot install for you; it checks they are present and installs the rest.

ToolWhyHow to install
Python 3.12+Runs the package and the testspython.org, pyenv, or your package manager
uvCreates the venv, installs deps, buildscurl -LsSf https://astral.sh/uv/install.sh | sh or brew install uv
Node.js + npmProvides markdownlint-cli for Markdown linting and VitePress for the docs sitenodejs.org, nvm, or your package manager

Ruff, mypy, and pytest are not prerequisites — make setup installs them into the virtual environment via the [dev] extra. There is no Docker or hadolint step: duckdb-code ships no Dockerfile.

What does each Makefile target do? ​

The Makefile consolidates the most frequent commands used throughout this project. Run make with no target to list them. The essentials:

  • make setup — check prerequisites, create the .venv, install tooling
  • make lint — Ruff lint and format check, mypy, and Markdown lint
  • make test — the pytest suite
  • make self-test — capability evidence: index the package's own source and run labelled requests against it
  • make quality — deterministic tool-quality checks on a pinned public corpus (see How do I check tool quality?)
  • make format — apply Ruff formatting
  • make build — build the wheel and sdist with uv
  • make clean — remove build artifacts and caches

For the documentation site: make setup-documentation (npm ci), make serve-documentation (local dev server), make build-documentation (the static site into documentation/.vitepress/dist), make preview-documentation, and make clean-documentation.

How do I set up a development environment? ​

shell
$ git clone https://github.com/bernard357/duckdb-code.git
$ cd duckdb-code
$ make setup   # checks prerequisites, creates the venv, installs tooling

make setup runs check-prerequisites, then setup-python (uv venv + the package with the dev extra) and setup-markdownlint (npm). Run make setup-python for just the Python environment if you manage the linters yourself.

make setup is the contributor path: an editable install with the dev extras (ruff, mypy, pytest) for running the suite and the linters. It is distinct from the end-user install story, which is uvx duckdb-code (or uv tool install duckdb-code) and pulls only the runtime dependencies on demand — see the README and the Kilo Code setup procedure. Reach for make setup when you are changing the package, not when you are only running it.

What does the lint cover? ​

shell
$ make lint        # ruff check + ruff format --check + mypy, markdownlint

Python (Ruff lint and format, plus mypy type checking over src/duckdb_code) and Markdown (markdownlint via .markdownlint.json). There is no Dockerfile lint — the package ships no Dockerfile. Use make format to apply Python formatting. All checks should pass before you open a pull request.

How do I run the tests? ​

shell
$ make test        # pytest -q
$ make self-test   # dogfood: index the package's own source

make test is the hermetic suite under tests/. make self-test indexes the package's own src/duckdb_code/ tree into a throwaway database and runs a sequence of labelled requests, each printing a result and asserting a structural fact — living evidence that the tool works on a real multi-module codebase. Add a test for any behavior you change or add.

How do I check tool quality? ​

shell
$ make quality                              # all cases, default embedder
$ make quality QUALITY_ARGS="--offline"     # hashing embedder, no semantic cases
$ make quality QUALITY_ARGS="--stdio --markdown tmp/quality.md"

make quality runs scripts/check_tool_quality.py. It calls each MCP tool directly, with no model or agent, and asserts on the JSON object the tool returns. The cases live in fixtures/benchmarks/tool-checks.yaml and follow questions q01–q09 of the Kilo Code benchmark (see the performance workbook). One more case checks that impact stays bounded on a high fan-in target.

  • Corpus. The four public repositories (duckdb-code, duckdb-vectors, boringsteps, ileap-mcp) at the commits pinned in fixtures/benchmarks/kilo-questions.yaml. They are cloned under ~/.cache/duckdb-code/quality/corpus, from ~/Public/<repository> when present (--source-root), else from GitHub, and indexed into a cache database next to it. The index is rebuilt only when the commits, the embedder, or the package source change.
  • Checks. Expected symbols and paths, at a rank (contains_all with within), precision (equals_set, excludes_any), list sizes (count), the bounds of a truncated impact (bounded), and a 128 KB byte budget on every response. The fixture header documents each check type.
  • Transport. Calls go in-process through the server's call_tool by default. --stdio spawns duckdb-code mcp and talks MCP over its stdin/stdout; both give the same scorecard.
  • Known gaps. A check marked xfail names the issue that tracks it (#38, #39, #41). Its failure is reported as XFAIL. When it starts passing it is reported as FIXED, so remove the marker. The command exits non-zero only on a regression (FAIL) or a tool error (ERROR).

Two runs on the same commit give the same scorecard. Run it before and after a change to the indexer, the resolver, or a tool, and read the diff.

How are releases cut? ​

Releases are tag-driven. The package version comes from the git vYYMMDD tag via hatch-vcs; there is no version string to bump by hand. Pushing a vYYMMDD tag triggers the deploy-pypi workflow, which builds the wheel and sdist and publishes to PyPI over OIDC Trusted Publishing. The documentation site redeploys to GitHub Pages from the deploy-vitepress workflow on push to main.