weavatrix 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,10 +2,11 @@
2
2
 
3
3
  **Code graph & blast-radius MCP server for AI coding agents.**
4
4
 
5
- Grep sees text. Weavatrix sees structure. It builds a dependency graph of any local repository
6
- files, symbols, and the imports/calls/inheritance connecting them and serves it to Claude Code,
7
- Codex, or any MCP client: change impact, transitive dependents, health audit, clone detection,
8
- coverage mapping. **36 tools available; 33 enabled by the default offline profile. Local-first: with
5
+ Weavatrix builds and compares the living architecture of any local repository: files, symbols,
6
+ imports, calls, inheritance, health findings, clone families and Git-history coupling. It serves that
7
+ evidence to Claude Code, Codex, or any MCP client for change impact, transitive dependents, health
8
+ audit, clone detection and coverage mapping. Text search is included as a convenience, not the product
9
+ core. **38 tools available; 34 enabled by the default offline profile. Local-first: with
9
10
  the defaults, no repository data leaves your machine.**
10
11
 
11
12
  - Website: [weavatrix.com](https://weavatrix.com)
@@ -14,8 +15,8 @@ the defaults, no repository data leaves your machine.**
14
15
 
15
16
  ## Why
16
17
 
17
- An AI agent editing code without the dependency graph is refactoring blind. Weavatrix gives it
18
- answers grep can't produce:
18
+ An AI agent editing code without architecture, health and history evidence is refactoring blind.
19
+ Weavatrix answers questions a text index cannot:
19
20
 
20
21
  - *"What breaks if I change this?"* → `change_impact` diffs your branch (staged, unstaged and
21
22
  untracked included), maps the changed files and symbols onto the graph, and lists everything that
@@ -28,6 +29,9 @@ answers grep can't produce:
28
29
  - *"Did my refactor actually decouple anything?"* → `graph_diff base_ref=HEAD~1` builds an immutable
29
30
  baseline graph without checking it out, then reports the structural delta: new module
30
31
  dependencies, broken or introduced import cycles, and symbols that lost their last caller.
32
+ - *"Where is the repo rotting or repeating itself?"* → `run_audit`, `find_dead_code`,
33
+ `find_duplicates`, `hot_path_review` and `git_history` combine deterministic findings, graph
34
+ connectivity and co-change evidence instead of treating a matching string as an architecture fact.
31
35
 
32
36
  - *"Is this change actually safe?"* -> `verified_change` accepts the task plus current diff/files and
33
37
  returns one proof-carrying `PASS`, `BLOCKED`, or `UNKNOWN`: compact edit contexts, exact-symbol
@@ -67,7 +71,7 @@ argument when a stricter repository boundary or a network feature is wanted:
67
71
  | `offline` (default) | Yes, only through `open_repo` | Yes, only through `trace_api_contract` | No | No |
68
72
  | `pinned` | No | No | No | No |
69
73
  | `osv` | Yes | Yes, only when called | Only when `refresh_advisories` is called | No |
70
- | `hosted` / `full` | Yes | Yes, only when called | Only when called | Only when `sync_graph` or `pull_architecture_contract` is called |
74
+ | `hosted` / `full` | Yes | Yes, only when called | Only when called | `preview_sync` is local; only `sync_graph` or `pull_architecture_contract` can make a request |
71
75
 
72
76
  ```sh
73
77
  # Hard-pin one repository and expose no network tools:
@@ -125,7 +129,7 @@ confirmed by its bundled `typescript-language-server` + TypeScript runtime to `E
125
129
  `CONFLICT` means evidence disagrees. `graph_stats` reports the provenance breakdown and the semantic
126
130
  provider's `COMPLETE`, `PARTIAL`, `UNAVAILABLE`, or `OFF` state; `OFF` means precision was explicitly
127
131
  disabled and only static evidence is active. Java and Rust language-server providers are not bundled
128
- in 0.2.7: their edges never become `EXACT_LSP`, even when a mixed repository reports a complete
132
+ in 0.2.9: their edges never become `EXACT_LSP`, even when a mixed repository reports a complete
129
133
  TypeScript/JavaScript overlay.
130
134
 
131
135
  The bounded JS/TS provider is enabled by default for new graphs. Set `WEAVATRIX_PRECISION=off`
@@ -133,21 +137,28 @@ before starting the MCP server for parser-only operation from the first build, o
133
137
  `precision:"off"` to `rebuild_graph` / `open_repo`. The MCPB installer exposes the same `lsp` / `off`
134
138
  choice as **TypeScript/JavaScript semantic precision**.
135
139
 
136
- **search / source** — `search_code` (ripgrep-backed, pure-Node fallback), `read_source` (a
140
+ **search / source** — `search_code` (ripgrep-backed, pure-Node fallback, with repository-relative
141
+ path globs on Windows/macOS/Linux), `read_source` (a
137
142
  symbol's actual code in one hop), `context_bundle` (definition plus grouped inbound/outbound
138
- containers, exact re-export sites and a few bounded source excerpts), `inspect_symbol` (one exact
143
+ containers, exact re-export sites, call-site/target provenance and bounded excerpts around decisive
144
+ edges), `inspect_symbol` (one exact
139
145
  bounded TS/JS reference query, logical containers, impact and source excerpts), `list_endpoints` (HTTP route inventory:
140
- Express/Fastify/Nest/Flask/FastAPI/Go mux/Rust axum and actix-web …)
146
+ Express/Fastify/Nest/Flask/FastAPI/Go mux/Rust axum and actix-web, including nested Express
147
+ `router.use(...)` mount composition, declaration/reachability counts and mount provenance),
148
+ `trace_endpoint` (one exact composed route → handler → bounded production call graph with call-site excerpts)
141
149
 
142
150
  **health** — `find_dead_code` (bounded review queue for statically unreferenced and test-only files,
143
151
  functions, methods, and symbols, with evidence tiers, completed/remaining verification and explicit
144
152
  public/framework/dynamic caveats),
145
- `run_audit` (unused files/exports/dependencies with per-finding manifest/indexed-source/script/config verification,
153
+ `run_audit` (unused files/exports/dependencies with per-finding manifest/indexed-source/script/config verification;
154
+ `category:dependencies` isolates missing/unused/duplicate declarations, unresolved imports and lockfile drift,
146
155
  missing npm/Go/Python deps, runtime
147
156
  cycles, type-only/compile-only coupling, orphans, boundary rules, offline OSV vulnerabilities + typosquat +
148
157
  lockfile drift; accepts an immutable `base_ref`, `changed_files`, and `debt: new|existing|all` for
149
- review-scoped results), `find_duplicates` (MOSS winnowing over method bodies catches copy-paste even
150
- after renames and can inspect strict small clones down to 12 tokens), `coverage_map` (existing coverage reports mapped onto the graph; untested hotspots
158
+ review-scoped results; production paths are the default and `include_classified:true` opts into
159
+ test/generated/docs evidence), `find_duplicates` (MOSS winnowing over method bodies catches copy-paste even
160
+ after renames and can inspect strict small clones down to 12 tokens; homogeneous router boilerplate
161
+ is suppressed unless `include_boilerplate:true`), `coverage_map` (existing coverage reports mapped onto the graph; untested hotspots
151
162
  ranked by connectivity — tests are never executed), `hot_path_review` (bounded local-cost evidence
152
163
  with separate graph/test risk), `verify_architecture`,
153
164
  `explain_architecture_violation`, `propose_architecture_exception`
@@ -197,24 +208,78 @@ not CFG, value-propagation, or taint analysis.
197
208
 
198
209
  `query_graph` accepts optional `seed_files` when an architectural question must start from exact
199
210
  entry points. Resolved explicit seeds are exclusive by default; set `augment_seeds:true` only when
200
- question-derived seeds are also wanted. Broad bootstrap/tool-execution/routing questions now rank
211
+ question-derived seeds are also wanted. A code-shaped identifier such as `startMitigate` is treated
212
+ as a stronger bounded seed than surrounding words such as controller/service/flow; all exact
213
+ same-name declarations are retained instead of adding unrelated concept seeds. Broad
214
+ bootstrap/tool-execution/routing questions rank
201
215
  conventional executables and graph-declared production entry points ahead of site, documentation, benchmark
202
216
  and fixture matches. Production-first classification also applies during traversal, so unrelated
203
217
  tests/generated/docs/benchmarks do not leak back from a production seed; name a class in the
204
218
  question or set `include_classified:true` to include it. Unreferenced unmatched constant/field leaves
205
219
  are hidden unless `include_low_signal:true`. Broad ranking remains orientation evidence; use
206
- `seed_files` when the intended entry point is already known.
220
+ `seed_files`, an exact endpoint, or `inspect_symbol` when the intended entry point is already known.
221
+ Instruction words such as “REST”, “path”, and “inspect” do not become fuzzy code seeds.
207
222
 
208
223
  **advisories** *(network, explicit opt-in)* — `refresh_advisories`
209
224
 
210
- **hosted** *(network, explicit opt-in)* — `pull_architecture_contract`, `sync_graph`
225
+ **hosted** *(explicit opt-in; preview is local, send/pull are networked)* — `preview_sync`,
226
+ `pull_architecture_contract`, `sync_graph`. `preview_sync` performs no request and returns the destination,
227
+ repository UUID, exact payload fields/sections/size/hash and a short-lived confirmation token for
228
+ the cached payload. `sync_graph` sends only with both that token and `dry_run:false`; its legacy
229
+ dry-run form remains a no-network compatibility alias.
211
230
 
212
231
  Quality of life: graph/health reads auto-reconcile and expose `none` / `incremental` / `full`
213
232
  freshness, with a short clean-read debounce to avoid rescanning the repository for every tool call;
214
233
  ambiguous name lookups are
215
234
  disclosed instead of silently guessed; and the server **hot-reloads its watched MCP tool entry
216
235
  modules and catalog** when those files change — other MCP helpers and analysis engines require a
217
- reconnect.
236
+ reconnect. Every MCP response also carries local, transient `_meta["weavatrix/metrics"]` with elapsed
237
+ time, output bytes/token estimate, graph freshness/revision/update and graph-cache status. These
238
+ metrics are not persisted or transmitted by Weavatrix.
239
+
240
+ ### 0.2.9 correctness, signal, and consent patch
241
+
242
+ - Express endpoint inventory composes nested imported `router.use(...)` mounts, so a local route such
243
+ as `/:attackId/startMitigate` is reported with its reachable `/warRoom/attack` prefix, declared vs
244
+ reachable counts and the exact static mount chain. `trace_endpoint` continues from that route into
245
+ the bounded call graph and call-site excerpts.
246
+ - `context_bundle` keeps the call-site file/line separate from the target definition and adds bounded
247
+ excerpts around decisive edges instead of stopping inside a long leading comment.
248
+ - `search_code` applies documented repository-relative path globs correctly when ripgrep runs on
249
+ Windows. Broad `query_graph` prompts no longer turn generic REST/instruction words into noisy
250
+ configuration-file seeds, and code-shaped identifiers outrank generic controller/service/flow
251
+ concepts.
252
+ - `run_audit` is production-first by default while retaining explicit access to classified evidence;
253
+ `category:dependencies` provides a focused dependency-health slice;
254
+ duplicate review suppresses homogeneous Express router boilerplate unless requested.
255
+ - the separate no-network `preview_sync` produces the exact payload approval artifact; `sync_graph`
256
+ requires that preview followed by an exact short-lived confirmation token,
257
+ validates the configured destination, requires HTTPS outside loopback, and never networks during
258
+ preview. Contract-pull HTTP failures expose actionable auth/not-found/not-ready states.
259
+ - Every MCP call exposes transient local execution/output/freshness metrics without collection or
260
+ egress. The public site, privacy/security pages, package metadata and license presentation now
261
+ describe the same 38-tool/34-offline surface and the same network boundary.
262
+
263
+ Full patch notes: [docs/releases/v0.2.9.md](docs/releases/v0.2.9.md).
264
+
265
+ ### 0.2.8 trust and precision patch
266
+
267
+ - Dead-code review no longer labels a declaration `test-only` when it has a same-file production
268
+ use. Revision-bound positive evidence from an exact `inspect_symbol` point query also removes that
269
+ declaration from later dead-code candidates instead of remaining isolated in its cache.
270
+ - Python dependency checks discover nested `requirements*.txt`, `requirements/*.txt`,
271
+ `pyproject.toml`, and `Pipfile` manifests and assign imports to their nearest manifest scope.
272
+ A root `test.py` is classified consistently as test code.
273
+ - Endpoint extraction masks commented-out routes without shifting source offsets and rejects
274
+ primitive path-to-name maps such as `{'/.codex': 'claude'}` while retaining real route tables.
275
+ - Rust symbols now expose concrete kinds, owner types, visibility/export state, selection ranges and
276
+ structural owner/member edges. This is parser evidence; Rust semantic/LSP precision remains
277
+ explicitly unavailable.
278
+ - Natural-language retrieval honors explicit Rust, Python, TypeScript, JavaScript, Go, Java, and C#
279
+ intent in mixed repositories. Compact `verified_change` text now includes decisive exact reference
280
+ counts and bounded inbound caller names for every selected edit symbol.
281
+
282
+ Full patch notes: [docs/releases/v0.2.8.md](docs/releases/v0.2.8.md).
218
283
 
219
284
  ### 0.2.7 verified-change workflow
220
285
 
@@ -473,20 +538,27 @@ Weavatrix itself initiates outbound HTTP only from three tools; all are absent f
473
538
  - `pull_architecture_contract` — sends the active repository's opaque stable UUID with bearer
474
539
  authentication, downloads the owner-approved target-architecture contract, validates it, and
475
540
  stores the contract in the local graph cache. It sends no source, symbol, or repository path.
476
- - `sync_graph` — builds a bounded evidence snapshot locally, then sends payload v3: the v2 graph
541
+ - `preview_sync` — builds a bounded evidence snapshot locally without sending anything. The preview
542
+ shows the destination host/path, normalized repository name, opaque UUID,
543
+ payload version, exact top-level fields, included sections, node/link/byte counts and canonical
544
+ SHA-256 hash. A `sync_graph` call with `dry_run:false` and its exact short-lived `confirm_token` sends
545
+ the cached payload to the same destination. A token alone does not send while the default dry-run
546
+ remains active. It
547
+ sends payload v3: the v2 graph
477
548
  allowlist plus module dependencies, runtime/compile-time cycles, declared boundary violations,
478
549
  health findings, complexity-threshold breaches, stack identifiers, a bounded direct/transitive
479
550
  dependency graph, direct package usage, and bounded clone/divergence review candidates. Local
480
551
  analyzers may read repository source and manifests to derive those
481
552
  facts. The wire contract has no fields for file bodies, snippets, absolute host paths, environment
482
553
  values or Git remotes; unknown fields are discarded and unsafe optional path metadata is omitted.
483
- The request also carries the normalized repository display name used by the
554
+ The confirmed request also carries the normalized repository display name used by the
484
555
  hosted list. The endpoint is **yours**, configured through `WEAVATRIX_SYNC_URL` and the optional
485
556
  `WEAVATRIX_SYNC_TOKEN`; the feature is off by default. Pass `payload_version: 2` only for an
486
557
  intentional graph-only compatibility sync—there is no silent downgrade that discards evidence.
487
558
  Graphs that predate current typed-edge/provenance metadata must be rebuilt once before V3 sync;
488
559
  V3 also refuses a stale graph so
489
- source-derived evidence cannot be mixed with old topology.
560
+ source-derived evidence cannot be mixed with old topology. HTTPS is required except for an
561
+ explicitly configured loopback endpoint; embedded URL credentials and fragments are rejected.
490
562
 
491
563
  Evidence sections carry independent `state` (`COMPLETE`, `PARTIAL`, `NOT_CHECKED`,
492
564
  `NOT_APPLICABLE`, `ERROR`) and `verdict` (`PASS`, `FAIL`, `UNKNOWN`) fields plus exact
@@ -512,7 +584,7 @@ vulnerability findings. This is where each capability comes from and how it is c
512
584
 
513
585
  | Capability alert | Why it exists | Activation and boundary |
514
586
  |---|---|---|
515
- | Network access | `refresh_advisories` sends pinned package names and versions to OSV; `pull_architecture_contract` sends an opaque repository UUID and receives an owner-approved contract; `sync_graph` sends a normalized repository label plus an allowlisted graph/evidence payload. Evidence is derived locally from source and manifests, but source bodies, snippets, absolute paths, environment values, credentials and Git remotes are excluded | `offline` and `pinned` expose no network tools. `osv` exposes only advisory refresh. `hosted`/`full` expose all three; every request still requires an explicit tool call, and hosted calls require `WEAVATRIX_SYNC_URL` |
587
+ | Network access | `refresh_advisories` sends pinned package names and versions to OSV; `pull_architecture_contract` sends an opaque repository UUID and receives an owner-approved contract; confirmed `sync_graph` sends a normalized repository label plus an allowlisted graph/evidence payload. Evidence is derived locally from source and manifests, but source bodies, snippets, absolute paths, environment values, credentials and Git remotes are excluded | `offline` and `pinned` expose no network tools. `osv` exposes only advisory refresh. `hosted`/`full` expose all three. Sync defaults to a no-network dry-run and then requires both `dry_run:false` and its exact short-lived token; hosted calls require `WEAVATRIX_SYNC_URL`, and non-loopback hosted endpoints require HTTPS |
516
588
  | Shell access | Local `git` powers staleness/change impact; `rg` accelerates search; the bundled TLS/tsserver process supplies bounded JS/TS semantic evidence; timed-out Windows child trees may be terminated; `verified_change` can optionally run an existing package test/check/verify script | The semantic provider never invokes repository code. Test execution separately requires `run_tests:true` plus `WEAVATRIX_ALLOW_TEST_RUNS=1`, rejects arbitrary commands and shell-sensitive arguments, and should be enabled only for trusted repository scripts |
517
589
  | Debug / dynamic loading | Cache-busted `import()` hot-reloads watched MCP tool entry modules; `createRequire` loads package metadata and parser dependencies | Loads files from the installed package; no `eval` |
518
590
  | Environment access | Reads `WEAVATRIX_*` configuration; ordinary local helpers inherit a credential-stripped environment, while TLS/tsserver receives only allowlisted OS/temp/locale values and a constrained executable path | `WEAVATRIX_SYNC_TOKEN` is removed from every child-process and worker environment. TLS/tsserver also receives no registry/proxy/cloud credentials or `NODE_OPTIONS` |
@@ -565,10 +637,10 @@ loader) behind the thin stdio entry `src/mcp-server.mjs`.
565
637
  - **Wrapper-aware API contracts** shipped in 0.2.2: persistent/ad-hoc configuration,
566
638
  conservative discovery, cross-repository handler liveness and explicit unknown states. The next
567
639
  hosted increment joins privacy-safe contract identities across separately synced services.
568
- - **Hosted architecture workbench** is live at
569
- [app.weavatrix.com](https://app.weavatrix.com): explicit source-free sync,
570
- immutable history, Flow/DSM/Map, target architecture and bounded review
571
- evidence. Hosted use remains optional and owner-authenticated.
640
+ - **Hosted architecture workbench** at
641
+ [app.weavatrix.com](https://app.weavatrix.com) is an access-controlled preview for
642
+ owner-authenticated source-free evidence and revision history. Its UI and backend evolve
643
+ independently from the public MCP release; local use remains fully optional.
572
644
  - **Semantic precision bridge** shipped for TypeScript/JavaScript in 0.2.4: a bounded, revision-bound
573
645
  local overlay validates references with the bundled language server while the parser graph remains
574
646
  the fallback. Java and Rust providers are not bundled yet and stay explicitly `UNAVAILABLE`.
@@ -584,4 +656,6 @@ The public alignment note for the fixed cross-product release sequence is in
584
656
 
585
657
  ## License
586
658
 
587
- [MIT](LICENSE) © 2026 Sergii Ziborov
659
+ The Weavatrix source in this repository is [MIT licensed](LICENSE) © 2026 Sergii Ziborov.
660
+ Third-party dependencies retain their own licenses. See the public
661
+ [license page](https://weavatrix.com/license) for the same notice.
package/SECURITY.md CHANGED
@@ -2,8 +2,13 @@
2
2
 
3
3
  ## Supported versions
4
4
 
5
- Security fixes are provided for the latest published version of Weavatrix. Upgrade to the newest
6
- release before reporting an issue that may already be resolved.
5
+ | Version | Supported |
6
+ |---|---|
7
+ | `0.2.9.x` | Yes |
8
+ | `0.2.8` and older | Upgrade to the latest release |
9
+
10
+ Security fixes are provided for the latest published version of Weavatrix. Upgrade before reporting
11
+ an issue that may already be resolved.
7
12
 
8
13
  ## Reporting a vulnerability
9
14
 
@@ -61,10 +66,25 @@ scripts are repository-controlled code and may have arbitrary side effects, so e
61
66
  for repositories and scripts you trust.
62
67
 
63
68
  Network capabilities are split by purpose. `osv` adds only `refresh_advisories`, which sends pinned
64
- package names and versions to OSV.dev when called. `hosted` / `full` also expose `sync_graph` and
65
- `pull_architecture_contract`; both require a user-configured endpoint, and contract pull requires
66
- bearer authentication. The legacy `online` capability remains an alias for advisory plus hosted
67
- networking so existing registrations do not break.
69
+ package names and versions to OSV.dev when called. `hosted` / `full` also expose the local-only
70
+ `preview_sync` plus networked `sync_graph` and `pull_architecture_contract`; the network tools require
71
+ a user-configured endpoint, and contract pull requires bearer authentication. The legacy `online`
72
+ capability remains an alias for advisory plus hosted networking so existing registrations do not break.
73
+
74
+ `preview_sync` is the first half of a two-step consent boundary. It constructs the strict allowlisted
75
+ payload locally but has no network path. It displays the configured hostname/path, normalized
76
+ repository name, opaque repository UUID, payload version, included sections, node/link/byte counts
77
+ and canonical SHA-256 hash. Sending requires `sync_graph` with both `dry_run:false` and the exact
78
+ confirmation token for that cached payload and destination; the token
79
+ expires after five minutes and is stored only in process memory. A token with the default dry-run,
80
+ or an incorrect/expired token, does not send. Sync rejects
81
+ embedded URL credentials and fragments and requires HTTPS except for explicit loopback development
82
+ endpoints. Query parameters may be used by a configured endpoint but are not echoed in previews or
83
+ errors.
84
+
85
+ Every MCP response includes transient local `_meta["weavatrix/metrics"]` timing, output-size/token
86
+ estimate, graph freshness/revision/update and graph-cache status. Weavatrix does not persist, aggregate
87
+ or transmit these measurements; they are response evidence for the caller, not product telemetry.
68
88
 
69
89
  Payload v3 runs bounded local analyzers over the active repository, then sends only a strict
70
90
  graph/evidence allowlist plus a normalized repository display name. That evidence can include a
@@ -77,4 +97,7 @@ remains an explicit compatibility option and is never selected as a silent fallb
77
97
 
78
98
  `pull_architecture_contract` sends only the active repository's opaque stable UUID, receives an
79
99
  owner-approved target contract, validates it, and caches it locally. It never sends source, symbols,
80
- file paths, or Git metadata.
100
+ file paths, or Git metadata. It applies the same HTTPS-outside-loopback and embedded-credential URL
101
+ rules before sending bearer authentication. Authentication, forbidden, not-found and repository-not-ready responses
102
+ are reported as distinct states so a missing registration or contract is not presented as an opaque
103
+ HTTP failure.
@@ -0,0 +1,123 @@
1
+ # Weavatrix 0.2.9
2
+
3
+ ## Correctness, signal, and consent
4
+
5
+ 0.2.9 is a focused MCP correctness release driven by dogfooding on a 10,000-node JavaScript REST
6
+ service. It fixes cases where broad repository orientation was useful but precision tools handed an
7
+ agent incomplete paths, mismatched call-site provenance, or noisy review queues.
8
+
9
+ ## Composed Express routes
10
+
11
+ `list_endpoints` now follows relative ESM/CommonJS router imports and nested `router.use(...)`
12
+ mounts. A handler declared as `/:attackId/startMitigate` below `/warRoom` and `/attack` is reported as
13
+ `/warRoom/attack/:attackId/startMitigate` while retaining its local declaration path. The inventory
14
+ separates declaration and reachable-route counts, labels local versus statically composed evidence,
15
+ and returns the exact mount files, lines and prefixes.
16
+
17
+ Composition is intentionally bounded. Dynamic mount expressions that cannot be resolved statically
18
+ remain local-path evidence rather than guessed full routes.
19
+
20
+ The new `trace_endpoint` tool resolves one exact composed route, binds its handler without silently
21
+ choosing among same-name symbols, and traverses a bounded production-only call graph with excerpts at
22
+ the actual edge lines. On the dogfood route it recovered router mount provenance, controller:268,
23
+ service, messaging and task calls in one graph-anchored response.
24
+
25
+ ## Edge-centered source context
26
+
27
+ `context_bundle` now keeps two facts separate:
28
+
29
+ - the file and line containing an outbound call;
30
+ - the target symbol's definition file.
31
+
32
+ The compact source bundle prioritizes the definition and bounded excerpts around decisive inbound and
33
+ outbound edge lines. Long Swagger/JSDoc comments at the beginning of a controller therefore no longer
34
+ consume the only excerpt before the real service call.
35
+
36
+ ## Search and retrieval signal
37
+
38
+ - Ripgrep-backed `search_code` runs from the repository root, so documented relative globs such as
39
+ `services/attack/**` behave consistently on Windows, macOS, and Linux.
40
+ - Broad `query_graph` prompts discard generic REST/instruction terms before fuzzy seeding and use a
41
+ smaller automatic seed set. When prose contains a code-shaped identifier such as `startMitigate`,
42
+ its exact same-name declarations are exclusive automatic seeds; generic controller/service/flow
43
+ words cannot add unrelated modules. Exact files, endpoints, and symbols remain the preferred
44
+ precision path.
45
+
46
+ ## Production-first review queues
47
+
48
+ - `run_audit` suppresses findings whose evidence is entirely in tests, generated output, docs,
49
+ benchmarks, fixtures, mocks, or stories. `include_classified:true` restores the full diagnostic
50
+ view; mixed production/non-production evidence remains visible by default.
51
+ - `run_audit category=dependencies` projects missing, unused and duplicate declarations, unresolved
52
+ imports and lockfile drift into one dependency-health review without relabelling their native
53
+ deterministic finding identities.
54
+ - `find_duplicates` suppresses groups made entirely of router boilerplate by default.
55
+ `include_boilerplate:true` restores them for framework-template review.
56
+ - Incomplete OSV coverage remains explicit as `NOT_CHECKED`; this release does not turn missing
57
+ advisory data into a clean security verdict.
58
+
59
+ ## Preview-confirm graph sync
60
+
61
+ `preview_sync` and `sync_graph` are now a two-step consent flow:
62
+
63
+ 1. `preview_sync` builds and validates the strict payload allowlist locally and has no network path.
64
+ 2. Display the destination, repository identity, exact top-level fields, payload
65
+ version/sections/size/hash and return a short-lived confirmation token.
66
+ 3. `sync_graph` sends only the exact cached payload when it supplies both `dry_run:false` and that token
67
+ within five minutes. A token alone remains a no-network dry-run.
68
+
69
+ Incorrect or expired tokens never send. Sync also rejects embedded URL credentials/fragments and
70
+ requires HTTPS except for loopback development endpoints. URL query values are not echoed in previews
71
+ or errors.
72
+
73
+ `pull_architecture_contract` applies the same HTTPS-outside-loopback and embedded-credential URL
74
+ rules before sending bearer authentication. It now distinguishes authentication, forbidden,
75
+ repository-not-registered, endpoint-not-found, repository-not-ready and general HTTP failures.
76
+ Hosted's `REPOSITORY_NOT_FOUND` response is therefore no longer confused with a wrong endpoint;
77
+ `NOT_CONFIGURED` explicitly means sync/authentication succeeded but no target contract was saved.
78
+
79
+ ## Per-call metrics without telemetry
80
+
81
+ Every MCP response carries transient local `_meta["weavatrix/metrics"]`: duration, text/structured
82
+ bytes, estimated output tokens, graph freshness/revision/update and graph-cache status. These values
83
+ are returned to the caller only; Weavatrix does not persist, aggregate or transmit them.
84
+
85
+ ## Public contract alignment
86
+
87
+ The README, package manifests, public home page, privacy policy, security policy, and new public
88
+ license/security pages now describe the same 38-tool surface, 34-tool offline default, preview-confirm
89
+ sync behavior, and source/data boundary. The optional Hosted workbench is described as an
90
+ independently evolving access-controlled preview. Payload v3 is unchanged, so existing Hosted storage,
91
+ history and evidence readers remain wire-compatible; Hosted contract tests validate v1/v2/v3.
92
+
93
+ ## Compatibility
94
+
95
+ The release adds `trace_endpoint` to the local source surface and `preview_sync` to the explicit
96
+ Hosted surface, taking the catalog from 36 to 38 tools and the default offline profile from 33 to 34.
97
+ Existing offline, pinned, osv, hosted, full and legacy online capability names remain valid.
98
+ `sync_graph` retains its no-network dry-run compatibility form, but `preview_sync` is the canonical
99
+ approval step for new clients.
100
+
101
+ ## Verification
102
+
103
+ - Full Node suite: 502 tests, 499 passed, 3 platform skips, 0 failed.
104
+ - Deterministic six-language, cross-repository, framework-convention and MCP lifecycle benchmark:
105
+ `PASS`.
106
+ - Real pinned corpus: 6 of 6 repositories `PASS` across TypeScript, JavaScript, Python, Go, Java and
107
+ Rust.
108
+ - Release metadata consistency: `PASS`; production npm audit: 0 vulnerabilities; npm 0.2.9 package
109
+ dry-run: 154 files, 413,993 bytes.
110
+ - Portable MCPB: manifest validation `PASS`; archive built at 6.7 MiB
111
+ (`5a5def22491bc0777c882c314b12be7153d56d29`). The local archive is unsigned.
112
+ - Direct `controller-rest-api` dogfood: 1,076 indexed files and 989 reachable composed endpoints;
113
+ public and internal composed
114
+ `startMitigate` routes found; documented attack glob returned real matches; context preserved
115
+ `controller:268 → service` with the call excerpt; production-first audit retained the manually
116
+ confirmed missing `mongodb` declaration and explicit `OSV NOT_CHECKED`; 2 router-only clone groups
117
+ were separated from 60 default groups; exact identifier seeding returned only controller, service
118
+ and messaging declarations.
119
+
120
+ Focused regressions cover nested Express mounts, call-site/target provenance and edge excerpts,
121
+ Windows path globs, generic REST and exact-identifier seed handling, production-first audit filtering,
122
+ router boilerplate suppression, no-network sync previews, confirmation-token mismatch, URL redaction,
123
+ and actionable contract 404 state.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "weavatrix",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
- "description": "Weavatrix — code graph & blast-radius MCP server for AI coding agents: change impact, transitive dependents, health audit, duplicate detection, and coverage mapping over any local repository.",
5
+ "description": "Weavatrix — architecture graph, health, clone and history MCP server for AI coding agents: blast radius, dependency audit, graph diff and coverage over any local repository.",
6
6
  "author": "Sergii Ziborov <sergii.ziborov@gmail.com>",
7
7
  "license": "MIT",
8
8
  "homepage": "https://weavatrix.com",
@@ -26,7 +26,7 @@
26
26
  "skill",
27
27
  "scripts/run-agent-task-benchmark.mjs",
28
28
  "docs/agent-task-benchmark.md",
29
- "docs/releases/v0.2.7.md",
29
+ "docs/releases/v0.2.9.md",
30
30
  "README.md",
31
31
  "SECURITY.md",
32
32
  "LICENSE"
package/skill/SKILL.md CHANGED
@@ -16,7 +16,7 @@ Tools are named `mcp__weavatrix__…`. If none are available, ask the user to re
16
16
  (`claude mcp add -s user weavatrix -- npx -y weavatrix <repoRoot>`; Codex:
17
17
  `codex mcp add weavatrix -- npx -y weavatrix <repoRoot>`), then retry.
18
18
 
19
- If `refresh_advisories`, `pull_architecture_contract`, or `sync_graph` is missing, do not diagnose a
19
+ If `refresh_advisories`, `preview_sync`, `pull_architecture_contract`, or `sync_graph` is missing, do not diagnose a
20
20
  broken installation: network tools are intentionally absent from `offline` and `pinned`.
21
21
 
22
22
  Profiles use the same npm package and binary:
@@ -24,7 +24,7 @@ Profiles use the same npm package and binary:
24
24
  - `offline` (default): all local analysis and explicit `open_repo`; no HTTP tools.
25
25
  - `pinned`: local analysis with no `open_repo`, no global/cross-repository graph access, and no HTTP tools.
26
26
  - `osv`: `offline` plus explicit `refresh_advisories`.
27
- - `hosted` / `full`: `osv` plus `pull_architecture_contract` and `sync_graph`.
27
+ - `hosted` / `full`: `osv` plus local-only `preview_sync`, `pull_architecture_contract` and `sync_graph`.
28
28
 
29
29
  The legacy `online` capability remains a compatibility alias for `advisories,hosted`; prefer the
30
30
  named profiles for new registrations.
@@ -117,8 +117,8 @@ Start from the task, not from the complete tool list:
117
117
  ONLY network-touching tools live in the optional
118
118
  `advisories` / `hosted` capabilities and run solely when explicitly called:
119
119
  `refresh_advisories` (queries OSV.dev with
120
- package names + versions so `run_audit` has fresh vulnerability data) and `sync_graph` (derives a
121
- bounded evidence snapshot locally, then pushes only an allowlisted graph/evidence contract to a
120
+ package names + versions so `run_audit` has fresh vulnerability data) and `sync_graph` (pushes only
121
+ the exact allowlisted graph/evidence contract previously serialized by local-only `preview_sync` to a
122
122
  user-configured endpoint; analyzers may read local source, but the wire contract has no body,
123
123
  snippet, absolute-host-path or environment fields, and unknown fields are discarded; disabled until
124
124
  `WEAVATRIX_SYNC_URL` is set). `pull_architecture_contract` sends only the active repository's opaque
@@ -175,7 +175,9 @@ Start from the task, not from the complete tool list:
175
175
  `REVIEW_REQUIRED` and `autoDelete:false` are hard
176
176
  safety semantics, not boilerplate. Use `.weavatrix-deps.json` entrypoints/nonRuntimeRoots only for
177
177
  verified conventions.
178
- - **API inventory**: `list_endpoints` (including Next.js App Router, Rust axum and actix-web).
178
+ - **API inventory**: `list_endpoints` (including mount provenance, Next.js App Router, Rust axum and
179
+ actix-web). When one exact route is known, use `trace_endpoint` for its composed mount chain,
180
+ handler and bounded production call graph instead of broad natural-language traversal.
179
181
  - **Cross-repository API impact**: ensure both repositories are in `list_known_repos`, then call
180
182
  `trace_api_contract backend=<uuid-or-label> clients=[<uuid-or-label>]`; narrow with `method`, `path`,
181
183
  or backend `changed_files`. `path` may be a segment-aligned fragment (`/query` can select
@@ -237,7 +239,10 @@ visible. `managedDependencies` documents modules provided outside the repo's Pyt
237
239
 
238
240
  ## Sync
239
241
 
240
- `sync_graph` defaults to payload v3: graph metadata plus deterministic architecture, health, stack,
242
+ Call `preview_sync` first. It validates the configured destination and serializes payload v3 locally,
243
+ returning the repository UUID, exact fields/sections, counts, bytes, hash and short-lived confirmation
244
+ token without networking. After explicit approval, call `sync_graph dry_run=false` with that token.
245
+ Payload v3 contains graph metadata plus deterministic architecture, health, stack,
241
246
  package-dependency and clone-review evidence. Read each section's `state`, `verdict` and completeness counts; `PARTIAL`,
242
247
  `NOT_CHECKED` and `ERROR` are unknown/incomplete, never a clean result. Architecture evidence
243
248
  contains concrete runtime versus compile-time cycles, declared boundary violations and separated
@@ -257,7 +262,7 @@ the working tree, must be rebuilt first. Sync remains unavailable until the user
257
262
  - `refresh_advisories` is unavailable → with the user's approval, re-register/reconfigure the MCP
258
263
  with the `osv` profile, reconnect it, and then invoke the tool. Do not enable network
259
264
  access merely to turn `NOT_CHECKED` into a cosmetic green state.
260
- - `pull_architecture_contract` / `sync_graph` is unavailable → use the `hosted` profile only after
265
+ - `preview_sync` / `pull_architecture_contract` / `sync_graph` is unavailable → use the `hosted` profile only after
261
266
  the user chooses hosted integration; configure `WEAVATRIX_SYNC_URL` and a bearer token for contract
262
267
  pull. Profile selection alone never performs a request.
263
268
  - `No coverage report` → run the repo's own tests with coverage (`vitest run --coverage`,
@@ -126,14 +126,29 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
126
126
 
127
127
  const symbolNames = new Set([...symById.values()].map((node) => bareName(node.label)).filter(Boolean));
128
128
  const occurrenceFiles = new Map();
129
+ const occurrenceCounts = new Map();
129
130
  for (const [file, text] of sources) for (const match of String(text || "").matchAll(IDENT_RE)) {
130
131
  const name = match[0];
131
132
  if (!symbolNames.has(name)) continue;
132
133
  const files = occurrenceFiles.get(name) || new Set();
133
134
  files.add(file);
134
135
  occurrenceFiles.set(name, files);
136
+ const counts = occurrenceCounts.get(name) || new Map();
137
+ counts.set(file, (counts.get(file) || 0) + 1);
138
+ occurrenceCounts.set(name, counts);
135
139
  }
136
140
 
141
+ const declarationCounts = new Map();
142
+ for (const node of symById.values()) {
143
+ const name = bareName(node.label);
144
+ if (!name) continue;
145
+ const key = `${node.source_file}\0${name}`;
146
+ declarationCounts.set(key, (declarationCounts.get(key) || 0) + 1);
147
+ }
148
+ const exactReferenceIds = new Set(graph.precisionReferenceSymbols || []);
149
+ const exactProductionReferenceIds = new Set(graph.precisionProductionReferenceSymbols || []);
150
+ const exactTestReferenceIds = new Set(graph.precisionTestReferenceSymbols || []);
151
+
137
152
  // decorated defs (@app.route/@app.event/@pytest.fixture…) are entered by the framework: trust the
138
153
  // builder's flag when present, else walk the source line(s) above the definition (graph-builder graphs).
139
154
  const lineCache = new Map();
@@ -155,6 +170,7 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
155
170
 
156
171
  const isReferenced = (n) => {
157
172
  if (inbound.has(n.id)) return true; // a real graph edge targets it
173
+ if (exactReferenceIds.has(String(n.id))) return true; // revision-bound point-query evidence found a caller
158
174
  const name = bareName(n.label);
159
175
  if (!name || !/^[A-Za-z_$]/.test(name)) return true; // selectors/odd labels → don't flag
160
176
  if (/^__\w+__$/.test(name)) return true; // dunders are invoked implicitly (with/str/==/iter…), never spelled
@@ -182,8 +198,14 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
182
198
  const name = bareName(n.label);
183
199
  const occurrenceSet = occurrenceFiles.get(name) || new Set();
184
200
  const externalOccurrences = [...occurrenceSet].filter((file) => file !== n.source_file);
201
+ const localOccurrences = occurrenceCounts.get(name)?.get(n.source_file) || 0;
202
+ const localDeclarations = declarationCounts.get(`${n.source_file}\0${name}`) || 0;
203
+ const hasLocalProductionUse = localOccurrences > localDeclarations;
185
204
  const lexicalTestOnly = externalOccurrences.length > 0 && externalOccurrences.every((file) => isTestFile(file));
186
- if (hasProductionInbound || (!hasTestInbound && !lexicalTestOnly)) continue;
205
+ const hasExactProductionInbound = exactProductionReferenceIds.has(String(n.id));
206
+ const hasExactTestInbound = exactTestReferenceIds.has(String(n.id));
207
+ if (hasProductionInbound || hasExactProductionInbound || hasLocalProductionUse
208
+ || (!hasTestInbound && !hasExactTestInbound && !lexicalTestOnly)) continue;
187
209
  testOnlySymbols.push({
188
210
  id: n.id,
189
211
  file: n.source_file,
@@ -191,7 +213,7 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
191
213
  test: false,
192
214
  reason: "referenced only from test/e2e code; no production consumer was found",
193
215
  testConsumerFiles: [...new Set(sourceFiles.filter((file) => file && isTestFile(file)))].sort(),
194
- evidence: hasTestInbound ? "graph" : "lexical",
216
+ evidence: hasTestInbound ? "graph" : hasExactTestInbound ? "exact-semantic" : "lexical",
195
217
  publicApi: n.exported === true || ["public", "protected"].includes(String(n.visibility || "").toLowerCase()),
196
218
  });
197
219
  }
@@ -1,6 +1,6 @@
1
1
  import { makeFinding } from "./findings.js";
2
2
 
3
- const TEST_PATH_RE = /(^|[/\\])(test|tests|__tests__|spec|e2e|__mocks__)([/\\]|$)|[._-](test|spec)\.[a-z0-9]+$|_test\.go$/i;
3
+ const TEST_PATH_RE = /(^|[/\\])(test|tests|__tests__|spec|e2e|__mocks__)([/\\]|$)|[._-](test|spec)\.[a-z0-9]+$|_test\.go$|(^|[/\\])test(?:_[^/\\]*)?\.py$/i;
4
4
  const escRe = (s) => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
5
  const mentioned = (blob, name) => new RegExp(`(^|[^\\w@.-])${escRe(name)}($|[^\\w.-])`).test(blob);
6
6
 
@@ -79,7 +79,7 @@ const PY_TOOL_DISTS = new Set(("pytest tox nox black ruff flake8 pylint mypy pyr
79
79
  "pip-tools uv virtualenv pipenv gunicorn uwsgi supervisor ipython jupyter jupyterlab notebook ipykernel codecov autopep8 yapf commitizen detect-secrets safety pip-audit hatchling flit flit-core pdm").split(" "));
80
80
  const pyNorm = (n) => String(n || "").toLowerCase().replace(/[-_.]+/g, "-");
81
81
 
82
- export function computePyDepFindings({
82
+ function computePyDepFindingsFlat({
83
83
  externalImports = [], pyManifest = null, configTexts = new Map(),
84
84
  managedDependencies = [], ignoredDependencies = [], nonRuntimeRoots = [],
85
85
  } = {}) {
@@ -161,3 +161,42 @@ export function computePyDepFindings({
161
161
  }
162
162
  return { findings, declared, managed };
163
163
  }
164
+
165
+ const normPyScope = (root) => String(root || "").replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+|\/+$/g, "");
166
+ const pyScopeOwns = (root, file) => !root || file === root || String(file || "").replace(/\\/g, "/").startsWith(`${root}/`);
167
+
168
+ export function computePyDepFindings(options = {}) {
169
+ const scopes = Array.isArray(options.pyManifest?.scopes) ? options.pyManifest.scopes : [];
170
+ if (!scopes.length) return computePyDepFindingsFlat(options);
171
+ const normalized = scopes.map((scope) => ({ ...scope, root: normPyScope(scope.root) }))
172
+ .sort((left, right) => right.root.length - left.root.length);
173
+ if (!normalized.some((scope) => !scope.root)) normalized.push({ root: "", present: false, deps: [], manifests: [] });
174
+ const importsByScope = new Map(normalized.map((scope) => [scope, []]));
175
+ const configByScope = new Map(normalized.map((scope) => [scope, new Map()]));
176
+ for (const entry of options.externalImports || []) {
177
+ const owner = normalized.find((scope) => pyScopeOwns(scope.root, entry.file)) || normalized.at(-1);
178
+ importsByScope.get(owner).push(entry);
179
+ }
180
+ for (const [file, text] of options.configTexts || new Map()) {
181
+ const owner = normalized.find((scope) => pyScopeOwns(scope.root, file)) || normalized.at(-1);
182
+ configByScope.get(owner).set(file, text);
183
+ }
184
+ const findings = [];
185
+ const declared = new Set();
186
+ const managed = new Set();
187
+ for (const scope of normalized) {
188
+ const result = computePyDepFindingsFlat({
189
+ ...options,
190
+ externalImports: importsByScope.get(scope),
191
+ configTexts: configByScope.get(scope),
192
+ pyManifest: {present: scope.present, deps: scope.deps || []},
193
+ });
194
+ findings.push(...result.findings.map((finding) => ({
195
+ ...finding,
196
+ ...(scope.manifests?.length ? {manifest: scope.manifests[0]} : {}),
197
+ })));
198
+ for (const name of result.declared) declared.add(`${scope.root || "."}:${name}`);
199
+ for (const name of result.managed) managed.add(name);
200
+ }
201
+ return { findings, declared, managed };
202
+ }