@swarmvaultai/engine 0.7.25 → 0.7.26

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.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  uniqueBy,
23
23
  writeFileIfChanged,
24
24
  writeJsonFile
25
- } from "./chunk-MB7HPUTR.js";
25
+ } from "./chunk-N56FAH4N.js";
26
26
  import {
27
27
  estimatePageTokens,
28
28
  estimateTokens,
@@ -495,6 +495,7 @@ async function autoCommitWikiChanges(rootDir, operation, detail, options) {
495
495
  // src/graph-export.ts
496
496
  import fs2 from "fs/promises";
497
497
  import path2 from "path";
498
+ import matter from "gray-matter";
498
499
 
499
500
  // src/graph-interchange.ts
500
501
  function exportHyperedgeNodeId(hyperedge) {
@@ -1593,14 +1594,27 @@ function deduplicateFileName(baseName, used) {
1593
1594
  used.add(name);
1594
1595
  return name;
1595
1596
  }
1596
- async function exportObsidianVault(rootDir, outputDir) {
1597
- const graph = await loadGraph(rootDir);
1598
- const resolvedOutputDir = path2.resolve(outputDir);
1599
- await ensureDir(resolvedOutputDir);
1600
- const nodesById = graphNodeById(graph);
1601
- const communities = sortedCommunities(graph);
1597
+ function typePluralDir(nodeType) {
1598
+ const map = {
1599
+ source: "sources",
1600
+ module: "modules",
1601
+ symbol: "symbols",
1602
+ concept: "concepts",
1603
+ entity: "entities",
1604
+ rationale: "rationales"
1605
+ };
1606
+ return map[nodeType] ?? "other";
1607
+ }
1608
+ function obsidianNodeSlug(node, pageById2) {
1609
+ if (node.pageId) {
1610
+ const page = pageById2.get(node.pageId);
1611
+ if (page) return path2.basename(page.path, ".md");
1612
+ }
1613
+ return slugify(node.label);
1614
+ }
1615
+ function buildAdjacency(edges) {
1602
1616
  const adjacency = /* @__PURE__ */ new Map();
1603
- for (const edge of graph.edges) {
1617
+ for (const edge of edges) {
1604
1618
  if (!adjacency.has(edge.source)) adjacency.set(edge.source, []);
1605
1619
  if (!adjacency.has(edge.target)) adjacency.set(edge.target, []);
1606
1620
  adjacency.get(edge.source).push({
@@ -1618,44 +1632,164 @@ async function exportObsidianVault(rootDir, outputDir) {
1618
1632
  direction: "in"
1619
1633
  });
1620
1634
  }
1621
- const usedFileNames = /* @__PURE__ */ new Set();
1622
- const nodeFileName = /* @__PURE__ */ new Map();
1623
- for (const node of [...graph.nodes].sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id))) {
1624
- const name = deduplicateFileName(safeFileName(node.label), usedFileNames);
1625
- nodeFileName.set(node.id, name);
1635
+ return adjacency;
1636
+ }
1637
+ async function listFilesRecursive2(dir, base = "") {
1638
+ const results = [];
1639
+ let entries;
1640
+ try {
1641
+ entries = await fs2.readdir(dir, { withFileTypes: true });
1642
+ } catch {
1643
+ return results;
1626
1644
  }
1627
- let fileCount = 0;
1645
+ for (const entry of entries) {
1646
+ const rel = base ? `${base}/${entry.name}` : entry.name;
1647
+ if (entry.isDirectory()) {
1648
+ results.push(...await listFilesRecursive2(path2.join(dir, entry.name), rel));
1649
+ } else {
1650
+ results.push(rel);
1651
+ }
1652
+ }
1653
+ return results;
1654
+ }
1655
+ function connectionsSection(nodeIds, adjacency, nodesById, wikilinkTarget) {
1656
+ const seen = /* @__PURE__ */ new Set();
1657
+ const lines = [];
1658
+ for (const nodeId of nodeIds) {
1659
+ for (const entry of adjacency.get(nodeId) ?? []) {
1660
+ const key = `${entry.neighborId}:${entry.relation}`;
1661
+ if (seen.has(key)) continue;
1662
+ seen.add(key);
1663
+ const neighbor = nodesById.get(entry.neighborId);
1664
+ if (!neighbor) continue;
1665
+ const target = wikilinkTarget.get(entry.neighborId);
1666
+ if (!target) continue;
1667
+ lines.push(`- [[${target}|${neighbor.label}]] \u2014 ${entry.relation} (${entry.evidenceClass}, ${entry.confidence.toFixed(2)})`);
1668
+ }
1669
+ }
1670
+ return lines;
1671
+ }
1672
+ async function exportObsidianVault(rootDir, outputDir) {
1673
+ const graph = await loadGraph(rootDir);
1674
+ const { paths } = await loadVaultConfig(rootDir);
1675
+ const resolvedOutputDir = path2.resolve(outputDir);
1676
+ await ensureDir(resolvedOutputDir);
1677
+ const nodesById = graphNodeById(graph);
1678
+ const pageById2 = graphPageById(graph);
1679
+ const communities = sortedCommunities(graph);
1680
+ const adjacency = buildAdjacency(graph.edges);
1681
+ const nodesByPageId = /* @__PURE__ */ new Map();
1628
1682
  for (const node of graph.nodes) {
1629
- const fileName = nodeFileName.get(node.id);
1630
- const lines = [
1631
- "---",
1632
- `id: ${JSON.stringify(node.id)}`,
1633
- `type: ${JSON.stringify(node.type)}`,
1634
- `community: ${JSON.stringify(node.communityId ?? null)}`,
1635
- `confidence: ${node.confidence ?? null}`,
1636
- `source_class: ${JSON.stringify(node.sourceClass ?? null)}`,
1637
- `tags: ${JSON.stringify(node.tags ?? [])}`,
1638
- "---",
1639
- "",
1640
- `# ${node.label}`,
1641
- ""
1642
- ];
1643
- const neighbors = adjacency.get(node.id) ?? [];
1644
- if (neighbors.length > 0) {
1645
- lines.push("## Connections", "");
1646
- for (const neighbor of neighbors) {
1647
- const neighborNode = nodesById.get(neighbor.neighborId);
1648
- if (!neighborNode) continue;
1649
- const neighborFile = nodeFileName.get(neighbor.neighborId) ?? safeFileName(neighborNode.label);
1650
- lines.push(`- [[${neighborFile}]] \u2014 ${neighbor.relation} (${neighbor.evidenceClass}, ${neighbor.confidence.toFixed(2)})`);
1683
+ if (node.pageId && pageById2.has(node.pageId)) {
1684
+ const list = nodesByPageId.get(node.pageId) ?? [];
1685
+ list.push(node);
1686
+ nodesByPageId.set(node.pageId, list);
1687
+ }
1688
+ }
1689
+ const orphanNodes = graph.nodes.filter((node) => !node.pageId || !pageById2.has(node.pageId));
1690
+ const usedOrphanSlugs = /* @__PURE__ */ new Set();
1691
+ const orphanFilePath = /* @__PURE__ */ new Map();
1692
+ for (const node of [...orphanNodes].sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id))) {
1693
+ const slug = deduplicateFileName(obsidianNodeSlug(node, pageById2), usedOrphanSlugs);
1694
+ orphanFilePath.set(node.id, `graph/nodes/${typePluralDir(node.type)}/${slug}.md`);
1695
+ }
1696
+ const wikilinkTarget = /* @__PURE__ */ new Map();
1697
+ for (const node of graph.nodes) {
1698
+ if (node.pageId) {
1699
+ const page = pageById2.get(node.pageId);
1700
+ if (page) {
1701
+ wikilinkTarget.set(node.id, page.path.replace(/\.md$/, ""));
1702
+ continue;
1651
1703
  }
1652
- lines.push("");
1653
1704
  }
1654
- await fs2.writeFile(path2.join(resolvedOutputDir, `${fileName}.md`), lines.join("\n"), "utf8");
1705
+ const orphanPath = orphanFilePath.get(node.id);
1706
+ if (orphanPath) {
1707
+ wikilinkTarget.set(node.id, orphanPath.replace(/\.md$/, ""));
1708
+ }
1709
+ }
1710
+ let fileCount = 0;
1711
+ const wikiFiles = await listFilesRecursive2(paths.wikiDir);
1712
+ const pageByPath = new Map(graph.pages.map((p) => [p.path, p]));
1713
+ for (const relPath of wikiFiles) {
1714
+ if (!relPath.endsWith(".md")) continue;
1715
+ const srcFile = path2.join(paths.wikiDir, relPath);
1716
+ const destFile = path2.join(resolvedOutputDir, relPath);
1717
+ await ensureDir(path2.dirname(destFile));
1718
+ let rawContent;
1719
+ try {
1720
+ rawContent = await fs2.readFile(srcFile, "utf8");
1721
+ } catch {
1722
+ continue;
1723
+ }
1724
+ const matchingPage = pageByPath.get(relPath);
1725
+ const pageNodes = matchingPage ? nodesByPageId.get(matchingPage.id) ?? [] : [];
1726
+ const parsed = matter(rawContent);
1727
+ const data = parsed.data;
1728
+ if (pageNodes.length > 0) {
1729
+ const primaryNode = pageNodes[0];
1730
+ if (primaryNode.communityId) {
1731
+ data.graph_community = primaryNode.communityId;
1732
+ }
1733
+ const title = data.title ?? "";
1734
+ const nodeAliases = pageNodes.map((n) => n.label).filter((label) => label.toLowerCase() !== title.toLowerCase());
1735
+ const existingAliases = Array.isArray(data.aliases) ? data.aliases : [];
1736
+ const mergedAliases = [.../* @__PURE__ */ new Set([...existingAliases, ...nodeAliases])];
1737
+ if (mergedAliases.length > 0) {
1738
+ data.aliases = mergedAliases;
1739
+ }
1740
+ }
1741
+ let outputContent = matter.stringify(parsed.content, data);
1742
+ if (pageNodes.length > 0) {
1743
+ const connLines = connectionsSection(
1744
+ pageNodes.map((n) => n.id),
1745
+ adjacency,
1746
+ nodesById,
1747
+ wikilinkTarget
1748
+ );
1749
+ if (connLines.length > 0) {
1750
+ outputContent = `${outputContent.trimEnd()}
1751
+
1752
+ ## Graph Connections
1753
+
1754
+ ${connLines.join("\n")}
1755
+ `;
1756
+ }
1757
+ }
1758
+ await fs2.writeFile(destFile, outputContent, "utf8");
1759
+ fileCount++;
1760
+ }
1761
+ for (const node of orphanNodes) {
1762
+ const relPath = orphanFilePath.get(node.id);
1763
+ const destFile = path2.join(resolvedOutputDir, relPath);
1764
+ await ensureDir(path2.dirname(destFile));
1765
+ const slug = path2.basename(relPath, ".md");
1766
+ const aliases = node.label !== slug ? [node.label] : [];
1767
+ const frontmatter = {
1768
+ id: node.id,
1769
+ type: node.type,
1770
+ community: node.communityId ?? null,
1771
+ confidence: node.confidence ?? null,
1772
+ source_class: node.sourceClass ?? null,
1773
+ tags: node.tags ?? []
1774
+ };
1775
+ if (aliases.length > 0) {
1776
+ frontmatter.aliases = aliases;
1777
+ }
1778
+ const lines = [`# ${node.label}`, ""];
1779
+ const connLines = connectionsSection([node.id], adjacency, nodesById, wikilinkTarget);
1780
+ if (connLines.length > 0) {
1781
+ lines.push("## Connections", "", ...connLines, "");
1782
+ }
1783
+ const content = matter.stringify(lines.join("\n"), frontmatter);
1784
+ await fs2.writeFile(destFile, content, "utf8");
1655
1785
  fileCount++;
1656
1786
  }
1657
1787
  const usedCommunityFileNames = /* @__PURE__ */ new Set();
1658
1788
  for (const community of communities) {
1789
+ const wikiCommunityPage = graph.pages.find(
1790
+ (p) => p.kind === "community_summary" && p.nodeIds.some((nid) => community.nodeIds.includes(nid))
1791
+ );
1792
+ if (wikiCommunityPage) continue;
1659
1793
  const memberNodes = community.nodeIds.map((id) => nodesById.get(id)).filter((n2) => Boolean(n2));
1660
1794
  const memberIdSet = new Set(community.nodeIds);
1661
1795
  let internalEdges = 0;
@@ -1674,35 +1808,102 @@ async function exportObsidianVault(rootDir, outputDir) {
1674
1808
  return nbNode && nbNode.communityId !== community.id;
1675
1809
  });
1676
1810
  });
1677
- const communityFileName = deduplicateFileName(`_Community_${safeFileName(community.label)}`, usedCommunityFileNames);
1678
- const lines = [
1679
- "---",
1680
- `id: ${JSON.stringify(community.id)}`,
1681
- `node_count: ${memberNodes.length}`,
1682
- `cohesion: ${cohesion.toFixed(4)}`,
1683
- "---",
1684
- "",
1685
- `# ${community.label}`,
1686
- "",
1687
- "## Members",
1688
- ""
1689
- ];
1811
+ const communitySlug = deduplicateFileName(safeFileName(community.label), usedCommunityFileNames);
1812
+ const destFile = path2.join(resolvedOutputDir, "graph", "communities", `${communitySlug}.md`);
1813
+ await ensureDir(path2.dirname(destFile));
1814
+ const lines = [`# ${community.label}`, "", "## Members", ""];
1690
1815
  for (const member of memberNodes) {
1691
- const memberFile = nodeFileName.get(member.id) ?? safeFileName(member.label);
1692
- lines.push(`- [[${memberFile}]]`);
1816
+ const target = wikilinkTarget.get(member.id);
1817
+ if (target) {
1818
+ lines.push(`- [[${target}|${member.label}]]`);
1819
+ } else {
1820
+ lines.push(`- ${member.label}`);
1821
+ }
1693
1822
  }
1694
1823
  lines.push("");
1695
1824
  if (bridgeNodes.length > 0) {
1696
1825
  lines.push("## Bridge Nodes", "");
1697
1826
  for (const bridge of bridgeNodes) {
1698
- const bridgeFile = nodeFileName.get(bridge.id) ?? safeFileName(bridge.label);
1699
- lines.push(`- [[${bridgeFile}]]`);
1827
+ const target = wikilinkTarget.get(bridge.id);
1828
+ if (target) {
1829
+ lines.push(`- [[${target}|${bridge.label}]]`);
1830
+ } else {
1831
+ lines.push(`- ${bridge.label}`);
1832
+ }
1700
1833
  }
1701
1834
  lines.push("");
1702
1835
  }
1703
- await fs2.writeFile(path2.join(resolvedOutputDir, `${communityFileName}.md`), lines.join("\n"), "utf8");
1836
+ const frontmatter = {
1837
+ id: community.id,
1838
+ node_count: memberNodes.length,
1839
+ cohesion: Number(cohesion.toFixed(4))
1840
+ };
1841
+ const content = matter.stringify(lines.join("\n"), frontmatter);
1842
+ await fs2.writeFile(destFile, content, "utf8");
1704
1843
  fileCount++;
1705
1844
  }
1845
+ const outputsAssetsDir = path2.join(paths.wikiDir, "outputs", "assets");
1846
+ try {
1847
+ const assetFiles = await listFilesRecursive2(outputsAssetsDir);
1848
+ for (const relAsset of assetFiles) {
1849
+ const src = path2.join(outputsAssetsDir, relAsset);
1850
+ const dest = path2.join(resolvedOutputDir, "outputs", "assets", relAsset);
1851
+ await ensureDir(path2.dirname(dest));
1852
+ await fs2.copyFile(src, dest);
1853
+ fileCount++;
1854
+ }
1855
+ } catch {
1856
+ }
1857
+ try {
1858
+ const rawAssetFiles = await listFilesRecursive2(paths.rawAssetsDir);
1859
+ for (const relAsset of rawAssetFiles) {
1860
+ const src = path2.join(paths.rawAssetsDir, relAsset);
1861
+ const dest = path2.join(resolvedOutputDir, "raw", "assets", relAsset);
1862
+ await ensureDir(path2.dirname(dest));
1863
+ await fs2.copyFile(src, dest);
1864
+ fileCount++;
1865
+ }
1866
+ } catch {
1867
+ }
1868
+ const obsidianDir = path2.join(resolvedOutputDir, ".obsidian");
1869
+ await ensureDir(obsidianDir);
1870
+ const projectIds = Object.keys(
1871
+ graph.pages.reduce(
1872
+ (acc, page) => {
1873
+ for (const pid of page.projectIds) acc[pid] = true;
1874
+ return acc;
1875
+ },
1876
+ {}
1877
+ )
1878
+ );
1879
+ const colorGroups = projectIds.map((pid, index) => ({
1880
+ query: `tag:#project/${pid}`,
1881
+ color: ["#0ea5e9", "#22c55e", "#f59e0b", "#8b5cf6", "#fb7185", "#14b8a6"][index % 6]
1882
+ }));
1883
+ await fs2.writeFile(
1884
+ path2.join(obsidianDir, "app.json"),
1885
+ JSON.stringify(
1886
+ { newFileLocation: "folder", newFileFolderPath: "outputs", attachmentFolderPath: "raw/assets", useMarkdownLinks: false },
1887
+ null,
1888
+ 2
1889
+ ),
1890
+ "utf8"
1891
+ );
1892
+ await fs2.writeFile(
1893
+ path2.join(obsidianDir, "core-plugins.json"),
1894
+ JSON.stringify(["file-explorer", "global-search", "graph", "backlink", "tag-pane", "page-preview", "outline"], null, 2),
1895
+ "utf8"
1896
+ );
1897
+ await fs2.writeFile(
1898
+ path2.join(obsidianDir, "graph.json"),
1899
+ JSON.stringify(
1900
+ { colorGroups, "collapse-filter": false, search: "", showTags: true, showAttachments: false, showOrphans: true },
1901
+ null,
1902
+ 2
1903
+ ),
1904
+ "utf8"
1905
+ );
1906
+ fileCount += 3;
1706
1907
  return { format: "obsidian", outputPath: resolvedOutputDir, fileCount };
1707
1908
  }
1708
1909
  async function exportObsidianCanvas(rootDir, outputPath) {
@@ -2782,7 +2983,7 @@ import fs11 from "fs/promises";
2782
2983
  import path12 from "path";
2783
2984
  import { pathToFileURL } from "url";
2784
2985
  import { Readability } from "@mozilla/readability";
2785
- import matter3 from "gray-matter";
2986
+ import matter4 from "gray-matter";
2786
2987
  import ignore from "ignore";
2787
2988
  import { isText } from "istextorbinary";
2788
2989
  import { JSDOM as JSDOM2 } from "jsdom";
@@ -6272,7 +6473,7 @@ function htmlCodeAnalysis(manifest, rootNode, diagnostics) {
6272
6473
  }
6273
6474
  continue;
6274
6475
  }
6275
- if (tagName && tagName.includes("-")) {
6476
+ if (tagName?.includes("-")) {
6276
6477
  if (!seenSymbolNames.has(tagName)) {
6277
6478
  seenSymbolNames.add(tagName);
6278
6479
  draftSymbols.push({
@@ -8039,6 +8240,7 @@ import { parse as parseCsvSync } from "csv-parse/sync";
8039
8240
  import { strFromU8, unzipSync } from "fflate";
8040
8241
  import { JSDOM } from "jsdom";
8041
8242
  import TurndownService from "turndown";
8243
+ import { fetchTranscript } from "youtube-transcript-plus";
8042
8244
  import { z } from "zod";
8043
8245
 
8044
8246
  // src/markdown-ast.ts
@@ -8237,6 +8439,107 @@ async function extractImageWithVision(rootDir, input) {
8237
8439
  await attachment.cleanup();
8238
8440
  }
8239
8441
  }
8442
+ async function extractAudioTranscription(rootDir, input) {
8443
+ let provider;
8444
+ try {
8445
+ provider = await getProviderForTask(rootDir, "audioProvider");
8446
+ } catch (error) {
8447
+ return {
8448
+ artifact: {
8449
+ ...extractionMetadata("audio", input.mimeType, "audio_transcription"),
8450
+ warnings: [`Audio transcription unavailable: ${error instanceof Error ? error.message : "provider not configured"}`]
8451
+ }
8452
+ };
8453
+ }
8454
+ if (!provider.capabilities.has("audio") || !provider.transcribeAudio) {
8455
+ return {
8456
+ artifact: {
8457
+ ...extractionMetadata("audio", input.mimeType, "audio_transcription"),
8458
+ warnings: [`Audio transcription unavailable for provider ${provider.id}. Configure a provider with audio capability.`]
8459
+ }
8460
+ };
8461
+ }
8462
+ try {
8463
+ const result = await provider.transcribeAudio({
8464
+ mimeType: input.mimeType,
8465
+ bytes: input.bytes,
8466
+ fileName: input.fileName
8467
+ });
8468
+ const metadata = {};
8469
+ if (result.duration !== void 0) {
8470
+ metadata.duration = String(result.duration);
8471
+ }
8472
+ if (result.language) {
8473
+ metadata.language = result.language;
8474
+ }
8475
+ return {
8476
+ extractedText: result.text || void 0,
8477
+ artifact: {
8478
+ ...extractionMetadata("audio", input.mimeType, "audio_transcription"),
8479
+ providerId: provider.id,
8480
+ providerModel: provider.model,
8481
+ metadata: Object.keys(metadata).length ? metadata : void 0
8482
+ }
8483
+ };
8484
+ } catch (error) {
8485
+ return {
8486
+ artifact: {
8487
+ ...extractionMetadata("audio", input.mimeType, "audio_transcription"),
8488
+ providerId: provider.id,
8489
+ providerModel: provider.model,
8490
+ warnings: [`Audio transcription failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8491
+ }
8492
+ };
8493
+ }
8494
+ }
8495
+ async function extractYoutubeTranscript(input) {
8496
+ try {
8497
+ const result = await fetchTranscript(input.videoId, { videoDetails: true });
8498
+ const details = result.videoDetails;
8499
+ const title = details?.title ?? `YouTube ${input.videoId}`;
8500
+ const transcriptText = result.segments?.map((part) => part.text).join(" ") ?? "";
8501
+ const sections = [`# ${title}`];
8502
+ const metaLines = [];
8503
+ if (details?.author) metaLines.push(`**Author:** ${details.author}`);
8504
+ if (details?.lengthSeconds) {
8505
+ const seconds = details.lengthSeconds;
8506
+ const minutes = Math.floor(seconds / 60);
8507
+ const secs = seconds % 60;
8508
+ metaLines.push(`**Duration:** ${minutes}:${String(secs).padStart(2, "0")}`);
8509
+ }
8510
+ if (details?.viewCount) metaLines.push(`**Views:** ${Number(details.viewCount).toLocaleString()}`);
8511
+ metaLines.push(`**URL:** ${input.url}`);
8512
+ if (metaLines.length) {
8513
+ sections.push(metaLines.join("\n"));
8514
+ }
8515
+ if (transcriptText.trim()) {
8516
+ sections.push(`## Transcript
8517
+
8518
+ ${transcriptText.trim()}`);
8519
+ }
8520
+ const extractedText = sections.join("\n\n");
8521
+ const metadata = {};
8522
+ if (details?.title) metadata.title = details.title;
8523
+ if (details?.author) metadata.author = details.author;
8524
+ if (details?.lengthSeconds) metadata.duration = String(details.lengthSeconds);
8525
+ if (details?.viewCount) metadata.viewCount = String(details.viewCount);
8526
+ return {
8527
+ title,
8528
+ extractedText: extractedText || void 0,
8529
+ artifact: {
8530
+ ...extractionMetadata("youtube", "text/html", "youtube_transcript"),
8531
+ metadata: Object.keys(metadata).length ? metadata : void 0
8532
+ }
8533
+ };
8534
+ } catch (error) {
8535
+ return {
8536
+ artifact: {
8537
+ ...extractionMetadata("youtube", "text/html", "youtube_transcript"),
8538
+ warnings: [`YouTube transcript extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8539
+ }
8540
+ };
8541
+ }
8542
+ }
8240
8543
  function normalizePdfMetadata(raw) {
8241
8544
  if (!raw || typeof raw !== "object") {
8242
8545
  return void 0;
@@ -10034,7 +10337,7 @@ async function extractSlackExportDirectory(directoryPath) {
10034
10337
  // src/logs.ts
10035
10338
  import fs8 from "fs/promises";
10036
10339
  import path8 from "path";
10037
- import matter from "gray-matter";
10340
+ import matter2 from "gray-matter";
10038
10341
  async function resolveUniqueSessionPath(rootDir, operation, title, startedAt) {
10039
10342
  const { paths } = await initWorkspace(rootDir);
10040
10343
  await ensureDir(paths.sessionsDir);
@@ -10087,7 +10390,7 @@ async function recordSession(rootDir, input) {
10087
10390
  token_usage: input.tokenUsage
10088
10391
  }).filter(([, value]) => value !== void 0)
10089
10392
  );
10090
- const content = matter.stringify(
10393
+ const content = matter2.stringify(
10091
10394
  [
10092
10395
  `# ${input.operation[0]?.toUpperCase() ?? ""}${input.operation.slice(1)} Session`,
10093
10396
  "",
@@ -10308,7 +10611,7 @@ async function managedSourceWorkingDir(rootDir, sourceId) {
10308
10611
  // src/watch-state.ts
10309
10612
  import fs10 from "fs/promises";
10310
10613
  import path11 from "path";
10311
- import matter2 from "gray-matter";
10614
+ import matter3 from "gray-matter";
10312
10615
  function pendingEntryKey(entry) {
10313
10616
  return entry.path;
10314
10617
  }
@@ -10420,13 +10723,13 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
10420
10723
  continue;
10421
10724
  }
10422
10725
  const raw = await fs10.readFile(absolutePath, "utf8");
10423
- const parsed = matter2(raw);
10726
+ const parsed = matter3(raw);
10424
10727
  if (parsed.data.freshness === "stale") {
10425
10728
  continue;
10426
10729
  }
10427
10730
  parsed.data.freshness = "stale";
10428
10731
  parsed.data.updated_at = now;
10429
- await writeFileIfChanged(absolutePath, matter2.stringify(parsed.content, parsed.data));
10732
+ await writeFileIfChanged(absolutePath, matter3.stringify(parsed.content, parsed.data));
10430
10733
  }
10431
10734
  return affectedPagePaths;
10432
10735
  }
@@ -10522,6 +10825,9 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
10522
10825
  if (mimeType === "application/vnd.openxmlformats-officedocument.presentationml.presentation" || mimeType === "application/vnd.ms-powerpoint.presentation.macroenabled.12" || mimeType === "application/vnd.ms-powerpoint.template.macroenabled.12" || mimeType === "application/vnd.openxmlformats-officedocument.presentationml.template" || filePath.toLowerCase().endsWith(".pptx") || filePath.toLowerCase().endsWith(".pptm") || filePath.toLowerCase().endsWith(".potx") || filePath.toLowerCase().endsWith(".potm")) {
10523
10826
  return "pptx";
10524
10827
  }
10828
+ if (mimeType.startsWith("audio/") || /\.(mp3|wav|m4a|ogg|flac|webm|aac|wma)$/i.test(filePath)) {
10829
+ return "audio";
10830
+ }
10525
10831
  if (mimeType.startsWith("image/") || isImagePath(filePath)) {
10526
10832
  return "image";
10527
10833
  }
@@ -10546,6 +10852,10 @@ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
10546
10852
  function isImagePath(filePath) {
10547
10853
  return IMAGE_EXTENSIONS.has(path12.extname(filePath).toLowerCase());
10548
10854
  }
10855
+ var YOUTUBE_URL_PATTERN = /(?:youtube\.com\/watch\?.*v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([\w-]{11})/i;
10856
+ function parseYoutubeVideoId(url) {
10857
+ return url.match(YOUTUBE_URL_PATTERN)?.[1];
10858
+ }
10549
10859
  function isStructuredTextMime(mimeType) {
10550
10860
  switch (mimeType) {
10551
10861
  case "application/json":
@@ -10813,7 +11123,7 @@ function normalizeSemanticMarkdownList(value) {
10813
11123
  return items.length ? items : void 0;
10814
11124
  }
10815
11125
  function semanticMarkdownTitle(fallback, content, filePath) {
10816
- const parsed = matter3(content);
11126
+ const parsed = matter4(content);
10817
11127
  const frontmatterTitle = normalizeSemanticMarkdownScalar(parsed.data.title);
10818
11128
  if (frontmatterTitle) {
10819
11129
  return frontmatterTitle;
@@ -10821,7 +11131,7 @@ function semanticMarkdownTitle(fallback, content, filePath) {
10821
11131
  return titleFromText(fallback, parsed.content, filePath);
10822
11132
  }
10823
11133
  function semanticMarkdownContent(content) {
10824
- const parsed = matter3(content);
11134
+ const parsed = matter4(content);
10825
11135
  const body = parsed.content.replace(/\r\n?/g, "\n").trim();
10826
11136
  const semanticFrontmatter = Object.fromEntries(
10827
11137
  MARKDOWN_SEMANTIC_FRONTMATTER_KEYS.flatMap((key) => {
@@ -11025,7 +11335,7 @@ function markdownFrontmatter(value) {
11025
11335
  ([, rawValue]) => Array.isArray(rawValue) ? rawValue.length > 0 : Boolean(typeof rawValue === "string" ? rawValue.trim() : rawValue)
11026
11336
  )
11027
11337
  );
11028
- return matter3.stringify("", normalized).trimEnd().split("\n").concat([""]);
11338
+ return matter4.stringify("", normalized).trimEnd().split("\n").concat([""]);
11029
11339
  }
11030
11340
  function prepareCapturedMarkdownInput(input) {
11031
11341
  return finalizePreparedInput({
@@ -11843,7 +12153,7 @@ function preparedMatchesManifest(manifest, prepared, contentHash) {
11843
12153
  return manifest.contentHash === contentHash && manifest.extractionHash === (prepared.extractionHash ?? buildExtractionHash(prepared.extractedText, prepared.extractionArtifact)) && manifest.semanticHash === (prepared.semanticHash ?? contentHash) && manifest.title === prepared.title && manifest.sourceKind === prepared.sourceKind && manifest.sourceType === prepared.sourceType && manifest.sourceClass === prepared.sourceClass && manifest.language === prepared.language && manifest.mimeType === prepared.mimeType && manifest.repoRelativePath === prepared.repoRelativePath && manifest.sourceGroupId === prepared.sourceGroupId && manifest.sourceGroupTitle === prepared.sourceGroupTitle && manifest.sourcePartKey === prepared.sourcePartKey && manifest.partIndex === prepared.partIndex && manifest.partCount === prepared.partCount && manifest.partTitle === prepared.partTitle && JSON.stringify(manifest.details ?? {}) === JSON.stringify(prepared.details ?? {});
11844
12154
  }
11845
12155
  function shouldDeferWatchSemanticRefresh(sourceKind) {
11846
- return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "docx" || sourceKind === "epub" || sourceKind === "csv" || sourceKind === "xlsx" || sourceKind === "pptx" || sourceKind === "transcript" || sourceKind === "chat_export" || sourceKind === "email" || sourceKind === "calendar" || sourceKind === "image";
12156
+ return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "docx" || sourceKind === "epub" || sourceKind === "csv" || sourceKind === "xlsx" || sourceKind === "pptx" || sourceKind === "transcript" || sourceKind === "chat_export" || sourceKind === "email" || sourceKind === "calendar" || sourceKind === "image" || sourceKind === "audio";
11847
12157
  }
11848
12158
  function pendingSemanticRefreshId(changeType, repoRoot, relativePath) {
11849
12159
  return `pending:${changeType}:${sha256(`${toPosix(repoRoot)}:${relativePath}`).slice(0, 12)}`;
@@ -12334,6 +12644,15 @@ async function prepareFileInputs(rootDir, absoluteInput, repoRoot, sourceClass)
12334
12644
  title = extracted.title?.trim() || title;
12335
12645
  extractedText = extracted.extractedText;
12336
12646
  extractionArtifact = extracted.artifact;
12647
+ } else if (sourceKind === "audio") {
12648
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
12649
+ const extracted = await extractAudioTranscription(rootDir, {
12650
+ mimeType,
12651
+ bytes: payloadBytes,
12652
+ fileName: absoluteInput
12653
+ });
12654
+ extractedText = extracted.extractedText;
12655
+ extractionArtifact = extracted.artifact;
12337
12656
  } else {
12338
12657
  title = path12.basename(absoluteInput, path12.extname(absoluteInput));
12339
12658
  }
@@ -12365,6 +12684,28 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
12365
12684
  }
12366
12685
  async function prepareUrlInputs(rootDir, input, options) {
12367
12686
  await validateUrlSafety(input);
12687
+ const youtubeVideoId = parseYoutubeVideoId(input);
12688
+ if (youtubeVideoId) {
12689
+ const extracted = await extractYoutubeTranscript({ videoId: youtubeVideoId, url: input });
12690
+ const title2 = extracted.title ?? `YouTube ${youtubeVideoId}`;
12691
+ const extractedText2 = extracted.extractedText;
12692
+ const payloadBytes2 = Buffer.from(extractedText2 ?? "", "utf8");
12693
+ return [
12694
+ finalizePreparedInput({
12695
+ title: title2,
12696
+ originType: "url",
12697
+ sourceKind: "youtube",
12698
+ url: normalizeOriginUrl(input),
12699
+ mimeType: "text/html",
12700
+ storedExtension: ".md",
12701
+ payloadBytes: payloadBytes2,
12702
+ extractedText: extractedText2,
12703
+ extractionArtifact: extracted.artifact,
12704
+ extractionHash: buildExtractionHash(extractedText2, extracted.artifact),
12705
+ details: extracted.artifact.metadata
12706
+ })
12707
+ ];
12708
+ }
12368
12709
  const response = await fetch(input);
12369
12710
  if (!response.ok) {
12370
12711
  throw new Error(`Failed to fetch ${input}: ${response.status} ${response.statusText}`);
@@ -12584,6 +12925,14 @@ async function prepareUrlInputs(rootDir, input, options) {
12584
12925
  title = extracted.title?.trim() || title;
12585
12926
  extractedText = extracted.extractedText;
12586
12927
  extractionArtifact = extracted.artifact;
12928
+ } else if (sourceKind === "audio") {
12929
+ const extracted = await extractAudioTranscription(rootDir, {
12930
+ mimeType,
12931
+ bytes: payloadBytes,
12932
+ fileName: inputUrl.pathname
12933
+ });
12934
+ extractedText = extracted.extractedText;
12935
+ extractionArtifact = extracted.artifact;
12587
12936
  }
12588
12937
  }
12589
12938
  return [
@@ -13171,7 +13520,7 @@ import fs19 from "fs/promises";
13171
13520
  import path23 from "path";
13172
13521
  import Graph from "graphology";
13173
13522
  import louvain from "graphology-communities-louvain";
13174
- import matter9 from "gray-matter";
13523
+ import matter10 from "gray-matter";
13175
13524
  import { z as z7 } from "zod";
13176
13525
 
13177
13526
  // src/analysis.ts
@@ -13607,7 +13956,7 @@ function conflictConfidence(claimA, claimB) {
13607
13956
  // src/deep-lint.ts
13608
13957
  import fs13 from "fs/promises";
13609
13958
  import path17 from "path";
13610
- import matter4 from "gray-matter";
13959
+ import matter5 from "gray-matter";
13611
13960
  import { z as z5 } from "zod";
13612
13961
 
13613
13962
  // src/findings.ts
@@ -13981,7 +14330,7 @@ async function loadContextPages(rootDir, graph) {
13981
14330
  contextPages.slice(0, 18).map(async (page) => {
13982
14331
  const absolutePath = path17.join(paths.wikiDir, page.path);
13983
14332
  const raw = await fs13.readFile(absolutePath, "utf8").catch(() => "");
13984
- const parsed = matter4(raw);
14333
+ const parsed = matter5(raw);
13985
14334
  return {
13986
14335
  id: page.id,
13987
14336
  title: page.title,
@@ -14917,7 +15266,7 @@ function enrichGraph(graph, manifests, analyses, extraSimilarityEdges = []) {
14917
15266
  }
14918
15267
 
14919
15268
  // src/markdown.ts
14920
- import matter5 from "gray-matter";
15269
+ import matter6 from "gray-matter";
14921
15270
  function uniqueStrings2(values) {
14922
15271
  return uniqueBy(values.filter(Boolean), (value) => value);
14923
15272
  }
@@ -15172,7 +15521,7 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
15172
15521
  compiledFrom: metadata.compiledFrom,
15173
15522
  managedBy: metadata.managedBy
15174
15523
  },
15175
- content: matter5.stringify(body, safeFrontmatter(frontmatter))
15524
+ content: matter6.stringify(body, safeFrontmatter(frontmatter))
15176
15525
  };
15177
15526
  }
15178
15527
  function buildModulePage(input) {
@@ -15322,7 +15671,7 @@ function buildModulePage(input) {
15322
15671
  compiledFrom: metadata.compiledFrom,
15323
15672
  managedBy: metadata.managedBy
15324
15673
  },
15325
- content: matter5.stringify(body, frontmatter)
15674
+ content: matter6.stringify(body, frontmatter)
15326
15675
  };
15327
15676
  }
15328
15677
  function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHashes, sourceSemanticHashes, schemaHash, metadata, relativePath, relatedOutputs = [], decorations, existingContent) {
@@ -15399,7 +15748,7 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
15399
15748
  compiledFrom: metadata.compiledFrom,
15400
15749
  managedBy: metadata.managedBy
15401
15750
  },
15402
- content: matter5.stringify(body, frontmatter)
15751
+ content: matter6.stringify(body, frontmatter)
15403
15752
  };
15404
15753
  }
15405
15754
  function buildIndexPage(pages, schemaHash, metadata, projectPages = []) {
@@ -15483,7 +15832,7 @@ function buildIndexPage(pages, schemaHash, metadata, projectPages = []) {
15483
15832
  }
15484
15833
  function buildSectionIndex(kind, pages, schemaHash, metadata, projectIds = []) {
15485
15834
  const title = kind.charAt(0).toUpperCase() + kind.slice(1);
15486
- return matter5.stringify(
15835
+ return matter6.stringify(
15487
15836
  [`# ${title}`, "", ...pages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`), ""].join("\n"),
15488
15837
  {
15489
15838
  page_id: `${kind}:index`,
@@ -15995,7 +16344,7 @@ function buildGraphReportPage(input) {
15995
16344
  compiledFrom: input.metadata.compiledFrom,
15996
16345
  managedBy: input.metadata.managedBy
15997
16346
  },
15998
- content: matter5.stringify(body, frontmatter)
16347
+ content: matter6.stringify(body, frontmatter)
15999
16348
  };
16000
16349
  }
16001
16350
  function buildCommunitySummaryPage(input) {
@@ -16079,11 +16428,11 @@ function buildCommunitySummaryPage(input) {
16079
16428
  compiledFrom: input.metadata.compiledFrom,
16080
16429
  managedBy: input.metadata.managedBy
16081
16430
  },
16082
- content: matter5.stringify(body, frontmatter)
16431
+ content: matter6.stringify(body, frontmatter)
16083
16432
  };
16084
16433
  }
16085
16434
  function buildProjectsIndex(projectPages, schemaHash, metadata) {
16086
- return matter5.stringify(
16435
+ return matter6.stringify(
16087
16436
  [
16088
16437
  "# Projects",
16089
16438
  "",
@@ -16114,7 +16463,7 @@ function buildProjectsIndex(projectPages, schemaHash, metadata) {
16114
16463
  }
16115
16464
  function buildProjectIndex(input) {
16116
16465
  const title = `Project: ${input.projectId}`;
16117
- return matter5.stringify(
16466
+ return matter6.stringify(
16118
16467
  [
16119
16468
  `# ${title}`,
16120
16469
  "",
@@ -16231,7 +16580,7 @@ function buildOutputPage(input) {
16231
16580
  outputFormat: input.outputFormat,
16232
16581
  outputAssets
16233
16582
  },
16234
- content: matter5.stringify(
16583
+ content: matter6.stringify(
16235
16584
  (input.outputFormat === "slides" ? [
16236
16585
  input.answer,
16237
16586
  "",
@@ -16359,7 +16708,7 @@ function buildExploreHubPage(input) {
16359
16708
  outputFormat: input.outputFormat,
16360
16709
  outputAssets
16361
16710
  },
16362
- content: matter5.stringify(
16711
+ content: matter6.stringify(
16363
16712
  (input.outputFormat === "slides" ? [
16364
16713
  `# ${title}`,
16365
16714
  "",
@@ -16625,12 +16974,12 @@ function buildOutputAssetManifest(input) {
16625
16974
  // src/outputs.ts
16626
16975
  import fs16 from "fs/promises";
16627
16976
  import path20 from "path";
16628
- import matter7 from "gray-matter";
16977
+ import matter8 from "gray-matter";
16629
16978
 
16630
16979
  // src/pages.ts
16631
16980
  import fs15 from "fs/promises";
16632
16981
  import path19 from "path";
16633
- import matter6 from "gray-matter";
16982
+ import matter7 from "gray-matter";
16634
16983
  function normalizeStringArray(value) {
16635
16984
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16636
16985
  }
@@ -16711,7 +17060,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
16711
17060
  };
16712
17061
  }
16713
17062
  const content = await fs15.readFile(absolutePath, "utf8");
16714
- const parsed = matter6(content);
17063
+ const parsed = matter7(content);
16715
17064
  return {
16716
17065
  status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
16717
17066
  managedBy: normalizePageManager(parsed.data.managed_by, defaults.managedBy ?? "system"),
@@ -16745,7 +17094,7 @@ function inferPageKind(relativePath, explicitKind = void 0) {
16745
17094
  return "index";
16746
17095
  }
16747
17096
  function parseStoredPage(relativePath, content, defaults = {}) {
16748
- const parsed = matter6(content);
17097
+ const parsed = matter7(content);
16749
17098
  const now = (/* @__PURE__ */ new Date()).toISOString();
16750
17099
  const fallbackCreatedAt = defaults.createdAt ?? now;
16751
17100
  const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
@@ -16800,7 +17149,7 @@ async function loadInsightPages(wikiDir) {
16800
17149
  for (const absolutePath of files) {
16801
17150
  const relativePath = toPosix(path19.relative(wikiDir, absolutePath));
16802
17151
  const content = await fs15.readFile(absolutePath, "utf8");
16803
- const parsed = matter6(content);
17152
+ const parsed = matter7(content);
16804
17153
  const stats = await fs15.stat(absolutePath);
16805
17154
  const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(absolutePath, ".md");
16806
17155
  const sourceIds = normalizeStringArray(parsed.data.source_ids);
@@ -16901,7 +17250,7 @@ async function loadSavedOutputPages(wikiDir) {
16901
17250
  const relativePath = path20.posix.join(current.relativeDir, entry.name);
16902
17251
  const absolutePath = path20.join(current.absoluteDir, entry.name);
16903
17252
  const content = await fs16.readFile(absolutePath, "utf8");
16904
- const parsed = matter7(content);
17253
+ const parsed = matter8(content);
16905
17254
  const slug = relativePath.replace(/^outputs\//, "").replace(/\.md$/, "");
16906
17255
  const title = typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(slug);
16907
17256
  const pageId = typeof parsed.data.page_id === "string" ? parsed.data.page_id : `output:${slug}`;
@@ -16955,7 +17304,7 @@ async function loadSavedOutputPages(wikiDir) {
16955
17304
  // src/search.ts
16956
17305
  import fs17 from "fs/promises";
16957
17306
  import path21 from "path";
16958
- import matter8 from "gray-matter";
17307
+ import matter9 from "gray-matter";
16959
17308
  function warningMessage(warning) {
16960
17309
  return warning instanceof Error ? warning.message : String(warning);
16961
17310
  }
@@ -17043,7 +17392,7 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
17043
17392
  for (const page of pages) {
17044
17393
  const absolutePath = path21.join(wikiDir, page.path);
17045
17394
  const content = await fs17.readFile(absolutePath, "utf8");
17046
- const parsed = matter8(content);
17395
+ const parsed = matter9(content);
17047
17396
  let body = parsed.content;
17048
17397
  const primarySourceId = Array.isArray(parsed.data.source_ids) && typeof parsed.data.source_ids[0] === "string" ? parsed.data.source_ids[0] : page.sourceIds[0];
17049
17398
  if ((page.kind === "source" || page.kind === "module") && primarySourceId) {
@@ -17454,7 +17803,7 @@ async function resolveImageGenerationProvider(rootDir) {
17454
17803
  if (!providerConfig) {
17455
17804
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
17456
17805
  }
17457
- const { createProvider: createProvider2 } = await import("./registry-UA42LQUQ.js");
17806
+ const { createProvider: createProvider2 } = await import("./registry-SYCRRA65.js");
17458
17807
  return createProvider2(preferredProviderId, providerConfig, rootDir);
17459
17808
  }
17460
17809
  async function generateOutputArtifacts(rootDir, input) {
@@ -17973,7 +18322,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
17973
18322
  {
17974
18323
  relativePath: "dashboards/index.md",
17975
18324
  title: "Dashboards",
17976
- content: (metadata) => matter9.stringify(
18325
+ content: (metadata) => matter10.stringify(
17977
18326
  [
17978
18327
  "# Dashboards",
17979
18328
  "",
@@ -18025,7 +18374,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18025
18374
  {
18026
18375
  relativePath: "dashboards/recent-sources.md",
18027
18376
  title: "Recent Sources",
18028
- content: (metadata) => matter9.stringify(
18377
+ content: (metadata) => matter10.stringify(
18029
18378
  [
18030
18379
  "# Recent Sources",
18031
18380
  "",
@@ -18068,7 +18417,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18068
18417
  {
18069
18418
  relativePath: "dashboards/reading-log.md",
18070
18419
  title: "Reading Log",
18071
- content: (metadata) => matter9.stringify(
18420
+ content: (metadata) => matter10.stringify(
18072
18421
  [
18073
18422
  "# Reading Log",
18074
18423
  "",
@@ -18128,7 +18477,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18128
18477
  {
18129
18478
  relativePath: "dashboards/timeline.md",
18130
18479
  title: "Timeline",
18131
- content: (metadata) => matter9.stringify(
18480
+ content: (metadata) => matter10.stringify(
18132
18481
  [
18133
18482
  "# Timeline",
18134
18483
  "",
@@ -18174,7 +18523,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18174
18523
  {
18175
18524
  relativePath: "dashboards/source-sessions.md",
18176
18525
  title: "Source Sessions",
18177
- content: (metadata) => matter9.stringify(
18526
+ content: (metadata) => matter10.stringify(
18178
18527
  [
18179
18528
  "# Source Sessions",
18180
18529
  "",
@@ -18231,7 +18580,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18231
18580
  {
18232
18581
  relativePath: "dashboards/source-guides.md",
18233
18582
  title: "Source Guides",
18234
- content: (metadata) => matter9.stringify(
18583
+ content: (metadata) => matter10.stringify(
18235
18584
  [
18236
18585
  "# Source Guides",
18237
18586
  "",
@@ -18284,7 +18633,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18284
18633
  {
18285
18634
  relativePath: "dashboards/research-map.md",
18286
18635
  title: "Research Map",
18287
- content: (metadata) => matter9.stringify(
18636
+ content: (metadata) => matter10.stringify(
18288
18637
  [
18289
18638
  "# Research Map",
18290
18639
  "",
@@ -18348,7 +18697,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18348
18697
  {
18349
18698
  relativePath: "dashboards/contradictions.md",
18350
18699
  title: "Contradictions",
18351
- content: (metadata) => matter9.stringify(
18700
+ content: (metadata) => matter10.stringify(
18352
18701
  [
18353
18702
  "# Contradictions",
18354
18703
  "",
@@ -18407,7 +18756,7 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18407
18756
  {
18408
18757
  relativePath: "dashboards/open-questions.md",
18409
18758
  title: "Open Questions",
18410
- content: (metadata) => matter9.stringify(
18759
+ content: (metadata) => matter10.stringify(
18411
18760
  [
18412
18761
  "# Open Questions",
18413
18762
  "",
@@ -18483,7 +18832,12 @@ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
18483
18832
  function indexCompiledFrom(pages) {
18484
18833
  return uniqueStrings3(pages.flatMap((page) => page.sourceIds));
18485
18834
  }
18486
- function deriveGraphMetrics(nodes, edges) {
18835
+ function autoResolution(nodeCount, edgeCount) {
18836
+ if (nodeCount <= 20) return 0.5;
18837
+ if (edgeCount / Math.max(1, nodeCount) < 2) return 0.8;
18838
+ return 1;
18839
+ }
18840
+ function deriveGraphMetrics(nodes, edges, options) {
18487
18841
  const adjacency = /* @__PURE__ */ new Map();
18488
18842
  const connect = (left, right) => {
18489
18843
  if (!adjacency.has(left)) {
@@ -18520,7 +18874,8 @@ function deriveGraphMetrics(nodes, edges) {
18520
18874
  }
18521
18875
  }
18522
18876
  }
18523
- const louvainMapping = louvainGraph.size > 0 ? louvain(louvainGraph, { resolution: 1 }) : {};
18877
+ const effectiveResolution = options?.resolution ?? autoResolution(louvainGraph.order, louvainGraph.size);
18878
+ const louvainMapping = louvainGraph.size > 0 ? louvain(louvainGraph, { resolution: effectiveResolution }) : {};
18524
18879
  const groupByCommunity = /* @__PURE__ */ new Map();
18525
18880
  let nextIsolated = -1;
18526
18881
  for (const node of nonSourceNodes) {
@@ -18666,7 +19021,7 @@ function detectContradictions(analyses) {
18666
19021
  }
18667
19022
  return contradictions;
18668
19023
  }
18669
- function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
19024
+ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex, options) {
18670
19025
  const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
18671
19026
  const goPackageSymbolLookups = buildGoPackageSymbolLookups(analyses, manifestsById);
18672
19027
  const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
@@ -19026,7 +19381,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
19026
19381
  manifests,
19027
19382
  analyses
19028
19383
  );
19029
- const metrics = deriveGraphMetrics(graphNodes, enriched.edges);
19384
+ const metrics = deriveGraphMetrics(graphNodes, enriched.edges, { resolution: options?.communityResolution });
19030
19385
  return {
19031
19386
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
19032
19387
  nodes: metrics.nodes,
@@ -19493,7 +19848,9 @@ async function syncVaultArtifacts(rootDir, input) {
19493
19848
  }
19494
19849
  const compiledPages = records.map((record) => record.page);
19495
19850
  const basePages = [...compiledPages, ...input.outputPages, ...input.insightPages];
19496
- const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex);
19851
+ const structuralGraph = buildGraph(input.manifests, input.analyses, basePages, input.sourceProjects, input.codeIndex, {
19852
+ communityResolution: config.graph?.communityResolution
19853
+ });
19497
19854
  const contradictions = detectContradictions(input.analyses);
19498
19855
  for (const contradiction of contradictions) {
19499
19856
  const edgeId = `contradiction:${contradiction.sourceIdA}->${contradiction.sourceIdB}`;
@@ -19515,7 +19872,9 @@ async function syncVaultArtifacts(rootDir, input) {
19515
19872
  const edges = uniqueBy([...structuralGraph.edges, ...embeddingEdges], (edge) => edge.id).sort(
19516
19873
  (left, right) => left.id.localeCompare(right.id)
19517
19874
  );
19518
- const metrics = deriveGraphMetrics(resetGraphNodeMetrics(structuralGraph.nodes), edges);
19875
+ const metrics = deriveGraphMetrics(resetGraphNodeMetrics(structuralGraph.nodes), edges, {
19876
+ resolution: config.graph?.communityResolution
19877
+ });
19519
19878
  return {
19520
19879
  ...structuralGraph,
19521
19880
  nodes: metrics.nodes,
@@ -20086,7 +20445,7 @@ async function executeQuery(rootDir, question, format) {
20086
20445
  const absolutePath = path23.join(paths.wikiDir, result.path);
20087
20446
  try {
20088
20447
  const content = await fs19.readFile(absolutePath, "utf8");
20089
- const parsed = matter9(content);
20448
+ const parsed = matter10(content);
20090
20449
  return `# ${result.title}
20091
20450
  ${truncate(normalizeWhitespace(parsed.content), 1200)}`;
20092
20451
  } catch {
@@ -20299,8 +20658,8 @@ function computeChangeSummary(current, staged, changeType) {
20299
20658
  if (changeType === "delete") return "Removed page";
20300
20659
  if (changeType === "promote") return "Promoted from candidate";
20301
20660
  if (!current || !staged) return "Updated page";
20302
- const currentParsed = matter9(current);
20303
- const stagedParsed = matter9(staged);
20661
+ const currentParsed = matter10(current);
20662
+ const stagedParsed = matter10(staged);
20304
20663
  const changes = [];
20305
20664
  const currentTags = currentParsed.data.tags ?? [];
20306
20665
  const stagedTags = stagedParsed.data.tags ?? [];
@@ -20501,9 +20860,9 @@ async function promoteCandidate(rootDir, target) {
20501
20860
  const graph = await readJsonFile(paths.graphPath);
20502
20861
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
20503
20862
  const raw = await fs19.readFile(path23.join(paths.wikiDir, candidate.path), "utf8");
20504
- const parsed = matter9(raw);
20863
+ const parsed = matter10(raw);
20505
20864
  const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
20506
- const nextContent = matter9.stringify(parsed.content, {
20865
+ const nextContent = matter10.stringify(parsed.content, {
20507
20866
  ...parsed.data,
20508
20867
  status: "active",
20509
20868
  updated_at: nextUpdatedAt,
@@ -20654,7 +21013,7 @@ async function initVault(rootDir, options = {}) {
20654
21013
  const now = (/* @__PURE__ */ new Date()).toISOString();
20655
21014
  await writeFileIfChanged(
20656
21015
  insightsIndexPath,
20657
- matter9.stringify(
21016
+ matter10.stringify(
20658
21017
  (isResearchProfile ? [
20659
21018
  "# Insights",
20660
21019
  "",
@@ -20701,7 +21060,7 @@ async function initVault(rootDir, options = {}) {
20701
21060
  );
20702
21061
  await writeFileIfChanged(
20703
21062
  path23.join(paths.wikiDir, "projects", "index.md"),
20704
- matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
21063
+ matter10.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
20705
21064
  page_id: "projects:index",
20706
21065
  kind: "index",
20707
21066
  title: "Projects",
@@ -20724,7 +21083,7 @@ async function initVault(rootDir, options = {}) {
20724
21083
  );
20725
21084
  await writeFileIfChanged(
20726
21085
  path23.join(paths.wikiDir, "candidates", "index.md"),
20727
- matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
21086
+ matter10.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
20728
21087
  page_id: "candidates:index",
20729
21088
  kind: "index",
20730
21089
  title: "Candidates",
@@ -20751,7 +21110,7 @@ async function initVault(rootDir, options = {}) {
20751
21110
  if (isResearchProfile) {
20752
21111
  await writeFileIfChanged(
20753
21112
  path23.join(paths.wikiDir, "insights", "research-playbook.md"),
20754
- matter9.stringify(
21113
+ matter10.stringify(
20755
21114
  [
20756
21115
  `# ${requestedProfile === "personal-research" ? "Personal Research Playbook" : "Research Playbook"}`,
20757
21116
  "",
@@ -21536,7 +21895,7 @@ async function benchmarkVault(rootDir, options = {}) {
21536
21895
  if (!await fileExists(absolutePath)) {
21537
21896
  continue;
21538
21897
  }
21539
- const parsed = matter9(await fs19.readFile(absolutePath, "utf8"));
21898
+ const parsed = matter10(await fs19.readFile(absolutePath, "utf8"));
21540
21899
  pageContentsById.set(page.id, parsed.content);
21541
21900
  }
21542
21901
  const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
@@ -21620,7 +21979,7 @@ async function readPage(rootDir, relativePath) {
21620
21979
  return null;
21621
21980
  }
21622
21981
  const raw = await fs19.readFile(absolutePath, "utf8");
21623
- const parsed = matter9(raw);
21982
+ const parsed = matter10(raw);
21624
21983
  return {
21625
21984
  path: relativePath,
21626
21985
  title: typeof parsed.data.title === "string" ? parsed.data.title : path23.basename(relativePath, path23.extname(relativePath)),
@@ -21835,7 +22194,7 @@ async function bootstrapDemo(rootDir, input) {
21835
22194
  }
21836
22195
 
21837
22196
  // src/mcp.ts
21838
- var SERVER_VERSION = "0.7.25";
22197
+ var SERVER_VERSION = "0.7.26";
21839
22198
  async function createMcpServer(rootDir) {
21840
22199
  const server = new McpServer({
21841
22200
  name: "swarmvault",
@@ -22520,7 +22879,7 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
22520
22879
  import { spawn as spawn2 } from "child_process";
22521
22880
  import fs22 from "fs/promises";
22522
22881
  import path26 from "path";
22523
- import matter10 from "gray-matter";
22882
+ import matter11 from "gray-matter";
22524
22883
  import { JSDOM as JSDOM3 } from "jsdom";
22525
22884
  var DEFAULT_CRAWL_MAX_PAGES = 12;
22526
22885
  var DEFAULT_CRAWL_MAX_DEPTH = 2;
@@ -23863,7 +24222,7 @@ async function buildGuidedUpdatePages(rootDir, scope, session) {
23863
24222
  const relativePath = useCanonicalTargets && targetPage ? targetPage.path : targetPage ? insightRelativePathForTarget(targetPage, scope) : `insights/topics/${slugify(scope.title)}.md`;
23864
24223
  const absolutePath = path26.join(paths.wikiDir, relativePath);
23865
24224
  const existingContent = await fileExists(absolutePath) ? await fs22.readFile(absolutePath, "utf8") : "";
23866
- const parsed = existingContent ? matter10(existingContent) : { data: {}, content: "" };
24225
+ const parsed = existingContent ? matter11(existingContent) : { data: {}, content: "" };
23867
24226
  const existingData = parsed.data;
23868
24227
  const existingSourceIds = Array.isArray(existingData.source_ids) ? existingData.source_ids.filter((value) => typeof value === "string") : [];
23869
24228
  const existingProjectIds = Array.isArray(existingData.project_ids) ? existingData.project_ids.filter((value) => typeof value === "string") : [];
@@ -23924,7 +24283,7 @@ async function buildGuidedUpdatePages(rootDir, scope, session) {
23924
24283
  ""
23925
24284
  ].join("\n");
23926
24285
  const nextBody = replaceMarkedSection(baseBody, scope.id, updateBlock);
23927
- const content = matter10.stringify(
24286
+ const content = matter11.stringify(
23928
24287
  `${nextBody.trimEnd()}
23929
24288
  `,
23930
24289
  JSON.parse(
@@ -24310,7 +24669,7 @@ import fs23 from "fs/promises";
24310
24669
  import http from "http";
24311
24670
  import path28 from "path";
24312
24671
  import { promisify as promisify2 } from "util";
24313
- import matter11 from "gray-matter";
24672
+ import matter12 from "gray-matter";
24314
24673
  import mime2 from "mime-types";
24315
24674
 
24316
24675
  // src/graph-presentation.ts
@@ -24942,7 +25301,7 @@ async function readViewerPage(rootDir, relativePath) {
24942
25301
  return null;
24943
25302
  }
24944
25303
  const raw = await fs23.readFile(absolutePath, "utf8");
24945
- const parsed = matter11(raw);
25304
+ const parsed = matter12(raw);
24946
25305
  return {
24947
25306
  path: relativePath,
24948
25307
  title: typeof parsed.data.title === "string" ? parsed.data.title : path28.basename(relativePath, path28.extname(relativePath)),