@swarmvaultai/engine 1.1.0 → 1.3.0

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
@@ -7073,6 +7073,322 @@ function enrichGraph(graph, manifests, analyses, extraSimilarityEdges = [], opti
7073
7073
  };
7074
7074
  }
7075
7075
 
7076
+ // src/graph-share.ts
7077
+ function displayVaultName(value) {
7078
+ const trimmed = value?.trim();
7079
+ return trimmed ? trimmed : "this vault";
7080
+ }
7081
+ function sortedFallbackHubs(graph) {
7082
+ return graph.nodes.filter((node) => node.type !== "source").sort(
7083
+ (left, right) => (right.degree ?? 0) - (left.degree ?? 0) || (right.bridgeScore ?? 0) - (left.bridgeScore ?? 0) || left.label.localeCompare(right.label)
7084
+ ).slice(0, 5);
7085
+ }
7086
+ function graphNodeMap(graph) {
7087
+ return new Map(graph.nodes.map((node) => [node.id, node]));
7088
+ }
7089
+ function compactJoin(values, fallback) {
7090
+ const filtered = values.filter(Boolean);
7091
+ if (!filtered.length) {
7092
+ return fallback;
7093
+ }
7094
+ if (filtered.length === 1) {
7095
+ return filtered[0] ?? fallback;
7096
+ }
7097
+ if (filtered.length === 2) {
7098
+ return `${filtered[0]} and ${filtered[1]}`;
7099
+ }
7100
+ return `${filtered.slice(0, -1).join(", ")}, and ${filtered[filtered.length - 1]}`;
7101
+ }
7102
+ function buildShortPost(input) {
7103
+ const topHubLine = input.topHubs.length ? `Top hubs: ${compactJoin(
7104
+ input.topHubs.slice(0, 3).map((node) => node.label),
7105
+ "still emerging"
7106
+ )}.` : "Top hubs are still emerging.";
7107
+ const surprise = input.surprisingConnections[0];
7108
+ const surpriseLine = surprise ? `Most surprising link: ${surprise.sourceLabel} ${surprise.relation} ${surprise.targetLabel}.` : "The graph is ready for its first surprising connection.";
7109
+ return [
7110
+ `I scanned ${input.vaultName} with SwarmVault: ${input.overview.sources} sources -> ${input.overview.pages} wiki pages, ${input.overview.nodes} graph nodes, ${input.overview.edges} edges.`,
7111
+ topHubLine,
7112
+ surpriseLine,
7113
+ "Everything stays local. Try: npm install -g @swarmvaultai/cli && swarmvault scan ./your-repo"
7114
+ ].join("\n");
7115
+ }
7116
+ function escapeXml(value) {
7117
+ return String(value ?? "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
7118
+ }
7119
+ function clipText(value, maxLength) {
7120
+ const normalized = value.replaceAll("\n", " ").replaceAll("\r", " ").trim();
7121
+ if (normalized.length <= maxLength) {
7122
+ return normalized;
7123
+ }
7124
+ return `${normalized.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
7125
+ }
7126
+ function svgText(input) {
7127
+ const attrs = [
7128
+ `x="${input.x}"`,
7129
+ `y="${input.y}"`,
7130
+ `font-size="${input.size}"`,
7131
+ `fill="${input.fill ?? "#f8fafc"}"`,
7132
+ `font-weight="${input.weight ?? 500}"`,
7133
+ `text-anchor="${input.anchor ?? "start"}"`,
7134
+ input.opacity === void 0 ? "" : `opacity="${input.opacity}"`
7135
+ ].filter(Boolean);
7136
+ return ` <text ${attrs.join(" ")}>${escapeXml(input.text)}</text>`;
7137
+ }
7138
+ function svgStatCard(input) {
7139
+ return [
7140
+ ` <rect x="${input.x}" y="${input.y}" width="168" height="92" rx="14" fill="#111827" stroke="#334155" />`,
7141
+ svgText({ x: input.x + 20, y: input.y + 36, text: String(input.value), size: 30, fill: "#ecfeff", weight: 800 }),
7142
+ svgText({ x: input.x + 20, y: input.y + 66, text: input.label, size: 16, fill: "#94a3b8", weight: 600 })
7143
+ ];
7144
+ }
7145
+ function svgListLines(input) {
7146
+ const items = input.items.length ? input.items.slice(0, input.maxItems) : [input.empty];
7147
+ const lines = [svgText({ x: input.x, y: input.y, text: input.title, size: 19, fill: "#a7f3d0", weight: 800 })];
7148
+ for (const [index, item] of items.entries()) {
7149
+ lines.push(
7150
+ svgText({ x: input.x, y: input.y + 38 + index * 30, text: `- ${clipText(item, 58)}`, size: 19, fill: "#e2e8f0", weight: 600 })
7151
+ );
7152
+ }
7153
+ return lines;
7154
+ }
7155
+ function buildGraphShareArtifact(input) {
7156
+ const { graph, report } = input;
7157
+ const vaultName = displayVaultName(input.vaultName);
7158
+ const nodesById = graphNodeMap(graph);
7159
+ const fallbackHubs = sortedFallbackHubs(graph);
7160
+ const reportHubs = report?.godNodes.map((node) => {
7161
+ const graphNode = nodesById.get(node.nodeId);
7162
+ return {
7163
+ nodeId: node.nodeId,
7164
+ label: node.label ?? graphNode?.label ?? node.nodeId,
7165
+ degree: node.degree ?? graphNode?.degree
7166
+ };
7167
+ }) ?? [];
7168
+ const fallbackHubHighlights = fallbackHubs.map((node) => ({
7169
+ nodeId: node.id,
7170
+ label: node.label,
7171
+ degree: node.degree
7172
+ }));
7173
+ const topHubs = (reportHubs.length ? reportHubs : fallbackHubHighlights).slice(0, 5);
7174
+ const reportBridgeNodes = report?.bridgeNodes.map((node) => {
7175
+ const graphNode = nodesById.get(node.nodeId);
7176
+ return {
7177
+ nodeId: node.nodeId,
7178
+ label: node.label ?? graphNode?.label ?? node.nodeId,
7179
+ bridgeScore: node.bridgeScore ?? graphNode?.bridgeScore
7180
+ };
7181
+ }) ?? [];
7182
+ const fallbackBridgeNodes = fallbackHubs.map((node) => ({
7183
+ nodeId: node.id,
7184
+ label: node.label,
7185
+ bridgeScore: node.bridgeScore
7186
+ }));
7187
+ const bridgeNodes = (reportBridgeNodes.length ? reportBridgeNodes : fallbackBridgeNodes).slice(0, 3).filter((node) => node.label);
7188
+ const surprisingConnections = (report?.surprisingConnections ?? []).slice(0, 3).map((connection) => {
7189
+ const source = nodesById.get(connection.sourceNodeId);
7190
+ const target = nodesById.get(connection.targetNodeId);
7191
+ return {
7192
+ sourceLabel: source?.label ?? connection.sourceNodeId,
7193
+ targetLabel: target?.label ?? connection.targetNodeId,
7194
+ relation: connection.relation,
7195
+ why: truncate(connection.why || connection.explanation || "Cross-community connection", 180)
7196
+ };
7197
+ });
7198
+ const overview = {
7199
+ sources: graph.sources.length,
7200
+ nodes: report?.overview.nodes ?? graph.nodes.length,
7201
+ edges: report?.overview.edges ?? graph.edges.length,
7202
+ pages: report?.overview.pages ?? graph.pages.length,
7203
+ communities: report?.overview.communities ?? graph.communities?.length ?? 0
7204
+ };
7205
+ const firstPartyOverview = report?.firstPartyOverview ?? {
7206
+ nodes: graph.nodes.filter((node) => node.sourceClass === "first_party").length,
7207
+ edges: graph.edges.length,
7208
+ pages: graph.pages.filter((page) => page.sourceClass === "first_party").length,
7209
+ communities: graph.communities?.length ?? 0
7210
+ };
7211
+ const relatedNodeIds = uniqueBy([...topHubs.map((node) => node.nodeId), ...bridgeNodes.map((node) => node.nodeId)], (value) => value);
7212
+ const relatedPageIds = uniqueBy(
7213
+ relatedNodeIds.map((nodeId) => nodesById.get(nodeId)?.pageId).filter((pageId) => Boolean(pageId)),
7214
+ (value) => value
7215
+ );
7216
+ const relatedSourceIds = uniqueBy(
7217
+ [...graph.sources.map((source) => source.sourceId), ...relatedNodeIds.flatMap((nodeId) => nodesById.get(nodeId)?.sourceIds ?? [])],
7218
+ (value) => value
7219
+ );
7220
+ const knowledgeGaps = report?.knowledgeGaps?.warnings?.length ? report.knowledgeGaps.warnings.slice(0, 3) : report?.warnings?.length ? report.warnings.slice(0, 3) : [];
7221
+ const tagline = `A local-first map of ${vaultName}: ${overview.sources} sources compiled into ${overview.nodes} graph nodes and ${overview.pages} wiki pages.`;
7222
+ const artifact = {
7223
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7224
+ vaultName,
7225
+ tagline,
7226
+ overview,
7227
+ firstPartyOverview,
7228
+ highlights: {
7229
+ topHubs,
7230
+ bridgeNodes,
7231
+ surprisingConnections,
7232
+ suggestedQuestions: (report?.suggestedQuestions ?? []).slice(0, 5)
7233
+ },
7234
+ knowledgeGaps,
7235
+ shortPost: "",
7236
+ relatedNodeIds,
7237
+ relatedPageIds,
7238
+ relatedSourceIds
7239
+ };
7240
+ return {
7241
+ ...artifact,
7242
+ shortPost: buildShortPost({
7243
+ vaultName,
7244
+ overview,
7245
+ topHubs,
7246
+ surprisingConnections
7247
+ })
7248
+ };
7249
+ }
7250
+ function renderGraphShareMarkdown(artifact) {
7251
+ const lines = [
7252
+ "# SwarmVault Share Card",
7253
+ "",
7254
+ `> ${artifact.tagline}`,
7255
+ "",
7256
+ "## Snapshot",
7257
+ "",
7258
+ `- Sources: ${artifact.overview.sources}`,
7259
+ `- Wiki pages: ${artifact.overview.pages}`,
7260
+ `- Graph nodes: ${artifact.overview.nodes}`,
7261
+ `- Graph edges: ${artifact.overview.edges}`,
7262
+ `- Communities: ${artifact.overview.communities}`,
7263
+ `- First-party focus: ${artifact.firstPartyOverview.nodes} nodes, ${artifact.firstPartyOverview.edges} edges, ${artifact.firstPartyOverview.pages} pages`,
7264
+ "",
7265
+ "## Highlights",
7266
+ "",
7267
+ artifact.highlights.topHubs.length ? `- Top hubs: ${compactJoin(
7268
+ artifact.highlights.topHubs.slice(0, 5).map((node) => node.degree ? `${node.label} (${node.degree})` : node.label),
7269
+ "none yet"
7270
+ )}` : "- Top hubs: none yet",
7271
+ artifact.highlights.bridgeNodes.length ? `- Bridge nodes: ${compactJoin(
7272
+ artifact.highlights.bridgeNodes.slice(0, 3).map((node) => node.label),
7273
+ "none yet"
7274
+ )}` : "- Bridge nodes: none yet",
7275
+ ...artifact.highlights.surprisingConnections.length ? artifact.highlights.surprisingConnections.map(
7276
+ (connection) => `- Surprising link: ${connection.sourceLabel} ${connection.relation} ${connection.targetLabel}. ${connection.why}`
7277
+ ) : ["- Surprising link: not enough cross-community evidence yet"],
7278
+ "",
7279
+ "## Ask Next",
7280
+ "",
7281
+ ...artifact.highlights.suggestedQuestions.length ? artifact.highlights.suggestedQuestions.map((question) => `- ${question}`) : ["- Add more sources, run `swarmvault compile`, then ask the graph what changed."],
7282
+ "",
7283
+ "## Share Post",
7284
+ "",
7285
+ "```text",
7286
+ artifact.shortPost,
7287
+ "```",
7288
+ "",
7289
+ "## Reproduce",
7290
+ "",
7291
+ "```bash",
7292
+ "npm install -g @swarmvaultai/cli",
7293
+ "swarmvault scan ./your-repo",
7294
+ "swarmvault graph share --post",
7295
+ "```",
7296
+ ""
7297
+ ];
7298
+ if (artifact.knowledgeGaps.length) {
7299
+ lines.splice(
7300
+ lines.indexOf("## Ask Next"),
7301
+ 0,
7302
+ "## Gaps To Strengthen",
7303
+ "",
7304
+ ...artifact.knowledgeGaps.map((warning) => `- ${warning}`),
7305
+ ""
7306
+ );
7307
+ }
7308
+ return `${lines.join("\n")}`;
7309
+ }
7310
+ function renderGraphShareSvg(artifact) {
7311
+ const topHubs = artifact.highlights.topHubs.map((node) => node.degree ? `${node.label} (${node.degree})` : node.label);
7312
+ const bridges = artifact.highlights.bridgeNodes.map((node) => node.label);
7313
+ const surprise = artifact.highlights.surprisingConnections[0];
7314
+ const surpriseLine = surprise ? `${surprise.sourceLabel} ${surprise.relation} ${surprise.targetLabel}` : "Add more sources to reveal the first surprising link";
7315
+ const generated = new Date(artifact.generatedAt);
7316
+ const generatedLabel = Number.isNaN(generated.getTime()) ? artifact.generatedAt : generated.toISOString().slice(0, 10);
7317
+ const lines = [
7318
+ '<?xml version="1.0" encoding="UTF-8"?>',
7319
+ `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">`,
7320
+ ` <title>SwarmVault share card for ${escapeXml(artifact.vaultName)}</title>`,
7321
+ ` <desc>${escapeXml(artifact.tagline)}</desc>`,
7322
+ " <defs>",
7323
+ ' <linearGradient id="background" x1="0" y1="0" x2="1" y2="1">',
7324
+ ' <stop offset="0%" stop-color="#020617" />',
7325
+ ' <stop offset="58%" stop-color="#0f172a" />',
7326
+ ' <stop offset="100%" stop-color="#063f37" />',
7327
+ " </linearGradient>",
7328
+ ' <linearGradient id="accent" x1="0" y1="0" x2="1" y2="0">',
7329
+ ' <stop offset="0%" stop-color="#22c55e" />',
7330
+ ' <stop offset="100%" stop-color="#06b6d4" />',
7331
+ " </linearGradient>",
7332
+ " </defs>",
7333
+ ' <rect width="1200" height="630" fill="url(#background)" />',
7334
+ ' <rect x="34" y="34" width="1132" height="562" rx="28" fill="#020617" opacity="0.72" stroke="#1f2937" />',
7335
+ ' <path d="M92 512 C214 386 314 456 438 326 C540 218 648 284 746 194 C860 88 1004 152 1110 84" fill="none" stroke="#22c55e" stroke-width="4" opacity="0.35" />',
7336
+ ' <circle cx="92" cy="512" r="9" fill="#22c55e" />',
7337
+ ' <circle cx="438" cy="326" r="10" fill="#06b6d4" />',
7338
+ ' <circle cx="746" cy="194" r="10" fill="#a7f3d0" />',
7339
+ ' <circle cx="1110" cy="84" r="9" fill="#22c55e" />',
7340
+ svgText({ x: 78, y: 96, text: "SwarmVault", size: 28, fill: "#86efac", weight: 900 }),
7341
+ svgText({ x: 78, y: 152, text: clipText(artifact.vaultName, 42), size: 54, fill: "#f8fafc", weight: 900 }),
7342
+ svgText({ x: 78, y: 196, text: clipText(artifact.tagline, 86), size: 22, fill: "#cbd5e1", weight: 600 }),
7343
+ ...svgStatCard({ x: 78, y: 242, label: "Sources", value: artifact.overview.sources }),
7344
+ ...svgStatCard({ x: 270, y: 242, label: "Wiki pages", value: artifact.overview.pages }),
7345
+ ...svgStatCard({ x: 462, y: 242, label: "Graph nodes", value: artifact.overview.nodes }),
7346
+ ...svgStatCard({ x: 654, y: 242, label: "Edges", value: artifact.overview.edges }),
7347
+ ` <rect x="870" y="240" width="246" height="94" rx="18" fill="url(#accent)" opacity="0.95" />`,
7348
+ svgText({ x: 993, y: 278, text: "Local-first", size: 22, fill: "#052e16", weight: 900, anchor: "middle" }),
7349
+ svgText({ x: 993, y: 307, text: "no API keys required", size: 18, fill: "#064e3b", weight: 800, anchor: "middle" }),
7350
+ ...svgListLines({
7351
+ x: 82,
7352
+ y: 398,
7353
+ title: "Top hubs",
7354
+ items: topHubs,
7355
+ empty: "Still emerging",
7356
+ maxItems: 3
7357
+ }),
7358
+ ...svgListLines({
7359
+ x: 470,
7360
+ y: 398,
7361
+ title: "Bridge nodes",
7362
+ items: bridges,
7363
+ empty: "Still emerging",
7364
+ maxItems: 3
7365
+ }),
7366
+ svgText({ x: 820, y: 398, text: "Surprising link", size: 19, fill: "#a7f3d0", weight: 800 }),
7367
+ svgText({ x: 820, y: 436, text: clipText(surpriseLine, 40), size: 21, fill: "#e2e8f0", weight: 800 }),
7368
+ svgText({
7369
+ x: 820,
7370
+ y: 470,
7371
+ text: clipText(surprise?.why ?? "Run compile again after adding more sources.", 44),
7372
+ size: 17,
7373
+ fill: "#94a3b8",
7374
+ weight: 600
7375
+ }),
7376
+ ` <rect x="78" y="536" width="744" height="42" rx="12" fill="#0f172a" stroke="#1e293b" />`,
7377
+ svgText({
7378
+ x: 100,
7379
+ y: 564,
7380
+ text: "npm install -g @swarmvaultai/cli && swarmvault scan ./your-repo",
7381
+ size: 18,
7382
+ fill: "#d1fae5",
7383
+ weight: 800
7384
+ }),
7385
+ svgText({ x: 1116, y: 564, text: `Generated ${generatedLabel}`, size: 16, fill: "#94a3b8", weight: 600, anchor: "end" }),
7386
+ "</svg>",
7387
+ ""
7388
+ ];
7389
+ return lines.join("\n");
7390
+ }
7391
+
7076
7392
  // src/graph-query-core.ts
