weavatrix 0.2.3 → 0.2.4
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 +56 -11
- package/SECURITY.md +18 -0
- package/package.json +3 -1
- package/skill/SKILL.md +36 -10
- package/src/analysis/dead-code-review.js +13 -3
- package/src/analysis/git-ref-graph.js +10 -2
- 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 +2 -0
- package/src/graph/freshness-probe.js +2 -2
- package/src/graph/incremental-refresh.js +1 -1
- package/src/graph/internal-builder.build.js +33 -4
- package/src/mcp/catalog.mjs +4 -4
- package/src/mcp/graph-context.mjs +143 -14
- 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 +46 -6
- package/src/mcp/tools-impact.mjs +21 -3
- 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 +847 -0
- package/src/precision/typescript-lsp-provider.js +682 -0
package/README.md
CHANGED
|
@@ -111,10 +111,19 @@ An agent skill with recipes ships in [skill/SKILL.md](skill/SKILL.md) — instal
|
|
|
111
111
|
compile-only edges (Rust module/use, Java imports) are reported separately where that distinction
|
|
112
112
|
changes the result.
|
|
113
113
|
|
|
114
|
-
Every current edge
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
`
|
|
114
|
+
Every current edge carries versioned provenance. The parser emits `EXTRACTED`, `RESOLVED`, and
|
|
115
|
+
`INFERRED`; the built-in bounded TypeScript/JavaScript precision overlay upgrades only references
|
|
116
|
+
confirmed by its bundled `typescript-language-server` + TypeScript runtime to `EXACT_LSP`.
|
|
117
|
+
`CONFLICT` means evidence disagrees. `graph_stats` reports the provenance breakdown and the semantic
|
|
118
|
+
provider's `COMPLETE`, `PARTIAL`, `UNAVAILABLE`, or `OFF` state; `OFF` means precision was explicitly
|
|
119
|
+
disabled and only static evidence is active. Java and Rust language-server providers are not bundled
|
|
120
|
+
in 0.2.4: their edges never become `EXACT_LSP`, even when a mixed repository reports a complete
|
|
121
|
+
TypeScript/JavaScript overlay.
|
|
122
|
+
|
|
123
|
+
The bounded JS/TS provider is enabled by default for new graphs. Set `WEAVATRIX_PRECISION=off`
|
|
124
|
+
before starting the MCP server for parser-only operation from the first build, or pass
|
|
125
|
+
`precision:"off"` to `rebuild_graph` / `open_repo`. The MCPB installer exposes the same `lsp` / `off`
|
|
126
|
+
choice as **TypeScript/JavaScript semantic precision**.
|
|
118
127
|
|
|
119
128
|
**search / source** — `search_code` (ripgrep-backed, pure-Node fallback), `read_source` (a
|
|
120
129
|
symbol's actual code in one hop), `list_endpoints` (HTTP route inventory:
|
|
@@ -159,8 +168,10 @@ in the per-user cache and never need to be committed to Git.
|
|
|
159
168
|
|
|
160
169
|
`query_graph` accepts optional `seed_files` when an architectural question must start from exact
|
|
161
170
|
entry points. Resolved explicit seeds are exclusive by default; set `augment_seeds:true` only when
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
question-derived seeds are also wanted. Broad bootstrap/tool-execution/routing questions now rank
|
|
172
|
+
conventional executables and graph-declared production entry points ahead of site, documentation, benchmark
|
|
173
|
+
and fixture matches. Broad ranking remains orientation evidence; use `seed_files` when the intended
|
|
174
|
+
entry point is already known.
|
|
164
175
|
|
|
165
176
|
**advisories** *(network, explicit opt-in)* — `refresh_advisories`
|
|
166
177
|
|
|
@@ -173,6 +184,40 @@ disclosed instead of silently guessed; and the server **hot-reloads its watched
|
|
|
173
184
|
modules and catalog** when those files change — other MCP helpers and analysis engines require a
|
|
174
185
|
reconnect.
|
|
175
186
|
|
|
187
|
+
### 0.2.4 graph-mode correctness and semantic precision patch
|
|
188
|
+
|
|
189
|
+
- `graph_diff base_ref=...` builds the immutable baseline in the current graph mode, so a
|
|
190
|
+
`no-tests` graph is never compared with a hidden `full` universe. Previous-rebuild snapshots with
|
|
191
|
+
mismatched modes are rejected instead of reporting false removals or cycle drift.
|
|
192
|
+
- `trace_api_contract` preserves each registered repository's build mode during refresh and exposes
|
|
193
|
+
that mode in `graphReconciliation`.
|
|
194
|
+
- `graph_stats` and `open_repo` expose the canonical graph path and build mode; an explicit mode
|
|
195
|
+
mismatch with `build:false` fails closed instead of silently retargeting.
|
|
196
|
+
- `module_map` is production-only by default and can opt back into classified tests, fixtures,
|
|
197
|
+
benchmarks, generated output and docs with `include_non_product:true`.
|
|
198
|
+
- A bundled, read-only TypeScript/JavaScript language server validates a bounded set of semantic
|
|
199
|
+
references after graph reconciliation. Confirmed edges become `EXACT_LSP`; zero-reference evidence
|
|
200
|
+
can strengthen an internal dead-code candidate only after a successful exact query. Partial,
|
|
201
|
+
unavailable and revision-mismatched overlays stay visible and never become cosmetic exactness.
|
|
202
|
+
The provider is Weavatrix's pinned `typescript-language-server` + TypeScript runtime, never a
|
|
203
|
+
repository binary or `npx` download. Automatic type acquisition is disabled. Its child environment
|
|
204
|
+
is reduced to OS/temp/locale basics with a Node/System path; registry, proxy, cloud, token and
|
|
205
|
+
`NODE_OPTIONS` values are not inherited. TypeScript may still read locally declared project
|
|
206
|
+
configuration, dependencies and type declarations; Weavatrix accepts returned evidence only after
|
|
207
|
+
repository realpath containment. Applicable config chains are audited before provider startup;
|
|
208
|
+
configured language-service plugins and unresolved/outside config are refused, while semantic
|
|
209
|
+
inputs are fingerprinted before cache reuse and rechecked after each run. No source or evidence is
|
|
210
|
+
transmitted, and the provider performs no Weavatrix HTTP request. MCP EOF/SIGTERM drains or
|
|
211
|
+
tree-terminates TLS and tsserver before the stdio process exits.
|
|
212
|
+
- Broad `query_graph` bootstrap and tool-execution ranking now prefers production executables and
|
|
213
|
+
graph-declared entry points over site, docs, benchmark and fixture surfaces. Exact `seed_files` remain
|
|
214
|
+
the deterministic option when the caller already knows the entry point.
|
|
215
|
+
- The bundled skill routes orientation, diff review, cross-repository API tracing and exact-symbol
|
|
216
|
+
work through Weavatrix's own evidence states. Java/Rust exact providers remain `UNAVAILABLE` in
|
|
217
|
+
this release instead of being presented as compiler-confirmed.
|
|
218
|
+
|
|
219
|
+
Full patch notes: [docs/releases/v0.2.4.md](docs/releases/v0.2.4.md).
|
|
220
|
+
|
|
176
221
|
### 0.2.3 real-wrapper patch
|
|
177
222
|
|
|
178
223
|
- Auto-discovery recognizes wrappers that pass a fixed HTTP client method and argument array to a
|
|
@@ -379,9 +424,9 @@ vulnerability findings. This is where each capability comes from and how it is c
|
|
|
379
424
|
| Capability alert | Why it exists | Activation and boundary |
|
|
380
425
|
|---|---|---|
|
|
381
426
|
| 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
|
|
427
|
+
| 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
428
|
| 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
|
|
429
|
+
| 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
430
|
| 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
431
|
| 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
432
|
|
|
@@ -435,9 +480,9 @@ loader) behind the thin stdio entry `src/mcp-server.mjs`.
|
|
|
435
480
|
[app.weavatrix.com](https://app.weavatrix.com): explicit source-free sync,
|
|
436
481
|
immutable history, Flow/DSM/Map, target architecture and bounded review
|
|
437
482
|
evidence. Hosted use remains optional and owner-authenticated.
|
|
438
|
-
- **Semantic precision bridge**
|
|
439
|
-
|
|
440
|
-
|
|
483
|
+
- **Semantic precision bridge** shipped for TypeScript/JavaScript in 0.2.4: a bounded, revision-bound
|
|
484
|
+
local overlay validates references with the bundled language server while the parser graph remains
|
|
485
|
+
the fallback. Java and Rust providers are not bundled yet and stay explicitly `UNAVAILABLE`.
|
|
441
486
|
- **Git-native architecture history** — bounded tag/ref timelines and branch
|
|
442
487
|
reports built outside the worktree; graph artifacts stay out of Git.
|
|
443
488
|
- **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.4",
|
|
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,28 @@ 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` and read semantic precision state,
|
|
44
|
+
provider and `EXACT_LSP` count. For TypeScript/JavaScript, use an exact node id with
|
|
45
|
+
`get_dependents` and inspect the edge provenance; use `find_dead_code` for bounded zero-reference
|
|
46
|
+
candidates. Treat `PARTIAL`, `UNAVAILABLE`, `OFF`, or zero exact edges as incomplete evidence, not
|
|
47
|
+
a compiler-exact result; `OFF` means the caller explicitly selected static-only mode. Java and Rust
|
|
48
|
+
providers are not bundled in 0.2.4, so their edges never become `EXACT_LSP` even when a mixed
|
|
49
|
+
repository has a complete TypeScript/JavaScript overlay.
|
|
50
|
+
- **Explore an architectural question**: use broad `query_graph` when entry points are unknown; its
|
|
51
|
+
bootstrap/tool-execution ranking prefers production executables over docs, sites and fixtures.
|
|
52
|
+
Pass exact `seed_files` when the intended entry points are already known.
|
|
53
|
+
|
|
32
54
|
## Ground rules
|
|
33
55
|
|
|
34
56
|
- **Evidence, not verdicts**: treat audit, hub, orphan and duplicate output as hypotheses. Confirm a
|
|
@@ -47,11 +69,12 @@ named profiles for new registrations.
|
|
|
47
69
|
`change_impact` and `graph_diff` label the distinction; `god_nodes` ranks unique connectivity and
|
|
48
70
|
reports repeated references separately. Do not schedule a runtime-cycle refactor from
|
|
49
71
|
compile-time-only edges.
|
|
50
|
-
- **Edge provenance**: distinguish how an edge was established from legacy confidence.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
- **Edge provenance**: distinguish how an edge was established from legacy confidence. The parser
|
|
73
|
+
emits `EXTRACTED`, `RESOLVED`, and `INFERRED`; the bundled bounded TypeScript/JavaScript language
|
|
74
|
+
server emits `EXACT_LSP` only for references it confirms. `CONFLICT` means evidence disagrees.
|
|
75
|
+
Treat `INFERRED`, `CONFLICT`, `PARTIAL`, `UNAVAILABLE`, and `OFF` as review signals rather than
|
|
76
|
+
compiler-exact facts; an `UNKNOWN` count or revision-mismatched overlay in `graph_stats` requires
|
|
77
|
+
a rebuild before precision-sensitive work.
|
|
55
78
|
- **Repository universe**: in Git repositories, graph and duplicate scans include tracked plus
|
|
56
79
|
non-ignored untracked files. If an old graph still contains packaged/generated output, rebuild it
|
|
57
80
|
before interpreting the result. Repository-root `.weavatrixignore` applies the same tracked-file
|
|
@@ -60,9 +83,10 @@ named profiles for new registrations.
|
|
|
60
83
|
roots. Dead-code/clone/audit review also suppresses `benchmarks/**` and `**/__temp/**` as classified
|
|
61
84
|
non-production surfaces. A verified production benchmark can opt back in narrowly through
|
|
62
85
|
`.weavatrix.json` `classify.product`, for example `{"classify":{"product":["benchmarks/core/**"]}}`.
|
|
63
|
-
- **Architectural queries**:
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
- **Architectural queries**: broad bootstrap/tool-execution/routing questions rank conventional
|
|
87
|
+
production and graph-declared entry points ahead of classified docs, sites, benchmarks and
|
|
88
|
+
fixtures. Inspect the returned seeds before trusting the traversal. Pass exact repository-relative
|
|
89
|
+
`seed_files` to `query_graph` when the intended entry points are already known.
|
|
66
90
|
- **Coverage**: `coverage_map` reads an existing report. `unavailable` means no supported report was
|
|
67
91
|
found, not 0% coverage; do not rank testing risk from that state.
|
|
68
92
|
- **Audit completeness**: read `checks.osv.status` and `checks.malware.status`. For OSV, `OK` is
|
|
@@ -71,8 +95,10 @@ named profiles for new registrations.
|
|
|
71
95
|
the same non-`OK` states as incomplete for malware scanning. Refresh OSV only when the user has
|
|
72
96
|
authorized selecting the optional `osv` profile (or `advisories` capability) and then
|
|
73
97
|
invoking `refresh_advisories`; enabling the group alone sends nothing.
|
|
74
|
-
- **Offline by design**: scans and graph queries
|
|
75
|
-
|
|
98
|
+
- **Offline by design**: scans and graph queries use local files; the semantic overlay may launch
|
|
99
|
+
Weavatrix's bundled read-only TypeScript language-server child process, but it never runs repository
|
|
100
|
+
scripts or downloads a provider. Coverage tools read existing reports and never run tests. The
|
|
101
|
+
ONLY network-touching tools live in the optional
|
|
76
102
|
`advisories` / `hosted` capabilities and run solely when explicitly called:
|
|
77
103
|
`refresh_advisories` (queries OSV.dev with
|
|
78
104
|
package names + versions so `run_audit` has fresh vulnerability data) and `sync_graph` (derives a
|
|
@@ -50,10 +50,18 @@ function symbolCandidate(item, node, context) {
|
|
|
50
50
|
const dynamicFile = context.dynamicTargets.has(file) || DYNAMIC_RE.test(source);
|
|
51
51
|
const reflectionFile = REFLECTION_RE.test(source);
|
|
52
52
|
const kind = kindOf(node);
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const exactNoReference = context.exactNoReferenceIds.has(String(item.id));
|
|
54
|
+
const internallyScoped = String(node?.visibility || "").toLowerCase() === "private"
|
|
55
|
+
|| (!publicSurface && ["method", "function"].includes(kind));
|
|
56
|
+
// Static absence alone is never high-confidence dead code. High is reserved for a successfully
|
|
57
|
+
// queried semantic declaration whose language server also returned no in-workspace references.
|
|
58
|
+
let confidence = internallyScoped && exactNoReference ? "high" : "medium";
|
|
55
59
|
const caveats = [];
|
|
56
60
|
|
|
61
|
+
if (internallyScoped && !exactNoReference) {
|
|
62
|
+
caveats.push("No complete exact semantic no-reference result is available for this declaration; static absence remains medium confidence.");
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
if (publicSurface) {
|
|
58
66
|
confidence = "low";
|
|
59
67
|
caveats.push("Public/exported APIs can be consumed by downstream packages, interfaces, reflection, dependency injection, templates, or configuration outside this repository.");
|
|
@@ -90,6 +98,7 @@ function symbolCandidate(item, node, context) {
|
|
|
90
98
|
evidence: [
|
|
91
99
|
{ kind: "graph", fact: "No inbound non-structural graph edge targets this symbol." },
|
|
92
100
|
{ kind: "source-index", fact: "Its identifier has no second indexed occurrence that establishes a caller." },
|
|
101
|
+
...(exactNoReference ? [{kind: "exact-lsp", fact: "The active language server returned no in-workspace references for this exact declaration."}] : []),
|
|
93
102
|
],
|
|
94
103
|
caveats,
|
|
95
104
|
publicApi: publicSurface,
|
|
@@ -169,7 +178,8 @@ export function computeDeadCodeReview(graph, sources, options = {}) {
|
|
|
169
178
|
const minConfidence = Object.hasOwn(CONFIDENCE_RANK, options.minConfidence) ? options.minConfidence : "medium";
|
|
170
179
|
const pathPrefix = normalizedPath(options.path || "").replace(/\/+$/, "");
|
|
171
180
|
const requestedKinds = new Set(Array.isArray(options.kinds) && options.kinds.length ? options.kinds : ["file", "function", "method", "symbol"]);
|
|
172
|
-
const
|
|
181
|
+
const exactNoReferenceIds = new Set(graph.precisionNoReferenceSymbols || options.exactNoReferenceIds || []);
|
|
182
|
+
const context = { sources, entrySet, dynamicTargets, frameworkByFile, classify, repoSignals, exactNoReferenceIds };
|
|
173
183
|
const dead = computeDead(graph, sources, { entrySet });
|
|
174
184
|
const nodesById = new Map((graph.nodes || []).map((node) => [String(node.id), node]));
|
|
175
185
|
const rawSymbols = dead.deadSymbols
|
|
@@ -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
|
}
|
package/src/build-graph.js
CHANGED
|
@@ -14,7 +14,8 @@ import { graphHomeDir, graphOutDirForRepo, graphStorageKey, repoTopFolders, summ
|
|
|
14
14
|
import { registerRepository } from "./graph/repo-registry.js";
|
|
15
15
|
import { refreshGraphIncrementally, snapshotRepository } from "./graph/incremental-refresh.js";
|
|
16
16
|
import { atomicWriteFileSync, withFileLock } from "./graph/file-lock.js";
|
|
17
|
-
import { repositoryFreshnessProbe, stampRepositoryFreshness } from "./graph/freshness-probe.js";
|
|
17
|
+
import { graphSchemaIsCurrent, repositoryFreshnessProbe, stampRepositoryFreshness } from "./graph/freshness-probe.js";
|
|
18
|
+
import { buildLspPrecisionOverlay, invalidatePrecisionOverlay, precisionSummary } from "./precision/lsp-overlay.js";
|
|
18
19
|
|
|
19
20
|
// The worker path deadlocks web-tree-sitter's WASM in Electron's worker threads (fine in plain Node) — off
|
|
20
21
|
// until that's Electron-safe. In-process + event-loop yielding keeps the window responsive without it.
|
|
@@ -26,6 +27,10 @@ const USE_BUILD_WORKER = false;
|
|
|
26
27
|
// freeze the whole app on the main thread). 4 min is generous for very large repos.
|
|
27
28
|
const BUILD_WORKER_TIMEOUT_MS = 4 * 60 * 1000;
|
|
28
29
|
|
|
30
|
+
export function defaultPrecisionMode(env = process.env) {
|
|
31
|
+
return env?.WEAVATRIX_PRECISION === "off" ? "off" : "lsp";
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
function buildGraphInWorker(payload) {
|
|
30
35
|
return new Promise((resolve, reject) => {
|
|
31
36
|
let worker;
|
|
@@ -56,7 +61,7 @@ function buildGraphInWorker(payload) {
|
|
|
56
61
|
});
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
async function buildAndWriteInProcess(repoPath, { mode, scope, graphJson, central }) {
|
|
64
|
+
async function buildAndWriteInProcess(repoPath, { mode, scope, precision, graphJson, central }) {
|
|
60
65
|
const { buildInternalGraph } = await import("./graph/internal-builder.js");
|
|
61
66
|
// Capture the cheap Git state on both sides of the authoritative build. Only an unchanged pair is
|
|
62
67
|
// safe to persist: if the working tree moves while parsing, the next process must take the slow path.
|
|
@@ -66,7 +71,7 @@ async function buildAndWriteInProcess(repoPath, { mode, scope, graphJson, centra
|
|
|
66
71
|
if (mode === "full" && !scope && existsSync(graphJson)) {
|
|
67
72
|
try {
|
|
68
73
|
const existing = JSON.parse(readFileSync(graphJson, "utf8"));
|
|
69
|
-
if (existing.graphBuildMode === "full" && !existing.graphBuildScope) {
|
|
74
|
+
if (existing.graphBuildMode === "full" && !existing.graphBuildScope && graphSchemaIsCurrent(existing)) {
|
|
70
75
|
refresh = await refreshGraphIncrementally(repoPath, existing, { buildGraph: buildInternalGraph });
|
|
71
76
|
graph = refresh.graph;
|
|
72
77
|
}
|
|
@@ -75,7 +80,7 @@ async function buildAndWriteInProcess(repoPath, { mode, scope, graphJson, centra
|
|
|
75
80
|
if (!graph && mode !== "full" && !scope && existsSync(graphJson)) {
|
|
76
81
|
try {
|
|
77
82
|
const existing = JSON.parse(readFileSync(graphJson, "utf8"));
|
|
78
|
-
if (existing.graphBuildMode === mode && existing.graphRevision) {
|
|
83
|
+
if (existing.graphBuildMode === mode && existing.graphRevision && graphSchemaIsCurrent(existing)) {
|
|
79
84
|
const snapshot = snapshotRepository(repoPath);
|
|
80
85
|
if (snapshot.revision === existing.graphRevision) {
|
|
81
86
|
graph = existing;
|
|
@@ -93,6 +98,9 @@ async function buildAndWriteInProcess(repoPath, { mode, scope, graphJson, centra
|
|
|
93
98
|
if (scope) graph = filterGraphByScope(graph, scope);
|
|
94
99
|
graph.graphBuildMode = mode;
|
|
95
100
|
graph.graphBuildScope = scope || "";
|
|
101
|
+
const requestedPrecision = precision === "off" ? "off" : "lsp";
|
|
102
|
+
const precisionModeChanged = graph.graphPrecisionMode !== requestedPrecision;
|
|
103
|
+
graph.graphPrecisionMode = requestedPrecision;
|
|
96
104
|
const probeAfter = scope ? null : repositoryFreshnessProbe(repoPath);
|
|
97
105
|
const stableProbe = probeBefore && probeAfter === probeBefore ? probeAfter : null;
|
|
98
106
|
const freshnessMetadataChanged = stampRepositoryFreshness(graph, stableProbe, mode);
|
|
@@ -100,20 +108,50 @@ async function buildAndWriteInProcess(repoPath, { mode, scope, graphJson, centra
|
|
|
100
108
|
// multi-megabyte graph merely to answer that nothing changed (or manufacture a newer mtime).
|
|
101
109
|
// A one-time metadata-only write is intentional for legacy graphs so the next MCP process can use
|
|
102
110
|
// the persisted probe instead of repeating a full repository snapshot.
|
|
103
|
-
if (refresh?.kind !== "none" || freshnessMetadataChanged) {
|
|
111
|
+
if (refresh?.kind !== "none" || freshnessMetadataChanged || precisionModeChanged) {
|
|
104
112
|
mkdirSync(central, { recursive: true });
|
|
105
113
|
atomicWriteFileSync(graphJson, JSON.stringify(graph), "utf8");
|
|
106
114
|
}
|
|
115
|
+
mkdirSync(central, { recursive: true });
|
|
116
|
+
let precisionOverlay = await buildLspPrecisionOverlay({
|
|
117
|
+
repoRoot: repoPath,
|
|
118
|
+
graph,
|
|
119
|
+
graphPath: graphJson,
|
|
120
|
+
mode: requestedPrecision,
|
|
121
|
+
});
|
|
122
|
+
if (requestedPrecision === "lsp") {
|
|
123
|
+
try {
|
|
124
|
+
// LSP deliberately runs after graph serialization and can outlive the initial snapshot. A
|
|
125
|
+
// complete second content snapshot catches source/config add-delete races (including Git
|
|
126
|
+
// assume-unchanged paths) that a status-only token cannot prove away.
|
|
127
|
+
if (snapshotRepository(repoPath).revision !== graph.graphRevision) {
|
|
128
|
+
precisionOverlay = invalidatePrecisionOverlay(graphJson, graph);
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
precisionOverlay = invalidatePrecisionOverlay(
|
|
132
|
+
graphJson,
|
|
133
|
+
graph,
|
|
134
|
+
"repository freshness could not be verified after semantic precision",
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
107
138
|
return {
|
|
108
139
|
nodes: graph.nodes.length,
|
|
109
140
|
links: graph.links.length,
|
|
110
141
|
communities: summarizeCommunities(graphJson),
|
|
111
142
|
hotspots: summarizeHotspots(graphJson),
|
|
112
143
|
refresh,
|
|
144
|
+
precision: precisionSummary(precisionOverlay),
|
|
113
145
|
};
|
|
114
146
|
}
|
|
115
147
|
|
|
116
|
-
export async function buildGraphForRepo(repoPath, {
|
|
148
|
+
export async function buildGraphForRepo(repoPath, {
|
|
149
|
+
mode = "full",
|
|
150
|
+
scope = "",
|
|
151
|
+
precision = defaultPrecisionMode(),
|
|
152
|
+
outDir,
|
|
153
|
+
graphHome,
|
|
154
|
+
} = {}) {
|
|
117
155
|
if (!existsSync(repoPath)) return { ok: false, error: "Repo path not found", builder: "internal" };
|
|
118
156
|
const registryHome = graphHome || graphHomeDir();
|
|
119
157
|
const canonicalDir = graphHome ? join(registryHome, graphStorageKey(repoPath)) : graphOutDirForRepo(repoPath);
|
|
@@ -126,11 +164,11 @@ export async function buildGraphForRepo(repoPath, { mode = "full", scope = "", o
|
|
|
126
164
|
// between file chunks, so the main-thread parse no longer freezes the window — the reason the worker was
|
|
127
165
|
// introduced. buildGraphInWorker/USE_BUILD_WORKER are kept for a future Electron-safe re-enable.
|
|
128
166
|
const build = () => USE_BUILD_WORKER
|
|
129
|
-
? buildGraphInWorker({ repoPath, mode, scope, graphJson, central }).catch((e) => {
|
|
130
|
-
if (e && e.workerStartFailed) return buildAndWriteInProcess(repoPath, { mode, scope, graphJson, central });
|
|
167
|
+
? buildGraphInWorker({ repoPath, mode, scope, precision, graphJson, central }).catch((e) => {
|
|
168
|
+
if (e && e.workerStartFailed) return buildAndWriteInProcess(repoPath, { mode, scope, precision, graphJson, central });
|
|
131
169
|
throw e;
|
|
132
170
|
})
|
|
133
|
-
: buildAndWriteInProcess(repoPath, { mode, scope, graphJson, central });
|
|
171
|
+
: buildAndWriteInProcess(repoPath, { mode, scope, precision, graphJson, central });
|
|
134
172
|
// Canonical graphs are shared by all local MCP clients. Serialize the complete read/refresh/write
|
|
135
173
|
// transaction so an older process cannot overwrite a newer incremental result.
|
|
136
174
|
const canonical = !scope && resolve(central) === resolve(canonicalDir);
|
|
@@ -154,7 +192,8 @@ export async function buildGraphForRepo(repoPath, { mode = "full", scope = "", o
|
|
|
154
192
|
communities: built.communities,
|
|
155
193
|
hotspots: built.hotspots,
|
|
156
194
|
refresh: built.refresh,
|
|
157
|
-
|
|
195
|
+
precision: built.precision,
|
|
196
|
+
log: `built-in builder: ${built.nodes} nodes, ${built.links} static links (${built.refresh?.kind || "full"}: ${built.refresh?.reason || "build"}); semantic precision ${built.precision?.state || "UNAVAILABLE"}, ${built.precision?.verifiedEdges || 0} EXACT_LSP edge(s)`
|
|
158
197
|
};
|
|
159
198
|
} catch (error) {
|
|
160
199
|
return { ok: false, error: `graph build failed: ${error.message}`, builder: "internal" };
|
|
@@ -8,20 +8,40 @@ import { parentPort, workerData } from "node:worker_threads";
|
|
|
8
8
|
import { buildInternalGraph } from "./internal-builder.js";
|
|
9
9
|
import { filterGraphForMode, filterGraphByScope, summarizeCommunities, summarizeHotspots } from "./layout.js";
|
|
10
10
|
import { atomicWriteFileSync } from "./file-lock.js";
|
|
11
|
+
import { snapshotRepository } from "./incremental-refresh.js";
|
|
12
|
+
import { buildLspPrecisionOverlay, invalidatePrecisionOverlay, precisionSummary } from "../precision/lsp-overlay.js";
|
|
11
13
|
|
|
12
14
|
(async () => {
|
|
13
|
-
const { repoPath, mode, scope, graphJson, central } = workerData || {};
|
|
15
|
+
const { repoPath, mode, scope, precision, graphJson, central } = workerData || {};
|
|
14
16
|
try {
|
|
15
17
|
let graph = await buildInternalGraph(repoPath);
|
|
16
18
|
if (mode === "no-tests" || mode === "tests-only") graph = filterGraphForMode(graph, mode, { repoRoot: repoPath });
|
|
17
19
|
if (scope) graph = filterGraphByScope(graph, scope);
|
|
20
|
+
graph.graphBuildMode = mode;
|
|
21
|
+
graph.graphBuildScope = scope || "";
|
|
22
|
+
graph.graphPrecisionMode = precision === "off" ? "off" : "lsp";
|
|
18
23
|
atomicWriteFileSync(graphJson, JSON.stringify(graph), "utf8");
|
|
24
|
+
let overlay = await buildLspPrecisionOverlay({repoRoot: repoPath, graph, graphPath: graphJson, mode: graph.graphPrecisionMode});
|
|
25
|
+
if (graph.graphPrecisionMode === "lsp") {
|
|
26
|
+
try {
|
|
27
|
+
if (snapshotRepository(repoPath).revision !== graph.graphRevision) {
|
|
28
|
+
overlay = invalidatePrecisionOverlay(graphJson, graph);
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
overlay = invalidatePrecisionOverlay(
|
|
32
|
+
graphJson,
|
|
33
|
+
graph,
|
|
34
|
+
"repository freshness could not be verified after semantic precision",
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
19
38
|
parentPort.postMessage({
|
|
20
39
|
ok: true,
|
|
21
40
|
nodes: graph.nodes.length,
|
|
22
41
|
links: graph.links.length,
|
|
23
42
|
communities: summarizeCommunities(graphJson),
|
|
24
43
|
hotspots: summarizeHotspots(graphJson),
|
|
44
|
+
precision: precisionSummary(overlay),
|
|
25
45
|
});
|
|
26
46
|
} catch (e) {
|
|
27
47
|
parentPort.postMessage({ ok: false, error: (e && e.message) || String(e) });
|
|
@@ -83,6 +83,7 @@ export default {
|
|
|
83
83
|
const id = nodeIds.has(base) ? `${base}:c${nameNode.startPosition.column + 1}` : base;
|
|
84
84
|
addSym(nameNode.text, lineOf(nameNode), callable, {
|
|
85
85
|
...extra,
|
|
86
|
+
selectionNode: nameNode,
|
|
86
87
|
...(id === base ? {} : { idSuffix: id.slice(base.length) }),
|
|
87
88
|
});
|
|
88
89
|
return nodeIds.has(id) ? id : null;
|
|
@@ -93,6 +93,7 @@ export default {
|
|
|
93
93
|
const exportStatement = !isMethod && exportStatementOf(cap.node);
|
|
94
94
|
addSym(cap.node.text, cap.node.startPosition.row + 1, cap.name !== "class", {
|
|
95
95
|
sourceNode: cap.node.parent,
|
|
96
|
+
selectionNode: cap.node,
|
|
96
97
|
...(exportStatement ? { exported: true } : {}),
|
|
97
98
|
...(isMethod ? methodMetadata(cap.node) : { symbolKind: cap.name === "class" ? "class" : "function", moduleDeclaration: isModuleDeclaration(cap.node) })
|
|
98
99
|
});
|
|
@@ -111,6 +112,7 @@ export default {
|
|
|
111
112
|
const exported = isExportedDecl(cap.node);
|
|
112
113
|
addSym(nameNode.text, nameNode.startPosition.row + 1, !!(val && CALLABLE.test(val.type)), {
|
|
113
114
|
sourceNode: val || cap.node,
|
|
115
|
+
selectionNode: nameNode,
|
|
114
116
|
...(exported ? { exported: true } : {}),
|
|
115
117
|
symbolKind: "variable",
|
|
116
118
|
moduleDeclaration: true
|
|
@@ -9,7 +9,7 @@ import {childProcessEnv} from '../child-env.js'
|
|
|
9
9
|
import {createRepoBoundary} from '../repo-path.js'
|
|
10
10
|
|
|
11
11
|
export const REPOSITORY_FRESHNESS_PROBE_V = 1
|
|
12
|
-
export const GRAPH_BUILDER_SCHEMA_V =
|
|
12
|
+
export const GRAPH_BUILDER_SCHEMA_V = 3
|
|
13
13
|
export const GRAPH_BUILDER_VERSION = (() => {
|
|
14
14
|
try { return String(createRequire(import.meta.url)('../../package.json').version) }
|
|
15
15
|
catch { return '0.0.0' }
|
|
@@ -25,7 +25,7 @@ const CURRENT_GRAPH_SCHEMA = Object.freeze({
|
|
|
25
25
|
complexityV: 1,
|
|
26
26
|
repoBoundaryV: 1,
|
|
27
27
|
barrelResolutionV: 1,
|
|
28
|
-
extractorSchemaV:
|
|
28
|
+
extractorSchemaV: 3,
|
|
29
29
|
})
|
|
30
30
|
const CONTROL_FILES = ['.gitignore', '.weavatrixignore', '.weavatrix.json']
|
|
31
31
|
const MAX_CONTROL_BYTES = 1_000_000
|
|
@@ -183,7 +183,7 @@ export async function refreshGraphIncrementally(repoDir, existingGraph, {
|
|
|
183
183
|
|
|
184
184
|
if (!existingGraph || !existingGraph.fileHashes || !existingGraph.fileExportSignatures
|
|
185
185
|
|| !existingGraph.controlHashes || !existingGraph.jsExportRecords || Number(existingGraph.barrelResolutionV) < 1
|
|
186
|
-
|| Number(existingGraph.extractorSchemaV) <
|
|
186
|
+
|| Number(existingGraph.extractorSchemaV) < 3 || Number(existingGraph.edgeProvenanceV) < 1) {
|
|
187
187
|
return full("incremental-baseline-unavailable");
|
|
188
188
|
}
|
|
189
189
|
if (!sameRecord(existingGraph.controlHashes, snapshot.controlHashes)) return full("ignore-or-control-config-changed");
|
|
@@ -89,6 +89,7 @@ export async function buildInternalGraph(repoDir, opts = {}) {
|
|
|
89
89
|
const suffix = /^:[A-Za-z0-9_-]+$/.test(extra?.idSuffix || "") ? extra.idSuffix : "";
|
|
90
90
|
const id = `${fileRel}#${name}@${line}${suffix}`; if (nodeIds.has(id)) return;
|
|
91
91
|
const sourceNode = extra && extra.sourceNode;
|
|
92
|
+
const selectionNode = extra && extra.selectionNode;
|
|
92
93
|
const endLine = sourceNode?.endPosition ? sourceNode.endPosition.row + 1 : 0;
|
|
93
94
|
let complexity = null;
|
|
94
95
|
if (callable && sourceNode) {
|
|
@@ -98,10 +99,38 @@ export async function buildInternalGraph(repoDir, opts = {}) {
|
|
|
98
99
|
addNode({
|
|
99
100
|
id,
|
|
100
101
|
label: callable ? `${name}()` : name,
|
|
102
|
+
...(callable ? { callable: true } : {}),
|
|
101
103
|
file_type: "code",
|
|
102
104
|
source_file: fileRel,
|
|
103
105
|
source_location: `L${line}`,
|
|
104
106
|
...(endLine >= line ? { source_end: `L${endLine}` } : {}),
|
|
107
|
+
...(sourceNode?.startPosition && sourceNode?.endPosition ? {
|
|
108
|
+
// web-tree-sitter Point columns are zero-based UTF-16 code-unit offsets. Keep the
|
|
109
|
+
// declaration body range as well as the identifier selection so LSP reference
|
|
110
|
+
// locations on a boundary line cannot be attributed to the wrong symbol.
|
|
111
|
+
source_range: {
|
|
112
|
+
start: {
|
|
113
|
+
line: sourceNode.startPosition.row,
|
|
114
|
+
character: sourceNode.startPosition.column,
|
|
115
|
+
},
|
|
116
|
+
end: {
|
|
117
|
+
line: sourceNode.endPosition.row,
|
|
118
|
+
character: sourceNode.endPosition.column,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
} : {}),
|
|
122
|
+
...(selectionNode?.startPosition && selectionNode?.endPosition ? {
|
|
123
|
+
// web-tree-sitter's JavaScript Point columns are already zero-based UTF-16 code-unit
|
|
124
|
+
// offsets, matching LSP positions exactly (including text before non-ASCII identifiers).
|
|
125
|
+
selection_start: {
|
|
126
|
+
line: selectionNode.startPosition.row,
|
|
127
|
+
character: selectionNode.startPosition.column,
|
|
128
|
+
},
|
|
129
|
+
selection_end: {
|
|
130
|
+
line: selectionNode.endPosition.row,
|
|
131
|
+
character: selectionNode.endPosition.column,
|
|
132
|
+
},
|
|
133
|
+
} : {}),
|
|
105
134
|
...(complexity ? { complexity } : {}),
|
|
106
135
|
...(extra && extra.exported ? { exported: true } : {}),
|
|
107
136
|
...(extra && extra.decorated ? { decorated: true } : {}),
|
|
@@ -217,7 +246,7 @@ export async function buildInternalGraph(repoDir, opts = {}) {
|
|
|
217
246
|
for (const cap of caps(grammar, lang.calls, tree.rootNode)) {
|
|
218
247
|
const caller = enclosing(fileRel, cap.node.startPosition.row + 1); if (!caller) continue;
|
|
219
248
|
const target = resolveCall(cap.node.text, fileRel); if (!target || target === caller.id) continue;
|
|
220
|
-
links.push({ source: caller.id, target, relation: "calls", confidence: "INFERRED" });
|
|
249
|
+
links.push({ source: caller.id, target, relation: "calls", confidence: "INFERRED", line: cap.node.startPosition.row + 1 });
|
|
221
250
|
}
|
|
222
251
|
// qualified/selector calls (Go): `pkg.Func()` → the imported package's dir; else `receiver.Method()` → the
|
|
223
252
|
// SAME package (heuristic by method name — connects lifecycle methods like peer.Enable() that need type info).
|
|
@@ -229,7 +258,7 @@ export async function buildInternalGraph(repoDir, opts = {}) {
|
|
|
229
258
|
const dir = fileRel.includes("/") ? fileRel.slice(0, fileRel.lastIndexOf("/")) : "";
|
|
230
259
|
const dm = goDirSymbols.get(imp && imp.targetDir ? imp.targetDir : dir);
|
|
231
260
|
const target = dm && dm.get(fld.text);
|
|
232
|
-
if (target && target !== caller.id) links.push({ source: caller.id, target, relation: "calls", confidence: "INFERRED" });
|
|
261
|
+
if (target && target !== caller.id) links.push({ source: caller.id, target, relation: "calls", confidence: "INFERRED", line: cap.node.startPosition.row + 1 });
|
|
233
262
|
}
|
|
234
263
|
for (const heritageSpec of lang.heritage || []) {
|
|
235
264
|
const query = typeof heritageSpec === "string" ? heritageSpec : heritageSpec.query;
|
|
@@ -253,7 +282,7 @@ export async function buildInternalGraph(repoDir, opts = {}) {
|
|
|
253
282
|
if (origin.status !== "resolved") continue;
|
|
254
283
|
const target = symByFileName.get(origin.origin.file)?.get(origin.origin.name);
|
|
255
284
|
const caller = enclosing(fileRel, cap.node.startPosition.row + 1);
|
|
256
|
-
if (target && caller && target !== caller.id) links.push({ source: caller.id, target, relation: "calls", confidence: "INFERRED" });
|
|
285
|
+
if (target && caller && target !== caller.id) links.push({ source: caller.id, target, relation: "calls", confidence: "INFERRED", line: cap.node.startPosition.row + 1 });
|
|
257
286
|
}
|
|
258
287
|
for (const cap of caps(grammar, `[
|
|
259
288
|
(jsx_opening_element name: (_) @jsx)
|
|
@@ -335,7 +364,7 @@ export async function buildInternalGraph(repoDir, opts = {}) {
|
|
|
335
364
|
complexityV: 1,
|
|
336
365
|
repoBoundaryV: 1,
|
|
337
366
|
barrelResolutionV: 1,
|
|
338
|
-
extractorSchemaV:
|
|
367
|
+
extractorSchemaV: 3,
|
|
339
368
|
jsExportRecords: Object.fromEntries([...jsExports.entries()].sort(([a], [b]) => a.localeCompare(b))),
|
|
340
369
|
fileHashes: snapshot.fileHashes,
|
|
341
370
|
fileExportSignatures: snapshot.fileExportSignatures,
|