weavatrix 0.1.0 → 0.1.1

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
@@ -10,7 +10,7 @@ never leaves it.**
10
10
 
11
11
  - Website: [weavatrix.com](https://weavatrix.com)
12
12
  - Source: [github.com/sergii-ziborov/weavatrix](https://github.com/sergii-ziborov/weavatrix)
13
- - Status: **pre-release**npm publish imminent; API may still shift before v0.1.
13
+ - npm: [`weavatrix`](https://www.npmjs.com/package/weavatrix)`npx -y weavatrix <repoRoot>`
14
14
 
15
15
  ## Why
16
16
 
@@ -125,7 +125,7 @@ Graphs are derived data and never live inside your repo: they go to a `weavatrix
125
125
 
126
126
  ```sh
127
127
  npm install
128
- npm test # node --test — 224 tests
128
+ npm test # node --test
129
129
  ```
130
130
 
131
131
  Design rule: **no source file exceeds 300 lines.** Larger concerns split into dotted-suffix modules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weavatrix",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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@edgehawk.io>",
@@ -11,13 +11,16 @@ import { buildResolvers } from "./internal-builder.resolvers.js";
11
11
 
12
12
  // Parse a repo directory into a graph-builder-compatible { nodes, links } graph.
13
13
  export async function buildInternalGraph(repoDir, opts = {}) {
14
- const langs = await ensureParser(opts);
14
+ const files = walk(repoDir);
15
+ // Lazy grammar loading: compile only the WASMs for languages this repo actually contains.
16
+ const wanted = new Set();
17
+ for (const f of files) { const g = EXT_LANG[extname(f)]; if (g) wanted.add(g); }
18
+ const langs = await ensureParser(opts, wanted);
15
19
  const qc = new Map();
16
20
  const q = (grammar, src) => { const k = grammar + ":" + src; if (qc.has(k)) return qc.get(k); let x = null; try { x = new Query(langs[grammar], src); } catch { x = null; } qc.set(k, x); return x; };
17
21
  const caps = (grammar, src, root) => { const query = src && q(grammar, src); return query ? query.captures(root) : []; };
18
22
  const field = (n, f) => (n && n.childForFieldName ? n.childForFieldName(f) : null);
19
23
 
20
- const files = walk(repoDir);
21
24
  const rel = (p) => relative(repoDir, p).replace(/\\/g, "/");
22
25
  const fileSet = new Set(files.map(rel));
23
26
  const nodes = []; const links = []; const nodeIds = new Set(); const nodeById = new Map();
@@ -52,11 +52,15 @@ const MAX_PARSE_BYTES = 1_500_000; // skip parsing files above this (minified
52
52
 
53
53
  let _ready = null;
54
54
  const _langs = {};
55
- async function ensureParser(opts = {}) {
55
+ // `wanted` (a Set of grammar names) makes loading LAZY: only grammars for extensions actually present
56
+ // in the repo get compiled. The heavy WASMs (rust ~3MB, c_sharp ~4MB) cost seconds to compile — a tax
57
+ // a JS-only repo must never pay. Omit `wanted` to load everything (old behavior).
58
+ async function ensureParser(opts = {}, wanted = null) {
56
59
  if (!_ready) _ready = Parser.init({ locateFile: () => opts.runtimeWasm || DEFAULT_RUNTIME_WASM });
57
60
  await _ready;
58
61
  const wasmDir = opts.wasmDir || DEFAULT_WASM_DIR;
59
- for (const g of GRAMMARS) if (!_langs[g]) { try { _langs[g] = await Language.load(join(wasmDir, `tree-sitter-${g}.wasm`)); } catch { _langs[g] = null; } }
62
+ const list = wanted ? GRAMMARS.filter((g) => wanted.has(g)) : GRAMMARS;
63
+ for (const g of list) if (!_langs[g]) { try { _langs[g] = await Language.load(join(wasmDir, `tree-sitter-${g}.wasm`)); } catch { _langs[g] = null; } }
60
64
  return _langs;
61
65
  }
62
66
 
@@ -78,7 +82,9 @@ function walk(dir, acc = [], seen = new Set(), depth = 0) {
78
82
  const full = join(dir, name);
79
83
  let st; try { st = statSync(full); } catch { continue; }
80
84
  if (st.isDirectory()) walk(full, acc, seen, depth + 1);
81
- else { const e = extname(name); if ((EXT_LANG[e] && _langs[EXT_LANG[e]]) || isDataFile(name) || isDocFile(name)) acc.push(full); }
85
+ // include by KNOWN extension, not by loaded grammar grammars now load lazily AFTER the walk
86
+ // (the parse passes skip files whose grammar failed to load, so the guarantee is unchanged)
87
+ else { const e = extname(name); if (EXT_LANG[e] || isDataFile(name) || isDocFile(name)) acc.push(full); }
82
88
  }
83
89
  return acc;
84
90
  }
@@ -22,8 +22,11 @@ import process from 'node:process'
22
22
  import {loadGraph} from './mcp/graph-context.mjs'
23
23
  import {loadHotApi, HOT_FILES} from './mcp/catalog.mjs'
24
24
  import {graphOutDirForRepo} from './graph/layout.js'
25
+ import {createRequire} from 'node:module'
25
26
 
26
- const SERVER_INFO = {name: 'weavatrix', version: '0.0.1'}
27
+ // version comes from package.json so serverInfo can never drift from the published package again
28
+ const PKG_VERSION = (() => { try { return createRequire(import.meta.url)('../package.json').version } catch { return '0.0.0' } })()
29
+ const SERVER_INFO = {name: 'weavatrix', version: PKG_VERSION}
27
30
  const DEFAULT_PROTOCOL = '2024-11-05'
28
31
  const log = (...a) => process.stderr.write(`[weavatrix] ${a.join(' ')}\n`)
29
32