Skip to main content

Brownfield Cartographer

CI License: Apache-2.0 Python 3.12+

What breaks if I change this? Cartographer finds the dependencies your language server can't see: a table written by a Python job, transformed in SQL, consumed by a dbt model. It maps them statically and locally, with file and line evidence, for you and your coding agent.

cartographer analyze .
cartographer impact models/staging/stg_orders.sql   # what is downstream of this file?
cartographer lineage customers --direction upstream # where does this table come from?
cartographer lineage customers::first_order          # which source columns feed this column?

Language servers and grep answer questions about code symbols. Cartographer answers questions that cross language boundaries through names in strings and configuration, starting with data lineage across Python, SQL and dbt. Every result carries source evidence and coverage, and unsupported cases are reported rather than guessed. It runs offline, needs no API key, and never executes repository code.

One query engine serves the CLI, a Python API, and a read-only MCP server for Claude Code, Codex and Cursor. The support table below lists current capabilities and known gaps.

Try it in 60 seconds

With uv installed, run it on dbt's public example project. Nothing is installed permanently, and analysis took 3 seconds here:

git clone --depth 1 https://github.com/dbt-labs/jaffle_shop && cd jaffle_shop
uvx --from brownfield-cartographer cartographer analyze .
uvx --from brownfield-cartographer cartographer impact models/staging/stg_payments.sql
analyze_impact | complete | snapshot eefa28cd4e13
┌───────────────┬──────────────┬──────────────────────────────────────┐
│ Symbol / node │ Relationship │ Evidence                             │
├───────────────┼──────────────┼──────────────────────────────────────┤
│ stg_payments  │ PRODUCES     │ models/staging/stg_payments.sql:1-26 │
│ orders        │ PRODUCES     │ models/orders.sql:1-57               │
│ customers     │ PRODUCES     │ models/customers.sql:1-70            │
└───────────────┴──────────────┴──────────────────────────────────────┘

Column lineage follows renames through each model:

uvx --from brownfield-cartographer cartographer lineage customers::number_of_orders
trace_data_lineage | complete | snapshot eefa28cd4e13
┌──────────────────────┬──────────────┬────────────────────────────────────┐
│ Symbol / node        │ Relationship │ Evidence                           │
├──────────────────────┼──────────────┼────────────────────────────────────┤
│ stg_orders::order_id │ DERIVES      │ models/customers.sql:1-70          │
│ raw_orders::id       │ DERIVES      │ models/staging/stg_orders.sql:1-24 │
└──────────────────────┴──────────────┴────────────────────────────────────┘

In Claude Code, the plugin lets you ask the same questions in plain language.

Install and analyze

Requires Python 3.12+ and Git. Install the command-line tools from PyPI with uv or pipx:

uv tool install brownfield-cartographer
# or: pipx install brownfield-cartographer
cartographer --version

# Local analysis needs no network, API key, or model service.
cartographer analyze /path/to/repository

# Force a fresh rebuild or choose an output root.
cartographer analyze /path/to/repository --full -o ./results

# Optional: pin the SQL dialect instead of choosing the best fit per file.
cartographer analyze /path/to/repository --dialect bigquery

# Optional: use a previously generated dbt v12 manifest.
cartographer analyze /path/to/dbt-project --dbt-manifest target/manifest.json

To run from a source checkout instead, clone the repository, run uv sync --frozen, and prefix commands with uv run, as in the examples below.

An explicit GitHub URL input still downloads a checkout:

cartographer analyze https://github.com/owner/repository

Local analysis never uploads source and never calls a model.

Outputs and queries

For a local checkout, artifacts default to <repository>/.cartography/<repository-name>/. With an explicit --output, local repositories use <output-root>/.cartography/<repository-name>--<path-hash>/ so same-named checkouts do not overwrite one another. The hash identifies the resolved checkout path; moving a checkout creates a separate index. The JSON analysis result includes artifact_directory, which can be passed to query commands as --index-dir. Existing custom-output indexes are left untouched; reanalyzing writes to the new directory. GitHub URL inputs use the current directory by default and an owner--repository folder name.

