Skip to main content

stitchgraph

Local-first code intelligence for humans and LLM agents. Point stitchgraph at a codebase and ask it plain questions — what's dead? what breaks if I change this? how does a request flow end to end? which tests should I run? It indexes 12 languages into a single SQLite graph on your machine, answers through three identical surfaces (Python library, CLI, MCP server), and attaches a confidence, a provenance, and a reason to double-check to every answer, so you always know how much to trust it.

Two design commitments make it different:

  • It never guesses confidently. Every result rides a universal envelope (confidence / provenance / needs_review / urgency), and the cardinal rule — live code is never confidently flagged dead — biases every liveness decision toward precision. When stitchgraph isn't sure, it says so and tells you why.
  • It measures what code does, not just what it says. Beyond the static graph, the behavioural toolkit decomposes a per-test coverage matrix (POD/SVD) into your suite's runtime behavioural modes — how many independent behaviours you actually test, which 6% of tests cover everything, which functions co-run with no static link. These are answers no amount of reading source can produce.

Everything runs offline against a plain SQLite file. stitchgraph is read-only on your code — it writes only to its own index, never executes your project, and every finding is advisory: ranked options for a human or agent to act on.


Contents


Install

pip install stitchgraph              # the full tool: CLI, MCP, 12 languages, all accelerators
pip install --no-deps stitchgraph   # lean: stdlib-only library core (Python analysis)

The default install is the full-power one (since v3.31.0): CLI, MCP server, polyglot grammars, jedi precision, SQL resolution, the mmapped adjacency sidecar (numpy), GraphBLAS and sparse solvers. Every accelerated path is pinned byte-identical to its pure-Python reference by the test suite, so fast-by-default costs nothing in trust. Two opt-outs:

  • Lean install: pip install --no-deps stitchgraph — the library core is stdlib-only (every dependency is a guarded import) and degrades gracefully; a CI job pins this. The old extras ([cli], [mcp], [treesitter], …) still exist for picking individual capabilities onto a --no-deps base.
  • Pure run: stitchgraph --pure …, stitchgraph-mcp --pure, or STITCHGRAPH_PURE=1 — everything installed, but sweeps use the reference implementations (identical results; for debugging or byte-reproducing old runs).

Run stitchgraph doctor (add --strict in CI) to check which grammars load.

Five-minute quickstart

Index once, then ask questions. The index is a single SQLite file; re-run reindex after large changes (or leave stitchgraph watch . running).

cd your-project
stitchgraph reindex . --db stitchgraph.db     # build the graph (12 languages, one pass)

stitchgraph orient --db stitchgraph.db        # new here? counts, entry points, top hubs
stitchgraph find-stale --db stitchgraph.db    # likely-dead code, precision-biased
stitchgraph scan --db stitchgraph.db          # ranked issues: stubs, holes, cycles, god objects
stitchgraph impact-of UserService --db stitchgraph.db   # blast radius + which tests to run
stitchgraph trace-path loadUsers users --db stitchgraph.db  # full-stack: JS → route → SQL table
stitchgraph report --db stitchgraph.db        # one Markdown report of all of the above

Every command takes --json for the raw envelope (machine-readable, full payload — text output truncates long lists). Exit codes: 0 clean, 1 RED findings exist, 2 operational failure (missing/unopenable --db) — safe to gate CI on.

The same operations, as a library:

import stitchgraph as sg

with sg.Store("stitchgraph.db") as store:
    sg.reindex(store, ".")
    print(sg.find_stale(store))       # every result is a Result envelope:
    print(sg.impact_of(store, "UserService"))  # .ok .result .confidence .needs_review

The operations

Thirty-one operations, one question each. All advisory, all read-only, all carrying the envelope.

Ask Operation(s)
Where is X, who calls it, what does it call? find_symbol, get_callers, get_callees
I'm new here — orient me orient, summarize_subsystem, find_subsystems
What's dead? What's referenced but missing? find_stale, find_holes
Sweep the repo for issues, ranked scan
What breaks if I change this? impact_of
How does a request flow end to end? trace_path (HTML form → route → handler → ORM → SQL table)
What's dangerous to touch? risk (git churn × centrality), find_chokepoints (cut vertices × blast radius)
Where's the code that does X / clones of this? find_similar — by tokens, or mode="structure" for body-shape clone detection (renamed/reordered clones a text diff misses)
How do two builds differ? graph_diff — call-level deltas plus body-shape changes (catches a data-flow bug that leaves the call graph identical)
Drill into one function get_matrix(layer="call" | "statement" | "expression") — call graph → program-dependence graph → value-flow graph
Ground liveness in reality ingest_trace (coverage.py JSON / LCOV / Go coverprofile)
Rebuild the index reindex (admin; --precise adds jedi)

