@swarmvaultai/engine 0.7.26 → 0.7.28

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.
Files changed (2) hide show
  1. package/dist/index.js +311 -29
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -493,7 +493,9 @@ async function autoCommitWikiChanges(rootDir, operation, detail, options) {
493
493
  }
494
494
 
495
495
  // src/graph-export.ts
496
+ import { readFileSync } from "fs";
496
497
  import fs2 from "fs/promises";
498
+ import { createRequire } from "module";
497
499
  import path2 from "path";
498
500
  import matter from "gray-matter";
499
501
 
@@ -941,6 +943,46 @@ document.getElementById("page-filter").addEventListener("input", function(e) {
941
943
  }
942
944
 
943
945
  // src/graph-export.ts
946
+ var _visNetworkJs;
947
+ function loadVisNetworkJs() {
948
+ if (!_visNetworkJs) {
949
+ const require3 = createRequire(import.meta.url);
950
+ const pkgDir = path2.dirname(require3.resolve("vis-network/package.json"));
951
+ _visNetworkJs = readFileSync(path2.join(pkgDir, "standalone/umd/vis-network.min.js"), "utf8");
952
+ }
953
+ return _visNetworkJs;
954
+ }
955
+ function hexToObsidianColor(hex) {
956
+ return { a: 1, rgb: Number.parseInt(hex.replace("#", ""), 16) };
957
+ }
958
+ var OBSIDIAN_PROPERTY_TYPES = {
959
+ page_id: "text",
960
+ kind: "text",
961
+ title: "text",
962
+ tags: "tags",
963
+ aliases: "aliases",
964
+ source_ids: "multitext",
965
+ project_ids: "multitext",
966
+ node_ids: "multitext",
967
+ freshness: "text",
968
+ status: "text",
969
+ confidence: "number",
970
+ created_at: "datetime",
971
+ updated_at: "datetime",
972
+ compiled_from: "multitext",
973
+ managed_by: "text",
974
+ backlinks: "multitext",
975
+ schema_hash: "text",
976
+ source_class: "text",
977
+ source_type: "text",
978
+ language: "text",
979
+ graph_community: "text",
980
+ degree: "number",
981
+ bridge_score: "number",
982
+ is_god_node: "checkbox",
983
+ community: "text",
984
+ cssclasses: "multitext"
985
+ };
944
986
  var NODE_COLORS = {
945
987
  source: "#f59e0b",
946
988
  module: "#fb7185",
@@ -1341,7 +1383,7 @@ function renderHtmlStandalone(graph) {
1341
1383
  <head>
1342
1384
  <meta charset="utf-8">
1343
1385
  <title>SwarmVault Graph</title>
1344
- <script src="https://unpkg.com/vis-network@9/standalone/umd/vis-network.min.js"></script>
1386
+ <script>${loadVisNetworkJs()}</script>
1345
1387
  <style>
1346
1388
  * { margin: 0; padding: 0; box-sizing: border-box; }
1347
1389
  body { font-family: system-ui, sans-serif; display: flex; height: 100vh; background: #0f172a; color: #e2e8f0; }
@@ -1669,6 +1711,25 @@ function connectionsSection(nodeIds, adjacency, nodesById, wikilinkTarget) {
1669
1711
  }
1670
1712
  return lines;
1671
1713
  }
1714
+ function typedLinkFrontmatter(nodeIds, adjacency, nodesById, wikilinkTarget) {
1715
+ const byRelation = /* @__PURE__ */ new Map();
1716
+ const seen = /* @__PURE__ */ new Set();
1717
+ for (const nodeId of nodeIds) {
1718
+ for (const entry of adjacency.get(nodeId) ?? []) {
1719
+ const key = `${entry.neighborId}:${entry.relation}`;
1720
+ if (seen.has(key)) continue;
1721
+ seen.add(key);
1722
+ const neighbor = nodesById.get(entry.neighborId);
1723
+ if (!neighbor) continue;
1724
+ const target = wikilinkTarget.get(entry.neighborId);
1725
+ if (!target) continue;
1726
+ const bucket = byRelation.get(entry.relation) ?? [];
1727
+ bucket.push(`[[${target}|${neighbor.label}]]`);
1728
+ byRelation.set(entry.relation, bucket);
1729
+ }
1730
+ }
1731
+ return Object.fromEntries(byRelation);
1732
+ }
1672
1733
  async function exportObsidianVault(rootDir, outputDir) {
1673
1734
  const graph = await loadGraph(rootDir);
1674
1735
  const { paths } = await loadVaultConfig(rootDir);
@@ -1730,6 +1791,9 @@ async function exportObsidianVault(rootDir, outputDir) {
1730
1791
  if (primaryNode.communityId) {
1731
1792
  data.graph_community = primaryNode.communityId;
1732
1793
  }
1794
+ data.degree = primaryNode.degree ?? 0;
1795
+ data.bridge_score = primaryNode.bridgeScore ?? 0;
1796
+ data.is_god_node = primaryNode.isGodNode ?? false;
1733
1797
  const title = data.title ?? "";
1734
1798
  const nodeAliases = pageNodes.map((n) => n.label).filter((label) => label.toLowerCase() !== title.toLowerCase());
1735
1799
  const existingAliases = Array.isArray(data.aliases) ? data.aliases : [];
@@ -1737,6 +1801,15 @@ async function exportObsidianVault(rootDir, outputDir) {
1737
1801
  if (mergedAliases.length > 0) {
1738
1802
  data.aliases = mergedAliases;
1739
1803
  }
1804
+ const typedLinks = typedLinkFrontmatter(
1805
+ pageNodes.map((n) => n.id),
1806
+ adjacency,
1807
+ nodesById,
1808
+ wikilinkTarget
1809
+ );
1810
+ for (const [relation, links] of Object.entries(typedLinks)) {
1811
+ data[relation] = links;
1812
+ }
1740
1813
  }
1741
1814
  let outputContent = matter.stringify(parsed.content, data);
1742
1815
  if (pageNodes.length > 0) {
@@ -1770,11 +1843,19 @@ ${connLines.join("\n")}
1770
1843
  community: node.communityId ?? null,
1771
1844
  confidence: node.confidence ?? null,
1772
1845
  source_class: node.sourceClass ?? null,
1773
- tags: node.tags ?? []
1846
+ degree: node.degree ?? 0,
1847
+ bridge_score: node.bridgeScore ?? 0,
1848
+ is_god_node: node.isGodNode ?? false,
1849
+ tags: node.tags ?? [],
1850
+ cssclasses: ["swarmvault", `sv-${node.type}`]
1774
1851
  };
1775
1852
  if (aliases.length > 0) {
1776
1853
  frontmatter.aliases = aliases;
1777
1854
  }
1855
+ const orphanTypedLinks = typedLinkFrontmatter([node.id], adjacency, nodesById, wikilinkTarget);
1856
+ for (const [relation, links] of Object.entries(orphanTypedLinks)) {
1857
+ frontmatter[relation] = links;
1858
+ }
1778
1859
  const lines = [`# ${node.label}`, ""];
1779
1860
  const connLines = connectionsSection([node.id], adjacency, nodesById, wikilinkTarget);
1780
1861
  if (connLines.length > 0) {
@@ -1876,10 +1957,19 @@ ${connLines.join("\n")}
1876
1957
  {}
1877
1958
  )
1878
1959
  );
1879
- const colorGroups = projectIds.map((pid, index) => ({
1960
+ const nodeTypeGroups = [
1961
+ { query: "tag:#source", color: hexToObsidianColor("#f59e0b") },
1962
+ { query: "tag:#module", color: hexToObsidianColor("#fb7185") },
1963
+ { query: "tag:#concept", color: hexToObsidianColor("#0ea5e9") },
1964
+ { query: "tag:#entity", color: hexToObsidianColor("#22c55e") },
1965
+ { query: "tag:#rationale", color: hexToObsidianColor("#14b8a6") },
1966
+ { query: "tag:#symbol", color: hexToObsidianColor("#8b5cf6") }
1967
+ ];
1968
+ const projectColorGroups = projectIds.map((pid, index) => ({
1880
1969
  query: `tag:#project/${pid}`,
1881
- color: ["#0ea5e9", "#22c55e", "#f59e0b", "#8b5cf6", "#fb7185", "#14b8a6"][index % 6]
1970
+ color: hexToObsidianColor(["#0ea5e9", "#22c55e", "#f59e0b", "#8b5cf6", "#fb7185", "#14b8a6"][index % 6])
1882
1971
  }));
1972
+ const colorGroups = [...nodeTypeGroups, ...projectColorGroups];
1883
1973
  await fs2.writeFile(
1884
1974
  path2.join(obsidianDir, "app.json"),
1885
1975
  JSON.stringify(
@@ -1903,7 +1993,48 @@ ${connLines.join("\n")}
1903
1993
  ),
1904
1994
  "utf8"
1905
1995
  );
1906
- fileCount += 3;
1996
+ await fs2.writeFile(path2.join(obsidianDir, "types.json"), JSON.stringify({ types: OBSIDIAN_PROPERTY_TYPES }, null, 2), "utf8");
1997
+ fileCount += 4;
1998
+ const dashboardDir = path2.join(resolvedOutputDir, "graph", "dashboards");
1999
+ await ensureDir(dashboardDir);
2000
+ const dvPages = [
2001
+ {
2002
+ name: "sources-by-confidence",
2003
+ title: "Sources by Confidence",
2004
+ query: "TABLE confidence, source_class, updated_at FROM #source SORT confidence DESC"
2005
+ },
2006
+ {
2007
+ name: "concepts-index",
2008
+ title: "Concepts Index",
2009
+ query: "TABLE degree, graph_community FROM #concept SORT degree DESC"
2010
+ },
2011
+ {
2012
+ name: "stale-pages",
2013
+ title: "Stale Pages",
2014
+ query: 'TABLE freshness, updated_at FROM "" WHERE freshness = "stale"'
2015
+ },
2016
+ {
2017
+ name: "god-nodes",
2018
+ title: "God Nodes",
2019
+ query: 'TABLE degree, bridge_score FROM "" WHERE is_god_node = true SORT degree DESC'
2020
+ }
2021
+ ];
2022
+ for (const dv of dvPages) {
2023
+ const dvFrontmatter = {
2024
+ title: dv.title,
2025
+ kind: "dashboard",
2026
+ tags: ["dashboard", "dataview"],
2027
+ cssclasses: ["swarmvault", "sv-dashboard"]
2028
+ };
2029
+ const dvBody = `# ${dv.title}
2030
+
2031
+ \`\`\`dataview
2032
+ ${dv.query}
2033
+ \`\`\`
2034
+ `;
2035
+ await fs2.writeFile(path2.join(dashboardDir, `${dv.name}.md`), matter.stringify(dvBody, dvFrontmatter), "utf8");
2036
+ fileCount++;
2037
+ }
1907
2038
  return { format: "obsidian", outputPath: resolvedOutputDir, fileCount };
1908
2039
  }
1909
2040
  async function exportObsidianCanvas(rootDir, outputPath) {
@@ -1918,6 +2049,7 @@ async function exportObsidianCanvas(rootDir, outputPath) {
1918
2049
  const GROUP_PAD = 50;
1919
2050
  const GRID_COLS = 3;
1920
2051
  const GROUP_GAP = 100;
2052
+ const pageById2 = graphPageById(graph);
1921
2053
  const canvasNodes = [];
1922
2054
  const canvasEdges = [];
1923
2055
  const nodeCanvasId = /* @__PURE__ */ new Map();
@@ -1969,19 +2101,33 @@ async function exportObsidianCanvas(rootDir, outputPath) {
1969
2101
  const nodeY = groupY + GROUP_PAD + 30 + innerRow * (NODE_HEIGHT + NODE_PAD_Y);
1970
2102
  const canvasId = `node-${node.id}`;
1971
2103
  nodeCanvasId.set(node.id, canvasId);
1972
- const communityLabel2 = community.id === "community:unassigned" ? "Unassigned" : community.label;
1973
- canvasNodes.push({
1974
- id: canvasId,
1975
- type: "text",
1976
- text: `**${node.label}**
2104
+ const page = node.pageId ? pageById2.get(node.pageId) : void 0;
2105
+ if (page) {
2106
+ canvasNodes.push({
2107
+ id: canvasId,
2108
+ type: "file",
2109
+ file: page.path,
2110
+ x: nodeX,
2111
+ y: nodeY,
2112
+ width: NODE_WIDTH,
2113
+ height: NODE_HEIGHT,
2114
+ color: COLORS[communityIndex % COLORS.length]
2115
+ });
2116
+ } else {
2117
+ const communityLabel2 = community.id === "community:unassigned" ? "Unassigned" : community.label;
2118
+ canvasNodes.push({
2119
+ id: canvasId,
2120
+ type: "text",
2121
+ text: `**${node.label}**
1977
2122
  Type: ${node.type}
1978
2123
  Community: ${communityLabel2}`,
1979
- x: nodeX,
1980
- y: nodeY,
1981
- width: NODE_WIDTH,
1982
- height: NODE_HEIGHT,
1983
- color: COLORS[communityIndex % COLORS.length]
1984
- });
2124
+ x: nodeX,
2125
+ y: nodeY,
2126
+ width: NODE_WIDTH,
2127
+ height: NODE_HEIGHT,
2128
+ color: COLORS[communityIndex % COLORS.length]
2129
+ });
2130
+ }
1985
2131
  });
1986
2132
  });
1987
2133
  for (const edge of graph.edges) {
@@ -1992,6 +2138,10 @@ Community: ${communityLabel2}`,
1992
2138
  id: `edge-${edge.id}`,
1993
2139
  fromNode: fromId,
1994
2140
  toNode: toId,
2141
+ fromSide: "right",
2142
+ toSide: "left",
2143
+ fromEnd: "none",
2144
+ toEnd: "arrow",
1995
2145
  label: edge.relation
1996
2146
  });
1997
2147
  }
@@ -2998,9 +3148,9 @@ import YAML2 from "yaml";
2998
3148
 
2999
3149
  // src/code-tree-sitter.ts
3000
3150
  import fs5 from "fs/promises";
3001
- import { createRequire } from "module";
3151
+ import { createRequire as createRequire2 } from "module";
3002
3152
  import path5 from "path";
3003
- var require2 = createRequire(import.meta.url);
3153
+ var require2 = createRequire2(import.meta.url);
3004
3154
  var TREE_SITTER_RUNTIME_PACKAGE = "@vscode/tree-sitter-wasm";
3005
3155
  var TREE_SITTER_EXTRA_GRAMMARS_PACKAGE = "tree-sitter-wasms";
3006
3156
  var SWIFT_TREE_SITTER_OPT_IN_ENV = "SWARMVAULT_ENABLE_SWIFT_TREE_SITTER";
@@ -3127,8 +3277,8 @@ function singleLineSignature(value) {
3127
3277
  180
3128
3278
  );
3129
3279
  }
3130
- function makeSymbolId(sourceId, name, seen) {
3131
- const base = slugify(name);
3280
+ function makeSymbolId(sourceId, name, kind, seen) {
3281
+ const base = `${slugify(name)}.${kind}`;
3132
3282
  const count = (seen.get(base) ?? 0) + 1;
3133
3283
  seen.set(base, count);
3134
3284
  return `symbol:${sourceId}:${count === 1 ? base : `${base}-${count}`}`;
@@ -3210,7 +3360,7 @@ function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportL
3210
3360
  }
3211
3361
  const seenSymbolIds = /* @__PURE__ */ new Map();
3212
3362
  const symbols = draftSymbols.map((symbol) => ({
3213
- id: makeSymbolId(manifest.sourceId, symbol.name, seenSymbolIds),
3363
+ id: makeSymbolId(manifest.sourceId, symbol.name, symbol.kind, seenSymbolIds),
3214
3364
  name: symbol.name,
3215
3365
  kind: symbol.kind,
3216
3366
  signature: symbol.signature,
@@ -7019,8 +7169,8 @@ function isNodeExported(node) {
7019
7169
  )
7020
7170
  );
7021
7171
  }
7022
- function makeSymbolId2(scope, name, seen) {
7023
- const base = slugify(name);
7172
+ function makeSymbolId2(scope, name, kind, seen) {
7173
+ const base = `${slugify(name)}.${kind}`;
7024
7174
  const count = (seen.get(base) ?? 0) + 1;
7025
7175
  seen.set(base, count);
7026
7176
  return `symbol:${scope}:${count === 1 ? base : `${base}-${count}`}`;
@@ -7187,7 +7337,7 @@ function finalizeCodeAnalysis2(manifest, language, imports, draftSymbols, export
7187
7337
  const seenSymbolIds = /* @__PURE__ */ new Map();
7188
7338
  const symbolScope = metadata?.namespace ? `ns:${slugify(metadata.namespace)}` : manifest.sourceId;
7189
7339
  const symbols = draftSymbols.map((symbol) => ({
7190
- id: makeSymbolId2(symbolScope, symbol.name, seenSymbolIds),
7340
+ id: makeSymbolId2(symbolScope, symbol.name, symbol.kind, seenSymbolIds),
7191
7341
  name: symbol.name,
7192
7342
  kind: symbol.kind,
7193
7343
  signature: symbol.signature,
@@ -7601,6 +7751,81 @@ function manifestBasenameWithoutExtension(manifest) {
7601
7751
  const target = manifest.repoRelativePath ?? manifest.originalPath ?? manifest.storedPath;
7602
7752
  return path6.posix.basename(stripCodeExtension2(normalizeAlias(target)));
7603
7753
  }
7754
+ async function readNearestTsconfigPaths(startPath, cache) {
7755
+ let current = path6.resolve(startPath);
7756
+ try {
7757
+ const stat = await fs6.stat(current);
7758
+ if (!stat.isDirectory()) {
7759
+ current = path6.dirname(current);
7760
+ }
7761
+ } catch {
7762
+ current = path6.dirname(current);
7763
+ }
7764
+ while (true) {
7765
+ if (cache.has(current)) {
7766
+ const cached = cache.get(current);
7767
+ return cached === null ? void 0 : cached;
7768
+ }
7769
+ const tsconfigPath = path6.join(current, "tsconfig.json");
7770
+ const exists = await fs6.access(tsconfigPath).then(() => true).catch(() => false);
7771
+ if (exists) {
7772
+ const configFile = ts.readConfigFile(tsconfigPath, (p) => ts.sys.readFile(p));
7773
+ if (!configFile.error && configFile.config) {
7774
+ const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, current);
7775
+ const rawPaths = parsed.options.paths;
7776
+ if (rawPaths && Object.keys(rawPaths).length > 0) {
7777
+ const baseUrl = parsed.options.baseUrl ? toPosix(path6.relative(current, parsed.options.baseUrl)) : ".";
7778
+ const config = { baseUrl, paths: rawPaths };
7779
+ cache.set(current, config);
7780
+ return config;
7781
+ }
7782
+ }
7783
+ cache.set(current, null);
7784
+ return void 0;
7785
+ }
7786
+ const parent = path6.dirname(current);
7787
+ if (parent === current) {
7788
+ cache.set(current, null);
7789
+ return void 0;
7790
+ }
7791
+ current = parent;
7792
+ }
7793
+ }
7794
+ function tsconfigPathAliasesForFile(repoRelativePath, config) {
7795
+ const aliases = [];
7796
+ const stripped = stripCodeExtension2(normalizeAlias(repoRelativePath));
7797
+ const indexStripped = stripped.endsWith("/index") ? stripped.slice(0, -"/index".length) : void 0;
7798
+ for (const [pattern, targets] of Object.entries(config.paths)) {
7799
+ for (const target of targets) {
7800
+ if (pattern.includes("*") && target.includes("*")) {
7801
+ const targetPrefix = normalizeAlias(
7802
+ config.baseUrl === "." ? target.replace("*", "") : path6.posix.join(config.baseUrl, target.replace("*", ""))
7803
+ );
7804
+ const patternBase = pattern.replace("*", "");
7805
+ for (const candidate of [stripped, indexStripped]) {
7806
+ if (candidate && candidate.startsWith(targetPrefix)) {
7807
+ aliases.push(patternBase + candidate.slice(targetPrefix.length));
7808
+ }
7809
+ }
7810
+ } else if (!pattern.includes("*") && !target.includes("*")) {
7811
+ const targetNorm = normalizeAlias(config.baseUrl === "." ? target : path6.posix.join(config.baseUrl, target));
7812
+ if (stripped === stripCodeExtension2(targetNorm) || indexStripped === stripCodeExtension2(targetNorm)) {
7813
+ aliases.push(pattern);
7814
+ }
7815
+ }
7816
+ }
7817
+ }
7818
+ if (config.baseUrl !== ".") {
7819
+ const basePrefix = normalizeAlias(config.baseUrl) + "/";
7820
+ if (stripped.startsWith(basePrefix)) {
7821
+ aliases.push(stripped.slice(basePrefix.length));
7822
+ }
7823
+ if (indexStripped && indexStripped.startsWith(basePrefix)) {
7824
+ aliases.push(indexStripped.slice(basePrefix.length));
7825
+ }
7826
+ }
7827
+ return aliases;
7828
+ }
7604
7829
  async function readNearestDartPackageInfo(startPath, cache) {
7605
7830
  let current = path6.resolve(startPath);
7606
7831
  try {
@@ -7763,6 +7988,7 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
7763
7988
  const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
7764
7989
  const goModuleCache = /* @__PURE__ */ new Map();
7765
7990
  const dartPackageCache = /* @__PURE__ */ new Map();
7991
+ const tsconfigCache = /* @__PURE__ */ new Map();
7766
7992
  const entries = [];
7767
7993
  for (const manifest of manifests) {
7768
7994
  const analysis = analysesBySourceId.get(manifest.sourceId);
@@ -7784,6 +8010,20 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
7784
8010
  recordAlias(aliases, normalizedModuleName);
7785
8011
  recordAlias(aliases, normalizedNamespace);
7786
8012
  switch (analysis.code.language) {
8013
+ case "javascript":
8014
+ case "jsx":
8015
+ case "typescript":
8016
+ case "tsx": {
8017
+ if (repoRelativePath && manifest.originalPath) {
8018
+ const tsconfigPaths = await readNearestTsconfigPaths(manifest.originalPath, tsconfigCache);
8019
+ if (tsconfigPaths) {
8020
+ for (const alias of tsconfigPathAliasesForFile(repoRelativePath, tsconfigPaths)) {
8021
+ recordAlias(aliases, alias);
8022
+ }
8023
+ }
8024
+ }
8025
+ break;
8026
+ }
7787
8027
  case "python":
7788
8028
  recordAlias(aliases, normalizedModuleName?.replace(/\//g, "."));
7789
8029
  break;
@@ -15322,6 +15562,9 @@ function sourceHashFrontmatter(sourceHashes, sourceSemanticHashes) {
15322
15562
  source_semantic_hashes: sourceSemanticHashes
15323
15563
  };
15324
15564
  }
15565
+ function cssclassesFor(kind) {
15566
+ return ["swarmvault", `sv-${kind.replace(/_/g, "-")}`];
15567
+ }
15325
15568
  function decoratedTags(baseTags, decorations) {
15326
15569
  return uniqueStrings2([
15327
15570
  ...baseTags,
@@ -15415,6 +15658,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
15415
15658
  const frontmatter = {
15416
15659
  page_id: pageId,
15417
15660
  kind: "source",
15661
+ cssclasses: cssclassesFor("source"),
15418
15662
  title: analysis.title,
15419
15663
  ...manifest.sourceType ? { source_type: manifest.sourceType } : {},
15420
15664
  ...manifest.sourceClass ? { source_class: manifest.sourceClass } : {},
@@ -15566,6 +15810,7 @@ function buildModulePage(input) {
15566
15810
  const frontmatter = {
15567
15811
  page_id: pageId,
15568
15812
  kind: "module",
15813
+ cssclasses: cssclassesFor("module"),
15569
15814
  title,
15570
15815
  ...manifest.sourceClass ? { source_class: manifest.sourceClass } : {},
15571
15816
  tags: decoratedTags(["module", "code", code.language], { projectIds: input.projectIds, extraTags: input.extraTags }),
@@ -15683,6 +15928,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
15683
15928
  const frontmatter = {
15684
15929
  page_id: pageId,
15685
15930
  kind,
15931
+ cssclasses: cssclassesFor(kind),
15686
15932
  title: name,
15687
15933
  ...decorations?.sourceClass ? { source_class: decorations.sourceClass } : {},
15688
15934
  tags: decoratedTags(metadata.status === "candidate" ? [kind, "candidate"] : [kind], decorations),
@@ -15837,6 +16083,7 @@ function buildSectionIndex(kind, pages, schemaHash, metadata, projectIds = []) {
15837
16083
  {
15838
16084
  page_id: `${kind}:index`,
15839
16085
  kind: "index",
16086
+ cssclasses: cssclassesFor("index"),
15840
16087
  title,
15841
16088
  tags: decoratedTags(["index", kind], { projectIds }),
15842
16089
  source_ids: [],
@@ -16204,6 +16451,7 @@ function buildGraphReportPage(input) {
16204
16451
  const frontmatter = {
16205
16452
  page_id: pageId,
16206
16453
  kind: "graph_report",
16454
+ cssclasses: cssclassesFor("graph_report"),
16207
16455
  title: "Graph Report",
16208
16456
  tags: ["graph", "report"],
16209
16457
  source_ids: relatedSourceIds,
@@ -16364,6 +16612,7 @@ function buildCommunitySummaryPage(input) {
16364
16612
  const frontmatter = {
16365
16613
  page_id: pageId,
16366
16614
  kind: "community_summary",
16615
+ cssclasses: cssclassesFor("community"),
16367
16616
  title: `Community: ${input.community.label}`,
16368
16617
  tags: ["graph", "community"],
16369
16618
  source_ids: relatedSourceIds,
@@ -16442,6 +16691,7 @@ function buildProjectsIndex(projectPages, schemaHash, metadata) {
16442
16691
  {
16443
16692
  page_id: "projects:index",
16444
16693
  kind: "index",
16694
+ cssclasses: cssclassesFor("index"),
16445
16695
  title: "Projects",
16446
16696
  tags: ["index", "projects"],
16447
16697
  source_ids: [],
@@ -16495,6 +16745,7 @@ function buildProjectIndex(input) {
16495
16745
  {
16496
16746
  page_id: `project:${input.projectId}:index`,
16497
16747
  kind: "index",
16748
+ cssclasses: cssclassesFor("index"),
16498
16749
  title,
16499
16750
  tags: decoratedTags(["index", "projects"], { projectIds: [input.projectId] }),
16500
16751
  source_ids: [],
@@ -16526,6 +16777,7 @@ function buildOutputPage(input) {
16526
16777
  const frontmatter = {
16527
16778
  page_id: pageId,
16528
16779
  kind: "output",
16780
+ cssclasses: cssclassesFor("output"),
16529
16781
  title: input.title ?? input.question,
16530
16782
  tags: decoratedTags(["output"], { projectIds: input.projectIds, extraTags: input.extraTags }),
16531
16783
  source_ids: input.citations,
@@ -16655,6 +16907,7 @@ function buildExploreHubPage(input) {
16655
16907
  const frontmatter = {
16656
16908
  page_id: pageId,
16657
16909
  kind: "output",
16910
+ cssclasses: cssclassesFor("output"),
16658
16911
  title,
16659
16912
  tags: decoratedTags(["output", "explore"], { projectIds: input.projectIds, extraTags: input.extraTags }),
16660
16913
  source_ids: relatedSourceIds,
@@ -20985,12 +21238,41 @@ async function ensureObsidianWorkspace(rootDir) {
20985
21238
  showTags: true,
20986
21239
  showAttachments: false,
20987
21240
  hideUnresolved: false,
20988
- colorGroups: projectIds.map((projectId, index) => ({
20989
- query: `tag:#project/${projectId}`,
20990
- color: ["#0ea5e9", "#22c55e", "#f59e0b", "#fb7185", "#8b5cf6", "#14b8a6"][index % 6]
20991
- })),
21241
+ colorGroups: [
21242
+ { query: "tag:#source", color: { a: 1, rgb: 16096779 } },
21243
+ { query: "tag:#module", color: { a: 1, rgb: 16478597 } },
21244
+ { query: "tag:#concept", color: { a: 1, rgb: 959977 } },
21245
+ { query: "tag:#entity", color: { a: 1, rgb: 2278750 } },
21246
+ { query: "tag:#rationale", color: { a: 1, rgb: 1357990 } },
21247
+ { query: "tag:#symbol", color: { a: 1, rgb: 9133302 } },
21248
+ ...projectIds.map((projectId, index) => ({
21249
+ query: `tag:#project/${projectId}`,
21250
+ color: { a: 1, rgb: [959977, 2278750, 16096779, 16478597, 9133302, 1357990][index % 6] }
21251
+ }))
21252
+ ],
20992
21253
  localJumps: false
20993
21254
  }),
21255
+ writeJsonFile(path23.join(obsidianDir, "types.json"), {
21256
+ types: {
21257
+ page_id: "text",
21258
+ kind: "text",
21259
+ title: "text",
21260
+ tags: "tags",
21261
+ aliases: "aliases",
21262
+ source_ids: "multitext",
21263
+ project_ids: "multitext",
21264
+ node_ids: "multitext",
21265
+ freshness: "text",
21266
+ status: "text",
21267
+ confidence: "number",
21268
+ created_at: "datetime",
21269
+ updated_at: "datetime",
21270
+ compiled_from: "multitext",
21271
+ managed_by: "text",
21272
+ backlinks: "multitext",
21273
+ cssclasses: "multitext"
21274
+ }
21275
+ }),
20994
21276
  writeJsonFile(path23.join(obsidianDir, "workspace.json"), {
20995
21277
  active: "root",
20996
21278
  lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
@@ -22194,7 +22476,7 @@ async function bootstrapDemo(rootDir, input) {
22194
22476
  }
22195
22477
 
22196
22478
  // src/mcp.ts
22197
- var SERVER_VERSION = "0.7.26";
22479
+ var SERVER_VERSION = "0.7.28";
22198
22480
  async function createMcpServer(rootDir) {
22199
22481
  const server = new McpServer({
22200
22482
  name: "swarmvault",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/engine",
3
- "version": "0.7.26",
3
+ "version": "0.7.28",
4
4
  "description": "Core engine for SwarmVault: ingest, compile, query, lint, and provider abstractions.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -72,6 +72,7 @@
72
72
  "tree-sitter-wasms": "^0.1.13",
73
73
  "turndown": "^7.2.1",
74
74
  "typescript": "^5.9.3",
75
+ "vis-network": "^10.0.2",
75
76
  "xlsx": "^0.18.5",
76
77
  "yaml": "^2.8.1",
77
78
  "youtube-transcript-plus": "^2.0.0",