Getting started#
This walkthrough builds an index for a Sphinx project and then answers a few questions with it. It uses this repository’s own documentation as the corpus, so you can follow along by cloning it, but every step works the same against any Sphinx project.
Set up the environment#
Sphinx Lens runs inside your documentation build. It has to import the same
MyST, autodoc, theme, and extension packages your conf.py imports, which means
it belongs in the same environment as your docs dependencies rather than in an
isolated one.
From the project root:
uv sync --group docs
That resolves dependencies and creates the local virtual environment. In your
own project, add sphinx-lens to whichever group already holds sphinx.
Build the index#
Sphinx Lens registers a builder named lens through Sphinx’s entry points, so
it is available as soon as the package is installed. You do not have to list it
in extensions:
uv run --group docs sphinx-build -b lens docs/ docs/_build/lens/
The result is docs/_build/lens/index.json: an ordinary Sphinx artifact, in the
same place as your HTML, with the same lifecycle. make clean removes it along
with everything else, and CI can cache or publish it the way it already caches
or publishes builds. This repository exposes the command as make lens.
If you prefer a single command that does not require you to spell out the paths,
uv run sphinx-lens build docs/ wraps the same builder and writes to the same
default location.
:::{note}
Both forms execute your conf.py, exactly like sphinx-build -b html does.
Index projects you trust, from a checkout you control.
:::
Find something#
Start with a phrase. locate searches titles, canonical names, and scoped body
text, and returns ranked references:
uv run --group docs sphinx-lens locate "separate artifact" --index docs/_build/lens/
Each result line begins with the reference you use everywhere else. There are three shapes, and they are stable across builds:
Shape |
Example |
What it is |
|---|---|---|
Document |
|
A source file |
Section |
|
A heading and its content |
Domain object |
|
Anything Sphinx’s domains know about |
When you already know the shape of what you want, say so and skip the ranking
entirely. This finds every documented object whose name ends in TOKEN:
uv run --group docs sphinx-lens locate 'TOKEN$' --regex --kind object \
--index docs/_build/lens/
In a project with an API, --domain py narrows the same query to Python
objects, which is usually what you want for a lookup like
'QuerySet\.(get|filter)'.
Read only what you need#
Hand a reference to read and you get that scope’s text, composed from the
scope and its descendants. The file it happens to live in does not come with it:
uv run --group docs sphinx-lens read std:term:PYTHONPATH --index docs/_build/lens/
A glossary term returns its definition. A section returns that section and its subsections. A class returns the class and its methods. This is the difference that matters when the consumer is paying by the token.
inspect returns the same entry as structured JSON (kind, title, document,
anchor, parent, domain) for when you need the metadata rather than the prose.
Follow the graph#
Sphinx resolved every cross-reference in the project while building. links
gives you both directions of that graph for any reference:
uv run --group docs sphinx-lens links std:term:GH_TOKEN --index docs/_build/lens/
That returns the two chapters that mention the term, resolved down to the exact section each mention came from.
Outgoing links tell you what a scope depends on. Incoming links tell you which parts of the documentation consider it relevant, which is often a better ranking signal than any text search: a concept cited from twenty places is the one the project actually treats as central, whether or not it uses the words you searched for.
Use it from Python#
Everything the CLI does is a thin layer over the Lens object:
from sphinx_lens import Lens
lens = Lens.open("docs/_build/lens/")
section = lens.resolve("design#why-a-separate-artifact")
matches = lens.locate("separate artifact")
outgoing = lens.references(section.ref)
Lens.open() also warns when the sources it was built from have changed on
disk, so a stale index says so rather than answering with last week’s
documentation.
CLI and Python API documents the full surface.
Give it to an agent#
The package ships an agent skill that teaches a coding agent this workflow: locate, then read the narrowest useful scope, then follow links only if needed. From a project that depends on Sphinx Lens:
uvx library-skills install --skill sphinx-lens --yes
Library Skills finds the skill inside the
installed package and links it into the project’s .agents/skills/ directory,
so it stays in sync with the version you have installed.
Next steps#
How it works covers the model behind the index and why it is a separate artifact rather than a reuse of
objects.invorsearchindex.js.Real-world corpora reports what happens on Django and CPython, including the parts that do not work yet.
Development workflow is for contributors.