Map a source tree into typed-edge markdown so an AI can find any symbol and see how it connects without reading the source. Python, TypeScript, and JavaScript. Design and rationale in DESIGN.md.
Python analysis uses the standard library only. Other languages are opt-in extras, so the base install stays dependency-free.
pip install trailmap # Python analysis, zero dependencies
pip install trailmap[typescript] # adds TypeScript/TSX and JavaScript/JSX (tree-sitter)
Installs a trailmap console command. Or run it in place: python -m trailmap ....
Python (stdlib ast, always available), and TypeScript/TSX plus JavaScript/JSX
(tree-sitter, the typescript extra — JS reuses the TS adapter and handles both
ES modules and CommonJS require/module.exports). Trailmap maps whatever it
recognizes in a tree and skips files whose language extra isn't installed,
telling you what to install. Each new language is an adapter behind its own
extra; the core stays pure Python.
python -m trailmap <source-tree> -o <output-dir>
Map Trailmap's own source (the demo):
python -m trailmap trailmap -o trailmap-out
INDEX.md— front door and how-to-read.modules/<module>.md— one page per module; frontmatter has imports, body has a section per symbol with its edges.symbols.tsv—fqn kind file line signature doc. Grep it to locate a symbol, or to search intent:docis a one-line summary harvested from the symbol's docstring (Python) or JSDoc/leading comment (TS/JS).edges.tsv—src type dst confidence resolved. Grepsrcfor dependencies,dstfor dependents.candidates.tsv— for a?-unresolved call, the possible in-project targets (caller method candidate n_candidates). A lead, kept out ofedges.tsv.entrypoints.tsv— where execution or the public API enters the tree (entry name fqn kind file line): Python__main__guards and console scripts, JS/TSpackage.jsonbins, and shebang'd files. The "start here" list; also summarized at the top ofINDEX.md.modules.tsv— the module-level dependency graph (src dst edges kinds): the resolved symbol edges rolled up to module granularity, so you can read the architecture — which module depends on which, and how much — without walking every symbol. Each module page also lists itsdepends:in frontmatter.trailmap.json— the full structured graph.
Edges are ast/parser (precise, trust) or heuristic (a lead). Unresolved
targets keep a trailing ?.
Re-running is incremental: each file's extraction is cached (by content hash) in
.trailmap-cache.json in the output directory, so only changed files are
re-parsed. Output is identical whether a module was parsed or reused. Add
.trailmap-cache.json to your .gitignore.
trailmap <src> -o out --dry-run # report added/modified/deleted files, no work
trailmap <src> -o out --no-cache # re-parse everything
Discovery honors .gitignore and .hgignore (from the scanned dir up to the
VCS root), on top of a built-in ignore list for .git, node_modules, .venv,
and the like. Use --no-ignore to map every source file regardless.
Python (stdlib ast), TypeScript/TSX, and JavaScript/JSX (tree-sitter extra).
Resolution is best-effort without a type checker: it follows imports, inferred
receiver types (annotations, constructors, and declared return types), the
inheritance chain, in-project star imports, and TypeScript workspace/tsconfig
package aliases with barrel re-exports, and offers candidate leads for the rest.
Beyond the relationship graph it harvests a one-line doc summary per symbol
(docstring or JSDoc/comment), marks declared entry points (__main__ guards,
console scripts, package.json bins, shebangs), and rolls the edges up to a
module-level dependency graph. Limits are in DESIGN.md. The core is
language-agnostic; new languages are adapters behind their own extra.
pip install trailmap[mcp] adds a trailmap-mcp command: an MCP server that
exposes the lookups as tools for any MCP host (Claude Desktop, Cursor, ...), so
an agent can navigate a codebase without reading raw source. Point a host at it:
{
"mcpServers": {
"trailmap": { "command": "trailmap-mcp" }
}
}
Tools: overview, find_symbol, callers, dependencies, candidates,
module_dependencies (the architecture view for a module), and refresh
(rebuild after code changes). Each takes a root (default .), so
launch the server with the repo as its working directory or pass root per call.
The graph is built in memory and cached per root.
Maintainers: scripts/release.sh minor (or patch/major) bumps the version,
builds a clean dist/, and publishes to PyPI; run with no argument to publish
the current version as-is. Needs uv and a PyPI token in ~/.pypirc. Commit
and push the version change separately.
MIT. See LICENSE.