7077
7393
  var NODE_TYPE_PRIORITY = {
7078
7394
  concept: 6,
@@ -8907,6 +9223,65 @@ function buildGraphReportPage(input) {
8907
9223
  content: matter2.stringify(body, frontmatter)
8908
9224
  };
8909
9225
  }
9226
+ function buildGraphSharePage(input) {
9227
+ const pageId = "graph:share-card";
9228
+ const pathValue = "graph/share-card.md";
9229
+ const artifact = input.artifact ?? buildGraphShareArtifact({
9230
+ graph: input.graph,
9231
+ report: input.report,
9232
+ vaultName: input.vaultName
9233
+ });
9234
+ const frontmatter = {
9235
+ page_id: pageId,
9236
+ kind: "graph_report",
9237
+ cssclasses: cssclassesFor("graph_report"),
9238
+ title: "Share Card",
9239
+ tags: ["graph", "share"],
9240
+ source_ids: artifact.relatedSourceIds,
9241
+ project_ids: [],
9242
+ node_ids: artifact.relatedNodeIds,
9243
+ freshness: "fresh",
9244
+ status: input.metadata.status,
9245
+ confidence: input.metadata.confidence,
9246
+ created_at: input.metadata.createdAt,
9247
+ updated_at: input.metadata.updatedAt,
9248
+ compiled_from: input.metadata.compiledFrom,
9249
+ managed_by: input.metadata.managedBy,
9250
+ backlinks: [],
9251
+ schema_hash: input.schemaHash,
9252
+ source_hashes: {},
9253
+ source_semantic_hashes: {},
9254
+ related_page_ids: artifact.relatedPageIds,
9255
+ related_node_ids: artifact.relatedNodeIds,
9256
+ related_source_ids: artifact.relatedSourceIds
9257
+ };
9258
+ return {
9259
+ page: {
9260
+ id: pageId,
9261
+ path: pathValue,
9262
+ title: "Share Card",
9263
+ kind: "graph_report",
9264
+ sourceIds: artifact.relatedSourceIds,
9265
+ projectIds: [],
9266
+ nodeIds: artifact.relatedNodeIds,
9267
+ freshness: "fresh",
9268
+ status: input.metadata.status,
9269
+ confidence: input.metadata.confidence,
9270
+ backlinks: [],
9271
+ schemaHash: input.schemaHash,
9272
+ sourceHashes: {},
9273
+ sourceSemanticHashes: {},
9274
+ relatedPageIds: artifact.relatedPageIds,
9275
+ relatedNodeIds: artifact.relatedNodeIds,
9276
+ relatedSourceIds: artifact.relatedSourceIds,
9277
+ createdAt: input.metadata.createdAt,
9278
+ updatedAt: input.metadata.updatedAt,
9279
+ compiledFrom: input.metadata.compiledFrom,
9280
+ managedBy: input.metadata.managedBy
9281
+ },
9282
+ content: matter2.stringify(renderGraphShareMarkdown(artifact), frontmatter)
9283
+ };
9284
+ }
8910
9285
  function buildCommunitySummaryPage(input) {
8911
9286
  const pageId = `graph:${input.community.id}`;
8912
9287
  const pathValue = communityPagePath(input.community.id);
@@ -20099,7 +20474,7 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
20099
20474
 
20100
20475
  // src/output-artifacts.ts
20101
20476
  import { z as z6 } from "zod";
20102
- function escapeXml(value) {
20477
+ function escapeXml2(value) {
20103
20478
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
20104
20479
  }
20105
20480
  function clampNumber(value, min, max) {
@@ -20170,7 +20545,7 @@ function renderChartSvg(spec) {
20170
20545
  const y = projectY(value);
20171
20546
  return [
20172
20547
  `<line x1="${margin.left}" y1="${y}" x2="${width - margin.right}" y2="${y}" stroke="#dbe4ec" stroke-width="1" />`,
20173
- `<text x="${margin.left - 16}" y="${y + 4}" text-anchor="end" font-size="14" fill="#475569">${escapeXml(value.toFixed(0))}</text>`
20548
+ `<text x="${margin.left - 16}" y="${y + 4}" text-anchor="end" font-size="14" fill="#475569">${escapeXml2(value.toFixed(0))}</text>`
20174
20549
  ].join("");
20175
20550
  }).join("");
20176
20551
  const bars = spec.kind === "bar" ? points.map((point) => {
@@ -20178,7 +20553,7 @@ function renderChartSvg(spec) {
20178
20553
  const barHeight = Math.max(8, Math.abs(zeroY - point.y));
20179
20554
  return [
20180
20555
  `<rect x="${point.centerX - barWidth / 2}" y="${top}" width="${barWidth}" height="${barHeight}" rx="12" fill="#0ea5e9" opacity="0.92" />`,
20181
- `<text x="${point.centerX}" y="${top - 10}" text-anchor="middle" font-size="13" fill="#0f172a">${escapeXml(
20556
+ `<text x="${point.centerX}" y="${top - 10}" text-anchor="middle" font-size="13" fill="#0f172a">${escapeXml2(
20182
20557
  point.value.toFixed(0)
20183
20558
  )}</text>`
20184
20559
  ].join("");
@@ -20188,33 +20563,33 @@ function renderChartSvg(spec) {
20188
20563
  `<path d="${linePath}" fill="none" stroke="#0ea5e9" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />`,
20189
20564
  ...points.map(
20190
20565
  (point) => `<circle cx="${point.centerX}" cy="${point.y}" r="8" fill="#f8fafc" stroke="#0ea5e9" stroke-width="4" />
20191
- <text x="${point.centerX}" y="${point.y - 18}" text-anchor="middle" font-size="13" fill="#0f172a">${escapeXml(
20566
+ <text x="${point.centerX}" y="${point.y - 18}" text-anchor="middle" font-size="13" fill="#0f172a">${escapeXml2(
20192
20567
  point.value.toFixed(0)
20193
20568
  )}</text>`
20194
20569
  )
20195
20570
  ].join("") : "";
20196
20571
  const labels = points.map(
20197
- (point) => `<text x="${point.centerX}" y="${height - margin.bottom + 28}" text-anchor="middle" font-size="14" fill="#334155">${escapeXml(
20572
+ (point) => `<text x="${point.centerX}" y="${height - margin.bottom + 28}" text-anchor="middle" font-size="14" fill="#334155">${escapeXml2(
20198
20573
  point.label
20199
20574
  )}</text>`
20200
20575
  ).join("");
20201
20576
  const notes = (spec.notes ?? []).map(
20202
- (note, index) => `<text x="${margin.left}" y="${height - 26 - index * 18}" font-size="13" fill="#475569">${escapeXml(note)}</text>`
20577
+ (note, index) => `<text x="${margin.left}" y="${height - 26 - index * 18}" font-size="13" fill="#475569">${escapeXml2(note)}</text>`
20203
20578
  ).join("");
20204
20579
  const svg = [
20205
- `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(spec.title)}">`,
20580
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml2(spec.title)}">`,
20206
20581
  '<rect width="100%" height="100%" fill="#f8fafc" />',
20207
- `<text x="${margin.left}" y="56" font-size="34" font-weight="700" fill="#0f172a">${escapeXml(spec.title)}</text>`,
20208
- spec.subtitle ? `<text x="${margin.left}" y="86" font-size="18" fill="#475569">${escapeXml(spec.subtitle)}</text>` : "",
20582
+ `<text x="${margin.left}" y="56" font-size="34" font-weight="700" fill="#0f172a">${escapeXml2(spec.title)}</text>`,
20583
+ spec.subtitle ? `<text x="${margin.left}" y="86" font-size="18" fill="#475569">${escapeXml2(spec.subtitle)}</text>` : "",
20209
20584
  gridLines,
20210
20585
  `<line x1="${margin.left}" y1="${zeroY}" x2="${width - margin.right}" y2="${zeroY}" stroke="#0f172a" stroke-width="2" />`,
20211
20586
  `<line x1="${margin.left}" y1="${margin.top}" x2="${margin.left}" y2="${height - margin.bottom}" stroke="#0f172a" stroke-width="2" />`,
20212
20587
  bars,
20213
20588
  lineMarks,
20214
20589
  labels,
20215
- spec.xLabel ? `<text x="${margin.left + chartWidth / 2}" y="${height - 46}" text-anchor="middle" font-size="15" fill="#475569">${escapeXml(spec.xLabel)}</text>` : "",
20216
- spec.yLabel ? `<text x="34" y="${margin.top + chartHeight / 2}" text-anchor="middle" font-size="15" fill="#475569" transform="rotate(-90 34 ${margin.top + chartHeight / 2})">${escapeXml(spec.yLabel)}</text>` : "",
20217
- spec.seriesLabel ? `<text x="${width - margin.right}" y="56" text-anchor="end" font-size="15" fill="#475569">${escapeXml(spec.seriesLabel)}</text>` : "",
20590
+ spec.xLabel ? `<text x="${margin.left + chartWidth / 2}" y="${height - 46}" text-anchor="middle" font-size="15" fill="#475569">${escapeXml2(spec.xLabel)}</text>` : "",
20591
+ spec.yLabel ? `<text x="34" y="${margin.top + chartHeight / 2}" text-anchor="middle" font-size="15" fill="#475569" transform="rotate(-90 34 ${margin.top + chartHeight / 2})">${escapeXml2(spec.yLabel)}</text>` : "",
20592
+ spec.seriesLabel ? `<text x="${width - margin.right}" y="56" text-anchor="end" font-size="15" fill="#475569">${escapeXml2(spec.seriesLabel)}</text>` : "",
20218
20593
  notes,
20219
20594
  "</svg>"
20220
20595
  ].filter(Boolean).join("");
@@ -20226,33 +20601,33 @@ function renderSceneSvg(spec) {
20226
20601
  const elements = spec.elements.map((element) => {
20227
20602
  const opacity = element.opacity === void 0 ? 1 : clampNumber(element.opacity, 0, 1);
20228
20603
  if (element.kind === "label") {
20229
- return `<text x="${element.x}" y="${element.y}" font-size="${clampNumber(element.fontSize ?? 28, 10, 72)}" fill="${escapeXml(
20604
+ return `<text x="${element.x}" y="${element.y}" font-size="${clampNumber(element.fontSize ?? 28, 10, 72)}" fill="${escapeXml2(
20230
20605
  element.fill ?? "#0f172a"
20231
- )}" opacity="${opacity}" font-family="'Avenir Next', 'Segoe UI', sans-serif">${escapeXml(element.text ?? "")}</text>`;
20606
+ )}" opacity="${opacity}" font-family="'Avenir Next', 'Segoe UI', sans-serif">${escapeXml2(element.text ?? "")}</text>`;
20232
20607
  }
20233
20608
  switch (element.shape) {
20234
20609
  case "circle":
20235
- return `<circle cx="${element.x}" cy="${element.y}" r="${Math.max(6, element.radius ?? 40)}" fill="${escapeXml(
20610
+ return `<circle cx="${element.x}" cy="${element.y}" r="${Math.max(6, element.radius ?? 40)}" fill="${escapeXml2(
20236
20611
  element.fill ?? "#dbeafe"
20237
- )}" stroke="${escapeXml(element.stroke ?? "#0ea5e9")}" stroke-width="${Math.max(1, element.strokeWidth ?? 2)}" opacity="${opacity}" />`;
20612
+ )}" stroke="${escapeXml2(element.stroke ?? "#0ea5e9")}" stroke-width="${Math.max(1, element.strokeWidth ?? 2)}" opacity="${opacity}" />`;
20238
20613
  case "line":
20239
- return `<line x1="${element.x}" y1="${element.y}" x2="${element.x + (element.width ?? 120)}" y2="${element.y + (element.height ?? 0)}" stroke="${escapeXml(element.stroke ?? "#475569")}" stroke-width="${Math.max(1, element.strokeWidth ?? 3)}" opacity="${opacity}" />`;
20614
+ return `<line x1="${element.x}" y1="${element.y}" x2="${element.x + (element.width ?? 120)}" y2="${element.y + (element.height ?? 0)}" stroke="${escapeXml2(element.stroke ?? "#475569")}" stroke-width="${Math.max(1, element.strokeWidth ?? 3)}" opacity="${opacity}" />`;
20240
20615
  default:
20241
20616
  return `<rect x="${element.x}" y="${element.y}" width="${Math.max(8, element.width ?? 160)}" height="${Math.max(
20242
20617
  8,
20243
20618
  element.height ?? 120
20244
- )}" rx="22" fill="${escapeXml(element.fill ?? "#e2e8f0")}" stroke="${escapeXml(element.stroke ?? "#94a3b8")}" stroke-width="${Math.max(
20619
+ )}" rx="22" fill="${escapeXml2(element.fill ?? "#e2e8f0")}" stroke="${escapeXml2(element.stroke ?? "#94a3b8")}" stroke-width="${Math.max(
20245
20620
  1,
20246
20621
  element.strokeWidth ?? 2
20247
20622
  )}" opacity="${opacity}" />`;
20248
20623
  }
20249
20624
  }).join("");
20250
20625
  const svg = [
20251
- `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(
20626
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml2(
20252
20627
  spec.alt
20253
20628
  )}">`,
20254
- `<rect width="100%" height="100%" fill="${escapeXml(spec.background ?? "#f8fafc")}" />`,
20255
- `<text x="48" y="64" font-size="34" font-weight="700" fill="#0f172a">${escapeXml(spec.title)}</text>`,
20629
+ `<rect width="100%" height="100%" fill="${escapeXml2(spec.background ?? "#f8fafc")}" />`,
20630
+ `<text x="48" y="64" font-size="34" font-weight="700" fill="#0f172a">${escapeXml2(spec.title)}</text>`,
20256
20631
  elements,
20257
20632
  `</svg>`
20258
20633
  ].join("");
@@ -20263,12 +20638,12 @@ function renderRasterPosterSvg(input) {
20263
20638
  const height = clampNumber(input.height ?? 720, 320, 1200);
20264
20639
  const inset = 42;
20265
20640
  const svg = [
20266
- `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(
20641
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml2(
20267
20642
  input.alt
20268
20643
  )}">`,
20269
20644
  '<rect width="100%" height="100%" fill="#f8fafc" />',
20270
- `<text x="${inset}" y="56" font-size="34" font-weight="700" fill="#0f172a">${escapeXml(input.title)}</text>`,
20271
- `<image href="${escapeXml(input.rasterFileName)}" x="${inset}" y="92" width="${width - inset * 2}" height="${height - 148}" preserveAspectRatio="xMidYMid meet" />`,
20645
+ `<text x="${inset}" y="56" font-size="34" font-weight="700" fill="#0f172a">${escapeXml2(input.title)}</text>`,
20646
+ `<image href="${escapeXml2(input.rasterFileName)}" x="${inset}" y="92" width="${width - inset * 2}" height="${height - 148}" preserveAspectRatio="xMidYMid meet" />`,
20272
20647
  `</svg>`
20273
20648
  ].join("");
20274
20649
  return { svg, width, height };
@@ -22641,9 +23016,31 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
22641
23016
  report
22642
23017
  })