…plus the eleven behavioural operations below.

The behavioural toolkit (runtime analysis)

The static graph describes structure. The behavioural toolkit measures what your test suite actually executes, and answers questions that cannot be answered by reading code — this is the part of stitchgraph that tells you things you don't already know.

It consumes one inert artifact: a per-test coverage matrix (which test executed which function). stitchgraph never runs your code — it generates a sandboxed capture kit and you run it in your own jail:

stitchgraph scaffold-coverage --db stitchgraph.db     # writes Docker/shell/CI recipes
# run the generated kit (it runs YOUR tests in YOUR sandbox) → coverage_modes.json

stitchgraph find-modes --coverage coverage_modes.json --db stitchgraph.db
Ask Operation
How many independent behaviours does my suite exercise? What are they? find_modes — POD/SVD of the coverage matrix: behavioural modes, intrinsic dimensionality, a minimal covering test set
Which tests should CI run for this change / this PR? select_tests (runtime evidence fused with the static blast radius; accepts comma-separated changesets)
What code moves together with X? co_change
What co-runs but has no static link? (hidden coupling) find_coupling
Which live functions does no test execute? find_gaps (fuses coverage with reachability: live-untested vs dead)
What order surfaces failures fastest? test_order (greedy new-coverage-first; the prefix is a minimal cover)
Which tests are coverage-identical? redundant_tests (review aid — parametrized tests share profiles legitimately; never auto-delete)
What's the always-on core? find_core
Which tests do something nothing else does? find_outlier_tests
Which files change often AND carry many behaviours? runtime_risk (churn × behavioural centrality)
What gained/lost test exposure between two snapshots? coverage_drift

Dogfood example (this repo, research/14): 2,349 tests turn out to exercise 27 independent behaviours; 64 tests cover every executed function; the one untested-dead function find_gaps reports is exactly the one find_stale flags statically — and find_coupling located a real config↔envelope side-channel blind.

For LLM agents (MCP)

stitchgraph is MCP-native: every operation above is an MCP tool with the same name and the same JSON envelope. Launch the server pointed at a built index (build it first with reindex — the server refuses to answer from a missing or never-indexed database rather than confidently reporting an empty codebase):

pip install 'stitchgraph[mcp,treesitter]'
stitchgraph reindex /path/to/project --db /path/to/stitchgraph.db
stitchgraph-mcp --db /path/to/stitchgraph.db     # or env STITCHGRAPH_DB

Claude Desktop / Claude Code configuration:

{
  "mcpServers": {
    "stitchgraph": {
      "command": "stitchgraph-mcp",
      "args": ["--db", "/absolute/path/to/stitchgraph.db"]
    }
  }
}

Rules of engagement for agents

The full rule file — written to be dropped into an agent's context — is AGENTS.md. The essentials:

  • Query the graph before grepping. orient first on unfamiliar code; impact_of <name> before editing anything; get_callers/get_callees instead of text search; trace_path for end-to-end flows.
  • Respect the envelope. needs_review: true means "unreached by my analysis", not "proven dead" — verify dynamic dispatch, plugins, and framework callbacks before acting. confidence and provenance (extracted > inferred > ambiguous) tell you whether a result is a fact or a ranked guess.
  • Never delete on find_stale alone. It is precision-biased and advisory by design; treat results as candidates to verify.
  • Use scan for triage, ordered by urgency (🔴 fix now / 🟠 look closer / 🟢 cleanup); a finding capped 🟢 with needs_review rests on name-ambiguous edges and is likely a resolution artifact.
  • Prefer select_tests over "run everything" when a coverage artifact exists — it returns the tests that actually executed the changed symbols.
  • Refusals are honest: a bare-name collision, a too-broad get_matrix scope, or a missing index returns an explanation and a suggested next call, not a guess.

Trust model

  • The envelope. Every answer: ok, result, confidence (0–1), provenance (extracted = read from syntax; inferred = heuristic; ambiguous = several candidates), needs_review + human-readable reasons, and for findings an urgency. Provenance caps urgency — a heuristic link can never shout RED.
  • The cardinal rule. Live code is never confidently flagged dead. Dozens of per-language liveness signals (exports, framework callbacks, dunders/magic methods, FFI/linker attributes, test conventions…) root the graph; ambiguity widens edges rather than dropping them. The deliberate trade-offs are documented — decision by decision — in LIMITATIONS.md.
  • Read-only, local, private. No code leaves your machine; nothing executes; the only file written is the index (plus explicitly requested reports/kits).
  • Verified. ~2,300 tests including differential oracles (streaming index == in-memory, incremental == full reindex, GraphBLAS == pure Python), per-language completeness batteries, and ground-truthing against ~47 real projects (Linux kernel core, WordPress, Magento, NestJS…) with zero crashes. Hostile inputs degrade to a smaller index, never a wrong confident answer.