Artifact Purpose
index.json Complete versioned graph, source locations, metadata, coverage
module_graph.json Structural graph view, including functions
lineage_graph.json Dataset/transformation graph view
analysis_summary.json Complete/partial status, diagnostics and stage results
CODEBASE.md Static repository context
onboarding_brief.md Key modules, data entry/exit points and change hotspots
cartography_trace.jsonl Retained run history with run IDs
state.json Last successful snapshot's content fingerprint

Explore interactively with cartographer query /path/to/repository. It accepts find, impact, deps, dependents, lineage, lineage-down, context, overview, help and quit, using the same engine as the commands below.

Or use individual commands with human-readable or JSON output:

uv run --no-sync cartographer overview /path/to/repository --json
uv run --no-sync cartographer find run --repo /path/to/repository --json
uv run --no-sync cartographer impact producer.py --repo /path/to/repository
uv run --no-sync cartographer lineage output --repo /path/to/repository --direction upstream --json

Use --index-dir for a custom output location. Python callers use brownfield_cartographer.query_api.QueryEngine.open(Path(repo)) and the same query methods. Responses carry snapshot identity, coverage, capped-output markers, and available source evidence. Ambiguous targets are rejected.

Review pull requests

cartographer diff reports what a branch changes and what that can affect, for a PR comment or a quick check before pushing:

cartographer diff --base main          # Markdown
cartographer diff --base main --json   # for scripts

Changed lines since the merge base with --base (committed, uncommitted and untracked) are mapped to functions, modules, SQL statements, dbt models, schema YAML and Airflow tasks, and their downstream dependents are listed with evidence. The merge base is analyzed too, so removed code counts: deleting a module, function or model reports the importers, callers and models that still use it.

To comment on every pull request, add a workflow:

on: pull_request
permissions:
  contents: read
  pull-requests: write
jobs:
  impact:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0   # the merge base must be available
      - uses: Sanoy24/the-brownfield-cartographer@v0.9.0

The action keeps one comment up to date and also writes the report to the job summary, which still works for pull requests from forks.

Connect your AI assistant

Claude Code plugin (needs uv; nothing else to install or configure):

/plugin marketplace add Sanoy24/the-brownfield-cartographer
/plugin install brownfield-cartographer@brownfield-cartographer

The plugin starts the MCP server for the current project and adds a data-impact skill. Ask questions such as "what breaks if I change stg_orders?". If the project has not been analyzed yet, the tools say so and the assistant runs cartographer analyze (offline; repository code is never executed) before answering.

Any MCP host:

cartographer analyze /absolute/path/to/repository
cartographer-mcp --repo /absolute/path/to/repository

The stdio server exposes get_architecture, find_symbol, trace_dependencies, analyze_impact, trace_data_lineage, and get_code_context. It reads a previously analyzed index inside a fixed repository scope and never calls a model. See MCP setup for Claude Code, Codex, and Cursor.

find is lexical matching. impact reports potential dependents, not a guarantee that every consumer will break. Read the persisted coverage and diagnostics before relying on results from an incomplete scan.

Analysis returns exit code 0 for complete/unchanged snapshots, 2 for partial coverage, and 1 for an input or fatal run error. A partial run does not advance the successful checkpoint. Changed working trees currently use full rebuilds; unchanged successful snapshots can reuse the complete index.

Support and safety

