weavatrix 0.2.3 → 0.2.5
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 +91 -20
- package/SECURITY.md +18 -0
- package/package.json +3 -1
- package/skill/SKILL.md +49 -13
- package/src/analysis/dead-check.js +48 -2
- package/src/analysis/dead-code-review.js +22 -8
- package/src/analysis/duplicates.compute.js +11 -6
- package/src/analysis/git-ref-graph.js +10 -2
- package/src/analysis/hot-path-review.js +228 -0
- package/src/analysis/internal-audit.run.js +36 -0
- package/src/analysis/source-complexity.report.js +5 -0
- package/src/analysis/source-complexity.walk.js +64 -10
- package/src/build-graph.js +49 -10
- package/src/graph/build-worker.js +21 -1
- package/src/graph/builder/lang-java.js +1 -0
- package/src/graph/builder/lang-js.js +24 -0
- package/src/graph/builder/lang-python.js +161 -4
- package/src/graph/freshness-probe.js +3 -3
- package/src/graph/incremental-refresh.js +1 -1
- package/src/graph/internal-builder.barrels.js +33 -4
- package/src/graph/internal-builder.build.js +52 -8
- package/src/mcp/catalog.mjs +14 -11
- package/src/mcp/graph-context.mjs +143 -14
- package/src/mcp/sync-payload.mjs +2 -1
- package/src/mcp/tools-actions.mjs +59 -20
- package/src/mcp/tools-company.mjs +11 -3
- package/src/mcp/tools-graph.mjs +12 -6
- package/src/mcp/tools-health.mjs +112 -11
- package/src/mcp/tools-impact.mjs +24 -6
- package/src/mcp/tools-source.mjs +237 -0
- package/src/mcp-server.mjs +99 -11
- package/src/path-classification.js +2 -2
- package/src/precision/lsp-client.js +682 -0
- package/src/precision/lsp-overlay.js +872 -0
- package/src/precision/symbol-query.js +137 -0
- package/src/precision/typescript-lsp-provider.js +682 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Grep sees text. Weavatrix sees structure. It builds a dependency graph of any local repository —
|
|
6
6
|
files, symbols, and the imports/calls/inheritance connecting them — and serves it to Claude Code,
|
|
7
7
|
Codex, or any MCP client: change impact, transitive dependents, health audit, clone detection,
|
|
8
|
-
coverage mapping. **
|
|
8
|
+
coverage mapping. **34 tools available; 31 enabled by the default offline profile. Local-first: with
|
|
9
9
|
the defaults, no repository data leaves your machine.**
|
|
10
10
|
|
|
11
11
|
- Website: [weavatrix.com](https://weavatrix.com)
|
|
@@ -21,8 +21,9 @@ answers grep can't produce:
|
|
|
21
21
|
untracked included), maps the changed files and symbols onto the graph, and lists everything that
|
|
22
22
|
depends on them — with test coverage attached, so the **untested part of the blast radius** stands
|
|
23
23
|
out before you ship.
|
|
24
|
-
- *"Who calls this function?"* → `
|
|
25
|
-
|
|
24
|
+
- *"Who calls this function?"* → `inspect_symbol` returns exact bounded TS/JS occurrences plus
|
|
25
|
+
source context; `get_dependents` walks the symbol-level reverse graph transitively. Opt into
|
|
26
|
+
`include_container_importers:true` only when a broader module-import radius is intended.
|
|
26
27
|
- *"Did my refactor actually decouple anything?"* → `graph_diff base_ref=HEAD~1` builds an immutable
|
|
27
28
|
baseline graph without checking it out, then reports the structural delta: new module
|
|
28
29
|
dependencies, broken or introduced import cycles, and symbols that lost their last caller.
|
|
@@ -111,25 +112,42 @@ An agent skill with recipes ships in [skill/SKILL.md](skill/SKILL.md) — instal
|
|
|
111
112
|
compile-only edges (Rust module/use, Java imports) are reported separately where that distinction
|
|
112
113
|
changes the result.
|
|
113
114
|
|
|
114
|
-
Every current edge
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
`
|
|
115
|
+
Every current edge carries versioned provenance. The parser emits `EXTRACTED`, `RESOLVED`, and
|
|
116
|
+
`INFERRED`; the built-in bounded TypeScript/JavaScript precision overlay upgrades only references
|
|
117
|
+
confirmed by its bundled `typescript-language-server` + TypeScript runtime to `EXACT_LSP`.
|
|
118
|
+
`CONFLICT` means evidence disagrees. `graph_stats` reports the provenance breakdown and the semantic
|
|
119
|
+
provider's `COMPLETE`, `PARTIAL`, `UNAVAILABLE`, or `OFF` state; `OFF` means precision was explicitly
|
|
120
|
+
disabled and only static evidence is active. Java and Rust language-server providers are not bundled
|
|
121
|
+
in 0.2.5: their edges never become `EXACT_LSP`, even when a mixed repository reports a complete
|
|
122
|
+
TypeScript/JavaScript overlay.
|
|
123
|
+
|
|
124
|
+
The bounded JS/TS provider is enabled by default for new graphs. Set `WEAVATRIX_PRECISION=off`
|
|
125
|
+
before starting the MCP server for parser-only operation from the first build, or pass
|
|
126
|
+
`precision:"off"` to `rebuild_graph` / `open_repo`. The MCPB installer exposes the same `lsp` / `off`
|
|
127
|
+
choice as **TypeScript/JavaScript semantic precision**.
|
|
118
128
|
|
|
119
129
|
**search / source** — `search_code` (ripgrep-backed, pure-Node fallback), `read_source` (a
|
|
120
|
-
symbol's actual code in one hop), `
|
|
130
|
+
symbol's actual code in one hop), `inspect_symbol` (one exact bounded TS/JS reference query,
|
|
131
|
+
logical containers, impact and source excerpts), `list_endpoints` (HTTP route inventory:
|
|
121
132
|
Express/Fastify/Nest/Flask/FastAPI/Go mux/Rust axum and actix-web …)
|
|
122
133
|
|
|
123
|
-
**health** — `find_dead_code` (bounded review queue for statically unreferenced files,
|
|
124
|
-
methods, and symbols, with confidence/evidence and explicit public/framework/dynamic caveats),
|
|
125
|
-
`run_audit` (unused files/exports/dependencies
|
|
134
|
+
**health** — `find_dead_code` (bounded review queue for statically unreferenced and test-only files,
|
|
135
|
+
functions, methods, and symbols, with confidence/evidence and explicit public/framework/dynamic caveats),
|
|
136
|
+
`run_audit` (unused files/exports/dependencies with an explicit manifest-check summary,
|
|
137
|
+
missing npm/Go/Python deps, runtime
|
|
126
138
|
cycles, type-only/compile-only coupling, orphans, boundary rules, offline OSV vulnerabilities + typosquat +
|
|
127
139
|
lockfile drift; accepts an immutable `base_ref`, `changed_files`, and `debt: new|existing|all` for
|
|
128
140
|
review-scoped results), `find_duplicates` (MOSS winnowing over method bodies — catches copy-paste even
|
|
129
|
-
after renames), `coverage_map` (existing coverage reports mapped onto the graph; untested hotspots
|
|
130
|
-
ranked by connectivity — tests are never executed), `
|
|
141
|
+
after renames and can inspect strict small clones down to 12 tokens), `coverage_map` (existing coverage reports mapped onto the graph; untested hotspots
|
|
142
|
+
ranked by connectivity — tests are never executed), `hot_path_review` (bounded local-cost evidence
|
|
143
|
+
with separate graph/test risk), `verify_architecture`,
|
|
131
144
|
`explain_architecture_violation`, `propose_architecture_exception`
|
|
132
145
|
|
|
146
|
+
`hot_path_review` ranks production symbols using parser-derived local time, memory, cyclomatic and
|
|
147
|
+
call-site facts plus exact inside-loop allocation, copy, scan, sort and recursion evidence. Graph
|
|
148
|
+
fan-in/fan-out and measured coverage (or explicitly labelled static test reachability) remain
|
|
149
|
+
separate from local syntax cost. The ranking is not profiler data or interprocedural Big-O.
|
|
150
|
+
|
|
133
151
|
**build** — `rebuild_graph` (reports the structural delta, keeps the prior state as
|
|
134
152
|
`graph.prev.json`)
|
|
135
153
|
|
|
@@ -159,8 +177,10 @@ in the per-user cache and never need to be committed to Git.
|
|
|
159
177
|
|
|
160
178
|
`query_graph` accepts optional `seed_files` when an architectural question must start from exact
|
|
161
179
|
entry points. Resolved explicit seeds are exclusive by default; set `augment_seeds:true` only when
|
|
162
|
-
|
|
163
|
-
|
|
180
|
+
question-derived seeds are also wanted. Broad bootstrap/tool-execution/routing questions now rank
|
|
181
|
+
conventional executables and graph-declared production entry points ahead of site, documentation, benchmark
|
|
182
|
+
and fixture matches. Broad ranking remains orientation evidence; use `seed_files` when the intended
|
|
183
|
+
entry point is already known.
|
|
164
184
|
|
|
165
185
|
**advisories** *(network, explicit opt-in)* — `refresh_advisories`
|
|
166
186
|
|
|
@@ -173,6 +193,57 @@ disclosed instead of silently guessed; and the server **hot-reloads its watched
|
|
|
173
193
|
modules and catalog** when those files change — other MCP helpers and analysis engines require a
|
|
174
194
|
reconnect.
|
|
175
195
|
|
|
196
|
+
### 0.2.5 exact-symbol and graph-fidelity patch
|
|
197
|
+
|
|
198
|
+
- New `inspect_symbol` spends a bounded LSP query on one requested TS/JS declaration, groups exact
|
|
199
|
+
occurrences by logical container, and returns definition/caller source context. Its separate LRU
|
|
200
|
+
cache is revision/config fingerprinted and never replaces the broad precision overlay.
|
|
201
|
+
- Default-object service facades resolve to their underlying helpers, while `get_dependents` no
|
|
202
|
+
longer silently expands every symbol query to all importers of its file.
|
|
203
|
+
- Python receiver-aware method dispatch and unambiguous wildcard imports distinguish same-named
|
|
204
|
+
methods across classes without promoting ambiguous dynamic evidence.
|
|
205
|
+
- `run_audit` makes unused-dependency checking visible even when clean; dead-code review identifies
|
|
206
|
+
production symbols referenced only by tests; small-clone scanning can be lowered to 12 tokens with
|
|
207
|
+
stricter evidence rules.
|
|
208
|
+
- `hot_path_review` adds a bounded offline performance-review queue with line-addressable local cost
|
|
209
|
+
evidence, separate graph risk, and honest measured/unavailable coverage states.
|
|
210
|
+
|
|
211
|
+
Full patch notes: [docs/releases/v0.2.5.md](docs/releases/v0.2.5.md).
|
|
212
|
+
|
|
213
|
+
### 0.2.4 graph-mode correctness and semantic precision patch
|
|
214
|
+
|
|
215
|
+
- `graph_diff base_ref=...` builds the immutable baseline in the current graph mode, so a
|
|
216
|
+
`no-tests` graph is never compared with a hidden `full` universe. Previous-rebuild snapshots with
|
|
217
|
+
mismatched modes are rejected instead of reporting false removals or cycle drift.
|
|
218
|
+
- `trace_api_contract` preserves each registered repository's build mode during refresh and exposes
|
|
219
|
+
that mode in `graphReconciliation`.
|
|
220
|
+
- `graph_stats` and `open_repo` expose the canonical graph path and build mode; an explicit mode
|
|
221
|
+
mismatch with `build:false` fails closed instead of silently retargeting.
|
|
222
|
+
- `module_map` is production-only by default and can opt back into classified tests, fixtures,
|
|
223
|
+
benchmarks, generated output and docs with `include_non_product:true`.
|
|
224
|
+
- A bundled, read-only TypeScript/JavaScript language server validates a bounded set of semantic
|
|
225
|
+
references after graph reconciliation. Confirmed edges become `EXACT_LSP`; zero-reference evidence
|
|
226
|
+
can strengthen an internal dead-code candidate only after a successful exact query. Partial,
|
|
227
|
+
unavailable and revision-mismatched overlays stay visible and never become cosmetic exactness.
|
|
228
|
+
The provider is Weavatrix's pinned `typescript-language-server` + TypeScript runtime, never a
|
|
229
|
+
repository binary or `npx` download. Automatic type acquisition is disabled. Its child environment
|
|
230
|
+
is reduced to OS/temp/locale basics with a Node/System path; registry, proxy, cloud, token and
|
|
231
|
+
`NODE_OPTIONS` values are not inherited. TypeScript may still read locally declared project
|
|
232
|
+
configuration, dependencies and type declarations; Weavatrix accepts returned evidence only after
|
|
233
|
+
repository realpath containment. Applicable config chains are audited before provider startup;
|
|
234
|
+
configured language-service plugins and unresolved/outside config are refused, while semantic
|
|
235
|
+
inputs are fingerprinted before cache reuse and rechecked after each run. No source or evidence is
|
|
236
|
+
transmitted, and the provider performs no Weavatrix HTTP request. MCP EOF/SIGTERM drains or
|
|
237
|
+
tree-terminates TLS and tsserver before the stdio process exits.
|
|
238
|
+
- Broad `query_graph` bootstrap and tool-execution ranking now prefers production executables and
|
|
239
|
+
graph-declared entry points over site, docs, benchmark and fixture surfaces. Exact `seed_files` remain
|
|
240
|
+
the deterministic option when the caller already knows the entry point.
|
|
241
|
+
- The bundled skill routes orientation, diff review, cross-repository API tracing and exact-symbol
|
|
242
|
+
work through Weavatrix's own evidence states. Java/Rust exact providers remain `UNAVAILABLE` in
|
|
243
|
+
this release instead of being presented as compiler-confirmed.
|
|
244
|
+
|
|
245
|
+
Full patch notes: [docs/releases/v0.2.4.md](docs/releases/v0.2.4.md).
|
|
246
|
+
|
|
176
247
|
### 0.2.3 real-wrapper patch
|
|
177
248
|
|
|
178
249
|
- Auto-discovery recognizes wrappers that pass a fixed HTTP client method and argument array to a
|
|
@@ -379,9 +450,9 @@ vulnerability findings. This is where each capability comes from and how it is c
|
|
|
379
450
|
| Capability alert | Why it exists | Activation and boundary |
|
|
380
451
|
|---|---|---|
|
|
381
452
|
| 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` |
|
|
382
|
-
| Shell access | Local `git` powers staleness/change impact; `rg` accelerates search; timed-out Windows child
|
|
453
|
+
| 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 | Used only by the corresponding local operation. The semantic provider is package-pinned, disables automatic type acquisition, and never invokes a repository binary, script, installer or `npx` |
|
|
383
454
|
| 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` |
|
|
384
|
-
| Environment access | Reads `WEAVATRIX_*` configuration; local
|
|
455
|
+
| 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` |
|
|
385
456
|
| Filesystem access | Reads the active repository, graph, lockfiles and coverage reports; writes derived graphs and advisory/architecture caches | Realpath containment blocks traversal and symlink/junction escapes. The `pinned` profile removes `open_repo`; the default `offline` profile permits only explicit local switching. The optional malware dependency scan may inspect installed dependency caches such as GOPATH |
|
|
386
457
|
| URL strings | Fixed OSV/documentation URLs plus a user-configured sync URL | A URL string causes no request by itself; only the three network-profile tools perform requests |
|
|
387
458
|
|
|
@@ -435,9 +506,9 @@ loader) behind the thin stdio entry `src/mcp-server.mjs`.
|
|
|
435
506
|
[app.weavatrix.com](https://app.weavatrix.com): explicit source-free sync,
|
|
436
507
|
immutable history, Flow/DSM/Map, target architecture and bounded review
|
|
437
508
|
evidence. Hosted use remains optional and owner-authenticated.
|
|
438
|
-
- **Semantic precision bridge**
|
|
439
|
-
|
|
440
|
-
|
|
509
|
+
- **Semantic precision bridge** shipped for TypeScript/JavaScript in 0.2.4: a bounded, revision-bound
|
|
510
|
+
local overlay validates references with the bundled language server while the parser graph remains
|
|
511
|
+
the fallback. Java and Rust providers are not bundled yet and stay explicitly `UNAVAILABLE`.
|
|
441
512
|
- **Git-native architecture history** — bounded tag/ref timelines and branch
|
|
442
513
|
reports built outside the worktree; graph artifacts stay out of Git.
|
|
443
514
|
- **Cross-repository company evidence** — endpoints, events and internal
|
package/SECURITY.md
CHANGED
|
@@ -28,6 +28,24 @@ dependency caches such as GOPATH. `offline` permits repository switching only th
|
|
|
28
28
|
local `open_repo` call; select `pinned` to remove that tool, the global repository listing, and
|
|
29
29
|
cross-repository API tracing, holding a hard startup-repository boundary.
|
|
30
30
|
|
|
31
|
+
The JS/TS precision overlay is enabled by default for new graphs and runs the package-pinned `typescript-language-server` and
|
|
32
|
+
TypeScript runtime as local child processes. It never resolves a repository executable, invokes
|
|
33
|
+
`npx`, runs package scripts, or installs dependencies. Automatic type acquisition is disabled, and
|
|
34
|
+
the provider receives an allowlisted OS/temp/locale environment with a constrained executable path;
|
|
35
|
+
registry, proxy, cloud, token and `NODE_OPTIONS` values are excluded. The provider makes no
|
|
36
|
+
Weavatrix HTTP request and Weavatrix transmits no source or evidence. TypeScript may still read
|
|
37
|
+
locally declared project configuration, dependencies and type declarations; returned evidence is
|
|
38
|
+
accepted only after repository realpath containment. Before spawning the provider, Weavatrix parses
|
|
39
|
+
the applicable repo-contained TypeScript/JavaScript config chains with its bundled TypeScript API and
|
|
40
|
+
refuses semantic mode when a configured language-service plugin, unresolved/outside config, or input
|
|
41
|
+
safety limit is encountered. Config and configured-project inputs are fingerprinted and rechecked
|
|
42
|
+
before cached evidence is used and after each provider run.
|
|
43
|
+
This is an application/process boundary rather than an operating-system
|
|
44
|
+
network sandbox. On stdio EOF, SIGTERM or SIGINT, the MCP server stops new providers, performs a
|
|
45
|
+
bounded graph-work drain, and closes or tree-terminates TLS/tsserver before exiting.
|
|
46
|
+
Set `WEAVATRIX_PRECISION=off` before server startup (or select `off` in the MCPB semantic-precision
|
|
47
|
+
setting) to keep new graphs parser-only from their first build.
|
|
48
|
+
|
|
31
49
|
Network capabilities are split by purpose. `osv` adds only `refresh_advisories`, which sends pinned
|
|
32
50
|
package names and versions to OSV.dev when called. `hosted` / `full` also expose `sync_graph` and
|
|
33
51
|
`pull_architecture_contract`; both require a user-configured endpoint, and contract pull requires
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weavatrix",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
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.",
|
|
6
6
|
"author": "Sergii Ziborov <sergii.ziborov@gmail.com>",
|
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"tree-sitter-wasms": "0.1.13",
|
|
58
|
+
"typescript": "5.9.3",
|
|
59
|
+
"typescript-language-server": "4.4.1",
|
|
58
60
|
"web-tree-sitter": "0.25.10"
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
package/skill/SKILL.md
CHANGED
|
@@ -29,6 +29,29 @@ Profiles use the same npm package and binary:
|
|
|
29
29
|
The legacy `online` capability remains a compatibility alias for `advisories,hosted`; prefer the
|
|
30
30
|
named profiles for new registrations.
|
|
31
31
|
|
|
32
|
+
## Intent router
|
|
33
|
+
|
|
34
|
+
Start from the task, not from the complete tool list:
|
|
35
|
+
|
|
36
|
+
- **Orient in a repository**: `graph_stats` -> verify `Repo`, `Graph`, and `Build mode`; if the target
|
|
37
|
+
is wrong, call `open_repo`, then `graph_stats` again -> `module_map` (production-only by default).
|
|
38
|
+
- **Review a branch/diff**: `change_impact` for changed-file blast radius; add
|
|
39
|
+
`graph_diff base_ref=<merge-base>` for structural drift, then drill into exact candidates with
|
|
40
|
+
`get_dependents` and `read_source`.
|
|
41
|
+
- **Trace an API across repositories**: `list_known_repos` -> `trace_api_contract` with an explicit
|
|
42
|
+
backend and client list; inspect each `graphReconciliation.buildMode` before using the verdict.
|
|
43
|
+
- **Inspect exact symbol references**: start with `graph_stats`, then call `inspect_symbol` with an
|
|
44
|
+
exact node ID (or an unambiguous label). It spends a bounded point query beyond the broad overlay
|
|
45
|
+
cap and returns occurrence containers plus source context. Use `get_dependents` for transitive
|
|
46
|
+
graph impact and `find_dead_code` for bounded zero-reference
|
|
47
|
+
candidates. Treat `PARTIAL`, `UNAVAILABLE`, `OFF`, or zero exact edges as incomplete evidence, not
|
|
48
|
+
a compiler-exact result; `OFF` means the caller explicitly selected static-only mode. Java and Rust
|
|
49
|
+
providers are not bundled in 0.2.5, so their edges never become `EXACT_LSP` even when a mixed
|
|
50
|
+
repository has a complete TypeScript/JavaScript overlay.
|
|
51
|
+
- **Explore an architectural question**: use broad `query_graph` when entry points are unknown; its
|
|
52
|
+
bootstrap/tool-execution ranking prefers production executables over docs, sites and fixtures.
|
|
53
|
+
Pass exact `seed_files` when the intended entry points are already known.
|
|
54
|
+
|
|
32
55
|
## Ground rules
|
|
33
56
|
|
|
34
57
|
- **Evidence, not verdicts**: treat audit, hub, orphan and duplicate output as hypotheses. Confirm a
|
|
@@ -47,11 +70,12 @@ named profiles for new registrations.
|
|
|
47
70
|
`change_impact` and `graph_diff` label the distinction; `god_nodes` ranks unique connectivity and
|
|
48
71
|
reports repeated references separately. Do not schedule a runtime-cycle refactor from
|
|
49
72
|
compile-time-only edges.
|
|
50
|
-
- **Edge provenance**: distinguish how an edge was established from legacy confidence.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
73
|
+
- **Edge provenance**: distinguish how an edge was established from legacy confidence. The parser
|
|
74
|
+
emits `EXTRACTED`, `RESOLVED`, and `INFERRED`; the bundled bounded TypeScript/JavaScript language
|
|
75
|
+
server emits `EXACT_LSP` only for references it confirms. `CONFLICT` means evidence disagrees.
|
|
76
|
+
Treat `INFERRED`, `CONFLICT`, `PARTIAL`, `UNAVAILABLE`, and `OFF` as review signals rather than
|
|
77
|
+
compiler-exact facts; an `UNKNOWN` count or revision-mismatched overlay in `graph_stats` requires
|
|
78
|
+
a rebuild before precision-sensitive work.
|
|
55
79
|
- **Repository universe**: in Git repositories, graph and duplicate scans include tracked plus
|
|
56
80
|
non-ignored untracked files. If an old graph still contains packaged/generated output, rebuild it
|
|
57
81
|
before interpreting the result. Repository-root `.weavatrixignore` applies the same tracked-file
|
|
@@ -60,19 +84,28 @@ named profiles for new registrations.
|
|
|
60
84
|
roots. Dead-code/clone/audit review also suppresses `benchmarks/**` and `**/__temp/**` as classified
|
|
61
85
|
non-production surfaces. A verified production benchmark can opt back in narrowly through
|
|
62
86
|
`.weavatrix.json` `classify.product`, for example `{"classify":{"product":["benchmarks/core/**"]}}`.
|
|
63
|
-
- **Architectural queries**:
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
- **Architectural queries**: broad bootstrap/tool-execution/routing questions rank conventional
|
|
88
|
+
production and graph-declared entry points ahead of classified docs, sites, benchmarks and
|
|
89
|
+
fixtures. Inspect the returned seeds before trusting the traversal. Pass exact repository-relative
|
|
90
|
+
`seed_files` to `query_graph` when the intended entry points are already known.
|
|
66
91
|
- **Coverage**: `coverage_map` reads an existing report. `unavailable` means no supported report was
|
|
67
92
|
found, not 0% coverage; do not rank testing risk from that state.
|
|
68
|
-
- **
|
|
93
|
+
- **Local hot paths**: `hot_path_review` ranks parser-derived local syntax cost and reports graph
|
|
94
|
+
fan-in/fan-out plus test evidence separately. `actualCoverage: NOT_AVAILABLE` is not zero coverage,
|
|
95
|
+
and the score is not profiler data or an interprocedural Big-O proof. Inspect its line evidence and
|
|
96
|
+
measure runtime before scheduling a performance rewrite.
|
|
97
|
+
- **Audit completeness**: read `dependencyReport.status` plus its unused/missing counts, then
|
|
98
|
+
`checks.osv.status` and `checks.malware.status`. A dependency result from a partial graph is not a
|
|
99
|
+
repository-wide clean zero. For OSV, `OK` is
|
|
69
100
|
complete for the recorded dependency fingerprint; `PARTIAL` is incomplete or stale,
|
|
70
101
|
`NOT_CHECKED` has no repository-specific result, and `ERROR` means the local check failed. Treat
|
|
71
102
|
the same non-`OK` states as incomplete for malware scanning. Refresh OSV only when the user has
|
|
72
103
|
authorized selecting the optional `osv` profile (or `advisories` capability) and then
|
|
73
104
|
invoking `refresh_advisories`; enabling the group alone sends nothing.
|
|
74
|
-
- **Offline by design**: scans and graph queries
|
|
75
|
-
|
|
105
|
+
- **Offline by design**: scans and graph queries use local files; the semantic overlay may launch
|
|
106
|
+
Weavatrix's bundled read-only TypeScript language-server child process, but it never runs repository
|
|
107
|
+
scripts or downloads a provider. Coverage tools read existing reports and never run tests. The
|
|
108
|
+
ONLY network-touching tools live in the optional
|
|
76
109
|
`advisories` / `hosted` capabilities and run solely when explicitly called:
|
|
77
110
|
`refresh_advisories` (queries OSV.dev with
|
|
78
111
|
package names + versions so `run_audit` has fresh vulnerability data) and `sync_graph` (derives a
|
|
@@ -92,8 +125,10 @@ named profiles for new registrations.
|
|
|
92
125
|
- **Orient in the configured repo**: `module_map` → `list_communities` → `god_nodes`. Hub ranking
|
|
93
126
|
is production-only by default; use `include_classified:true` only when tests/generated/build
|
|
94
127
|
surfaces are deliberately part of the question.
|
|
95
|
-
- **Refactor safety for one symbol**: `get_dependents` → `coverage_map` (low coverage × many
|
|
128
|
+
- **Refactor safety for one symbol**: `inspect_symbol` → `get_dependents` → `coverage_map` (low coverage × many
|
|
96
129
|
dependents ⇒ write tests first) → `read_source`.
|
|
130
|
+
- **Performance review**: `hot_path_review` → inspect its local evidence with `read_source` → use
|
|
131
|
+
`get_dependents` for change risk → confirm with the repository's profiler/benchmark before editing.
|
|
97
132
|
- **Pre-PR review of your current changes**: `change_impact` (auto merge-base; includes uncommitted
|
|
98
133
|
and untracked work, coverage attached, untested hotspots called out) → drill with `get_dependents`.
|
|
99
134
|
- **Impact of a PR that is NOT checked out**: pass its changed-file list explicitly —
|
|
@@ -105,7 +140,8 @@ named profiles for new registrations.
|
|
|
105
140
|
orphan drift rather than raw edge counts.
|
|
106
141
|
- **Health sweep**: for a branch/PR review prefer
|
|
107
142
|
`run_audit base_ref=<merge-base> debt=new changed_files=[…]`; for repository maintenance use
|
|
108
|
-
`run_audit debt=all`. Then call `find_dead_code`, `find_duplicates
|
|
143
|
+
`run_audit debt=all`. Then call `find_dead_code`, `find_duplicates`, `coverage_map` and
|
|
144
|
+
`hot_path_review`. A changed-file-only audit is
|
|
109
145
|
scope, not proof of new debt. Inspect the source behind shortlisted findings before proposing edits.
|
|
110
146
|
- **Dead-code and dead-method review**: call `find_dead_code` for a bounded production-code queue of
|
|
111
147
|
files, functions, methods and symbols. Narrow with `path` or `kinds=["method"]`; keep the default
|
|
@@ -100,7 +100,16 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
|
|
|
100
100
|
const nodes = graph.nodes || [], links = graph.links || [];
|
|
101
101
|
const ep = (v) => (v && typeof v === "object" ? v.id : v);
|
|
102
102
|
const inbound = new Set();
|
|
103
|
-
|
|
103
|
+
const inboundSources = new Map();
|
|
104
|
+
const nodesById = new Map(nodes.map((node) => [String(node.id), node]));
|
|
105
|
+
for (const l of links) if (!isStructuralRelation(l.relation)) {
|
|
106
|
+
const target = String(ep(l.target));
|
|
107
|
+
const source = String(ep(l.source));
|
|
108
|
+
inbound.add(target);
|
|
109
|
+
const values = inboundSources.get(target) || [];
|
|
110
|
+
values.push(source);
|
|
111
|
+
inboundSources.set(target, values);
|
|
112
|
+
}
|
|
104
113
|
|
|
105
114
|
// whole-repo identifier frequency: a symbol whose name appears MORE than once total (its definition + at least
|
|
106
115
|
// one use, same-file OR cross-file) is referenced. Errs toward "alive" (common-named symbols never flagged).
|
|
@@ -115,6 +124,16 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
|
|
|
115
124
|
(symsByFile.get(n.source_file) || symsByFile.set(n.source_file, []).get(n.source_file)).push(n);
|
|
116
125
|
}
|
|
117
126
|
|
|
127
|
+
const symbolNames = new Set([...symById.values()].map((node) => bareName(node.label)).filter(Boolean));
|
|
128
|
+
const occurrenceFiles = new Map();
|
|
129
|
+
for (const [file, text] of sources) for (const match of String(text || "").matchAll(IDENT_RE)) {
|
|
130
|
+
const name = match[0];
|
|
131
|
+
if (!symbolNames.has(name)) continue;
|
|
132
|
+
const files = occurrenceFiles.get(name) || new Set();
|
|
133
|
+
files.add(file);
|
|
134
|
+
occurrenceFiles.set(name, files);
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
// decorated defs (@app.route/@app.event/@pytest.fixture…) are entered by the framework: trust the
|
|
119
138
|
// builder's flag when present, else walk the source line(s) above the definition (graph-builder graphs).
|
|
120
139
|
const lineCache = new Map();
|
|
@@ -151,6 +170,32 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
|
|
|
151
170
|
}
|
|
152
171
|
const deadSet = new Set(deadSymbols.map((s) => s.id));
|
|
153
172
|
|
|
173
|
+
// A production declaration consumed only by tests is live to the raw graph but dead to production.
|
|
174
|
+
// Keep it in a separate review class: this is useful evidence, never an automatic delete verdict.
|
|
175
|
+
const testOnlySymbols = [];
|
|
176
|
+
for (const n of symById.values()) {
|
|
177
|
+
if (deadSet.has(n.id) || isTestFile(n.source_file) || isDecorated(n)) continue;
|
|
178
|
+
const sourcesForSymbol = inboundSources.get(String(n.id)) || [];
|
|
179
|
+
const sourceFiles = sourcesForSymbol.map((id) => nodesById.get(id)?.source_file || (id.includes("#") ? id.split("#")[0] : id));
|
|
180
|
+
const hasTestInbound = sourceFiles.some((file) => isTestFile(file));
|
|
181
|
+
const hasProductionInbound = sourceFiles.some((file) => file && !isTestFile(file));
|
|
182
|
+
const name = bareName(n.label);
|
|
183
|
+
const occurrenceSet = occurrenceFiles.get(name) || new Set();
|
|
184
|
+
const externalOccurrences = [...occurrenceSet].filter((file) => file !== n.source_file);
|
|
185
|
+
const lexicalTestOnly = externalOccurrences.length > 0 && externalOccurrences.every((file) => isTestFile(file));
|
|
186
|
+
if (hasProductionInbound || (!hasTestInbound && !lexicalTestOnly)) continue;
|
|
187
|
+
testOnlySymbols.push({
|
|
188
|
+
id: n.id,
|
|
189
|
+
file: n.source_file,
|
|
190
|
+
label: n.label,
|
|
191
|
+
test: false,
|
|
192
|
+
reason: "referenced only from test/e2e code; no production consumer was found",
|
|
193
|
+
testConsumerFiles: [...new Set(sourceFiles.filter((file) => file && isTestFile(file)))].sort(),
|
|
194
|
+
evidence: hasTestInbound ? "graph" : "lexical",
|
|
195
|
+
publicApi: n.exported === true || ["public", "protected"].includes(String(n.visibility || "").toLowerCase()),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
154
199
|
const deadFiles = [];
|
|
155
200
|
for (const n of nodes) {
|
|
156
201
|
if (String(n.id).includes("#")) continue; // file nodes only
|
|
@@ -161,9 +206,10 @@ export function computeDead(graph, sources, { entrySet = new Set() } = {}) {
|
|
|
161
206
|
|
|
162
207
|
return {
|
|
163
208
|
deadSymbols,
|
|
209
|
+
testOnlySymbols,
|
|
164
210
|
deadFiles,
|
|
165
211
|
referencedIds: [...symById.values()].filter(isReferenced).map((n) => n.id),
|
|
166
|
-
stats: { symbols: symById.size, deadSymbols: deadSymbols.length, files: nodes.filter((n) => !String(n.id).includes("#")).length, deadFiles: deadFiles.length },
|
|
212
|
+
stats: { symbols: symById.size, deadSymbols: deadSymbols.length, testOnlySymbols: testOnlySymbols.length, files: nodes.filter((n) => !String(n.id).includes("#")).length, deadFiles: deadFiles.length },
|
|
167
213
|
};
|
|
168
214
|
}
|
|
169
215
|
|
|
@@ -45,15 +45,24 @@ function symbolCandidate(item, node, context) {
|
|
|
45
45
|
const source = String(context.sources.get(file) || "");
|
|
46
46
|
const pathInfo = context.classify(file, source);
|
|
47
47
|
const publicSurface = isPublicSurface(node);
|
|
48
|
+
const testOnly = item.testOnly === true;
|
|
48
49
|
const externalEntry = context.entrySet.has(file) || isFrameworkEntryFile(file);
|
|
49
50
|
const framework = context.frameworkByFile.get(file) || null;
|
|
50
51
|
const dynamicFile = context.dynamicTargets.has(file) || DYNAMIC_RE.test(source);
|
|
51
52
|
const reflectionFile = REFLECTION_RE.test(source);
|
|
52
53
|
const kind = kindOf(node);
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
const exactNoReference = context.exactNoReferenceIds.has(String(item.id));
|
|
55
|
+
const internallyScoped = String(node?.visibility || "").toLowerCase() === "private"
|
|
56
|
+
|| (!publicSurface && ["method", "function"].includes(kind));
|
|
57
|
+
// Static absence alone is never high-confidence dead code. High is reserved for a successfully
|
|
58
|
+
// queried semantic declaration whose language server also returned no in-workspace references.
|
|
59
|
+
let confidence = internallyScoped && exactNoReference ? "high" : "medium";
|
|
55
60
|
const caveats = [];
|
|
56
61
|
|
|
62
|
+
if (internallyScoped && !exactNoReference) {
|
|
63
|
+
caveats.push("No complete exact semantic no-reference result is available for this declaration; static absence remains medium confidence.");
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
if (publicSurface) {
|
|
58
67
|
confidence = "low";
|
|
59
68
|
caveats.push("Public/exported APIs can be consumed by downstream packages, interfaces, reflection, dependency injection, templates, or configuration outside this repository.");
|
|
@@ -78,7 +87,7 @@ function symbolCandidate(item, node, context) {
|
|
|
78
87
|
return {
|
|
79
88
|
id: String(item.id),
|
|
80
89
|
kind,
|
|
81
|
-
classification: publicSurface ? `public-${kind}` : kind === "method" ? "internal-method" : kind === "function" ? "internal-function" : "unreferenced-symbol",
|
|
90
|
+
classification: testOnly ? `test-only-${kind}` : publicSurface ? `public-${kind}` : kind === "method" ? "internal-method" : kind === "function" ? "internal-function" : "unreferenced-symbol",
|
|
82
91
|
file,
|
|
83
92
|
line: lineOf(node),
|
|
84
93
|
symbol: bareLabel(node?.label || item.label),
|
|
@@ -88,15 +97,18 @@ function symbolCandidate(item, node, context) {
|
|
|
88
97
|
confidence,
|
|
89
98
|
reason: item.reason,
|
|
90
99
|
evidence: [
|
|
91
|
-
{
|
|
92
|
-
|
|
100
|
+
...(testOnly ? [{kind: item.evidence || "graph", fact: `Only test/e2e consumers were found${item.testConsumerFiles?.length ? `: ${item.testConsumerFiles.join(", ")}` : "."}`}]
|
|
101
|
+
: [{ kind: "graph", fact: "No inbound non-structural graph edge targets this symbol." }, { kind: "source-index", fact: "Its identifier has no second indexed occurrence that establishes a caller." }]),
|
|
102
|
+
...(exactNoReference ? [{kind: "exact-lsp", fact: "The active language server returned no in-workspace references for this exact declaration."}] : []),
|
|
93
103
|
],
|
|
94
104
|
caveats,
|
|
95
105
|
publicApi: publicSurface,
|
|
96
106
|
externallyEnteredFile: externalEntry,
|
|
97
107
|
pathClasses: pathInfo.classes || [],
|
|
98
108
|
matchedPathRule: pathInfo.matchedRule || null,
|
|
99
|
-
reviewAction:
|
|
109
|
+
reviewAction: testOnly
|
|
110
|
+
? "Confirm that no production/config/framework consumer exists; decide whether the declaration is intentional test support or removable with its tests. Never auto-delete."
|
|
111
|
+
: "Confirm with read_source, get_dependents, exact search, framework/config inspection and tests; never auto-delete.",
|
|
100
112
|
};
|
|
101
113
|
}
|
|
102
114
|
|
|
@@ -169,10 +181,11 @@ export function computeDeadCodeReview(graph, sources, options = {}) {
|
|
|
169
181
|
const minConfidence = Object.hasOwn(CONFIDENCE_RANK, options.minConfidence) ? options.minConfidence : "medium";
|
|
170
182
|
const pathPrefix = normalizedPath(options.path || "").replace(/\/+$/, "");
|
|
171
183
|
const requestedKinds = new Set(Array.isArray(options.kinds) && options.kinds.length ? options.kinds : ["file", "function", "method", "symbol"]);
|
|
172
|
-
const
|
|
184
|
+
const exactNoReferenceIds = new Set(graph.precisionNoReferenceSymbols || options.exactNoReferenceIds || []);
|
|
185
|
+
const context = { sources, entrySet, dynamicTargets, frameworkByFile, classify, repoSignals, exactNoReferenceIds };
|
|
173
186
|
const dead = computeDead(graph, sources, { entrySet });
|
|
174
187
|
const nodesById = new Map((graph.nodes || []).map((node) => [String(node.id), node]));
|
|
175
|
-
const rawSymbols = dead.deadSymbols
|
|
188
|
+
const rawSymbols = [...dead.deadSymbols, ...(dead.testOnlySymbols || []).map((item) => ({...item, testOnly: true}))]
|
|
176
189
|
.map((item) => ({ item, node: nodesById.get(String(item.id)) }))
|
|
177
190
|
.filter((entry) => entry.node)
|
|
178
191
|
.map(({ item, node }) => symbolCandidate(item, node, context));
|
|
@@ -226,6 +239,7 @@ export function computeDeadCodeReview(graph, sources, options = {}) {
|
|
|
226
239
|
indexedSymbols: dead.stats.symbols,
|
|
227
240
|
indexedFiles: dead.stats.files,
|
|
228
241
|
rawDeadSymbols: rawSymbols.length,
|
|
242
|
+
rawTestOnlySymbols: (dead.testOnlySymbols || []).length,
|
|
229
243
|
rawDeadFiles: rawFiles.length,
|
|
230
244
|
reviewCandidates: candidates.length,
|
|
231
245
|
byConfidence: {
|
|
@@ -6,7 +6,7 @@ import { createPathClassifier, hasPathClass } from "../path-classification.js";
|
|
|
6
6
|
import { listRepoFiles } from "./internal-audit.collect.js";
|
|
7
7
|
import { stripNonCode, bodyEndLineCount, tokenize, fingerprints, extractLargeStrings } from "./duplicates.tokenize.js";
|
|
8
8
|
|
|
9
|
-
const FLOOR_TOKENS =
|
|
9
|
+
const FLOOR_TOKENS = 12; // absolute safety floor; normal scans still default to 30+ tokens
|
|
10
10
|
const FLOOR_SIM = 0.5; // pairs below this are not reported (UI slider min)
|
|
11
11
|
const MAX_BODY_LINES = 400;
|
|
12
12
|
// File types with no code SYMBOLS to fragment (stylesheets, markup, docs, single-file components). The
|
|
@@ -93,7 +93,11 @@ function pairsForMode(frags, mode) {
|
|
|
93
93
|
const i = Math.floor(key / 1000000), j = key % 1000000;
|
|
94
94
|
const union = eff[i] + eff[j] - count;
|
|
95
95
|
const sim = union > 0 ? count / union : 0;
|
|
96
|
-
|
|
96
|
+
const smallFragment = Math.min(frags[i].n, frags[j].n) < 30;
|
|
97
|
+
const enoughEvidence = smallFragment
|
|
98
|
+
? sim >= 0.95 && count >= 2
|
|
99
|
+
: count >= MIN_SHARED_FP;
|
|
100
|
+
if (sim >= FLOOR_SIM && enoughEvidence) pairs.push([i, j, Math.round(sim * 100)]);
|
|
97
101
|
}
|
|
98
102
|
return pairs;
|
|
99
103
|
}
|
|
@@ -125,6 +129,7 @@ function loadGraph(graphInput) {
|
|
|
125
129
|
|
|
126
130
|
export function computeDuplicates(repoPath, graphJsonPath, opts = {}) {
|
|
127
131
|
const includeStrings = !!opts.includeStrings;
|
|
132
|
+
const scanTokenFloor = Math.max(FLOOR_TOKENS, Math.min(400, Number(opts.minTokens) || 30));
|
|
128
133
|
const graph = loadGraph(graphJsonPath);
|
|
129
134
|
const byFile = symbolRanges(graph);
|
|
130
135
|
// total symbol nodes in the graph — 0 means a file-only graph (built by an older builder before
|
|
@@ -169,7 +174,7 @@ export function computeDuplicates(repoPath, graphJsonPath, opts = {}) {
|
|
|
169
174
|
const end = start + body.length - 1;
|
|
170
175
|
if (end - start < 2) continue;
|
|
171
176
|
const toks = tokenize(stripNonCode(body.join("\n"), py));
|
|
172
|
-
if (toks.strict.length <
|
|
177
|
+
if (toks.strict.length < scanTokenFloor) continue;
|
|
173
178
|
frags.push({
|
|
174
179
|
id: syms[i].id, label: syms[i].label, file, start, end,
|
|
175
180
|
n: toks.strict.length,
|
|
@@ -184,7 +189,7 @@ export function computeDuplicates(repoPath, graphJsonPath, opts = {}) {
|
|
|
184
189
|
const base = file.split("/").pop();
|
|
185
190
|
const pushStr = (startLine, endLine, content) => {
|
|
186
191
|
const toks = tokenize(content);
|
|
187
|
-
if (toks.strict.length <
|
|
192
|
+
if (toks.strict.length < scanTokenFloor) return;
|
|
188
193
|
frags.push({
|
|
189
194
|
id: `${file}#str@${startLine}`, label: `${base}:${startLine} ~${endLine - startLine + 1}-line string`,
|
|
190
195
|
file, start: startLine, end: endLine, n: toks.strict.length, ...classificationFields(pathInfo), kind: "string",
|
|
@@ -224,7 +229,7 @@ export function computeDuplicates(repoPath, graphJsonPath, opts = {}) {
|
|
|
224
229
|
const end = Math.min(start + WINDOW_LINES - 1, lines.length);
|
|
225
230
|
if (end - start < 2) continue;
|
|
226
231
|
const toks = tokenize(stripNonCode(lines.slice(start - 1, end).join("\n"), false));
|
|
227
|
-
if (toks.strict.length <
|
|
232
|
+
if (toks.strict.length < scanTokenFloor) continue;
|
|
228
233
|
frags.push({
|
|
229
234
|
id: `${file}#win@${start}`, label: `${file.split("/").pop()}:${start}-${end}`, file, start, end,
|
|
230
235
|
n: toks.strict.length, ...classificationFields(pathInfo),
|
|
@@ -250,6 +255,6 @@ export function computeDuplicates(repoPath, graphJsonPath, opts = {}) {
|
|
|
250
255
|
},
|
|
251
256
|
nameTwinsTruncated: !!nameTwins && nameTwins.length >= 200,
|
|
252
257
|
},
|
|
253
|
-
floors: { tokens: FLOOR_TOKENS, sim: FLOOR_SIM * 100 },
|
|
258
|
+
floors: { tokens: scanTokenFloor, absoluteTokens: FLOOR_TOKENS, sim: FLOOR_SIM * 100 },
|
|
254
259
|
};
|
|
255
260
|
}
|
|
@@ -7,6 +7,7 @@ import {tmpdir} from 'node:os'
|
|
|
7
7
|
import {spawnSync} from 'node:child_process'
|
|
8
8
|
import {childProcessEnv} from '../child-env.js'
|
|
9
9
|
import {buildInternalGraph} from '../graph/internal-builder.js'
|
|
10
|
+
import {filterGraphForMode} from '../graph/graph-filter.js'
|
|
10
11
|
|
|
11
12
|
const SAFE_REF = /^(?!-)[A-Za-z0-9][A-Za-z0-9._\/@{}+~^-]{0,199}$/
|
|
12
13
|
|
|
@@ -68,7 +69,14 @@ export async function withGitRefCheckout(repoRoot, requestedRef, operation) {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
export async function buildGraphAtGitRef(repoRoot, requestedRef) {
|
|
72
|
-
const
|
|
72
|
+
export async function buildGraphAtGitRef(repoRoot, requestedRef, {mode = 'full'} = {}) {
|
|
73
|
+
const buildMode = ['full', 'no-tests', 'tests-only'].includes(mode) ? mode : 'full'
|
|
74
|
+
const result = await withGitRefCheckout(repoRoot, requestedRef, async (checkout) => {
|
|
75
|
+
const fullGraph = await buildInternalGraph(checkout)
|
|
76
|
+
return {
|
|
77
|
+
...filterGraphForMode(fullGraph, buildMode, {repoRoot: checkout}),
|
|
78
|
+
graphBuildMode: buildMode,
|
|
79
|
+
}
|
|
80
|
+
})
|
|
73
81
|
return result.ok ? {...result, graph: result.value} : result
|
|
74
82
|
}
|