Weld
A local codebase graph for AI coding agents. Weld scans code, docs, CI, build files, runtime configs, and repo boundaries into a deterministic graph. Agents can query this graph through CLI or MCP instead of rediscovering the repository from scratch every session.
The graph lives on disk (.weld/graph.json), stays under your control, and
answers the questions agents and humans repeatedly ask about a codebase: where
a capability lives, which docs are authoritative, what build and test surfaces
a change touches, and what boundaries constrain the implementation.
Evaluators: start with v0.19.1. v0.19.1 is the current recommended starting point. Headline features added since v0.14.0: a 14-tool MCP server for graph-backed agent context (
weld_query,weld_find,weld_context,weld_path,weld_brief,weld_stale,weld_callers,weld_references,weld_export,weld_trace,weld_impact,weld_enrich,weld_diff,weld_review);wd impactblast-radius queries driven by node, file list, working tree, or git diff range, with a stale-graph gate;wd reviewJSON-first triage for speculative edges; an end-to-end C# strategy stack (solution/project parsing, MSBuild targets, test-framework detection, ASP.NET routes, EF Core, inheritance edges, per-method call graphs) that auto-wires onwd initwhen matching artifacts are present;wd initauto-wiring of the interface strategies (gRPC.protoservices and Python bindings, Kafka / Celery / Redis event channels,runtime-contract.mdhealthchecks, and generic DDS.idldata contracts and topic channels) when matching artifacts are present; multi-language origin classification, Bazelsrcs/depsedges, Dockerfile and Compose copy edges, and multi-language test-peer edges across Python, Go, TypeScript / JavaScript, Rust, Java, C#, and C++;wd communitiestopic-level navigation of large graphs; opt-in eager inverted-index aggregation for faster cold-cache queries on large federations; a C++ amalgamation-file rank boost so single-file headers (e.g.nlohmann/json) surface ahead of incidental mentions; alias-aware lookup that resolves legacy node IDs through one minor version; and human-readable text output by default for the retrieval surface, with--jsonavailable for tools and the MCP server. ROS2 is labeled Tier 2 (preview) until its own harness pass runs; every other language family (Python, C#, Java, C++) follows the Tier-1 language support contract for entrypoints, modules, call graphs, test peers, and origin classification. SeeCHANGELOG.mdfor the per-release entries from v0.15.0 onward.
Try it in 5 minutes → docs/tutorial-5-minutes.md
walks through wd init, discover, brief, query, context, and path
against demo workspaces. Spin up a clean demo with one command:
scripts/create-polyrepo-demo.sh /tmp/weld-polyrepo-demo
# or
scripts/create-monorepo-demo.sh /tmp/weld-monorepo-demo
Each script materializes a self-contained demo directory with seeded source
files, .weld configs, and committed git history -- ready for wd discover.
If you have Weld installed but no source checkout, the same demos are
available through the CLI: wd demo list, wd demo monorepo --init <dir>,
wd demo polyrepo --init <dir>.
Use Weld when…
- your repo is too large for an agent to understand in one pass
- your system spans multiple repositories
- architecture is spread across code, docs, CI, configs, and service contracts
- you want reproducible repo context instead of ad-hoc chat memory
When not to use Weld
- Your repo is small (under ~50 files). An agent can read it end-to-end; a graph adds overhead without payoff.
grepplus your IDE already answers your questions. If nothing is missing from that workflow, Weld has nothing to add.- You only need symbol navigation. Go-to-definition and find-references are an LSP job. Weld covers architecture, contracts, docs, and CI -- not IDE jump-to.
- You expect compiler-grade static analysis. Weld is a pragmatic graph, not a type checker or dataflow engine. It will not catch every reference or prove correctness.
- You do not want repo-local configuration. Weld writes config to
.weld/(discover.yaml,workspaces.yaml,strategies/) and expects that config to be committed alongside your code. Generated graphs (graph.json,agent-graph.json) are gitignored by default; the opt-inwd init --track-graphsteam workflow commits them for warm-CI / warm-MCP setups instead. If even committing config is unacceptable, Weld is the wrong tool.
How Weld compares
Weld is not a replacement for the tools below -- it sits alongside them and gives agents a persistent, queryable map of the repository. Each of these tools is excellent at what it does; Weld adds the connected structure they were not designed to provide.
| Tool | Gives you | Weld adds |
|---|---|---|
| grep / ripgrep | Fast literal and regex search over file contents. | Typed nodes and edges -- a symbol, route, doc, or config is an addressable entity with neighbours, not a line of text. |
| ctags / LSP | Symbol navigation and go-to-definition inside one language. | A cross-language graph that also covers docs, CI, configs, service contracts, and repo boundaries -- surfaces an IDE was never meant to index. |
| Sourcegraph | Hosted code search and references across large fleets of repos. | A local, repo-local graph that lives next to your code. By default Weld tracks only config and lets you opt in (wd init --track-graphs) to commit the generated graph for warm-CI / warm-MCP setups. No server, no indexing fleet; agents query it offline through CLI or MCP. |
| vector DB / RAG | Embedding-based semantic recall over chunks of text. | Deterministic structure. Query results are exact nodes and edges with provenance, not top-k fuzzy matches, so agents can follow relationships instead of guessing. |
| Copilot / Claude Code / OpenCode | In-editor and agentic code generation and chat. | Shared repo context those agents can read through MCP -- the same graph across sessions and tools, instead of each agent rediscovering the repo on every run. |
Key features
- Whole-codebase discovery — not just source code. Covers docs, config, CI workflows, infrastructure, and build files.
- Startup and runtime flow — models common Python, C#/.NET, and C++ entrypoints and connects them to services, boundaries, and deploy/runtime surfaces.
- Config-driven — point
.weld/discover.yamlat your repo and tune what gets extracted. - Multi-language — tree-sitter strategies ship for Python, TypeScript/JS,
Go, Rust, C#, C++, Java, and ROS2. Tree-sitter Python packages are an
optional extra (
pip install configflux-weld[tree-sitter]); without them only Python is extracted natively. See Supported languages for the per-language status and the optional libclang path for C++. - Plugin architecture — drop a
.pyfile in.weld/strategies/to extract anything repo-specific. - Agent Graph — discover agents, skills, prompts, commands, hooks,
instructions, MCP servers, and platform-specific copies into
.weld/agent-graph.json; see the Agent Graph guide for node and edge types, authority/drift, and limitations, and the platform support matrix for tested surfaces. - Agent-native — generates MCP config snippets by default and ships an optional stdio MCP server so Claude Code, Codex, and other agents can query the graph directly.
- Zero external dependencies — runs from a plain checkout with Python >= 3.10. Tree-sitter is optional.
Quickstart
# Install (recommended — see the Install section for alternatives)
uv tool install configflux-weld
# Bootstrap config for your repo
wd init
# Run discovery and save the graph (safe mode by default — see Trust model below)
wd discover --safe --output .weld/graph.json
# Query the graph
wd query "authentication"
wd trace "how does this service start"
wd find "login"
wd context file:src/auth/handler
wd viz --no-open
wd stale
Drop --safe once you trust the repository's project-local strategies and
external-JSON adapters; the Trust model section below explains
what --safe disables and when it is appropriate to remove.
Try it on a real example: examples/04-monorepo-typescript (monorepo) · examples/05-polyrepo (polyrepo federation).
Sample output (wd query "auth" — default human form, trimmed):
# query: auth
matches (1):
1. symbol:src/auth/handler.py:authenticate [type: function]
label: authenticate
confidence: definite
description: Validate a bearer token and return the caller identity.
neighbors (1):
- route:/login [type: route]
Each match shows its confidence (definite / inferred / speculative)
so an agent can weight strong hits over guesses. By default wd query also
hides unresolved-symbol sentinels (call-graph callees that could not be
linked to a definition, origin=unresolved) — they are noise in the result
set. Pass --include-speculative to bring them back. The --json envelope
applies the same default filter, and so does the MCP weld_query tool
(include_speculative: true restores them there) — the two surfaces return the
same matches by construction. Every match still carries its confidence so a
client can weight or discount hits itself.
wd query and wd context (and their MCP peers weld_query / weld_context)
also bound the read envelope by default so a large graph stays usable on
both surfaces. First the neighborhood is dieted: neighbors that are stdlib
symbols, unresolved sentinels, or speculative external symbols are dropped (real
external package dependencies are kept), dangling edges are removed, and the
fan-out is capped so a hub node cannot blow up the envelope. Then a deterministic
byte budget prunes the lowest-priority survivors until the serialized
envelope fits the agent tool cap. Nothing is hidden silently — the --json
envelope reports neighbors_filtered: true and an omitted_neighbors count by
reason (stdlib, unresolved, external_symbol, fanout_capped, and
size_capped for the byte budget). Pass --full-neighborhood (CLI) or
full_neighborhood: true (MCP) to restore the full, unfiltered neighborhood, or
--full-size / full_size: true to keep the diet but skip the byte budget.
wd brief / weld_brief are bounded the same way: edges are de-dangled to the
nodes the brief actually emits and the byte budget applies (a warnings entry
records any node dropped for size); --full-size / full_size: true returns the
unbounded brief.
All wd retrieval commands default to human-readable text and accept
--json for the stable JSON envelope. Pass --json when
piping to jq or other scripted consumers. query and context
additionally carry the neighbor-diet annotation (neighbors_filtered +
omitted_neighbors); the rest of the envelope is unchanged.
Sample wd query "auth" --json:
{
"query": "auth",
"matches": [
{
"id": "symbol:src/auth/handler.py:authenticate",
"label": "authenticate",
"type": "function",
"props": {
"file": "src/auth/handler.py",
"exports": ["authenticate"],
"description": "Validate a bearer token and return the caller identity."
}
}
],
"neighbors": [{"id": "route:/login", "type": "route"}],
"edges": [
{"from": "route:/login", "to": "symbol:src/auth/handler.py:authenticate", "type": "calls"}
],
"neighbors_filtered": true,
"omitted_neighbors": {"stdlib": 0, "unresolved": 0, "external_symbol": 0, "fanout_capped": 0, "size_capped": 0}
}
See Install for alternatives (local checkout, pip, raw source).
Agent Graph for AI customizations
Weld also maps the AI customization layer around a repository: agents, skills, instructions, prompts, commands, hooks, MCP servers, tool permissions, and platform variants. The Agent Graph is static and repo-bound; discovery reads known customization files and does not execute project code.
wd agents discover
wd agents list
wd agents audit
wd agents explain planner
wd agents impact .github/agents/planner.agent.md
wd agents plan-change "planner should always include test strategy"
wd agents viz --no-open
Use --json on list, explain, impact, audit, and plan-change for
agent-friendly output. Use wd agents rediscover when you want an explicit
refresh of .weld/agent-graph.json before inspecting the persisted graph.
Use wd agents viz after discovery to open a local read-only browser explorer
for the persisted Agent Graph.
Static discovery and configuration generation are available for several
agent platforms; runtime validation is tracked per client in the
platform support matrix. The
Agent Graph guide documents node and edge types,
authority and drift, and the read-only-first policy.
Agent-first onboarding
If an agent or coding assistant is driving setup, use the short bootstrap path:
uv tool install configflux-weld # recommended — see Install for alternatives
wd prime # show setup status + per-framework surface matrix
wd bootstrap claude # writes .claude/commands/weld.md
wd bootstrap codex # writes .codex/skills/weld/SKILL.md + .codex/config.toml
wd bootstrap copilot # writes .github/skills/weld/SKILL.md + .github/instructions/weld.instructions.md
wd bootstrap cursor # writes .cursor/rules/weld.mdc + .cursor/mcp.json
wd bootstrap aider # writes CONVENTIONS.md + .aider.conf.yml (wiki fallback; no MCP)
wd bootstrap gemini-cli # writes .gemini/skills/weld.md + .gemini/mcp.json
wd bootstrap copilot-cli # writes .copilot/skills/weld.md + .copilot/config.json
Cursor, Gemini CLI, and Copilot CLI register the local weld stdio MCP server
in the host-native config file. Aider has no native MCP protocol, so its
CONVENTIONS.md stanza points at the agent-readable wiki export: run
wd export --format=wiki --output=.weld/wiki and read .weld/wiki/index.md
to navigate the graph.
All seven wd bootstrap frameworks accept opt-out flags:
--no-mcp— skip the MCP pair (.codex/config.tomlfor codex; the.mcp.jsonguidance block for copilot/claude).--no-enrich— write the.cli.mdvariant that omitswd enrich.--cli-only— shorthand for--no-mcp --no-enrich.
To upgrade existing bootstrap files after pulling a new weld release, use the diff-aware upgrade path:
wd bootstrap <framework> --diff— print unified diffs between bundled templates and your on-disk copies without writing. Exits 1 when any file differs, 0 otherwise, so it composes with CI checks.wd bootstrap <framework> --force— overwrite targeted files while still honouring the opt-out (--no-mcp,--no-enrich,--cli-only) and federation template behaviour.
wd prime is idempotent and safe to re-run — it reports what is
already configured and what is still missing. Pass
--agent {auto,claude,codex,copilot,all} to force the active agent's row
into the matrix even when that framework has no files yet (e.g. a Codex user
in a Claude-only checkout sees codex: skill no, mcp no -> wd bootstrap codex
instead of silence). auto is the default and infers the agent from
environment variables such as CODEX_*.
Trust model
Weld's trust posture is explicit and narrow:
- Default: bundled discovery reads source files and writes the local
graph (
.weld/graph.json). It does not execute discovered application code and does not open network connections. - Safe mode: when enabled with
--safe, safe mode disables project-local strategies (.weld/strategies/) and theexternal_jsonadapter forwd discover, and refuses network/LLM enrichment providers forwd enrich. Passwd discover --safeto scan an untrusted repository without executing any code from it; passwd enrich --safeto refuse network egress (every currently registered provider — Anthropic, OpenAI, Ollama, Copilot CLI — is refused). Safe mode produces a stable[weld] safe mode: ...stderr line for each refused path. - Advanced strategies: project-local strategies are Python modules
loaded at discovery time, and
strategy: external_jsonexecutes configured commands fromdiscover.yaml. Only enable these on repositories you trust.
See SECURITY.md for the full policy and reporting process.
Local telemetry
Weld records the success or failure of every wd CLI invocation and every
MCP tool call to a local-only file. There is no remote endpoint and no
upload — the file never leaves your machine unless you explicitly export
and share it.
What is recorded. Each event is one JSON line with a strict allowlist of fields: subcommand or tool name, exit code, duration in milliseconds, and the exception class name on failure. Paths, query strings, error messages, flag values, and usernames are never recorded. The redaction is enforced at write time, so the file on disk is already safe to attach to a bug report.
Where it lives. In a single repo, the file is
<repo>/.weld/telemetry.jsonl. In a polyrepo workspace, every event from
the root and from any child repo aggregates into
<workspace_root>/.weld/telemetry.jsonl — one shareable artifact per
workspace. Invocations outside any project (for example wd --version in
/tmp) fall back to ${XDG_STATE_HOME:-~/.local/state}/weld/telemetry.jsonl.
The file is gitignored and rotates at 1 MiB to keep the trailing 500 events.
How to opt out. Any one of these disables recording:
WELD_TELEMETRY=off in the environment, the --no-telemetry flag on a
single invocation, or wd telemetry disable to write a persistent sentinel
at the resolved root. Run wd telemetry --help for the full subcommand
surface (status, show, path, export, clear, disable, enable),
and see docs/telemetry.md for the full event
schema and design rationale.
Supported languages
Weld's only built-in extractor is for Python. Every other language
listed below depends on the [tree-sitter] optional extra. Without
it, the tree-sitter strategies silently no-op on ImportError and the
graph will contain zero nodes for those languages — by design, so weld
still runs in a minimal environment. Install the extra to actually use
multi-language support:
uv tool install "configflux-weld[tree-sitter]"
# or
pip install "configflux-weld[tree-sitter]"
Status ladder. Every language is classified on a single ladder: Tier 1 (passes the binding tier-check harness criteria on the pinned corpora; description-coverage is measured and reported as an advisory signal rather than a gate, because enrichment quality reflects LLM provider output rather than weld discovery) → Tier 2 (ships and is usable; fails one or more binding criteria with disclosed gaps) → Preview (ships with documented correctness issues; not for production use) → Experimental (opt-in extra, off by default) → Not supported. Languages move tiers only via tier-check harness output, not by editorial claim. The per-language Status column below is generated from the harness baselines, so it always reflects the current verdict; a language without a recorded baseline keeps its listed status pending its own harness run.
| Language | Extraction surface | Grammar package | Status |
|---|---|---|---|
| Python | modules, classes, functions, imports, call graph | built-in (no extra) | Tier 1 |
| TypeScript | exports, classes, imports | tree-sitter-typescript |
Tier 1 |
| JavaScript | exports, classes, imports | tree-sitter-javascript |
Tier 2 |
| Go | exports, types, imports | tree-sitter-go |
Tier 1 |
| Rust | exports, types, imports | tree-sitter-rust |
Tier 1 |
| C# | types, methods, properties, attributes, namespaces, using dependencies, best-effort call graph | tree-sitter-c-sharp |
Tier 1 |
| C++ | classes, structs, namespaces, functions, methods, inherits edges, includes, CMake build targets, best-effort call graph | tree-sitter-cpp |
Tier 1 |
| Java | classes, interfaces, methods, fields, constructors, annotations, imports, inherits / implements edges | tree-sitter-java |
Tier 1 |
Frameworks (reuse a language's extractor; status inherits from the host language):
| Framework | Host language | Extraction surface | Status |
|---|---|---|---|
| ROS2 | C++ / Python | packages, nodes, topics, services, actions, parameters | Preview |
| DDS (CycloneDDS / FastDDS) | IDL (.idl) |
data contracts (structs) with typed fields, enums, pub/sub topic channels | Preview |
Discovery also adds deterministic closure edges from files to source-backed symbols and from import/include/use declarations to local files or external package nodes across every listed language.
For non-preview tree-sitter languages, exact identifier queries such as
wd query GetAsync prefer first-class definition symbol: nodes before
owning files or package-level fallbacks. File results remain available when
the graph has no exact symbol candidate.
C++ — Tier 1 details
Status: Tier 1. The C++ extraction surface passes the binding
tier-check harness criteria against the pinned C++ corpora
(nlohmann/json, googletest, abseil-cpp, Kitware/CMake,
grpc/grpc); see docs/bench/tier1-cpp-baseline.md
for the per-criterion measurement snapshot. Promotion is anchored by
the bundled fixture contract gate, which exercises a Shape / Circle
/ Rectangle / Drawable inheritance tree under a real CMake project
layout.
C++ has two extraction paths:
-
Tree-sitter (default once
[tree-sitter]is installed). Indexes.hpp,.cpp,.cc,.h,.hh,.hxx,.cxx,.ipp,.tppfiles intofile:andsymbol:nodes. Emitsinheritsedges originating at the derived-class symbol (so awd contexton a concrete class surfaces its base classes directly, not via the owning file). Query patterns live in weld/languages/cpp.yaml. This is the fast path; no compilation database required and the path the tier-check harness measures. -
libclang (optional, off by default). Adds macro-expansion, template-instantiation, and cross-translation-unit call edges that tree-sitter cannot resolve from a syntactic walk alone. Requires:
pip install "configflux-weld[cpp-libclang]"(Python bindings)- A
compile_commands.jsonat the repo root, e.g.cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . WELD_CPP_LIBCLANG=1in the environment that runswd discover
When any prerequisite is missing the libclang strategy silently returns no nodes — tree-sitter still runs.
A CMake build-graph strategy (cpp_cmake) parses each
CMakeLists.txt and emits project:, build-target:, and
package: nodes plus depends_on edges so internal target
dependencies (target_link_libraries) and find_package declarations
are queryable as first-class graph entries.
Framework markers. The C++ framework strategies declare
ros2, cmake, conan, gtest, and catch2 markers; the
tier-check harness reports them stub-by-design when a corpus is a
plain library that does not consume a C++ test or robotics
framework in its public surface. Corpora that do consume them
(downstream applications, services, ROS2 packages) light up
criterion 3 directly. If you adopt C++ support and measure it
against your own corpus, please share the numbers — public
measurements are the fastest way to keep the harness honest.
To use the built-in semantic enrichment providers:
uv tool install "configflux-weld[openai]" # or [anthropic], [ollama], or [llm]
The copilot-cli provider needs no Python extra — install the standalone
GitHub Copilot CLI binary (copilot) and run
wd enrich --provider copilot-cli. Set WELD_COPILOT_BINARY to override
the binary path.
First-run enrichment prompt
When you run wd discover for the first time and weld detects an
enrichment provider through the usual environment variables (the
Anthropic API key, the OpenAI API key, an OLLAMA_HOST value, or
the copilot binary on PATH), discover prints a cost-honest
prompt with the estimated dollar range and asks whether to run
enrichment now. The answer is persisted to
.weld/.enrichment-prompted and the prompt is not re-shown.
wd discover --no-enrichskips the prompt for one invocation.WELD_NO_ENRICH=1skips it globally (CI-friendly).wd discover --safeimplies skip (network/LLM calls forbidden).wd enrich --reset-promptclears.weld/.enrichment-promptedso the nextwd discoverre-asks (useful after configuring a provider for the first time).
Graphs over 2,000 nodes are out of the auto-flow: the message points
you at the explicit wd enrich --batch=N path instead. Inside an
agent harness (Claude Code, Cursor, Codex, etc.) with no provider
configured, the prompt is replaced by a tip to run /enrich-weld.
For a source-checkout install (contributors editing Weld itself), see CONTRIBUTING.md.
Agents can also enrich nodes without provider extras or API keys by reading the relevant source or documentation and writing reviewed enrichment manually:
wd stale
wd context "<node-id>"
wd add-node "<node-id>" --type "<node-type>" --label "<label>" --merge --props '{"description":"...","purpose":"...","enrichment":{"provider":"manual","model":"agent-reviewed","timestamp":"<ISO-8601 UTC timestamp>","description":"...","purpose":"...","suggested_tags":["lowercase","tags"]}}'
wd graph validate
wd graph stats
wd graph communities --format markdown
Manual enrichment writes .weld/graph.json directly and is preserved across
later wd discover runs: discovery re-attaches props.enrichment to the
rebuilt node, keyed by node id. A record written by wd enrich is re-validated
against a node source fingerprint and dropped only when that node's own source
changes; manual enrichment carries no fingerprint, so it persists until you
re-enrich it. Manual inferred edges should use explicit provenance such as
{"source": "manual"} after the relationship is verified from source content.
wd graph communities --write derives .weld/graph-communities.json,
.weld/graph-community-report.md, and .weld/graph-community-index.md
from the existing graph without modifying .weld/graph.json.
Without tree-sitter, the built-in Python module strategy and non-language strategies (markdown, YAML, config, frontmatter) still work.
MCP
Weld generates MCP config snippets for Claude Code, VS Code, Cursor, and Codex in the default install:
wd mcp config --client=claude
wd mcp config --client=vscode
wd mcp config --client=cursor
Running the stdio MCP server requires the optional MCP SDK extra:
uv tool install "configflux-weld[mcp]"
python -m weld.mcp_server --help
Point your client at python -m weld.mcp_server:
{"mcpServers": {"weld": {"command": "python", "args": ["-m", "weld.mcp_server"]}}}
See docs/mcp.md for the full tool reference, per-client configs, example prompts, troubleshooting, and the exact dependency model. See the platform support matrix for per-client support status and runtime validation.
Discovery configuration
Weld is driven by .weld/discover.yaml. Each entry maps a file pattern
to an extraction strategy:
sources:
- glob: "src/**/*.py"
type: file
strategy: python_module
- glob: "docs/**/*.md"
type: doc
strategy: markdown
- glob: ".github/workflows/*.yml"
type: workflow
strategy: yaml_meta
Run wd init to generate a starter config, or write one by hand. See
the Strategy Cookbook for the full list
of bundled strategies.
.weld/.gitignore
wd init and wd workspace bootstrap write a managed .weld/.gitignore
the first time they touch a .weld/ directory (idempotent — never
overwrites an existing file). Three policies are available:
-
Default — config-only. Tracks the source-of-truth config (
discover.yaml,workspaces.yaml,agents.yaml,strategies/,adapters/,README.md) and ignores everything else weld writes, including the generated graphs (graph.json,agent-graph.json), graph-community reports (graph-communities.json,graph-community-report.md,graph-community-index.md), and per-machine state (discovery-state.json,graph-previous.json,workspace-state.json,workspace.lock,query_state.bin). A fresh contributor gets a cleangit statusafter the first run. -
Track-graphs (opt-in team workflow for warm CI / warm MCP). Pass
--track-graphsto widen the default so the canonical graphs are committed alongside config. Use this when every contributor should share a pre-built graph:wd init --track-graphs wd workspace bootstrap --track-graphs -
Ignore-all (opt-in). Pass
--ignore-allfor early experimentation or test installs where no weld state should be committed yet:wd init --ignore-all wd workspace bootstrap --ignore-allThis writes a heavy-handed
*/!.gitignoreso every weld file is ignored.
--track-graphs and --ignore-all are mutually exclusive; passing both
is a usage error.
Migration from earlier versions. Pre-existing .weld/.gitignore
files written by older wd init / wd workspace bootstrap runs are
not rewritten — the helper is idempotent. To pick up the new
default, delete the file and re-run init:
rm .weld/.gitignore
wd init # config-only default, generated graphs ignored
# or wd init --track-graphs to keep tracking the graphs as before
To opt out entirely, just delete .weld/.gitignore after init — the
skip-if-exists guard means it won't be recreated until the next init
or bootstrap.
Warm graphs from CI (wd warm)
The config-only default keeps generated graphs out of git, which means a
fresh clone has no graph until the first wd discover. On a larger team you
can hand everyone a warm graph without committing it by building it once
in CI and distributing it as a build artifact. This rides your existing CI
artifact storage — there is no shared graph server and no hosted index.
Two pieces:
-
Publish in CI. The bundled
graph-artifact.ymlworkflow runswd discover --safeon every push tomainand uploadsgraph.jsonplus agraph.json.sha256integrity tag as an artifact keyed by the commit SHA. The graph is content-addressable, so the SHA identifies the graph content exactly. Adapt the workflow's storage/upload step to wherever your team already keeps build artifacts. -
Fetch locally.
wd warmfinds the nearest-ancestor commit that has a published graph, verifies it against the published hash, lands it as your local.weld/graph.json, and refreshes it to yourHEAD:# Point at the artifact source (a directory, or an https URL template # containing {sha}); or set WELD_WARM_SOURCE once for the whole team. wd warm --source /path/to/artifact-store wd warm --source "https://artifacts.example.com/weld/{sha}/graph.json"The artifact store is laid out as
<source>/<sha>/graph.json(and the siblinggraph.json.sha256).wd warmprobesHEADand its recent ancestors (--max-ancestors, default 50), so a developer a few commits ahead of the last CI build still gets a warm start and only re-discovers the handful of changed files.
Always-safe fallback. When no artifact is reachable — nothing published
yet, the source is unavailable, the integrity check fails, or you are outside a
git checkout — wd warm falls back to a full local wd discover. It never
leaves you worse off than running discover directly, and a tampered or corrupt
artifact is refused, never used. Pass --no-fallback if you want warm to skip
the local discover and simply report a miss.
Custom strategies
Drop a Python file in .weld/strategies/ to extract repo-specific
artifacts. The strategy signature:
def extract(root: Path, source: dict, context: dict) -> StrategyResult:
...
See examples/02-custom-strategy for a working example that extracts TODO comments as graph nodes, and docs/extending-discovery.md for the full step-by-step guide (contract, capability matrix, fixtures, and a worked end-to-end walkthrough).
Polyrepo Federation
Weld supports federated polyrepo workspaces where a root directory contains
several child git repositories, each owning its own .weld/ directory. The
root maintains a meta-graph of cross-repo relationships without duplicating
child content. Children remain portable and independently publishable.
You do not need to cd into each child to keep the workspace usable.
wd workspace bootstrap onboards an entire polyrepo root in one command,
and wd discover --recurse refreshes every child plus the root meta-graph
in a single pass. This section walks the full lifecycle:
bootstrap -> status -> query -> refresh.
Lifecycle at a glance
| Step | Command | What it does |
|---|---|---|
| Onboard | wd workspace bootstrap |
Init the root, scan and init every nested child, discover each child, build the root meta-graph |
| Inspect | wd workspace status |
Show every child's lifecycle state (present / missing / uninitialized / corrupt), the derived stale view when a present child has drifted past its graph, and git ref |
| Query | wd query <term> |
Search the federated graph from the root; surfaces repo:<name> nodes and child-namespaced symbols |
| Read | wd context / path / callers / references / find / communities / trace / impact |
Every read tool federates across children (the wd CLI and matching MCP tools alike); trace/impact reach child dependents via a read-time flatten, and impact --from-diff/--working-tree discover seeds from every present child's git repo |
| Refresh | wd discover --recurse (or per-child wd discover) |
Rebuild child graphs and the root meta-graph; you choose the cadence |
Onboarding a workspace (one-shot bootstrap)
Run wd workspace bootstrap at the workspace root. It is the fastest way to
go from "a directory full of git repos" to "a queryable federated graph". In
a single pass it:
- Initializes the root (writes
.weld/discover.yamland a managed.weld/.gitignore) if it is not already a Weld project. - Scans for nested git repositories and writes
.weld/workspaces.yamllisting each one as a child. - Initializes any child that is not yet a Weld project.
- Runs discovery inside every present child (the same cascade as
wd discover --recurse). - Builds the root meta-graph of
repo:<name>nodes.
cd ~/workspace-root
wd workspace bootstrap
Example output for a root with three children:
[weld] recurse: discovering libs-shared-models ...
[weld] recurse: libs-shared-models done
[weld] recurse: discovering services-api ...
[weld] recurse: services-api done
[weld] recurse: discovering services-auth ...
[weld] recurse: services-auth done
Bootstrapped workspace at: 3 child repo(s) discovered
* root init: already initialized (no-op)
* workspaces.yaml: written
* per-child init: all children already initialized
* discover: libs-shared-models, services-api, services-auth
* present after bootstrap: 3 of 3
Bootstrap is idempotent: re-running it on an already-onboarded root re-scans,
re-discovers, and rebuilds the meta-graph without clobbering child config.
Add --json for a machine-readable summary with children_discovered,
children_initialized, children_present, and errors keys. Common flags:
wd workspace bootstrap --max-depth 2 # limit how deep the scan walks for nested .git
wd workspace bootstrap --exclude-path vendor # skip a dir by name/path/glob (repeatable, persisted)
wd workspace bootstrap --respect-gitignore # skip scan-only children ignored by Git
--exclude-path and --respect-gitignore are persisted into
workspaces.yaml, so subsequent bootstraps stay scoped without re-passing
the flags. Explicit children entries you add by hand always win over the
scan, even when gitignored.
Cross-repo edges are opt-in. Bootstrap writes
cross_repo_strategies: []-- it discovers structure but does not guess which resolvers apply. To wire calls between children, declare resolvers inworkspaces.yamland re-run discovery (see Cross-repo resolvers).
Alternative: manual setup with wd init
If you prefer to onboard the root without immediately discovering children,
run wd init at the workspace root instead. When nested git repositories are
detected, weld scaffolds .weld/workspaces.yaml alongside the usual
discover.yaml, but does not init or discover the children for you:
cd ~/workspace-root
wd init # detects children, writes workspaces.yaml
wd init --max-depth 2 # limit scan depth for large directory trees
The --max-depth flag controls how many directory levels deep the scanner
looks for nested .git directories (default: 4). You then initialize and
discover each child yourself, or run wd discover --recurse at the root to
cascade discovery into every present child in one pass.
workspaces.yaml format
The workspace registry lists every child repo and declares which cross-repo resolvers are active:
version: 1
scan:
max_depth: 4
respect_gitignore: false
exclude_paths: [.worktrees, vendor, "scratch/**", "generated/**/*.tmp"]
children:
- name: services-api
path: services/api
tags:
category: services
- name: services-auth
path: services/auth
tags:
category: services
cross_repo_strategies: [service_graph]
- version: Schema
No comments yet
Be the first to share your take.