Languages

Depth Languages
Deep (stdlib ast; optional jedi --precise) Python 3.11+
Full graph via tree-sitter (definitions, calls, imports/inheritance, tests, body matrix) JavaScript, TypeScript/TSX, Go, Rust, C, C++, C#, Java, Ruby, PHP, Bash
Cross-language seams Flask/FastAPI/Django/Express/Spring routes, HTML forms, JS fetch, events, SQL (sqlglot), SQLAlchemy/Django ORM — all converging in one graph, so trace_path crosses language boundaries

Per-language support matrix: docs/LANGUAGES.md.

Scale

reindex streams the graph to SQLite in constant memory (auto-enabled for large on-disk trees), byte-identical to the in-memory path and pinned by a differential oracle. Measured:

  • Magento (4,300 PHP files): 269 MB peak instead of 3.2 GB.
  • A homonym-fanout Python corpus producing 8.6M edges: 50 MB peak (the pre-v3.28.0 Python path materialized its edge list first — a field report of Home Assistant OOMing at 7 GB exposed it; fixed and now gated in CI by a hard-memory-cap test).
  • Home Assistant 2024.3.3 (6,728 Python files, 59k nodes, 16.0M edges): the repo from the field report completes a clean end-to-end streaming reindex under a 4 GB address-space ulimit at 158 MB peak RSS (~34 min).
  • Query sweeps use a derived mmapped adjacency sidecar (<db>.adjcache/, built lazily on the first sweep, auto-invalidated by a generation counter): on the 16M-edge graph, find_stale drops from 119 s / 1.97 GB to 2.1 s / 516 MB warm. Without numpy the sweeps stream from SQLite instead (~2 GB on the same graph).

Details: docs/V2_STREAMING_DESIGN.md. Wondering how long your repo will take? docs/PERFORMANCE.md has measured anchors and an estimation method (edges drive cost, not files; watch the db growth rate for a live ETA; a flat ~150 MB RSS with an active WAL is healthy).

Develop

pip install -e '.[all,dev]'
PYTHONPATH=src python -m pytest -q

CI runs the suite on Python 3.11/3.12 plus a no-extras job that guards the stdlib-only core. Design: docs/design.md · capability map: docs/OVERVIEW.md · status/roadmap: docs/STATUS.md · release history: CHANGELOG.md and docs/RELEASE_NOTES_v*.md (campaign overview: docs/RELEASE_SUMMARY_v3.28-v3.38.md; field validation — the call graph measured at 99.1% recall against Home Assistant's real test-run coverage: research/18) · review process: CONTRIBUTING.md, REVIEW_HISTORY.md.

MIT licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stitchgraph-3.39.0.tar.gz (1.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stitchgraph-3.39.0-py3-none-any.whl (367.9 kB view details)

Uploaded Python 3

File details

Details for the file stitchgraph-3.39.0.tar.gz.

File metadata

  • Download URL: stitchgraph-3.39.0.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for stitchgraph-3.39.0.tar.gz
Algorithm Hash digest
SHA256 c6ec8b2fb5b89acae00540a91fd70f4da189c05f60b18c94bede0864e7921f25
MD5 04b3406667e40dbb75894c632b4516ec
BLAKE2b-256 06c18bc11bf2fdb3cf74cc0ada2a03c87562f84619836e640b036b8e75df2d0a

See more details on using hashes here.

File details

Details for the file stitchgraph-3.39.0-py3-none-any.whl.

File metadata

  • Download URL: stitchgraph-3.39.0-py3-none-any.whl
  • Upload date:
  • Size: 367.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for stitchgraph-3.39.0-py3-none-any.whl
Algorithm Hash digest
SHA256 753d7822a3008f6e88f2bb298c08dd72e62c93d04e70d360c97d3d1edf457009
MD5 884bb475b19bc7251effc67a25b647f4
BLAKE2b-256 39b59b22d8720ec7239f85171e3f32adb8565d70a6f7c832b870435878403efe

See more details on using hashes here.

Release history Release notifications | RSS feed

3.51.0

2 files

3.50.0

2 files

3.49.0

2 files

3.48.0

2 files

3.47.1

2 files

3.47.0

2 files

3.46.0

2 files

3.45.0

2 files

3.44.0

2 files

3.43.0

2 files

3.42.0

2 files

3.41.0

2 files

3.40.0

2 files

This release

3.39.0 This release

2 files

3.27.1

2 files

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