22643
23018
  );
23019
+ const shareArtifact = buildGraphShareArtifact({
23020
+ graph,
23021
+ report,
23022
+ vaultName: path26.basename(paths.rootDir)
23023
+ });
23024
+ const shareRecord = await buildManagedGraphPage(
23025
+ path26.join(paths.wikiDir, "graph", "share-card.md"),
23026
+ {
23027
+ managedBy: "system",
23028
+ compiledFrom: uniqueStrings4(graph.pages.flatMap((page) => page.sourceIds)),
23029
+ confidence: 1
23030
+ },
23031
+ (metadata) => buildGraphSharePage({
23032
+ graph,
23033
+ schemaHash,
23034
+ metadata,
23035
+ artifact: shareArtifact,
23036
+ report,
23037
+ vaultName: path26.basename(paths.rootDir)
23038
+ })
23039
+ );
22644
23040
  return {
22645
- records: [reportRecord, ...communityRecords],
22646
- report
23041
+ records: [reportRecord, shareRecord, ...communityRecords],
23042
+ report,
23043
+ shareSvg: renderGraphShareSvg(shareArtifact)
22647
23044
  };
22648
23045
  }
22649
23046
  async function writePage(wikiDir, relativePath, content, changedPages) {
@@ -23264,6 +23661,7 @@ async function syncVaultArtifacts(rootDir, input) {
23264
23661
  }
23265
23662
  await writeJsonFile(paths.graphPath, graph);
23266
23663
  await writeJsonFile(path26.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
23664
+ await writeFileIfChanged(path26.join(paths.wikiDir, "graph", "share-card.svg"), graphOrientation.shareSvg);
23267
23665
  await writeJsonFile(paths.codeIndexPath, input.codeIndex);
23268
23666
  await writeJsonFile(paths.compileStatePath, {
23269
23667
  generatedAt: graph.generatedAt,
@@ -23322,7 +23720,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
23322
23720
  compileState?.generatedAt,
23323
23721
  [],
23324
23722
  config
23325
- ) : { records: [], report: null };
23723
+ ) : { records: [], report: null, shareSvg: "" };
23326
23724
  const dashboardRecords = currentGraph ? await buildDashboardRecords(
23327
23725
  config,
23328
23726
  paths,
@@ -23458,6 +23856,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
23458
23856
  }
23459
23857
  if (graphOrientation.report) {
23460
23858
  await writeJsonFile(path26.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
23859
+ await writeFileIfChanged(path26.join(paths.wikiDir, "graph", "share-card.svg"), graphOrientation.shareSvg);
23461
23860
  }
23462
23861
  const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path26.relative(paths.wikiDir, absolutePath)));
23463
23862
  const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
@@ -26615,7 +27014,7 @@ async function getWatchStatus(rootDir) {
26615
27014
  }
26616
27015
 
26617
27016
  // src/mcp.ts
26618
- var SERVER_VERSION = "1.1.0";
27017
+ var SERVER_VERSION = "1.3.0";
26619
27018
  async function createMcpServer(rootDir) {
26620
27019
  const server = new McpServer({
26621
27020
  name: "swarmvault",
@@ -30072,6 +30471,7 @@ export {
30072
30471
  blastRadiusVault,
30073
30472
  bootstrapDemo,
30074
30473
  buildConfiguredRedactor,
30474
+ buildGraphShareArtifact,
30075
30475
  buildRedactor,
30076
30476
  compileVault,
30077
30477
  computeDecayScore,
@@ -30147,6 +30547,8 @@ export {
30147
30547
  rejectApproval,
30148
30548
  reloadManagedSources,
30149
30549
  removeWatchedRoot,
30550
+ renderGraphShareMarkdown,
30551
+ renderGraphShareSvg,
30150
30552
  resetDecay,
30151
30553
  resolveConsolidationConfig,
30152
30554
  resolveDecayConfig,