CodeMap's indexers and bridges are plugin-first by design (ADR-004). Internal indexers and third-party packages register through the same entry-point groups and the same Protocols โ there is no "first-class" path reserved for the main repository.
This guide walks through the steps to ship a new language indexer (or a
new bridge) as an independent PyPI package. The reference
implementation is the
codemap-typescript plugin that lives
in this repository under plugins/.
A plugin is a normal Python package with one entry-point declaration in
pyproject.toml. The minimum useful shape is:
codemap-yourlang/
โโโ pyproject.toml # entry_points + dependency on `codemap`
โโโ README.md
โโโ src/codemap_yourlang/
โโโ __init__.py # exports the indexer class
โโโ indexer.py # implements the protocol
Tests under tests/ go through the same codemap.core and
codemap.indexers.base imports the host CLI uses โ there is no extra
test harness to learn.
Implement
codemap.indexers.base.Indexer:
class Indexer(Protocol):
name: ClassVar[str] # unique short id ('typescript', 'go', ...)
version: ClassVar[str] # plugin version, independent of CodeMap
file_patterns: ClassVar[list[str]] # ['*.ts', '*.tsx']
languages: ClassVar[list[str]] # ['typescript']
def supports(self, path: Path) -> bool: ...
def index_file(
self,
path: Path,
source: bytes,
ctx: IndexContext,
) -> IndexResult: ...IndexResult is a Pydantic model exposing symbols, edges, routes,
diagnostics. SymbolIDs use the SCIP grammar (ADR-001) โ see
codemap.core.symbol for the parser
and helpers.
Use scip-<lang> for the obvious one-language case. Reserve a fresh
scheme name for any non-source asset (scip-openapi, scip-prisma,
โฆ) so other plugins can grow alongside without collision.
SCHEME = "scip-yourlang"The convention every existing indexer follows: the file path is a chain
of namespace descriptors. The trailing component keeps its extension,
making symbols visually identifiable at the CLI:
scip-yourlang . . . src/foo/Bar.yl/Klass#method().
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโ โโโโโโโ
scheme path namespaces type method
[project.entry-points."codemap.indexers"]
yourlang = "codemap_yourlang:YourLangIndexer"That single line is the only coupling between your package and the
host. The plugin is now discoverable by codemap doctor:
$ pip install codemap-yourlang
$ codemap doctor
Registered indexers
โ name โ version โ languages โ file_patterns โ
โ yourlang โ 0.1.0 โ yourlang โ *.yl โ
โฆ
Bridges register under the parallel codemap.bridges group and
implement
codemap.core.bridge.base.Bridge.
Typical bridges:
- HTTP-route resolver (pattern:
codemap-yourlang-http) - Cross-module call resolver (pattern:
codemap-yourlang-cross-module) - Container / DI wiring resolver
- IDL / Schema cross-validators (protobuf, OpenAPI, GraphQL, โฆ)
A bridge runs after every indexer and operates on the
ReadOnlyStore view, so it can be language-neutral if its inputs are
metadata the indexer attached to Symbol.extra.
pytest. The codemap.core / codemap.indexers.base imports work
directly, so unit tests look like the ones in
tests/unit/test_python_indexer.py
or the codemap-typescript plugin tests.
codemap.indexers.disabled: ["yourlang"]in.codemap/config.yamldisables the plugin without uninstalling.codemap doctorandcodemap config showboth reflect plugin state immediately โ no main-repo change ever needed.codemap index --dry-runreports per-indexer file counts so the plugin's contribution is visible.
Follow SemVer on the plugin and pin a minimum codemap version in
dependencies. The Indexer / Bridge Protocols evolve under the design
doc's ADR-006 schema-version policy; a minor CodeMap bump never
breaks a plugin that satisfied the previous protocol.
plugins/codemap-typescript/ is a
fully working example:
tree-sitter-typescriptfor parsing.*.ts+*.tsxfile patterns.- Functions / classes / interfaces / module-level variables.
pip install -e plugins/codemap-typescript/makescodemap doctorlist it next to the built-in indexers.- 14 unit tests, kept inside the plugin package so the host repo's CI doesn't have to know about them.
That's the entire footprint. No magic, no main-repo PR, no privileged "core" status โ your plugin and the built-in Python indexer use the same door.