Input Current support
Python Functions/classes, supported absolute/relative imports, and calls between repository functions (same-module names, imported names and modules, self/cls methods; calls on other objects and dynamic calls are not guessed); conservative literal I/O associations (pandas, Spark readers and writers). SQL passed to execute/executemany/executescript, .sql() and read_sql is parsed from literals and string constants (same module or imported from another repository module), including text() and DB-API placeholders. f-strings that interpolate only values (comparisons, IN, LIMIT, quoted literals) are resolved; SQL with interpolated table names or projections, concatenation or .format is reported as dynamic, not guessed
SQL SELECT/UNION, CTAS, INSERT, UPDATE, DELETE and MERGE; statement spans and table dependencies. DDL and transaction statements without table lineage are skipped; other statements, such as COPY, are reported individually without dropping the rest of the file. Each file is parsed with the best-fitting dialect (DuckDB, BigQuery, Snowflake, Postgres, MySQL, Spark, T-SQL, Trino), recorded as sql:<dialect> in evidence; --dialect pins one
dbt Explicit manifest v12 ingestion for models, seeds, snapshots and sources; basic SQL/YAML fallback without a manifest. Projects are found anywhere in the repository, including several per repository
Columns Column-level lineage for CREATE ... AS, INSERT ... SELECT (including column lists) in SQL files, SQL embedded in Python, Airflow inline SQL, dbt models from a compiled manifest (--dbt-manifest, after dbt compile), and dbt source models whose only Jinja is ref/source/config/comments. Each edge is copy or derived. SELECT * over CTEs and subqueries is expanded; SELECT * over tables, unqualified columns in joins, and models with other Jinja are counted as unresolved, not guessed
Airflow Tasks from operators and @task functions in with DAG, @dag and dag= DAGs; order from >>/<<, lists, set_downstream, chain() and TaskFlow data passing, with line evidence. Tasks link to what they run: python_callable, @task functions, sql= files (including template_searchpath) and inline SQL lineage, and python x.py bash commands. Impact flows from code through tasks to downstream tasks. Tasks created in loops or with computed ids are reported, not guessed
JavaScript/TypeScript .js .mjs .cjs .jsx .ts .mts .cts .tsx: functions, arrow functions, class methods; ESM, type-only, require() and import() imports with literal relative specifiers and tsconfig/jsconfig paths/baseUrl aliases (nearest config, extends within the repository). Package imports are external

The shared scanner respects nested .gitignore and .cartographerignore, prunes build/environment directories, rejects symlinks/junctions, and caps file size, total bytes, file count, entry count and traversal depth. Conventional secret filenames are excluded; secrets embedded in normal source are not detected. Large, binary, unreadable or unsupported files are reported rather than treated as fully analyzed. No repository source code is executed by local analysis.

See dbt artifact ingestion for supported manifest versions, dataset identifiers, artifact freshness, and scanner limits.

The accuracy benchmark scores extraction against hand-written expected results for pinned dbt, Python and SQL corpora, including known gaps. It runs in CI.

Development and branching

uv sync --frozen
uv run --no-sync pytest -q
uv run --no-sync ruff check src tests scripts
uv run --no-sync mypy -p brownfield_cartographer.scanner -p brownfield_cartographer.persistence -p brownfield_cartographer.query_api -p brownfield_cartographer.analyzers.javascript_analyzer -p brownfield_cartographer.analyzers.embedded_sql -p brownfield_cartographer.analyzers.sql_lineage -p brownfield_cartographer.analyzers.airflow_dag -p brownfield_cartographer.analyzers.dbt_manifest -p brownfield_cartographer.diff -p brownfield_cartographer.analyzers.python_calls --follow-imports=skip
uv build
uv run --no-sync python scripts/check_wheel.py

Use short-lived feat/, fix/, docs/, and chore/ branches and pull requests into stable main. Do not push feature work directly to main. See CONTRIBUTING.md for the workflow and release conventions, and CHANGELOG.md for release history.

Community and license

Participation is governed by the Code of Conduct. Report vulnerabilities privately as described in SECURITY.md.

Licensed under the Apache License 2.0.

Metadata

Release files for brownfield-cartographer 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for brownfield-cartographer 0.9.0
File Size Uploaded Provenance
brownfield_cartographer-0.9.0.tar.gz 223.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for brownfield-cartographer 0.9.0
File Interpreter ABI Platform
brownfield_cartographer-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 313.9 kB

Release files / brownfield_cartographer-0.9.0.tar.gz

Download URL brownfield_cartographer-0.9.0.tar.gz
Size 223.6 kB
Tags Source
SHA-256 checksum
How to use checksums
376896618524add3fad0ee0ae26aad650e67ab8dbdf354bd1e7d5113dc57c627
BLAKE2b-256 checksum
How to use checksums
1ec6a44fe334bf2c2c666defc3ffb32dc3c1878d53250701dd6acdf9a3564c16
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Release files / brownfield_cartographer-0.9.0-py3-none-any.whl

Download URL brownfield_cartographer-0.9.0-py3-none-any.whl
Size 90.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aca1758251d68db3f4a8724bf6d64537fbfb94034a44b38b658db4b28ccc8adc
BLAKE2b-256 checksum
How to use checksums
89ad6069151801eec8e2b68a8a430c26c4764a35d5184b038c156fe063559038
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Security for brownfield-cartographer 0.9.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for brownfield_cartographer-0.9.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page