@swarmvaultai/engine 0.7.22 → 0.7.23

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/dist/index.d.ts CHANGED
@@ -767,6 +767,7 @@ interface WatchOptions {
767
767
  lint?: boolean;
768
768
  debounceMs?: number;
769
769
  repo?: boolean;
770
+ codeOnly?: boolean;
770
771
  }
771
772
  interface PendingSemanticRefreshEntry {
772
773
  id: string;
package/dist/index.js CHANGED
@@ -2263,7 +2263,7 @@ function managedHookBlock(vaultRoot) {
2263
2263
  `swarmvault_bin=${shellQuote(resolvedExecutable)}`,
2264
2264
  '[ ! -x "$swarmvault_bin" ] && swarmvault_bin=$(command -v swarmvault 2>/dev/null || true)',
2265
2265
  'if [ -n "$swarmvault_bin" ] && [ -x "$swarmvault_bin" ]; then',
2266
- ` "$swarmvault_bin" watch --repo --once >/dev/null 2>&1 || printf '[swarmvault hook] refresh failed\\n' >&2`,
2266
+ ` "$swarmvault_bin" watch --repo --once --code-only >/dev/null 2>&1 || printf '[swarmvault hook] refresh failed\\n' >&2`,
2267
2267
  "fi",
2268
2268
  hookEnd,
2269
2269
  ""
@@ -12748,6 +12748,8 @@ function buildSchemaPrompt(schema, instruction) {
12748
12748
  // src/vault.ts
12749
12749
  import fs19 from "fs/promises";
12750
12750
  import path23 from "path";
12751
+ import Graph from "graphology";
12752
+ import louvain from "graphology-communities-louvain";
12751
12753
  import matter9 from "gray-matter";
12752
12754
  import { z as z7 } from "zod";
12753
12755
 
@@ -18026,26 +18028,32 @@ function deriveGraphMetrics(nodes, edges) {
18026
18028
  }
18027
18029
  const communityMap = /* @__PURE__ */ new Map();
18028
18030
  const communities = [];
18029
- const visited = /* @__PURE__ */ new Set();
18031
+ const nonSourceIdSet = new Set(nonSourceNodes.map((node) => node.id));
18032
+ const louvainGraph = new Graph({ type: "undirected" });
18030
18033
  for (const node of nonSourceNodes) {
18031
- if (visited.has(node.id)) {
18032
- continue;
18033
- }
18034
- const queue = [node.id];
18035
- const memberIds = [];
18036
- visited.add(node.id);
18037
- while (queue.length) {
18038
- const current = queue.shift();
18039
- memberIds.push(current);
18040
- for (const neighbor of adjacency.get(current) ?? []) {
18041
- if (!visited.has(neighbor) && nodes.find((candidate) => candidate.id === neighbor)?.type !== "source") {
18042
- visited.add(neighbor);
18043
- queue.push(neighbor);
18044
- }
18034
+ louvainGraph.addNode(node.id);
18035
+ }
18036
+ for (const node of nonSourceNodes) {
18037
+ for (const neighbor of adjacency.get(node.id) ?? []) {
18038
+ if (nonSourceIdSet.has(neighbor) && !louvainGraph.hasEdge(node.id, neighbor)) {
18039
+ louvainGraph.addEdge(node.id, neighbor);
18045
18040
  }
18046
18041
  }
18047
- const labelSeed = nodes.find((candidate) => candidate.id === memberIds[0])?.label ?? `cluster-${communities.length + 1}`;
18048
- const communityId = buildCommunityId(labelSeed, communities.length);
18042
+ }
18043
+ const louvainMapping = louvainGraph.size > 0 ? louvain(louvainGraph, { resolution: 1 }) : {};
18044
+ const groupByCommunity = /* @__PURE__ */ new Map();
18045
+ let nextIsolated = -1;
18046
+ for (const node of nonSourceNodes) {
18047
+ const communityNumber = louvainMapping[node.id] ?? nextIsolated--;
18048
+ if (!groupByCommunity.has(communityNumber)) {
18049
+ groupByCommunity.set(communityNumber, []);
18050
+ }
18051
+ groupByCommunity.get(communityNumber).push(node.id);
18052
+ }
18053
+ let communityIndex = 0;
18054
+ for (const memberIds of groupByCommunity.values()) {
18055
+ const labelSeed = nodes.find((candidate) => candidate.id === memberIds[0])?.label ?? `cluster-${communityIndex + 1}`;
18056
+ const communityId = buildCommunityId(labelSeed, communityIndex);
18049
18057
  communities.push({
18050
18058
  id: communityId,
18051
18059
  label: labelSeed,
@@ -18054,6 +18062,7 @@ function deriveGraphMetrics(nodes, edges) {
18054
18062
  for (const memberId of memberIds) {
18055
18063
  communityMap.set(memberId, communityId);
18056
18064
  }
18065
+ communityIndex++;
18057
18066
  }
18058
18067
  const degreeMap = /* @__PURE__ */ new Map();
18059
18068
  for (const node of nodes) {
@@ -21243,7 +21252,7 @@ async function bootstrapDemo(rootDir, input) {
21243
21252
  }
21244
21253
 
21245
21254
  // src/mcp.ts
21246
- var SERVER_VERSION = "0.7.22";
21255
+ var SERVER_VERSION = "0.7.23";
21247
21256
  async function createMcpServer(rootDir) {
21248
21257
  const server = new McpServer({
21249
21258
  name: "swarmvault",
@@ -23875,16 +23884,38 @@ var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
23875
23884
  ".r",
23876
23885
  ".R"
23877
23886
  ]);
23887
+ var FILE_CHANGE_RE = /^(?:add|change|unlink):(.+)$/;
23878
23888
  function isCodeOnlyChange(reasons) {
23879
23889
  for (const reason of reasons) {
23880
- const match = reason.match(/^(?:add|change|unlink):(.+)$/);
23881
- if (!match) continue;
23882
- const filePath = match[1];
23883
- const ext = path27.extname(filePath).toLowerCase();
23890
+ const match = reason.match(FILE_CHANGE_RE);
23891
+ if (!match) return false;
23892
+ const ext = path27.extname(match[1]).toLowerCase();
23884
23893
  if (!ext || !CODE_EXTENSIONS.has(ext)) return false;
23885
23894
  }
23886
23895
  return reasons.size > 0;
23887
23896
  }
23897
+ function hasNonCodeChanges(reasons) {
23898
+ for (const reason of reasons) {
23899
+ const match = reason.match(FILE_CHANGE_RE);
23900
+ if (!match) return true;
23901
+ const ext = path27.extname(match[1]).toLowerCase();
23902
+ if (!ext || !CODE_EXTENSIONS.has(ext)) return true;
23903
+ }
23904
+ return false;
23905
+ }
23906
+ function collectNonCodePaths(reasons) {
23907
+ const result = [];
23908
+ for (const reason of reasons) {
23909
+ const match = reason.match(FILE_CHANGE_RE);
23910
+ if (!match) {
23911
+ result.push(reason);
23912
+ continue;
23913
+ }
23914
+ const ext = path27.extname(match[1]).toLowerCase();
23915
+ if (!ext || !CODE_EXTENSIONS.has(ext)) result.push(match[1]);
23916
+ }
23917
+ return result;
23918
+ }
23888
23919
  function hasIgnoredRepoSegment(baseDir, targetPath) {
23889
23920
  const relativePath = path27.relative(baseDir, targetPath);
23890
23921
  if (!relativePath || relativePath.startsWith("..")) {
@@ -23957,7 +23988,7 @@ async function runWatchCycle(rootDir, options = {}) {
23957
23988
  changedPages: []
23958
23989
  };
23959
23990
  try {
23960
- result = await performWatchCycle(rootDir, paths, options);
23991
+ result = await performWatchCycle(rootDir, paths, options, options.codeOnly ?? false);
23961
23992
  return result;
23962
23993
  } catch (caught) {
23963
23994
  success = false;
@@ -24107,10 +24138,18 @@ async function watchVault(rootDir, options = {}) {
24107
24138
  pending = false;
24108
24139
  running = true;
24109
24140
  const startedAt = /* @__PURE__ */ new Date();
24110
- const codeOnlyChange = isCodeOnlyChange(reasons);
24141
+ const detectedCodeOnly = isCodeOnlyChange(reasons);
24142
+ const hasDeferredNonCode = !detectedCodeOnly && hasNonCodeChanges(reasons);
24143
+ const codeOnlyChange = options.codeOnly || detectedCodeOnly || hasDeferredNonCode;
24111
24144
  const runReasons = [...reasons];
24112
24145
  reasons.clear();
24113
- if (codeOnlyChange) {
24146
+ if (hasDeferredNonCode) {
24147
+ const nonCodePaths = collectNonCodePaths(new Set(runReasons));
24148
+ process3.stderr.write(
24149
+ `[swarmvault watch] Non-code changes detected (${nonCodePaths.length} file(s)) \u2014 run \`swarmvault compile\` to include LLM re-analysis.
24150
+ `
24151
+ );
24152
+ } else if (codeOnlyChange) {
24114
24153
  process3.stderr.write("[swarmvault watch] Code-only changes detected \u2014 skipping LLM re-analysis.\n");
24115
24154
  }
24116
24155
  let importedCount = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/engine",
3
- "version": "0.7.22",
3
+ "version": "0.7.23",
4
4
  "description": "Core engine for SwarmVault: ingest, compile, query, lint, and provider abstractions.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,6 +50,8 @@
50
50
  "csv-parse": "^6.2.1",
51
51
  "fast-xml-parser": "^5.5.11",
52
52
  "fflate": "^0.8.2",
53
+ "graphology": "^0.26.0",
54
+ "graphology-communities-louvain": "^2.0.2",
53
55
  "gray-matter": "^4.0.3",
54
56
  "ignore": "^7.0.5",
55
57
  "ini": "^6.0.0",
@@ -81,6 +83,7 @@
81
83
  "@types/mime-types": "^3.0.1",
82
84
  "@types/node": "^24.6.0",
83
85
  "@types/turndown": "^5.0.5",
86
+ "graphology-types": "^0.24.8",
84
87
  "tsup": "^8.5.0",
85
88
  "vitest": "^3.2.4"
86
89
  },