{"id":48822968,"url":"https://github.com/danlentz/clj-xref","last_synced_at":"2026-04-14T16:00:51.511Z","repository":{"id":351173209,"uuid":"1209831643","full_name":"danlentz/clj-xref","owner":"danlentz","description":" LLM-friendly cross-reference database for Clojure code. Query who-calls, calls-who, who-implements, ns-deps to feed        precise dependency neighborhoods to AI assistants instead of entire source trees. Built on clj-kondo. ","archived":false,"fork":false,"pushed_at":"2026-04-13T23:10:36.000Z","size":35,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-13T23:29:05.328Z","etag":null,"topics":["agentic-coding","clojure","xref"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danlentz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-13T20:32:02.000Z","updated_at":"2026-04-13T23:10:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/danlentz/clj-xref","commit_stats":null,"previous_names":["danlentz/clj-xref"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/danlentz/clj-xref","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fclj-xref","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fclj-xref/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fclj-xref/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fclj-xref/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danlentz","download_url":"https://codeload.github.com/danlentz/clj-xref/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danlentz%2Fclj-xref/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31803790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T11:13:53.975Z","status":"ssl_error","status_checked_at":"2026-04-14T11:13:53.299Z","response_time":153,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agentic-coding","clojure","xref"],"created_at":"2026-04-14T16:00:31.787Z","updated_at":"2026-04-14T16:00:51.497Z","avatar_url":"https://github.com/danlentz.png","language":"Clojure","funding_links":["https://github.com/sponsors/borkdude"],"categories":[],"sub_categories":[],"readme":"# clj-xref\n\nA cross-reference database for Clojure code, built on [clj-kondo](https://github.com/clj-kondo/clj-kondo) static analysis ([sponsor Borkdude!](https://github.com/sponsors/borkdude)). Analyze your project once, then ask questions like \"who calls this function?\", \"what does this function depend on?\", and \"where is this protocol implemented?\"\n\n## What is it?\n\nclj-xref builds a searchable cross-reference database from your Clojure source code. It works in two phases:\n\n1. **Generate** — Analyze your source files and write an EDN database to disk (via a Leiningen plugin or deps.edn tool)\n2. **Query** — Load the database and ask questions about your code from the REPL, scripts, or other tools\n\nThink of it as [ctags](https://ctags.io/) or [cscope](http://cscope.sourceforge.net/) for Clojure, but with semantic understanding of vars, namespaces, protocols, and multimethods. The API is inspired by [SBCL's `sb-introspect`](http://www.sbcl.org/manual/#sb_002dintrospect) (`who-calls`, `who-references`, `who-macroexpands`) and [Smalltalk's senders/implementors](https://wiki.c2.com/?FinderTool) queries.\n\n### Why you might want it\n\n**Feeding context to LLMs.** Instead of guessing or having it search your entire `src/` tree, it can query the xref database for the dependency neighborhood of the function you're working on — just the relevant code, in far fewer tokens.\n\n**Understanding unfamiliar code.** You inherit a large Clojure codebase. You need to know what calls `process-payment` before you change its signature. `who-calls` gives you the answer instantly, across every namespace.\n\n**Dead code detection.** Find vars that are defined but never referenced from anywhere. Stop carrying code that nothing uses.\n\n**Codebase visualization.** Export the call graph or namespace dependency graph for documentation, onboarding, or architecture reviews.\n\n**CI and automation.** Run `lein xref` or `clj -T:xref generate` in CI to produce a cross-reference artifact. Use it downstream for impact analysis, change-risk estimation, or custom linting rules.\n\n**REPL-driven exploration.** Load the database in your REPL and explore interactively. Since queries return plain Clojure maps and vectors, you can compose them freely with `filter`, `map`, `group-by`, or anything else.\n\n### What it is not\n\n- **Not an IDE or editor plugin.** clj-xref is a library. It produces data. Editor integration can be built on top of it (and the API is designed for that), but clj-xref itself has no UI.\n- **Not a replacement for clojure-lsp.** If you want real-time find-references in your editor, use [clojure-lsp](https://clojure-lsp.io/). clj-xref is for programmatic, batch, and REPL use cases.\n- **Not a type checker or linter.** Use [clj-kondo](https://github.com/clj-kondo/clj-kondo) for that. (clj-xref uses clj-kondo internally for analysis.)\n\n## Installation\n\n### Leiningen\n\nAdd clj-xref to your `:plugins` vector in `project.clj`:\n\n```clojure\n:plugins [[com.github.danlentz/clj-xref \"0.1.0\"]]\n```\n\nTo also use the query API from your REPL, add it to `:dependencies`:\n\n```clojure\n:dependencies [[com.github.danlentz/clj-xref \"0.1.0\"]]\n```\n\n### deps.edn\n\nAdd a tool alias to your `deps.edn`:\n\n```clojure\n{:aliases\n {:xref {:extra-deps {com.github.danlentz/clj-xref {:mvn/version \"0.1.0\"}}\n         :ns-default clj-xref.tool}}}\n```\n\nTo use the query API in your project, add clj-xref as a dependency:\n\n```clojure\n{:deps {com.github.danlentz/clj-xref {:mvn/version \"0.1.0\"}}}\n```\n\n## Usage\n\n### Generating the database\n\n```bash\n# Leiningen — analyzes :source-paths and :test-paths\nlein xref\n\n# deps.edn\nclj -T:xref generate\n\n# Custom paths or output location\nlein xref :output target/xref.edn\nclj -T:xref generate :paths '[\"src\"]' :output '\"target/xref.edn\"'\n```\n\nThis produces `.clj-xref/xref.edn` — a plain EDN file containing every var definition, var usage, protocol implementation, and multimethod dispatch in your project.\n\nIncremental mode re-analyzes specific files and merges into the existing database:\n\n```bash\nlein xref :only src/myapp/orders.clj\nclj -T:xref generate :only '[\"src/myapp/orders.clj\"]'\n```\n\nNote: incremental mode only re-analyzes the specified files. If you change an exported definition (e.g., rename a function or change it to a macro), callers in other files retain stale metadata until a full rebuild. The update is aborted if analysis errors are detected, preserving the existing database.\n\n### Querying from the REPL\n\n```clojure\n(require '[clj-xref.core :as xref])\n\n;; Load the generated database\n(def db (xref/load-db))\n\n;; Who calls this function?\n(xref/who-calls db 'myapp.orders/process-payment)\n;; =\u003e [{:kind :call, :from myapp.web/checkout-handler,\n;;      :to myapp.orders/process-payment,\n;;      :file \"src/myapp/web.clj\", :line 47, :col 5, :arity 2}\n;;     {:kind :call, :from myapp.batch/retry-failed,\n;;      :to myapp.orders/process-payment,\n;;      :file \"src/myapp/batch.clj\", :line 23, :col 7, :arity 2}]\n\n;; What does this function call?\n(xref/calls-who db 'myapp.web/checkout-handler)\n\n;; Who implements this protocol?\n(xref/who-implements db 'myapp.protocols/Billable)\n\n;; What dispatch values exist for this multimethod?\n(xref/who-dispatches db 'myapp.events/handle-event)\n\n;; Which namespaces depend on this one?\n(xref/ns-dependents db 'myapp.orders)\n\n;; Find dead code\n(xref/unused-vars db)\n\n;; Transitive call graph (depth 3, outgoing)\n(xref/call-graph db 'myapp.orders/process-payment {:depth 3 :direction :outgoing})\n;; =\u003e #{[myapp.orders/process-payment myapp.db/transact]\n;;      [myapp.db/transact myapp.db/get-conn] ...}\n\n;; Search for vars by name\n(xref/apropos db #\"process\")\n```\n\nSince results are plain maps and vectors, compose with standard Clojure:\n\n```clojure\n;; All external callers of process-payment (excluding same-namespace calls)\n(-\u003e\u003e (xref/who-calls db 'myapp.orders/process-payment)\n     (remove #(= \"myapp.orders\" (namespace (:from %)))))\n\n;; Vars in myapp.util that nothing in the project references\n(xref/unused-vars db)\n```\n\n### Querying without generating a file\n\nIf you don't need the EDN file, you can analyze and query in-memory directly:\n\n```clojure\n(def db (xref/analyze [\"src\" \"test\"]))\n(xref/who-calls db 'myapp.orders/process-payment)\n```\n\nThis requires clj-kondo on the classpath.\n\n## Query API Reference\n\nAll query functions take a database (from `load-db` or `analyze`) and return vectors of maps.\n\n| Function | Returns |\n|---|---|\n| `(who-calls db sym)` | Call sites of `sym` |\n| `(calls-who db sym)` | Vars called by `sym` |\n| `(who-references db sym)` | All references to `sym` (calls, reads, macroexpansions) |\n| `(who-macroexpands db sym)` | Sites where the macro `sym` is expanded |\n| `(who-implements db sym)` | Implementations of the protocol `sym` |\n| `(who-dispatches db sym)` | `defmethod` dispatch values for the multimethod `sym` |\n| `(ns-vars db ns-sym)` | All var definitions in a namespace |\n| `(ns-deps db ns-sym)` | Namespaces that `ns-sym` depends on |\n| `(ns-dependents db ns-sym)` | Namespaces that depend on `ns-sym` |\n| `(unused-vars db)` | Vars defined but never referenced |\n| `(call-graph db sym opts)` | Transitive call graph as `#{[from to] ...}` edges |\n| `(apropos db pattern)` | Vars matching a name pattern (string or regex) |\n\n### Xref entry shape\n\nEach entry in a query result is a map:\n\n```clojure\n{:kind     :call          ; :call, :reference, :macroexpand, :dispatch, :implement\n :from     'myapp.web/handler  ; the var containing this reference\n :to       'myapp.db/query     ; the var being referenced\n :file     \"src/myapp/web.clj\"\n :line     42\n :col      5\n :arity    2}             ; for :call kind — which arity was used\n```\n\n### Graphviz output\n\nThe `clj-xref.graph` namespace generates DOT format for visualization:\n\n```clojure\n(require '[clj-xref.graph :as graph])\n\n;; Namespace dependency graph\n(spit \"ns-deps.dot\" (graph/ns-dep-dot db))\n\n;; Call graph from a specific function\n(spit \"calls.dot\" (graph/call-graph-dot db 'myapp.web/handler {:depth 2}))\n```\n\nThen render with `dot -Tpng ns-deps.dot -o ns-deps.png`.\n\nHere is clj-xref's own namespace dependency graph, generated by running the tool on itself:\n\n![Namespace dependencies](doc/ns-deps.png)\n\nAnd the internal call graph (project-internal edges only):\n\n![Internal call graph](doc/call-graph.png)\n\n## How it works\n\nclj-xref uses [clj-kondo](https://github.com/clj-kondo/clj-kondo) as its analysis engine. clj-kondo statically parses your Clojure source (without evaluating it) and produces detailed analysis data: every var definition, every var usage, every protocol implementation.\n\nclj-xref transforms this raw analysis into a normalized data model and writes it as EDN. At query time, it reads the EDN and builds in-memory indexes (`group-by :to`, `group-by :from`, `group-by :file`) for O(1) lookups.\n\n```\nSource files  --\u003e  clj-kondo  --\u003e  clj-xref.analyze  --\u003e  .clj-xref/xref.edn\n                                                                |\n                                                          clj-xref.core\n                                                                |\n                                                     who-calls, ns-deps, ...\n```\n\n### Limitations\n\n- **Macros.** clj-xref sees macro call sites (as `:macroexpand` entries), but cannot see what the macro expands into. If a macro generates calls to other functions, those calls are invisible unless clj-kondo has a [hook](https://github.com/clj-kondo/clj-kondo/blob/master/doc/hooks.md) for that macro.\n- **Dynamic dispatch.** `(map f coll)` — clj-xref knows that `map` is called, but cannot resolve what `f` is. Higher-order function patterns create edges that no static analysis can fully capture.\n- **No runtime data.** clj-xref works purely from source. It does not evaluate your code or require a running REPL. This is a deliberate tradeoff: no side effects, no startup penalty, but no access to runtime-only information.\n\n## EDN Database Format\n\nThe generated file is plain, human-readable EDN:\n\n```clojure\n{\n :version 1\n :generated \"2026-04-13T20:23:57Z\"\n :project \"my-app\"\n :paths [\"src\" \"test\"]\n :namespaces [\n  {:name my.app.core, :file \"src/my/app/core.clj\", :line 1, :col 1}\n ]\n :vars [\n  {:name my.app.core/main, :ns my.app.core, :local-name main, ...}\n ]\n :refs [\n  {:kind :call, :from my.app.core/main, :to my.app.db/connect, :file \"src/my/app/core.clj\", :line 12, :col 5}\n ]\n}\n```\n\nOne entry per line within vectors, so the file is greppable and diffable.\n\n## Measuring token savings\n\nclj-xref includes a benchmark that measures the token reduction from xref-guided context selection. It asks Claude the same questions about the codebase under two strategies — whole source tree vs xref-guided neighborhood — and compares input token counts and answer quality.\n\n```bash\n# Requires ANTHROPIC_API_KEY and clj-http (included in :dev profile)\nlein measure-improvement\nlein measure-improvement :model claude-sonnet-4-6\n```\n\nOn clj-xref's own source, the xref-guided approach selects 1-3 files instead of 8, reducing context by 66-80%.\n\n## Acknowledgments\n\nclj-xref is built entirely on the analysis engine of [clj-kondo](https://github.com/clj-kondo/clj-kondo) by [Michiel Borkent (borkdude)](https://github.com/borkdude). clj-kondo's fast, accurate static analysis of Clojure code — without requiring evaluation — is what makes clj-xref possible. If you find clj-xref useful, consider [sponsoring clj-kondo](https://github.com/sponsors/borkdude).\n\n### References\n\n- [clj-kondo](https://github.com/clj-kondo/clj-kondo) — Static analyzer and linter for Clojure. Provides the analysis data that clj-xref transforms into a queryable cross-reference database.\n- [clj-kondo analysis data format](https://github.com/clj-kondo/clj-kondo/blob/master/analysis/README.md) — Documentation for the `:analysis` output that clj-xref consumes.\n- [SBCL cross-reference facility](http://www.sbcl.org/manual/#sb_002dintrospect) — The `who-calls`, `who-references`, `who-macroexpands` API that inspired clj-xref's query interface.\n- [clojure-lsp](https://clojure-lsp.io/) — Language Server Protocol implementation for Clojure, also built on clj-kondo. Provides real-time editor integration; clj-xref targets programmatic and batch use cases instead.\n\n## License\n\nCopyright 2026\n\nThis program and the accompanying materials are made available under the\nterms of the Eclipse Public License 2.0 which is available at\nhttps://www.eclipse.org/legal/epl-2.0.\n\nThis Source Code may also be made available under the following Secondary\nLicenses when the conditions for such availability set forth in the Eclipse\nPublic License, v. 2.0 are satisfied: GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or (at your\noption) any later version, with the GNU Classpath Exception which is available\nat https://www.gnu.org/software/classpath/license.html.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanlentz%2Fclj-xref","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanlentz%2Fclj-xref","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanlentz%2Fclj-xref/lists"}